",
+ "
",
+ " {platform === \"macos\" && }",
+ "
",
+ "
",
+ " {center}",
+ "
",
+ " {platform === \"windows\" &&
}",
+ "
",
+ ");",
+].join("\n");
+
+/**
+ * React / 现状 — File=DesktopTitlebar:现在的样子,React 组件当普通函数:输入参数一格塞整个解构、类型一格塞整个类型字面量,
+ * 输出参数「未标注」;两个钩子、一个派生值是普通变量块,返回的 JSX 是一整段译文。
+ */
+function currentTitlebar(ctx) {
+ const node = frame(ctx, "File=DesktopTitlebar", { component: true, width: TREE_W, gap: 8 });
+ const { content } = cardShell(ctx, "Block / L12", BLOCK_KINDS.function, { parent: node, kids: true });
+ propRow(ctx, content, "名称", [{ text: TITLEBAR_CARD.name, color: COLORS.tokenFunction }]);
+ propRow(ctx, content, "说明", [{ text: TITLEBAR_CARD.doc, color: COLORS.comment }]);
+ propRow(ctx, content, "访问级别", [{ text: "公共", color: COLORS.tokenControl }]);
+ propRow(ctx, content, "异步", yesNo(false));
+ propRow(ctx, content, "输入参数", countSegments(["{ platform }"]), { twisty: true });
+ paramRows(ctx, content, [{ name: "{ platform }", type: '{ platform: DesktopShell["platform"] }', value: "" }], "typescript");
+ propRow(ctx, content, "输出参数", [{ text: "未标注", color: COLORS.desc }]);
+ const { list } = levelList(ctx, node, "Children / L12");
+ for (const [line, name, value] of TITLEBAR_BODY) {
+ const { content: body } = cardShell(ctx, `Block / L${line}`, BLOCK_KINDS.variable, { parent: list });
+ propRow(ctx, body, "名称", [{ text: name, color: COLORS.tokenVariable }]);
+ propRow(ctx, body, "初始值", tsSegments(value));
+ }
+ const { content: returned } = cardShell(ctx, "Block / L17", BLOCK_KINDS.return, { parent: list });
+ richText(ctx, returned, "Plain", tsSegments(TITLEBAR_JSX));
+ return node;
+}
+
+/** Explorer / ReactSidebar — File=titlebar:侧栏,luffy 的目录展开到两个示例文件,DesktopTitlebar.tsx 选中;行和主画板同一套。 */
+function reactSidebar(ctx, lib) {
+ const node = frame(ctx, "File=titlebar", { component: true, width: SIDEBAR_W, height: MAIN_H, fill: COLORS.side });
+ node.clipsContent = true;
+ node.overflowDirection = "VERTICAL";
+ const header = lib.explorerHeader.get("default").createInstance();
+ header.name = "Header";
+ append(node, header, true);
+ rootRow(ctx, node, REACT_ROOT, 0);
+ treeRows(ctx, node, lib, REACT_TREE, "titlebar");
+ return node;
+}
+
+/** Reader / FilterBar — File=titlebar:预览顶部的筛选栏,这个文件里出现过的属性各一个下拉框,右边块数。 */
+function reactFilterBar(ctx, lib) {
+ const node = frame(ctx, "File=titlebar", { component: true, dir: "HORIZONTAL", width: TREE_W, height: FILTER_BAR_H, gap: 8, align: "CENTER" });
+ filterLabel(ctx, node);
+ for (const facet of ["种类", "访问级别", "仅类型"]) {
+ const chip = append(node, lib.filterChip.get("default").createInstance());
+ chip.name = `Chip / ${facet}`;
+ setText(chip, "Text", facet);
+ }
+ append(node, frame(ctx, "Spacer", { width: 10, height: 1 }), true);
+ text(ctx, node, "Summary", `共 ${TITLEBAR_IMPORTS.length + 1} 块`, { hug: true, size: 12, lineHeight: 16, color: COLORS.desc });
+ return node;
+}
+
+/** Prototype / React 组件 · 简单:编辑器全貌,侧栏 DesktopTitlebar.tsx 选中,预览里五条导入后面是它的 React 组件卡片。 */
+function reactWindow(ctx, page, lib) {
+ const name = "React 组件 · 简单";
+ return timed(`Prototype / ${name}`, () => {
+ const { screen, editor, side } = windowShell(ctx, page, lib, name, { mode: "preview", file: REACT_FILES.titlebar, root: REACT_ROOT, crumbs: "titlebar",
+ sidebar: (main) => {
+ const pane = lib.reactSidebar.get("titlebar").createInstance();
+ pane.name = "Sidebar";
+ append(main, pane);
+ pane.layoutSizingVertical = "FILL";
+ return pane;
+ } });
+ const row = filled(ctx, editor, "Filter row", { align: "CENTER", px: VIEW_PAD, fill: COLORS.editor });
+ row.strokes = paint(COLORS.border);
+ row.strokeBottomWeight = 1;
+ row.strokeTopWeight = row.strokeLeftWeight = row.strokeRightWeight = 0;
+ const bar = lib.reactFilter.get("titlebar").createInstance();
+ bar.name = "Filter bar";
+ append(row, bar);
+ const view = filled(ctx, editor, "Preview view", { align: "CENTER", px: VIEW_PAD });
+ view.layoutSizingVertical = "FILL";
+ view.clipsContent = true;
+ view.overflowDirection = "VERTICAL";
+ const tree = append(view, frame(ctx, `Block tree / ${REACT_FILES.titlebar.name}`, { width: TREE_W, py: 20, fill: COLORS.editor }));
+ const { list } = levelList(ctx, tree, "Level 0");
+ const blocks = [...TITLEBAR_IMPORTS.map(([line]) => [line, lib.titlebarBlocks.get(line)]), [TITLEBAR_CARD.line, lib.reactCards.get(TITLEBAR_CARD.name)]];
+ for (const [line, component] of blocks) {
+ const instance = component.createInstance();
+ instance.name = `Block / L${line}`;
+ append(list, instance, true);
+ }
+ return { screen, sidebar: side, view, tree };
+ });
+}
+
+/** 只放卡片的画板(和主画板的库规则画板一样宽)。 */
+function cardBoard(ctx, page, name, components) {
+ const board = frame(ctx, name, { width: TREE_W + 2 * VIEW_PAD, pad: VIEW_PAD, gap: 8, fill: COLORS.editor });
+ page.appendChild(board);
+ for (const [blockName, component] of components) {
+ const instance = component.createInstance();
+ instance.name = blockName;
+ append(board, instance, true);
+ }
+ return board;
+}
+
+// 说明板上的例子,都照抄 luffy 的真实写法:
+// ShareButton(components/share/ShareButton.tsx)的属性里有回调;StepLabel(referral/components/poster/PosterDialog.tsx)的属性里有 children
+const SHARE_PROPS = { rows: [
+ { name: "open", type: "boolean", required: true },
+ { name: "onOpenChange", type: "(next: boolean) => void", required: true, role: "callback" },
+ { name: "deployUrl", type: "string | null", required: true, doc: "已发布的持久 URL; null = 尚未发布 (点击后由父组件驱动发布)。" },
+ { name: "sessionId", type: "string", required: true },
+ { name: "title", type: "string", required: true },
+] };
+const STEP_LABEL_PROPS = { rows: [{ name: "n", type: "number", required: true }, { name: "children", type: "ReactNode", required: true, role: "children" }] };
+// 钩子 → 分区:[写法, 放进, 怎么画]
+const REACT_HOOK_SECTIONS = [
+ ["[f|useState] / [f|useReducer]", "状态", "名称 / 初始值 / 改它的"],
+ ["[f|useRef]", "引用", "名称 / 初始值 / 用在"],
+ ["[f|useContext]、自定义钩子 [f|useXxx]", "读取的数据", "变量 / 来自 / 意思,调用读成中文:[f|useDesktopShell](([v|s]) => [v|s].[v|title]) → 从 desktop store 读 [v|title]"],
+ ["组件体里的常量、[f|useMemo] / [f|useCallback]", "派生值", "名称 / 怎么算 / 什么时候算,用中文读出来;useMemo / useCallback 写依赖"],
+ ["[f|useEffect] / [f|useLayoutEffect]", "副作用", "每个一张子卡片:时机 / 做什么 / 清理"],
+ ["组件里定义的函数", "事件处理", "名称 / 绑定在 / 做什么,点名称展开子卡片"],
+ ["[k|return] 的 JSX", "渲染", "缩进的结构大纲,写法见下"],
+];
+// 副作用的时机按依赖数组读
+const EFFECT_TIMINGS = [["[[v|doc], [v|headings]]", "[v|doc]、[v|headings] [k|变了时]"], ["[]", "[k|只在挂载时]"], ["[d|不写]", "[k|每次渲染后]"]];
+// 渲染大纲的写法各一段:DesktopTitlebar 的一行原生标签和组件、DeleteAppDialog(components/apps)的三元和列表、
+// DesktopSettingsDialog(components/desktop)的提前返回、AppVaultSummary(app/(main)/chat/code)的片段
+const OUTLINE_TAGS = [
+ { el: "span", cls: "max-w-[40vw] truncate font-medium text-foreground", text: "[d|{][v|center][d|}]" },
+ { el: "WindowControls", comp: true },
+];
+const OUTLINE_WHEN = [{ when: '[v|platform] [k|是] [q|"windows"]', el: "WindowControls", comp: true }];
+const OUTLINE_TERNARY = [{ el: "Button", comp: true, attrs: '[v|variant]=[q|"destructive"] [v|onClick]=[d|() => ][v|remove].[f|mutate]() [v|disabled]=[s|非] [v|ready] [s|或者] [v|pending]', kids: [
+ { ternary: "[v|pending]", yes: [{ text: '[q|"删除中…"]' }], no: [{ text: '[q|"确认删除并清空数据"]' }] },
+] }];
+const OUTLINE_LIST = [{ el: "ul", cls: "flex flex-col gap-2 text-[13px] text-foreground", kids: [
+ { each: "[v|losses]", item: "{ [v|icon]: [r|Icon], [v|text] }", key: "[v|text]", kids: [
+ { el: "li", cls: "flex items-start gap-2", kids: [
+ { el: "Icon", comp: true, cls: "mt-0.5 shrink-0 text-destructive", attrs: "[v|size]=[n|14]" },
+ { el: "span", text: "[d|{][v|text][d|}]" },
+ ] },
+ ] },
+] }];
+const OUTLINE_RETURNS = [
+ { when: "[v|section] [k|没有值]", none: true },
+ { when: null, nodes: [{ el: "Dialog", comp: true, attrs: "[v|open] [v|onOpenChange]=[d|(next) => ][s|非] [v|next] [s|并且] [f|closeSettings]()", kids: [
+ { el: "DialogContent", comp: true, cls: "h-[560px] overflow-hidden p-0 sm:max-w-[880px]", kids: [{ more: "Tabs:左边分页列表,右边 4 个分页" }] },
+ ] }] },
+];
+const OUTLINE_FRAGMENT = [{ frag: true, kids: [
+ { el: "SettingHeader", comp: true, attrs: '[v|compact] [v|align]=[q|"center"] [v|title]=[q|"密钥与变量"] [v|right]=[r|