@pyreon/mcp 0.13.0 → 0.14.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.
- package/README.md +62 -1
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +1306 -303
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +7 -1
- package/lib/types/index.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/anti-patterns.ts +210 -0
- package/src/api-reference.ts +495 -72
- package/src/changelog.ts +433 -0
- package/src/index.ts +279 -33
- package/src/manifest.ts +187 -0
- package/src/patterns.ts +243 -0
- package/src/tests/anti-patterns.test.ts +180 -0
- package/src/tests/changelog-server.test.ts +176 -0
- package/src/tests/changelog.test.ts +312 -0
- package/src/tests/manifest-snapshot.test.ts +36 -0
- package/src/tests/patterns-code.test.ts +216 -0
- package/src/tests/patterns-content.test.ts +147 -0
- package/src/tests/patterns-server.test.ts +160 -0
- package/src/tests/patterns.test.ts +236 -0
- package/src/tests/server-integration.test.ts +155 -0
- package/src/tests/test-audit-server.test.ts +128 -0
- package/src/tests/validate.test.ts +69 -0
package/src/api-reference.ts
CHANGED
|
@@ -655,6 +655,8 @@ setSearch({ page: "2" })`,
|
|
|
655
655
|
// @pyreon/head
|
|
656
656
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
657
657
|
|
|
658
|
+
// <gen-docs:api-reference:start @pyreon/head>
|
|
659
|
+
|
|
658
660
|
'head/useHead': {
|
|
659
661
|
signature: 'useHead(input: UseHeadInput | (() => UseHeadInput)): void',
|
|
660
662
|
example: `// Static:
|
|
@@ -665,23 +667,58 @@ useHead(() => ({
|
|
|
665
667
|
title: \`\${username()} — Profile\`,
|
|
666
668
|
meta: [{ property: "og:title", content: username() }]
|
|
667
669
|
}))`,
|
|
670
|
+
notes: 'Register head tags from any component in the tree. Pass a static `UseHeadInput` object for one-shot registration, or a `() => UseHeadInput` thunk for reactive re-registration when signal reads inside the thunk change. Calling `useHead()` outside a `HeadProvider` ancestor (CSR) or `renderWithHead()` invocation (SSR) is a silent no-op — it does not throw. See also: HeadProvider, renderWithHead.',
|
|
671
|
+
mistakes: `- Using \`\${...}\` in a \`titleTemplate\` string — the placeholder is \`%s\` (or pass a function form \`(title) => …\`)
|
|
672
|
+
- Calling \`useHead()\` outside any \`HeadProvider\` / \`renderWithHead()\` boundary — silent no-op, the entries simply go nowhere
|
|
673
|
+
- Wrapping the input in \`computed()\` instead of a thunk — pass a plain \`() => ({...})\` arrow; \`useHead\` registers its own effect
|
|
674
|
+
- Expecting \`</script>\` inside an inline script body to render verbatim — the SSR escaper rewrites it as \`<\/script>\` to prevent breaking out of the inline tag`,
|
|
668
675
|
},
|
|
669
676
|
|
|
670
677
|
'head/HeadProvider': {
|
|
671
|
-
signature: '
|
|
672
|
-
example:
|
|
678
|
+
signature: '(props: HeadProviderProps) => VNodeChild',
|
|
679
|
+
example: `<HeadProvider>{children}</HeadProvider>
|
|
680
|
+
|
|
681
|
+
// Client-side setup:
|
|
673
682
|
mount(
|
|
674
683
|
<HeadProvider>
|
|
675
684
|
<App />
|
|
676
685
|
</HeadProvider>,
|
|
677
686
|
document.getElementById("app")!
|
|
678
687
|
)`,
|
|
688
|
+
notes: 'Client-side context provider that collects every `useHead()` call from descendants and syncs the resolved tags into the live `document.head` element. Mount once near the application root. Auto-creates a `HeadContextValue` when no `context` prop is passed; nested providers each own an independent context. See also: useHead, renderWithHead, createHeadContext.',
|
|
689
|
+
mistakes: `- Mounting two \`HeadProvider\` instances at sibling roots — each owns an independent context, so a \`useHead()\` deeper in tree A is invisible to tree B
|
|
690
|
+
- Forgetting to mount \`HeadProvider\` and expecting \`useHead()\` to still update \`document.head\` — silent no-op outside a provider`,
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
'head/renderWithHead': {
|
|
694
|
+
signature: 'renderWithHead(app: VNode): Promise<{ html: string; head: string; htmlAttrs: string; bodyAttrs: string }>',
|
|
695
|
+
example: `import { renderWithHead } from '@pyreon/head'
|
|
696
|
+
|
|
697
|
+
const { html, head, htmlAttrs, bodyAttrs } = await renderWithHead(<App />)
|
|
698
|
+
const doc = \`<!doctype html><html\${htmlAttrs}><head>\${head}</head><body\${bodyAttrs}>\${html}</body></html>\``,
|
|
699
|
+
notes: 'SSR companion to `HeadProvider`. Renders the app to HTML via `renderToString` while collecting every `useHead()` call from the tree, then serializes the resolved tags into a single `head` string plus separate `htmlAttrs` / `bodyAttrs` strings. Async components that call `useHead()` in their body work — the renderer awaits suspended subtrees before serialization. See also: useHead, HeadProvider.',
|
|
700
|
+
mistakes: `- Awaiting \`renderWithHead\` and then NOT splicing \`head\` into the \`<head>\` element — every \`useHead()\` call quietly disappears
|
|
701
|
+
- Forgetting to interpolate \`htmlAttrs\` / \`bodyAttrs\` (the leading space is included in each string) — \`htmlAttrs.lang\` and \`bodyAttrs.class\` set via \`useHead\` won\'t reach the DOM`,
|
|
679
702
|
},
|
|
680
703
|
|
|
704
|
+
'head/createHeadContext': {
|
|
705
|
+
signature: '() => HeadContextValue',
|
|
706
|
+
example: `import { createHeadContext, HeadContext } from '@pyreon/head'
|
|
707
|
+
|
|
708
|
+
const ctx = createHeadContext()
|
|
709
|
+
provide(HeadContext, ctx)
|
|
710
|
+
// ... render tree that calls useHead() ...
|
|
711
|
+
const { tags, htmlAttrs, bodyAttrs } = ctx.resolve()`,
|
|
712
|
+
notes: 'Manual factory for a `HeadContextValue` — only needed when wiring up a custom SSR pipeline that bypasses `renderWithHead`, or when running multiple isolated head contexts in the same process. The value exposes `add` / `remove` / `resolve` / `resolveTitleTemplate` / `resolveHtmlAttrs` / `resolveBodyAttrs` for full programmatic control. See also: HeadProvider, renderWithHead.',
|
|
713
|
+
},
|
|
714
|
+
// <gen-docs:api-reference:end @pyreon/head>
|
|
715
|
+
|
|
681
716
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
682
717
|
// @pyreon/server
|
|
683
718
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
684
719
|
|
|
720
|
+
// <gen-docs:api-reference:start @pyreon/server>
|
|
721
|
+
|
|
685
722
|
'server/createHandler': {
|
|
686
723
|
signature: 'createHandler(options: HandlerOptions): (req: Request) => Promise<Response>',
|
|
687
724
|
example: `import { createHandler } from "@pyreon/server"
|
|
@@ -692,17 +729,26 @@ export default createHandler({
|
|
|
692
729
|
clientEntry: "/src/entry-client.ts",
|
|
693
730
|
mode: "stream", // or "string"
|
|
694
731
|
})`,
|
|
732
|
+
notes: 'Build a production SSR handler from your `App`, `routes`, and optional template / client entry / middleware. The template is precompiled once at handler-creation (split into 4 parts to skip three string scans per request); a missing `<!--pyreon-app-->` placeholder throws at creation time, not per request. Middleware runs before render with `ctx.locals` for cross-middleware data passing — return a `Response` to short-circuit the chain. `mode: "stream"` uses `renderToStream` so Suspense boundaries flush out-of-order; `mode: "string"` uses `renderToString` (default). See also: prerender, island, useRequestLocals.',
|
|
733
|
+
mistakes: `- Omitting \`<!--pyreon-app-->\` from the custom template — throws at handler-creation, not per request
|
|
734
|
+
- Returning a \`Response\` from middleware and expecting downstream middleware to still run — the chain short-circuits on the first \`Response\`
|
|
735
|
+
- Reading \`ctx.locals\` from inside the component without \`useRequestLocals()\` — the component tree only sees locals when bridged through that hook
|
|
736
|
+
- Forgetting to escape user data inserted into a custom template — \`createHandler\` only escapes its own loader-data injection (\`</script>\` → \`<\/script>\`); your template content is your responsibility`,
|
|
695
737
|
},
|
|
696
738
|
|
|
697
739
|
'server/island': {
|
|
698
|
-
signature:
|
|
699
|
-
'island(loader: () => Promise<ComponentFn>, options: { name: string; hydrate?: HydrationStrategy }): ComponentFn',
|
|
740
|
+
signature: 'island(loader: () => Promise<ComponentFn>, options: { name: string; hydrate?: HydrationStrategy }): ComponentFn',
|
|
700
741
|
example: `const SearchBar = island(
|
|
701
742
|
() => import("./SearchBar"),
|
|
702
743
|
{ name: "SearchBar", hydrate: "visible" }
|
|
703
744
|
)
|
|
704
745
|
|
|
705
746
|
// Hydration strategies: "load" | "idle" | "visible" | "media" | "never"`,
|
|
747
|
+
notes: 'Wrap a lazily-loaded component in a `<pyreon-island>` boundary with a hydration strategy. The rest of the page stays HTML-only; only the island fetches its JS bundle and hydrates. Strategies: `"load"` (immediate), `"idle"` (`requestIdleCallback`), `"visible"` (IntersectionObserver), `"media(query)"` (matchMedia), `"never"` (HTML-only, no JS). Props passed to islands are JSON-serialized — non-JSON values (functions, symbols, undefined, children) are stripped. See also: createHandler, hydrateIslands.',
|
|
748
|
+
mistakes: `- Passing function props (event handlers, callbacks) — silently stripped during JSON serialization, the island sees \`undefined\`
|
|
749
|
+
- Passing children to an island — stripped; islands cannot render arbitrary descendant trees from props
|
|
750
|
+
- Forgetting to call \`hydrateIslands({ Name: () => import("./Path") })\` on the client — islands render as HTML and never hydrate
|
|
751
|
+
- Using a duplicate \`name\` across two islands — the client-side registry collapses them, only one loader will fire`,
|
|
706
752
|
},
|
|
707
753
|
|
|
708
754
|
'server/prerender': {
|
|
@@ -712,7 +758,12 @@ export default createHandler({
|
|
|
712
758
|
paths: ["/", "/about", "/blog/1", "/blog/2"],
|
|
713
759
|
outDir: "./dist",
|
|
714
760
|
})`,
|
|
761
|
+
notes: 'Static-site generator built on `createHandler`. Walks the `paths` array (or async generator), invokes the handler for each path, and writes the rendered HTML to `outDir/<path>.html`. The `onPage(path, html)` callback fires per page so callers can post-process or stream output. Validates `outDir` against path traversal (`../` segments are rejected). Errors per-page are collected in the result, not thrown. See also: createHandler.',
|
|
762
|
+
mistakes: `- Passing a relative \`outDir\` and being surprised when it resolves against \`process.cwd()\` — pass an absolute path for predictability
|
|
763
|
+
- Expecting per-page errors to throw — they\'re collected in \`result.errors\`; check the array after \`await\`
|
|
764
|
+
- Generating thousands of paths without batching — the function processes the array sequentially; if you need parallelism, batch the \`paths\` array yourself`,
|
|
715
765
|
},
|
|
766
|
+
// <gen-docs:api-reference:end @pyreon/server>
|
|
716
767
|
|
|
717
768
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
718
769
|
// @pyreon/runtime-dom
|
|
@@ -2127,6 +2178,8 @@ Posts.useTable({ columns: ['title', 'author'] })`,
|
|
|
2127
2178
|
// @pyreon/lint
|
|
2128
2179
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2129
2180
|
|
|
2181
|
+
// <gen-docs:api-reference:start @pyreon/lint>
|
|
2182
|
+
|
|
2130
2183
|
'lint/lint': {
|
|
2131
2184
|
signature: 'lint(options?: LintOptions): LintResult',
|
|
2132
2185
|
example: `import { lint } from "@pyreon/lint"
|
|
@@ -2144,34 +2197,29 @@ lint({
|
|
|
2144
2197
|
"pyreon/no-window-in-ssr": { exemptPaths: ["src/foundation/"] },
|
|
2145
2198
|
},
|
|
2146
2199
|
})`,
|
|
2147
|
-
notes:
|
|
2148
|
-
'Programmatic API. 59 rules across 12 categories. Auto-loads .pyreonlintrc.json. Presets: recommended, strict, app, lib. Per-rule options via tuple form in config (`["error", { exemptPaths: [...] }]`) or `ruleOptionsOverrides`. Wrong-typed options surface on `result.configDiagnostics`. Uses oxc-parser with AST caching.',
|
|
2200
|
+
notes: '59 rules across 12 categories. Auto-loads `.pyreonlintrc.json`. Presets: `recommended`, `strict`, `app`, `lib`. Per-rule options via tuple form in config (`["error", { exemptPaths: [...] }]`) or `ruleOptionsOverrides`. Wrong-typed options surface on `result.configDiagnostics`. Uses `oxc-parser` with AST caching. See also: lintFile, getPreset, AstCache.',
|
|
2149
2201
|
},
|
|
2150
2202
|
|
|
2151
2203
|
'lint/lintFile': {
|
|
2152
|
-
signature:
|
|
2153
|
-
'lintFile(filePath: string, sourceText: string, rules: Rule[], config: LintConfig, cache?: AstCache, configDiagnosticsSink?: ConfigDiagnostic[]): LintFileResult',
|
|
2204
|
+
signature: 'lintFile(filePath: string, sourceText: string, rules: Rule[], config: LintConfig, cache?: AstCache, configDiagnosticsSink?: ConfigDiagnostic[]): LintFileResult',
|
|
2154
2205
|
example: `import { lintFile, allRules, getPreset, AstCache } from "@pyreon/lint"
|
|
2155
2206
|
|
|
2156
2207
|
const cache = new AstCache()
|
|
2157
2208
|
const config = getPreset("recommended")
|
|
2158
2209
|
const configSink: ConfigDiagnostic[] = []
|
|
2159
2210
|
const result = lintFile("app.tsx", source, allRules, config, cache, configSink)`,
|
|
2160
|
-
notes:
|
|
2161
|
-
'Low-level single-file API. Optional AstCache for repeat runs (FNV-1a hash keyed). Optional `configDiagnosticsSink` collects malformed-option diagnostics; without it they print to stderr.',
|
|
2211
|
+
notes: 'Low-level single-file API. Optional `AstCache` for repeat runs (FNV-1a hash keyed). Optional `configDiagnosticsSink` collects malformed-option diagnostics; without it they print to stderr. See also: lint, AstCache.',
|
|
2162
2212
|
},
|
|
2163
2213
|
|
|
2164
2214
|
'lint/cli': {
|
|
2165
|
-
signature:
|
|
2166
|
-
'pyreon-lint [--preset name] [--fix] [--format text|json|compact] [--quiet] [--watch] [--list] [--config path] [--ignore path] [--rule id=severity] [--rule-options id=\'{json}\'] [path...]',
|
|
2215
|
+
signature: `pyreon-lint [--preset name] [--fix] [--format text|json|compact] [--quiet] [--watch] [--list] [--config path] [--ignore path] [--rule id=severity] [--rule-options id='{json}'] [path...]`,
|
|
2167
2216
|
example: `pyreon-lint --preset strict --quiet # CI mode
|
|
2168
2217
|
pyreon-lint --fix # auto-fix
|
|
2169
2218
|
pyreon-lint --watch src/ # watch mode
|
|
2170
2219
|
pyreon-lint --list # list all 59 rules
|
|
2171
2220
|
pyreon-lint --format json # machine-readable
|
|
2172
2221
|
pyreon-lint --rule-options 'pyreon/no-window-in-ssr={"exemptPaths":["src/foundation/"]}' src/`,
|
|
2173
|
-
notes:
|
|
2174
|
-
"CLI entry. Config: .pyreonlintrc.json (reference schema/pyreonlintrc.schema.json for IDE autocomplete), package.json 'pyreonlint' field. Ignore: .pyreonlintignore + .gitignore. Watch: fs.watch recursive with 100ms debounce. `--rule-options id='{json}'` passes per-rule options on a single run.",
|
|
2222
|
+
notes: `CLI entry. Config: \`.pyreonlintrc.json\` (reference \`schema/pyreonlintrc.schema.json\` for IDE autocomplete) or \`package.json\`'s \`'pyreonlint'\` field. Ignore: \`.pyreonlintignore\` + \`.gitignore\`. Watch: \`fs.watch\` recursive with 100ms debounce. \`--rule-options id='{json}'\` passes per-rule options on a single run. See also: lint.`,
|
|
2175
2223
|
},
|
|
2176
2224
|
|
|
2177
2225
|
'lint/no-process-dev-gate': {
|
|
@@ -2184,8 +2232,7 @@ if (__DEV__) console.warn('hello')
|
|
|
2184
2232
|
// @ts-ignore — provided by Vite/Rolldown at build time
|
|
2185
2233
|
const __DEV__ = import.meta.env?.DEV === true
|
|
2186
2234
|
if (__DEV__) console.warn('hello')`,
|
|
2187
|
-
notes:
|
|
2188
|
-
"The `typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'` pattern works in vitest (Node, `process` is defined) but is silently dead code in real Vite browser bundles because Vite does NOT polyfill `process` for the client. Every `console.warn` gated on the broken constant never fires for real users in dev mode — unit tests pass while users get nothing. Use `import.meta.env.DEV` instead — Vite/Rolldown literal-replace it at build time, prod tree-shakes the warning to zero bytes, and vitest sets it to `true` automatically. Server-only packages (`zero`, `core/server`, `core/runtime-server`, `vite-plugin`, `cli`, `lint`, `mcp`, `storybook`, `typescript`) and test files are exempt. Reference implementation: `packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions`. The rule has an auto-fix that replaces the broken expression with `import.meta.env?.DEV === true`.",
|
|
2235
|
+
notes: `The \`typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\` pattern works in vitest (Node, \`process\` is defined) but is silently dead code in real Vite browser bundles because Vite does NOT polyfill \`process\` for the client. Every \`console.warn\` gated on the broken constant never fires for real users in dev mode — unit tests pass while users get nothing. Use \`import.meta.env.DEV\` instead — Vite/Rolldown literal-replace it at build time, prod tree-shakes the warning to zero bytes, and vitest sets it to \`true\` automatically. Server-only packages (\`zero\`, \`core/server\`, \`core/runtime-server\`, \`vite-plugin\`, \`cli\`, \`lint\`, \`mcp\`, \`storybook\`, \`typescript\`) and test files are exempt. Reference implementation: \`packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions\`. The rule has an auto-fix that replaces the broken expression with \`import.meta.env?.DEV === true\`. See also: require-browser-smoke-test.`,
|
|
2189
2236
|
mistakes: `- Copying the \`typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\` pattern from existing codebases — it works in Node but is dead in browser bundles
|
|
2190
2237
|
- Trying to test with \`delete globalThis.process\` — vitest's own \`import.meta.env\` depends on \`process\`, so deleting it breaks the FIXED gate too (not because the gate is wrong, but because vitest can't resolve it)
|
|
2191
2238
|
- Adding \`process: { env: { ... } }\` polyfills to vite.config.ts as a workaround — fix the source instead
|
|
@@ -2206,13 +2253,19 @@ if (__DEV__) console.warn('hello')`,
|
|
|
2206
2253
|
]
|
|
2207
2254
|
}
|
|
2208
2255
|
}`,
|
|
2209
|
-
notes:
|
|
2210
|
-
"Locks in the durability of the T1.1 browser smoke harness (PRs #224, #227, #229, #231). Every browser-categorized package MUST ship at least one \`*.browser.test.{ts,tsx}\` file under \`src/\`. Without this rule, new browser packages can quietly ship without smoke coverage and we drift back to the world before T1.1 — happy-dom silently masks environment-divergence bugs (PR #197 mock-vnode metadata drop, PR #200 \`typeof process\` dead code, multi-word event delegation bug). Default browser-package list mirrors \`.claude/rules/test-environment-parity.md\`. The rule fires once per package on its \`src/index.ts\`, walks the package directory looking for \`*.browser.test.*\`, and reports if none are found. Off in \`app\` preset because apps don't ship as packages with smoke obligations.",
|
|
2256
|
+
notes: `Locks in the durability of the T1.1 browser smoke harness (PRs #224, #227, #229, #231). Every browser-categorized package MUST ship at least one \`*.browser.test.{ts,tsx}\` file under \`src/\`. Without this rule, new browser packages can quietly ship without smoke coverage and we drift back to the world before T1.1 — happy-dom silently masks environment-divergence bugs (PR #197 mock-vnode metadata drop, PR #200 \`typeof process\` dead code, multi-word event delegation bug). Default browser-package list mirrors \`.claude/rules/test-environment-parity.md\`. The rule fires once per package on its \`src/index.ts\`, walks the package directory looking for \`*.browser.test.*\`, and reports if none are found. Off in \`app\` preset because apps don't ship as packages with smoke obligations. See also: no-process-dev-gate.`,
|
|
2211
2257
|
mistakes: `- Adding a new browser-running package without a browser test — the rule will fail your PR
|
|
2212
2258
|
- Hardcoding the browser-package list in the rule — the list lives in \`.claude/rules/browser-packages.json\` (single source of truth), not in the rule source
|
|
2213
2259
|
- Disabling the rule globally — use \`exemptPaths\` to exempt specific packages still under construction
|
|
2214
2260
|
- Shipping a \`sanity.browser.test.ts\` with \`expect(1).toBe(1)\` just to satisfy the rule — it passes but provides zero signal. The rule is a GATE, not a quality check; review actual contents on PR`,
|
|
2215
2261
|
},
|
|
2262
|
+
// <gen-docs:api-reference:end @pyreon/lint>
|
|
2263
|
+
|
|
2264
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2265
|
+
// @pyreon/mcp
|
|
2266
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2267
|
+
|
|
2268
|
+
// <gen-docs:api-reference:start @pyreon/mcp>
|
|
2216
2269
|
|
|
2217
2270
|
'mcp/get_browser_smoke_status': {
|
|
2218
2271
|
signature: 'tool: get_browser_smoke_status — no args',
|
|
@@ -2220,18 +2273,103 @@ if (__DEV__) console.warn('hello')`,
|
|
|
2220
2273
|
// "which Pyreon packages are missing browser smoke coverage?"
|
|
2221
2274
|
// Tool walks packages/, matches against .claude/rules/browser-packages.json,
|
|
2222
2275
|
// returns a coverage report.`,
|
|
2223
|
-
notes:
|
|
2224
|
-
"Companion to the `pyreon/require-browser-smoke-test` lint rule. Reports which browser-categorized Pyreon packages have at least one `*.browser.test.{ts,tsx}` file under `src/`. Uses the same `.claude/rules/browser-packages.json` single source of truth as the rule + the CI script. Lets an AI agent check coverage before writing a new browser package (so it adds a smoke test in the same PR) instead of discovering the failure when CI runs. Falls back with a clear message if the JSON isn't present (e.g. consumer apps that don't ship the Pyreon monorepo layout).",
|
|
2276
|
+
notes: `Companion to the \`pyreon/require-browser-smoke-test\` lint rule. Reports which browser-categorized Pyreon packages have at least one \`*.browser.test.{ts,tsx}\` file under \`src/\`. Uses the same \`.claude/rules/browser-packages.json\` single source of truth as the rule + the CI script. Lets an AI agent check coverage before writing a new browser package (so it adds a smoke test in the same PR) instead of discovering the failure when CI runs. Falls back with a clear message if the JSON isn't present (e.g. consumer apps that don't ship the Pyreon monorepo layout). See also: audit_test_environment.`,
|
|
2225
2277
|
mistakes: `- Using the tool's output as a substitute for running the CI script — this tool only checks file existence, not the self-expiring-exemption check that \`bun run lint:browser-smoke\` performs`,
|
|
2226
2278
|
},
|
|
2227
2279
|
|
|
2280
|
+
'mcp/get_api': {
|
|
2281
|
+
signature: 'tool: get_api({ package: string; symbol: string }) → APIEntry',
|
|
2282
|
+
example: `// Agent-side
|
|
2283
|
+
get_api({ package: 'flow', symbol: 'createFlow' })
|
|
2284
|
+
get_api({ package: '@pyreon/router', symbol: 'useTypedSearchParams' })`,
|
|
2285
|
+
notes: `Look up any Pyreon API by \`package\` (e.g. \`"flow"\` or \`"@pyreon/flow"\`) and \`symbol\` (e.g. \`"createFlow"\`). Returns the canonical signature, example, foot-gun catalogue, and cross-references — drawn from \`api-reference.ts\`, which is regenerated from each package\'s \`manifest.ts\`. The single agent-facing entry point for "what does this API do and how do I avoid the common mistakes." See also: validate, get_pattern.`,
|
|
2286
|
+
},
|
|
2287
|
+
|
|
2288
|
+
'mcp/validate': {
|
|
2289
|
+
signature: 'tool: validate({ code: string; filename?: string }) → Diagnostics[]',
|
|
2290
|
+
example: `validate({ code: \`
|
|
2291
|
+
function MyComp(props) {
|
|
2292
|
+
const { value } = props // → props-destructured
|
|
2293
|
+
return <For each={items}>{...}</For> // → for-missing-by
|
|
2294
|
+
}
|
|
2295
|
+
\` })`,
|
|
2296
|
+
notes: 'Two AST-based detectors run in parallel: `detectReactPatterns` flags "coming from React" mistakes (`useState`, `useEffect`, `className`, `onChange` on inputs, React-package imports), and `detectPyreonPatterns` flags "using Pyreon wrong" mistakes (`<For>` missing `by`, props destructured at component signature, `typeof process` dev gates, raw `addEventListener`, `Date.now() + Math.random()` IDs). Diagnostics are merged + sorted by line / column for top-down reading. See also: get_anti_patterns, migrate_react.',
|
|
2297
|
+
},
|
|
2298
|
+
|
|
2299
|
+
'mcp/migrate_react': {
|
|
2300
|
+
signature: 'tool: migrate_react({ code: string; filename?: string }) → MigrationResult',
|
|
2301
|
+
example: `migrate_react({ code: \`
|
|
2302
|
+
import { useState, useEffect } from 'react'
|
|
2303
|
+
function Counter() {
|
|
2304
|
+
const [count, setCount] = useState(0)
|
|
2305
|
+
useEffect(() => { console.log(count) }, [count])
|
|
2306
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
|
2307
|
+
}
|
|
2308
|
+
\` })`,
|
|
2309
|
+
notes: 'Convert React code to idiomatic Pyreon. Handles `useState` → `signal()`, `useEffect` → `effect()`, `className` → `class`, `onChange` → `onInput`, `useMemo` → `computed()`, React imports → Pyreon imports. Reports per-edit fixable diagnostics so callers can apply or review. See also: validate.',
|
|
2310
|
+
},
|
|
2311
|
+
|
|
2312
|
+
'mcp/diagnose': {
|
|
2313
|
+
signature: 'tool: diagnose({ error: string }) → DiagnoseResult',
|
|
2314
|
+
example: `diagnose({ error: 'Cannot redefine property X on object [object Object]' })
|
|
2315
|
+
// → cause: configurable: false on a getter; fix: set configurable: true`,
|
|
2316
|
+
notes: 'Parse a Pyreon runtime / build error message into structured fix information: probable cause, recommended fix, related docs, and the `.claude/rules/anti-patterns.md` entry (if any) the error matches. Useful when an agent sees a stack trace and wants to skip the "search the codebase for similar errors" step. See also: validate, get_anti_patterns.',
|
|
2317
|
+
},
|
|
2318
|
+
|
|
2319
|
+
'mcp/get_routes': {
|
|
2320
|
+
signature: 'tool: get_routes() → Route[]',
|
|
2321
|
+
example: `get_routes()
|
|
2322
|
+
// → [{ path: '/', name: 'home', hasLoader: true, params: [] }, ...]`,
|
|
2323
|
+
notes: 'List every route in the current project — path, loader presence, guards, params, and named-route name. Walks the project source from `process.cwd()` down. Cached per server instance with auto-invalidation on `cwd` change. See also: get_components.',
|
|
2324
|
+
},
|
|
2325
|
+
|
|
2326
|
+
'mcp/get_components': {
|
|
2327
|
+
signature: 'tool: get_components() → ComponentInfo[]',
|
|
2328
|
+
example: `get_components()
|
|
2329
|
+
// → [{ name: 'Button', file: 'src/Button.tsx', props: ['onClick', 'children'], signals: ['count'] }, ...]`,
|
|
2330
|
+
notes: 'List every component in the current project with its props and signal usage. Same scanner as `get_routes`. Useful for an agent before generating new code that needs to reference existing components. See also: get_routes.',
|
|
2331
|
+
},
|
|
2332
|
+
|
|
2333
|
+
'mcp/get_pattern': {
|
|
2334
|
+
signature: 'tool: get_pattern({ name?: string }) → PatternBody | string[]',
|
|
2335
|
+
example: `get_pattern({ name: 'controllable-state' })
|
|
2336
|
+
// → full canonical pattern body
|
|
2337
|
+
get_pattern({})
|
|
2338
|
+
// → [{ name: 'controllable-state', summary: '...' }, ...]`,
|
|
2339
|
+
notes: 'Fetch a canonical "how do I do X" pattern body from `docs/patterns/`. Eight foundational patterns ship: `dev-warnings`, `controllable-state`, `ssr-safe-hooks`, `signal-writes`, `keyed-lists`, `reactive-context`, `event-listeners`, `form-fields`. Omit `name` to list available patterns. Drop a new `docs/patterns/<slug>.md` file to add one — picked up on next call. See also: get_anti_patterns.',
|
|
2340
|
+
},
|
|
2341
|
+
|
|
2342
|
+
'mcp/get_anti_patterns': {
|
|
2343
|
+
signature: `tool: get_anti_patterns({ category?: 'reactivity' | 'jsx' | 'context' | 'architecture' | 'testing' | 'lifecycle' | 'documentation' | 'all' }) → AntiPattern[]`,
|
|
2344
|
+
example: `get_anti_patterns({ category: 'reactivity' })
|
|
2345
|
+
// → ['Bare signal in JSX text', 'Stale closures', 'Destructuring props', ...]`,
|
|
2346
|
+
notes: 'Browse the anti-patterns catalog parsed from `.claude/rules/anti-patterns.md`. Each entry surfaces its `[detector: <code>]` tag inline so an agent can pair the catalog entry with the live static detector exposed by `validate`. Optional `category` filter; default returns all categories. See also: validate, get_pattern.',
|
|
2347
|
+
},
|
|
2348
|
+
|
|
2349
|
+
'mcp/get_changelog': {
|
|
2350
|
+
signature: 'tool: get_changelog({ package?: string; limit?: number; includeDependencyUpdates?: boolean; since?: string }) → ChangelogEntry[]',
|
|
2351
|
+
example: `get_changelog({ package: 'flow', limit: 5 })
|
|
2352
|
+
get_changelog({ package: '@pyreon/router', since: '0.12.0' })`,
|
|
2353
|
+
notes: 'Recent release notes for any `@pyreon/*` package without scraping `git log`. Parses `packages/**/CHANGELOG.md` into version entries (`{ version, changes[], dependencyUpdates[], empty }`) and returns the N most recent substantive versions (default 5). Filters out ceremonial version bumps (pure dependency-update releases with no user-facing body) by default — opt back in with `includeDependencyUpdates: true`. `since: "0.12.0"` returns the delta from a known floor — useful when an agent knows the version it was trained against. See also: get_api.',
|
|
2354
|
+
},
|
|
2355
|
+
|
|
2356
|
+
'mcp/audit_test_environment': {
|
|
2357
|
+
signature: `tool: audit_test_environment({ minRisk?: 'high' | 'medium' | 'low'; limit?: number }) → AuditReport`,
|
|
2358
|
+
example: `audit_test_environment({ minRisk: 'medium', limit: 10 })
|
|
2359
|
+
// → grouped report with HIGH / MEDIUM / LOW sections`,
|
|
2360
|
+
notes: `Scan every \`*.test.{ts,tsx}\` under \`packages/\` for the mock-vnode anti-pattern that caused PR #197\'s silent metadata drop. Files are classified HIGH / MEDIUM / LOW based on the balance of mock-vnode literals + helpers + helper-call sites vs real \`h()\` calls + \`@pyreon/core\` import. Three context-aware skips (helper-def vs binding discrimination, type-guard call-arg skip, template-string fixture mask) keep the false-positive rate low. Run before merging a new test file or after a framework change. See also: get_browser_smoke_status.`,
|
|
2361
|
+
},
|
|
2362
|
+
// <gen-docs:api-reference:end @pyreon/mcp>
|
|
2363
|
+
|
|
2364
|
+
|
|
2228
2365
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2229
2366
|
// @pyreon/ui-core
|
|
2230
2367
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2231
2368
|
|
|
2369
|
+
// <gen-docs:api-reference:start @pyreon/ui-core>
|
|
2370
|
+
|
|
2232
2371
|
'ui-core/PyreonUI': {
|
|
2233
|
-
signature:
|
|
2234
|
-
"PyreonUI(props: { theme?: Theme; mode?: 'light' | 'dark' | 'system'; inversed?: boolean; children: VNodeChild }): VNodeChild",
|
|
2372
|
+
signature: `(props: { theme?: Theme; mode?: 'light' | 'dark' | 'system'; inversed?: boolean; children: VNodeChild }) => VNodeChild`,
|
|
2235
2373
|
example: `import { PyreonUI } from "@pyreon/ui-core"
|
|
2236
2374
|
import { enrichTheme } from "@pyreon/unistyle"
|
|
2237
2375
|
|
|
@@ -2243,27 +2381,32 @@ const theme = enrichTheme({ colors: { primary: "#3b82f6" } })
|
|
|
2243
2381
|
|
|
2244
2382
|
// mode="system" auto-detects OS dark mode via prefers-color-scheme
|
|
2245
2383
|
// inversed flips the resolved mode (light↔dark)`,
|
|
2246
|
-
notes:
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
-
|
|
2384
|
+
notes: `Unified provider replacing the previous theme / mode / config split (3 nested providers became 1). Accepts an enriched \`theme\` object (merge with defaults via \`enrichTheme()\`), a \`mode\` of \`'light' | 'dark' | 'system'\`, and an optional \`inversed\` flip. When \`mode='system'\`, the provider subscribes to \`matchMedia('(prefers-color-scheme: dark)')\` and re-resolves the mode reactively. Calls \`init()\` internally so consumers don\'t need to wire it up themselves. Whole-theme swaps (user-preference themes) propagate through the styler resolver and re-resolve CSS without remounting the VNode. See also: useMode, enrichTheme, init.`,
|
|
2385
|
+
mistakes: `- Using \`ThemeProvider\` + \`ModeProvider\` + \`ConfigProvider\` separately — \`PyreonUI\` is the single replacement covering all three
|
|
2386
|
+
- Forgetting \`enrichTheme()\` — raw theme objects miss default breakpoints / spacing / unit utilities
|
|
2387
|
+
- Destructuring \`props\` inside the provider — components run once; destructuring captures values at setup. Read \`props.mode\` lazily inside reactive scopes
|
|
2388
|
+
- Re-augmenting the \`ThemeDefault\` / \`StylesDefault\` interfaces in your app — \`@pyreon/ui-theme\` already augments them; double-augmentation throws TS2320`,
|
|
2250
2389
|
},
|
|
2251
2390
|
|
|
2252
2391
|
'ui-core/useMode': {
|
|
2253
|
-
signature:
|
|
2392
|
+
signature: `useMode(): Signal<'light' | 'dark'>`,
|
|
2254
2393
|
example: `import { useMode } from "@pyreon/ui-core"
|
|
2255
2394
|
|
|
2256
2395
|
const mode = useMode()
|
|
2257
2396
|
// mode() returns "light" or "dark" (resolved, reactive)
|
|
2258
2397
|
// Reflects OS preference when PyreonUI mode="system"`,
|
|
2259
|
-
notes:
|
|
2260
|
-
|
|
2398
|
+
notes: `Returns the currently resolved mode as a reactive signal — \`'light'\` or \`'dark'\`. When the nearest \`PyreonUI\` ancestor uses \`mode='system'\`, the signal reflects the OS preference and updates when the user changes their system setting. When \`inversed\` is true on any ancestor, the mode is flipped before resolution. Component-scoped subscription — readers re-run only when the resolved mode actually changes. See also: PyreonUI.`,
|
|
2399
|
+
mistakes: `- Reading \`useMode()\` without calling it — the value is a \`Signal\`; use \`mode()\` to read
|
|
2400
|
+
- Using \`useMode()\` outside any \`PyreonUI\` ancestor — falls back to a default but loses the reactive system / inversed handling`,
|
|
2261
2401
|
},
|
|
2402
|
+
// <gen-docs:api-reference:end @pyreon/ui-core>
|
|
2262
2403
|
|
|
2263
2404
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2264
2405
|
// @pyreon/unistyle
|
|
2265
2406
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2266
2407
|
|
|
2408
|
+
// <gen-docs:api-reference:start @pyreon/unistyle>
|
|
2409
|
+
|
|
2267
2410
|
'unistyle/enrichTheme': {
|
|
2268
2411
|
signature: 'enrichTheme(theme: PartialTheme): Theme',
|
|
2269
2412
|
example: `import { enrichTheme } from "@pyreon/unistyle"
|
|
@@ -2274,10 +2417,101 @@ const theme = enrichTheme({
|
|
|
2274
2417
|
})
|
|
2275
2418
|
|
|
2276
2419
|
// Merges user overrides with default breakpoints, spacing, and units`,
|
|
2277
|
-
notes:
|
|
2278
|
-
|
|
2420
|
+
notes: 'Merge a partial theme with the full default theme (breakpoints, spacing, unit utilities, fallback colors). Always call this before passing a user theme to `PyreonUI` — raw theme objects miss the default breakpoints and spacing scale that the rest of the UI system reads from. Idempotent: enriching an already-enriched theme is a no-op. See also: breakpoints, createMediaQueries.',
|
|
2421
|
+
mistakes: `- Passing the raw partial theme to \`<PyreonUI theme={...}>\` without enriching — \`theme.breakpoints\` is undefined and every responsive prop falls back to the desktop value
|
|
2422
|
+
- Mutating the theme after passing it to \`PyreonUI\` — the styler resolver caches off the theme identity; clone + re-enrich for whole-theme swaps`,
|
|
2279
2423
|
},
|
|
2280
2424
|
|
|
2425
|
+
'unistyle/breakpoints': {
|
|
2426
|
+
signature: 'breakpoints(): Breakpoints',
|
|
2427
|
+
example: `import { breakpoints } from '@pyreon/unistyle'
|
|
2428
|
+
|
|
2429
|
+
const bp = breakpoints()
|
|
2430
|
+
// { xs: 0, sm: 640, md: 768, lg: 1024, xl: 1280, xxl: 1536 }`,
|
|
2431
|
+
notes: 'Return the default breakpoint set keyed by name (`xs`, `sm`, `md`, `lg`, `xl`, `xxl`) with min-width values in pixels. The same map is folded into `enrichTheme()` output, so most consumers read `theme.breakpoints` rather than calling this directly. Use it when you need the defaults outside a theme context (e.g. building a custom theme programmatically). See also: enrichTheme, createMediaQueries.',
|
|
2432
|
+
},
|
|
2433
|
+
|
|
2434
|
+
'unistyle/createMediaQueries': {
|
|
2435
|
+
signature: 'createMediaQueries(breakpoints: Breakpoints): Record<string, string>',
|
|
2436
|
+
example: `import { createMediaQueries, breakpoints } from '@pyreon/unistyle'
|
|
2437
|
+
|
|
2438
|
+
const queries = createMediaQueries(breakpoints())
|
|
2439
|
+
// { xs: '@media (min-width: 0)', sm: '@media (min-width: 640px)', md: '@media (min-width: 768px)', ... }`,
|
|
2440
|
+
notes: 'Build a record of media-query strings keyed by breakpoint name. Each value is a `min-width` query — `xs` is `(min-width: 0)`, `sm` becomes `(min-width: 640px)`, and so on. Used internally by `makeItResponsive()`; expose to consumers when they need to compose custom CSS-in-JS rules outside the responsive-prop pipeline. See also: breakpoints, makeItResponsive.',
|
|
2441
|
+
},
|
|
2442
|
+
|
|
2443
|
+
'unistyle/makeItResponsive': {
|
|
2444
|
+
signature: 'makeItResponsive<T>(options: { value: T | T[] | Record<string, T>; property: string; theme: Theme }): string',
|
|
2445
|
+
example: `import { makeItResponsive } from '@pyreon/unistyle'
|
|
2446
|
+
|
|
2447
|
+
makeItResponsive({ value: 16, property: 'padding', theme })
|
|
2448
|
+
// → 'padding: 16px;'
|
|
2449
|
+
|
|
2450
|
+
makeItResponsive({ value: [8, 12, 16], property: 'padding', theme })
|
|
2451
|
+
// → 'padding: 8px; @media (min-width: 640px) { padding: 12px } @media (min-width: 768px) { padding: 16px }'
|
|
2452
|
+
|
|
2453
|
+
makeItResponsive({ value: { xs: 8, md: 16, xl: 24 }, property: 'padding', theme })
|
|
2454
|
+
// → '@media (min-width: 0) { padding: 8px } @media (min-width: 768px) { padding: 16px } @media (min-width: 1280px) { padding: 24px }'`,
|
|
2455
|
+
notes: 'Resolve a responsive prop value to CSS for the current screen. Accepts three input shapes: single value (applies at all breakpoints), mobile-first array `[xs, sm, md, lg]` (each entry maps to the next breakpoint), or breakpoint object `{ xs: ..., md: ..., xl: ... }` (named keys map directly). The output is a CSS string with media queries already embedded; insert into a styled component template literal. See also: createMediaQueries, styles.',
|
|
2456
|
+
mistakes: `- Passing CSS-spec property names (\`borderTopWidth\`) — unistyle uses property-first naming (\`borderWidthTop\`); the responsive transformer expects the unistyle convention
|
|
2457
|
+
- Forgetting to pass an enriched theme — without \`theme.breakpoints\`, the array form falls back to the first value at every breakpoint`,
|
|
2458
|
+
},
|
|
2459
|
+
|
|
2460
|
+
'unistyle/styles': {
|
|
2461
|
+
signature: 'styles(theme: Theme): string',
|
|
2462
|
+
example: `import { styles, enrichTheme } from '@pyreon/unistyle'
|
|
2463
|
+
|
|
2464
|
+
const theme = enrichTheme({ colors: { primary: '#3b82f6' } })
|
|
2465
|
+
const css = styles(theme)
|
|
2466
|
+
// → ':root { --color-primary: #3b82f6; --spacing-xs: 4px; ... }'`,
|
|
2467
|
+
notes: `Generate the CSS string for a complete theme — colors, spacing, fonts, breakpoints, the works. Used to produce the cascade of CSS variables / global declarations that backs every styled component. Most consumers don\'t call this directly; the \`PyreonUI\` provider invokes it internally on theme mount. See also: enrichTheme, extendCss.`,
|
|
2468
|
+
},
|
|
2469
|
+
|
|
2470
|
+
'unistyle/alignContent': {
|
|
2471
|
+
signature: `alignContent(options: { alignX?: AlignXKey; alignY?: AlignYKey; direction?: 'row' | 'column' | 'inline' | 'rows' }): string`,
|
|
2472
|
+
example: `import { alignContent } from '@pyreon/unistyle'
|
|
2473
|
+
|
|
2474
|
+
alignContent({ alignX: 'center', alignY: 'start', direction: 'row' })
|
|
2475
|
+
// → 'justify-content: center; align-items: flex-start;'
|
|
2476
|
+
|
|
2477
|
+
alignContent({ alignX: 'spaceBetween', direction: 'inline' })
|
|
2478
|
+
// → 'justify-content: space-between;'`,
|
|
2479
|
+
notes: `Resolve \`alignX\` / \`alignY\` / \`direction\` shorthand to the matching flex / grid CSS (\`justify-content\`, \`align-items\`). The Element / Row / Column primitives use this internally — it\'s exposed for custom layout components that want the same alignment semantics. \`direction: "inline"\` maps to \`row\`; \`direction: "rows"\` maps to \`column\`. See also: makeItResponsive.`,
|
|
2480
|
+
},
|
|
2481
|
+
|
|
2482
|
+
'unistyle/extendCss': {
|
|
2483
|
+
signature: 'extendCss(base: ExtendCss, override?: ExtendCss): ExtendCss',
|
|
2484
|
+
example: `import { extendCss } from '@pyreon/unistyle'
|
|
2485
|
+
|
|
2486
|
+
const base = { color: 'red', hover: { color: 'darkred' } }
|
|
2487
|
+
const extended = extendCss(base, { hover: { background: 'pink' } })
|
|
2488
|
+
// → { color: 'red', hover: { color: 'darkred', background: 'pink' } }`,
|
|
2489
|
+
notes: 'Extend a CSS definition (theme block, style descriptor) with overrides — deep-merges nested objects without losing the base. Used by rocketstyle dimension chains to layer dimension-specific CSS over a baseline. The base is not mutated; the result is a new object. See also: styles.',
|
|
2490
|
+
},
|
|
2491
|
+
|
|
2492
|
+
'unistyle/stripUnit': {
|
|
2493
|
+
signature: 'stripUnit(value: string | number): number',
|
|
2494
|
+
example: `import { stripUnit } from '@pyreon/unistyle'
|
|
2495
|
+
|
|
2496
|
+
stripUnit('16px') // → 16
|
|
2497
|
+
stripUnit('1.5rem') // → 1.5
|
|
2498
|
+
stripUnit(16) // → 16`,
|
|
2499
|
+
notes: 'Strip the unit suffix from a CSS value and return the numeric part (`"16px"` → `16`, `"1.5rem"` → `1.5`). Returns the input unchanged when already a number. Useful for arithmetic on theme values declared as strings (`"16px"`) without manually parsing. See also: value, values.',
|
|
2500
|
+
},
|
|
2501
|
+
|
|
2502
|
+
'unistyle/value': {
|
|
2503
|
+
signature: 'value(input: PropertyValue, fallback?: PropertyValue): UnitValue',
|
|
2504
|
+
example: `import { value } from '@pyreon/unistyle'
|
|
2505
|
+
|
|
2506
|
+
value(16) // → { value: 16, unit: 'px' }
|
|
2507
|
+
value('1.5rem') // → { value: 1.5, unit: 'rem' }
|
|
2508
|
+
value('50%') // → { value: 50, unit: '%' }
|
|
2509
|
+
value('garbage', 0) // → { value: 0, unit: 'px' }`,
|
|
2510
|
+
notes: 'Parse and validate a single property value into a `UnitValue` shape (`{ value, unit }`). Accepts numbers (treated as pixels), strings with units (`"16px"`, `"1rem"`, `"50%"`), or objects already in `UnitValue` form. Optional `fallback` is returned when the input is invalid. The companion `values()` does the same over an array. See also: stripUnit, values.',
|
|
2511
|
+
},
|
|
2512
|
+
// <gen-docs:api-reference:end @pyreon/unistyle>
|
|
2513
|
+
|
|
2514
|
+
|
|
2281
2515
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2282
2516
|
// @pyreon/storybook
|
|
2283
2517
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -2289,12 +2523,12 @@ const theme = enrichTheme({
|
|
|
2289
2523
|
// <gen-docs:api-reference:start @pyreon/rx>
|
|
2290
2524
|
|
|
2291
2525
|
'rx/rx': {
|
|
2292
|
-
signature: 'Readonly<{ filter, map, sortBy, groupBy, keyBy, uniqBy, take, skip, last, chunk, flatten, find, mapValues, count, sum, min, max, average, distinct, scan, combine, debounce, throttle, search, pipe }>',
|
|
2526
|
+
signature: 'Readonly<{ filter, map, sortBy, groupBy, keyBy, uniqBy, take, skip, last, chunk, flatten, find, mapValues, first, compact, reverse, partition, takeWhile, dropWhile, unique, sample, count, sum, min, max, average, reduce, every, some, distinct, scan, combine, zip, merge, debounce, throttle, search, pipe }>',
|
|
2293
2527
|
example: `const active = rx.filter(users, u => u.active) // Computed<User[]>
|
|
2294
2528
|
const sorted = rx.sortBy(active, 'name') // Computed<User[]>
|
|
2295
2529
|
const total = rx.sum(users, u => u.age) // Computed<number>
|
|
2296
2530
|
const grouped = rx.groupBy(users, u => u.department) // Computed<Map<string, User[]>>`,
|
|
2297
|
-
notes: 'Namespaced object exposing all
|
|
2531
|
+
notes: 'Namespaced object exposing all 37 reactive transform functions plus `pipe`. Use `rx.filter(...)` for dot-notation style, or destructure individual functions for tree-shaking. Every function is overloaded: `Signal<T[]>` input produces `Computed<T[]>` that auto-tracks, plain `T[]` input produces a static result. See also: pipe, filter.',
|
|
2298
2532
|
mistakes: `- Expecting \`rx.filter(signal, pred)\` to return a plain array — signal inputs always produce \`Computed\` outputs. Call the result to read: \`active()\`
|
|
2299
2533
|
- Passing a signal accessor (\`() => items()\`) instead of the signal itself — pass \`items\` not \`() => items()\`; the function checks for \`.subscribe\` to detect signals`,
|
|
2300
2534
|
},
|
|
@@ -2406,6 +2640,8 @@ setUrlRouter(router)
|
|
|
2406
2640
|
// @pyreon/document-primitives
|
|
2407
2641
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2408
2642
|
|
|
2643
|
+
// <gen-docs:api-reference:start @pyreon/document-primitives>
|
|
2644
|
+
|
|
2409
2645
|
'document-primitives/extractDocNode': {
|
|
2410
2646
|
signature: 'extractDocNode(templateFn: () => VNode, options?: ExtractOptions): DocNode',
|
|
2411
2647
|
example: `import {
|
|
@@ -2414,52 +2650,239 @@ setUrlRouter(router)
|
|
|
2414
2650
|
} from '@pyreon/document-primitives'
|
|
2415
2651
|
import { download } from '@pyreon/document'
|
|
2416
2652
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
)
|
|
2434
|
-
}
|
|
2435
|
-
|
|
2436
|
-
// One-step extraction. The two-step createDocumentExport(...).getDocNode()
|
|
2437
|
-
// form is still exported for callers that want to pass the helper
|
|
2438
|
-
// object around, but extractDocNode is the recommended form.
|
|
2439
|
-
const tree = extractDocNode(() => <ResumeTemplate resume={store.resume} />)
|
|
2440
|
-
await download(tree, 'resume.pdf')
|
|
2441
|
-
await download(tree, 'resume.docx')
|
|
2442
|
-
await download(tree, 'resume.html')
|
|
2443
|
-
await download(tree, 'resume.md')`,
|
|
2444
|
-
notes:
|
|
2445
|
-
"18 primitives: DocDocument, DocPage, DocSection, DocRow, DocColumn, DocHeading, DocText, DocLink, DocImage, DocTable, DocList, DocListItem, DocCode, DocDivider, DocSpacer, DocButton, DocQuote, DocPageBreak. Same component tree renders in browser AND exports — primitives carry _documentType statics that extractDocumentTree (from @pyreon/connector-document) walks to produce a DocNode for @pyreon/document's render() to consume. DocDocument's title/author/subject accept either a string OR a `() => string` accessor; function values are stored in _documentProps and resolved at extraction time so reactive metadata works without `const initial = get()` workarounds. PR #197 also fixed a latent bug in extractDocumentTree: it now CALLS rocketstyle component functions to read post-attrs _documentProps, where before it only looked at the JSX vnode's props directly — every primitive's metadata was silently dropped during export until that fix landed.",
|
|
2446
|
-
mistakes: `- Calling props.title() at the top of a template body to "fix" reactivity — components run ONCE at mount, so this captures the initial value forever. Pass the accessor through to DocDocument as-is: <DocDocument title={() => get().name}>
|
|
2447
|
-
- DocRow direction: layout props (direction, gap) go in .attrs() not .theme(). Element accepts 'inline' | 'rows' | 'reverseInline' | 'reverseRows' — 'row' is NOT valid
|
|
2448
|
-
- For text children reactivity, pass a signal accessor and read inside body: <DocText>{() => store.field()}</DocText>
|
|
2449
|
-
- Don't declare runtime-filled fields (tag, _documentProps) in the rocketstyle .attrs<P>() generic — they leak as required JSX props
|
|
2450
|
-
- Using createDocumentExport(...).getDocNode() in new code — prefer extractDocNode(fn) which is one call instead of two. createDocumentExport is kept for backward compat`,
|
|
2653
|
+
const tree = extractDocNode(() => (
|
|
2654
|
+
<DocDocument title="Quarterly Report" author="Aisha">
|
|
2655
|
+
<DocPage>
|
|
2656
|
+
<DocHeading level="h1">Q4 Results</DocHeading>
|
|
2657
|
+
<DocText>Revenue grew 23% YoY.</DocText>
|
|
2658
|
+
</DocPage>
|
|
2659
|
+
</DocDocument>
|
|
2660
|
+
))
|
|
2661
|
+
await download(tree, 'report.pdf')
|
|
2662
|
+
await download(tree, 'report.docx')`,
|
|
2663
|
+
notes: `18 primitives: \`DocDocument\`, \`DocPage\`, \`DocSection\`, \`DocRow\`, \`DocColumn\`, \`DocHeading\`, \`DocText\`, \`DocLink\`, \`DocImage\`, \`DocTable\`, \`DocList\`, \`DocListItem\`, \`DocCode\`, \`DocDivider\`, \`DocSpacer\`, \`DocButton\`, \`DocQuote\`, \`DocPageBreak\`. Same component tree renders in browser AND exports — primitives carry \`_documentType\` statics that \`extractDocumentTree\` (from \`@pyreon/connector-document\`) walks to produce a \`DocNode\` for \`@pyreon/document\`\'s \`render()\` to consume. \`DocDocument\`\'s \`title\` / \`author\` / \`subject\` accept either a string OR a \`() => string\` accessor; function values are stored in \`_documentProps\` and resolved at extraction time so reactive metadata works without \`const initial = get()\` workarounds. PR #197 also fixed a latent bug in \`extractDocumentTree\`: it now CALLS rocketstyle component functions to read post-attrs \`_documentProps\`, where before it only looked at the JSX vnode\'s props directly — every primitive\'s metadata was silently dropped during export until that fix landed. See also: createDocumentExport.`,
|
|
2664
|
+
mistakes: `- Calling \`props.title()\` at the top of a template body to "fix" reactivity — components run ONCE at mount, so this captures the initial value forever. Pass the accessor through to DocDocument as-is: \`<DocDocument title={() => get().name}>\`
|
|
2665
|
+
- DocRow direction: layout props (direction, gap) go in \`.attrs()\` not \`.theme()\`. Element accepts \`'inline'\` | \`'rows'\` | \`'reverseInline'\` | \`'reverseRows'\` — \`'row'\` is NOT valid
|
|
2666
|
+
- For text children reactivity, pass a signal accessor and read inside body: \`<DocText>{() => store.field()}</DocText>\`
|
|
2667
|
+
- Don't declare runtime-filled fields (\`tag\`, \`_documentProps\`) in the rocketstyle \`.attrs<P>()\` generic — they leak as required JSX props
|
|
2668
|
+
- Using \`createDocumentExport(...).getDocNode()\` in new code — prefer \`extractDocNode(fn)\` which is one call instead of two. \`createDocumentExport\` is kept for backward compat`,
|
|
2451
2669
|
},
|
|
2452
2670
|
|
|
2453
2671
|
'document-primitives/createDocumentExport': {
|
|
2454
|
-
signature:
|
|
2455
|
-
'createDocumentExport(templateFn: () => VNode): { getDocNode(): DocNode }',
|
|
2672
|
+
signature: 'createDocumentExport(templateFn: () => VNode): { getDocNode(): DocNode }',
|
|
2456
2673
|
example: `// Two-step form (kept for backward compat). New code should
|
|
2457
2674
|
// prefer the one-step extractDocNode helper.
|
|
2458
2675
|
import { createDocumentExport } from '@pyreon/document-primitives'
|
|
2459
2676
|
|
|
2460
2677
|
const helper = createDocumentExport(() => <Resume name="Aisha" />)
|
|
2461
2678
|
const tree = helper.getDocNode()`,
|
|
2462
|
-
notes:
|
|
2463
|
-
|
|
2464
|
-
|
|
2679
|
+
notes: 'Wrapper around `extractDocNode`. The wrapper-object form is kept for callers that want to pass the helper around (e.g. to wrapper components that take a `DocumentExport` instance). New code should use `extractDocNode(templateFn)` which is one call instead of two. See also: extractDocNode.',
|
|
2680
|
+
},
|
|
2681
|
+
|
|
2682
|
+
'document-primitives/DocDocument': {
|
|
2683
|
+
signature: '(props: { title?: string | (() => string); author?: string | (() => string); subject?: string | (() => string); children: VNodeChild }) => VNodeChild',
|
|
2684
|
+
example: `<DocDocument title="Quarterly Report" author="Aisha" subject="Q4 2025">
|
|
2685
|
+
<DocPage>...</DocPage>
|
|
2686
|
+
</DocDocument>
|
|
2687
|
+
|
|
2688
|
+
// Reactive metadata via accessor
|
|
2689
|
+
<DocDocument title={() => \`\${user().name} — Resume\`}>
|
|
2690
|
+
<DocPage>...</DocPage>
|
|
2691
|
+
</DocDocument>`,
|
|
2692
|
+
notes: 'Root container for a document tree — produces a `_documentType: "document"` node. Accepts optional metadata: `title`, `author`, `subject`. Each accepts either a plain string OR a `() => string` accessor; function values are stored in `_documentProps` and resolved at extraction time so each export call reads the LIVE value from any underlying signal. See also: DocPage, extractDocNode.',
|
|
2693
|
+
},
|
|
2694
|
+
|
|
2695
|
+
'document-primitives/DocPage': {
|
|
2696
|
+
signature: `(props: { size?: string; orientation?: 'portrait' | 'landscape'; children: VNodeChild }) => VNodeChild`,
|
|
2697
|
+
example: `<DocDocument>
|
|
2698
|
+
<DocPage size="A4" orientation="portrait">
|
|
2699
|
+
<DocHeading level="h1">Page 1</DocHeading>
|
|
2700
|
+
</DocPage>
|
|
2701
|
+
<DocPage size="A4" orientation="landscape">
|
|
2702
|
+
<DocHeading level="h1">Page 2 — landscape</DocHeading>
|
|
2703
|
+
</DocPage>
|
|
2704
|
+
</DocDocument>`,
|
|
2705
|
+
notes: 'A page boundary inside a `DocDocument`. Paginated outputs (PDF, DOCX) treat each `DocPage` as a separate page; flow outputs (HTML, Markdown) render the contents inline with no page boundary. `size` and `orientation` configure paginated formats — common values: `"A4"`, `"Letter"`, `"Legal"`. See also: DocDocument, DocPageBreak.',
|
|
2706
|
+
},
|
|
2707
|
+
|
|
2708
|
+
'document-primitives/DocSection': {
|
|
2709
|
+
signature: `(props: { direction?: 'column' | 'row'; children: VNodeChild }) => VNodeChild`,
|
|
2710
|
+
example: `<DocPage>
|
|
2711
|
+
<DocSection direction="column">
|
|
2712
|
+
<DocHeading level="h2">Introduction</DocHeading>
|
|
2713
|
+
<DocText>Background paragraph.</DocText>
|
|
2714
|
+
</DocSection>
|
|
2715
|
+
</DocPage>`,
|
|
2716
|
+
notes: 'Semantic grouping inside a page. Default `direction` is `"column"` (children stack vertically); `"row"` arranges them horizontally. Use to group related content for visual rhythm and for export targets that emit semantic section markers (HTML `<section>`, DOCX section breaks). See also: DocRow, DocColumn.',
|
|
2717
|
+
},
|
|
2718
|
+
|
|
2719
|
+
'document-primitives/DocRow': {
|
|
2720
|
+
signature: '(props: { children: VNodeChild }) => VNodeChild',
|
|
2721
|
+
example: `<DocRow>
|
|
2722
|
+
<DocText>Name:</DocText>
|
|
2723
|
+
<DocText>Aisha Patel</DocText>
|
|
2724
|
+
</DocRow>`,
|
|
2725
|
+
notes: 'Horizontal layout container — children flow inline with a fixed 8px gap. Use for side-by-side content (label + value pairs, columns of metadata, button rows). Layout-only — no user-configurable props on this primitive; for columns with custom widths use `DocColumn` inside. See also: DocColumn, DocSection.',
|
|
2726
|
+
},
|
|
2727
|
+
|
|
2728
|
+
'document-primitives/DocColumn': {
|
|
2729
|
+
signature: '(props: { width?: number | string; children: VNodeChild }) => VNodeChild',
|
|
2730
|
+
example: `<DocRow>
|
|
2731
|
+
<DocColumn width="30%">
|
|
2732
|
+
<DocText>Label</DocText>
|
|
2733
|
+
</DocColumn>
|
|
2734
|
+
<DocColumn width="70%">
|
|
2735
|
+
<DocText>Value</DocText>
|
|
2736
|
+
</DocColumn>
|
|
2737
|
+
</DocRow>`,
|
|
2738
|
+
notes: `A column inside a row layout. Optional \`width\` controls the column\'s share of the row — accepts a number (interpreted as pixels) or a string (\`"50%"\`, \`"1fr"\`). When omitted, columns share available width equally. Most common shape is \`<DocRow><DocColumn width="30%" /> <DocColumn width="70%" /></DocRow>\`. See also: DocRow, DocSection.`,
|
|
2739
|
+
},
|
|
2740
|
+
|
|
2741
|
+
'document-primitives/DocHeading': {
|
|
2742
|
+
signature: `(props: { level?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; children: VNodeChild }) => VNodeChild`,
|
|
2743
|
+
example: `<DocHeading level="h1">Quarterly Report</DocHeading>
|
|
2744
|
+
<DocHeading level="h2">Q4 Results</DocHeading>
|
|
2745
|
+
<DocHeading level="h3">Revenue Breakdown</DocHeading>`,
|
|
2746
|
+
notes: 'Heading text — `level` (`"h1"` through `"h6"`) controls both visual size and the semantic level emitted to outputs (HTML `<h1>...<h6>`, DOCX heading styles, Markdown `#`...`######`). Default `level` is `"h1"`. Used for document structure that downstream tooling can build a TOC from. See also: DocText, DocSection.',
|
|
2747
|
+
},
|
|
2748
|
+
|
|
2749
|
+
'document-primitives/DocText': {
|
|
2750
|
+
signature: '(props: { children: VNodeChild }) => VNodeChild',
|
|
2751
|
+
example: `<DocText>Static paragraph content.</DocText>
|
|
2752
|
+
|
|
2753
|
+
// Reactive children
|
|
2754
|
+
<DocText>{() => \`Hello, \${user().name}\`}</DocText>`,
|
|
2755
|
+
notes: 'Paragraph / inline text. The most common primitive — wraps any text content for the document. Children may be string literals OR signal accessors (`{() => store.field()}`) for reactive content. Visual styling (font weight, variant) is controlled via rocketstyle dimension props on the wrapping component definition. See also: DocHeading, DocLink.',
|
|
2756
|
+
},
|
|
2757
|
+
|
|
2758
|
+
'document-primitives/DocLink': {
|
|
2759
|
+
signature: '(props: { href?: string; children: VNodeChild }) => VNodeChild',
|
|
2760
|
+
example: `<DocText>
|
|
2761
|
+
Read more on
|
|
2762
|
+
<DocLink href="https://pyreon.dev">our blog</DocLink>
|
|
2763
|
+
for the latest releases.
|
|
2764
|
+
</DocText>`,
|
|
2765
|
+
notes: 'Hyperlink within text. `href` is the URL — defaults to `"#"`. Outputs that support hyperlinks (HTML, PDF, DOCX, email) render this as a clickable link; flat outputs (plain text, certain Slack variants) render the link target inline as `text (href)`. See also: DocText.',
|
|
2766
|
+
},
|
|
2767
|
+
|
|
2768
|
+
'document-primitives/DocImage': {
|
|
2769
|
+
signature: '(props: { src: string; alt?: string; width?: number; height?: number; caption?: string }) => VNodeChild',
|
|
2770
|
+
example: `<DocImage
|
|
2771
|
+
src="/charts/q4-revenue.png"
|
|
2772
|
+
alt="Revenue grew 23% in Q4"
|
|
2773
|
+
width={600}
|
|
2774
|
+
height={400}
|
|
2775
|
+
caption="Figure 1: Quarterly revenue, 2024-2025"
|
|
2776
|
+
/>`,
|
|
2777
|
+
notes: 'An image embedded in the document. `src` is the image URL or data URI. `alt` is the accessible description (also used as fallback text in non-visual outputs). `width` / `height` constrain dimensions in pixels. Optional `caption` renders a caption beneath the image. See also: DocCode.',
|
|
2778
|
+
},
|
|
2779
|
+
|
|
2780
|
+
'document-primitives/DocTable': {
|
|
2781
|
+
signature: '(props: { columns: TableColumn[]; rows: TableRow[]; headerStyle?: object; striped?: boolean; bordered?: boolean; caption?: string }) => VNodeChild',
|
|
2782
|
+
example: `<DocTable
|
|
2783
|
+
caption="Q4 results by region"
|
|
2784
|
+
bordered
|
|
2785
|
+
striped
|
|
2786
|
+
columns={[
|
|
2787
|
+
{ key: 'region', label: 'Region', align: 'left' },
|
|
2788
|
+
{ key: 'revenue', label: 'Revenue', align: 'right' },
|
|
2789
|
+
{ key: 'growth', label: 'YoY Growth', align: 'right' },
|
|
2790
|
+
]}
|
|
2791
|
+
rows={[
|
|
2792
|
+
{ region: 'NA', revenue: '$12.4M', growth: '+23%' },
|
|
2793
|
+
{ region: 'EU', revenue: '$8.7M', growth: '+18%' },
|
|
2794
|
+
{ region: 'APAC', revenue: '$5.1M', growth: '+41%' },
|
|
2795
|
+
]}
|
|
2796
|
+
/>`,
|
|
2797
|
+
notes: 'Tabular data. `columns` defines the header cells (label, key, optional alignment). `rows` is an array of data rows keyed by column key. `striped` adds alternating row backgrounds; `bordered` adds cell borders; `caption` renders an accessible table caption. Both `rows` and `columns` are filtered before reaching the DOM via `.attrs(..., { filter: [...] })` because `HTMLTableElement.rows` / `.cells` are read-only DOM properties — assignment would crash. See also: DocList, DocSection.',
|
|
2798
|
+
},
|
|
2799
|
+
|
|
2800
|
+
'document-primitives/DocList': {
|
|
2801
|
+
signature: '(props: { ordered?: boolean; children: VNodeChild }) => VNodeChild',
|
|
2802
|
+
example: `<DocList>
|
|
2803
|
+
<DocListItem>First bullet</DocListItem>
|
|
2804
|
+
<DocListItem>Second bullet</DocListItem>
|
|
2805
|
+
</DocList>
|
|
2806
|
+
|
|
2807
|
+
<DocList ordered>
|
|
2808
|
+
<DocListItem>First step</DocListItem>
|
|
2809
|
+
<DocListItem>Second step</DocListItem>
|
|
2810
|
+
</DocList>`,
|
|
2811
|
+
notes: 'Bulleted (default) or numbered (`ordered`) list. Children are typically `DocListItem` instances. Outputs map this to the right native list type — HTML `<ul>` / `<ol>`, Markdown `-` / `1.`, DOCX list styles. See also: DocListItem.',
|
|
2812
|
+
},
|
|
2813
|
+
|
|
2814
|
+
'document-primitives/DocListItem': {
|
|
2815
|
+
signature: '(props: { children: VNodeChild }) => VNodeChild',
|
|
2816
|
+
example: `<DocList>
|
|
2817
|
+
<DocListItem>Top-level item</DocListItem>
|
|
2818
|
+
<DocListItem>
|
|
2819
|
+
Item with nested list
|
|
2820
|
+
<DocList>
|
|
2821
|
+
<DocListItem>Nested A</DocListItem>
|
|
2822
|
+
<DocListItem>Nested B</DocListItem>
|
|
2823
|
+
</DocList>
|
|
2824
|
+
</DocListItem>
|
|
2825
|
+
</DocList>`,
|
|
2826
|
+
notes: `Single item inside a \`DocList\`. Children may be plain text, \`DocText\`, nested \`DocList\` for sublists, or any other inline primitive. Visual marker (bullet vs number) is decided by the parent list\'s \`ordered\` prop, not by the item. See also: DocList.`,
|
|
2827
|
+
},
|
|
2828
|
+
|
|
2829
|
+
'document-primitives/DocCode': {
|
|
2830
|
+
signature: '(props: { language?: string; children: VNodeChild }) => VNodeChild',
|
|
2831
|
+
example: `<DocCode language="typescript">{
|
|
2832
|
+
\`const flow = createFlow({
|
|
2833
|
+
nodes: [{ id: '1', position: { x: 0, y: 0 } }],
|
|
2834
|
+
edges: [],
|
|
2835
|
+
})\`
|
|
2836
|
+
}</DocCode>`,
|
|
2837
|
+
notes: 'Monospace code block. Optional `language` hint enables syntax highlighting in outputs that support it (HTML via Prism / Shiki, Markdown fenced code blocks with language tag). Whitespace is preserved verbatim — pass code as a single string child to keep newlines. See also: DocText.',
|
|
2838
|
+
},
|
|
2839
|
+
|
|
2840
|
+
'document-primitives/DocDivider': {
|
|
2841
|
+
signature: '(props: { color?: string; thickness?: number }) => VNodeChild',
|
|
2842
|
+
example: `<DocText>Above the divider.</DocText>
|
|
2843
|
+
<DocDivider color="#e5e7eb" thickness={1} />
|
|
2844
|
+
<DocText>Below the divider.</DocText>`,
|
|
2845
|
+
notes: 'Horizontal rule — visual section separator. `color` controls the line color (any CSS color string); `thickness` controls the line thickness in pixels. Outputs map this to native dividers — HTML `<hr>`, Markdown `---`, DOCX horizontal rule. See also: DocSpacer.',
|
|
2846
|
+
},
|
|
2847
|
+
|
|
2848
|
+
'document-primitives/DocSpacer': {
|
|
2849
|
+
signature: '(props: { height?: number }) => VNodeChild',
|
|
2850
|
+
example: `<DocSection>
|
|
2851
|
+
<DocHeading level="h2">Section A</DocHeading>
|
|
2852
|
+
<DocText>Content...</DocText>
|
|
2853
|
+
<DocSpacer height={32} />
|
|
2854
|
+
<DocHeading level="h2">Section B</DocHeading>
|
|
2855
|
+
<DocText>More content...</DocText>
|
|
2856
|
+
</DocSection>`,
|
|
2857
|
+
notes: 'Vertical whitespace — adds a blank vertical gap. `height` is in pixels (default 16). Use to space out content beyond what `DocSection` / `DocPage` margins provide. In flow outputs this becomes a styled blank block; in plain-text outputs, a sequence of newlines. See also: DocDivider.',
|
|
2858
|
+
},
|
|
2859
|
+
|
|
2860
|
+
'document-primitives/DocButton': {
|
|
2861
|
+
signature: '(props: { href?: string; children: VNodeChild }) => VNodeChild',
|
|
2862
|
+
example: `<DocButton href="https://pyreon.dev/signup">
|
|
2863
|
+
Get started
|
|
2864
|
+
</DocButton>`,
|
|
2865
|
+
notes: 'Call-to-action button. Renders as a styled clickable element in HTML / email outputs (mail-safe button table layout for email), and as a labeled link in PDF / DOCX. `href` is the action URL — defaults to `"#"`. Visual style (variant) is controlled via rocketstyle dimensions on the component definition. See also: DocLink.',
|
|
2866
|
+
},
|
|
2867
|
+
|
|
2868
|
+
'document-primitives/DocQuote': {
|
|
2869
|
+
signature: '(props: { borderColor?: string; children: VNodeChild }) => VNodeChild',
|
|
2870
|
+
example: `<DocQuote borderColor="#3b82f6">
|
|
2871
|
+
<DocText>"The best way to predict the future is to build it."</DocText>
|
|
2872
|
+
<DocText>— Aisha Patel, Q4 keynote</DocText>
|
|
2873
|
+
</DocQuote>`,
|
|
2874
|
+
notes: 'Block quote — sets off a quoted passage with an indented left border. `borderColor` controls the indicator stripe (any CSS color). Outputs map this to native quote styling — HTML `<blockquote>`, Markdown `> ...`, DOCX quote style. See also: DocText.',
|
|
2875
|
+
},
|
|
2876
|
+
|
|
2877
|
+
'document-primitives/DocPageBreak': {
|
|
2878
|
+
signature: '() => VNodeChild',
|
|
2879
|
+
example: `<DocPage>
|
|
2880
|
+
<DocHeading level="h1">Section 1</DocHeading>
|
|
2881
|
+
<DocText>...long content...</DocText>
|
|
2882
|
+
<DocPageBreak />
|
|
2883
|
+
<DocHeading level="h1">Section 2 — new page</DocHeading>
|
|
2884
|
+
</DocPage>`,
|
|
2885
|
+
notes: 'Explicit page boundary inside a `DocPage`. Forces the renderer to start a new page at this point in paginated outputs (PDF, DOCX). In flow outputs (HTML, Markdown), it renders as visible whitespace or is omitted entirely. Use for explicit pagination control beyond what `DocPage` boundaries already provide. See also: DocPage.',
|
|
2886
|
+
},
|
|
2887
|
+
// <gen-docs:api-reference:end @pyreon/document-primitives>
|
|
2465
2888
|
}
|