diff --git a/src/renderer/src/components/editor/EditorPanel.tsx b/src/renderer/src/components/editor/EditorPanel.tsx index 21b116ea246..43a9276b15e 100644 --- a/src/renderer/src/components/editor/EditorPanel.tsx +++ b/src/renderer/src/components/editor/EditorPanel.tsx @@ -346,8 +346,9 @@ function EditorPanelInner({ activeMarkdownContent && extractFrontMatter(activeMarkdownContent) ) + // Why: front-matter shows by default; the map only carries per-file hide overrides. const isMarkdownFrontmatterVisible = - markdownFrontmatterVisible[markdownDocumentStateFileId] ?? false + markdownFrontmatterVisible[markdownDocumentStateFileId] ?? true const isMarkdownTableOfContentsVisible = markdownTableOfContentsVisible[markdownDocumentStateFileId] ?? false diff --git a/src/renderer/src/components/editor/MarkdownPreview.tsx b/src/renderer/src/components/editor/MarkdownPreview.tsx index 38d146c1312..f47b3610f3c 100644 --- a/src/renderer/src/components/editor/MarkdownPreview.tsx +++ b/src/renderer/src/components/editor/MarkdownPreview.tsx @@ -619,12 +619,11 @@ export default function MarkdownPreview({ .replace(/\r?\n(?:---|\+\+\+)\r?\n?$/, '') .trim() }, [frontMatter]) - // Why: front matter is hidden by default (#4468) and controlled from the - // markdown preview actions menu, keeping metadata out of the reading surface - // unless the user explicitly asks for it. + // Why: front matter shows by default and is toggled off from the markdown + // preview actions menu; the store map only carries per-file hide overrides. const toggleableSourceFileId: string | null = sourceFileId ?? null const frontmatterVisible = toggleableSourceFileId - ? (frontmatterVisibleByFile[toggleableSourceFileId] ?? false) + ? (frontmatterVisibleByFile[toggleableSourceFileId] ?? true) : true const [activeAnnotationBlockKey, setActiveAnnotationBlockKey] = useState(null) const [reviewNotesCopied, setReviewNotesCopied] = useState(false) diff --git a/src/renderer/src/lib/workspace-session.test.ts b/src/renderer/src/lib/workspace-session.test.ts index 2e24a297132..ee2884ac400 100644 --- a/src/renderer/src/lib/workspace-session.test.ts +++ b/src/renderer/src/lib/workspace-session.test.ts @@ -176,18 +176,18 @@ describe('buildWorkspaceSessionPayload', () => { expect(payload.browserTabsByWorktree?.['wt-1'][0].loading).toBe(false) }) - it('persists front-matter visibility only for restored editor files', () => { + it('persists front-matter hide overrides only for restored editor files', () => { const payload = buildWorkspaceSessionPayload( createSnapshot({ markdownFrontmatterVisible: { - '/tmp/demo.ts': true, - '/tmp/demo.diff': true, - '/tmp/closed.md': true + '/tmp/demo.ts': false, + '/tmp/demo.diff': false, + '/tmp/closed.md': false } }) ) - expect(payload.markdownFrontmatterVisible).toEqual({ '/tmp/demo.ts': true }) + expect(payload.markdownFrontmatterVisible).toEqual({ '/tmp/demo.ts': false }) }) it('does not persist empty split groups from transient simulator tab creation', () => { diff --git a/src/renderer/src/lib/workspace-session.ts b/src/renderer/src/lib/workspace-session.ts index 21a11eb0eec..f0acc22e620 100644 --- a/src/renderer/src/lib/workspace-session.ts +++ b/src/renderer/src/lib/workspace-session.ts @@ -188,10 +188,12 @@ export function buildEditorSessionData( WorkspaceVisibleTabType > const allEditFileIds = new Set(Object.values(editFileIdsByWorktree).flatMap((ids) => [...ids])) + // Why: preserve the actual value so per-file hide overrides survive restart; + // the map only ever carries `false` entries (visible is the default). const persistedMarkdownFrontmatterVisible = Object.fromEntries( - Object.keys(markdownFrontmatterVisible ?? {}) - .filter((fileId) => allEditFileIds.has(fileId)) - .map((fileId) => [fileId, true]) + Object.entries(markdownFrontmatterVisible ?? {}).filter(([fileId]) => + allEditFileIds.has(fileId) + ) ) return { diff --git a/src/renderer/src/store/slices/editor.test.ts b/src/renderer/src/store/slices/editor.test.ts index 0859e675615..ac54883ebe3 100644 --- a/src/renderer/src/store/slices/editor.test.ts +++ b/src/renderer/src/store/slices/editor.test.ts @@ -1482,7 +1482,7 @@ describe('createEditorSlice markdown view state', () => { }, { preview: true } ) - store.getState().setMarkdownFrontmatterVisible('/repo/docs/README.md', true) + store.getState().setMarkdownFrontmatterVisible('/repo/docs/README.md', false) store.getState().setMarkdownTableOfContentsVisible('/repo/docs/README.md', true) store.getState().openDiff('wt-1', '/repo/docs/guide.md', 'docs/guide.md', 'markdown', false, { @@ -1512,7 +1512,7 @@ describe('createEditorSlice markdown view state', () => { worktreeId: 'wt-1', language: 'markdown' }) - store.getState().setMarkdownFrontmatterVisible('/repo/docs/README.md', true) + store.getState().setMarkdownFrontmatterVisible('/repo/docs/README.md', false) store.getState().setMarkdownTableOfContentsVisible('/repo/docs/README.md', true) store.getState().openDiff('wt-1', '/repo/docs/guide.md', 'docs/guide.md', 'markdown', false, { @@ -1520,7 +1520,7 @@ describe('createEditorSlice markdown view state', () => { }) expect(store.getState().markdownFrontmatterVisible).toEqual({ - '/repo/docs/README.md': true + '/repo/docs/README.md': false }) expect(store.getState().markdownTableOfContentsVisible).toEqual({ '/repo/docs/README.md': true @@ -1573,28 +1573,28 @@ describe('createEditorSlice editor view mode', () => { }) describe('createEditorSlice markdown frontmatter visibility (#4468)', () => { - it('stores visible=true as an explicit entry keyed by fileId', () => { + it('stores hidden=false as an explicit entry keyed by fileId', () => { const store = createEditorStore() - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) - - expect(store.getState().markdownFrontmatterVisible).toEqual({ '/repo/notes.md': true }) - }) - - it('deletes the entry when visibility resets to hidden', () => { - const store = createEditorStore() - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) + expect(store.getState().markdownFrontmatterVisible).toEqual({ '/repo/notes.md': false }) + }) + + it('deletes the entry when visibility resets to visible', () => { + const store = createEditorStore() + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) + + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) + expect(store.getState().markdownFrontmatterVisible).toEqual({}) }) - it('is a no-op when hiding a file that was never shown', () => { + it('is a no-op when showing a file that was never hidden', () => { const store = createEditorStore() const before = store.getState().markdownFrontmatterVisible - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) expect(store.getState().markdownFrontmatterVisible).toBe(before) }) @@ -1608,7 +1608,7 @@ describe('createEditorSlice markdown frontmatter visibility (#4468)', () => { language: 'markdown', mode: 'edit' }) - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) store.getState().closeFile('/repo/notes.md') @@ -1630,11 +1630,11 @@ describe('createEditorSlice markdown frontmatter visibility (#4468)', () => { worktreeId: 'wt-1', language: 'markdown' }) - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) store.getState().closeFile('/repo/notes.md') - expect(store.getState().markdownFrontmatterVisible).toEqual({ '/repo/notes.md': true }) + expect(store.getState().markdownFrontmatterVisible).toEqual({ '/repo/notes.md': false }) store.getState().closeFile('markdown-preview::/repo/notes.md') @@ -1662,7 +1662,7 @@ describe('createEditorSlice markdown frontmatter visibility (#4468)', () => { }, { sourceFileId: '/repo/notes.md' } ) - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) store.getState().openFile( { @@ -1675,7 +1675,7 @@ describe('createEditorSlice markdown frontmatter visibility (#4468)', () => { { preview: true } ) - expect(store.getState().markdownFrontmatterVisible).toEqual({ '/repo/notes.md': true }) + expect(store.getState().markdownFrontmatterVisible).toEqual({ '/repo/notes.md': false }) }) it('drops the visibility flag when all files are closed', () => { @@ -1687,7 +1687,7 @@ describe('createEditorSlice markdown frontmatter visibility (#4468)', () => { language: 'markdown', mode: 'edit' }) - store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', true) + store.getState().setMarkdownFrontmatterVisible('/repo/notes.md', false) store.getState().closeAllFiles() diff --git a/src/renderer/src/store/slices/editor.ts b/src/renderer/src/store/slices/editor.ts index 8e9e86c0d93..7e825ec6f77 100644 --- a/src/renderer/src/store/slices/editor.ts +++ b/src/renderer/src/store/slices/editor.ts @@ -1405,11 +1405,11 @@ export const createEditorSlice: StateCreator = (s markdownFrontmatterVisible: {}, setMarkdownFrontmatterVisible: (fileId, visible) => set((s) => { - // Why: default is hidden. Writing `false` explicitly when no entry exists - // would grow the record unnecessarily; delete instead so the shape stays - // minimal and hydration round-trips cleanly — same trade-off as - // setEditorViewMode above. - if (!visible) { + // Why: default is visible. Writing `true` explicitly when no entry exists + // would grow the record unnecessarily; delete instead so the map only + // carries hide overrides and hydration round-trips cleanly — same + // trade-off as setEditorViewMode above. + if (visible) { if (!(fileId in s.markdownFrontmatterVisible)) { return s } @@ -1417,7 +1417,7 @@ export const createEditorSlice: StateCreator = (s delete next[fileId] return { markdownFrontmatterVisible: next } } - return { markdownFrontmatterVisible: { ...s.markdownFrontmatterVisible, [fileId]: true } } + return { markdownFrontmatterVisible: { ...s.markdownFrontmatterVisible, [fileId]: false } } }), // Markdown table of contents visibility @@ -4496,24 +4496,26 @@ export const createEditorSlice: StateCreator = (s const nextActiveTabType = nextActiveFileId || activeTabType !== 'editor' ? activeTabType : 'terminal' const openFileIds = new Set(openFiles.map((file) => file.id)) - const visibleFrontmatterEntries = new Map() + // Why: visible is the default, so only restore per-file hide overrides + // (`false`); legacy `true` entries collapse back to the default. + const hiddenFrontmatterEntries = new Map() for (const [persistedFileId, visible] of Object.entries( persistedMarkdownFrontmatterVisible )) { - if (!visible) { + if (visible) { continue } if (openFileIds.has(persistedFileId)) { - visibleFrontmatterEntries.set(persistedFileId, true) + hiddenFrontmatterEntries.set(persistedFileId, false) } for (const migrations of Object.values(editorFileIdMigrationsByWorktree)) { const migratedFileId = migrations.get(persistedFileId) if (migratedFileId && openFileIds.has(migratedFileId)) { - visibleFrontmatterEntries.set(migratedFileId, true) + hiddenFrontmatterEntries.set(migratedFileId, false) } } } - const markdownFrontmatterVisible = Object.fromEntries(visibleFrontmatterEntries) + const markdownFrontmatterVisible = Object.fromEntries(hiddenFrontmatterEntries) return { openFiles, diff --git a/src/renderer/src/store/slices/settings.test.ts b/src/renderer/src/store/slices/settings.test.ts index dd113fa668a..ad4c4983b58 100644 --- a/src/renderer/src/store/slices/settings.test.ts +++ b/src/renderer/src/store/slices/settings.test.ts @@ -242,7 +242,7 @@ describe('createSettingsSlice runtime switching', () => { editorDrafts: { '/env-1/repo/stale.md': 'stale' }, markdownViewMode: { '/env-1/repo/stale.md': 'rich' }, editorViewMode: { '/env-1/repo/stale.md': 'changes' }, - markdownFrontmatterVisible: { '/env-1/repo/stale.md': true }, + markdownFrontmatterVisible: { '/env-1/repo/stale.md': false }, editorCursorLine: { '/env-1/repo/stale.md': 4 }, showDotfilesByWorktree: { 'repo-env-1::/env-1/repo': false }, gitIgnoredPathsByWorktree: { 'repo-env-1::/env-1/repo': ['dist/'] }, @@ -300,7 +300,7 @@ describe('createSettingsSlice runtime switching', () => { expect(store.getState().markdownViewMode).toEqual({ '/env-1/repo/stale.md': 'rich' }) expect(store.getState().editorViewMode).toEqual({ '/env-1/repo/stale.md': 'changes' }) expect(store.getState().markdownFrontmatterVisible).toEqual({ - '/env-1/repo/stale.md': true + '/env-1/repo/stale.md': false }) expect(store.getState().editorCursorLine).toEqual({ '/env-1/repo/stale.md': 4 }) expect(store.getState().showDotfilesByWorktree).toEqual({ 'repo-env-1::/env-1/repo': false }) diff --git a/src/renderer/src/store/slices/store-session-cascades.test.ts b/src/renderer/src/store/slices/store-session-cascades.test.ts index f938dfecdb0..2a9a3de8e94 100644 --- a/src/renderer/src/store/slices/store-session-cascades.test.ts +++ b/src/renderer/src/store/slices/store-session-cascades.test.ts @@ -2079,7 +2079,7 @@ describe('hydrateEditorSession', () => { }, activeFileIdByWorktree: { [wt]: '/path/wt1/src/index.ts' }, activeTabTypeByWorktree: { [wt]: 'editor' }, - markdownFrontmatterVisible: { '/path/wt1/README.md': true } + markdownFrontmatterVisible: { '/path/wt1/README.md': false } }) const s = store.getState() @@ -2088,7 +2088,7 @@ describe('hydrateEditorSession', () => { expect(s.openFiles[0].mode).toBe('edit') expect(s.openFiles[0].isDirty).toBe(false) expect(s.openFiles[1].isPreview).toBe(true) - expect(s.markdownFrontmatterVisible).toEqual({ '/path/wt1/README.md': true }) + expect(s.markdownFrontmatterVisible).toEqual({ '/path/wt1/README.md': false }) expect(s.activeFileId).toBe('/path/wt1/src/index.ts') expect(s.activeTabType).toBe('editor') }) @@ -2167,10 +2167,46 @@ describe('hydrateEditorSession', () => { [FLOATING_TERMINAL_WORKTREE_ID]: filePath }, activeTabTypeByWorktree: { [FLOATING_TERMINAL_WORKTREE_ID]: 'editor' }, + markdownFrontmatterVisible: { [filePath]: false } + }) + + expect(store.getState().markdownFrontmatterVisible).toEqual({ [fileId]: false }) + }) + + it('drops legacy visible=true front-matter entries so upgraded sessions fall back to the visible default', () => { + const store = createTestStore() + const filePath = '/orca/userData/floating-workspace/note.md' + const fileId = ownedEditorFileId(filePath, FLOATING_TERMINAL_WORKTREE_ID, null) + + store.setState({ activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID }) + + store.getState().hydrateEditorSession({ + activeRepoId: null, + activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID, + activeTabId: null, + tabsByWorktree: {}, + terminalLayoutsByTabId: {}, + openFilesByWorktree: { + [FLOATING_TERMINAL_WORKTREE_ID]: [ + { + filePath, + relativePath: 'note.md', + worktreeId: FLOATING_TERMINAL_WORKTREE_ID, + language: 'markdown', + runtimeEnvironmentId: null + } + ] + }, + activeFileIdByWorktree: { + [FLOATING_TERMINAL_WORKTREE_ID]: filePath + }, + activeTabTypeByWorktree: { [FLOATING_TERMINAL_WORKTREE_ID]: 'editor' }, + // Pre-flip sessions stored `true` for the (then non-default) visible state. markdownFrontmatterVisible: { [filePath]: true } }) - expect(store.getState().markdownFrontmatterVisible).toEqual({ [fileId]: true }) + expect(store.getState().markdownFrontmatterVisible).toEqual({}) + expect(fileId in store.getState().markdownFrontmatterVisible).toBe(false) }) it('falls back to the floating workspace file id when duplicate paths are owner-qualified', () => {