✨ feat(vscode): 代码阅读预览加 ⌘F 查找栏:整份文件查(折叠着的块里的也算,跳过去时展开),按页面上显示的字不分大小写匹配、中文名也认原名,Enter / ⌘G 跳、Esc 关,高亮直接画在页面上不重画卡片
This commit is contained in:
1 parent
cc3fcda051
commit
bd474986bd
27 files changed
+1833
-114
No files matched your search
@@ -8,7 +8,7 @@ import { inlineText } from './segments'
|
||||
import { SegmentText } from './SegmentText'
|
||||
import { subRowsLayout, tableCells, tableTemplate } from './subRows'
|
||||
import { TextPreview } from './TextPreview'
|
||||
import { propertyItemId, propertyRowId, tableId } from './viewState'
|
||||
import { propertyItemId, propertyRowId, tableId, textKey } from './viewState'
|
||||
|
||||
/** 列表子行里除最后一列以外各列的宽度(字典的键),最后一列占满剩下的;编辑区窄时按比例收,不把最后一列挤没 */
|
||||
const CELL_WIDTHS = ['min(150px, 28%)', 'min(220px, 38%)']
|
||||
@@ -64,18 +64,18 @@ function CellText({ cell, parts }: { cell: PreviewText, parts: TextDiff | undefi
|
||||
return 'markdown' in cell ? <TextPreview text={cell} className="min-w-0" /> : <SegmentText segments={cell} />
|
||||
}
|
||||
|
||||
/** 一格连外面那层:着色的词排在 .cr-code 的段落里,Markdown 说明是块,lead(差异记号、展开按钮)排在它左边。 */
|
||||
function CellBox({ cell, parts, className, lead, ...attrs }: { cell: PreviewText, parts: TextDiff | undefined, className?: string, lead?: ReactNode, role?: string }): ReactElement {
|
||||
/** 一格连外面那层:着色的词排在 .cr-code 的段落里,Markdown 说明是块,lead(差异记号、展开按钮)排在它左边。`find` 是这一格的记号(见 [textKey]),记在外面那层上。 */
|
||||
function CellBox({ cell, parts, className, lead, find, ...attrs }: { cell: PreviewText, parts: TextDiff | undefined, className?: string, lead?: ReactNode, find?: string, role?: string }): ReactElement {
|
||||
if (isBlock(cell, parts)) {
|
||||
return (
|
||||
<div className={cn('flex min-w-0 items-start gap-1', className)} {...attrs}>
|
||||
<div className={cn('flex min-w-0 items-start gap-1', className)} data-find={find} {...attrs}>
|
||||
{lead}
|
||||
<CellText cell={cell} parts={parts} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<p className={cn('cr-code min-w-0', className)} {...attrs}>
|
||||
<p className={cn('cr-code min-w-0', className)} data-find={find} {...attrs}>
|
||||
{lead}
|
||||
<CellText cell={cell} parts={parts} />
|
||||
</p>
|
||||
@@ -91,12 +91,12 @@ function cellLabel(cell: PreviewText): string {
|
||||
* 子行的第一列(字典的键):带下一层的子行前面是折叠箭头(图标、大小、颜色同属性行的),点箭头或这一列切换;
|
||||
* 同一层里不带下一层的子行在同样的位置留空,第一列才对得齐。
|
||||
*/
|
||||
function ItemHead({ id, cell, foldable, folding }: { id: string, cell: PreviewText, foldable: boolean, folding: Folding }): ReactElement {
|
||||
function ItemHead({ id, cell, foldable, folding, find }: { id: string, cell: PreviewText, foldable: boolean, folding: Folding, find?: string }): ReactElement {
|
||||
if (!foldable) {
|
||||
return (
|
||||
<div className="flex min-w-0 gap-1">
|
||||
<span className="w-3.5 shrink-0" />
|
||||
<CellBox cell={cell} parts={undefined} />
|
||||
<CellBox cell={cell} parts={undefined} find={find} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -116,7 +116,7 @@ function ItemHead({ id, cell, foldable, folding }: { id: string, cell: PreviewTe
|
||||
<Chevron size={14} strokeWidth={1.8} className="text-[var(--vscode-descriptionForeground)] group-hover:text-[var(--vscode-foreground)]" />
|
||||
</button>
|
||||
<div className="min-w-0 cursor-pointer" onClick={toggle}>
|
||||
<CellBox cell={cell} parts={undefined} />
|
||||
<CellBox cell={cell} parts={undefined} find={find} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -163,9 +163,12 @@ export function PropertyItems({ parentId, items, folding, marks }: { parentId: s
|
||||
data-diff={change}
|
||||
style={{ gridTemplateColumns: gridTemplate(item.cells.length) }}
|
||||
>
|
||||
{item.cells.map((cell, i) => i === 0 && nested
|
||||
? <ItemHead key={i} id={id} cell={cell} foldable={children.length > 0 && change !== 'del'} folding={folds} />
|
||||
: <CellBox key={i} cell={cell} parts={mark?.cells?.[i]} />)}
|
||||
{item.cells.map((cell, i) => {
|
||||
const find = change === 'del' ? undefined : textKey(id, String(i))
|
||||
return i === 0 && nested
|
||||
? <ItemHead key={i} id={id} cell={cell} foldable={children.length > 0 && change !== 'del'} folding={folds} find={find} />
|
||||
: <CellBox key={i} cell={cell} parts={mark?.cells?.[i]} find={find} />
|
||||
})}
|
||||
</div>
|
||||
{(open || item.table) && (change === 'del' ? <div className="cr-diff-gone">{below}</div> : below)}
|
||||
</>
|
||||
@@ -214,17 +217,18 @@ export function PropertyTable({ parentId, columns, items, marks, folding, toggle
|
||||
const cells = tableCells(item, columns)
|
||||
const listed = (item.items?.length ?? 0) > 0
|
||||
const sign = (i: number): ReactNode => signed && i === 0 && <span className="cr-diff-sign" data-change={change} aria-hidden>{SIGNS[change ?? 'none']}</span>
|
||||
const find = (i: number): string | undefined => change === 'del' ? undefined : textKey(id, String(i))
|
||||
/** 一格:讲子块的那一格是展开它的按钮(只放着色的词),别的照常(见 CellBox) */
|
||||
const cellBox = (cell: PreviewText, i: number, className?: string): ReactElement => toggle && i === 0
|
||||
? (
|
||||
<p className={cn('cr-code min-w-0', className)} role="cell">
|
||||
<p className={cn('cr-code min-w-0', className)} role="cell" data-find={find(i)}>
|
||||
{sign(i)}
|
||||
<button type="button" className="cr-item-toggle" aria-expanded={toggle.open} aria-label={`${toggle.open ? '收起' : '展开'}${cellLabel(cell)}`} onClick={toggle.onToggle}>
|
||||
<CellText cell={cell} parts={mark?.cells?.[i]} />
|
||||
</button>
|
||||
</p>
|
||||
)
|
||||
: <CellBox cell={cell} parts={mark?.cells?.[i]} className={className} lead={sign(i)} role="cell" />
|
||||
: <CellBox cell={cell} parts={mark?.cells?.[i]} className={className} lead={sign(i)} find={find(i)} role="cell" />
|
||||
const tint = toggle?.open && toggle.badge
|
||||
? { 'data-badge': '', 'style': { '--kind-dark': toggle.badge.color.dark, '--kind-light': toggle.badge.color.light } as CSSProperties }
|
||||
: {}
|
||||
@@ -233,7 +237,7 @@ export function PropertyTable({ parentId, columns, items, marks, folding, toggle
|
||||
{cells.map((cell, i) => (listed || item.table) && i === cells.length - 1
|
||||
? (
|
||||
<div key={i} className="cr-table-cell flex min-w-0 flex-col gap-[3px]" role="cell">
|
||||
<CellBox cell={cell} parts={mark?.cells?.[i]} lead={sign(i)} />
|
||||
<CellBox cell={cell} parts={mark?.cells?.[i]} lead={sign(i)} find={find(i)} />
|
||||
{listed && <PropertyItems parentId={id} items={item.items ?? []} folding={change === 'del' ? FIXED : folding} marks={mark?.items} />}
|
||||
{item.table && <PropertyTable parentId={tableId(id)} columns={item.table.columns} items={item.table.items} marks={mark?.table} folding={change === 'del' ? FIXED : folding} />}
|
||||
</div>
|
||||
@@ -248,7 +252,7 @@ export function PropertyTable({ parentId, columns, items, marks, folding, toggle
|
||||
<div className="pl-[5px]">
|
||||
<div className="cr-table" role="table" style={{ gridTemplateColumns: tableTemplate(columns.length) }}>
|
||||
<div className="cr-table-head" role="row">
|
||||
{columns.map((name, i) => <span key={i} className="cr-table-cell" role="columnheader">{name}</span>)}
|
||||
{columns.map((name, i) => <span key={i} className="cr-table-cell" role="columnheader" data-find={textKey(parentId, `h${i}`)}>{name}</span>)}
|
||||
</div>
|
||||
{removed(-1)}
|
||||
{items.map((item, index) => {
|
||||
@@ -308,14 +312,15 @@ export function PropertyGrid({ blockId, rows, collapsed, onToggleFold, marks, sl
|
||||
className="cr-prop-label group flex h-5 items-center gap-1 rounded-sm"
|
||||
aria-label={`${folded ? '展开' : '折叠'}${row.label}`}
|
||||
aria-expanded={!folded}
|
||||
data-find={textKey(id, 'label')}
|
||||
onClick={() => onToggleFold(id)}
|
||||
>
|
||||
<Chevron size={14} strokeWidth={1.8} className="shrink-0 text-[var(--vscode-descriptionForeground)] group-hover:text-[var(--vscode-foreground)]" />
|
||||
{row.label}
|
||||
</button>
|
||||
)
|
||||
: <span className="cr-prop-label flex h-5 items-center">{row.label}</span>}
|
||||
<RowValue row={row} mark={mark} />
|
||||
: <span className="cr-prop-label flex h-5 items-center" data-find={textKey(id, 'label')}>{row.label}</span>}
|
||||
<RowValue row={row} mark={mark} id={id} />
|
||||
</div>
|
||||
{!folded && layout === 'table' && <PropertyTable parentId={id} columns={row.columns ?? []} items={row.items ?? []} marks={mark?.items} folding={folding} toggles={toggles} />}
|
||||
{!folded && layout === 'list' && <PropertyItems parentId={id} items={row.items ?? []} folding={folding} marks={mark?.items} />}
|
||||
@@ -330,16 +335,17 @@ export function PropertyGrid({ blockId, rows, collapsed, onToggleFold, marks, sl
|
||||
|
||||
/**
|
||||
* 一行的值:着色的词或 Markdown(改了的逐截 / 逐词标);跟着说明(PropertyRow.note,输出参数的 `@return`)时说明接在值后面,
|
||||
* 同一行放得下就并排,剩的地方不到 12 个字宽时换到下一行。
|
||||
* 同一行放得下就并排,剩的地方不到 12 个字宽时换到下一行。`id` 是这一行的 id,值和说明按它记上记号(见 [textKey]);删掉的行没有。
|
||||
*/
|
||||
function RowValue({ row, mark }: { row: PropertyRow, mark: RowDiff | undefined }): ReactElement {
|
||||
const value = mark?.value ? <DiffText diff={mark.value} className="min-w-0" /> : <TextPreview text={row.value} className="min-w-0" />
|
||||
function RowValue({ row, mark, id }: { row: PropertyRow, mark: RowDiff | undefined, id?: string }): ReactElement {
|
||||
const find = (part: string): string | undefined => id === undefined ? undefined : textKey(id, part)
|
||||
const value = mark?.value ? <DiffText diff={mark.value} className="min-w-0" find={find('value')} /> : <TextPreview text={row.value} className="min-w-0" find={find('value')} />
|
||||
if (!row.note)
|
||||
return value
|
||||
return (
|
||||
<div className="flex min-w-0 flex-wrap items-baseline gap-x-2">
|
||||
{value}
|
||||
{mark?.note ? <DiffText diff={mark.note} className="min-w-0 flex-[1_1_12em]" /> : <TextPreview text={row.note} className="min-w-0 flex-[1_1_12em]" />}
|
||||
{mark?.note ? <DiffText diff={mark.note} className="min-w-0 flex-[1_1_12em]" find={find('note')} /> : <TextPreview text={row.note} className="min-w-0 flex-[1_1_12em]" find={find('note')} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user