✨ feat(vscode): 代码阅读预览界面
Vite 加 code-reader.html 第二入口;代码块树(层级竖线、折叠、种类标签配色)、译文 / 源码按词性着色、块内直接编辑(防抖回写整行、Esc 回预览)、翻译下拉、解析失败提示;看板与代码阅读共用带类型的 acquireVsCodeApi 包装和 ErrorBoundary。
This commit is contained in:
1 parent
cab5ac0ebc
commit
af56985d9a
16 files changed
+775
-74
No files matched your search
@@ -0,0 +1,118 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import type { BlockKind, BlockView } from './types'
|
||||
import { ChevronDown, ChevronRight, Eye, Pencil } from 'lucide-react'
|
||||
import { Fragment } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CodeEditor } from './CodeEditor'
|
||||
import { SegmentText } from './SegmentText'
|
||||
|
||||
const KIND_LABEL: Record<BlockKind, string> = {
|
||||
import: '导入',
|
||||
function: '函数',
|
||||
class: '类',
|
||||
type: '类型',
|
||||
field: '字段',
|
||||
const: '常量',
|
||||
variable: '变量',
|
||||
if: '条件',
|
||||
else: '否则',
|
||||
loop: '循环',
|
||||
try: '尝试',
|
||||
catch: '捕获',
|
||||
finally: '最终',
|
||||
return: '返回',
|
||||
statement: '语句',
|
||||
comment: '注释',
|
||||
}
|
||||
|
||||
export interface BlockTreeState {
|
||||
version: number
|
||||
editing: ReadonlySet<string>
|
||||
collapsed: ReadonlySet<string>
|
||||
/** 刚点了「编辑」、要把光标放进去的块 */
|
||||
focusId: string | null
|
||||
onFocused: () => void
|
||||
onToggleEdit: (id: string) => void
|
||||
onToggleFold: (id: string) => void
|
||||
}
|
||||
|
||||
/** 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。 */
|
||||
export function BlockLevel({ blocks, tree }: { blocks: BlockView[], tree: BlockTreeState }): ReactElement {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex w-3 shrink-0 justify-center">
|
||||
<div className="w-px bg-[var(--vscode-editorIndentGuide-background1)]" />
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-2">
|
||||
{blocks.map(block => (
|
||||
<Fragment key={block.id}>
|
||||
<BlockCard block={block} tree={tree} />
|
||||
{block.children.length > 0 && !tree.collapsed.has(block.id) && (
|
||||
<BlockLevel blocks={block.children} tree={tree} />
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** 一块:折叠箭头、种类标签、译文或源码、右边的「编辑 / 预览」切换。 */
|
||||
function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }): ReactElement {
|
||||
const editing = tree.editing.has(block.id)
|
||||
const collapsed = tree.collapsed.has(block.id)
|
||||
const hasChildren = block.children.length > 0
|
||||
const Chevron = collapsed ? ChevronRight : ChevronDown
|
||||
return (
|
||||
<div
|
||||
className="cr-block flex items-start gap-2.5 rounded-lg py-2 pr-2.5 pl-2"
|
||||
data-kind={block.kind}
|
||||
data-editing={editing ? '' : undefined}
|
||||
>
|
||||
{hasChildren
|
||||
? (
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-5 w-3.5 shrink-0 items-center justify-center rounded-sm text-[var(--vscode-descriptionForeground)] hover:text-[var(--vscode-foreground)]"
|
||||
aria-label={collapsed ? '展开子块' : '折叠子块'}
|
||||
aria-expanded={!collapsed}
|
||||
onClick={() => tree.onToggleFold(block.id)}
|
||||
>
|
||||
<Chevron size={14} strokeWidth={1.8} />
|
||||
</button>
|
||||
)
|
||||
: <span className="w-3.5 shrink-0" />}
|
||||
<span className="cr-badge flex h-5 w-11 shrink-0 items-center justify-center rounded text-[11px] leading-[14px] font-medium">
|
||||
{KIND_LABEL[block.kind]}
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
{editing
|
||||
? (
|
||||
<CodeEditor
|
||||
block={block}
|
||||
version={tree.version}
|
||||
autoFocus={tree.focusId === block.id}
|
||||
onAutoFocused={tree.onFocused}
|
||||
onExit={() => tree.onToggleEdit(block.id)}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<p className="cr-code">
|
||||
<SegmentText segments={block.translation} />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex shrink-0 items-center gap-1 rounded px-2 py-0.5 text-xs leading-4 text-[var(--vscode-descriptionForeground)] hover:bg-[var(--vscode-toolbar-hoverBackground)]',
|
||||
editing && 'bg-[var(--vscode-toolbar-activeBackground)]',
|
||||
)}
|
||||
onClick={() => tree.onToggleEdit(block.id)}
|
||||
>
|
||||
{editing ? <Eye size={13} strokeWidth={1.8} /> : <Pencil size={13} strokeWidth={1.8} />}
|
||||
{editing ? '预览' : '编辑'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user