diff --git a/apps/tolaria/README.md b/apps/tolaria/README.md index 652a5dd..ec51e59 100644 --- a/apps/tolaria/README.md +++ b/apps/tolaria/README.md @@ -12,7 +12,8 @@ ## 当前界面迭代 · 冰湖视觉层 -`patches/` 中的补丁按编号顺序应用: +`patches/` 中的补丁以系列入口恢复。`0003` 已包含 `0001`、`0002`, +因此不能将 `0001`、`0002`、`0003` 连续重复应用: 1. `0001-feat-add-Guanghu-ice-lake-visual-layer.patch` - 为浅色与深色模式增加冰湖光层、网格层和语义颜色; @@ -28,20 +29,35 @@ - 收敛为轻量、可切换的光湖配套主题; - 修正 macOS 顶栏间距、深色应用图标和桌面窗口拖拽; - 增强本地 smoke 测试在大规模模拟笔记列表中的可重复性。 +5. `0005-Add-portable-callout-page-blocks.patch` + - 在 BlockNote 编辑器中增加 info / success / warning / danger 彩色提示块; + - 以可携带 Markdown callout 语法保存,重新打开不丢失。 +6. `0006-fix-make-Guanghu-home-and-themes-visible.patch` + - 首次启动默认显示 HoloLake Era 世界首页; + - 使湖光浅色与湖心深色同步实际亮/暗模式,不再只改按钮文字。 +7. `0007-feat-complete-portable-page-block-set.patch` + - 恢复 toggle heading / toggle list 折叠块入口; + - 增加批注块、关系卡和路径包,继续复用可携带 Markdown callout 协议; + - 保留现有代码块、引用、Mermaid、表格、白板与媒体块。 ## 恢复与验证 ```bash git clone https://guanghubingshuo.com/code/bingshuo/guanghu.git cd guanghu -git am /path/to/0001-feat-add-Guanghu-ice-lake-visual-layer.patch -git am /path/to/0002-test-align-updater-coverage-with-Guanghu-policy.patch git am /path/to/0003-20260713-hololake-era-shell-prototype-series.patch git am /path/to/0004-20260715-tolaria-lightweight-themes-drag-fix.patch +git am /path/to/0005-Add-portable-callout-page-blocks.patch +git am /path/to/0006-fix-make-Guanghu-home-and-themes-visible.patch +git am /path/to/0007-feat-complete-portable-page-block-set.patch pnpm build pnpm lint ``` +`0001`、`0002` 保留为早期两补丁拆分档案;只有在不使用 `0003` +原型系列时才单独应用。上述 `0003 → 0007` 顺序已于 2026-07-17 +在服务器最新 `bingshuo/guanghu@514ab19` 的新克隆上完整复现。 + ## 研发规则 - 新的页面、渲染模块和交互协议先在本目录登记,再以独立补丁落到可编译基线; diff --git a/apps/tolaria/patches/0005-Add-portable-callout-page-blocks.patch b/apps/tolaria/patches/0005-Add-portable-callout-page-blocks.patch new file mode 100644 index 0000000..c3f5fda --- /dev/null +++ b/apps/tolaria/patches/0005-Add-portable-callout-page-blocks.patch @@ -0,0 +1,635 @@ +From edf46c67334ed8bd5adbb0be719ab0dd51ffa266 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> +Date: Thu, 16 Jul 2026 22:30:25 +0800 +Subject: [PATCH 5/7] Add portable callout page blocks + +--- + src/components/Editor.css | 62 +++++++++++ + src/components/editorSchema.tsx | 46 ++++++++ + .../tolariaEditorFormatting.test.tsx | 58 ++++++++++ + .../tolariaEditorFormattingConfig.ts | 101 +++++++++++++++++- + src/utils/blockNoteDirectMarkdown.test.ts | 12 +++ + src/utils/blockNoteDirectMarkdown.ts | 12 +++ + src/utils/calloutMarkdown.ts | 70 ++++++++++++ + src/utils/editorDurableMarkdown.test.ts | 17 +++ + src/utils/editorDurableMarkdown.ts | 3 +- + 9 files changed, 379 insertions(+), 2 deletions(-) + create mode 100644 src/utils/calloutMarkdown.ts + +diff --git a/src/components/Editor.css b/src/components/Editor.css +index 24f0738..5db3fe1 100644 +--- a/src/components/Editor.css ++++ b/src/components/Editor.css +@@ -80,6 +80,68 @@ + line-height: 1.65; + } + ++.tolaria-callout { ++ --tolaria-callout-accent: #2563eb; ++ --tolaria-callout-bg: color-mix(in srgb, #2563eb 9%, var(--bg-primary)); ++ display: grid; ++ grid-template-columns: 4px minmax(0, 1fr); ++ gap: 12px; ++ width: 100%; ++ margin: 0.45rem 0; ++ padding: 11px 13px 11px 0; ++ border: 1px solid color-mix(in srgb, var(--tolaria-callout-accent) 28%, var(--border)); ++ border-radius: 8px; ++ background: var(--tolaria-callout-bg); ++} ++ ++.tolaria-callout[data-kind="success"] { ++ --tolaria-callout-accent: #168a48; ++ --tolaria-callout-bg: color-mix(in srgb, #168a48 10%, var(--bg-primary)); ++} ++ ++.tolaria-callout[data-kind="warning"] { ++ --tolaria-callout-accent: #b7791f; ++ --tolaria-callout-bg: color-mix(in srgb, #b7791f 13%, var(--bg-primary)); ++} ++ ++.tolaria-callout[data-kind="danger"] { ++ --tolaria-callout-accent: #dc2626; ++ --tolaria-callout-bg: color-mix(in srgb, #dc2626 10%, var(--bg-primary)); ++} ++ ++.tolaria-callout__marker { ++ width: 4px; ++ min-height: 100%; ++ border-radius: 999px; ++ background: var(--tolaria-callout-accent); ++} ++ ++.tolaria-callout__body { ++ display: grid; ++ gap: 4px; ++ min-width: 0; ++} ++ ++.tolaria-callout__label { ++ color: var(--tolaria-callout-accent); ++ font-size: 11px; ++ font-weight: 700; ++ line-height: 1.2; ++ text-transform: uppercase; ++} ++ ++.tolaria-callout__content { ++ min-width: 0; ++ color: var(--text-primary); ++ font-size: 14px; ++ line-height: 1.6; ++} ++ ++.tolaria-callout__content:empty::before { ++ color: var(--text-muted); ++ content: 'Write a note'; ++} ++ + /* Breadcrumb bar: border can still react to the data attribute, but the + breadcrumb filename/title stays visible at all times. */ + .breadcrumb-bar { +diff --git a/src/components/editorSchema.tsx b/src/components/editorSchema.tsx +index 2984eff..7d62518 100644 +--- a/src/components/editorSchema.tsx ++++ b/src/components/editorSchema.tsx +@@ -25,6 +25,11 @@ import { MERMAID_BLOCK_TYPE, mermaidFenceSource } from '../utils/mermaidMarkdown + import { TLDRAW_BLOCK_TYPE, TLDRAW_DEFAULT_HEIGHT } from '../utils/tldrawMarkdown' + import { MARKDOWN_HIGHLIGHT_STYLE } from '../utils/markdownHighlightMarkdown' + import { GUANGHU_PORTAL_BLOCK_TYPE } from '../utils/guanghuPortalMarkdown' ++import { ++ calloutKind, ++ TOLARIA_CALLOUT_BLOCK_TYPE, ++ type TolariaCalloutKind, ++} from '../utils/calloutMarkdown' + import type { VaultEntry } from '../types' + import { createTolariaCodeBlockOptions } from './codeBlockOptions' + import { NoteTitleIcon } from './NoteTitleIcon' +@@ -350,6 +355,45 @@ const GuanghuPortalBlock = createReactBlockSpec( + }, + ) + ++const CALLOUT_LABELS: Record = { ++ danger: 'Danger', ++ info: 'Info', ++ success: 'Success', ++ warning: 'Warning', ++} ++ ++function TolariaCalloutRenderer({ ++ block, ++ contentRef, ++}: { ++ block: { props: { kind?: string } } ++ contentRef: (node: HTMLElement | null) => void ++}) { ++ const kind = calloutKind(block.props.kind) ++ return ( ++ ++ ) ++} ++ ++const TolariaCalloutBlock = createReactBlockSpec( ++ { ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ propSchema: { ++ kind: { default: 'info' }, ++ }, ++ content: 'inline', ++ }, ++ { ++ render: TolariaCalloutRenderer, ++ }, ++) ++ + export function mediaBlockPropsForPreviewRuntime( + props: T, + externalMediaPreview: boolean, +@@ -450,6 +494,7 @@ const codeBlock = createCodeBlockSpec(createTolariaCodeBlockOptions()) + const audioBlock = AudioBlockSpec() + const mathBlock = MathBlock() + const guanghuPortalBlock = GuanghuPortalBlock() ++const tolariaCalloutBlock = TolariaCalloutBlock() + const mermaidBlock = MermaidBlock() + const tldrawBlock = TldrawBlock() + const videoBlock = VideoBlockSpec() +@@ -485,6 +530,7 @@ export const schema = BlockNoteSchema.create({ + blockSpecs: { + audio: audioBlock, + guanghuPortalBlock, ++ [TOLARIA_CALLOUT_BLOCK_TYPE]: tolariaCalloutBlock, + mathBlock, + mermaidBlock, + tldrawBlock, +diff --git a/src/components/tolariaEditorFormatting.test.tsx b/src/components/tolariaEditorFormatting.test.tsx +index 5c5143b..7bd06c2 100644 +--- a/src/components/tolariaEditorFormatting.test.tsx ++++ b/src/components/tolariaEditorFormatting.test.tsx +@@ -7,7 +7,9 @@ vi.mock('../lib/telemetry', () => ({ + })) + + import { ++ addItemsAfterKey, + addItemsToMediaGroup, ++ createCalloutSlashMenuItems, + createMathSlashMenuItem, + filterTolariaFormattingToolbarItems, + filterTolariaSlashMenuItems, +@@ -18,6 +20,7 @@ import { + import { trackEvent } from '../lib/telemetry' + import { MATH_BLOCK_TYPE } from '../utils/mathMarkdown' + import { mermaidFenceSource } from '../utils/mermaidMarkdown' ++import { TOLARIA_CALLOUT_BLOCK_TYPE } from '../utils/calloutMarkdown' + + describe('tolariaEditorFormatting', () => { + it('keeps the markdown-safe toolbar controls and block type select', () => { +@@ -73,6 +76,7 @@ describe('tolariaEditorFormatting', () => { + { key: 'heading_4', title: 'Heading 4', onItemClick: () => {} }, + { key: 'bullet_list', title: 'Bullet List', subtext: 'Default list copy', onItemClick: () => {} }, + { key: 'code_block', title: 'Code Block', subtext: 'Default code copy', onItemClick: () => {} }, ++ { key: 'tolaria_callout_info', title: 'Info callout', onItemClick: () => {} }, + { key: 'heading_5', title: 'Heading 5', onItemClick: () => {} }, + { key: 'heading_6', title: 'Heading 6', onItemClick: () => {} }, + ] satisfies TolariaSlashMenuTestItem[]) +@@ -82,12 +86,14 @@ describe('tolariaEditorFormatting', () => { + 'heading_4', + 'bullet_list', + 'code_block', ++ 'tolaria_callout_info', + ]) + expect(items.map((item) => item.subtext)).toEqual([ + undefined, + undefined, + undefined, + undefined, ++ undefined, + ]) + }) + +@@ -224,6 +230,58 @@ describe('tolariaEditorFormatting', () => { + ]) + }) + ++ it('places callout commands after quote blocks', () => { ++ type TolariaSlashMenuTestItem = { ++ key: string ++ title: string ++ group: string ++ onItemClick: () => void ++ } ++ ++ const items = addItemsAfterKey([ ++ { key: 'paragraph', title: 'Paragraph', group: 'Basic blocks', onItemClick: () => {} }, ++ { key: 'quote', title: 'Quote', group: 'Basic blocks', onItemClick: () => {} }, ++ { key: 'code_block', title: 'Code Block', group: 'Basic blocks', onItemClick: () => {} }, ++ ] satisfies TolariaSlashMenuTestItem[], 'quote', [ ++ { key: 'tolaria_callout_info', title: 'Info callout', group: 'Basic blocks', onItemClick: () => {} }, ++ ]) ++ ++ expect(items.map(item => item.key)).toEqual([ ++ 'paragraph', ++ 'quote', ++ 'tolaria_callout_info', ++ 'code_block', ++ ]) ++ }) ++ ++ it('keeps callout slash commands searchable and inserts an editable callout block', async () => { ++ const block = { id: 'active-block' } ++ const editor = { ++ getTextCursorPosition: () => ({ block }), ++ replaceBlocks: () => {}, ++ } ++ const replaceBlocks = vi.spyOn(editor, 'replaceBlocks') ++ const items = createCalloutSlashMenuItems(editor as never) ++ const warningItem = items.find(item => item.key === 'tolaria_callout_warning') ++ ++ expect(warningItem).toEqual(expect.objectContaining({ ++ title: 'Warning callout', ++ aliases: expect.arrayContaining(['warning', '警告']), ++ })) ++ ++ warningItem?.onItemClick() ++ ++ expect(replaceBlocks).toHaveBeenCalledWith([block], [{ ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ props: { kind: 'warning' }, ++ content: [{ ++ type: 'text', ++ text: 'Warning callout', ++ styles: {}, ++ }], ++ }]) ++ }) ++ + it('creates a math slash command with a default display equation', () => { + const block = { id: 'active-block' } + const editor = { +diff --git a/src/components/tolariaEditorFormattingConfig.ts b/src/components/tolariaEditorFormattingConfig.ts +index b55dede..614cbf5 100644 +--- a/src/components/tolariaEditorFormattingConfig.ts ++++ b/src/components/tolariaEditorFormattingConfig.ts +@@ -6,9 +6,11 @@ import { + import { createElement, type ReactElement } from 'react' + import { + CodeBlock, ++ CheckCircle, + File, + FlowArrow, + ImageSquare, ++ Info, + ListBullets, + ListChecks, + ListNumbers, +@@ -26,6 +28,8 @@ import { + TextHFour, + TextHFive, + TextHSix, ++ Warning, ++ XCircle, + Video, + type Icon as PhosphorIcon, + } from '@phosphor-icons/react' +@@ -38,6 +42,10 @@ import { + import { MATH_BLOCK_TYPE } from '../utils/mathMarkdown' + import { MERMAID_BLOCK_TYPE, mermaidFenceSource } from '../utils/mermaidMarkdown' + import { TLDRAW_BLOCK_TYPE, TLDRAW_DEFAULT_HEIGHT } from '../utils/tldrawMarkdown' ++import { ++ TOLARIA_CALLOUT_BLOCK_TYPE, ++ type TolariaCalloutKind, ++} from '../utils/calloutMarkdown' + + type TolariaSlashMenuItem = DefaultReactSuggestionItem & { key: string } + type TolariaBlockTypeSelectItem = RichEditorBlockTypeDefinition & { +@@ -55,6 +63,12 @@ type BlockSlashMenuItemConfig = { + title: string + type: string + } ++type CalloutSlashMenuItemConfig = { ++ aliases: string[] ++ key: string ++ kind: TolariaCalloutKind ++ title: string ++} + type TolariaSlashMenuLabels = { + mathTitle: string + } +@@ -115,6 +129,10 @@ const TOLARIA_SLASH_MENU_ICONS: Partial> = { + numbered_list: ListNumbers, + paragraph: Paragraph, + quote: Quotes, ++ tolaria_callout_danger: XCircle, ++ tolaria_callout_info: Info, ++ tolaria_callout_success: CheckCircle, ++ tolaria_callout_warning: Warning, + table: Table, + toggle_heading: TextHOne, + toggle_heading_2: TextHTwo, +@@ -164,6 +182,64 @@ function createMermaidSlashMenuItem( + }) + } + ++function createCalloutSlashMenuItem( ++ editor: Parameters[0], ++ config: CalloutSlashMenuItemConfig, ++): TolariaSlashMenuItem { ++ const blockEditor = editor as unknown as SlashInsertEditor ++ ++ return { ++ key: config.key, ++ title: config.title, ++ aliases: config.aliases, ++ group: 'Basic blocks', ++ onItemClick: () => { ++ const block = blockEditor.getTextCursorPosition().block ++ blockEditor.replaceBlocks([block], [{ ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ props: { kind: config.kind }, ++ content: [{ ++ type: 'text', ++ text: config.title, ++ styles: {}, ++ }], ++ }]) ++ trackEvent('editor_callout_slash_command_used', { kind: config.kind }) ++ }, ++ } as TolariaSlashMenuItem ++} ++ ++export function createCalloutSlashMenuItems( ++ editor: Parameters[0], ++): TolariaSlashMenuItem[] { ++ return [ ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_info', ++ title: 'Info callout', ++ aliases: ['callout', 'note', 'info', 'tip', '提示', '高亮'], ++ kind: 'info', ++ }), ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_success', ++ title: 'Success callout', ++ aliases: ['callout', 'success', 'done', 'ok', '完成', '成功'], ++ kind: 'success', ++ }), ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_warning', ++ title: 'Warning callout', ++ aliases: ['callout', 'warning', 'alert', 'todo', '警告', '注意'], ++ kind: 'warning', ++ }), ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_danger', ++ title: 'Danger callout', ++ aliases: ['callout', 'danger', 'risk', 'error', '风险', '危险'], ++ kind: 'danger', ++ }), ++ ] ++} ++ + export function createMathSlashMenuItem( + editor: Parameters[0], + labels: TolariaSlashMenuLabels = { mathTitle: 'Math' }, +@@ -218,6 +294,23 @@ export function addItemsToMediaGroup( + return nextItems + } + ++export function addItemsAfterKey( ++ items: TolariaSlashMenuItem[], ++ insertAfterKey: string, ++ insertedItems: TolariaSlashMenuItem[], ++): TolariaSlashMenuItem[] { ++ const nextItems = [...items] ++ const insertIndex = nextItems.findIndex((item) => item.key === insertAfterKey) ++ ++ if (insertIndex === -1) { ++ nextItems.unshift(...insertedItems) ++ return nextItems ++ } ++ ++ nextItems.splice(insertIndex + 1, 0, ...insertedItems) ++ return nextItems ++} ++ + function createTolariaSlashMenuIcon(Icon: PhosphorIcon) { + return createElement( + 'span', +@@ -273,8 +366,14 @@ export function getTolariaSlashMenuItems( + query: string, + labels?: TolariaSlashMenuLabels, + ) { ++ const defaultItems = getDefaultReactSlashMenuItems(editor) as TolariaSlashMenuItem[] ++ const itemsWithCallouts = addItemsAfterKey( ++ defaultItems, ++ 'quote', ++ createCalloutSlashMenuItems(editor), ++ ) + const items = addItemsToMediaGroup( +- getDefaultReactSlashMenuItems(editor) as TolariaSlashMenuItem[], ++ itemsWithCallouts, + [ + createMermaidSlashMenuItem(editor), + createMathSlashMenuItem(editor, labels), +diff --git a/src/utils/blockNoteDirectMarkdown.test.ts b/src/utils/blockNoteDirectMarkdown.test.ts +index 524b29c..cabba60 100644 +--- a/src/utils/blockNoteDirectMarkdown.test.ts ++++ b/src/utils/blockNoteDirectMarkdown.test.ts +@@ -5,6 +5,7 @@ import { + serializeBlockNoteMarkdown, + type DirectMarkdownCapableSerializer, + } from './blockNoteDirectMarkdown' ++import { TOLARIA_CALLOUT_BLOCK_TYPE } from './calloutMarkdown' + import { serializeRichEditorBodyToMarkdown } from './richEditorMarkdown' + + function makeEditor(document: unknown[]): DirectMarkdownCapableSerializer & { document: unknown[] } { +@@ -117,6 +118,17 @@ describe('BlockNote direct Markdown serialization', () => { + ].join('\n')) + }) + ++ it('serializes Tolaria callout blocks as portable Markdown callouts', () => { ++ expect(blocksToMarkdownDirect([ ++ { ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ props: { kind: 'success' }, ++ content: [{ type: 'text', text: 'Package is ready', styles: {} }], ++ children: [], ++ }, ++ ]).markdown).toBe('> [!success] Package is ready') ++ }) ++ + it('caches unchanged block objects across rich-editor body serialization', () => { + const block = { + type: 'paragraph', +diff --git a/src/utils/blockNoteDirectMarkdown.ts b/src/utils/blockNoteDirectMarkdown.ts +index c075831..2ac99a0 100644 +--- a/src/utils/blockNoteDirectMarkdown.ts ++++ b/src/utils/blockNoteDirectMarkdown.ts +@@ -1,3 +1,5 @@ ++import { calloutKind, TOLARIA_CALLOUT_BLOCK_TYPE } from './calloutMarkdown' ++ + interface TextStyles { + [style: string]: string | boolean | undefined + } +@@ -228,6 +230,15 @@ function quoteMarkdown(block: BlockLike): string { + return text.split('\n').map(line => `> ${line}`).join('\n') + } + ++function calloutMarkdown(block: BlockLike): string { ++ const kind = calloutKind(block.props?.kind) ++ const text = serializeInlineContent(contentArray(block.content)) || 'Write a note' ++ return `[!${kind}] ${text}` ++ .split('\n') ++ .map(line => `> ${line}`) ++ .join('\n') ++} ++ + function tableCellMarkdown(cell: TableCellValue): string { + const text = typeof cell === 'string' + ? cell +@@ -273,6 +284,7 @@ const BLOCK_MARKDOWN_HANDLERS: Record = { + heading: headingMarkdown, + quote: quoteMarkdown, + table: tableMarkdown, ++ [TOLARIA_CALLOUT_BLOCK_TYPE]: calloutMarkdown, + } + + function blockMarkdownWithoutChildren(block: BlockLike, context: SerializeContext): string | null { +diff --git a/src/utils/calloutMarkdown.ts b/src/utils/calloutMarkdown.ts +new file mode 100644 +index 0000000..b6881b6 +--- /dev/null ++++ b/src/utils/calloutMarkdown.ts +@@ -0,0 +1,70 @@ ++import type { BlockLike } from './durableMarkdownBlocks' ++ ++export const TOLARIA_CALLOUT_BLOCK_TYPE = 'tolariaCallout' ++ ++export const TOLARIA_CALLOUT_KINDS = ['info', 'success', 'warning', 'danger'] as const ++ ++export type TolariaCalloutKind = typeof TOLARIA_CALLOUT_KINDS[number] ++ ++const CALLOUT_MARKER_RE = /^\[!(info|success|warning|danger)\]\s*(.*)$/iu ++ ++function isCalloutKind(value: unknown): value is TolariaCalloutKind { ++ return typeof value === 'string' && TOLARIA_CALLOUT_KINDS.includes(value as TolariaCalloutKind) ++} ++ ++function readTextOnlyContent(content: BlockLike['content']): string | null { ++ if (!Array.isArray(content) || content.length === 0) return null ++ ++ let text = '' ++ for (const item of content) { ++ if (item.type !== 'text' || typeof item.text !== 'string') return null ++ text += item.text ++ } ++ return text ++} ++ ++function calloutMatchFromQuote(block: BlockLike) { ++ if (block.type !== 'quote') return null ++ ++ const text = readTextOnlyContent(block.content) ++ if (text === null) return null ++ ++ const match = CALLOUT_MARKER_RE.exec(text) ++ if (!match) return null ++ ++ const kind = match[1]?.toLowerCase() ++ if (!isCalloutKind(kind)) return null ++ ++ return { ++ kind, ++ text: match[2] ?? '', ++ } ++} ++ ++export function injectTolariaCalloutBlocks(blocks: unknown[]): unknown[] { ++ return (blocks as BlockLike[]).map((block) => { ++ const match = calloutMatchFromQuote(block) ++ const children = Array.isArray(block.children) ++ ? injectTolariaCalloutBlocks(block.children) as BlockLike[] ++ : block.children ++ ++ if (!match) return { ...block, children } ++ ++ return { ++ ...block, ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ props: { ++ ...(block.props ?? {}), ++ kind: match.kind, ++ }, ++ content: match.text ++ ? [{ type: 'text', text: match.text, styles: {} }] ++ : [], ++ children, ++ } ++ }) ++} ++ ++export function calloutKind(value: unknown): TolariaCalloutKind { ++ return isCalloutKind(value) ? value : 'info' ++} +diff --git a/src/utils/editorDurableMarkdown.test.ts b/src/utils/editorDurableMarkdown.test.ts +index f5a230d..4e6679e 100644 +--- a/src/utils/editorDurableMarkdown.test.ts ++++ b/src/utils/editorDurableMarkdown.test.ts +@@ -6,6 +6,7 @@ import { + preProcessDurableEditorMarkdown, + serializeDurableEditorBlocks, + } from './editorDurableMarkdown' ++import { TOLARIA_CALLOUT_BLOCK_TYPE } from './calloutMarkdown' + import { MERMAID_BLOCK_TYPE } from './mermaidMarkdown' + import { TLDRAW_BLOCK_TYPE } from './tldrawMarkdown' + +@@ -70,4 +71,20 @@ describe('editor durable markdown blocks', () => { + }, + }) + }) ++ ++ it('restores Markdown callouts as Tolaria page blocks', () => { ++ const blocks = injectDurableEditorMarkdownBlocks([ ++ { ++ type: 'quote', ++ content: [{ type: 'text', text: '[!warning] Check signing before sharing.', styles: {} }], ++ children: [], ++ }, ++ ]) as Array<{ type: string; props?: Record; content?: Array<{ text?: string }> }> ++ ++ expect(blocks[0]).toMatchObject({ ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ props: { kind: 'warning' }, ++ content: [{ text: 'Check signing before sharing.' }], ++ }) ++ }) + }) +diff --git a/src/utils/editorDurableMarkdown.ts b/src/utils/editorDurableMarkdown.ts +index 6e6d30a..baa5670 100644 +--- a/src/utils/editorDurableMarkdown.ts ++++ b/src/utils/editorDurableMarkdown.ts +@@ -16,6 +16,7 @@ import { serializeMathAwareBlocks } from './mathMarkdown' + import { mermaidMarkdownCodec } from './mermaidMarkdown' + import { tldrawMarkdownCodec } from './tldrawMarkdown' + import { guanghuPortalMarkdownCodec } from './guanghuPortalMarkdown' ++import { injectTolariaCalloutBlocks } from './calloutMarkdown' + + const EDITOR_DURABLE_MARKDOWN_CODECS = [ + guanghuPortalMarkdownCodec, +@@ -36,7 +37,7 @@ export function injectDurableEditorMarkdownBlocks(blocks: unknown[]): unknown[] + blocks, + codecs: EDITOR_DURABLE_MARKDOWN_CODECS, + }) +- return injectFileAttachmentBlocks(withDurableBlocks) ++ return injectTolariaCalloutBlocks(injectFileAttachmentBlocks(withDurableBlocks)) + } + + export function serializeDurableEditorBlocks( +-- +2.50.1 (Apple Git-155) + diff --git a/apps/tolaria/patches/0006-fix-make-Guanghu-home-and-themes-visible.patch b/apps/tolaria/patches/0006-fix-make-Guanghu-home-and-themes-visible.patch new file mode 100644 index 0000000..19c7c7c --- /dev/null +++ b/apps/tolaria/patches/0006-fix-make-Guanghu-home-and-themes-visible.patch @@ -0,0 +1,134 @@ +From 9fc5e885742a48c2794a4b4fcd8ddae66501aa28 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> +Date: Fri, 17 Jul 2026 10:30:04 +0800 +Subject: [PATCH 6/7] fix: make Guanghu home and themes visible + +--- + src/App.tsx | 4 ++-- + src/components/HoloLakeHome.tsx | 1 + + src/hooks/useGuanghuTheme.test.ts | 30 ++++++++++++++++++++++++++++++ + src/hooks/useGuanghuTheme.ts | 12 +++++++++++- + src/lib/guanghuTheme.ts | 6 +++--- + 5 files changed, 47 insertions(+), 6 deletions(-) + create mode 100644 src/hooks/useGuanghuTheme.test.ts + +diff --git a/src/App.tsx b/src/App.tsx +index 2b4ef17..66c879d 100644 +--- a/src/App.tsx ++++ b/src/App.tsx +@@ -170,8 +170,7 @@ function App() { + + function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | null }) { + const aiWorkspaceWindow = false +- const [showHoloLakeHome, setShowHoloLakeHome] = useState(false) +- const { theme: guanghuTheme, cycleTheme } = useGuanghuTheme() ++ const [showHoloLakeHome, setShowHoloLakeHome] = useState(true) + const { dragRegionRef } = useDragRegion() + const [selection, setSelection] = useState(DEFAULT_SELECTION) + const [noteListFilter, setNoteListFilter] = useState('open') +@@ -426,6 +425,7 @@ function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | nu + settings, + settingsLoaded, + }) ++ const { theme: guanghuTheme, cycleTheme } = useGuanghuTheme() + const quickPromptTarget = lastAiWorkspaceTarget ?? aiAgentPreferences.defaultAiTarget + const quickPromptTargetReady = aiTargetReady(quickPromptTarget, aiAgentsStatus) + +diff --git a/src/components/HoloLakeHome.tsx b/src/components/HoloLakeHome.tsx +index 86b2891..49127ca 100644 +--- a/src/components/HoloLakeHome.tsx ++++ b/src/components/HoloLakeHome.tsx +@@ -4,6 +4,7 @@ type HoloLakeHomeProps = { + + const modules = [ + ['知识湖', '把代码仓库、文档与路径转译成可阅读、可追溯的知识页面。', '进入知识库'], ++ ['页面组件', '提示块、批注、关系卡、路径包、折叠块与高亮代码已接入笔记编辑器。', '在笔记中输入 /'], + ['协作空间', '人类与人格体在同一上下文中对话、确认、留存回执。', '筹备中'], + ['授权中心', '任何真实操作先形成可读计划,再由人类一次确认。', '筹备中'], + ] +diff --git a/src/hooks/useGuanghuTheme.test.ts b/src/hooks/useGuanghuTheme.test.ts +new file mode 100644 +index 0000000..8fa948a +--- /dev/null ++++ b/src/hooks/useGuanghuTheme.test.ts +@@ -0,0 +1,30 @@ ++import { act, renderHook } from '@testing-library/react' ++import { beforeEach, describe, expect, it } from 'vitest' ++import { GUANGHU_THEME_STORAGE_KEY } from '../lib/guanghuTheme' ++import { THEME_MODE_STORAGE_KEY } from '../lib/themeMode' ++import { useGuanghuTheme } from './useGuanghuTheme' ++ ++describe('useGuanghuTheme', () => { ++ beforeEach(() => { ++ window.localStorage.clear() ++ document.documentElement.removeAttribute('data-guanghu-theme') ++ document.documentElement.removeAttribute('data-theme') ++ document.documentElement.classList.remove('dark') ++ }) ++ ++ it('makes the lake presets visibly switch the document between light and dark', () => { ++ window.localStorage.setItem(GUANGHU_THEME_STORAGE_KEY, 'lake-light') ++ const { result } = renderHook(() => useGuanghuTheme()) ++ ++ expect(document.documentElement).toHaveAttribute('data-guanghu-theme', 'lake-light') ++ expect(document.documentElement).toHaveAttribute('data-theme', 'light') ++ expect(window.localStorage.getItem(THEME_MODE_STORAGE_KEY)).toBe('light') ++ ++ act(() => result.current.cycleTheme()) ++ ++ expect(document.documentElement).toHaveAttribute('data-guanghu-theme', 'lake-dark') ++ expect(document.documentElement).toHaveAttribute('data-theme', 'dark') ++ expect(document.documentElement).toHaveClass('dark') ++ expect(window.localStorage.getItem(THEME_MODE_STORAGE_KEY)).toBe('dark') ++ }) ++}) +diff --git a/src/hooks/useGuanghuTheme.ts b/src/hooks/useGuanghuTheme.ts +index 2c42f2d..ec175ce 100644 +--- a/src/hooks/useGuanghuTheme.ts ++++ b/src/hooks/useGuanghuTheme.ts +@@ -6,6 +6,16 @@ import { + writeGuanghuTheme, + type GuanghuTheme, + } from '../lib/guanghuTheme' ++import { applyThemeModeToDocument, writeStoredThemeMode } from '../lib/themeMode' ++ ++function applyGuanghuTheme(theme: GuanghuTheme): void { ++ document.documentElement.setAttribute('data-guanghu-theme', theme) ++ if (theme === 'native') return ++ ++ const mode = theme === 'lake-light' ? 'light' : 'dark' ++ applyThemeModeToDocument(document, mode) ++ writeStoredThemeMode(window.localStorage, mode) ++} + + export function useGuanghuTheme(): { theme: GuanghuTheme; cycleTheme: () => void } { + const [theme, setTheme] = useState(() => ( +@@ -13,7 +23,7 @@ export function useGuanghuTheme(): { theme: GuanghuTheme; cycleTheme: () => void + )) + + useEffect(() => { +- document.documentElement.setAttribute('data-guanghu-theme', theme) ++ applyGuanghuTheme(theme) + writeGuanghuTheme(window.localStorage, theme) + }, [theme]) + +diff --git a/src/lib/guanghuTheme.ts b/src/lib/guanghuTheme.ts +index 711a589..7ed2806 100644 +--- a/src/lib/guanghuTheme.ts ++++ b/src/lib/guanghuTheme.ts +@@ -4,9 +4,9 @@ export const GUANGHU_THEMES = ['native', 'lake-light', 'lake-dark'] as const + export type GuanghuTheme = typeof GUANGHU_THEMES[number] + + export const GUANGHU_THEME_LABELS: Record = { +- native: '原生', +- 'lake-light': '湖光浅色', +- 'lake-dark': '湖心深色', ++ native: '跟随原生', ++ 'lake-light': '湖光浅色(亮)', ++ 'lake-dark': '湖心深色(暗)', + } + + export function normalizeGuanghuTheme(value: unknown): GuanghuTheme { +-- +2.50.1 (Apple Git-155) + diff --git a/apps/tolaria/patches/0007-feat-complete-portable-page-block-set.patch b/apps/tolaria/patches/0007-feat-complete-portable-page-block-set.patch new file mode 100644 index 0000000..261ffcc --- /dev/null +++ b/apps/tolaria/patches/0007-feat-complete-portable-page-block-set.patch @@ -0,0 +1,216 @@ +From 2b57543d7855487e75d52fce12714501811fb650 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> +Date: Fri, 17 Jul 2026 10:30:04 +0800 +Subject: [PATCH 7/7] feat: complete portable page block set + +--- + src/components/Editor.css | 16 +++++++++++ + src/components/editorSchema.tsx | 3 +++ + .../tolariaEditorFormatting.test.tsx | 12 ++++++++- + .../tolariaEditorFormattingConfig.ts | 27 ++++++++++++++++--- + src/utils/blockNoteDirectMarkdown.test.ts | 9 +++++++ + src/utils/calloutMarkdown.ts | 12 +++++++-- + 6 files changed, 72 insertions(+), 7 deletions(-) + +diff --git a/src/components/Editor.css b/src/components/Editor.css +index 5db3fe1..11a0813 100644 +--- a/src/components/Editor.css ++++ b/src/components/Editor.css +@@ -109,6 +109,22 @@ + --tolaria-callout-bg: color-mix(in srgb, #dc2626 10%, var(--bg-primary)); + } + ++.tolaria-callout[data-kind="annotation"] { ++ --tolaria-callout-accent: #8b5cf6; ++ --tolaria-callout-bg: color-mix(in srgb, #8b5cf6 11%, var(--bg-primary)); ++} ++ ++.tolaria-callout[data-kind="relationship"] { ++ --tolaria-callout-accent: #0f9f8f; ++ --tolaria-callout-bg: color-mix(in srgb, #0f9f8f 11%, var(--bg-primary)); ++} ++ ++.tolaria-callout[data-kind="path"] { ++ --tolaria-callout-accent: #4f7ecb; ++ --tolaria-callout-bg: color-mix(in srgb, #4f7ecb 11%, var(--bg-primary)); ++ border-style: dashed; ++} ++ + .tolaria-callout__marker { + width: 4px; + min-height: 100%; +diff --git a/src/components/editorSchema.tsx b/src/components/editorSchema.tsx +index 7d62518..75ceddf 100644 +--- a/src/components/editorSchema.tsx ++++ b/src/components/editorSchema.tsx +@@ -356,8 +356,11 @@ const GuanghuPortalBlock = createReactBlockSpec( + ) + + const CALLOUT_LABELS: Record = { ++ annotation: '批注', + danger: 'Danger', + info: 'Info', ++ path: '路径', ++ relationship: '关系', + success: 'Success', + warning: 'Warning', + } +diff --git a/src/components/tolariaEditorFormatting.test.tsx b/src/components/tolariaEditorFormatting.test.tsx +index 7bd06c2..b32ebb7 100644 +--- a/src/components/tolariaEditorFormatting.test.tsx ++++ b/src/components/tolariaEditorFormatting.test.tsx +@@ -60,7 +60,7 @@ describe('tolariaEditorFormatting', () => { + ]) + }) + +- it('filters unsupported toggle slash-menu variants and removes command descriptions', () => { ++ it('keeps Notion-style toggles, filters oversized headings, and removes command descriptions', () => { + type TolariaSlashMenuTestItem = { + key: string + title: string +@@ -82,6 +82,8 @@ describe('tolariaEditorFormatting', () => { + ] satisfies TolariaSlashMenuTestItem[]) + + expect(items.map((item) => item.key)).toEqual([ ++ 'toggle_heading', ++ 'toggle_list', + 'heading', + 'heading_4', + 'bullet_list', +@@ -94,6 +96,8 @@ describe('tolariaEditorFormatting', () => { + undefined, + undefined, + undefined, ++ undefined, ++ undefined, + ]) + }) + +@@ -280,6 +284,12 @@ describe('tolariaEditorFormatting', () => { + styles: {}, + }], + }]) ++ ++ expect(items.map(item => item.key)).toEqual(expect.arrayContaining([ ++ 'tolaria_callout_annotation', ++ 'tolaria_callout_relationship', ++ 'tolaria_callout_path', ++ ])) + }) + + it('creates a math slash command with a default display equation', () => { +diff --git a/src/components/tolariaEditorFormattingConfig.ts b/src/components/tolariaEditorFormattingConfig.ts +index 614cbf5..41d9225 100644 +--- a/src/components/tolariaEditorFormattingConfig.ts ++++ b/src/components/tolariaEditorFormattingConfig.ts +@@ -9,6 +9,7 @@ import { + CheckCircle, + File, + FlowArrow, ++ GitBranch, + ImageSquare, + Info, + ListBullets, +@@ -21,6 +22,7 @@ import { + ScribbleLoop, + Smiley, + SpeakerHigh, ++ Tag, + Table, + TextHOne, + TextHTwo, +@@ -90,10 +92,6 @@ const UNSUPPORTED_FORMATTING_TOOLBAR_KEYS = new Set([ + const UNSUPPORTED_SLASH_MENU_KEYS = new Set([ + 'heading_5', + 'heading_6', +- 'toggle_heading', +- 'toggle_heading_2', +- 'toggle_heading_3', +- 'toggle_list', + ]) + + const TOLARIA_BLOCK_TYPE_SELECT_ICONS: Record = { +@@ -131,6 +129,9 @@ const TOLARIA_SLASH_MENU_ICONS: Partial> = { + quote: Quotes, + tolaria_callout_danger: XCircle, + tolaria_callout_info: Info, ++ tolaria_callout_annotation: Tag, ++ tolaria_callout_path: FlowArrow, ++ tolaria_callout_relationship: GitBranch, + tolaria_callout_success: CheckCircle, + tolaria_callout_warning: Warning, + table: Table, +@@ -237,6 +238,24 @@ export function createCalloutSlashMenuItems( + aliases: ['callout', 'danger', 'risk', 'error', '风险', '危险'], + kind: 'danger', + }), ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_annotation', ++ title: '批注 / Annotation', ++ aliases: ['annotation', 'comment', 'note', '批注', '注释'], ++ kind: 'annotation', ++ }), ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_relationship', ++ title: '关系卡 / Relationship', ++ aliases: ['relationship', 'relation', 'wikilink', '关系', '关联'], ++ kind: 'relationship', ++ }), ++ createCalloutSlashMenuItem(editor, { ++ key: 'tolaria_callout_path', ++ title: '路径包 / Path map', ++ aliases: ['path', 'breadcrumb', 'route', 'map', '路径', '面包屑', '导航'], ++ kind: 'path', ++ }), + ] + } + +diff --git a/src/utils/blockNoteDirectMarkdown.test.ts b/src/utils/blockNoteDirectMarkdown.test.ts +index cabba60..358df27 100644 +--- a/src/utils/blockNoteDirectMarkdown.test.ts ++++ b/src/utils/blockNoteDirectMarkdown.test.ts +@@ -127,6 +127,15 @@ describe('BlockNote direct Markdown serialization', () => { + children: [], + }, + ]).markdown).toBe('> [!success] Package is ready') ++ ++ expect(blocksToMarkdownDirect([ ++ { ++ type: TOLARIA_CALLOUT_BLOCK_TYPE, ++ props: { kind: 'relationship' }, ++ content: [{ type: 'text', text: '[[TCS]] → [[HoloLake]]', styles: {} }], ++ children: [], ++ }, ++ ]).markdown).toBe('> [!relationship] [[TCS]] → [[HoloLake]]') + }) + + it('caches unchanged block objects across rich-editor body serialization', () => { +diff --git a/src/utils/calloutMarkdown.ts b/src/utils/calloutMarkdown.ts +index b6881b6..2fa2449 100644 +--- a/src/utils/calloutMarkdown.ts ++++ b/src/utils/calloutMarkdown.ts +@@ -2,11 +2,19 @@ import type { BlockLike } from './durableMarkdownBlocks' + + export const TOLARIA_CALLOUT_BLOCK_TYPE = 'tolariaCallout' + +-export const TOLARIA_CALLOUT_KINDS = ['info', 'success', 'warning', 'danger'] as const ++export const TOLARIA_CALLOUT_KINDS = [ ++ 'info', ++ 'success', ++ 'warning', ++ 'danger', ++ 'annotation', ++ 'relationship', ++ 'path', ++] as const + + export type TolariaCalloutKind = typeof TOLARIA_CALLOUT_KINDS[number] + +-const CALLOUT_MARKER_RE = /^\[!(info|success|warning|danger)\]\s*(.*)$/iu ++const CALLOUT_MARKER_RE = /^\[!(info|success|warning|danger|annotation|relationship|path)\]\s*(.*)$/iu + + function isCalloutKind(value: unknown): value is TolariaCalloutKind { + return typeof value === 'string' && TOLARIA_CALLOUT_KINDS.includes(value as TolariaCalloutKind) +-- +2.50.1 (Apple Git-155) + diff --git a/docs/NEXT-TODO-20260717.md b/docs/NEXT-TODO-20260717.md index 56a21ef..5e8d693 100644 --- a/docs/NEXT-TODO-20260717.md +++ b/docs/NEXT-TODO-20260717.md @@ -4,7 +4,7 @@ > > 适用仓库: `bingshuo/hololake-platform` > -> 状态: TODO_REGISTERED +> 状态: PAGE_BLOCKS_COMPLETE_RUNNER_PENDING ## 1 · 补 Gitea Actions Runner @@ -37,6 +37,16 @@ docs/ops/GITEA-ACTIONS-RUNNER-WINDOWS-INTERNAL.md ## 2 · 光湖 App 页面内容块增强 +2026-07-17 恢复进度: + +```text +已完成并登记为平台仓补丁 0005—0007。 +已完成:彩色提示块、批注块、关系卡、路径包、折叠块恢复、主题可见性修复、默认世界首页。 +现有能力已确认:代码块、引用、Mermaid、表格、白板、图片/音视频。 +前端定向测试:5 个文件,24 项通过。 +TypeScript、ESLint 与 Vite production build 通过。 +``` + 冰朔反馈: ```text @@ -98,8 +108,7 @@ research/progress/HLP-DEV-0002-ICE-GL-INFINITY-HOLOLAKE-ERA-PROTOTYPE-REVIEW.md 先补 runner: docs/ops/GITEA-ACTIONS-RUNNER-WINDOWS-INTERNAL.md -再开页面内容块: +页面内容块已进入验收与打包: apps/tolaria/modules/02-knowledge-rendering/README.md docs/NEXT-TODO-20260717.md ``` - diff --git a/research/module-registry.yml b/research/module-registry.yml index 86d109c..46f5fe6 100644 --- a/research/module-registry.yml +++ b/research/module-registry.yml @@ -3,7 +3,7 @@ registry: name: 光湖研发模块注册表 status: active source_of_truth: true - updated_at: "2026-07-13T22:30:00+08:00" + updated_at: "2026-07-17T10:30:00+08:00" rules: - 模块编号永久唯一,废弃后不可复用。 @@ -41,9 +41,9 @@ modules: broadcast: research/broadcast/HLP-BRD-0003-ICE-GL-INFINITY-ZERO-CORE-ACTIVATION.md code_home: apps/tolaria/modules/02-knowledge-rendering/ summary: 轻量知识页、路径卡、媒体和图表的可读渲染;重型全局皮肤不作为当前方向。 - evidence: apps/tolaria/patches/0001-feat-add-Guanghu-ice-lake-visual-layer.patch - latest_progress: research/progress/HLP-DEV-0003-ICE-GL-INFINITY-TOLARIA-LIGHTWEIGHT-UI-AND-DRAG-FIX.md - updated_at: "2026-07-15T17:00:00+08:00" + evidence: apps/tolaria/patches/0007-feat-complete-portable-page-block-set.patch + latest_progress: research/progress/HLP-DEV-0004-HOLOLAKE-PAGE-BLOCKS-AND-THEME-RECOVERY.md + updated_at: "2026-07-17T10:30:00+08:00" - id: HLP-MOD-0003 name: 仓库路径与知识索引 product: Tolaria / 光湖 App diff --git a/research/progress/HLP-DEV-0004-HOLOLAKE-PAGE-BLOCKS-AND-THEME-RECOVERY.md b/research/progress/HLP-DEV-0004-HOLOLAKE-PAGE-BLOCKS-AND-THEME-RECOVERY.md new file mode 100644 index 0000000..35331fb --- /dev/null +++ b/research/progress/HLP-DEV-0004-HOLOLAKE-PAGE-BLOCKS-AND-THEME-RECOVERY.md @@ -0,0 +1,49 @@ +# HLP-DEV-0004 · HoloLake Era 页面块与主题恢复 + +> 日期:2026-07-17 +> +> 关联模块:`HLP-MOD-0002` +> +> 状态:已验证 + +## 为什么做 + +桌面同时存在三个同 Bundle ID、同图标的 App 副本,macOS LaunchServices +会将它们视为同一应用。上一轮所谓“Notion 类页面组件”实际只完成了 +四种 callout;首页默认隐藏,浅色主题也没有同步应用的基础亮暗模式。 + +## 实际改动 + +1. 恢复并完善可携带 Markdown callout 块:info / success / warning / danger。 +2. 增加 annotation / relationship / path 三种语义块,支持中英文斜杠命令搜索。 +3. 重新开放 BlockNote 原生 toggle heading / toggle list 入口。 +4. 保留并复用现有代码、引用、Mermaid、表格、白板和媒体块。 +5. HoloLake Era 世界首页改为默认可见,首页直接显示页面组件已就绪。 +6. 湖光浅色与湖心深色现在会切换实际 `data-theme` 亮/暗模式。 + +## 代码与补丁路径 + +- 可编译开发基线:`bingshuo/guanghu`(REPO-004,只作零件/上游基线)。 +- 产品事实与付款路径:`bingshuo/hololake-platform` 的独立补丁。 +- `apps/tolaria/patches/0005-Add-portable-callout-page-blocks.patch` +- `apps/tolaria/patches/0006-fix-make-Guanghu-home-and-themes-visible.patch` +- `apps/tolaria/patches/0007-feat-complete-portable-page-block-set.patch` + +## 验证 + +- Vitest:5 个定向文件,24 项全部通过。 +- ESLint:本次修改文件通过。 +- TypeScript:`tsc --noEmit` 通过。 +- Vite:production build 通过。 +- Tauri:Apple Silicon release App 与 DMG 构建通过。 +- 原生视觉验收:默认世界首页、光湖 Logo、页面组件入口、独立 0.1.3 版本号可见。 +- 内测 Bundle ID:`com.guanghu.desktop.review.20260717`,不与旧 `com.guanghu.desktop` 副本串版。 +- DMG SHA-256 本地已核验,仓库仅记缩略值:`29bda0c9…40fda73`。 +- 在服务器最新 `bingshuo/guanghu@514ab19` 新克隆上,按 `0003 → 0004 → 0005 → 0006 → 0007` 应用成功。 +- CodeScene / Codacy:当前本机没有 MCP、CLI 或 API 环境,未能执行,不伪造通过结果。 + +## 未完成项 + +1. 用户确认 0.1.3 Review 后,将桌面三个旧副本移入归档或废纸篓。 +2. 安装 CodeScene / Codacy 验证环境后补跑两项质量门。 +3. Windows 自动打包仍需 Gitea Actions runner。