@brett_lamy/docstream-editor 1.2.1 → 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 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 is a peer dependency. `@brett_lamy/docstream` is a runtime dependency because the editor uses the same parser, serializer, OpenAPI rendering, and asset helpers.
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.1",
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.1",
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",
@@ -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
  }
@@ -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 }),
@@ -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"