✨ feat(vscode): 代码阅读块悬停高亮

This commit is contained in:
cruldra committed 2026-09-25 01:54:12 +08:00
1 parent f6eb07a91d
commit 72d33032f0
2 files changed
+45 -16

No files matched your search

@@ -36,19 +36,22 @@ export interface BlockTreeState {
onToggleFold: (id: string) => void onToggleFold: (id: string) => void
} }
/** 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。 */ /**
export function BlockLevel({ blocks, tree }: { blocks: BlockView[], tree: BlockTreeState }): ReactElement { * 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。
* kind 是这一层所属块的种类(顶层没有),悬停那个块时竖线换成它的颜色。
*/
export function BlockLevel({ blocks, kind, tree }: { blocks: BlockView[], kind?: BlockKind, tree: BlockTreeState }): ReactElement {
return ( return (
<div className="flex gap-2"> <div className="cr-level flex gap-2" data-kind={kind}>
<div className="flex w-3 shrink-0 justify-center"> <div className="flex w-3 shrink-0 justify-center">
<div className="w-px bg-[var(--vscode-editorIndentGuide-background1)]" /> <div className="cr-guide w-px" />
</div> </div>
<div className="flex min-w-0 flex-1 flex-col gap-2"> <div className="flex min-w-0 flex-1 flex-col gap-2">
{blocks.map(block => ( {blocks.map(block => (
<Fragment key={block.id}> <Fragment key={block.id}>
<BlockCard block={block} tree={tree} /> <BlockCard block={block} tree={tree} />
{block.children.length > 0 && !tree.collapsed.has(block.id) && ( {block.children.length > 0 && !tree.collapsed.has(block.id) && (
<BlockLevel blocks={block.children} tree={tree} /> <BlockLevel blocks={block.children} kind={block.kind} tree={tree} />
)} )}
</Fragment> </Fragment>
))} ))}
+37 -11
View File
@@ -52,21 +52,27 @@ body.vscode-high-contrast-light {
--cr-kind-comment: #008000; --cr-kind-comment: #008000;
} }
/* 种类颜色:同一类语义共用一个颜色,标签文字区分具体是哪种 */ /* 种类颜色:同一类语义共用一个颜色,标签文字区分具体是哪种。卡片取自己的种类,子层取所属块的种类 */
.cr-block[data-kind="import"] { --kind: var(--cr-kind-import); } [data-kind="import"] { --kind: var(--cr-kind-import); }
.cr-block[data-kind="function"] { --kind: var(--cr-kind-function); } [data-kind="function"] { --kind: var(--cr-kind-function); }
.cr-block:is([data-kind="class"], [data-kind="type"]) { --kind: var(--cr-kind-type); } :is([data-kind="class"], [data-kind="type"]) { --kind: var(--cr-kind-type); }
.cr-block:is([data-kind="field"], [data-kind="const"], [data-kind="variable"]) { --kind: var(--cr-kind-data); } :is([data-kind="field"], [data-kind="const"], [data-kind="variable"]) { --kind: var(--cr-kind-data); }
.cr-block:is([data-kind="if"], [data-kind="else"]) { --kind: var(--cr-kind-branch); } :is([data-kind="if"], [data-kind="else"]) { --kind: var(--cr-kind-branch); }
.cr-block[data-kind="loop"] { --kind: var(--cr-kind-loop); } [data-kind="loop"] { --kind: var(--cr-kind-loop); }
.cr-block:is([data-kind="try"], [data-kind="catch"], [data-kind="finally"]) { --kind: var(--cr-kind-error); } :is([data-kind="try"], [data-kind="catch"], [data-kind="finally"]) { --kind: var(--cr-kind-error); }
.cr-block[data-kind="return"] { --kind: var(--cr-kind-return); } [data-kind="return"] { --kind: var(--cr-kind-return); }
.cr-block[data-kind="statement"] { --kind: var(--cr-kind-statement); } [data-kind="statement"] { --kind: var(--cr-kind-statement); }
.cr-block[data-kind="comment"] { --kind: var(--cr-kind-comment); } [data-kind="comment"] { --kind: var(--cr-kind-comment); }
/* 卡片和它的子层是相邻兄弟,:hover 只落在鼠标下最里面那张卡片上,父卡片不跟着亮 */
.cr-block { .cr-block {
background: color-mix(in srgb, var(--kind) 7%, transparent); background: color-mix(in srgb, var(--kind) 7%, transparent);
border: 1px solid color-mix(in srgb, var(--kind) 35%, transparent); border: 1px solid color-mix(in srgb, var(--kind) 35%, transparent);
transition: background-color 100ms ease-out, border-color 100ms ease-out;
}
.cr-block:hover {
background: color-mix(in srgb, var(--kind) 14%, transparent);
border-color: var(--kind);
} }
.cr-block[data-kind="comment"] { .cr-block[data-kind="comment"] {
border-style: dashed; border-style: dashed;
@@ -75,6 +81,26 @@ body.vscode-high-contrast-light {
background: var(--vscode-editor-background); background: var(--vscode-editor-background);
border-color: var(--vscode-focusBorder); border-color: var(--vscode-focusBorder);
} }
.cr-block[data-editing]:hover {
background: color-mix(in srgb, var(--kind) 7%, var(--vscode-editor-background));
}
/* 层级竖线:悬停一个块时,它子层的竖线换成它的种类色,标出这个块管到哪里为止 */
.cr-guide {
background: var(--vscode-editorIndentGuide-background1);
transition: background-color 100ms ease-out;
}
.cr-block:hover + .cr-level > * > .cr-guide {
background: var(--kind);
}
@media (prefers-reduced-motion: reduce) {
.cr-block,
.cr-guide {
transition: none;
}
}
.cr-badge { .cr-badge {
color: var(--kind); color: var(--kind);
background: color-mix(in srgb, var(--kind) 18%, transparent); background: color-mix(in srgb, var(--kind) 18%, transparent);