diff --git a/packages/client/internals/SideEditor.vue b/packages/client/internals/SideEditor.vue index b354d2fa0d..f672657f05 100644 --- a/packages/client/internals/SideEditor.vue +++ b/packages/client/internals/SideEditor.vue @@ -17,18 +17,16 @@ const tab = ref<'content' | 'note'>('content') const content = ref('') const note = ref('') const dirty = ref(false) -const frontmatter = ref({}) const { info, update } = useDynamicSlideInfo(currentSlideNo) watch( info, (v) => { - frontmatter.value = v?.frontmatter || {} - if (!isInputting.value) { note.value = (v?.note || '').trim() - content.value = (v?.content || '').trim() + const frontmatterPart = v?.frontmatterRaw?.trim() ? `---\n${v.frontmatterRaw.trim()}\n---\n\n` : '' + content.value = frontmatterPart + (v?.content || '').trim() dirty.value = false } }, @@ -37,10 +35,17 @@ watch( async function save() { dirty.value = false + + let frontmatterRaw: string | undefined + const contentOnly = content.value.trim().replace(/^---\n([\s\S]*?)\n---\n/, (_, f) => { + frontmatterRaw = f + return '' + }) + await update({ note: note.value || undefined, - content: content.value, - // frontmatter: frontmatter.value, + content: contentOnly, + frontmatterRaw, }) } diff --git a/packages/parser/src/fs.ts b/packages/parser/src/fs.ts index a66c7d3632..15a2fc95b4 100644 --- a/packages/parser/src/fs.ts +++ b/packages/parser/src/fs.ts @@ -103,14 +103,15 @@ export async function load(userRoot: string, filepath: string, loadedSource: Rec } else { slides.push({ - index: slides.length, - importChain, - source: slide, frontmatter: { ...slide.frontmatter, ...frontmatterOverride }, content: slide.content, + frontmatterRaw: slide.frontmatterRaw, note: slide.note, title: slide.title, level: slide.level, + index: slides.length, + importChain, + source: slide, }) } } diff --git a/packages/slidev/node/vite/loaders.ts b/packages/slidev/node/vite/loaders.ts index 0252205341..cace9b7ab8 100644 --- a/packages/slidev/node/vite/loaders.ts +++ b/packages/slidev/node/vite/loaders.ts @@ -4,6 +4,7 @@ import type { ResolvedSlidevOptions, SlideInfo, SlidePatch, SlidevServerOptions import * as parser from '@slidev/parser/fs' import equal from 'fast-deep-equal' import type { LoadResult } from 'rollup' +import YAML from 'yaml' import { getBodyJson, updateFrontmatterPatch } from '../utils' import { templates } from '../virtual' import { templateTitleRendererMd } from '../virtual/titles' @@ -91,6 +92,18 @@ export function createSlidesLoader( if (body.content) slide.content = slide.source.content = body.content + if (body.frontmatterRaw != null) { + if (body.frontmatterRaw.trim() === '') { + slide.source.frontmatterDoc = slide.source.frontmatterStyle = undefined + } + else { + const parsed = YAML.parseDocument(body.frontmatterRaw) + if (parsed.errors.length) + console.error('ERROR when saving frontmatter', parsed.errors) + else + slide.source.frontmatterDoc = parsed + } + } if (body.note) slide.note = slide.source.note = body.note if (body.frontmatter) diff --git a/packages/slidev/package.json b/packages/slidev/package.json index e80a8219cb..33aef111d0 100644 --- a/packages/slidev/package.json +++ b/packages/slidev/package.json @@ -117,6 +117,7 @@ "vite-plugin-vue-server-ref": "catalog:", "vitefu": "^0.2.5", "vue": "catalog:", + "yaml": "catalog:", "yargs": "^17.7.2" }, "devDependencies": { diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index aa7c004ce9..199f378f2e 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -8,6 +8,7 @@ export type FrontmatterStyle = 'frontmatter' | 'yaml' export interface SlideInfoBase { frontmatter: Record content: string + frontmatterRaw?: string note?: string title?: string level?: number @@ -33,7 +34,6 @@ export interface SourceSlideInfo extends SlideInfoBase { * Slides import by this slide. */ imports?: SourceSlideInfo[] - frontmatterRaw?: string frontmatterDoc?: YAML.Document frontmatterStyle?: FrontmatterStyle } @@ -57,7 +57,7 @@ export interface SlideInfo extends SlideInfoBase { /** * Editable fields for a slide */ -export type SlidePatch = Partial> & { +export type SlidePatch = Partial> & { skipHmr?: boolean /** * The frontmatter patch (only the changed fields) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1031a48cd4..02d652ce09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -738,6 +738,9 @@ importers: vue: specifier: 'catalog:' version: 3.4.38(typescript@5.5.4) + yaml: + specifier: 'catalog:' + version: 2.5.0 yargs: specifier: ^17.7.2 version: 17.7.2 diff --git a/test/__snapshots__/parser.test.ts.snap b/test/__snapshots__/parser.test.ts.snap index bf1f01fcdb..ed162f2076 100644 --- a/test/__snapshots__/parser.test.ts.snap +++ b/test/__snapshots__/parser.test.ts.snap @@ -88,6 +88,12 @@ exports[`md parser > frontmatter.md > slides 1`] = ` }, "layout": "cover", }, + "frontmatterRaw": "layout: cover +fonts: + sans: Roboto, Lato + serif: Mate SC + mono: Fira Code +", "importChain": undefined, "index": 0, "level": 1, @@ -149,6 +155,11 @@ fonts: "title": "FooBar", }, }, + "frontmatterRaw": "meta: + title: FooBar + duration: 12 +layout: center +", "importChain": undefined, "index": 1, "level": 1, @@ -204,6 +215,7 @@ This is note { "content": "# Morning", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 2, "level": 1, @@ -236,6 +248,8 @@ Hey", "frontmatter": { "layout": "text", }, + "frontmatterRaw": "layout: text +", "importChain": undefined, "index": 3, "level": undefined, @@ -287,6 +301,7 @@ this should be treated as code block Also part of the code block \`\`\`", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 4, "level": undefined, @@ -334,6 +349,10 @@ Also part of the code block "frontmatter": { "layout": "from yaml", }, + "frontmatterRaw": " +# The first yaml block should be treated as frontmatter +layout: from yaml +", "importChain": undefined, "index": 5, "level": undefined, @@ -381,6 +400,8 @@ Content 2", "frontmatter": { "layout": "cover", }, + "frontmatterRaw": "layout: cover +", "importChain": undefined, "index": 6, "level": 1, @@ -435,6 +456,7 @@ layout: should not from yaml 2 Content 3", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 7, "level": 1, @@ -554,6 +576,8 @@ exports[`md parser > mdc.md > slides 1`] = ` "frontmatter": { "mdc": true, }, + "frontmatterRaw": "mdc: true +", "importChain": undefined, "index": 0, "level": 1, @@ -674,6 +698,7 @@ Sample Text console.log('Hello World') \`\`\`", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 0, "level": 1, @@ -724,6 +749,7 @@ console.log('Hello World') - Hey - Yo", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 1, "level": 1, @@ -763,6 +789,7 @@ console.log('Hello World') { "content": "Nice to meet you", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 2, "level": undefined, @@ -864,6 +891,7 @@ exports[`md parser > multi-entries.md > slides 1`] = ` { "content": "# Page 1", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": [ { "content": "", @@ -939,6 +967,8 @@ src: sub/page1.md "background": "https://sli.dev/demo-cover.png#2", "layout": "cover", }, + "frontmatterRaw": "layout: cover +", "importChain": [ { "content": "", @@ -1041,6 +1071,7 @@ layout: cover "frontmatter": { "background": "https://sli.dev/demo-cover.png#34", }, + "frontmatterRaw": undefined, "importChain": [ { "content": "", @@ -1150,6 +1181,8 @@ background: https://sli.dev/demo-cover.png#34 "background": "https://sli.dev/demo-cover.png#34", "layout": "cover", }, + "frontmatterRaw": "layout: cover +", "importChain": [ { "content": "", @@ -1269,6 +1302,7 @@ layout: cover "frontmatter": { "background": "https://sli.dev/demo-cover.png#14", }, + "frontmatterRaw": undefined, "importChain": [ { "content": "", @@ -1546,6 +1580,8 @@ src: /sub/page1.md "background": "https://sli.dev/demo-cover.png#14", "layout": "cover", }, + "frontmatterRaw": "layout: cover +", "importChain": [ { "content": "", @@ -1846,6 +1882,7 @@ layout: cover "frontmatter": { "background": "https://sli.dev/demo-cover.png#14", }, + "frontmatterRaw": undefined, "importChain": [ { "content": "", @@ -2153,6 +2190,8 @@ src: ../sub/pages3-4.md "background": "https://sli.dev/demo-cover.png#14", "layout": "cover", }, + "frontmatterRaw": "layout: cover +", "importChain": [ { "content": "", @@ -2470,6 +2509,7 @@ layout: cover $x+2$", "frontmatter": {}, + "frontmatterRaw": undefined, "importChain": undefined, "index": 8, "level": 1,