mirror of
https://github.com/stablyai/orca.git
synced 2026-09-28 00:02:41 +00:00
Show markdown front-matter by default in the rich editor and preview (#8623)
Flip the default so the front-matter banner is visible unless the user explicitly hides it. The per-file visibility map now stores only hide overrides (false) instead of show overrides: read defaults resolve to true, the setter writes false / deletes on show, session persistence preserves the real value, and hydration keeps false entries while dropping legacy true entries back to the visible default. Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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<string | null>(null)
|
||||
const [reviewNotesCopied, setReviewNotesCopied] = useState(false)
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -1405,11 +1405,11 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (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<AppState, [], [], EditorSlice> = (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<AppState, [], [], EditorSlice> = (s
|
||||
const nextActiveTabType =
|
||||
nextActiveFileId || activeTabType !== 'editor' ? activeTabType : 'terminal'
|
||||
const openFileIds = new Set(openFiles.map((file) => file.id))
|
||||
const visibleFrontmatterEntries = new Map<string, boolean>()
|
||||
// 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<string, boolean>()
|
||||
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,
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user