✨ feat(vscode): React 文件照源码一块一张卡片,组件卡片只留名称、说明、构造参数

- 组件体的语句不再并进组件卡片,照源码顺序是它的子块;状态、引用、数据源、派生值、副作用、事件处理函数、渲染输出各成一张卡片,回调和函数里的语句是这些卡片下面的子卡片
- 普通变量、提前返回的 if、注释照语言默认的卡片;表格格子里只写名字和位置,去掉「改它的」「来自」格子里单独打开源码的那条路
- 用词改成书面叫法(更新位置、使用位置、计算表达式、依赖项、触发时机、清理函数、绑定事件、样式取决于……),新增 toggle-right、anchor、download、sigma、layout-template 五个种类图标
- 夹具加上 luffy 的 QueuedBubble.tsx、PlanChecklist.tsx,规格的 React 一节照新样子改写
This commit is contained in:
cruldra committed 2026-10-08 07:17:25 +08:00
1 parent 194544b4ba
commit 6ce4ef7ee2
20 files changed
+1327 -989

No files matched your search

+13 -21
View File
@@ -1,5 +1,5 @@
import type { ChangeEvent, KeyboardEvent, ReactElement } from 'react'
import type { Segment } from './types'
import type { BlockView } from './types'
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { recolor } from './segments'
import { SegmentText } from './SegmentText'
@@ -7,17 +7,9 @@ import { postMessage } from './vscode'
const EDIT_DEBOUNCE_MS = 200
/** 编辑框要的那一段源码:整块,或格子里单独打开的一段,字段一样。 */
export interface EditableSource {
startLine: number
endLine: number
code: string
codeTokens: Segment[]
}
interface CodeEditorProps {
source: EditableSource
/** 文档版本:每来一份新文档都要对一次输入框内容,哪怕这一段的代码没变 */
block: BlockView
/** 文档版本:每来一份新文档都要对一次输入框内容,哪怕这一块的代码没变 */
version: number
autoFocus: boolean
onAutoFocused: () => void
@@ -25,11 +17,11 @@ interface CodeEditorProps {
}
/** 一块源码的编辑状态:着色层显示,透明输入框接收输入,改动防抖后整行替换回文件。 */
export function CodeEditor({ source, version, autoFocus, onAutoFocused, onExit }: CodeEditorProps): ReactElement {
const [text, setText] = useState(source.code)
export function CodeEditor({ block, version, autoFocus, onAutoFocused, onExit }: CodeEditorProps): ReactElement {
const [text, setText] = useState(block.code)
const textareaRef = useRef<HTMLTextAreaElement>(null)
const sourceRef = useRef(source)
sourceRef.current = source
const blockRef = useRef(block)
blockRef.current = block
const textRef = useRef(text)
textRef.current = text
/** 本地改过、还没在新文档里看到的文字;有它时新文档不能覆盖有焦点的输入框 */
@@ -45,7 +37,7 @@ export function CodeEditor({ source, version, autoFocus, onAutoFocused, onExit }
if (!unsent.current)
return
unsent.current = false
const { startLine, endLine } = sourceRef.current
const { startLine, endLine } = blockRef.current
const value = textRef.current
postMessage({
type: 'edit',
@@ -59,16 +51,16 @@ export function CodeEditor({ source, version, autoFocus, onAutoFocused, onExit }
useEffect(() => {
const el = textareaRef.current
const focused = el !== null && document.activeElement === el
if (focused && unconfirmed.current !== null && unconfirmed.current !== source.code)
if (focused && unconfirmed.current !== null && unconfirmed.current !== block.code)
return
unconfirmed.current = null
sentLineCount.current = null
if (source.code === textRef.current)
if (block.code === textRef.current)
return
if (focused)
restoreSelection.current = [el.selectionStart, el.selectionEnd]
setText(source.code)
}, [version, source.code])
setText(block.code)
}, [version, block.code])
useLayoutEffect(() => {
const el = textareaRef.current
@@ -129,7 +121,7 @@ export function CodeEditor({ source, version, autoFocus, onAutoFocused, onExit }
onBlur={flush}
/>
<pre className="cr-code" aria-hidden="true">
<SegmentText segments={recolor(source.codeTokens, source.code, text)} />
<SegmentText segments={recolor(block.codeTokens, block.code, text)} />
{text === '' || text.endsWith('\n') ? ' ' : ''}
</pre>
</div>