@brett_lamy/docstream-editor 1.2.0 → 1.2.2
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/README.md +3 -2
- package/package.json +4 -2
- package/src/editor/convert.ts +13 -0
- package/src/editor/extensions.ts +15 -0
- package/src/editor/nodes.tsx +32 -0
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,8 @@ TipTap editor components for Docstream GitBook-style markdown documents.
|
|
|
15
15
|
- Preserves Docstream/GitBook block semantics when converting back to markdown.
|
|
16
16
|
- Copy a docs page (docstream's "Copy page") and paste it in: the editor shows the same page and
|
|
17
17
|
serializes it back byte-for-byte — demos with their inline files, install command boxes, titled
|
|
18
|
-
tab sets and all.
|
|
18
|
+
tab sets and all. Inline code may combine with links, bold, italic and strike
|
|
19
|
+
(``[`@brett_lamy/ui`](url)``, ``**bold `x` text**``); the editor's `GbCode` mark allows it.
|
|
19
20
|
- Preserves mounted source-file/export provenance and can write edits to the real file through Vite.
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
@@ -24,7 +25,7 @@ TipTap editor components for Docstream GitBook-style markdown documents.
|
|
|
24
25
|
npm install @brett_lamy/docstream-editor @brett_lamy/docstream react
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
React
|
|
28
|
+
React and the TipTap packages (including `@tiptap/extension-code`) are peer dependencies. `@brett_lamy/docstream` is a runtime dependency because the editor uses the same parser, serializer, OpenAPI rendering, and asset helpers.
|
|
28
29
|
|
|
29
30
|
## Basic Setup
|
|
30
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brett_lamy/docstream-editor",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "TipTap editor for Docstream GitBook-style markdown documents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -34,12 +34,13 @@
|
|
|
34
34
|
"./styles.css": "./src/styles.css"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@brett_lamy/docstream": "1.2.
|
|
37
|
+
"@brett_lamy/docstream": "1.2.2",
|
|
38
38
|
"gpu-lexer": "0.0.2",
|
|
39
39
|
"lucide-react": "^1.17.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@tiptap/core": "^3.26.1",
|
|
43
|
+
"@tiptap/extension-code": "^3.26.1",
|
|
43
44
|
"@tiptap/extension-code-block": "^3.26.1",
|
|
44
45
|
"@tiptap/extension-list": "^3.26.1",
|
|
45
46
|
"@tiptap/extension-placeholder": "^3.26.1",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@agent-wasm/core": "^0.4.0",
|
|
59
60
|
"@tiptap/core": "^3.26.1",
|
|
61
|
+
"@tiptap/extension-code": "^3.26.1",
|
|
60
62
|
"@tiptap/extension-code-block": "^3.26.1",
|
|
61
63
|
"@tiptap/extension-list": "^3.26.1",
|
|
62
64
|
"@tiptap/extension-placeholder": "^3.26.1",
|
package/src/editor/convert.ts
CHANGED
|
@@ -99,6 +99,7 @@ function blockToPM(b: Block): PMNode {
|
|
|
99
99
|
language: b.language,
|
|
100
100
|
title: b.title,
|
|
101
101
|
lineNumbers: b.lineNumbers,
|
|
102
|
+
lineNumbersExplicit: b.lineNumbersExplicit ? true : null,
|
|
102
103
|
live: !!b.live,
|
|
103
104
|
entry: b.entry ?? null,
|
|
104
105
|
collapsedCodeLines: b.collapsedCodeLines ?? null,
|
|
@@ -188,6 +189,11 @@ function blockToPM(b: Block): PMNode {
|
|
|
188
189
|
variants: b.variants?.length ? b.variants.map((v) => ({ id: v.id, label: v.label })) : null,
|
|
189
190
|
viewport: b.viewport ?? null,
|
|
190
191
|
entry: b.entry ?? null,
|
|
192
|
+
status: b.status ?? null,
|
|
193
|
+
bleed: b.bleed ?? null,
|
|
194
|
+
className: b.className ?? null,
|
|
195
|
+
surface: b.surface ?? null,
|
|
196
|
+
variantsWidth: b.variantsWidth ?? null,
|
|
191
197
|
files: b.files?.length ? b.files.map((f) => ({ ...f })) : null,
|
|
192
198
|
},
|
|
193
199
|
}
|
|
@@ -344,6 +350,7 @@ function pmToBlock(n: PMNode): Block | null {
|
|
|
344
350
|
language: (n.attrs?.language as string) || null,
|
|
345
351
|
title: (n.attrs?.title as string) || null,
|
|
346
352
|
lineNumbers: !!n.attrs?.lineNumbers,
|
|
353
|
+
...(n.attrs?.lineNumbersExplicit ? { lineNumbersExplicit: true } : {}),
|
|
347
354
|
live: !!n.attrs?.live,
|
|
348
355
|
entry: (n.attrs?.entry as string) || null,
|
|
349
356
|
...(collapsed ? { collapsedCodeLines: collapsed } : {}),
|
|
@@ -504,6 +511,7 @@ export function pmToDemo(a: Record<string, unknown>): DemoNode {
|
|
|
504
511
|
const layout = String(a.layout ?? "")
|
|
505
512
|
const files = pmDemoFiles(a.files)
|
|
506
513
|
const viewport = String(a.viewport ?? "")
|
|
514
|
+
const bleed = a.bleed === true || a.bleed === "true" ? true : a.bleed === false || a.bleed === "false" ? false : undefined
|
|
507
515
|
return {
|
|
508
516
|
type: "demo",
|
|
509
517
|
src: String(a.src ?? ""),
|
|
@@ -514,6 +522,11 @@ export function pmToDemo(a: Record<string, unknown>): DemoNode {
|
|
|
514
522
|
...(variants.length ? { variants } : {}),
|
|
515
523
|
...(DEMO_VIEWPORTS.includes(viewport) ? { viewport: viewport as DemoViewport } : {}),
|
|
516
524
|
...(a.entry ? { entry: String(a.entry) } : {}),
|
|
525
|
+
...(a.status ? { status: String(a.status) } : {}),
|
|
526
|
+
...(bleed === undefined ? {} : { bleed }),
|
|
527
|
+
...(a.className ? { className: String(a.className) } : {}),
|
|
528
|
+
...(a.surface ? { surface: String(a.surface) } : {}),
|
|
529
|
+
...(a.variantsWidth ? { variantsWidth: String(a.variantsWidth) } : {}),
|
|
517
530
|
...(files.length ? { files } : {}),
|
|
518
531
|
}
|
|
519
532
|
}
|
package/src/editor/extensions.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import StarterKit from "@tiptap/starter-kit"
|
|
2
|
+
import Code from "@tiptap/extension-code"
|
|
2
3
|
import Placeholder from "@tiptap/extension-placeholder"
|
|
3
4
|
import { TaskItem, TaskList } from "@tiptap/extension-list"
|
|
4
5
|
import { Table, TableCell, TableHeader, TableRow } from "@tiptap/extension-table"
|
|
@@ -15,6 +16,18 @@ export const GbTable = Table.extend({
|
|
|
15
16
|
},
|
|
16
17
|
})
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Inline code that coexists with link, bold, italic and strike. TipTap's code mark
|
|
21
|
+
* excludes every other mark (`excludes: "_"`), so Markdown such as
|
|
22
|
+
* ``[`@brett_lamy/ui`](url)`` or ``**bold `x` text**`` — which docstream parses into one
|
|
23
|
+
* text run with both marks — failed the schema ("Invalid collection of marks for node
|
|
24
|
+
* text: link,code") and dropped the whole paste. Docstream serializes every combination
|
|
25
|
+
* back byte-for-byte.
|
|
26
|
+
*/
|
|
27
|
+
export const GbCode = Code.extend({
|
|
28
|
+
excludes: "",
|
|
29
|
+
})
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* `inner` on the link mark: the link sits inside its emphasis (`**[x](url)**`)
|
|
20
33
|
* rather than around it (`[**x**](url)`). Kept so pasted Markdown round-trips
|
|
@@ -84,8 +97,10 @@ export function createGitbookExtensions(options: GitbookExtensionOptions = {}):
|
|
|
84
97
|
const list: AnyExtension[] = [
|
|
85
98
|
StarterKit.configure({
|
|
86
99
|
codeBlock: false,
|
|
100
|
+
code: false,
|
|
87
101
|
...(disableHistory ? { undoRedo: false } : {}),
|
|
88
102
|
}),
|
|
103
|
+
GbCode,
|
|
89
104
|
GbCodeBlock,
|
|
90
105
|
TaskList,
|
|
91
106
|
TaskItem.configure({ nested: true }),
|
package/src/editor/nodes.tsx
CHANGED
|
@@ -707,6 +707,16 @@ function DemoFilesEditor({ files, entry, onChange }: {
|
|
|
707
707
|
)
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
+
/** A string attribute carried as `data-<name>` in the editor's HTML (clipboard between editors). */
|
|
711
|
+
function dataAttr(key: string, name = key) {
|
|
712
|
+
return {
|
|
713
|
+
default: null,
|
|
714
|
+
parseHTML: (element: HTMLElement) => element.getAttribute(`data-${name}`),
|
|
715
|
+
renderHTML: (attributes: Record<string, unknown>) =>
|
|
716
|
+
attributes[key] ? { [`data-${name}`]: String(attributes[key]) } : {},
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
710
720
|
function DemoView({ node, updateAttributes, editor }: NodeViewProps) {
|
|
711
721
|
const editable = editor.isEditable
|
|
712
722
|
const { demoResolver, demoRuntime, demoDependencies } = useEditorRuntime()
|
|
@@ -769,6 +779,11 @@ function DemoView({ node, updateAttributes, editor }: NodeViewProps) {
|
|
|
769
779
|
viewport={demo.viewport}
|
|
770
780
|
files={files}
|
|
771
781
|
entry={entry}
|
|
782
|
+
status={demo.status}
|
|
783
|
+
bleed={demo.bleed}
|
|
784
|
+
canvasClassName={demo.className}
|
|
785
|
+
surface={demo.surface}
|
|
786
|
+
variantsWidth={demo.variantsWidth}
|
|
772
787
|
{...(demoRuntime ? { runtime: demoRuntime } : {})}
|
|
773
788
|
{...(demoDependencies ? { dependencies: demoDependencies } : {})}
|
|
774
789
|
/>
|
|
@@ -796,6 +811,21 @@ export const GbDemo = Node.create({
|
|
|
796
811
|
variants: { default: null, rendered: false },
|
|
797
812
|
viewport: { default: null },
|
|
798
813
|
entry: { default: null },
|
|
814
|
+
// Look (`status="Live" bleed="true" className="…" surface="…" variantsWidth="220"`),
|
|
815
|
+
// kept verbatim; the tag's values win over the resolver's meta.json.
|
|
816
|
+
status: dataAttr("status"),
|
|
817
|
+
bleed: {
|
|
818
|
+
default: null,
|
|
819
|
+
parseHTML: (element: HTMLElement) => {
|
|
820
|
+
const raw = element.getAttribute("data-bleed")
|
|
821
|
+
return raw === null ? null : raw !== "false"
|
|
822
|
+
},
|
|
823
|
+
renderHTML: (attributes: Record<string, unknown>) =>
|
|
824
|
+
typeof attributes.bleed === "boolean" ? { "data-bleed": String(attributes.bleed) } : {},
|
|
825
|
+
},
|
|
826
|
+
className: dataAttr("className", "canvas-class"),
|
|
827
|
+
surface: dataAttr("surface"),
|
|
828
|
+
variantsWidth: dataAttr("variantsWidth", "variants-width"),
|
|
799
829
|
// `{ path, content, language? }[]` — the files between `{% demo %}` and `{% enddemo %}`.
|
|
800
830
|
files: {
|
|
801
831
|
default: null,
|
|
@@ -1115,6 +1145,8 @@ export const GbCodeBlock = CodeBlock.extend({
|
|
|
1115
1145
|
...this.parent?.(),
|
|
1116
1146
|
title: { default: null },
|
|
1117
1147
|
lineNumbers: { default: false },
|
|
1148
|
+
// The source wrote `lineNumbers="…"` explicitly: kept so the fence round-trips byte-for-byte.
|
|
1149
|
+
lineNumbersExplicit: { default: null, rendered: false },
|
|
1118
1150
|
live: { default: false },
|
|
1119
1151
|
entry: { default: null },
|
|
1120
1152
|
collapsedCodeLines: { default: null, rendered: false },
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type { PMNode } from "./editor/convert"
|
|
|
5
5
|
|
|
6
6
|
// Building blocks for composing a custom editor (e.g. with Yjs collaboration
|
|
7
7
|
// or a bespoke slash menu) instead of the batteries-included GitbookEditor.
|
|
8
|
-
export { createGitbookExtensions, getGitbookSchema, GbTable, GbLinkInner } from "./editor/extensions"
|
|
8
|
+
export { createGitbookExtensions, getGitbookSchema, GbCode, GbTable, GbLinkInner } from "./editor/extensions"
|
|
9
9
|
export type { GitbookExtensionOptions, ReferenceSources } from "./editor/extensions"
|
|
10
10
|
export { gitbookNodes, GbCodeBlock, GbCommand, GbDemo, GbReference } from "./editor/nodes"
|
|
11
11
|
export { GbAttachment, formatBytes } from "./editor/attachments"
|