@brett_lamy/docstream 1.3.0 → 1.5.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.
Files changed (228) hide show
  1. package/README.md +79 -5
  2. package/dist/assets.d.ts +3 -0
  3. package/dist/assets.d.ts.map +1 -0
  4. package/dist/assets.js +20 -0
  5. package/dist/assets.js.map +1 -0
  6. package/dist/demo/DemoViewer.d.ts +78 -0
  7. package/dist/demo/DemoViewer.d.ts.map +1 -0
  8. package/dist/demo/DemoViewer.js +467 -0
  9. package/dist/demo/DemoViewer.js.map +1 -0
  10. package/dist/demo/clipboard.d.ts +70 -0
  11. package/dist/demo/clipboard.d.ts.map +1 -0
  12. package/dist/demo/clipboard.js +187 -0
  13. package/dist/demo/clipboard.js.map +1 -0
  14. package/dist/demo/context.d.ts +20 -0
  15. package/dist/demo/context.d.ts.map +1 -0
  16. package/dist/demo/context.js +31 -0
  17. package/dist/demo/context.js.map +1 -0
  18. package/dist/demo/copy-demo.d.ts +23 -0
  19. package/dist/demo/copy-demo.d.ts.map +1 -0
  20. package/dist/demo/copy-demo.js +81 -0
  21. package/dist/demo/copy-demo.js.map +1 -0
  22. package/dist/demo/glob.d.ts +60 -0
  23. package/dist/demo/glob.d.ts.map +1 -0
  24. package/dist/demo/glob.js +158 -0
  25. package/dist/demo/glob.js.map +1 -0
  26. package/dist/demo/index.d.ts +16 -0
  27. package/dist/demo/index.d.ts.map +1 -0
  28. package/dist/demo/index.js +9 -0
  29. package/dist/demo/index.js.map +1 -0
  30. package/dist/demo/markdown.d.ts +60 -0
  31. package/dist/demo/markdown.d.ts.map +1 -0
  32. package/dist/demo/markdown.js +181 -0
  33. package/dist/demo/markdown.js.map +1 -0
  34. package/dist/demo/surface.d.ts +11 -0
  35. package/dist/demo/surface.d.ts.map +1 -0
  36. package/dist/demo/surface.js +69 -0
  37. package/dist/demo/surface.js.map +1 -0
  38. package/dist/demo/types.d.ts +91 -0
  39. package/dist/demo/types.d.ts.map +1 -0
  40. package/dist/demo/types.js +2 -0
  41. package/dist/demo/types.js.map +1 -0
  42. package/dist/demo/vfs.d.ts +51 -0
  43. package/dist/demo/vfs.d.ts.map +1 -0
  44. package/dist/demo/vfs.js +156 -0
  45. package/dist/demo/vfs.js.map +1 -0
  46. package/dist/docs/CollapsibleCode.d.ts +14 -0
  47. package/dist/docs/CollapsibleCode.d.ts.map +1 -0
  48. package/dist/docs/CollapsibleCode.js +19 -0
  49. package/dist/docs/CollapsibleCode.js.map +1 -0
  50. package/dist/docs/DocsRenderer.d.ts +55 -0
  51. package/dist/docs/DocsRenderer.d.ts.map +1 -0
  52. package/dist/docs/DocsRenderer.js +279 -0
  53. package/dist/docs/DocsRenderer.js.map +1 -0
  54. package/dist/docs/HighlightedCode.d.ts +6 -0
  55. package/dist/docs/HighlightedCode.d.ts.map +1 -0
  56. package/dist/docs/HighlightedCode.js +40 -0
  57. package/dist/docs/HighlightedCode.js.map +1 -0
  58. package/dist/docs/Mermaid.d.ts +7 -0
  59. package/dist/docs/Mermaid.d.ts.map +1 -0
  60. package/dist/docs/Mermaid.js +39 -0
  61. package/dist/docs/Mermaid.js.map +1 -0
  62. package/dist/docs/PageActions.d.ts +33 -0
  63. package/dist/docs/PageActions.d.ts.map +1 -0
  64. package/dist/docs/PageActions.js +115 -0
  65. package/dist/docs/PageActions.js.map +1 -0
  66. package/dist/docs/controls.d.ts +29 -0
  67. package/dist/docs/controls.d.ts.map +1 -0
  68. package/dist/docs/controls.js +46 -0
  69. package/dist/docs/controls.js.map +1 -0
  70. package/dist/docs/copy.d.ts +24 -0
  71. package/dist/docs/copy.d.ts.map +1 -0
  72. package/dist/docs/copy.js +62 -0
  73. package/dist/docs/copy.js.map +1 -0
  74. package/dist/docs/math.d.ts +3 -0
  75. package/dist/docs/math.d.ts.map +1 -0
  76. package/dist/docs/math.js +12 -0
  77. package/dist/docs/math.js.map +1 -0
  78. package/dist/docs/reference.d.ts +27 -0
  79. package/dist/docs/reference.d.ts.map +1 -0
  80. package/dist/docs/reference.js +73 -0
  81. package/dist/docs/reference.js.map +1 -0
  82. package/dist/docs/tabs-sync.d.ts +5 -0
  83. package/dist/docs/tabs-sync.d.ts.map +1 -0
  84. package/dist/docs/tabs-sync.js +58 -0
  85. package/dist/docs/tabs-sync.js.map +1 -0
  86. package/dist/gitbook/ast.d.ts +506 -0
  87. package/dist/gitbook/ast.d.ts.map +1 -0
  88. package/dist/gitbook/ast.js +8 -0
  89. package/dist/gitbook/ast.js.map +1 -0
  90. package/dist/gitbook/attrs.d.ts +14 -0
  91. package/dist/gitbook/attrs.d.ts.map +1 -0
  92. package/dist/gitbook/attrs.js +36 -0
  93. package/dist/gitbook/attrs.js.map +1 -0
  94. package/dist/gitbook/flatten.d.ts +14 -0
  95. package/dist/gitbook/flatten.d.ts.map +1 -0
  96. package/dist/gitbook/flatten.js +45 -0
  97. package/dist/gitbook/flatten.js.map +1 -0
  98. package/dist/gitbook/index.d.ts +10 -0
  99. package/dist/gitbook/index.d.ts.map +1 -0
  100. package/dist/gitbook/index.js +8 -0
  101. package/dist/gitbook/index.js.map +1 -0
  102. package/dist/gitbook/inline.d.ts +28 -0
  103. package/dist/gitbook/inline.d.ts.map +1 -0
  104. package/dist/gitbook/inline.js +956 -0
  105. package/dist/gitbook/inline.js.map +1 -0
  106. package/dist/gitbook/outline.d.ts +16 -0
  107. package/dist/gitbook/outline.d.ts.map +1 -0
  108. package/dist/gitbook/outline.js +44 -0
  109. package/dist/gitbook/outline.js.map +1 -0
  110. package/dist/gitbook/package-managers.d.ts +12 -0
  111. package/dist/gitbook/package-managers.d.ts.map +1 -0
  112. package/dist/gitbook/package-managers.js +93 -0
  113. package/dist/gitbook/package-managers.js.map +1 -0
  114. package/dist/gitbook/parse.d.ts +44 -0
  115. package/dist/gitbook/parse.d.ts.map +1 -0
  116. package/dist/gitbook/parse.js +1113 -0
  117. package/dist/gitbook/parse.js.map +1 -0
  118. package/dist/gitbook/serialize.d.ts +23 -0
  119. package/dist/gitbook/serialize.d.ts.map +1 -0
  120. package/dist/gitbook/serialize.js +498 -0
  121. package/dist/gitbook/serialize.js.map +1 -0
  122. package/dist/index.d.ts +34 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +24 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/openapi/OpenApiOperation.d.ts +6 -0
  127. package/dist/openapi/OpenApiOperation.d.ts.map +1 -0
  128. package/dist/openapi/OpenApiOperation.js +112 -0
  129. package/dist/openapi/OpenApiOperation.js.map +1 -0
  130. package/dist/playground/InlineDemoPreview.d.ts +24 -0
  131. package/dist/playground/InlineDemoPreview.d.ts.map +1 -0
  132. package/dist/playground/InlineDemoPreview.js +48 -0
  133. package/dist/playground/InlineDemoPreview.js.map +1 -0
  134. package/dist/playground/PlaygroundStreamdown.d.ts +16 -0
  135. package/dist/playground/PlaygroundStreamdown.d.ts.map +1 -0
  136. package/dist/playground/PlaygroundStreamdown.js +22 -0
  137. package/dist/playground/PlaygroundStreamdown.js.map +1 -0
  138. package/dist/playground/ReactCodePreview.d.ts +64 -0
  139. package/dist/playground/ReactCodePreview.d.ts.map +1 -0
  140. package/dist/playground/ReactCodePreview.js +108 -0
  141. package/dist/playground/ReactCodePreview.js.map +1 -0
  142. package/dist/playground/filesystem.d.ts +27 -0
  143. package/dist/playground/filesystem.d.ts.map +1 -0
  144. package/dist/playground/filesystem.js +65 -0
  145. package/dist/playground/filesystem.js.map +1 -0
  146. package/dist/playground/index.d.ts +9 -0
  147. package/dist/playground/index.d.ts.map +1 -0
  148. package/dist/playground/index.js +7 -0
  149. package/dist/playground/index.js.map +1 -0
  150. package/dist/playground/project.d.ts +57 -0
  151. package/dist/playground/project.d.ts.map +1 -0
  152. package/dist/playground/project.js +137 -0
  153. package/dist/playground/project.js.map +1 -0
  154. package/dist/replay/NativeReplay.d.ts +9 -0
  155. package/dist/replay/NativeReplay.d.ts.map +1 -0
  156. package/dist/replay/NativeReplay.js +173 -0
  157. package/dist/replay/NativeReplay.js.map +1 -0
  158. package/dist/replay/ReplayPreview.d.ts +33 -0
  159. package/dist/replay/ReplayPreview.d.ts.map +1 -0
  160. package/dist/replay/ReplayPreview.js +91 -0
  161. package/dist/replay/ReplayPreview.js.map +1 -0
  162. package/dist/replay/index.d.ts +5 -0
  163. package/dist/replay/index.d.ts.map +1 -0
  164. package/dist/replay/index.js +4 -0
  165. package/dist/replay/index.js.map +1 -0
  166. package/dist/replay/sanitize.d.ts +4 -0
  167. package/dist/replay/sanitize.d.ts.map +1 -0
  168. package/dist/replay/sanitize.js +86 -0
  169. package/dist/replay/sanitize.js.map +1 -0
  170. package/dist/replay/url.d.ts +8 -0
  171. package/dist/replay/url.d.ts.map +1 -0
  172. package/dist/replay/url.js +41 -0
  173. package/dist/replay/url.js.map +1 -0
  174. package/dist/source/SourcePreview.d.ts +13 -0
  175. package/dist/source/SourcePreview.d.ts.map +1 -0
  176. package/dist/source/SourcePreview.js +47 -0
  177. package/dist/source/SourcePreview.js.map +1 -0
  178. package/dist/source/client.d.ts +7 -0
  179. package/dist/source/client.d.ts.map +1 -0
  180. package/dist/source/client.js +52 -0
  181. package/dist/source/client.js.map +1 -0
  182. package/dist/source/index.d.ts +6 -0
  183. package/dist/source/index.d.ts.map +1 -0
  184. package/dist/source/index.js +3 -0
  185. package/dist/source/index.js.map +1 -0
  186. package/dist/source/types.d.ts +33 -0
  187. package/dist/source/types.d.ts.map +1 -0
  188. package/dist/source/types.js +2 -0
  189. package/dist/source/types.js.map +1 -0
  190. package/dist/streamdown.d.ts +19 -0
  191. package/dist/streamdown.d.ts.map +1 -0
  192. package/dist/streamdown.js +15 -0
  193. package/dist/streamdown.js.map +1 -0
  194. package/dist/styles.css +2441 -0
  195. package/dist/video/VideoEmbed.d.ts +17 -0
  196. package/dist/video/VideoEmbed.d.ts.map +1 -0
  197. package/dist/video/VideoEmbed.js +12 -0
  198. package/dist/video/VideoEmbed.js.map +1 -0
  199. package/dist/video/index.d.ts +3 -0
  200. package/dist/video/index.d.ts.map +1 -0
  201. package/dist/video/index.js +2 -0
  202. package/dist/video/index.js.map +1 -0
  203. package/dist/vite/index.d.ts +21 -0
  204. package/dist/vite/index.d.ts.map +1 -0
  205. package/dist/vite/index.js +137 -0
  206. package/dist/vite/index.js.map +1 -0
  207. package/dist/viz/VizEmbed.d.ts +18 -0
  208. package/dist/viz/VizEmbed.d.ts.map +1 -0
  209. package/dist/viz/VizEmbed.js +12 -0
  210. package/dist/viz/VizEmbed.js.map +1 -0
  211. package/dist/viz/index.d.ts +4 -0
  212. package/dist/viz/index.d.ts.map +1 -0
  213. package/dist/viz/index.js +3 -0
  214. package/dist/viz/index.js.map +1 -0
  215. package/package.json +65 -32
  216. package/src/demo/DemoViewer.tsx +56 -11
  217. package/src/demo/clipboard.ts +228 -0
  218. package/src/demo/context.ts +7 -0
  219. package/src/demo/copy-demo.tsx +102 -0
  220. package/src/demo/index.ts +29 -2
  221. package/src/demo/vfs.ts +197 -0
  222. package/src/docs/DocsRenderer.tsx +13 -4
  223. package/src/docs/Mermaid.tsx +5 -3
  224. package/src/index.ts +19 -0
  225. package/src/replay/ReplayPreview.tsx +0 -1
  226. package/src/streamdown.tsx +2 -0
  227. package/src/styles.css +91 -0
  228. package/src/replay/styles.css +0 -87
@@ -0,0 +1,17 @@
1
+ import type { CSSProperties } from "react";
2
+ export interface VideoEmbedProps {
3
+ /** A direct media asset or a public player URL. */
4
+ src: string;
5
+ title?: string;
6
+ className?: string;
7
+ /** CSS aspect-ratio for iframe embeds. Defaults to 16 / 9. */
8
+ aspectRatio?: CSSProperties["aspectRatio"];
9
+ controls?: boolean;
10
+ autoplay?: boolean;
11
+ loop?: boolean;
12
+ muted?: boolean;
13
+ poster?: string;
14
+ allowFullScreen?: boolean;
15
+ }
16
+ export declare function VideoEmbed({ src, title, className, aspectRatio, controls, autoplay, loop, muted, poster, allowFullScreen, }: VideoEmbedProps): import("react").JSX.Element;
17
+ //# sourceMappingURL=VideoEmbed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VideoEmbed.d.ts","sourceRoot":"","sources":["../../src/video/VideoEmbed.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAMD,wBAAgB,UAAU,CAAC,EACzB,GAAG,EACH,KAAe,EACf,SAAS,EACT,WAAsB,EACtB,QAAe,EACf,QAAgB,EAChB,IAAY,EACZ,KAAa,EACb,MAAM,EACN,eAAsB,GACvB,EAAE,eAAe,+BA6BjB"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ function isDirectVideo(src) {
3
+ return /\.(?:mp4|webm|ogg)(?:[?#]|$)/i.test(src);
4
+ }
5
+ export function VideoEmbed({ src, title = "Video", className, aspectRatio = "16 / 9", controls = true, autoplay = false, loop = false, muted = false, poster, allowFullScreen = true, }) {
6
+ const mediaClass = `video-embed__media ${className ?? ""}`.trim();
7
+ if (isDirectVideo(src)) {
8
+ return (_jsx("video", { className: mediaClass, src: src, title: title, controls: controls, autoPlay: autoplay, loop: loop, muted: muted, poster: poster, playsInline: true }));
9
+ }
10
+ return (_jsx("iframe", { className: mediaClass, src: src, title: title, loading: "lazy", allow: "autoplay; fullscreen", allowFullScreen: allowFullScreen, style: { aspectRatio }, referrerPolicy: "strict-origin-when-cross-origin" }));
11
+ }
12
+ //# sourceMappingURL=VideoEmbed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VideoEmbed.js","sourceRoot":"","sources":["../../src/video/VideoEmbed.tsx"],"names":[],"mappings":";AAiBA,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,+BAA+B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EACzB,GAAG,EACH,KAAK,GAAG,OAAO,EACf,SAAS,EACT,WAAW,GAAG,QAAQ,EACtB,QAAQ,GAAG,IAAI,EACf,QAAQ,GAAG,KAAK,EAChB,IAAI,GAAG,KAAK,EACZ,KAAK,GAAG,KAAK,EACb,MAAM,EACN,eAAe,GAAG,IAAI,GACN;IAChB,MAAM,UAAU,GAAG,sBAAsB,SAAS,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACjE,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CACL,gBACE,SAAS,EAAE,UAAU,EACrB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,WAAW,SACX,CACH,CAAA;IACH,CAAC;IACD,OAAO,CACL,iBACE,SAAS,EAAE,UAAU,EACrB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,OAAO,EAAC,MAAM,EACd,KAAK,EAAC,sBAAsB,EAC5B,eAAe,EAAE,eAAe,EAChC,KAAK,EAAE,EAAE,WAAW,EAAE,EACtB,cAAc,EAAC,iCAAiC,GAChD,CACH,CAAA;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { VideoEmbed } from "./VideoEmbed.js";
2
+ export type { VideoEmbedProps } from "./VideoEmbed.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/video/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { VideoEmbed } from "./VideoEmbed.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/video/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,21 @@
1
+ import type { Plugin } from "vite";
2
+ export interface DocstreamSourceMount {
3
+ /** Stable name used by Markdown source references. */
4
+ name: string;
5
+ /** Real source directory. Relative paths are resolved from the Vite root. */
6
+ root: string;
7
+ /** Allow PUT requests to edit files in this mount. Defaults to true. */
8
+ writable?: boolean;
9
+ }
10
+ export interface DocstreamSourcesOptions {
11
+ mounts: readonly DocstreamSourceMount[];
12
+ /** HTTP endpoint prefix. Defaults to `/@docstream`. */
13
+ apiBase?: string;
14
+ }
15
+ /**
16
+ * Mount real source directories behind safe Vite dev-server endpoints.
17
+ * Markdown references import live modules from these files, and editor writes
18
+ * go back to disk so Vite's normal watcher/HMR pipeline remains authoritative.
19
+ */
20
+ export declare function docstreamSources(options: DocstreamSourcesOptions): Plugin;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAA;AAEjD,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACvC,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AA6GD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAgCzE"}
@@ -0,0 +1,137 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ const VIRTUAL_ID = "virtual:docstream-sources";
4
+ const RESOLVED_VIRTUAL_ID = "\0virtual:docstream-sources";
5
+ function send(response, status, body, type = "application/json; charset=utf-8") {
6
+ response.statusCode = status;
7
+ response.setHeader("content-type", type);
8
+ response.setHeader("cache-control", "no-store");
9
+ response.end(body);
10
+ }
11
+ function requestUrl(request) {
12
+ return new URL(request.url ?? "/", "http://docstream.local");
13
+ }
14
+ async function requestBody(request) {
15
+ const chunks = [];
16
+ for await (const chunk of request)
17
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
18
+ return Buffer.concat(chunks).toString("utf8");
19
+ }
20
+ function normalizeRelativePath(value) {
21
+ const source = value.replaceAll("\\", "/").replace(/^\.\//, "");
22
+ if (!source || source.startsWith("/") || source.split("/").some((part) => part === ".." || part === "")) {
23
+ throw new Error(`Invalid mounted source path: ${value}`);
24
+ }
25
+ return source;
26
+ }
27
+ function sourcePath(mount, requestedPath) {
28
+ const relativePath = normalizeRelativePath(requestedPath);
29
+ const absolutePath = path.resolve(mount.root, relativePath);
30
+ const prefix = mount.root.endsWith(path.sep) ? mount.root : `${mount.root}${path.sep}`;
31
+ if (!absolutePath.startsWith(prefix))
32
+ throw new Error(`Source path escapes mount ${mount.name}: ${requestedPath}`);
33
+ return { relativePath, absolutePath };
34
+ }
35
+ function provenance(url, mount, relativePath, absolutePath, mtimeMs) {
36
+ return {
37
+ mount: mount.name,
38
+ path: relativePath,
39
+ exportName: url.searchParams.get("export") || "default",
40
+ kind: url.searchParams.get("kind") === "story" ? "story" : "component",
41
+ absolutePath,
42
+ mtimeMs,
43
+ };
44
+ }
45
+ function findMount(url, mounts) {
46
+ const name = url.searchParams.get("mount") ?? "";
47
+ const mount = mounts.find((candidate) => candidate.name === name);
48
+ if (!mount)
49
+ throw new Error(`Unknown Docstream source mount: ${name}`);
50
+ return mount;
51
+ }
52
+ function errorMessage(error) {
53
+ return error instanceof Error ? error.message : String(error);
54
+ }
55
+ function installMiddleware(server, apiBase, viteBase, mounts) {
56
+ server.middlewares.use(async (request, response, next) => {
57
+ const url = requestUrl(request);
58
+ if (url.pathname !== `${apiBase}/source` && url.pathname !== `${apiBase}/module`)
59
+ return next();
60
+ try {
61
+ const mount = findMount(url, mounts);
62
+ const requestedPath = url.searchParams.get("path") ?? "";
63
+ const { relativePath, absolutePath } = sourcePath(mount, requestedPath);
64
+ if (url.pathname === `${apiBase}/module`) {
65
+ if (request.method !== "GET")
66
+ return send(response, 405, "Method not allowed", "text/plain; charset=utf-8");
67
+ await fs.access(absolutePath);
68
+ const sourceUrl = `${viteBase.replace(/\/$/, "")}/@fs/${absolutePath.replaceAll(path.sep, "/")}?docstream=${Date.now()}`;
69
+ const module = `import * as source from ${JSON.stringify(sourceUrl)};\nexport default source;\n`;
70
+ return send(response, 200, module, "text/javascript; charset=utf-8");
71
+ }
72
+ if (request.method === "GET") {
73
+ const [content, stat] = await Promise.all([fs.readFile(absolutePath, "utf8"), fs.stat(absolutePath)]);
74
+ return send(response, 200, JSON.stringify({ content, provenance: provenance(url, mount, relativePath, absolutePath, stat.mtimeMs) }));
75
+ }
76
+ if (request.method === "PUT") {
77
+ if (!mount.writable)
78
+ return send(response, 403, `Source mount ${mount.name} is read-only`, "text/plain; charset=utf-8");
79
+ await fs.access(absolutePath);
80
+ const parsed = JSON.parse(await requestBody(request));
81
+ if (typeof parsed.content !== "string")
82
+ return send(response, 400, "Expected a JSON string field named content", "text/plain; charset=utf-8");
83
+ await fs.writeFile(absolutePath, parsed.content, "utf8");
84
+ const stat = await fs.stat(absolutePath);
85
+ server.watcher.add(absolutePath);
86
+ server.ws.send({ type: "full-reload", path: "*" });
87
+ return send(response, 200, JSON.stringify({ content: parsed.content, provenance: provenance(url, mount, relativePath, absolutePath, stat.mtimeMs) }));
88
+ }
89
+ return send(response, 405, "Method not allowed", "text/plain; charset=utf-8");
90
+ }
91
+ catch (error) {
92
+ const status = error && typeof error === "object" && "code" in error && error.code === "ENOENT" ? 404 : 400;
93
+ return send(response, status, errorMessage(error), "text/plain; charset=utf-8");
94
+ }
95
+ });
96
+ }
97
+ /**
98
+ * Mount real source directories behind safe Vite dev-server endpoints.
99
+ * Markdown references import live modules from these files, and editor writes
100
+ * go back to disk so Vite's normal watcher/HMR pipeline remains authoritative.
101
+ */
102
+ export function docstreamSources(options) {
103
+ const apiBase = `/${(options.apiBase ?? "/@docstream").replace(/^\/+|\/+$/g, "")}`;
104
+ let mounts = [];
105
+ let viteBase = "/";
106
+ return {
107
+ name: "docstream-sources",
108
+ enforce: "pre",
109
+ configResolved(config) {
110
+ viteBase = config.base || "/";
111
+ const names = new Set();
112
+ mounts = options.mounts.map((mount) => {
113
+ if (!mount.name || names.has(mount.name))
114
+ throw new Error(`Docstream source mount names must be unique: ${mount.name}`);
115
+ names.add(mount.name);
116
+ return {
117
+ name: mount.name,
118
+ root: path.resolve(config.root, mount.root),
119
+ writable: mount.writable !== false,
120
+ };
121
+ });
122
+ },
123
+ configureServer(server) {
124
+ installMiddleware(server, apiBase, viteBase, mounts);
125
+ },
126
+ resolveId(id) {
127
+ if (id === VIRTUAL_ID)
128
+ return RESOLVED_VIRTUAL_ID;
129
+ },
130
+ load(id) {
131
+ if (id !== RESOLVED_VIRTUAL_ID)
132
+ return;
133
+ return `export const apiBase = ${JSON.stringify(apiBase)};\nexport const mounts = ${JSON.stringify(options.mounts.map(({ name, writable = true }) => ({ name, writable })))};\n`;
134
+ },
135
+ };
136
+ }
137
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,IAAI,MAAM,WAAW,CAAA;AAyB5B,MAAM,UAAU,GAAG,2BAA2B,CAAA;AAC9C,MAAM,mBAAmB,GAAG,6BAA6B,CAAA;AAEzD,SAAS,IAAI,CAAC,QAAwB,EAAE,MAAc,EAAE,IAAY,EAAE,IAAI,GAAG,iCAAiC;IAC5G,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,CAAC,CAAA;IACxC,QAAQ,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAA;IAC/C,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,OAAwB;IAC1C,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,wBAAwB,CAAC,CAAA;AAC9D,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAwB;IACjD,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACnG,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAC/C,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAC/D,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC;QACxG,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAA;IAC1D,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,UAAU,CAAC,KAAoB,EAAE,aAAqB;IAC7D,MAAM,YAAY,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAA;IACzD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtF,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC,CAAA;IAClH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAA;AACvC,CAAC;AAED,SAAS,UAAU,CAAC,GAAQ,EAAE,KAAoB,EAAE,YAAoB,EAAE,YAAoB,EAAE,OAAe;IAC7G,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,IAAI;QACjB,IAAI,EAAE,YAAY;QAClB,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;QACvD,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW;QACtE,YAAY;QACZ,OAAO;KACR,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAQ,EAAE,MAAgC;IAC3D,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IACjE,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAA;IACtE,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAqB,EAAE,OAAe,EAAE,QAAgB,EAAE,MAAgC;IACnH,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QACvD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;QAC/B,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,OAAO,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,OAAO,SAAS;YAAE,OAAO,IAAI,EAAE,CAAA;QAE/F,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YACpC,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;YACxD,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;YAEvE,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,OAAO,SAAS,EAAE,CAAC;gBACzC,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK;oBAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,oBAAoB,EAAE,2BAA2B,CAAC,CAAA;gBAC3G,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;gBAC7B,MAAM,SAAS,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,cAAc,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;gBACxH,MAAM,MAAM,GAAG,2BAA2B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,6BAA6B,CAAA;gBAChG,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,gCAAgC,CAAC,CAAA;YACtE,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC7B,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;gBACrG,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;YACvI,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,QAAQ;oBAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,gBAAgB,KAAK,CAAC,IAAI,eAAe,EAAE,2BAA2B,CAAC,CAAA;gBACvH,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;gBAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,CAA0B,CAAA;gBAC9E,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;oBAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,4CAA4C,EAAE,2BAA2B,CAAC,CAAA;gBAC7I,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;gBACxD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;gBACxC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;gBAChC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAA;gBAClD,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;YACvJ,CAAC;YAED,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,oBAAoB,EAAE,2BAA2B,CAAC,CAAA;QAC/E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAK,KAA2B,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;YAClI,OAAO,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,2BAA2B,CAAC,CAAA;QACjF,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,aAAa,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAA;IAClF,IAAI,MAAM,GAAoB,EAAE,CAAA;IAChC,IAAI,QAAQ,GAAG,GAAG,CAAA;IAElB,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,KAAK;QACd,cAAc,CAAC,MAAM;YACnB,QAAQ,GAAG,MAAM,CAAC,IAAI,IAAI,GAAG,CAAA;YAC7B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;YAC/B,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;gBACvH,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACrB,OAAO;oBACL,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;oBAC3C,QAAQ,EAAE,KAAK,CAAC,QAAQ,KAAK,KAAK;iBACnC,CAAA;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,eAAe,CAAC,MAAM;YACpB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;QACtD,CAAC;QACD,SAAS,CAAC,EAAE;YACV,IAAI,EAAE,KAAK,UAAU;gBAAE,OAAO,mBAAmB,CAAA;QACnD,CAAC;QACD,IAAI,CAAC,EAAE;YACL,IAAI,EAAE,KAAK,mBAAmB;gBAAE,OAAM;YACtC,OAAO,0BAA0B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;QAClL,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { PlayerAudio, VideoScene } from "@brett_lamy/viz-engine";
2
+ export interface VizEmbedProps {
3
+ scene: VideoScene;
4
+ audio?: PlayerAudio;
5
+ title?: string;
6
+ className?: string;
7
+ autoplay?: boolean;
8
+ loop?: boolean;
9
+ showCaptions?: boolean;
10
+ }
11
+ /**
12
+ * Mount a deterministic VizEngine scene inside a Docstream document. This is
13
+ * intentionally a component API rather than a markdown escape hatch: callers
14
+ * choose the scene and its evidence, while Docstream supplies document chrome
15
+ * and accessible figure semantics.
16
+ */
17
+ export declare function VizEmbed({ scene, audio, title, className, autoplay, loop, showCaptions, }: VizEmbedProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=VizEmbed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VizEmbed.d.ts","sourceRoot":"","sources":["../../src/viz/VizEmbed.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGrE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,SAAS,EACT,QAAe,EACf,IAAW,EACX,YAAoB,GACrB,EAAE,aAAa,+BAcf"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { VizPlayer } from "@brett_lamy/viz-engine";
3
+ /**
4
+ * Mount a deterministic VizEngine scene inside a Docstream document. This is
5
+ * intentionally a component API rather than a markdown escape hatch: callers
6
+ * choose the scene and its evidence, while Docstream supplies document chrome
7
+ * and accessible figure semantics.
8
+ */
9
+ export function VizEmbed({ scene, audio, title, className, autoplay = true, loop = true, showCaptions = false, }) {
10
+ return (_jsxs("figure", { className: `docstream-viz ${className ?? ""}`.trim(), children: [_jsx(VizPlayer, { scene: scene, ...(audio === undefined ? {} : { audio }), autoplay: autoplay, loop: loop, showCaptions: showCaptions, className: "docstream-viz__player" }), title && _jsx("figcaption", { children: title })] }));
11
+ }
12
+ //# sourceMappingURL=VizEmbed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VizEmbed.js","sourceRoot":"","sources":["../../src/viz/VizEmbed.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAYlD;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,SAAS,EACT,QAAQ,GAAG,IAAI,EACf,IAAI,GAAG,IAAI,EACX,YAAY,GAAG,KAAK,GACN;IACd,OAAO,CACL,kBAAQ,SAAS,EAAE,iBAAiB,SAAS,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,aAC1D,KAAC,SAAS,IACR,KAAK,EAAE,KAAK,KACR,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAC1C,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAC,uBAAuB,GACjC,EACD,KAAK,IAAI,+BAAa,KAAK,GAAc,IACnC,CACV,CAAA;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+
2
+ export { VizEmbed } from "./VizEmbed.js";
3
+ export type { VizEmbedProps } from "./VizEmbed.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/viz/index.ts"],"names":[],"mappings":"AAAA,OAAO,mCAAmC,CAAA;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,3 @@
1
+ import "@brett_lamy/viz-engine/styles.css";
2
+ export { VizEmbed } from "./VizEmbed.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/viz/index.ts"],"names":[],"mappings":"AAAA,OAAO,mCAAmC,CAAA;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,18 +1,21 @@
1
1
  {
2
2
  "name": "@brett_lamy/docstream",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "GitBook-aware readonly markdown and AI stream renderer.",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "type": "module",
9
9
  "scripts": {
10
+ "build": "node scripts/build.mjs",
10
11
  "typecheck": "tsc -p tsconfig.json --noEmit",
11
- "test": "vitest run"
12
+ "test": "npm run build && vitest run",
13
+ "prepack": "npm run build"
12
14
  },
13
- "main": "./src/index.ts",
14
- "types": "./src/index.ts",
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
15
17
  "files": [
18
+ "dist",
16
19
  "src",
17
20
  "!src/**/*.test.ts",
18
21
  "!src/**/*.test.tsx",
@@ -24,58 +27,84 @@
24
27
  ],
25
28
  "exports": {
26
29
  ".": {
27
- "types": "./src/index.ts",
28
- "import": "./src/index.ts"
30
+ "source": "./src/index.ts",
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js",
33
+ "default": "./dist/index.js"
29
34
  },
30
35
  "./gitbook": {
31
- "types": "./src/gitbook/index.ts",
32
- "import": "./src/gitbook/index.ts"
36
+ "source": "./src/gitbook/index.ts",
37
+ "types": "./dist/gitbook/index.d.ts",
38
+ "import": "./dist/gitbook/index.js",
39
+ "default": "./dist/gitbook/index.js"
33
40
  },
34
41
  "./streamdown": {
35
- "types": "./src/streamdown.tsx",
36
- "import": "./src/streamdown.tsx"
42
+ "source": "./src/streamdown.tsx",
43
+ "types": "./dist/streamdown.d.ts",
44
+ "import": "./dist/streamdown.js",
45
+ "default": "./dist/streamdown.js"
37
46
  },
38
47
  "./reference": {
39
- "types": "./src/docs/reference.tsx",
40
- "import": "./src/docs/reference.tsx"
48
+ "source": "./src/docs/reference.tsx",
49
+ "types": "./dist/docs/reference.d.ts",
50
+ "import": "./dist/docs/reference.js",
51
+ "default": "./dist/docs/reference.js"
41
52
  },
42
53
  "./assets": {
43
- "types": "./src/assets.ts",
44
- "import": "./src/assets.ts"
54
+ "source": "./src/assets.ts",
55
+ "types": "./dist/assets.d.ts",
56
+ "import": "./dist/assets.js",
57
+ "default": "./dist/assets.js"
45
58
  },
46
59
  "./openapi": {
47
- "types": "./src/openapi/OpenApiOperation.tsx",
48
- "import": "./src/openapi/OpenApiOperation.tsx"
60
+ "source": "./src/openapi/OpenApiOperation.tsx",
61
+ "types": "./dist/openapi/OpenApiOperation.d.ts",
62
+ "import": "./dist/openapi/OpenApiOperation.js",
63
+ "default": "./dist/openapi/OpenApiOperation.js"
49
64
  },
50
65
  "./replay": {
51
- "types": "./src/replay/index.ts",
52
- "import": "./src/replay/index.ts"
66
+ "source": "./src/replay/index.ts",
67
+ "types": "./dist/replay/index.d.ts",
68
+ "import": "./dist/replay/index.js",
69
+ "default": "./dist/replay/index.js"
53
70
  },
54
71
  "./video": {
55
- "types": "./src/video/index.ts",
56
- "import": "./src/video/index.ts"
72
+ "source": "./src/video/index.ts",
73
+ "types": "./dist/video/index.d.ts",
74
+ "import": "./dist/video/index.js",
75
+ "default": "./dist/video/index.js"
57
76
  },
58
77
  "./viz": {
59
- "types": "./src/viz/index.ts",
60
- "import": "./src/viz/index.ts"
78
+ "source": "./src/viz/index.ts",
79
+ "types": "./dist/viz/index.d.ts",
80
+ "import": "./dist/viz/index.js",
81
+ "default": "./dist/viz/index.js"
61
82
  },
62
83
  "./playground": {
63
- "types": "./src/playground/index.ts",
64
- "import": "./src/playground/index.ts"
84
+ "source": "./src/playground/index.ts",
85
+ "types": "./dist/playground/index.d.ts",
86
+ "import": "./dist/playground/index.js",
87
+ "default": "./dist/playground/index.js"
65
88
  },
66
89
  "./source": {
67
- "types": "./src/source/index.ts",
68
- "import": "./src/source/index.ts"
90
+ "source": "./src/source/index.ts",
91
+ "types": "./dist/source/index.d.ts",
92
+ "import": "./dist/source/index.js",
93
+ "default": "./dist/source/index.js"
69
94
  },
70
95
  "./demo": {
71
- "types": "./src/demo/index.ts",
72
- "import": "./src/demo/index.ts"
96
+ "source": "./src/demo/index.ts",
97
+ "types": "./dist/demo/index.d.ts",
98
+ "import": "./dist/demo/index.js",
99
+ "default": "./dist/demo/index.js"
73
100
  },
74
101
  "./vite": {
75
- "types": "./src/vite/index.ts",
76
- "import": "./src/vite/index.ts"
102
+ "source": "./src/vite/index.ts",
103
+ "types": "./dist/vite/index.d.ts",
104
+ "import": "./dist/vite/index.js",
105
+ "default": "./dist/vite/index.js"
77
106
  },
78
- "./styles.css": "./src/styles.css"
107
+ "./styles.css": "./dist/styles.css"
79
108
  },
80
109
  "dependencies": {
81
110
  "framer-motion": "^13.1.1",
@@ -83,6 +112,7 @@
83
112
  "katex": "^0.18.5",
84
113
  "lucide-react": "^1.17.0",
85
114
  "mermaid": "^11.15.0",
115
+ "react-aria": "^3.52.1",
86
116
  "rrweb": "2.0.0-alpha.20",
87
117
  "yaml": "^2.9.0"
88
118
  },
@@ -113,8 +143,11 @@
113
143
  "@brett_lamy/viz-engine": "^0.2.1",
114
144
  "@types/node": "^24.0.0",
115
145
  "@types/react": "^18.3.3",
146
+ "@types/react-dom": "^18.3.7",
147
+ "happy-dom": "^20.14.5",
116
148
  "typescript": "^5.5.4",
117
149
  "vite": "^7.0.0",
118
150
  "vitest": "^3.2.4"
119
- }
151
+ },
152
+ "module": "./dist/index.js"
120
153
  }
@@ -24,7 +24,7 @@ import {
24
24
  type KeyboardEvent as ReactKeyboardEvent,
25
25
  type PointerEvent as ReactPointerEvent,
26
26
  type ReactNode,
27
- type MutableRefObject,
27
+ type Ref,
28
28
  } from "react"
29
29
  import { motion, useReducedMotion } from "framer-motion"
30
30
  import { ExternalLink, FileCode2, Folder, Maximize2, Monitor, Smartphone, Tablet, X } from "lucide-react"
@@ -36,8 +36,10 @@ import { HighlightedCode } from "../docs/HighlightedCode"
36
36
  import { CopyButton } from "../docs/copy"
37
37
  import { PillTabs, Segmented, rovingKeyDown } from "../docs/controls"
38
38
  import { useDemoResolver, useDemoRuntime } from "./context"
39
+ import { createDocstreamClipboard, type DemoSourceSnapshot, type DocstreamClipboard } from "./clipboard"
40
+ import { CopyDemoButton } from "./copy-demo"
39
41
  import { languageForPath } from "./glob"
40
- import { inlineDemoMarkdown } from "./markdown"
42
+ import { useDemoVFSContext, useDemoVFSEntry, type DemoVFS } from "./vfs"
41
43
  import { cssToSurface, surfaceStyle, surfaceToCss } from "./surface"
42
44
  import type { DemoComponent, DemoFile, DemoMeta, DemoResolver, DemoVariant, InlineDemoRuntime } from "./types"
43
45
 
@@ -67,6 +69,11 @@ export interface DemoViewerProps {
67
69
  runtime?: InlineDemoRuntime
68
70
  /** Extra npm dependencies for the runtime, merged over `demoDependencies` from context. */
69
71
  dependencies?: Record<string, string>
72
+ /**
73
+ * Demo files held in memory by `src` (pasted demos). Used, like inline files, when the block
74
+ * has none of its own and no resolver knows `src`. Defaults to the VFS in context (`demoVFS`).
75
+ */
76
+ vfs?: DemoVFS
70
77
  /** Class on the viewer's outer element. (The preview canvas's class is `canvasClassName`.) */
71
78
  className?: string
72
79
  /*
@@ -120,6 +127,32 @@ export async function preloadDemos(resolver: DemoResolver, srcs: string[]): Prom
120
127
  )
121
128
  }
122
129
 
130
+ /**
131
+ * Fetch demos' meta and files (not their components) into the viewer's cache, so
132
+ * `peekDemoSource` can answer synchronously — e.g. when a copy event needs a demo's files.
133
+ */
134
+ export async function prefetchDemoSources(resolver: DemoResolver, srcs: string[]): Promise<void> {
135
+ const cache = cacheFor(resolver)
136
+ const settle = async (key: string, run: () => Promise<unknown>) => {
137
+ if (cache.get(key)?.status === "ready") return
138
+ try {
139
+ cache.set(key, { status: "ready", value: await run() })
140
+ } catch (cause) {
141
+ cache.set(key, { status: "error", error: cause instanceof Error ? cause : new Error(String(cause)) })
142
+ }
143
+ }
144
+ await Promise.all(srcs.flatMap((src) => [settle(`meta:${src}`, () => resolver.meta(src)), settle(`files:${src}`, () => resolver.files(src))]))
145
+ }
146
+
147
+ /** A demo's files (and meta) if the resolver has already delivered them (viewer, `preloadDemos`, `prefetchDemoSources`). */
148
+ export function peekDemoSource(resolver: DemoResolver, src: string): DemoSourceSnapshot | undefined {
149
+ const cache = settled.get(resolver)
150
+ const files = cache?.get(`files:${src}`)
151
+ if (files?.status !== "ready" || !(files.value as DemoFile[]).length) return undefined
152
+ const meta = cache?.get(`meta:${src}`)
153
+ return { files: files.value as DemoFile[], ...(meta?.status === "ready" && meta.value ? { meta: meta.value as DemoMeta } : {}) }
154
+ }
155
+
123
156
  /** Resolves `run()` once `active`, remembering settled values so remounts don't flash. */
124
157
  function useResolved<T>(resolver: DemoResolver | undefined, key: string, active: boolean, run: () => Promise<T>): Loaded<T> {
125
158
  const cached = resolver ? (cacheFor(resolver).get(key) as Loaded<T> | undefined) : undefined
@@ -165,10 +198,11 @@ const useComponent = (resolver: DemoResolver | undefined, src: string, active: b
165
198
 
166
199
  /** True once the element has come within `margin` of the viewport (stays true). */
167
200
  function useNearViewport<T extends Element>(margin = "400px 0px") {
168
- const ref = useRef<T | null>(null)
201
+ // A callback ref, so an element that appears later (a VFS demo arriving) is observed too.
202
+ const [el, setEl] = useState<T | null>(null)
203
+ const ref = useMemo(() => (node: T | null) => setEl(node), [])
169
204
  const [near, setNear] = useState(false)
170
205
  useEffect(() => {
171
- const el = ref.current
172
206
  if (!el || near) return
173
207
  if (typeof IntersectionObserver === "undefined") {
174
208
  setNear(true)
@@ -179,7 +213,7 @@ function useNearViewport<T extends Element>(margin = "400px 0px") {
179
213
  }, { rootMargin: margin })
180
214
  observer.observe(el)
181
215
  return () => observer.disconnect()
182
- }, [near, margin])
216
+ }, [el, near, margin])
183
217
  return [ref, near] as const
184
218
  }
185
219
 
@@ -594,7 +628,12 @@ export function DemoViewer(props: DemoViewerProps) {
594
628
  const runtimeOptions = useDemoRuntime({ runtime: props.runtime, dependencies: props.dependencies })
595
629
  const { src } = props
596
630
  const [ref, near] = useNearViewport<HTMLElement>()
597
- const inlineFiles = useMemo(() => orderInline(props.files, props.entry), [props.files, props.entry])
631
+ const vfs = useDemoVFSContext(props.vfs)
632
+ // Pasted demos: files held by the VFS stand in for inline files the block doesn't carry.
633
+ const vfsEntry = useDemoVFSEntry(vfs, props.files?.length ? "" : src)
634
+ const ownFiles = props.files?.length ? props.files : vfsEntry?.files
635
+ const ownEntry = props.files?.length ? props.entry : props.entry ?? vfsEntry?.meta?.entry
636
+ const inlineFiles = useMemo(() => orderInline(ownFiles, ownEntry), [ownFiles, ownEntry])
598
637
 
599
638
  // A resolver is consulted unless the block brings its own files and the resolver
600
639
  // says (via `list()`) that it does not know this src.
@@ -607,7 +646,7 @@ export function DemoViewer(props: DemoViewerProps) {
607
646
  const mode: "resolver" | "inline" | "none" =
608
647
  tryResolver && !(inlineFiles && resolverFailed) ? "resolver" : inlineFiles ? "inline" : "none"
609
648
 
610
- const metaValue = mode === "resolver" && meta.status === "ready" ? meta.value : undefined
649
+ const metaValue = mode === "resolver" ? (meta.status === "ready" ? meta.value : undefined) : mode === "inline" && !props.files?.length ? vfsEntry?.meta : undefined
611
650
  const files: Loaded<DemoFile[]> = mode === "inline" ? { status: "ready", value: inlineFiles! } : resolvedFiles
612
651
  const title = props.title ?? metaValue?.title ?? (src.split("/").pop() || "Demo")
613
652
  const description = props.description ?? metaValue?.description
@@ -669,7 +708,12 @@ export function DemoViewer(props: DemoViewerProps) {
669
708
  ...(props.surface ? { surface: typeof props.surface === "string" ? props.surface : surfaceToCss(props.surface) } : {}),
670
709
  ...(props.variantsWidth !== undefined && props.variantsWidth !== "" ? { variantsWidth: String(props.variantsWidth) } : {}),
671
710
  }
672
- const copyDemo = () => inlineDemoMarkdown(node, fileList ?? [], metaValue)
711
+ // "Copy demo": the exact block (src-only stays src-only; inline files stay inline) plus its files.
712
+ const copyDemo = (): DocstreamClipboard | null => {
713
+ if (!fileList?.length) return null
714
+ const exact: DemoNode = props.files?.length ? { ...node, ...(props.entry ? { entry: props.entry } : {}), files: props.files.map((f) => ({ ...f })) } : node
715
+ return createDocstreamClipboard({ type: "doc", children: [exact] }, { lookup: () => ({ files: fileList, ...(metaValue ? { meta: metaValue } : {}) }) })
716
+ }
673
717
 
674
718
  const dialog = (
675
719
  <FullscreenDialog
@@ -702,6 +746,7 @@ export function DemoViewer(props: DemoViewerProps) {
702
746
  <span className="docs-demo-spacer" />
703
747
  <StatusBadge status={status} />
704
748
  <VariantSwitch variants={variants} value={variant} onChange={setVariant} title={title} width={variantsWidth} />
749
+ {fileList?.length ? <CopyDemoButton clipboard={copyDemo} className="docs-demo-icon-btn" /> : null}
705
750
  {runnable ? <FullscreenButton onOpen={() => setFullscreen(true)} title={title} /> : null}
706
751
  </header>
707
752
  {runnable ? (
@@ -766,7 +811,7 @@ export function DemoViewer(props: DemoViewerProps) {
766
811
  }
767
812
 
768
813
  function MultiFileViewer({ rootRef, className, baseId, mode, src, title, description, variants, variant, onVariant, files, preview, near, height, viewport, bleed, look, status, variantsWidth, href, onFullscreen, copyDemo, dialog }: {
769
- rootRef: MutableRefObject<HTMLElement | null>
814
+ rootRef: Ref<HTMLElement>
770
815
  className: string
771
816
  baseId: string
772
817
  mode: "resolver" | "inline"
@@ -787,7 +832,7 @@ function MultiFileViewer({ rootRef, className, baseId, mode, src, title, descrip
787
832
  variantsWidth: number | string | undefined
788
833
  href: string | undefined
789
834
  onFullscreen: () => void
790
- copyDemo: (() => string) | undefined
835
+ copyDemo: (() => DocstreamClipboard | null) | undefined
791
836
  dialog: ReactNode
792
837
  }) {
793
838
  const runnable = preview.kind !== "unavailable"
@@ -835,7 +880,7 @@ function MultiFileViewer({ rootRef, className, baseId, mode, src, title, descrip
835
880
  ))}
836
881
  </div>
837
882
  ) : null}
838
- {copyDemo ? <CopyButton text={copyDemo} label="Copy demo as Markdown" className="docs-demo-icon-btn" /> : null}
883
+ {copyDemo ? <CopyDemoButton clipboard={copyDemo} className="docs-demo-icon-btn" /> : null}
839
884
  {runnable ? <FullscreenButton onOpen={onFullscreen} title={title} /> : null}
840
885
  <NewTabLink href={href} title={title} />
841
886
  </div>