@brett_lamy/docstream 1.1.1 → 1.2.1

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
@@ -284,9 +284,79 @@ half-arrived tag is hidden rather than shown as text.
284
284
  examples/button/sizes/
285
285
  index.tsx ← default export: ({ variant }) => …
286
286
  Sizes.css
287
- meta.json ← { title, description, height, variants: [{ id, label }], entry, layout, bleed }
287
+ meta.json ← { title, description, height, variants: [{ id, label }], entry, layout, bleed,
288
+ className, surface, status, variantsWidth }
288
289
  ```
289
290
 
291
+ `meta.height` is a minimum for single-file previews (in-page components may grow
292
+ past it) and the stage height for the multi-file viewer. `className` / `surface`
293
+ (a style object, CSS custom properties welcome) style the preview canvas,
294
+ `status` shows a small label in the header ("Live", "Needs network"), and
295
+ `variantsWidth` sizes the variant switch. Block-level demo roots fill the canvas
296
+ width; intrinsically sized ones (a button, an image) are centred.
297
+
298
+ #### Demos that carry their files (block form)
299
+
300
+ A `{% demo %}` can carry its files inline, one titled fence per file (entry
301
+ first, or `entry="App.tsx"`), closed by `{% enddemo %}`. A file that contains
302
+ fences gets a longer fence. This is what **Copy page** produces, so a copied page
303
+ renders back as the same page anywhere:
304
+
305
+ `````md
306
+ {% demo src="composer/scroll-fab" title="Scroll → FAB" height="380" variants="full:Full,compact:Compact" %}
307
+ ```tsx title="index.tsx"
308
+ import { items } from "./data"
309
+ export default function ScrollFab({ variant }) { … }
310
+ ```
311
+ ```ts title="data.ts"
312
+ export const items = ["a", "b"]
313
+ ```
314
+ ````md title="README.md"
315
+ ```sh
316
+ npm start
317
+ ```
318
+ ````
319
+ {% enddemo %}
320
+ `````
321
+
322
+ The self-closing tag (`{% demo … %}` or `{% demo … /%}`) keeps working. A tag
323
+ followed by a blank line and an ordinary code block (no `{% enddemo %}`) means
324
+ what it did before 1.2: a demo, then a code block. While streaming, a block whose
325
+ `{% enddemo %}` hasn't arrived is `open`: its code shows, its preview waits.
326
+
327
+ | The block has… | and there is… | Preview | Code view |
328
+ | --- | --- | --- | --- |
329
+ | `src` only | a resolver that knows `src` | the component, in-page | the resolver's files |
330
+ | inline files | a resolver that knows `src` (its `list()` includes it, and it loads) | the component, in-page | the resolver's files (they win) |
331
+ | inline files | no resolver, or one that doesn't know `src` | the files, run by the `demoRuntime` | the inline files |
332
+ | inline files | no runtime (e.g. the `/streamdown` entry) | a one-line note; the viewer opens on Code | the inline files |
333
+ | `src` only | no resolver | a placeholder chip | — |
334
+
335
+ The package root's `GitbookStreamdown` (`PlaygroundStreamdown`) runs inline demos
336
+ in almost-node by default. Elsewhere pass `demoRuntime`:
337
+
338
+ ```tsx
339
+ import { createAlmostNodeDemoRuntime } from "@brett_lamy/docstream/playground"
340
+
341
+ const demoRuntime = createAlmostNodeDemoRuntime({ workspaceOptions: { basePath: "/docs" } })
342
+
343
+ <MarkdownContent markdown={page} demoResolver={demoResolver} demoRuntime={demoRuntime}
344
+ demoDependencies={{ "@brett_lamy/ui": "^1.2.0" }} />
345
+ ```
346
+
347
+ How it runs (`createInlineDemoProject`): the files go under `/src/demo/`; a tiny
348
+ wrapper entry imports the entry file's default export (or first exported
349
+ function) and renders it with `variant` from the iframe's `?variant=` query, so
350
+ switching variants reloads the iframe, not Vite. An entry that mounts itself
351
+ (`createRoot(…)`) is used directly and gets no `variant`. npm imports: `react`
352
+ and `react-dom` are provided; `demoDependencies` (or a `package.json` among the
353
+ files) declares others. Limits: almost-node resolves bare imports from esm.sh at
354
+ the declared major version, so packages must work as browser ESM there; CSS
355
+ imported from packages may not load (import the CSS file from the demo instead,
356
+ or inline it); each inline demo boots its own container (the fullscreen overlay
357
+ boots a second one). `preloadDemos(resolver, srcs)` settles resolver demos before
358
+ the first render (server rendering, static export).
359
+
290
360
  Tell docstream where demos live with a `DemoResolver`. With Vite, three
291
361
  `import.meta.glob` maps are enough:
292
362
 
@@ -317,15 +387,21 @@ root and from `@brett_lamy/docstream/demo`, not from the Node-only `./vite` entr
317
387
  `pageActions` renders a shadcn-style **Copy page ▾** above the content, with
318
388
  Copy as Markdown, View as Markdown, Open in ChatGPT and Open in Claude. Pass
319
389
  `true`, or pass options such as `{ markdownUrl, pageUrl, prompt, actions, transform }`.
320
- You can also place `<DocPageActions markdown={page} />` yourself. Copying
321
- resolves every `{% demo %}` into its real files as titled fences, so the copied
322
- page is self-contained. The same resolution is available as a pure helper for
323
- build-time `.md` or `llms.txt` generation:
390
+ You can also place `<DocPageActions markdown={page} />` yourself. Copy and View
391
+ give **renderable docstream Markdown**: every `{% demo %}` keeps its tag (with its
392
+ `meta.json` defaults folded in as attributes) and gains its files inline, and
393
+ everything else is returned byte-for-byte, so pasting the copy into docstream
394
+ renders the same page — with or without the resolver. Open in ChatGPT / Claude
395
+ send the page URL. The same resolution is a pure helper for build-time `.md` or
396
+ `llms.txt` generation; the plain form is only produced when you ask for it:
324
397
 
325
398
  ```ts
326
399
  import { resolveDemosToMarkdown } from "@brett_lamy/docstream/demo"
327
400
 
328
- const md = await resolveDemosToMarkdown(page, demoResolver)
401
+ const md = await resolveDemosToMarkdown(page, demoResolver) // renderable (default)
402
+ const prompt = await resolveDemosToMarkdown(page, demoResolver, { format: "plain" })
403
+ // plain: "**Example — Title**" + fences per demo, commands as npm `sh` fences,
404
+ // titled tab sets as a heading + a bold label per tab — for LLM prompts only.
329
405
  ```
330
406
 
331
407
  ### Synced tabs (install instructions)
@@ -351,7 +427,61 @@ pnpm add @brett_lamy/docstream
351
427
  {% endtabs %}
352
428
  ````
353
429
 
354
- Every code block now also has a copy button.
430
+ Every code block has a copy button. Untitled blocks show just the code with the
431
+ button floating top-right; a header bar appears only when the block has a
432
+ `title` (file name).
433
+
434
+ ### Installation sections and command boxes
435
+
436
+ `{% command %}` holds **one** npm / npx command. docstream derives the pnpm,
437
+ yarn and bun forms and shows them as tabs (pnpm first) with a terminal glyph and
438
+ a copy button; the reader's manager is synced page-wide and across visits (key
439
+ `pm`, shared with `{% tabs sync="pm" %}`). Derivations: `npm install|i x` → `pnpm
440
+ add x` / `yarn add x` / `bun add x` (`-D`, `-E`, `-O`, `-g` mapped), `npm install`
441
+ → `<pm> install`, `npm uninstall x` → `<pm> remove x`, `npx x` / `npm exec x` →
442
+ `pnpm dlx x` / `yarn dlx x` / `bunx x`, `npm run s` → `pnpm s` / `yarn s` / `bun
443
+ run s`, `npm test|start` → `pnpm test` / `bun run test`, `npm create x` → `<pm>
444
+ create x`; other lines (comments, `cd`) are kept, `a && b` is converted per part.
445
+ Override a manager verbatim with `pnpm="…"`, `yarn="…"` or `bun="…"`; use another
446
+ sync group with `sync="…"`. Multi-line commands use the block form:
447
+
448
+ ```md
449
+ {% command %}npm install @brett_lamy/ui{% endcommand %}
450
+
451
+ {% command bun="bun add -d vitest" %}
452
+ npm install -D vitest
453
+ npm run test -- --watch
454
+ {% endcommand %}
455
+ ```
456
+
457
+ A tab set with a `title` renders as a section: the title is a real heading
458
+ (`h2`, or `level="3"`/`"4"`) with an anchor id, the tab switch sits as pill tabs
459
+ on the right of the heading row, and the active tab's blocks follow in the page
460
+ flow (no card around them). `documentOutline(doc)` lists these titles with the
461
+ page's headings for a table of contents.
462
+
463
+ `````md
464
+ {% tabs title="Installation" sync="install-method" %}
465
+ {% tab title="npm" %}
466
+ {% command %}npm install @brett_lamy/ui{% endcommand %}
467
+
468
+ Import the stylesheet once at your app's entry, then the parts:
469
+
470
+ ```tsx
471
+ import '@brett_lamy/ui/styles.css'
472
+ import { Composer } from '@brett_lamy/ui'
473
+ ```
474
+ {% endtab %}
475
+
476
+ {% tab title="shadcn CLI" %}
477
+ {% command %}npx shadcn@latest add https://blamy.github.io/ui/r/composer.json{% endcommand %}
478
+ {% endtab %}
479
+ {% endtabs %}
480
+ `````
481
+
482
+ Both round-trip through `parseMarkdown`/`serializeMarkdown`, are kept as they are
483
+ by Copy page, and a half-streamed one-line `{% command %}` is held back until it
484
+ closes.
355
485
 
356
486
  ## Assets and OpenAPI Specs
357
487
 
@@ -378,7 +508,10 @@ You can also resolve paths yourself with `resolveAsset`.
378
508
  - `createBundledSourceClient`: Imports bundled source modules in read-only production builds.
379
509
  - `DemoViewer` / `DemoFullscreen`: The `{% demo %}` viewer, and one demo filling the viewport (for `?demo=` deep links).
380
510
  - `createGlobDemoResolver`, `demoHref`, `demoFromSearch`: Build a `DemoResolver` from `import.meta.glob` maps; deep-link helpers.
381
- - `resolveDemosToMarkdown(markdown, resolver)`: Inlines each `{% demo %}` as its real files (pure, async).
511
+ - `resolveDemosToMarkdown(markdown, resolver, { format })`: Gives each `{% demo %}` its real files inline — renderable docstream by default, `format: "plain"` for LLM prompts (pure, async).
512
+ - `inlineDemoMarkdown(node, files, meta)`: One demo as a pasteable block-form `{% demo %}`.
513
+ - `preloadDemos(resolver, srcs)`: Settle resolver demos before the first render (SSR, static export).
514
+ - `createAlmostNodeDemoRuntime(options)` / `InlineDemoPreview` / `createInlineDemoProject(files, { entry, dependencies })` (`/playground`): Run inline demo files in almost-node.
382
515
  - `DocPageActions`: The "Copy page ▾" menu.
383
516
 
384
517
  ### Parser and Serializer
@@ -391,6 +524,9 @@ You can also resolve paths yourself with `resolveAsset`.
391
524
  - `serializeInline(nodes)`: Serializes inline nodes.
392
525
  - `plainText(nodes)`: Extracts plain text from inline nodes.
393
526
  - `refDefinitions(markdown)`: Reads reference-style link definitions.
527
+ - `packageManagerCommands(npmCommand, overrides)`: The npm / pnpm / yarn / bun forms of a command.
528
+ - `documentOutline(doc)` / `slugify(text)`: Headings (incl. titled tab sets) with anchor ids, for a TOC.
529
+ - `flattenForPlainMarkdown(doc)`: docstream-only blocks (commands, titled tabs) as plain Markdown.
394
530
 
395
531
  ### Types
396
532
 
@@ -411,7 +547,9 @@ The package CSS is intentionally token-driven. It uses normal CSS variables and
411
547
  }
412
548
  ```
413
549
 
414
- Import the CSS once, then set tokens globally in your app. Components also expose stable classes such as `docs-code`, `docs-tabs`, `docs-hint`, `docs-table`, and `docs-openapi`.
550
+ Import the CSS once, then set tokens globally in your app. The command box's
551
+ terminal glyph has its own tokens (`--gb-term-bg`, `--gb-term-fg`), so setting
552
+ panel colors to `currentColor` doesn't blank it. Components also expose stable classes such as `docs-code`, `docs-tabs`, `docs-hint`, `docs-table`, and `docs-openapi`.
415
553
 
416
554
  Code highlighting follows the surrounding `color-scheme`: set `color-scheme: dark` on a dark page (or any dark container) and code switches to the dark palette. Each token color is a custom property (`--docs-tok-keyword`, `--docs-tok-string`, `--docs-tok-type`, `--docs-tok-function`, `--docs-tok-number`, `--docs-tok-constant`, `--docs-tok-operator`, `--docs-tok-comment`) if you want your own palette.
417
555
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brett_lamy/docstream",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "GitBook-aware readonly markdown and AI stream renderer.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -15,6 +15,8 @@
15
15
  "files": [
16
16
  "src",
17
17
  "!src/**/*.test.ts",
18
+ "!src/**/*.test.tsx",
19
+ "!src/**/__fixtures__",
18
20
  "README.md"
19
21
  ],
20
22
  "sideEffects": [