📝 docs(vscode): 代码阅读入口标记三种样式的原型

This commit is contained in:
cruldra committed 2026-10-08 07:17:25 +08:00
1 parent 190b63e3fb
commit 5827defd51
3 files changed
+7455

No files matched your search

+378
View File
@@ -0,0 +1,378 @@
// 入口文件标记的三种样式,一列一种:代码阅读侧栏、编辑器标签栏、预览里的入口函数卡片。
// 颜色照 VS Code Dark Modern(extensions/theme-defaults/themes/dark_modern.json),主题没写的取默认深色:
// git 改过 gitDecoration.modifiedResourceForeground #E2C08D、绿色 testing.iconPassed #73C991、
// 树里搜索命中的高亮 list.highlightForeground #2AAAFF。卡片的画法照 build.js。
// 侧栏照 VS Code 的树:22px 行高、13px 字、每层缩进 8px、Seti 文件图标(目录没有图标)。
// 文件装饰(git 的 M、方案 1 的 ▶)照 VS Code 合并规则:同一个文件有几条装饰时,字母用「, 」连起来,
// 名字和字母都用排在最前的那条的颜色;目录里有改过的文件时,目录名跟着变色、行尾一个淡圆点。
const C = {
board: "#121212",
editor: "#1f1f1f",
side: "#181818",
border: "#2b2b2b",
fg: "#cccccc",
fgStrong: "#e8e8e8",
white: "#ffffff",
desc: "#9d9d9d",
focus: "#0078d4",
inactiveSelection: "#37373d",
card: "#242424",
comment: "#6a9955",
fn: "#dcdcaa",
type: "#4ec9b0",
modified: "#e2c08d",
passed: "#73c991",
highlight: "#2aaaff",
rust: "#e37933",
pyBlue: "#3d7ab8",
pyYellow: "#f2c94c",
};
const SCHEMES = [
{
key: "badge",
title: "方案 1 · ▶ 小标记",
sentence: "行尾加绿色 ▶,和 git 的 M 同位置;资源管理器、标签页也带。",
},
{
key: "bold",
title: "方案 2 · 名字整个高亮",
sentence: "文件名用强调色加粗,只在代码阅读侧栏里有,没有额外符号。",
},
{
key: "icon",
title: "方案 3 · 换成入口图标",
sentence: "文件图标换成绿色播放图标,最醒目,但看不出是哪种语言的文件。",
},
];
const TREE = [
{ kind: "folder", name: "luffy-agent-platform", depth: 0 },
{ kind: "folder", name: "backend-rust", depth: 1 },
{ kind: "folder", name: "src", depth: 2 },
{ kind: "file", name: "lib.rs", lang: "rust", depth: 3 },
{ kind: "file", name: "main.rs", lang: "rust", depth: 3, entry: true, selected: true },
{ kind: "file", name: "routes.rs", lang: "rust", depth: 3, modified: true },
{ kind: "folder", name: "lib", depth: 1 },
{ kind: "folder", name: "agent-cli", depth: 2 },
{ kind: "folder", name: "src", depth: 3 },
{ kind: "folder", name: "agent_cli", depth: 4 },
{ kind: "file", name: "__init__.py", lang: "python", depth: 5 },
{ kind: "file", name: "main.py", lang: "python", depth: 5, entry: true, modified: true },
];
const TABS = [
{ name: "main.rs", lang: "rust", entry: true, active: true },
{ name: "routes.rs", lang: "rust", modified: true },
];
const ENTRY_FN = { name: "main", summary: "启动接口服务", visibility: "本模块", returns: "()" };
function solid(color) {
return [{ type: "solid", color }];
}
function text(parent, content, o) {
const s = String(content);
const opt = o || {};
const mono = Boolean(opt.mono) && !/[^\x00-\x7F]/.test(s);
const node = {
type: "text",
name: opt.name || s.slice(0, 32),
content: s,
fontFamily: mono ? "Menlo" : "PingFang SC",
fontSize: opt.size || 13,
fontWeight: opt.weight || 400,
lineHeight: opt.lh || 1.45,
fill: solid(opt.color || C.fg),
height: "fit_content",
};
if (opt.wrap) {
node.width = opt.width || "fill_container";
node.textGrowth = "fixed-width";
} else {
node.width = "fit_content";
node.textGrowth = "auto";
}
return I(parent, node);
}
function icon(parent, name, color, size) {
return I(parent, {
type: "icon_font",
name: name,
iconFontName: name,
width: size,
height: size,
fill: solid(color),
});
}
function box(parent, name, width, height, o) {
const opt = o || {};
return I(parent, {
type: "frame",
name,
layout: opt.layout || "horizontal",
width,
height,
gap: 0,
alignItems: opt.align || "center",
justifyContent: opt.justify || "center",
fill: opt.fill ? solid(opt.fill) : undefined,
});
}
function row(parent, name, o) {
const opt = o || {};
return I(parent, {
type: "frame",
name,
layout: "horizontal",
width: opt.width || "fill_container",
height: opt.height || "fit_content",
gap: opt.gap == null ? 8 : opt.gap,
padding: opt.padding,
alignItems: opt.align || "center",
justifyContent: opt.justify,
fill: opt.fill ? solid(opt.fill) : undefined,
});
}
function col(parent, name, o) {
const opt = o || {};
return I(parent, {
type: "frame",
name,
layout: "vertical",
width: opt.width || "fill_container",
height: "fit_content",
gap: opt.gap == null ? 4 : opt.gap,
padding: opt.padding,
alignItems: opt.align,
fill: opt.fill ? solid(opt.fill) : undefined,
stroke: opt.stroke ? { thickness: 1, fill: solid(opt.stroke) } : undefined,
clipContent: false,
});
}
function spacer(parent) {
I(parent, { type: "frame", name: "Spacer", width: "fill_container", height: 4 });
}
// Seti 的文件图标:Rust 是橙色 R,Python 是蓝黄两块;方案 3 的入口换成绿色播放图标。
function fileIcon(parent, lang, entryIcon) {
const slot = box(parent, "Icon", 22, 22, { justify: "start" });
if (entryIcon) {
icon(slot, "play", C.passed, 16);
return;
}
if (lang === "rust") {
const glyph = box(slot, "Rust", 16, 16);
text(glyph, "R", { size: 12, weight: 700, color: C.rust, mono: true, lh: 1.2 });
return;
}
const glyph = I(slot, { type: "frame", name: "Python", width: 16, height: 16, layout: "none" });
I(glyph, { type: "rectangle", name: "Blue", x: 1, y: 1, width: 9, height: 9, cornerRadius: 2.5, fill: solid(C.pyBlue) });
I(glyph, { type: "rectangle", name: "Yellow", x: 6, y: 6, width: 9, height: 9, cornerRadius: 2.5, fill: solid(C.pyYellow) });
}
// 一个文件在这一列里怎么显示:名字的颜色、粗细、行尾字母、图标。
function fileLook(item, scheme) {
const marks = [];
if (scheme === "badge" && item.entry) marks.push({ letter: "▶", color: C.passed });
if (item.modified) marks.push({ letter: "M", color: C.modified });
const look = {
color: marks.length ? marks[0].color : null,
weight: 400,
badge: marks.length ? { text: marks.map((mark) => mark.letter).join(", "), color: marks[0].color } : null,
entryIcon: scheme === "icon" && Boolean(item.entry),
};
if (scheme === "bold" && item.entry) {
look.color = C.highlight;
look.weight = 700;
}
return look;
}
function treeRow(list, item, scheme) {
const line = I(list, {
type: "frame",
name: item.name,
layout: "horizontal",
width: "fill_container",
height: 22,
gap: 0,
alignItems: "center",
padding: [0, 12, 0, 8 + item.depth * 8],
fill: item.selected ? solid(C.inactiveSelection) : undefined,
});
const twisty = box(line, "Twisty", 22, 22, { justify: "start" });
if (item.kind === "folder") {
icon(twisty, "chevron-down", C.fg, 16);
text(line, item.name, { color: C.modified });
spacer(line);
const dot = box(line, "Bubble", 16, 16);
I(dot, { type: "ellipse", name: "Dot", width: 7, height: 7, opacity: 0.4, fill: solid(C.modified) });
return;
}
const look = fileLook(item, scheme);
fileIcon(line, item.lang, look.entryIcon);
text(line, item.name, { color: look.color || C.fg, weight: look.weight });
spacer(line);
if (look.badge) text(line, look.badge.text, { size: 12, color: look.badge.color });
}
function sidebar(parent, scheme) {
const side = col(parent, "Sidebar", { width: 400, gap: 0, fill: C.side, stroke: C.border });
const header = row(side, "Header", { height: 35, padding: [0, 12, 0, 20], justify: "space_between" });
text(header, "代码阅读", { size: 11, color: C.fg });
const actions = row(header, "Actions", { width: "fit_content", gap: 12 });
for (const name of ["git-pull-request", "refresh-cw", "copy-minus"]) icon(actions, name, C.fg, 15);
const list = col(side, "Tree", { gap: 0, padding: [0, 0, 10, 0] });
for (const item of TREE) treeRow(list, item, scheme);
}
// 标签栏只有方案 1 跟着变:那是文件装饰,标签页也会带;另两种只改代码阅读自己的树和预览。
function tab(bar, item, scheme) {
const look = fileLook(item, scheme === "badge" ? "badge" : "none");
const node = I(bar, {
type: "frame",
name: "Tab / " + item.name,
layout: "vertical",
width: "fit_content",
height: "fit_content",
gap: 0,
fill: solid(item.active ? C.editor : C.side),
});
I(node, { type: "rectangle", name: "Top", width: "fill_container", height: 1, fill: solid(item.active ? C.focus : C.side) });
const label = row(node, "Label", { width: "fit_content", height: 34, padding: [0, 0, 0, 10], gap: 0 });
fileIcon(label, item.lang, false);
text(label, item.name, { color: look.color || (item.active ? C.white : C.desc) });
if (look.badge) {
I(label, { type: "frame", name: "Gap", width: 6, height: 4 });
text(label, look.badge.text, { size: 12, color: look.badge.color });
}
const close = box(label, "Close", 32, 34);
if (item.active) icon(close, "x", C.fg, 15);
I(node, { type: "rectangle", name: "Bottom", width: "fill_container", height: 1, fill: solid(item.active ? C.editor : C.border) });
I(bar, { type: "rectangle", name: "Tab border", width: 1, height: 36, fill: solid(C.border) });
}
function tabBar(parent, scheme) {
const bar = row(parent, "Tabs", { gap: 0, align: "start", fill: C.side });
for (const item of TABS) tab(bar, item, scheme);
const rest = I(bar, {
type: "frame",
name: "Rest",
layout: "vertical",
width: "fill_container",
height: 36,
gap: 0,
justifyContent: "end",
});
I(rest, { type: "rectangle", name: "Bottom", width: "fill_container", height: 1, fill: solid(C.border) });
}
function prop(parent, label, value, color, mono) {
const line = row(parent, "Prop / " + label, { gap: 12, align: "start" });
const key = I(line, { type: "frame", name: "Key", width: 72, height: "fit_content", layout: "vertical" });
text(key, label, { size: 12, color: C.desc, wrap: true });
const val = I(line, { type: "frame", name: "Value", width: "fill_container", height: "fit_content", layout: "vertical" });
text(val, value, { size: 13, color, mono, wrap: true });
}
function nameLine(parent, scheme) {
const line = row(parent, "Prop / 名称", { gap: 12 });
const key = I(line, { type: "frame", name: "Key", width: 72, height: "fit_content" });
text(key, "名称", { size: 12, color: C.desc });
const value = row(line, "Value", { width: "fit_content", gap: 6 });
if (scheme === "bold") {
text(value, ENTRY_FN.name, { color: C.highlight, mono: true, weight: 700 });
return;
}
text(value, ENTRY_FN.name, { color: C.fn, mono: true, weight: 600 });
if (scheme === "badge") text(value, "▶", { size: 12, color: C.passed });
}
function tools(parent) {
const bar = row(parent, "Tools", { width: "fit_content", gap: 2 });
for (const name of ["sparkles", "pencil"]) {
const button = box(bar, name, 22, 22);
icon(button, name, C.desc, 14);
}
}
function fnCard(parent, scheme) {
const node = I(parent, {
type: "frame",
name: "函数",
layout: "horizontal",
width: "fill_container",
height: "fit_content",
gap: 10,
padding: [8, 10, 8, 8],
cornerRadius: 8,
alignItems: "start",
fill: solid(C.card),
stroke: { thickness: 1, fill: solid(C.fn) },
clipContent: false,
});
const fold = box(node, "Fold", 14, 20);
icon(fold, "chevron-down", C.desc, 14);
if (scheme === "icon") icon(node, "play", C.passed, 18);
else icon(node, "square-function", C.fn, 18);
const content = col(node, "Content", { gap: 4 });
nameLine(content, scheme);
prop(content, "说明", ENTRY_FN.summary, C.comment, false);
prop(content, "可见性", ENTRY_FN.visibility, C.desc, false);
prop(content, "返回", ENTRY_FN.returns, C.type, true);
tools(node);
}
function editor(parent, scheme) {
const pane = col(parent, "Editor", { width: 400, gap: 0, fill: C.editor, stroke: C.border });
tabBar(pane, scheme);
const preview = col(pane, "Preview", { gap: 0, padding: [14, 16, 18, 16] });
fnCard(preview, scheme);
}
function header(parent, scheme) {
const head = col(parent, "Head / " + scheme.title, { width: 400, gap: 6 });
text(head, scheme.title, { size: 15, weight: 600, color: C.fgStrong });
text(head, scheme.sentence, { size: 13, color: C.desc, wrap: true });
}
function band(parent, name) {
return I(parent, {
type: "frame",
name,
layout: "horizontal",
width: "fit_content",
height: "fit_content",
gap: 40,
alignItems: "start",
clipContent: false,
});
}
const board = I(null, {
type: "frame",
name: "Prototype / 入口标记 · 三种样式",
x: 0,
y: 0,
width: "fit_content",
height: "fit_content",
layout: "vertical",
gap: 16,
padding: 40,
fill: solid(C.board),
clipContent: false,
});
const heads = band(board, "列头");
for (const scheme of SCHEMES) header(heads, scheme);
const sides = band(board, "侧栏");
for (const scheme of SCHEMES) sidebar(sides, scheme.key);
const editors = band(board, "编辑器");
for (const scheme of SCHEMES) editor(editors, scheme.key);
File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB