From 8ac868ebe2a710d0e9b8bb8e0001dc66f8599802 Mon Sep 17 00:00:00 2001 From: Simon <10131203+gaomeng1900@users.noreply.github.com> Date: Sun, 12 Oct 2025 00:34:17 +0800 Subject: [PATCH] chore: clean up lint warnings --- pages/components/CodeEditor.tsx | 13 +- pages/components/JSConsole.tsx | 452 +++++++++++++-------------- pages/main.tsx | 13 - pages/test-pages/async-test.tsx | 271 ++++++++-------- pages/test-pages/navigation-test.tsx | 267 +++++++++++----- src/llms/index.ts | 15 +- 6 files changed, 573 insertions(+), 458 deletions(-) diff --git a/pages/components/CodeEditor.tsx b/pages/components/CodeEditor.tsx index 8b8e8a8..2ac29bb 100644 --- a/pages/components/CodeEditor.tsx +++ b/pages/components/CodeEditor.tsx @@ -74,11 +74,14 @@ const CodeEditor: React.FC = ({ {showLineNumbers && (
- {lines.map((_, index) => ( -
- {index + 1} -
- ))} + {lines.map((line, lineIdx) => { + const lineNum = lineIdx + 1 + return ( +
+ {lineNum} +
+ ) + })}
)} diff --git a/pages/components/JSConsole.tsx b/pages/components/JSConsole.tsx index 417fe5a..16da2f2 100644 --- a/pages/components/JSConsole.tsx +++ b/pages/components/JSConsole.tsx @@ -1,9 +1,8 @@ /** * JS 调试台,适合在文档中直接让用户运行代码,并且实时查看运行结果 */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-base-to-string */ -import { KeyboardEvent, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react' +import { KeyboardEvent, useEffect, useImperativeHandle, useRef, useState } from 'react' import HighlightSyntax from './HighlightSyntax' @@ -29,7 +28,6 @@ class ConsoleInterceptor { } static getInstance() { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!ConsoleInterceptor.instance) { ConsoleInterceptor.instance = new ConsoleInterceptor() } @@ -90,6 +88,7 @@ interface JSConsoleProps { height?: string onExecute?: (code: string, result: unknown) => void placeholder?: string + ref?: React.Ref } export interface JSConsoleRef { @@ -104,266 +103,267 @@ interface OutputItem { timestamp: number } -const JSConsole = forwardRef( - ( - { context = {}, height = '400px', onExecute, placeholder = 'Enter JavaScript code...' }, - ref - ) => { - const [input, setInput] = useState('') - const [outputs, setOutputs] = useState([]) - const [isExecuting, setIsExecuting] = useState(false) - const inputRef = useRef(null) - const outputRef = useRef(null) +const DEFAULT_CONTEXT = {} - // 持久的执行上下文,用于多轮对话共享作用域 - const executionContextRef = useRef>({}) +function JSConsole({ + context = DEFAULT_CONTEXT, + height = '400px', + onExecute, + placeholder = 'Enter JavaScript code...', + ref, +}: JSConsoleProps) { + const [input, setInput] = useState('') + const [outputs, setOutputs] = useState([]) + const [isExecuting, setIsExecuting] = useState(false) + const inputRef = useRef(null) + const outputRef = useRef(null) - // 格式化结果 - const formatResult = (value: unknown): string => { - if (value === null) return 'null' - if (value === undefined) return 'undefined' - if (typeof value === 'string') return `"${value}"` - if (typeof value === 'function') return `[Function: ${value.name || 'anonymous'}]` - if (typeof value === 'object') { - try { - return JSON.stringify(value, null, 2) - } catch { - return value.toString() - } + // 持久的执行上下文,用于多轮对话共享作用域 + const executionContextRef = useRef>({}) + + // 格式化结果 + const formatResult = (value: unknown): string => { + if (value === null) return 'null' + if (value === undefined) return 'undefined' + if (typeof value === 'string') return `"${value}"` + if (typeof value === 'function') return `[Function: ${value.name || 'anonymous'}]` + if (typeof value === 'object') { + try { + return JSON.stringify(value, null, 2) + } catch { + return value.toString() } - return String(value) } + return String(value) + } - // 全局console拦截处理 - useEffect(() => { - const interceptor = ConsoleInterceptor.getInstance() + // 全局console拦截处理 + useEffect(() => { + const interceptor = ConsoleInterceptor.getInstance() - const handleGlobalConsole = (type: string, args: unknown[]) => { - const content = args.map((arg) => formatResult(arg)).join(' ') + const handleGlobalConsole = (type: string, args: unknown[]) => { + const content = args.map((arg) => formatResult(arg)).join(' ') - const outputItem: OutputItem = { - type: type as any, - content: content, - timestamp: Date.now(), - } - - setOutputs((prev) => [...prev, outputItem]) - - // 自动滚动到底部 - setTimeout(() => { - if (outputRef.current) { - outputRef.current.scrollTop = outputRef.current.scrollHeight - } - }, 0) - } - - interceptor.subscribe(handleGlobalConsole) - - return () => { - interceptor.unsubscribe(handleGlobalConsole) - } - }, []) - - // 执行代码 - const executeCode = async (code: string): Promise => { - if (!code.trim()) return - - setIsExecuting(true) - - // 添加输入到输出 - const inputItem: OutputItem = { - type: 'input', - content: code, + const outputItem: OutputItem = { + type: type as any, + content: content, timestamp: Date.now(), } - setOutputs((prev) => [...prev, inputItem]) + setOutputs((prev) => [...prev, outputItem]) - try { - // 创建异步函数以支持 await - const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor - - // 合并外部上下文和持久执行上下文 - const allContext = { ...context, ...executionContextRef.current } - const contextKeys = Object.keys(allContext) - const contextValues = Object.values(allContext) - - // 注入 console.log 重定向 - const logs: string[] = [] - const mockConsole = { - log: (...args: unknown[]) => { - logs.push(args.map((arg) => formatResult(arg)).join(' ')) - }, - error: (...args: unknown[]) => { - logs.push('ERROR: ' + args.map((arg) => formatResult(arg)).join(' ')) - }, - warn: (...args: unknown[]) => { - logs.push('WARN: ' + args.map((arg) => formatResult(arg)).join(' ')) - }, + // 自动滚动到底部 + setTimeout(() => { + if (outputRef.current) { + outputRef.current.scrollTop = outputRef.current.scrollHeight } + }, 0) + } - // 检测代码是否是表达式还是语句 - const trimmedCode = code.trim() - const isExpression = - !trimmedCode.includes(';') && - !trimmedCode.startsWith('const ') && - !trimmedCode.startsWith('let ') && - !trimmedCode.startsWith('var ') && - !trimmedCode.startsWith('function ') && - !trimmedCode.startsWith('class ') && - !trimmedCode.startsWith('if ') && - !trimmedCode.startsWith('for ') && - !trimmedCode.startsWith('while ') && - !trimmedCode.startsWith('try ') && - !trimmedCode.startsWith('{') && - !trimmedCode.includes('\n') + interceptor.subscribe(handleGlobalConsole) - // 如果是表达式,自动添加 return - const codeToExecute = isExpression ? `return ${code}` : code + return () => { + interceptor.unsubscribe(handleGlobalConsole) + } + }, []) - const wrappedCode = ` + // 执行代码 + const executeCode = async (code: string): Promise => { + if (!code.trim()) return + + setIsExecuting(true) + + // 添加输入到输出 + const inputItem: OutputItem = { + type: 'input', + content: code, + timestamp: Date.now(), + } + + setOutputs((prev) => [...prev, inputItem]) + + try { + // 创建异步函数以支持 await + const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor + + // 合并外部上下文和持久执行上下文 + const allContext = { ...context, ...executionContextRef.current } + const contextKeys = Object.keys(allContext) + const contextValues = Object.values(allContext) + + // 注入 console.log 重定向 + const logs: string[] = [] + const mockConsole = { + log: (...args: unknown[]) => { + logs.push(args.map((arg) => formatResult(arg)).join(' ')) + }, + error: (...args: unknown[]) => { + logs.push('ERROR: ' + args.map((arg) => formatResult(arg)).join(' ')) + }, + warn: (...args: unknown[]) => { + logs.push('WARN: ' + args.map((arg) => formatResult(arg)).join(' ')) + }, + } + + // 检测代码是否是表达式还是语句 + const trimmedCode = code.trim() + const isExpression = + !trimmedCode.includes(';') && + !trimmedCode.startsWith('const ') && + !trimmedCode.startsWith('let ') && + !trimmedCode.startsWith('var ') && + !trimmedCode.startsWith('function ') && + !trimmedCode.startsWith('class ') && + !trimmedCode.startsWith('if ') && + !trimmedCode.startsWith('for ') && + !trimmedCode.startsWith('while ') && + !trimmedCode.startsWith('try ') && + !trimmedCode.startsWith('{') && + !trimmedCode.includes('\n') + + // 如果是表达式,自动添加 return + const codeToExecute = isExpression ? `return ${code}` : code + + const wrappedCode = ` return (async function() { ${codeToExecute} })(); ` - // 执行代码 - const func = new AsyncFunction('console', ...contextKeys, wrappedCode) - const result = await func(mockConsole, ...contextValues) + // 执行代码 + const func = new AsyncFunction('console', ...contextKeys, wrappedCode) + const result = await func(mockConsole, ...contextValues) - // 添加 console.log 输出 - if (logs.length > 0) { - const logItem: OutputItem = { - type: 'log', - content: logs.join('\n'), - timestamp: Date.now(), - } - setOutputs((prev) => [...prev, logItem]) - } - - // 总是添加执行结果输出(包括 undefined) - const outputItem: OutputItem = { - type: 'output', - content: formatResult(result), + // 添加 console.log 输出 + if (logs.length > 0) { + const logItem: OutputItem = { + type: 'log', + content: logs.join('\n'), timestamp: Date.now(), } - setOutputs((prev) => [...prev, outputItem]) - - onExecute?.(code, result) - return result - } catch (error) { - const errorItem: OutputItem = { - type: 'error', - content: error instanceof Error ? error.message : String(error), - timestamp: Date.now(), - } - setOutputs((prev) => [...prev, errorItem]) - throw error - } finally { - setIsExecuting(false) - // 滚动到底部 - setTimeout(() => { - if (outputRef.current) { - outputRef.current.scrollTop = outputRef.current.scrollHeight - } - }, 0) + setOutputs((prev) => [...prev, logItem]) } - } - // 清空控制台 - const clear = () => { - setOutputs([]) - // 同时清空执行上下文 - executionContextRef.current = {} - } - - // 添加输出 - const appendOutput = (content: string) => { + // 总是添加执行结果输出(包括 undefined) const outputItem: OutputItem = { type: 'output', - content, + content: formatResult(result), timestamp: Date.now(), } setOutputs((prev) => [...prev, outputItem]) + + onExecute?.(code, result) + return result + } catch (error) { + const errorItem: OutputItem = { + type: 'error', + content: error instanceof Error ? error.message : String(error), + timestamp: Date.now(), + } + setOutputs((prev) => [...prev, errorItem]) + throw error + } finally { + setIsExecuting(false) + // 滚动到底部 + setTimeout(() => { + if (outputRef.current) { + outputRef.current.scrollTop = outputRef.current.scrollHeight + } + }, 0) } + } - // 暴露方法给父组件 - useImperativeHandle(ref, () => ({ - executeCode, - clear, - appendOutput, - })) + // 清空控制台 + const clear = () => { + setOutputs([]) + // 同时清空执行上下文 + executionContextRef.current = {} + } - // 处理键盘事件 - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Enter') { - if (e.shiftKey) { - // Shift+Enter 换行 - return - } else { - // Enter 执行 - e.preventDefault() - if (!isExecuting && input.trim()) { - executeCode(input) - setInput('') - setTimeout(() => inputRef.current?.focus(), 0) - } + // 添加输出 + const appendOutput = (content: string) => { + const outputItem: OutputItem = { + type: 'output', + content, + timestamp: Date.now(), + } + setOutputs((prev) => [...prev, outputItem]) + } + + // 暴露方法给父组件 + useImperativeHandle(ref, () => ({ + executeCode, + clear, + appendOutput, + })) + + // 处理键盘事件 + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter') { + if (e.shiftKey) { + // Shift+Enter 换行 + return + } else { + // Enter 执行 + e.preventDefault() + if (!isExecuting && input.trim()) { + executeCode(input) + setInput('') + setTimeout(() => inputRef.current?.focus(), 0) } } } - - function getPrompt(type: string) { - let prompt = ' ' - if (type === 'input') { - prompt = '>' - } else if (type === 'output') { - prompt = '<' - } - return prompt - } - - return ( -
- {/* 历史记录和输入区域 */} -
- {outputs.map((item, index) => ( -
- {getPrompt(item.type)} -
-								
-							
-
- ))} - {isExecuting && ( -
- {'> '} - Executing... -
- )} -
- - {/* 当前输入行 */} -
- {'> '} -