@brett_lamy/docstream 1.7.0 → 1.9.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 (70) hide show
  1. package/README.md +18 -0
  2. package/dist/demo/DemoViewer.d.ts +8 -0
  3. package/dist/demo/DemoViewer.d.ts.map +1 -1
  4. package/dist/demo/DemoViewer.js +76 -3
  5. package/dist/demo/DemoViewer.js.map +1 -1
  6. package/dist/demo/PathBreadcrumb.d.ts +54 -0
  7. package/dist/demo/PathBreadcrumb.d.ts.map +1 -0
  8. package/dist/demo/PathBreadcrumb.js +245 -0
  9. package/dist/demo/PathBreadcrumb.js.map +1 -0
  10. package/dist/docs/DocsRenderer.d.ts +9 -2
  11. package/dist/docs/DocsRenderer.d.ts.map +1 -1
  12. package/dist/docs/DocsRenderer.js +13 -6
  13. package/dist/docs/DocsRenderer.js.map +1 -1
  14. package/dist/docs/DocumentProperties.d.ts +11 -0
  15. package/dist/docs/DocumentProperties.d.ts.map +1 -0
  16. package/dist/docs/DocumentProperties.js +63 -0
  17. package/dist/docs/DocumentProperties.js.map +1 -0
  18. package/dist/docs/TaskCheckbox.d.ts +24 -0
  19. package/dist/docs/TaskCheckbox.d.ts.map +1 -0
  20. package/dist/docs/TaskCheckbox.js +103 -0
  21. package/dist/docs/TaskCheckbox.js.map +1 -0
  22. package/dist/docs/properties.css +28 -0
  23. package/dist/docs/tasks.css +13 -0
  24. package/dist/gitbook/ast.d.ts +14 -0
  25. package/dist/gitbook/ast.d.ts.map +1 -1
  26. package/dist/gitbook/ast.js.map +1 -1
  27. package/dist/gitbook/index.d.ts +3 -0
  28. package/dist/gitbook/index.d.ts.map +1 -1
  29. package/dist/gitbook/index.js +2 -0
  30. package/dist/gitbook/index.js.map +1 -1
  31. package/dist/gitbook/parse.d.ts.map +1 -1
  32. package/dist/gitbook/parse.js +17 -3
  33. package/dist/gitbook/parse.js.map +1 -1
  34. package/dist/gitbook/properties.d.ts +15 -0
  35. package/dist/gitbook/properties.d.ts.map +1 -0
  36. package/dist/gitbook/properties.js +45 -0
  37. package/dist/gitbook/properties.js.map +1 -0
  38. package/dist/gitbook/serialize.d.ts.map +1 -1
  39. package/dist/gitbook/serialize.js +5 -2
  40. package/dist/gitbook/serialize.js.map +1 -1
  41. package/dist/gitbook/tasks.d.ts +11 -0
  42. package/dist/gitbook/tasks.d.ts.map +1 -0
  43. package/dist/gitbook/tasks.js +21 -0
  44. package/dist/gitbook/tasks.js.map +1 -0
  45. package/dist/index.d.ts +7 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +4 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/streamdown.d.ts +1 -1
  50. package/dist/streamdown.d.ts.map +1 -1
  51. package/dist/streamdown.js +2 -2
  52. package/dist/streamdown.js.map +1 -1
  53. package/dist/styles.css +228 -4
  54. package/package.json +15 -1
  55. package/src/demo/DemoViewer.tsx +98 -4
  56. package/src/demo/PathBreadcrumb.tsx +367 -0
  57. package/src/docs/DocsRenderer.tsx +22 -5
  58. package/src/docs/DocumentProperties.tsx +84 -0
  59. package/src/docs/TaskCheckbox.tsx +111 -0
  60. package/src/docs/properties.css +28 -0
  61. package/src/docs/tasks.css +13 -0
  62. package/src/gitbook/ast.ts +16 -0
  63. package/src/gitbook/index.ts +3 -0
  64. package/src/gitbook/parse.ts +15 -3
  65. package/src/gitbook/properties.ts +49 -0
  66. package/src/gitbook/serialize.ts +5 -2
  67. package/src/gitbook/tasks.ts +25 -0
  68. package/src/index.ts +7 -0
  69. package/src/streamdown.tsx +4 -0
  70. package/src/styles.css +228 -4
@@ -1 +1 @@
1
- {"version":3,"file":"streamdown.d.ts","sourceRoot":"","sources":["../src/streamdown.tsx"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAKjI,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACnG,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACtG,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAA;AACtH,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAA;AAE1G,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,cAAc,CAAC,EAAE,uBAAuB,CAAA;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,WAAmB,EACnB,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,WAAW,GACZ,EAAE,sBAAsB,+BA2BxB"}
1
+ {"version":3,"file":"streamdown.d.ts","sourceRoot":"","sources":["../src/streamdown.tsx"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAKjI,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACnG,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACtG,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAA;AACtH,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAA;AAE1G,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,cAAc,CAAC,EAAE,uBAAuB,CAAA;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,WAAmB,EACnB,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,kBAAkB,EAClB,iBAAiB,GAClB,EAAE,sBAAsB,+BA6BxB"}
@@ -7,9 +7,9 @@ import { parseMarkdown, trimPartialInlineToken } from "./gitbook/parse.js";
7
7
  export { CitationSources, DocstreamRefContext, InlineReference } from "./docs/reference.js";
8
8
  export { DocPageActions } from "./docs/PageActions.js";
9
9
  export { DocstreamDemoContext, DocstreamDemoRuntimeContext, DemoFullscreen, DemoViewer } from "./demo/index.js";
10
- export function GitbookStreamdown({ children, className, isAnimating, isStreaming = false, markdown, liveRenderer, sourceRenderer, demoResolver, demoRuntime, demoDependencies, demoVFS, pageActions, }) {
10
+ export function GitbookStreamdown({ children, className, isAnimating, isStreaming = false, markdown, liveRenderer, sourceRenderer, demoResolver, demoRuntime, demoDependencies, demoVFS, pageActions, documentProperties, onTaskStateChange, }) {
11
11
  const content = markdown ?? children ?? "";
12
12
  const gitbookDoc = useMemo(() => parseMarkdown(isStreaming ? trimPartialInlineToken(content) : content), [content, isStreaming]);
13
- return (_jsx("div", { "aria-busy": isAnimating ?? isStreaming, className: className, "data-docstream": "", "data-streaming": isStreaming ? "" : undefined, children: _jsx(DocsRenderer, { doc: gitbookDoc, markdown: content, liveRenderer: liveRenderer, sourceRenderer: sourceRenderer, demoResolver: demoResolver, demoRuntime: demoRuntime, demoDependencies: demoDependencies, demoVFS: demoVFS, pageActions: isStreaming ? false : pageActions }) }));
13
+ return (_jsx("div", { "aria-busy": isAnimating ?? isStreaming, className: className, "data-docstream": "", "data-streaming": isStreaming ? "" : undefined, children: _jsx(DocsRenderer, { doc: gitbookDoc, markdown: content, liveRenderer: liveRenderer, sourceRenderer: sourceRenderer, demoResolver: demoResolver, demoRuntime: demoRuntime, demoDependencies: demoDependencies, demoVFS: demoVFS, pageActions: isStreaming ? false : pageActions, documentProperties: documentProperties, onTaskStateChange: onTaskStateChange }) }));
14
14
  }
15
15
  //# sourceMappingURL=streamdown.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"streamdown.js","sourceRoot":"","sources":["../src/streamdown.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAiF,MAAM,qBAAqB,CAAA;AACjI,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAEvE,uEAAuE;AACvE,yDAAyD;AACzD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AActG,MAAM,UAAU,iBAAiB,CAAC,EAChC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,WAAW,GAAG,KAAK,EACnB,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,WAAW,GACY;IACvB,MAAM,OAAO,GAAG,QAAQ,IAAI,QAAQ,IAAI,EAAE,CAAA;IAC1C,MAAM,UAAU,GAAG,OAAO,CACxB,GAAG,EAAE,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAC5E,CAAC,OAAO,EAAE,WAAW,CAAC,CACvB,CAAA;IAED,OAAO,CACL,2BACa,WAAW,IAAI,WAAW,EACrC,SAAS,EAAE,SAAS,oBACL,EAAE,oBACD,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,YAE5C,KAAC,YAAY,IACX,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,OAAO,EACjB,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,EAC9B,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,gBAAgB,EAClC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,GAC9C,GACE,CACP,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"streamdown.js","sourceRoot":"","sources":["../src/streamdown.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAiF,MAAM,qBAAqB,CAAA;AACjI,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAEvE,uEAAuE;AACvE,yDAAyD;AACzD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AActG,MAAM,UAAU,iBAAiB,CAAC,EAChC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,WAAW,GAAG,KAAK,EACnB,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,kBAAkB,EAClB,iBAAiB,GACM;IACvB,MAAM,OAAO,GAAG,QAAQ,IAAI,QAAQ,IAAI,EAAE,CAAA;IAC1C,MAAM,UAAU,GAAG,OAAO,CACxB,GAAG,EAAE,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAC5E,CAAC,OAAO,EAAE,WAAW,CAAC,CACvB,CAAA;IAED,OAAO,CACL,2BACa,WAAW,IAAI,WAAW,EACrC,SAAS,EAAE,SAAS,oBACL,EAAE,oBACD,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,YAE5C,KAAC,YAAY,IACX,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,OAAO,EACjB,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,EAC9B,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,gBAAgB,EAClC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,EAC9C,kBAAkB,EAAE,kBAAkB,EACtC,iBAAiB,EAAE,iBAAiB,GACpC,GACE,CACP,CAAA;AACH,CAAC"}
package/dist/styles.css CHANGED
@@ -1,3 +1,5 @@
1
+ @import "./docs/properties.css";
2
+ @import "./docs/tasks.css";
1
3
 
2
4
  [data-docstream],
3
5
  .docs-article {
@@ -2064,13 +2066,76 @@
2064
2066
  .docs-demo-files {
2065
2067
  flex-shrink: 0;
2066
2068
  box-sizing: border-box;
2067
- width: 188px;
2069
+ /* --docs-demo-files-w is set (px) once the reader drags the handle; 188px until then. */
2070
+ width: var(--docs-demo-files-w, 188px);
2068
2071
  padding: 8px;
2069
2072
  overflow-y: auto;
2070
2073
  border-right: 1px solid var(--gb-border);
2071
2074
  background: var(--gb-muted);
2072
2075
  }
2073
2076
 
2077
+ /* The drag handle between the file list and the source: zero layout width, with a wider hit area
2078
+ and the same grip as the preview handle straddling the divider. */
2079
+ .docs-demo-files-handle {
2080
+ position: relative;
2081
+ z-index: 2;
2082
+ flex: 0 0 0;
2083
+ width: 0;
2084
+ cursor: col-resize;
2085
+ touch-action: none;
2086
+ }
2087
+
2088
+ .docs-demo-files-handle::before {
2089
+ position: absolute;
2090
+ top: 0;
2091
+ bottom: 0;
2092
+ left: -5px;
2093
+ width: 10px;
2094
+ content: "";
2095
+ }
2096
+
2097
+ .docs-demo-files-handle::after {
2098
+ position: absolute;
2099
+ top: 50%;
2100
+ left: -2.5px;
2101
+ width: 5px;
2102
+ height: 40px;
2103
+ margin-top: -20px;
2104
+ border-radius: 3px;
2105
+ background: var(--gb-stage-dot);
2106
+ content: "";
2107
+ opacity: 0;
2108
+ transition: opacity 150ms ease, background 150ms ease, height 200ms ease, margin 200ms ease;
2109
+ }
2110
+
2111
+ .docs-demo-code:hover .docs-demo-files-handle::after {
2112
+ opacity: 1;
2113
+ }
2114
+
2115
+ .docs-demo-files-handle:hover::after,
2116
+ .docs-demo-files-handle:focus-visible::after,
2117
+ .docs-demo-files-handle[data-dragging]::after {
2118
+ height: 56px;
2119
+ margin-top: -28px;
2120
+ background: var(--gb-primary);
2121
+ opacity: 1;
2122
+ }
2123
+
2124
+ .docs-demo-files-handle:focus-visible {
2125
+ outline: none;
2126
+ }
2127
+
2128
+ .docs-demo-files-handle:focus-visible::before {
2129
+ outline: 2px solid var(--gb-primary);
2130
+ outline-offset: -1px;
2131
+ border-radius: 2px;
2132
+ }
2133
+
2134
+ .docs-demo-code[data-dragging] {
2135
+ cursor: col-resize;
2136
+ user-select: none;
2137
+ }
2138
+
2074
2139
  .docs-demo-files-h {
2075
2140
  padding: 6px 8px 8px;
2076
2141
  color: var(--gb-muted-foreground);
@@ -2143,18 +2208,174 @@
2143
2208
  align-items: center;
2144
2209
  justify-content: space-between;
2145
2210
  gap: 8px;
2146
- padding: 5px 6px 5px 14px;
2211
+ padding: 5px 6px 5px 8px;
2147
2212
  border-bottom: 1px solid var(--gb-border);
2148
2213
  color: var(--gb-muted-foreground);
2149
2214
  font: 12px/1 var(--docs-demo-mono);
2150
2215
  }
2151
2216
 
2152
- .docs-demo-source-path {
2217
+ /* Breadcrumb path: one button per segment. The list is clipped to the header; the fit logic in
2218
+ PathBreadcrumb collapses the middle to a "…" before that matters. */
2219
+ .docs-demo-crumbs {
2220
+ position: relative;
2221
+ flex: 1;
2222
+ min-width: 0;
2223
+ overflow: hidden;
2224
+ }
2225
+
2226
+ .docs-demo-crumbs > .docs-demo-crumbs-list {
2227
+ display: flex;
2228
+ align-items: center;
2229
+ margin: 0;
2230
+ padding: 0;
2231
+ list-style: none;
2232
+ white-space: nowrap;
2233
+ }
2234
+
2235
+ .docs-demo-crumbs > .docs-demo-crumbs-measure {
2236
+ position: absolute;
2237
+ top: 0;
2238
+ left: 0;
2239
+ height: 0;
2240
+ visibility: hidden;
2241
+ pointer-events: none;
2242
+ }
2243
+
2244
+ .docs-demo-crumbs-list > .docs-demo-crumb-item {
2245
+ display: flex;
2246
+ flex: 0 0 auto;
2247
+ align-items: center;
2248
+ min-width: 0;
2249
+ margin: 0;
2250
+ padding: 0;
2251
+ }
2252
+
2253
+ .docs-demo-crumbs-list > .docs-demo-crumb-item[data-last] {
2254
+ flex: 0 1 auto;
2255
+ }
2256
+
2257
+ .docs-demo-crumb-sep {
2258
+ flex-shrink: 0;
2259
+ color: var(--gb-muted-foreground);
2260
+ opacity: 0.7;
2261
+ }
2262
+
2263
+ .docs-demo-crumb {
2264
+ display: inline-flex;
2265
+ align-items: center;
2266
+ gap: 5px;
2267
+ min-width: 0;
2268
+ box-sizing: border-box;
2269
+ height: 24px;
2270
+ padding: 0 6px;
2271
+ border: 0;
2272
+ border-radius: 6px;
2273
+ background: none;
2274
+ color: var(--gb-muted-foreground);
2275
+ font: inherit;
2276
+ cursor: pointer;
2277
+ transition: background 150ms ease, color 150ms ease;
2278
+ }
2279
+
2280
+ .docs-demo-crumb svg {
2281
+ flex-shrink: 0;
2282
+ }
2283
+
2284
+ .docs-demo-crumb-text {
2153
2285
  overflow: hidden;
2154
2286
  text-overflow: ellipsis;
2155
2287
  white-space: nowrap;
2156
2288
  }
2157
2289
 
2290
+ .docs-demo-crumb:hover,
2291
+ .docs-demo-crumb[aria-expanded="true"] {
2292
+ background: var(--gb-hover);
2293
+ color: var(--gb-panel-foreground);
2294
+ }
2295
+
2296
+ .docs-demo-crumb[data-current] {
2297
+ color: var(--gb-panel-foreground);
2298
+ font-weight: 600;
2299
+ }
2300
+
2301
+ .docs-demo-crumb[data-current] svg {
2302
+ color: var(--gb-primary);
2303
+ }
2304
+
2305
+ .docs-demo-crumb:focus-visible {
2306
+ outline: 2px solid var(--gb-primary);
2307
+ outline-offset: -2px;
2308
+ }
2309
+
2310
+ /* The sibling / hidden-segment menu. Portalled next to the docs root (so it isn't clipped by the
2311
+ viewer) and fixed under its trigger; --crumb-x / --crumb-y are set from the trigger's rect. */
2312
+ .docs-demo-crumb-menu {
2313
+ position: fixed;
2314
+ top: var(--crumb-y, 0);
2315
+ left: var(--crumb-x, 0);
2316
+ z-index: 60;
2317
+ box-sizing: border-box;
2318
+ min-width: 160px;
2319
+ max-width: min(320px, calc(100vw - 16px));
2320
+ max-height: min(320px, calc(100vh - 16px));
2321
+ padding: 4px;
2322
+ overflow-y: auto;
2323
+ border: 1px solid var(--gb-border);
2324
+ border-radius: 10px;
2325
+ background: var(--gb-panel);
2326
+ box-shadow: var(--gb-menu-shadow);
2327
+ color: var(--gb-panel-foreground);
2328
+ font: 12.5px/1.3 var(--docs-demo-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
2329
+ }
2330
+
2331
+ .docs-demo-crumb-option {
2332
+ display: flex;
2333
+ align-items: center;
2334
+ gap: 7px;
2335
+ box-sizing: border-box;
2336
+ width: 100%;
2337
+ padding: 6px 8px;
2338
+ border: 0;
2339
+ border-radius: 6px;
2340
+ background: none;
2341
+ color: inherit;
2342
+ font: inherit;
2343
+ text-align: left;
2344
+ cursor: pointer;
2345
+ }
2346
+
2347
+ .docs-demo-crumb-option svg {
2348
+ flex-shrink: 0;
2349
+ color: var(--gb-muted-foreground);
2350
+ }
2351
+
2352
+ .docs-demo-crumb-option:hover,
2353
+ .docs-demo-crumb-option:focus-visible {
2354
+ outline: none;
2355
+ background: var(--gb-hover);
2356
+ }
2357
+
2358
+ .docs-demo-crumb-option[data-current] {
2359
+ color: var(--gb-primary);
2360
+ font-weight: 600;
2361
+ }
2362
+
2363
+ .docs-demo-crumb-option[data-current] svg {
2364
+ color: var(--gb-primary);
2365
+ }
2366
+
2367
+ .docs-demo-crumb-option-text {
2368
+ flex: 1;
2369
+ min-width: 0;
2370
+ overflow: hidden;
2371
+ text-overflow: ellipsis;
2372
+ white-space: nowrap;
2373
+ }
2374
+
2375
+ .docs-demo-crumb-option .docs-demo-crumb-check {
2376
+ margin-left: auto;
2377
+ }
2378
+
2158
2379
  .docs-demo-source-body {
2159
2380
  flex: 1;
2160
2381
  min-height: 0;
@@ -2195,7 +2416,8 @@
2195
2416
  }
2196
2417
 
2197
2418
  .docs-demo-files-h,
2198
- .docs-demo-dir {
2419
+ .docs-demo-dir,
2420
+ .docs-demo-files-handle {
2199
2421
  display: none;
2200
2422
  }
2201
2423
 
@@ -2338,6 +2560,8 @@
2338
2560
  }
2339
2561
 
2340
2562
  .docs-demo-handle::after,
2563
+ .docs-demo-files-handle::after,
2564
+ .docs-demo-crumb,
2341
2565
  .docs-page-actions-more svg {
2342
2566
  transition: none;
2343
2567
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brett_lamy/docstream",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "GitBook-aware readonly markdown and AI stream renderer.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -38,6 +38,20 @@
38
38
  "import": "./dist/gitbook/index.js",
39
39
  "default": "./dist/gitbook/index.js"
40
40
  },
41
+ "./properties": {
42
+ "source": "./src/docs/DocumentProperties.tsx",
43
+ "types": "./dist/docs/DocumentProperties.d.ts",
44
+ "import": "./dist/docs/DocumentProperties.js",
45
+ "default": "./dist/docs/DocumentProperties.js"
46
+ },
47
+ "./properties/styles.css": "./dist/docs/properties.css",
48
+ "./tasks": {
49
+ "source": "./src/docs/TaskCheckbox.tsx",
50
+ "types": "./dist/docs/TaskCheckbox.d.ts",
51
+ "import": "./dist/docs/TaskCheckbox.js",
52
+ "default": "./dist/docs/TaskCheckbox.js"
53
+ },
54
+ "./tasks/styles.css": "./dist/docs/tasks.css",
41
55
  "./streamdown": {
42
56
  "source": "./src/streamdown.tsx",
43
57
  "types": "./dist/streamdown.d.ts",
@@ -35,9 +35,10 @@ import { CollapsibleCode } from "../docs/CollapsibleCode"
35
35
  import { HighlightedCode } from "../docs/HighlightedCode"
36
36
  import { CopyButton } from "../docs/copy"
37
37
  import { PillTabs, Segmented, rovingKeyDown } from "../docs/controls"
38
- import { useDemoResolver, useDemoRuntime } from "./context"
38
+ import { DocstreamDemoContext, useDemoResolver, useDemoRuntime } from "./context"
39
39
  import { createDocstreamClipboard, type DemoSourceSnapshot, type DocstreamClipboard } from "./clipboard"
40
40
  import { CopyDemoButton } from "./copy-demo"
41
+ import { PathBreadcrumb } from "./PathBreadcrumb"
41
42
  import { languageForPath } from "./glob"
42
43
  import { useDemoVFSContext, useDemoVFSEntry, type DemoVFS } from "./vfs"
43
44
  import { cssToSurface, surfaceStyle, surfaceToCss } from "./surface"
@@ -455,6 +456,22 @@ function fileTree(files: DemoFile[]): TreeRow[] {
455
456
  return rows
456
457
  }
457
458
 
459
+ export const FILES_DEFAULT_WIDTH = 188
460
+ export const FILES_MIN_WIDTH = 140
461
+ /** The file list never takes more than this share of the viewer, so the source pane can't collapse. */
462
+ export const FILES_MAX_SHARE = 0.6
463
+
464
+ /** The widest the file list may be inside a viewer `container` px wide (never below the minimum). */
465
+ export function filesMaxWidth(container: number): number {
466
+ return Math.max(FILES_MIN_WIDTH, Math.floor(container * FILES_MAX_SHARE))
467
+ }
468
+
469
+ /** Clamp a file list width to [min, 60% of the viewer]. An unmeasured viewer (0) only enforces the minimum. */
470
+ export function clampFilesWidth(width: number, container: number): number {
471
+ const max = container > 0 ? filesMaxWidth(container) : Infinity
472
+ return Math.round(Math.min(max, Math.max(FILES_MIN_WIDTH, width)))
473
+ }
474
+
458
475
  function CodeView({ src, files, height, id }: { src: string; files: DemoFile[]; height: string; id: string }) {
459
476
  const [active, setActive] = useState(files[0]?.path ?? "")
460
477
  useEffect(() => {
@@ -465,8 +482,67 @@ function CodeView({ src, files, height, id }: { src: string; files: DemoFile[];
465
482
  const paths = files.map((f) => f.path)
466
483
  const tabId = (path: string) => `${id}-file-${paths.indexOf(path)}`
467
484
  const panelId = `${id}-source`
485
+
486
+ // The file list width is the user's choice (null: the default). It lives here, so it survives file switches.
487
+ const code = useRef<HTMLDivElement | null>(null)
488
+ const [chosen, setChosen] = useState<number | null>(null)
489
+ const [container, setContainer] = useState(0)
490
+ const [dragging, setDragging] = useState(false)
491
+ useEffect(() => {
492
+ const el = code.current
493
+ if (!el || typeof ResizeObserver === "undefined") return
494
+ const observer = new ResizeObserver(() => setContainer(Math.round(el.getBoundingClientRect().width)))
495
+ observer.observe(el)
496
+ return () => observer.disconnect()
497
+ }, [])
498
+ const width = clampFilesWidth(chosen ?? FILES_DEFAULT_WIDTH, container)
499
+ const max = container > 0 ? filesMaxWidth(container) : undefined
500
+
501
+ const startDrag = (event: ReactPointerEvent<HTMLDivElement>) => {
502
+ const el = code.current
503
+ if (!el || event.button !== 0) return
504
+ event.preventDefault()
505
+ const handle = event.currentTarget
506
+ const pointer = event.pointerId
507
+ try {
508
+ handle.setPointerCapture(pointer)
509
+ } catch {
510
+ /* not capturable (synthetic event): the window listeners still follow the drag */
511
+ }
512
+ const left = el.getBoundingClientRect().left
513
+ setDragging(true)
514
+ const move = (ev: PointerEvent) => setChosen(clampFilesWidth(ev.clientX - left, el.getBoundingClientRect().width))
515
+ const up = () => {
516
+ setDragging(false)
517
+ if (handle.hasPointerCapture?.(pointer)) handle.releasePointerCapture(pointer)
518
+ window.removeEventListener("pointermove", move)
519
+ window.removeEventListener("pointerup", up)
520
+ window.removeEventListener("pointercancel", up)
521
+ }
522
+ window.addEventListener("pointermove", move)
523
+ window.addEventListener("pointerup", up)
524
+ window.addEventListener("pointercancel", up)
525
+ }
526
+
527
+ const onResizeKey = (event: ReactKeyboardEvent<HTMLDivElement>) => {
528
+ const step = event.shiftKey ? 50 : 10
529
+ let next: number | undefined
530
+ if (event.key === "ArrowLeft") next = width - step
531
+ else if (event.key === "ArrowRight") next = width + step
532
+ else if (event.key === "Home") next = FILES_MIN_WIDTH
533
+ else if (event.key === "End") next = max ?? width
534
+ if (next === undefined) return
535
+ event.preventDefault()
536
+ setChosen(clampFilesWidth(next, container))
537
+ }
538
+
468
539
  return (
469
- <div className="docs-demo-code" style={{ height }}>
540
+ <div
541
+ ref={code}
542
+ className="docs-demo-code"
543
+ style={{ height, ...(chosen === null ? null : { "--docs-demo-files-w": `${width}px` }) } as CSSProperties}
544
+ data-dragging={dragging || undefined}
545
+ >
470
546
  <div
471
547
  className="docs-demo-files"
472
548
  role="tablist"
@@ -502,9 +578,25 @@ function CodeView({ src, files, height, id }: { src: string; files: DemoFile[];
502
578
  ),
503
579
  )}
504
580
  </div>
581
+ <div
582
+ className="docs-demo-files-handle"
583
+ role="separator"
584
+ tabIndex={0}
585
+ aria-orientation="vertical"
586
+ aria-label="Resize file list"
587
+ aria-controls={panelId}
588
+ aria-valuemin={FILES_MIN_WIDTH}
589
+ aria-valuemax={max}
590
+ aria-valuenow={width}
591
+ aria-valuetext={`${width} pixels wide`}
592
+ data-dragging={dragging || undefined}
593
+ onPointerDown={startDrag}
594
+ onKeyDown={onResizeKey}
595
+ onDoubleClick={() => setChosen(null)}
596
+ />
505
597
  <div className="docs-demo-source" role="tabpanel" id={panelId} aria-labelledby={file ? tabId(file.path) : undefined}>
506
598
  <div className="docs-demo-source-h">
507
- <span className="docs-demo-source-path">{src && file ? `${src}/${file.path}` : file?.path}</span>
599
+ {file ? <PathBreadcrumb src={src} path={file.path} paths={paths} onSelect={setActive} /> : <span className="docs-demo-crumbs" />}
508
600
  {file ? <CopyButton text={file.content} label={`Copy ${file.path}`} /> : null}
509
601
  </div>
510
602
  <div className="docs-demo-source-body" tabIndex={0} aria-label={file ? `${file.path} source` : undefined}>
@@ -954,7 +1046,9 @@ export function DemoFullscreen({ src, resolver: override, variant, className, st
954
1046
  if (!resolver) return <DemoError message={`No demo resolver for ${src}`} />
955
1047
  return (
956
1048
  <div className={className ? `docs-demo-fullscreen ${className}` : "docs-demo-fullscreen"} style={style} data-demo-src={src}>
957
- <DemoCanvas src={src} preview={{ kind: "component", component }} variant={chosen} bleed={metaValue?.bleed} look={metaValue} />
1049
+ <DocstreamDemoContext.Provider value={resolver}>
1050
+ <DemoCanvas src={src} preview={{ kind: "component", component }} variant={chosen} bleed={metaValue?.bleed} look={metaValue} />
1051
+ </DocstreamDemoContext.Provider>
958
1052
  </div>
959
1053
  )
960
1054
  }