@abraca/convert 2.11.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abracadabra-convert.cjs +199 -0
- package/dist/abracadabra-convert.cjs.map +1 -1
- package/dist/abracadabra-convert.esm.js +190 -1
- package/dist/abracadabra-convert.esm.js.map +1 -1
- package/dist/index.d.ts +56 -1
- package/package.json +1 -1
- package/src/code.ts +208 -0
- package/src/file-blocks/manifest.ts +5 -0
- package/src/file-blocks/paths.ts +17 -0
- package/src/index.ts +15 -0
- package/src/markdown-to-yjs.ts +8 -0
- package/src/types.ts +5 -0
- package/src/yjs-to-markdown.ts +5 -0
package/dist/index.d.ts
CHANGED
|
@@ -60,6 +60,9 @@ interface DocPageMeta extends Record<string, unknown> {
|
|
|
60
60
|
spModelDocId?: string;
|
|
61
61
|
slidesTransition?: 'none' | 'fade' | 'slide';
|
|
62
62
|
slidesTheme?: 'dark' | 'light';
|
|
63
|
+
language?: string;
|
|
64
|
+
fileExtension?: string;
|
|
65
|
+
codeTheme?: string;
|
|
63
66
|
__schemaVersion?: number;
|
|
64
67
|
}
|
|
65
68
|
//#endregion
|
|
@@ -118,6 +121,49 @@ declare function populateYDocFromHtml(fragment: Y.XmlFragment, html: string, fal
|
|
|
118
121
|
*/
|
|
119
122
|
declare function appendHtmlToFragment(fragment: Y.XmlFragment, html: string): void;
|
|
120
123
|
//#endregion
|
|
124
|
+
//#region packages/convert/src/code.d.ts
|
|
125
|
+
/** Y.Text key holding a code document's content. */
|
|
126
|
+
declare const CODE_TEXT_KEY = "code";
|
|
127
|
+
/** Read a code document's raw text. Empty string when absent. */
|
|
128
|
+
declare function readCodeText(doc: Y.Doc): string;
|
|
129
|
+
/**
|
|
130
|
+
* Replace a code document's full text in a single transaction. `origin`
|
|
131
|
+
* is forwarded to `transact` so callers (e.g. fs-sync) can tag the write
|
|
132
|
+
* and skip their own observers.
|
|
133
|
+
*/
|
|
134
|
+
declare function writeCodeText(doc: Y.Doc, content: string, origin?: unknown): void;
|
|
135
|
+
/** Normalize an extension: strip leading dot(s), lowercase. */
|
|
136
|
+
declare function normalizeExtension(ext: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Whether a file extension should import as a `code` page type. Note
|
|
139
|
+
* `md`/`markdown` are intentionally NOT code extensions here — fs-sync
|
|
140
|
+
* treats `.md` as the prose/envelope format, so it's excluded from the
|
|
141
|
+
* code-import gate even though it has a language mapping for in-editor use.
|
|
142
|
+
*/
|
|
143
|
+
declare function isCodeExtension(ext: string): boolean;
|
|
144
|
+
/** CodeMirror language id for an extension, or undefined when unknown. */
|
|
145
|
+
declare function extToLanguage(ext: string): string | undefined;
|
|
146
|
+
/** Preferred on-disk extension (no dot) for a language, or undefined. */
|
|
147
|
+
declare function languageToExtension(language: string): string | undefined;
|
|
148
|
+
/**
|
|
149
|
+
* Derive `{ language, fileExtension }` from a filename for code import.
|
|
150
|
+
* Returns undefined when the extension is not a recognized code extension
|
|
151
|
+
* (so callers fall back to the prose/doc path).
|
|
152
|
+
*/
|
|
153
|
+
declare function inferCodeMetaFromFilename(filename: string): {
|
|
154
|
+
language: string;
|
|
155
|
+
fileExtension: string;
|
|
156
|
+
} | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* Choose the on-disk extension for a code doc on export. Prefers an
|
|
159
|
+
* explicit `fileExtension`, then the language's primary extension, then
|
|
160
|
+
* `txt`.
|
|
161
|
+
*/
|
|
162
|
+
declare function codeFileExtension(meta?: {
|
|
163
|
+
fileExtension?: string;
|
|
164
|
+
language?: string;
|
|
165
|
+
}): string;
|
|
166
|
+
//#endregion
|
|
121
167
|
//#region packages/convert/src/diff.d.ts
|
|
122
168
|
type JsonAttr = string | number | boolean | null | JsonAttr[] | {
|
|
123
169
|
[key: string]: JsonAttr;
|
|
@@ -277,6 +323,8 @@ interface ManifestEntry {
|
|
|
277
323
|
contentHash: string;
|
|
278
324
|
lastWrittenAt: number;
|
|
279
325
|
lastReadAt: number;
|
|
326
|
+
codePath?: string;
|
|
327
|
+
codeHash?: string;
|
|
280
328
|
uploads: UploadManifestEntry[];
|
|
281
329
|
}
|
|
282
330
|
interface FsSyncManifest {
|
|
@@ -329,6 +377,13 @@ declare function hasChildren(docId: string, treeData: Record<string, FsTreeEntry
|
|
|
329
377
|
* - Collision resolution via ~XXXX suffix
|
|
330
378
|
*/
|
|
331
379
|
declare function buildRelativePath(docId: string, treeData: Record<string, FsTreeEntry>, manifest: FsSyncManifest): string;
|
|
380
|
+
/**
|
|
381
|
+
* Companion code-file path for a `code` doc, derived from its `.md`
|
|
382
|
+
* envelope path by swapping the extension. e.g.
|
|
383
|
+
* `src/main.md` + `rs` -> `src/main.rs`. A code doc never has children,
|
|
384
|
+
* so the `_index.md` form is not expected here, but is handled defensively.
|
|
385
|
+
*/
|
|
386
|
+
declare function codeCompanionPath(mdRelativePath: string, fileExtension: string): string;
|
|
332
387
|
/**
|
|
333
388
|
* Get the directory portion of a relative path for a doc.
|
|
334
389
|
* For _index.md docs: returns the directory containing _index.md
|
|
@@ -353,4 +408,4 @@ declare function getTreeData(treeMap: any): Record<string, FsTreeEntry>;
|
|
|
353
408
|
*/
|
|
354
409
|
declare function nextOrder(treeData: Record<string, FsTreeEntry>, parentId: string | null): number;
|
|
355
410
|
//#endregion
|
|
356
|
-
export { type DocPageMeta, type FrontmatterResult, type FsAdapter, type FsSyncManifest, type FsTreeEntry, type JsonAttr, MARK_SPECS, MARK_SPEC_BY_DELIM, MARK_SPEC_BY_NAME, type ManifestEntry, type MarkAttrSpec, type MarkSpec, type MarkWireKind, type MetaValueType, NODE_SPECS, NODE_SPEC_BY_NAME, type NodeAttrSpec, type NodeSpec, type NodeWireKind, UNIVERSAL_META_KEYS, UNIVERSAL_META_KEY_NAMES, type UniversalMetaKey, type UploadManifestEntry, type YjsDiff, type YjsJsonElement, type YjsJsonNode, type YjsJsonText, type YjsTextRun, appendHtmlToFragment, buildAliasMap, buildRelativePath, buildReverseLookup, conflictsDir, createEmptyManifest, diffJson, diffYjs, filenameToLabel, fsFilenameToLabel, getDocDir, getFsAdapter, getTreeData, hasChildren, jsonToYFragment, labelToFilename, loadManifest, lookupByDocId, lookupByHash, lookupByPath, manifestDir, mdcTagOf, nextOrder, orphansDir, parseFrontmatter, populateYDocFromHtml, populateYDocFromMarkdown, removeEntry, resolveParentFromPath, saveManifest, setEntry, setFsAdapter, simpleHash, stringifyJsonNode, trashDir, yfragmentToJson, yjsToHtml, yjsToMarkdown, yjsToPlainText };
|
|
411
|
+
export { CODE_TEXT_KEY, type DocPageMeta, type FrontmatterResult, type FsAdapter, type FsSyncManifest, type FsTreeEntry, type JsonAttr, MARK_SPECS, MARK_SPEC_BY_DELIM, MARK_SPEC_BY_NAME, type ManifestEntry, type MarkAttrSpec, type MarkSpec, type MarkWireKind, type MetaValueType, NODE_SPECS, NODE_SPEC_BY_NAME, type NodeAttrSpec, type NodeSpec, type NodeWireKind, UNIVERSAL_META_KEYS, UNIVERSAL_META_KEY_NAMES, type UniversalMetaKey, type UploadManifestEntry, type YjsDiff, type YjsJsonElement, type YjsJsonNode, type YjsJsonText, type YjsTextRun, appendHtmlToFragment, buildAliasMap, buildRelativePath, buildReverseLookup, codeCompanionPath, codeFileExtension, conflictsDir, createEmptyManifest, diffJson, diffYjs, extToLanguage, filenameToLabel, fsFilenameToLabel, getDocDir, getFsAdapter, getTreeData, hasChildren, inferCodeMetaFromFilename, isCodeExtension, jsonToYFragment, labelToFilename, languageToExtension, loadManifest, lookupByDocId, lookupByHash, lookupByPath, manifestDir, mdcTagOf, nextOrder, normalizeExtension, orphansDir, parseFrontmatter, populateYDocFromHtml, populateYDocFromMarkdown, readCodeText, removeEntry, resolveParentFromPath, saveManifest, setEntry, setFsAdapter, simpleHash, stringifyJsonNode, trashDir, writeCodeText, yfragmentToJson, yjsToHtml, yjsToMarkdown, yjsToPlainText };
|
package/package.json
CHANGED
package/src/code.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Code page-type helpers for @abraca/convert.
|
|
2
|
+
//
|
|
3
|
+
// A `code` document stores its content in a plain `Y.Text` under the
|
|
4
|
+
// `code` key (edited via CodeMirror + y-codemirror.next), separate from
|
|
5
|
+
// the TipTap prose fragment that carries the `documentHeader`/
|
|
6
|
+
// `documentMeta` envelope. fs-sync writes that text out to a real code
|
|
7
|
+
// file (e.g. `main.rs`) alongside the `.md` envelope.
|
|
8
|
+
//
|
|
9
|
+
// This module owns the single source of truth for:
|
|
10
|
+
// - the `code` Y.Text key name,
|
|
11
|
+
// - reading/replacing that text,
|
|
12
|
+
// - the file-extension ↔ CodeMirror-language mapping used by both
|
|
13
|
+
// fs-sync directions and the renderer.
|
|
14
|
+
//
|
|
15
|
+
// `yjs` stays a type-only peer import — these helpers only call methods
|
|
16
|
+
// on the Y.Doc handed in by the caller, never construct Y types.
|
|
17
|
+
|
|
18
|
+
import type * as Y from 'yjs'
|
|
19
|
+
|
|
20
|
+
/** Y.Text key holding a code document's content. */
|
|
21
|
+
export const CODE_TEXT_KEY = 'code'
|
|
22
|
+
|
|
23
|
+
/** Read a code document's raw text. Empty string when absent. */
|
|
24
|
+
export function readCodeText(doc: Y.Doc): string {
|
|
25
|
+
return doc.getText(CODE_TEXT_KEY).toString()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Replace a code document's full text in a single transaction. `origin`
|
|
30
|
+
* is forwarded to `transact` so callers (e.g. fs-sync) can tag the write
|
|
31
|
+
* and skip their own observers.
|
|
32
|
+
*/
|
|
33
|
+
export function writeCodeText(doc: Y.Doc, content: string, origin?: unknown): void {
|
|
34
|
+
const ytext = doc.getText(CODE_TEXT_KEY)
|
|
35
|
+
doc.transact(() => {
|
|
36
|
+
if (ytext.length > 0) ytext.delete(0, ytext.length)
|
|
37
|
+
if (content.length > 0) ytext.insert(0, content)
|
|
38
|
+
}, origin)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Extension ↔ language ─────────────────────────────────────────────────────
|
|
42
|
+
//
|
|
43
|
+
// Keys are lowercase extensions without the leading dot; values are the
|
|
44
|
+
// canonical CodeMirror language id the renderer resolves against
|
|
45
|
+
// `@codemirror/language-data`. The first extension listed for a language
|
|
46
|
+
// in EXT_BY_LANGUAGE is the one fs-sync uses when writing the file.
|
|
47
|
+
|
|
48
|
+
const LANGUAGE_BY_EXT: Readonly<Record<string, string>> = {
|
|
49
|
+
// JS/TS family
|
|
50
|
+
js: 'javascript',
|
|
51
|
+
mjs: 'javascript',
|
|
52
|
+
cjs: 'javascript',
|
|
53
|
+
jsx: 'jsx',
|
|
54
|
+
ts: 'typescript',
|
|
55
|
+
mts: 'typescript',
|
|
56
|
+
cts: 'typescript',
|
|
57
|
+
tsx: 'tsx',
|
|
58
|
+
// Web
|
|
59
|
+
html: 'html',
|
|
60
|
+
htm: 'html',
|
|
61
|
+
css: 'css',
|
|
62
|
+
scss: 'scss',
|
|
63
|
+
less: 'less',
|
|
64
|
+
vue: 'vue',
|
|
65
|
+
svelte: 'svelte',
|
|
66
|
+
// Data / config
|
|
67
|
+
json: 'json',
|
|
68
|
+
jsonc: 'json',
|
|
69
|
+
json5: 'json',
|
|
70
|
+
yaml: 'yaml',
|
|
71
|
+
yml: 'yaml',
|
|
72
|
+
toml: 'toml',
|
|
73
|
+
xml: 'xml',
|
|
74
|
+
ini: 'properties',
|
|
75
|
+
env: 'properties',
|
|
76
|
+
// Systems
|
|
77
|
+
rs: 'rust',
|
|
78
|
+
go: 'go',
|
|
79
|
+
c: 'c',
|
|
80
|
+
h: 'c',
|
|
81
|
+
cpp: 'cpp',
|
|
82
|
+
cc: 'cpp',
|
|
83
|
+
cxx: 'cpp',
|
|
84
|
+
hpp: 'cpp',
|
|
85
|
+
cs: 'csharp',
|
|
86
|
+
java: 'java',
|
|
87
|
+
kt: 'kotlin',
|
|
88
|
+
kts: 'kotlin',
|
|
89
|
+
swift: 'swift',
|
|
90
|
+
m: 'objective-c',
|
|
91
|
+
mm: 'objective-c',
|
|
92
|
+
// Scripting
|
|
93
|
+
py: 'python',
|
|
94
|
+
pyi: 'python',
|
|
95
|
+
rb: 'ruby',
|
|
96
|
+
php: 'php',
|
|
97
|
+
pl: 'perl',
|
|
98
|
+
lua: 'lua',
|
|
99
|
+
r: 'r',
|
|
100
|
+
// Shell
|
|
101
|
+
sh: 'shell',
|
|
102
|
+
bash: 'shell',
|
|
103
|
+
zsh: 'shell',
|
|
104
|
+
fish: 'shell',
|
|
105
|
+
// Query / misc
|
|
106
|
+
sql: 'sql',
|
|
107
|
+
graphql: 'graphql',
|
|
108
|
+
gql: 'graphql',
|
|
109
|
+
dockerfile: 'dockerfile',
|
|
110
|
+
// Markup that is genuinely "code" in a vault context
|
|
111
|
+
md: 'markdown',
|
|
112
|
+
markdown: 'markdown',
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Preferred on-disk extension per language (reverse of LANGUAGE_BY_EXT). */
|
|
116
|
+
const EXT_BY_LANGUAGE: Readonly<Record<string, string>> = {
|
|
117
|
+
javascript: 'js',
|
|
118
|
+
jsx: 'jsx',
|
|
119
|
+
typescript: 'ts',
|
|
120
|
+
tsx: 'tsx',
|
|
121
|
+
html: 'html',
|
|
122
|
+
css: 'css',
|
|
123
|
+
scss: 'scss',
|
|
124
|
+
less: 'less',
|
|
125
|
+
vue: 'vue',
|
|
126
|
+
svelte: 'svelte',
|
|
127
|
+
json: 'json',
|
|
128
|
+
yaml: 'yaml',
|
|
129
|
+
toml: 'toml',
|
|
130
|
+
xml: 'xml',
|
|
131
|
+
properties: 'ini',
|
|
132
|
+
rust: 'rs',
|
|
133
|
+
go: 'go',
|
|
134
|
+
c: 'c',
|
|
135
|
+
cpp: 'cpp',
|
|
136
|
+
csharp: 'cs',
|
|
137
|
+
java: 'java',
|
|
138
|
+
kotlin: 'kt',
|
|
139
|
+
swift: 'swift',
|
|
140
|
+
'objective-c': 'm',
|
|
141
|
+
python: 'py',
|
|
142
|
+
ruby: 'rb',
|
|
143
|
+
php: 'php',
|
|
144
|
+
perl: 'pl',
|
|
145
|
+
lua: 'lua',
|
|
146
|
+
r: 'r',
|
|
147
|
+
shell: 'sh',
|
|
148
|
+
sql: 'sql',
|
|
149
|
+
graphql: 'graphql',
|
|
150
|
+
dockerfile: 'dockerfile',
|
|
151
|
+
markdown: 'md',
|
|
152
|
+
plain: 'txt',
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Normalize an extension: strip leading dot(s), lowercase. */
|
|
156
|
+
export function normalizeExtension(ext: string): string {
|
|
157
|
+
return ext.replace(/^\.+/, '').toLowerCase()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Whether a file extension should import as a `code` page type. Note
|
|
162
|
+
* `md`/`markdown` are intentionally NOT code extensions here — fs-sync
|
|
163
|
+
* treats `.md` as the prose/envelope format, so it's excluded from the
|
|
164
|
+
* code-import gate even though it has a language mapping for in-editor use.
|
|
165
|
+
*/
|
|
166
|
+
export function isCodeExtension(ext: string): boolean {
|
|
167
|
+
const e = normalizeExtension(ext)
|
|
168
|
+
if (e === 'md' || e === 'markdown') return false
|
|
169
|
+
return e in LANGUAGE_BY_EXT
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** CodeMirror language id for an extension, or undefined when unknown. */
|
|
173
|
+
export function extToLanguage(ext: string): string | undefined {
|
|
174
|
+
return LANGUAGE_BY_EXT[normalizeExtension(ext)]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Preferred on-disk extension (no dot) for a language, or undefined. */
|
|
178
|
+
export function languageToExtension(language: string): string | undefined {
|
|
179
|
+
return EXT_BY_LANGUAGE[language.toLowerCase()]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Derive `{ language, fileExtension }` from a filename for code import.
|
|
184
|
+
* Returns undefined when the extension is not a recognized code extension
|
|
185
|
+
* (so callers fall back to the prose/doc path).
|
|
186
|
+
*/
|
|
187
|
+
export function inferCodeMetaFromFilename(
|
|
188
|
+
filename: string,
|
|
189
|
+
): { language: string, fileExtension: string } | undefined {
|
|
190
|
+
const dot = filename.lastIndexOf('.')
|
|
191
|
+
if (dot < 0) return undefined
|
|
192
|
+
const ext = normalizeExtension(filename.slice(dot + 1))
|
|
193
|
+
if (!isCodeExtension(ext)) return undefined
|
|
194
|
+
const language = LANGUAGE_BY_EXT[ext]
|
|
195
|
+
if (!language) return undefined
|
|
196
|
+
return { language, fileExtension: ext }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Choose the on-disk extension for a code doc on export. Prefers an
|
|
201
|
+
* explicit `fileExtension`, then the language's primary extension, then
|
|
202
|
+
* `txt`.
|
|
203
|
+
*/
|
|
204
|
+
export function codeFileExtension(meta?: { fileExtension?: string, language?: string }): string {
|
|
205
|
+
if (meta?.fileExtension) return normalizeExtension(meta.fileExtension)
|
|
206
|
+
if (meta?.language) return languageToExtension(meta.language) ?? 'txt'
|
|
207
|
+
return 'txt'
|
|
208
|
+
}
|
|
@@ -58,6 +58,11 @@ export interface ManifestEntry {
|
|
|
58
58
|
contentHash: string // hash of last-written/read markdown
|
|
59
59
|
lastWrittenAt: number
|
|
60
60
|
lastReadAt: number
|
|
61
|
+
// Code page type: the companion code file written alongside the `.md`
|
|
62
|
+
// envelope (e.g. "src/main.rs"), and the hash of its last-synced text.
|
|
63
|
+
// Present only for `code` docs.
|
|
64
|
+
codePath?: string
|
|
65
|
+
codeHash?: string
|
|
61
66
|
uploads: UploadManifestEntry[]
|
|
62
67
|
}
|
|
63
68
|
|
package/src/file-blocks/paths.ts
CHANGED
|
@@ -126,6 +126,23 @@ export function buildRelativePath(
|
|
|
126
126
|
return `${parentPath}${parentPath ? '/' : ''}${resolvedFilename}.md`
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Companion code-file path for a `code` doc, derived from its `.md`
|
|
131
|
+
* envelope path by swapping the extension. e.g.
|
|
132
|
+
* `src/main.md` + `rs` -> `src/main.rs`. A code doc never has children,
|
|
133
|
+
* so the `_index.md` form is not expected here, but is handled defensively.
|
|
134
|
+
*/
|
|
135
|
+
export function codeCompanionPath(mdRelativePath: string, fileExtension: string): string {
|
|
136
|
+
const ext = fileExtension.replace(/^\.+/, '').toLowerCase() || 'txt'
|
|
137
|
+
if (mdRelativePath.endsWith('/_index.md')) {
|
|
138
|
+
return `${mdRelativePath.slice(0, -'/_index.md'.length)}/_index.${ext}`
|
|
139
|
+
}
|
|
140
|
+
if (mdRelativePath.endsWith('.md')) {
|
|
141
|
+
return `${mdRelativePath.slice(0, -'.md'.length)}.${ext}`
|
|
142
|
+
}
|
|
143
|
+
return `${mdRelativePath}.${ext}`
|
|
144
|
+
}
|
|
145
|
+
|
|
129
146
|
/**
|
|
130
147
|
* Get the directory portion of a relative path for a doc.
|
|
131
148
|
* For _index.md docs: returns the directory containing _index.md
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,20 @@ export {
|
|
|
24
24
|
|
|
25
25
|
export type { DocPageMeta } from './types.ts'
|
|
26
26
|
|
|
27
|
+
// Code page-type helpers — Y.Text('code') accessors + the canonical
|
|
28
|
+
// extension ↔ CodeMirror-language mapping shared by fs-sync and renderers.
|
|
29
|
+
export {
|
|
30
|
+
CODE_TEXT_KEY,
|
|
31
|
+
readCodeText,
|
|
32
|
+
writeCodeText,
|
|
33
|
+
normalizeExtension,
|
|
34
|
+
isCodeExtension,
|
|
35
|
+
extToLanguage,
|
|
36
|
+
languageToExtension,
|
|
37
|
+
inferCodeMetaFromFilename,
|
|
38
|
+
codeFileExtension,
|
|
39
|
+
} from './code.ts'
|
|
40
|
+
|
|
27
41
|
// Structural Y.XmlFragment ↔ JSON converter + diff. Used by the
|
|
28
42
|
// integration test suite and available to consumers who want to
|
|
29
43
|
// author golden fixtures or do their own assertions.
|
|
@@ -94,6 +108,7 @@ export {
|
|
|
94
108
|
fsFilenameToLabel,
|
|
95
109
|
hasChildren,
|
|
96
110
|
buildRelativePath,
|
|
111
|
+
codeCompanionPath,
|
|
97
112
|
getDocDir,
|
|
98
113
|
resolveParentFromPath,
|
|
99
114
|
simpleHash,
|
package/src/markdown-to-yjs.ts
CHANGED
|
@@ -146,6 +146,14 @@ export function parseFrontmatter(markdown: string): FrontmatterResult {
|
|
|
146
146
|
const url = getStr(['url'])
|
|
147
147
|
if (url) meta.url = url
|
|
148
148
|
|
|
149
|
+
// Code page type meta.
|
|
150
|
+
const language = getStr(['language'])
|
|
151
|
+
if (language) meta.language = language
|
|
152
|
+
const fileExtension = getStr(['fileExtension'])
|
|
153
|
+
if (fileExtension) meta.fileExtension = fileExtension
|
|
154
|
+
const codeTheme = getStr(['codeTheme'])
|
|
155
|
+
if (codeTheme) meta.codeTheme = codeTheme
|
|
156
|
+
|
|
149
157
|
const ratingRaw = getStr(['rating'])
|
|
150
158
|
if (ratingRaw !== undefined) {
|
|
151
159
|
const n = Number(ratingRaw)
|
package/src/types.ts
CHANGED
|
@@ -84,6 +84,11 @@ export interface DocPageMeta extends Record<string, unknown> {
|
|
|
84
84
|
slidesTransition?: 'none' | 'fade' | 'slide'
|
|
85
85
|
slidesTheme?: 'dark' | 'light'
|
|
86
86
|
|
|
87
|
+
// Code page type
|
|
88
|
+
language?: string
|
|
89
|
+
fileExtension?: string
|
|
90
|
+
codeTheme?: string
|
|
91
|
+
|
|
87
92
|
// Schema migration version
|
|
88
93
|
__schemaVersion?: number
|
|
89
94
|
}
|
package/src/yjs-to-markdown.ts
CHANGED
|
@@ -503,6 +503,11 @@ function generateFrontmatter(label: string | undefined, meta?: DocPageMeta, type
|
|
|
503
503
|
}
|
|
504
504
|
|
|
505
505
|
if (meta.checked !== undefined) lines.push(`checked: ${meta.checked}`)
|
|
506
|
+
// Code page type: syntax-highlight language + on-disk extension. These
|
|
507
|
+
// round-trip the `.md` envelope so the companion code file can be re-derived.
|
|
508
|
+
if (meta.language) lines.push(`language: ${yamlScalar(meta.language)}`)
|
|
509
|
+
if (meta.fileExtension) lines.push(`fileExtension: ${yamlScalar(meta.fileExtension)}`)
|
|
510
|
+
if (meta.codeTheme) lines.push(`codeTheme: ${yamlScalar(meta.codeTheme)}`)
|
|
506
511
|
if (meta.dateStart) lines.push(`dateStart: "${escapeYaml(meta.dateStart)}"`)
|
|
507
512
|
if (meta.dateEnd) lines.push(`dateEnd: "${escapeYaml(meta.dateEnd)}"`)
|
|
508
513
|
if (meta.subtitle) lines.push(`subtitle: "${escapeYaml(meta.subtitle)}"`)
|