diff --git a/src/renderer/src/assets/markdown-preview.css b/src/renderer/src/assets/markdown-preview.css index 9388731bb9c..7f5afaf76f1 100644 --- a/src/renderer/src/assets/markdown-preview.css +++ b/src/renderer/src/assets/markdown-preview.css @@ -556,7 +556,7 @@ line-height: 1.3; } -/* Heading toggle sizes mirror the plain h2–h4 scales (1.4 / 1.15 / 1em). */ +/* Heading toggle sizes mirror the plain h2–h5 scales (1.4 / 1.15 / 1 / 0.92em). */ .markdown-body details.orca-details[data-orca-toggle='heading-2'] > summary { font-size: 1.4em; line-height: 1.3; @@ -572,6 +572,12 @@ line-height: 1.3; } +.markdown-body details.orca-details[data-orca-toggle='heading-5'] > summary { + font-size: 0.92em; + color: var(--muted-foreground); + line-height: 1.3; +} + .markdown-body details.orca-details > :not(summary) { margin-left: 0.15em; } diff --git a/src/renderer/src/assets/rich-markdown-editor.css b/src/renderer/src/assets/rich-markdown-editor.css index 48c13e0057d..4b918455221 100644 --- a/src/renderer/src/assets/rich-markdown-editor.css +++ b/src/renderer/src/assets/rich-markdown-editor.css @@ -764,7 +764,7 @@ height: 2.405em; } -/* Heading toggle sizes mirror the plain h2–h4 scales (1.4 / 1.15 / 1em). */ +/* Heading toggle sizes mirror the plain h2–h5 scales (1.4 / 1.15 / 1 / 0.92em). */ .rich-markdown-editor details.orca-details[data-orca-toggle='heading-2'] > summary, .rich-markdown-editor .orca-details[data-type='details'][data-orca-toggle='heading-2'] summary { font-size: 1.4em; @@ -787,6 +787,13 @@ line-height: 1.3; } +.rich-markdown-editor details.orca-details[data-orca-toggle='heading-5'] > summary, +.rich-markdown-editor .orca-details[data-type='details'][data-orca-toggle='heading-5'] summary { + font-size: 0.92em; + color: var(--muted-foreground); + line-height: 1.3; +} + .rich-markdown-editor blockquote { padding: 0.5em 1em; border-left: 3px solid var(--border); diff --git a/src/renderer/src/components/editor/MarkdownPreview.tsx b/src/renderer/src/components/editor/MarkdownPreview.tsx index 4c09bd0a8b5..3fa53012683 100644 --- a/src/renderer/src/components/editor/MarkdownPreview.tsx +++ b/src/renderer/src/components/editor/MarkdownPreview.tsx @@ -315,7 +315,7 @@ const markdownPreviewSanitizeSchema = { ...(defaultSchema.attributes?.details ?? []), 'open', ['className', 'orca-details'], - ['dataOrcaToggle', 'heading-1', 'heading-2', 'heading-3', 'heading-4'] + ['dataOrcaToggle', 'heading-1', 'heading-2', 'heading-3', 'heading-4', 'heading-5'] ], h1: [...(defaultSchema.attributes?.h1 ?? []), 'id'], h2: [...(defaultSchema.attributes?.h2 ?? []), 'id'], diff --git a/src/renderer/src/components/editor/details-markdown-html.test.ts b/src/renderer/src/components/editor/details-markdown-html.test.ts index e381f453835..c4e1493a198 100644 --- a/src/renderer/src/components/editor/details-markdown-html.test.ts +++ b/src/renderer/src/components/editor/details-markdown-html.test.ts @@ -2,6 +2,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { extractDetailsSummaryHtml, isEditableDetailsHtmlBlock, + parseDetailsAttributes, + parseToggleHeadingVariant, type DetailsHtmlBlock } from './details-markdown-html' @@ -45,4 +47,31 @@ describe('details markdown html', () => { ) expect(usedSummaryCapture).toBe(false) }) + + it('accepts heading-5 toggle variants and rejects unsupported levels', () => { + expect(parseToggleHeadingVariant('heading-5')).toBe('heading-5') + expect(parseToggleHeadingVariant('heading-6')).toBeNull() + expect(parseDetailsAttributes(' data-orca-toggle="heading-5"')).toMatchObject({ + variant: 'heading-5' + }) + expect(parseDetailsAttributes(' data-orca-toggle="heading-6"')).toMatchObject({ + variant: null + }) + + const editableHeading5: DetailsHtmlBlock = { + raw: '', + openingAttributes: ' data-orca-toggle="heading-5"', + inner: 'Toggle

Body

', + hasNestedDetails: false + } + const unsupportedHeading6: DetailsHtmlBlock = { + raw: '', + openingAttributes: ' data-orca-toggle="heading-6"', + inner: 'Toggle

Body

', + hasNestedDetails: false + } + + expect(isEditableDetailsHtmlBlock(editableHeading5)).toBe(true) + expect(isEditableDetailsHtmlBlock(unsupportedHeading6)).toBe(false) + }) }) diff --git a/src/renderer/src/components/editor/details-markdown-html.ts b/src/renderer/src/components/editor/details-markdown-html.ts index 6534793ce63..a2f4238b658 100644 --- a/src/renderer/src/components/editor/details-markdown-html.ts +++ b/src/renderer/src/components/editor/details-markdown-html.ts @@ -1,14 +1,20 @@ import type { MarkdownToken } from '@tiptap/core' -// Toggle summaries can render at heading scales 1–4, mirroring the plain -// heading levels the slash menu / toolbar dropdown offer (h1–h4). -export type ToggleHeadingVariant = 'heading-1' | 'heading-2' | 'heading-3' | 'heading-4' +// Toggle summaries can render at heading scales 1–5, mirroring the plain +// heading levels the slash menu / toolbar dropdown offer (h1–h5). +export type ToggleHeadingVariant = + | 'heading-1' + | 'heading-2' + | 'heading-3' + | 'heading-4' + | 'heading-5' export const TOGGLE_HEADING_VARIANTS: readonly ToggleHeadingVariant[] = [ 'heading-1', 'heading-2', 'heading-3', - 'heading-4' + 'heading-4', + 'heading-5' ] export function parseToggleHeadingVariant(value: unknown): ToggleHeadingVariant | null { @@ -49,7 +55,7 @@ export function parseDetailsAttributes(rawAttributes: string): Record { ) }) - it.each(['heading-2', 'heading-3', 'heading-4'])( + it.each(['heading-2', 'heading-3', 'heading-4', 'heading-5'])( 'preserves %s-styled details blocks', (variant) => { expect( @@ -166,6 +166,12 @@ describe('rich markdown round trip', () => { expect(roundTripMarkdown(input)).toBe(input.trimEnd()) }) + it('preserves details blocks with unsupported toggle variants as passthrough html', () => { + const input = + '
Toggle

Body

\n' + expect(roundTripMarkdown(input)).toBe(input.trimEnd()) + }) + it('preserves details blocks with closing tags inside fenced code as passthrough html', () => { const input = [ '
Toggle', @@ -208,7 +214,8 @@ describe('rich markdown round trip', () => { it.each([ ['toggle-h2', 'heading-2'], ['toggle-h3', 'heading-3'], - ['toggle-h4', 'heading-4'] + ['toggle-h4', 'heading-4'], + ['toggle-h5', 'heading-5'] ] as const)('inserts editable %s toggles from slash commands', (commandId, variant) => { expect(slashCommandMarkdown(commandId)).toBe( `
\n\n\n\n\n
` diff --git a/src/renderer/src/components/editor/rich-markdown-commands.test.ts b/src/renderer/src/components/editor/rich-markdown-commands.test.ts index 5caffa90e7a..7a043e5e860 100644 --- a/src/renderer/src/components/editor/rich-markdown-commands.test.ts +++ b/src/renderer/src/components/editor/rich-markdown-commands.test.ts @@ -35,6 +35,7 @@ describe('rich markdown slash commands', () => { expect(getCommand('inline-math').aliases).toContain('latex') expect(getCommand('math-block').aliases).toContain('equation block') expect(getCommand('emoji').aliases).toContain('reaction') + expect(getCommand('toggle-h5').aliases).toContain('toggle-h5') }) it('orders commands under section headers', () => { @@ -48,6 +49,7 @@ describe('rich markdown slash commands', () => { 'Toggle headings:toggle-h2', 'Toggle headings:toggle-h3', 'Toggle headings:toggle-h4', + 'Toggle headings:toggle-h5', 'Basic blocks:blockquote', 'Basic blocks:ordered-list', 'Basic blocks:bullet-list', diff --git a/src/renderer/src/components/editor/rich-markdown-details-extension.ts b/src/renderer/src/components/editor/rich-markdown-details-extension.ts index c36fa5753e1..151fe512330 100644 --- a/src/renderer/src/components/editor/rich-markdown-details-extension.ts +++ b/src/renderer/src/components/editor/rich-markdown-details-extension.ts @@ -22,7 +22,8 @@ const TOGGLE_HEADING_PLACEHOLDERS: Record { insertToggle(editor, 'heading-4') } + }, + { + id: 'toggle-h5', + get label() { + return translate( + 'auto.components.editor.rich.markdown.slash.commands.21d8c463e5', + 'Toggle H5' + ) + }, + aliases: ['toggle-h5'], + icon: icon(ChevronRight), + group: 'Toggle headings', + get description() { + return translate( + 'auto.components.editor.rich.markdown.slash.commands.dc239b41ad', + 'Create a collapsible section with a deep heading summary.' + ) + }, + run: (editor) => { + insertToggle(editor, 'heading-5') + } } ] diff --git a/src/renderer/src/components/editor/rich-markdown-slash-command-primitives.ts b/src/renderer/src/components/editor/rich-markdown-slash-command-primitives.ts index 3f982d9bba9..6cd7bd429ae 100644 --- a/src/renderer/src/components/editor/rich-markdown-slash-command-primitives.ts +++ b/src/renderer/src/components/editor/rich-markdown-slash-command-primitives.ts @@ -23,6 +23,7 @@ export type SlashCommandId = | 'heading-4' | 'toggle-h4' | 'heading-5' + | 'toggle-h5' | 'task-list' | 'bullet-list' | 'ordered-list' diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 1575481e14b..5d48f72c149 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -12030,7 +12030,9 @@ "2f9d6b4e10": "Toggle H3", "8c1a3e7d52": "Create a collapsible section with a small heading summary.", "5e0b9c2a71": "Toggle H4", - "d4f16a8b39": "Create a collapsible section with a nested heading summary." + "d4f16a8b39": "Create a collapsible section with a nested heading summary.", + "21d8c463e5": "Toggle H5", + "dc239b41ad": "Create a collapsible section with a deep heading summary." } } } diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index 3a5311cb76c..163138a3f18 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -12007,7 +12007,9 @@ "2f9d6b4e10": "Alternar título 3", "8c1a3e7d52": "Cree una sección plegable con un resumen de encabezado pequeño.", "5e0b9c2a71": "Alternar título 4", - "d4f16a8b39": "Cree una sección plegable con un resumen de encabezado anidado." + "d4f16a8b39": "Cree una sección plegable con un resumen de encabezado anidado.", + "21d8c463e5": "Alternar título 5", + "dc239b41ad": "Cree una sección plegable con un resumen de encabezado profundo." } } } diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index 34481f1ac18..9bcc9051304 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -12007,7 +12007,9 @@ "2f9d6b4e10": "見出し 3 を切り替えます", "8c1a3e7d52": "小さな見出しの概要を含む折りたたみ可能なセクションを作成します。", "5e0b9c2a71": "見出し 4 を切り替えます", - "d4f16a8b39": "入れ子の見出しの概要を含む折りたたみ可能なセクションを作成します。" + "d4f16a8b39": "入れ子の見出しの概要を含む折りたたみ可能なセクションを作成します。", + "21d8c463e5": "見出し 5 を切り替えます", + "dc239b41ad": "深い見出しの概要を含む折りたたみ可能なセクションを作成します。" } } } diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index e8d8ba49319..73b1d12b378 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -12007,7 +12007,9 @@ "2f9d6b4e10": "토글 제목 3", "8c1a3e7d52": "작은 제목 요약이 포함된 접을 수 있는 섹션을 만듭니다.", "5e0b9c2a71": "토글 제목 4", - "d4f16a8b39": "중첩된 제목 요약이 포함된 접을 수 있는 섹션을 만듭니다." + "d4f16a8b39": "중첩된 제목 요약이 포함된 접을 수 있는 섹션을 만듭니다.", + "21d8c463e5": "토글 제목 5", + "dc239b41ad": "깊은 제목 요약이 포함된 접을 수 있는 섹션을 만듭니다." } } } diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 2ec328ba2e1..874022be095 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -12007,7 +12007,9 @@ "2f9d6b4e10": "切换标题 3", "8c1a3e7d52": "创建一个带有小标题摘要的可折叠部分。", "5e0b9c2a71": "切换标题 4", - "d4f16a8b39": "创建一个带有嵌套标题摘要的可折叠部分。" + "d4f16a8b39": "创建一个带有嵌套标题摘要的可折叠部分。", + "21d8c463e5": "切换标题 5", + "dc239b41ad": "创建一个带有深层标题摘要的可折叠部分。" } } }