@avodado/render 0.0.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/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/index.d.ts +301 -0
- package/dist/index.js +2995 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Avodado contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @avodado/render
|
|
2
|
+
|
|
3
|
+
`renderDocument(doc) → string`. Pure function: takes a parsed `@avodado/core` Document, returns a standalone HTML string with inlined CSS and inline SVG diagrams. No browser, no DOM, no I/O.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pnpm add @avodado/render @avodado/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { parseDocument } from '@avodado/core';
|
|
15
|
+
import { renderDocument } from '@avodado/render';
|
|
16
|
+
|
|
17
|
+
const html = renderDocument(parseDocument(markdown, 'orders'));
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Exports
|
|
21
|
+
|
|
22
|
+
- **`renderDocument(doc)`** — full standalone HTML page (`<!doctype html>…`).
|
|
23
|
+
- **`houseCss`** — the house stylesheet as a string. Useful if you want to render into a fragment and inject CSS elsewhere.
|
|
24
|
+
- **`htmlRenderers`** — `Record<BlockType, (data) => string>`. Per-block renderer registry; typed so omitting a block type is a compile error.
|
|
25
|
+
- **`renderProse(text)`** — Markdown prose → HTML wrapped in `.prose`.
|
|
26
|
+
- **`escapeHtml(value)`** — entity-escape `&<>"`.
|
|
27
|
+
|
|
28
|
+
## Fidelity
|
|
29
|
+
|
|
30
|
+
CSS is ported verbatim from the reference renderer at `resources/avodado-renderer.html`. Block DOM signatures match the reference's class names and element structure. SVG geometry uses integer-only coordinates, so output is byte-deterministic.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { Document, BlockType, BlockDataMap } from '@avodado/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The house CSS — ported verbatim from `resources/doc-studio.jsx`.
|
|
5
|
+
*
|
|
6
|
+
* All output is namespaced under `.docskin` so the stylesheet can coexist with
|
|
7
|
+
* a host page's own styles. Theme switching works by overriding the CSS
|
|
8
|
+
* variables on the `.docskin` root (see {@link themes}).
|
|
9
|
+
*
|
|
10
|
+
* Exported as a single string so it can be inlined into a `<style>` tag in the
|
|
11
|
+
* standalone HTML produced by {@link renderDocument} (or copied verbatim into
|
|
12
|
+
* a static stylesheet).
|
|
13
|
+
*/
|
|
14
|
+
declare const houseCss = "*{box-sizing:border-box;margin:0;padding:0;}\nhtml{scroll-behavior:smooth;}\n/* Design tokens live on :root so a theme (applied as :root overrides) reaches\n the whole page \u2014 body chrome included, not just .docskin content. */\n:root{\n --navy:#0e54a1; --navy-tint:#b5d4f2; --blue:#1a6dbe; --light-blue:#e5eff8;\n --charcoal:#1a1a2e; --slate:#374151; --gray:#6b7280; --light-gray:#f3f4f6;\n --rule:#d1d5db; --highlight:#f7952c; --highlight-soft:#fde7cd;\n --positive:#1f9747; --positive-soft:#dcf1e2; --negative:#991b1b; --negative-soft:#fee2e2;\n --purple:#6b21a8; --purple-soft:#ede9fe; --teal:#0f766e; --teal-soft:#ccfbf1; --white:#fff;\n --radius:0px;\n --font-display:Georgia,\"Times New Roman\",serif;\n --font-body:Arial,\"Helvetica Neue\",sans-serif;\n --font-mono:\"Consolas\",\"Monaco\",\"Courier New\",monospace;\n}\nbody{background:var(--white);color:var(--charcoal);font-family:var(--font-body);font-size:14px;line-height:1.55;}\n.docskin{\n background:var(--white); color:var(--charcoal); font-family:var(--font-body); font-size:14px; line-height:1.55;\n max-width:1180px; margin:0 auto; padding:0 56px 128px;\n}\n.docskin .cover-bar{height:8px;background:var(--navy);margin:0 -56px 36px;}\n.docskin .cover-pad{padding:0 0 40px;margin-bottom:56px;border-bottom:1px solid var(--rule);}\n.docskin .cover-meta{display:flex;justify-content:space-between;flex-wrap:wrap;gap:16px;font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--gray);font-weight:600;margin-bottom:32px;}\n.docskin .cover-meta .accent{color:var(--highlight);}\n.docskin .cover-title{font-family:var(--font-display);font-weight:700;font-size:clamp(32px,4.4vw,50px);line-height:1.1;letter-spacing:-.02em;color:var(--navy);margin:0 0 20px;}\n.docskin .cover-sub{font-size:17px;line-height:1.5;color:var(--slate);max-width:820px;margin:0 0 36px;}\n.docskin .section{padding:0;margin-bottom:64px;}\n.docskin .section > *:last-child{margin-bottom:0;}\n.docskin .section-num{font-size:11px;text-transform:uppercase;letter-spacing:.14em;color:var(--highlight);font-weight:700;margin-bottom:8px;}\n.docskin .section-head{margin-bottom:28px;padding-bottom:16px;border-bottom:2px solid var(--navy);}\n.docskin .section-head .section-title{border-bottom:0;padding-bottom:0;margin-bottom:14px;}\n.docskin .section-title{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3vw,32px);line-height:1.15;letter-spacing:-.015em;color:var(--navy);margin:0 0 14px;padding-bottom:12px;border-bottom:2px solid var(--navy);}\n.docskin .section-lede{font-size:14.5px;color:var(--slate);line-height:1.55;max-width:820px;margin:0;}\n.docskin .section-block{margin-bottom:64px;}\n.docskin .section-block:last-child{margin-bottom:0;}\n.docskin .diagram{margin:28px 0 36px;border:1px solid var(--rule);background:var(--white);padding:22px 26px 18px;border-radius:var(--radius);}\n.docskin .diagram-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:10px;padding-bottom:12px;margin-bottom:16px;border-bottom:1px dashed var(--rule);}\n.docskin .diagram-tag{font-family:var(--font-mono);font-size:10px;font-weight:700;padding:3px 9px;background:var(--navy);color:var(--white);letter-spacing:.08em;text-transform:uppercase;}\n.docskin .diagram-tag.post{background:var(--navy);} .docskin .diagram-tag.get{background:var(--positive);} .docskin .diagram-tag.c4{background:var(--blue);}\n.docskin .diagram-title{font-family:var(--font-display);font-weight:700;font-size:16px;color:var(--charcoal);flex:1;}\n.docskin .diagram-fignum{font-size:10px;color:var(--gray);text-transform:uppercase;letter-spacing:.1em;font-weight:700;}\n.docskin .diagram-desc{font-size:13px;color:var(--slate);margin:0 0 12px;}\n.docskin .diagram svg{display:block;margin:0 auto;max-width:100%;height:auto;}\n/* sequence */\n.docskin .lane-head{fill:var(--navy);} .docskin .lane-head.ext{fill:var(--slate);}\n.docskin .lane-head-text{fill:var(--white);font-family:var(--font-body);font-size:12px;font-weight:700;text-anchor:middle;}\n.docskin .lane-head-sub{fill:var(--navy-tint);font-family:var(--font-mono);font-size:9px;text-anchor:middle;letter-spacing:.06em;}\n.docskin .lane-head-sub.ext{fill:#cbd5e1;}\n.docskin .lifeline{stroke:var(--gray);stroke-width:1;stroke-dasharray:3 3;}\n.docskin .activation{fill:var(--light-blue);stroke:var(--navy);stroke-width:1;} .docskin .activation.pg{fill:var(--positive-soft);stroke:var(--positive);}\n.docskin .msg-line{stroke:var(--charcoal);stroke-width:1.2;fill:none;}\n.docskin .msg-line.dashed{stroke-dasharray:5 3;} .docskin .msg-line.err{stroke:var(--negative);stroke-width:1.4;}\n.docskin .msg-text{fill:var(--charcoal);font-family:var(--font-mono);font-size:10.5px;}\n.docskin .msg-text.em{fill:var(--navy);font-weight:700;} .docskin .msg-text.err{fill:var(--negative);font-weight:700;} .docskin .msg-text.note{fill:var(--gray);font-style:italic;}\n.docskin .step-badge{fill:var(--navy);} .docskin .step-badge.err{fill:var(--negative);}\n.docskin .step-badge-text{fill:var(--white);font-family:var(--font-mono);font-size:10px;font-weight:700;text-anchor:middle;}\n.docskin .seq-steps{margin-top:16px;padding:14px 18px;background:var(--light-gray);border:1px solid var(--rule);}\n.docskin .seq-steps-title{font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--navy);font-weight:700;margin-bottom:8px;}\n.docskin .seq-steps ol{list-style:none;counter-reset:step;padding:0;margin:0;}\n.docskin .seq-steps li{counter-increment:step;padding:7px 0 8px 40px;position:relative;border-bottom:1px solid var(--rule);}\n.docskin .seq-steps li:last-child{border-bottom:none;}\n.docskin .seq-steps li::before{content:counter(step);position:absolute;left:0;top:7px;width:26px;height:20px;background:var(--navy);color:var(--white);font-family:var(--font-mono);font-size:11px;font-weight:700;text-align:center;line-height:20px;border-radius:2px;}\n.docskin .seq-steps li.err::before{background:var(--negative);}\n.docskin .step-actor{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--navy);margin-right:8px;text-transform:uppercase;letter-spacing:.06em;}\n.docskin .step-actor.err{color:var(--negative);}\n.docskin .step-summary{font-size:13px;color:var(--charcoal);}\n/* c4 */\n.docskin .c4-name{font-family:var(--font-display);font-size:14px;font-weight:700;}\n.docskin .c4-tech{font-family:var(--font-mono);font-size:9.5px;}\n.docskin .c4-desc{font-family:var(--font-body);font-size:10px;}\n.docskin .c4-chip{font-family:var(--font-body);font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;}\n.docskin .edge-label{font-family:var(--font-body);font-size:9.5px;fill:var(--slate);text-anchor:middle;}\n.docskin .edge-label.err{fill:var(--negative);font-weight:700;}\n.docskin .c4-boundary{fill:none;stroke:var(--navy);stroke-width:1.4;stroke-dasharray:8 5;}\n.docskin .c4-boundary-label{font-family:var(--font-body);font-size:10px;font-weight:700;fill:var(--navy);letter-spacing:.08em;text-transform:uppercase;}\n.docskin .legend{display:flex;flex-wrap:wrap;gap:8px 16px;margin-top:14px;padding-top:12px;border-top:1px dashed var(--rule);}\n.docskin .legend .item{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--slate);}\n.docskin .legend .sw{width:13px;height:13px;border-radius:3px;}\n/* code block */\n.docskin .code-block{margin:12px 0 16px;border:1px solid var(--rule);}\n.docskin .code-header{display:flex;justify-content:space-between;padding:6px 14px;background:var(--light-gray);font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--slate);border-bottom:1px solid var(--rule);letter-spacing:.04em;}\n.docskin .code-block pre{padding:14px 16px;font-family:var(--font-mono);font-size:12px;line-height:1.55;color:var(--charcoal);overflow-x:auto;background:var(--white);white-space:pre;margin:0;}\n.docskin .code-block .kw{color:var(--navy);font-weight:700;} .docskin .code-block .com{color:var(--gray);font-style:italic;}\n.docskin .code-block .str{color:var(--positive);} .docskin .code-block .num{color:var(--purple);} .docskin .code-block .fn{color:var(--blue);} .docskin .code-block .ty{color:var(--teal);}\n/* er */\n.docskin .er-head-text{fill:#fff;font-family:var(--font-display);font-size:13px;font-weight:700;text-anchor:middle;}\n.docskin .er-col{font-family:var(--font-mono);font-size:10.5px;fill:var(--charcoal);} .docskin .er-col.dim{fill:var(--gray);}\n.docskin .er-key{font-family:var(--font-mono);font-size:9px;font-weight:700;fill:var(--navy);} .docskin .er-key.fk{fill:var(--highlight);}\n.docskin .er-rowline{stroke:var(--light-gray);stroke-width:1;}\n/* block / state / flow shared text */\n.docskin .blk-name{font-family:var(--font-display);font-size:13px;font-weight:700;}\n.docskin .blk-tech{font-family:var(--font-mono);font-size:9.5px;}\n.docskin .blk-chip{font-family:var(--font-body);font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;}\n.docskin .grp-label{font-family:var(--font-body);font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;}\n.docskin .sm-name{font-family:var(--font-display);font-size:13px;font-weight:700;text-anchor:middle;}\n.docskin .fc-label{font-family:var(--font-display);font-size:12px;font-weight:700;text-anchor:middle;}\n.docskin .endpoint-card{border:1px solid var(--rule);margin:16px 0;padding:18px 22px;background:var(--white);}\n.docskin .endpoint-header{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px dashed var(--rule);}\n.docskin .endpoint-method{font-family:var(--font-mono);font-size:11px;font-weight:700;padding:4px 10px;color:var(--white);letter-spacing:.08em;text-transform:uppercase;}\n.docskin .endpoint-method.get{background:var(--positive);} .docskin .endpoint-method.post{background:var(--navy);} .docskin .endpoint-method.patch{background:var(--highlight);} .docskin .endpoint-method.delete{background:var(--negative);} .docskin .endpoint-method.put{background:var(--blue);}\n.docskin .endpoint-path{font-family:var(--font-mono);font-size:15px;font-weight:700;color:var(--charcoal);flex:1;}\n.docskin .endpoint-status{font-family:var(--font-mono);font-size:11.5px;color:var(--positive);font-weight:700;}\n.docskin .endpoint-desc{font-size:13px;color:var(--slate);margin:0 0 8px;}\n.docskin .endpoint-card h4{font-size:11px;text-transform:uppercase;letter-spacing:.12em;font-weight:700;color:var(--highlight);margin:14px 0 6px;}\n.docskin .endpoint-card ul{margin:0 0 0 20px;} .docskin .endpoint-card li{font-size:13px;margin-bottom:3px;}\n.docskin code{font-family:var(--font-mono);font-size:.86em;background:var(--light-gray);padding:2px 6px;border-radius:2px;border:1px solid var(--rule);}\n.docskin .transition-table{width:100%;border-collapse:collapse;margin:16px 0 8px;font-size:12px;}\n.docskin .transition-table thead{background:var(--navy);color:#fff;}\n.docskin .transition-table th{text-align:left;padding:8px 10px;font-size:9.5px;text-transform:uppercase;letter-spacing:.1em;}\n.docskin .transition-table td{padding:8px 10px;border-bottom:1px solid var(--rule);vertical-align:top;}\n.docskin .pill{display:inline-block;font-family:var(--font-mono);font-size:10px;font-weight:700;padding:2px 7px;border-radius:2px;text-transform:uppercase;}\n.docskin .pill-init{background:var(--light-gray);color:var(--slate);border:1px solid var(--rule);}\n.docskin .pill-active{background:var(--positive-soft);color:var(--positive);border:1px solid var(--positive);}\n.docskin .pill-wait{background:var(--highlight-soft);color:var(--highlight);border:1px solid var(--highlight);}\n.docskin .pill-end{background:var(--charcoal);color:#fff;}\n/* presentation: comparison table */\n.docskin .pres-table{width:100%;border-collapse:collapse;margin:14px 0;font-size:13px;}\n.docskin .pres-table thead{background:var(--navy);color:#fff;}\n.docskin .pres-table th{padding:9px 12px;text-align:left;font-family:var(--font-body);font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;}\n.docskin .pres-table th.r,.docskin .pres-table td.r{text-align:right;} .docskin .pres-table th.c,.docskin .pres-table td.c{text-align:center;}\n.docskin .pres-table th.hi{background:var(--highlight);}\n.docskin .pres-table td{padding:9px 12px;border-bottom:1px solid var(--rule);}\n.docskin .pres-table tbody tr:nth-child(even){background:var(--light-gray);}\n.docskin .pres-table td.lead{font-weight:700;color:var(--navy);font-family:var(--font-display);}\n.docskin .pres-table td.hi{background:var(--highlight-soft);}\n.docskin .cell-pos{color:var(--positive);font-weight:700;} .docskin .cell-neg{color:var(--negative);font-weight:700;} .docskin .cell-warn{color:var(--highlight);font-weight:700;} .docskin .cell-muted{color:var(--gray);}\n.docskin .badge{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;font-size:12px;font-weight:700;}\n.docskin .badge.yes{background:var(--positive-soft);color:var(--positive);} .docskin .badge.no{background:var(--negative-soft);color:var(--negative);}\n.docskin .tbl-note{font-size:11px;color:var(--gray);font-style:italic;margin-top:6px;}\n/* presentation: stat cards */\n.docskin .stat-row{display:flex;flex-wrap:wrap;gap:14px;margin:16px 0;}\n.docskin .stat-card{flex:1 1 150px;border:1px solid var(--rule);border-top:3px solid var(--navy);padding:16px 18px;background:var(--white);}\n.docskin .stat-value{font-family:var(--font-display);font-size:30px;font-weight:700;color:var(--navy);line-height:1;}\n.docskin .stat-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--gray);font-weight:700;margin-top:8px;}\n.docskin .stat-delta{font-family:var(--font-mono);font-size:12px;font-weight:700;margin-top:6px;}\n.docskin .stat-delta.up{color:var(--positive);} .docskin .stat-delta.down{color:var(--negative);} .docskin .stat-delta.flat{color:var(--gray);}\n/* presentation: timeline */\n.docskin .tl{position:relative;margin:18px 0;padding-left:8px;}\n.docskin .tl::before{content:\"\";position:absolute;left:9px;top:6px;bottom:6px;width:2px;background:var(--rule);}\n.docskin .tl-item{position:relative;padding:0 0 18px 30px;}\n.docskin .tl-item:last-child{padding-bottom:0;}\n.docskin .tl-dot{position:absolute;left:2px;top:2px;width:16px;height:16px;border-radius:50%;background:var(--white);border:3px solid var(--rule);box-sizing:border-box;}\n.docskin .tl-dot.done{background:var(--positive);border-color:var(--positive);} .docskin .tl-dot.current{background:var(--highlight);border-color:var(--highlight);} .docskin .tl-dot.next{border-color:var(--navy);}\n.docskin .tl-date{font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--highlight);text-transform:uppercase;letter-spacing:.06em;}\n.docskin .tl-label{font-family:var(--font-display);font-size:15px;font-weight:700;color:var(--navy);margin:1px 0 2px;}\n.docskin .tl-desc{font-size:12.5px;color:var(--slate);}\n/* presentation: quadrant */\n.docskin .quad-axis{stroke:var(--charcoal);stroke-width:1.5;}\n.docskin .quad-end{font-family:var(--font-body);font-size:10px;font-weight:700;fill:var(--gray);text-transform:uppercase;letter-spacing:.06em;}\n.docskin .quad-title{font-family:var(--font-body);font-size:11px;font-weight:700;fill:var(--navy);text-transform:uppercase;letter-spacing:.08em;}\n.docskin .quad-pt-label{font-family:var(--font-body);font-size:11px;font-weight:700;fill:var(--charcoal);}\n.docskin .toc{margin:18px 0 6px;padding:14px 20px;background:var(--light-gray);border:1px solid var(--rule);border-left:4px solid var(--highlight);}\n.docskin .toc-title{font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--navy);font-weight:700;margin-bottom:8px;}\n.docskin .toc ol{margin:0;padding-left:20px;} .docskin .toc li{font-size:13px;margin-bottom:4px;color:var(--slate);}\n.docskin .toc li span{color:var(--gray);font-family:var(--font-mono);font-size:11px;}\n/* swimlane */\n.docskin .sl-lane-label{font-family:var(--font-display);font-size:12px;font-weight:700;fill:#fff;}\n.docskin .sl-step{font-family:var(--font-body);font-size:11px;font-weight:700;text-anchor:middle;}\n/* callouts */\n.docskin .callout{border:1px solid var(--rule);border-left:4px solid var(--navy);padding:12px 16px;margin:10px 0;}\n.docskin .callout.note{border-left-color:var(--navy);background:var(--light-blue);} .docskin .callout.tip{border-left-color:var(--positive);background:var(--positive-soft);} .docskin .callout.warn{border-left-color:var(--highlight);background:var(--highlight-soft);} .docskin .callout.danger{border-left-color:var(--negative);background:var(--negative-soft);}\n.docskin .callout-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;font-weight:700;margin-bottom:4px;}\n.docskin .callout.note .callout-title{color:var(--navy);} .docskin .callout.tip .callout-title{color:var(--positive);} .docskin .callout.warn .callout-title{color:#b45309;} .docskin .callout.danger .callout-title{color:var(--negative);}\n.docskin .callout-body{font-size:13px;color:var(--slate);}\n/* prose */\n.docskin .prose h2{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3vw,32px);line-height:1.15;letter-spacing:-.015em;color:var(--navy);margin:40px 0 14px;padding-bottom:12px;border-bottom:2px solid var(--navy);}\n.docskin .prose h2:first-child{margin-top:0;}\n.docskin .prose h3{font-family:var(--font-display);font-weight:700;font-size:19px;letter-spacing:-.005em;color:var(--navy);margin:36px 0 12px;}\n.docskin .prose h4{font-size:11px;text-transform:uppercase;letter-spacing:.12em;font-weight:700;color:var(--highlight);margin:22px 0 8px;}\n.docskin .prose p{font-size:14px;color:var(--charcoal);margin:0 0 14px;line-height:1.6;max-width:880px;}\n.docskin .prose ul,.docskin .prose ol{margin:0 0 14px 22px;}\n.docskin .prose li{font-size:13.5px;color:var(--charcoal);margin-bottom:4px;max-width:880px;line-height:1.55;}\n.docskin .prose blockquote{border-left:3px solid var(--highlight);padding:4px 14px;margin:14px 0;color:var(--slate);font-style:italic;font-family:var(--font-display);}\n.docskin .prose code{font-family:var(--font-mono);font-size:.86em;background:var(--light-gray);padding:2px 6px;border-radius:2px;color:var(--charcoal);border:1px solid var(--rule);}\n.docskin .prose strong{font-weight:700;color:var(--charcoal);}\n.docskin .prose em{font-style:italic;}\n/* glossary */\n.docskin .glossary{margin:10px 0;}\n.docskin .glossary .row{display:grid;grid-template-columns:170px 1fr;gap:14px;padding:9px 0;border-bottom:1px solid var(--rule);}\n.docskin .glossary dt{font-family:var(--font-mono);font-size:13px;font-weight:700;color:var(--navy);}\n.docskin .glossary dd{margin:0;font-size:13px;color:var(--slate);}\n/* pros / cons */\n.docskin .pc{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:12px 0;}\n.docskin .pc-col{border:1px solid var(--rule);padding:14px 16px;}\n.docskin .pc-col.pro{border-top:3px solid var(--positive);} .docskin .pc-col.con{border-top:3px solid var(--negative);}\n.docskin .pc-head{font-size:11px;text-transform:uppercase;letter-spacing:.1em;font-weight:700;margin-bottom:8px;}\n.docskin .pc-col.pro .pc-head{color:var(--positive);} .docskin .pc-col.con .pc-head{color:var(--negative);}\n.docskin .pc-item{font-size:13px;color:var(--slate);padding:4px 0 4px 22px;position:relative;}\n.docskin .pc-item::before{position:absolute;left:0;top:4px;font-weight:700;}\n.docskin .pc-col.pro .pc-item::before{content:\"\\2713\";color:var(--positive);} .docskin .pc-col.con .pc-item::before{content:\"\\2717\";color:var(--negative);}\n/* current / target */\n.docskin .ct{display:flex;align-items:stretch;margin:12px 0;}\n.docskin .ct-panel{flex:1;border:1px solid var(--rule);padding:14px 18px;}\n.docskin .ct-panel.cur{background:var(--light-gray);} .docskin .ct-panel.tgt{border-top:3px solid var(--navy);}\n.docskin .ct-arrow{display:flex;align-items:center;padding:0 14px;color:var(--highlight);font-size:22px;font-weight:700;}\n.docskin .ct-label{font-size:11px;text-transform:uppercase;letter-spacing:.1em;font-weight:700;color:var(--gray);margin-bottom:8px;}\n.docskin .ct-panel.tgt .ct-label{color:var(--navy);}\n.docskin .ct-item{font-size:13px;color:var(--slate);padding:3px 0;}\n/* kanban */\n.docskin .kanban{display:flex;gap:14px;margin:12px 0;overflow-x:auto;}\n.docskin .kan-col{flex:1 1 0;min-width:150px;background:var(--light-gray);border:1px solid var(--rule);}\n.docskin .kan-head{background:var(--navy);color:#fff;font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;padding:8px 12px;}\n.docskin .kan-card{background:var(--white);border:1px solid var(--rule);border-left:3px solid var(--highlight);margin:8px;padding:9px 11px;}\n.docskin .kan-card-title{font-size:13px;font-weight:700;color:var(--charcoal);}\n.docskin .kan-card-tag{display:inline-block;font-family:var(--font-mono);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--gray);margin-top:4px;}\n/* pass 2 chart labels */\n.docskin .dfd-name{font-family:var(--font-display);font-size:12px;font-weight:700;text-anchor:middle;}\n.docskin .dfd-num{font-family:var(--font-mono);font-size:9px;font-weight:700;}\n.docskin .gantt-label{font-family:var(--font-body);font-size:11px;fill:var(--charcoal);}\n.docskin .gantt-head{font-family:var(--font-mono);font-size:9.5px;fill:var(--gray);text-anchor:middle;}\n.docskin .funnel-label{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;text-anchor:middle;}\n.docskin .funnel-val{font-family:var(--font-mono);font-size:11px;fill:#fff;text-anchor:middle;opacity:.9;}\n.docskin .pyr-label{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;text-anchor:middle;}\n.docskin .pyr-desc{font-family:var(--font-body);font-size:10px;fill:#fff;text-anchor:middle;opacity:.85;}\n/* indented tree */\n.docskin .tree-list{margin:10px 0;font-size:13px;}\n.docskin .tree-row{display:flex;align-items:baseline;padding:3px 0;}\n.docskin .tree-row .tw{color:var(--gray);margin-right:8px;font-family:var(--font-mono);font-size:11px;}\n.docskin .tree-row.branch .tw{color:var(--navy);}\n.docskin .tree-row .tlabel{color:var(--charcoal);font-family:var(--font-mono);}\n.docskin .tree-row.branch .tlabel{font-weight:700;color:var(--navy);}\n.docskin .tree-row .tnote{color:var(--gray);font-size:11px;margin-left:10px;font-family:var(--font-body);font-style:italic;}\n/* agenda */\n.docskin .agenda{margin:10px 0;}\n.docskin .agenda-row{display:grid;grid-template-columns:88px 1fr;gap:14px;padding:10px 0;border-bottom:1px solid var(--rule);}\n.docskin .agenda-time{font-family:var(--font-mono);font-size:12px;font-weight:700;color:var(--navy);}\n.docskin .agenda-dur{font-family:var(--font-mono);font-size:10px;color:var(--gray);margin-top:2px;}\n.docskin .agenda-title{font-family:var(--font-display);font-size:14px;font-weight:700;color:var(--charcoal);}\n.docskin .agenda-owner{font-size:10.5px;color:var(--highlight);font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-left:8px;}\n.docskin .agenda-desc{font-size:12.5px;color:var(--slate);margin-top:2px;}\n/* tracker */\n.docskin .trk{width:100%;border-collapse:collapse;margin:12px 0;font-size:13px;}\n.docskin .trk thead{background:var(--navy);color:#fff;} .docskin .trk th{padding:8px 10px;text-align:left;font-size:9.5px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;}\n.docskin .trk td{padding:8px 10px;border-bottom:1px solid var(--rule);vertical-align:middle;}\n.docskin .trk tr.done .trk-task{text-decoration:line-through;color:var(--gray);}\n.docskin .st{display:inline-block;font-family:var(--font-mono);font-size:9.5px;font-weight:700;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.04em;}\n.docskin .st.todo{background:var(--light-gray);color:var(--slate);border:1px solid var(--rule);} .docskin .st.doing{background:var(--highlight-soft);color:#b45309;} .docskin .st.done{background:var(--positive-soft);color:var(--positive);} .docskin .st.blocked{background:var(--negative-soft);color:var(--negative);}\n.docskin .pri{font-family:var(--font-mono);font-size:10px;font-weight:700;} .docskin .pri.high{color:var(--negative);} .docskin .pri.med{color:var(--highlight);} .docskin .pri.low{color:var(--gray);}\n/* cluster */\n.docskin .cl-head{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;}\n.docskin .cl-kind{font-family:var(--font-mono);font-size:9px;fill:#cfe0f3;text-anchor:end;text-transform:uppercase;letter-spacing:.06em;}\n/* user story */\n.docskin .story{border:1px solid var(--rule);border-left:4px solid var(--navy);padding:18px 22px;margin:12px 0;background:var(--white);}\n.docskin .story-stmt{font-family:var(--font-display);font-size:18px;line-height:1.55;color:var(--charcoal);}\n.docskin .story-stmt b{color:var(--navy);}\n.docskin .story-meta{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap;}\n.docskin .story-chip{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding:3px 9px;border-radius:3px;background:var(--light-gray);color:var(--slate);border:1px solid var(--rule);}\n.docskin .ac-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.12em;color:var(--highlight);font-weight:700;margin:16px 0 8px;}\n.docskin .ac-item{border:1px solid var(--rule);padding:10px 14px;margin-bottom:8px;background:var(--white);}\n.docskin .gwt{display:grid;grid-template-columns:60px 1fr;gap:4px 12px;font-size:13px;}\n.docskin .gwt .k{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;padding-top:1px;}\n.docskin .gwt .k.g{color:var(--positive);} .docskin .gwt .k.w{color:var(--navy);} .docskin .gwt .k.t{color:var(--highlight);}\n.docskin .gwt .v{color:var(--charcoal);}\n.docskin .links-row{display:flex;gap:8px;flex-wrap:wrap;}\n.docskin .link-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;color:var(--navy);border:1px solid var(--navy);background:var(--white);padding:5px 11px;border-radius:20px;cursor:pointer;}\n.docskin .link-chip .lt{color:var(--gray);font-family:var(--font-mono);font-size:9px;text-transform:uppercase;letter-spacing:.06em;}\n.docskin .footer{margin-top:8px;padding:18px 32px 28px;border-top:2px solid var(--navy);font-size:11px;color:var(--gray);text-transform:uppercase;letter-spacing:.1em;font-weight:700;display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;}\n.docskin .footer .accent{color:var(--highlight);}\n.docskin .layer-label{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;}\n.docskin .uml-name{font-family:var(--font-display);font-size:13px;font-weight:700;text-anchor:middle;fill:#0a3a6e;}\n.docskin .uml-stereo{font-family:var(--font-body);font-size:9px;font-style:italic;text-anchor:middle;fill:#6b7280;}\n.docskin .uml-row{font-family:var(--font-mono);font-size:10.5px;fill:var(--charcoal);}\n.docskin .uml-sep{stroke:#0e54a1;stroke-width:1;}\n.docskin .ft-note{font-family:var(--font-mono);font-size:9px;}\n.docskin .tree-link{stroke:#9ca3af;stroke-width:1.3;fill:none;}\n/* wireframe / UI mockups */\n.docskin .wf-h{font-family:var(--font-display);font-size:18px;font-weight:700;}\n.docskin .wf-sub{font-family:var(--font-body);font-size:12px;font-weight:600;}\n.docskin .wf-btn{font-family:var(--font-body);font-size:11px;font-weight:700;fill:#fff;}\n.docskin .wf-ph-text{font-family:var(--font-body);font-size:10px;fill:var(--gray);}\n.docskin .wf-status{font-family:var(--font-mono);font-size:9px;fill:var(--charcoal);font-weight:700;}\n.docskin .wf-url{font-family:var(--font-mono);font-size:8.5px;fill:var(--gray);}\n.docskin .wf-tab{font-family:var(--font-body);font-size:8px;}\n.docskin .wf-caption{font-family:var(--font-mono);font-size:10px;fill:var(--gray);letter-spacing:.04em;}\n/* parse error */\n.docskin .err{font-family:var(--font-mono);font-size:12px;color:var(--negative);background:#fdf2f2;border:1px solid #f3c9c9;padding:8px 12px;margin:12px 0;white-space:pre-wrap;}";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* HTML entity escaping, matching the reference renderer's `esc` semantics.
|
|
18
|
+
*
|
|
19
|
+
* Escapes `&`, `<`, `>`, and `"`. `null`/`undefined` become the empty string.
|
|
20
|
+
* Non-string values are coerced via `String()`.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Escapes a value for safe insertion into HTML text or attribute content.
|
|
24
|
+
*
|
|
25
|
+
* @param value - The value to escape (any type).
|
|
26
|
+
* @returns The escaped string. Empty string for `null`/`undefined`.
|
|
27
|
+
*/
|
|
28
|
+
declare function escapeHtml(value: unknown): string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Output-safety helpers.
|
|
32
|
+
*
|
|
33
|
+
* Avodado documents are normally authored by trusted hands, but rendered output
|
|
34
|
+
* is also shown in untrusted contexts — the playground renders documents from
|
|
35
|
+
* shared URLs, and any hosted preview renders whatever a visitor pastes. So the
|
|
36
|
+
* renderer treats author-supplied values (colours, link hrefs, prose HTML) as
|
|
37
|
+
* untrusted and neutralises anything that could break out of its target context.
|
|
38
|
+
*
|
|
39
|
+
* These functions are pure and allocation-light; they run on every render.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Returns `value` if it is a safe CSS colour, otherwise `fallback`.
|
|
43
|
+
*
|
|
44
|
+
* Use this for any author-supplied colour that gets interpolated into an SVG
|
|
45
|
+
* attribute (`fill`, `stroke`) or a `style` declaration.
|
|
46
|
+
*
|
|
47
|
+
* @param value - The candidate colour (any author string, or undefined).
|
|
48
|
+
* @param fallback - The colour to use when `value` is missing or unsafe.
|
|
49
|
+
*/
|
|
50
|
+
declare function safeColor(value: string | undefined, fallback: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Returns `url` if its scheme is safe to put in an `href`/`src`, otherwise `#`.
|
|
53
|
+
*
|
|
54
|
+
* Relative URLs, fragments (`#id`), and the usual web schemes pass through;
|
|
55
|
+
* `javascript:`, `data:`, and `vbscript:` are rejected.
|
|
56
|
+
*
|
|
57
|
+
* @param url - The candidate URL.
|
|
58
|
+
*/
|
|
59
|
+
declare function safeUrl(url: string): string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Renders a prose segment to HTML via `marked`.
|
|
63
|
+
*
|
|
64
|
+
* Hardened for untrusted input (the renderer's output is shown in the
|
|
65
|
+
* playground and hosted previews, which render documents from strangers):
|
|
66
|
+
*
|
|
67
|
+
* - Raw HTML is **not** passed through — `marked` v14 emits raw HTML by
|
|
68
|
+
* default, which is an XSS vector. We decline the block `html` and inline
|
|
69
|
+
* `tag` tokenizers so any literal `<tag>` falls through to text and is
|
|
70
|
+
* entity-escaped. This also matches Avodado's house rule: express structure
|
|
71
|
+
* through blocks, never raw HTML.
|
|
72
|
+
* - Link / image hrefs with `javascript:`, `data:`, or `vbscript:` schemes are
|
|
73
|
+
* rewritten to `#` (see {@link safeUrl}).
|
|
74
|
+
*
|
|
75
|
+
* Output is deterministic (no auto heading ids, no smartypants).
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* Renders Markdown prose to an HTML string wrapped in `<div class="prose">`.
|
|
79
|
+
*
|
|
80
|
+
* @param text - The Markdown source.
|
|
81
|
+
* @returns The HTML output, wrapped in a `prose` div for styling scope.
|
|
82
|
+
*/
|
|
83
|
+
declare function renderProse(text: string): string;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Theme system. Each theme is a set of CSS variable overrides applied via
|
|
87
|
+
* `style="--navy: …; --blue: …"` on the `.docskin` root element. The full
|
|
88
|
+
* stylesheet (see {@link houseCss}) reads those variables.
|
|
89
|
+
*
|
|
90
|
+
* Adding a new theme = add an entry here. No CSS changes needed.
|
|
91
|
+
*
|
|
92
|
+
* Ported verbatim from `resources/doc-studio.jsx` `THEMES`.
|
|
93
|
+
*/
|
|
94
|
+
/** Built-in theme names. */
|
|
95
|
+
type ThemeName = 'navy' | 'teal' | 'plum' | 'slate' | 'dark' | 'soft';
|
|
96
|
+
/** Default theme used when none is specified. */
|
|
97
|
+
declare const DEFAULT_THEME: ThemeName;
|
|
98
|
+
interface ThemeDef {
|
|
99
|
+
/** Human-readable label, for UI surfaces. */
|
|
100
|
+
readonly label: string;
|
|
101
|
+
/** CSS variable overrides applied at the `.docskin` root. */
|
|
102
|
+
readonly vars: Readonly<Record<string, string>>;
|
|
103
|
+
}
|
|
104
|
+
/** The four built-in themes. */
|
|
105
|
+
declare const themes: Readonly<Record<ThemeName, ThemeDef>>;
|
|
106
|
+
/**
|
|
107
|
+
* Returns the CSS variable overrides for a theme as an inline-style string
|
|
108
|
+
* (e.g. `"--navy:#0f766e;--blue:#0e7490;"`). Empty string for the default
|
|
109
|
+
* navy theme.
|
|
110
|
+
*/
|
|
111
|
+
declare function themeStyle(name: ThemeName): string;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Renders a parsed {@link Document} into its composable PARTS — the CSS, the
|
|
115
|
+
* theme-variable string, the inner body HTML (for a `<div class="docskin">`),
|
|
116
|
+
* the title, and a section index for navigation.
|
|
117
|
+
*
|
|
118
|
+
* {@link renderDocument} (in `document.ts`) wraps these parts into a standalone
|
|
119
|
+
* `<!doctype html>` page. Embedding consumers (e.g. a React app) inject the
|
|
120
|
+
* parts directly: one `<style>` with `css`, a scoped `<style>` that sets the
|
|
121
|
+
* theme vars, and the `body` inside their own `.docskin` host. This avoids
|
|
122
|
+
* nesting a full HTML document inside the page and enables live theme switching
|
|
123
|
+
* (swap only the theme-vars style) and section-level navigation (each section
|
|
124
|
+
* carries an `id`).
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/** Options shared by {@link renderDocumentParts} and the page renderer. */
|
|
128
|
+
interface RenderPartsOptions {
|
|
129
|
+
/** Theme name. Defaults to `navy`. */
|
|
130
|
+
readonly theme?: ThemeName;
|
|
131
|
+
/**
|
|
132
|
+
* Custom CSS-variable overrides applied after the named theme (they win),
|
|
133
|
+
* e.g. `{ '--navy': '#123456' }`.
|
|
134
|
+
*/
|
|
135
|
+
readonly themeVars?: Readonly<Record<string, string>>;
|
|
136
|
+
}
|
|
137
|
+
/** One navigable section of a rendered document. */
|
|
138
|
+
interface DocumentSection {
|
|
139
|
+
/** DOM id of the `<section>` (e.g. `section-01`). */
|
|
140
|
+
readonly id: string;
|
|
141
|
+
/** 1-based section number. */
|
|
142
|
+
readonly num: number;
|
|
143
|
+
/** Section label (e.g. `Roadmap`, `Sequence`). */
|
|
144
|
+
readonly label: string;
|
|
145
|
+
/** The block's title, if it has one. */
|
|
146
|
+
readonly title?: string;
|
|
147
|
+
}
|
|
148
|
+
/** The composable pieces of a rendered document. */
|
|
149
|
+
interface DocumentParts {
|
|
150
|
+
/** Theme-independent house stylesheet. Inject once. */
|
|
151
|
+
readonly css: string;
|
|
152
|
+
/** Theme variable declarations (e.g. `--navy:#0f766e;`), or `''` for default. */
|
|
153
|
+
readonly themeVars: string;
|
|
154
|
+
/** Inner HTML for a `<div class="docskin">` host: defs + cover + sections. */
|
|
155
|
+
readonly body: string;
|
|
156
|
+
/** Document title (from the `meta` block). */
|
|
157
|
+
readonly title: string;
|
|
158
|
+
/** Section index for navigation. */
|
|
159
|
+
readonly sections: readonly DocumentSection[];
|
|
160
|
+
}
|
|
161
|
+
/** Builds the theme-variable declaration string (named theme + overrides). */
|
|
162
|
+
declare function buildThemeVars(theme: ThemeName, vars?: Readonly<Record<string, string>>): string;
|
|
163
|
+
/**
|
|
164
|
+
* Renders a document into its composable parts.
|
|
165
|
+
*
|
|
166
|
+
* @param doc - The parsed Avodado document.
|
|
167
|
+
* @param opts - Optional theme + variable overrides.
|
|
168
|
+
* @returns The CSS, theme vars, body HTML, title, and section index.
|
|
169
|
+
*/
|
|
170
|
+
declare function renderDocumentParts(doc: Document, opts?: RenderPartsOptions): DocumentParts;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Renders a parsed {@link Document} to a standalone HTML string.
|
|
174
|
+
*
|
|
175
|
+
* - Inlines the house CSS in `<style>` so the output is self-contained.
|
|
176
|
+
* - Wraps the body in `<div class="docskin">` so the CSS rules apply.
|
|
177
|
+
* - Applies an optional theme by setting CSS variables on `:root`.
|
|
178
|
+
*
|
|
179
|
+
* The actual rendering is done by {@link renderDocumentParts} (in `parts.ts`);
|
|
180
|
+
* this function just wraps those parts into a full HTML page. Embedding
|
|
181
|
+
* consumers (e.g. a React app) should use `renderDocumentParts` directly.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* import { parseDocument } from '@avodado/core';
|
|
186
|
+
* import { renderDocument } from '@avodado/render';
|
|
187
|
+
*
|
|
188
|
+
* const html = renderDocument(parseDocument(md, 'orders'), { theme: 'teal' });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
/** Options for {@link renderDocument}. */
|
|
193
|
+
type RenderOptions = RenderPartsOptions;
|
|
194
|
+
/**
|
|
195
|
+
* Renders a document to a standalone HTML page.
|
|
196
|
+
*
|
|
197
|
+
* @param doc - The parsed Avodado document.
|
|
198
|
+
* @param opts - Optional render options (theme).
|
|
199
|
+
* @returns A complete HTML string (`<!doctype html>…</html>`).
|
|
200
|
+
*/
|
|
201
|
+
declare function renderDocument(doc: Document, opts?: RenderOptions): string;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* HTML renderer registry — mirrors the core block registry's exhaustiveness.
|
|
205
|
+
*
|
|
206
|
+
* Each block type maps to a function that takes its typed data and returns an
|
|
207
|
+
* HTML string. The mapped type `{ [K in BlockType]: ... }` ensures omitting a
|
|
208
|
+
* block type is a compile error — adding a new BlockType in core fails tsc here
|
|
209
|
+
* until the renderer is added.
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
/** Per-block HTML renderer signature. */
|
|
213
|
+
type HtmlRenderer<K extends BlockType> = (data: BlockDataMap[K]) => string;
|
|
214
|
+
/** Mapped type — adding a {@link BlockType} without an entry fails tsc. */
|
|
215
|
+
type HtmlRendererRegistry = {
|
|
216
|
+
readonly [K in BlockType]: HtmlRenderer<K>;
|
|
217
|
+
};
|
|
218
|
+
/** The HTML renderer registry. `meta` is intentionally a no-op (cover is rendered separately). */
|
|
219
|
+
declare const htmlRenderers: HtmlRendererRegistry;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Global SVG `<defs>` shared by multiple block renderers (markers used as edge
|
|
223
|
+
* arrow heads, a drop-shadow filter for boxed nodes).
|
|
224
|
+
*
|
|
225
|
+
* Emitted once near the top of the rendered body so subsequent SVGs can
|
|
226
|
+
* reference them by id. Ported from `resources/doc-studio.jsx` `GlobalDefs`.
|
|
227
|
+
*/
|
|
228
|
+
/**
|
|
229
|
+
* Returns the SVG element containing global marker + filter definitions.
|
|
230
|
+
* The element is invisible (width/height = 0) but the defs are scoped to the
|
|
231
|
+
* document so any subsequent `<svg>` can use `marker-end="url(#gArrow)"` etc.
|
|
232
|
+
*/
|
|
233
|
+
declare function globalDefsSvg(): string;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Manhattan / orthogonal edge routing between two rectangular nodes.
|
|
237
|
+
*
|
|
238
|
+
* Returns the SVG path `d` attribute and a midpoint for label placement.
|
|
239
|
+
* The route turns at the midpoint along the dominant axis, so edges read
|
|
240
|
+
* cleanly even when the source and target overlap on one axis.
|
|
241
|
+
*
|
|
242
|
+
* Ported from `resources/doc-studio.jsx` `ortho`.
|
|
243
|
+
*/
|
|
244
|
+
/** A rectangular node bounding box (top-left + size). */
|
|
245
|
+
interface Box {
|
|
246
|
+
readonly x: number;
|
|
247
|
+
readonly y: number;
|
|
248
|
+
readonly w: number;
|
|
249
|
+
readonly h: number;
|
|
250
|
+
}
|
|
251
|
+
/** Routed edge: SVG `d` plus a midpoint for the edge label. */
|
|
252
|
+
interface Route {
|
|
253
|
+
readonly d: string;
|
|
254
|
+
readonly lx: number;
|
|
255
|
+
readonly ly: number;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Routes an orthogonal edge from box A to box B.
|
|
259
|
+
*
|
|
260
|
+
* @param A - Source box.
|
|
261
|
+
* @param B - Target box.
|
|
262
|
+
* @returns SVG path data and label midpoint.
|
|
263
|
+
*/
|
|
264
|
+
declare function ortho(A: Box, B: Box): Route;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Wraps text to fit within an approximate character-per-line budget, capping
|
|
268
|
+
* the total number of lines. Word-aware (won't break mid-word).
|
|
269
|
+
*
|
|
270
|
+
* Used by diagram renderers for fixed-size labels. Ported from
|
|
271
|
+
* `resources/doc-studio.jsx` `wrapText`.
|
|
272
|
+
*/
|
|
273
|
+
/**
|
|
274
|
+
* @param text - Source text (any value is coerced via `String(...)`).
|
|
275
|
+
* @param max - Approximate maximum characters per line.
|
|
276
|
+
* @param maxLines - Maximum number of lines to return (later lines dropped).
|
|
277
|
+
* @returns An array of wrapped lines; empty array for empty input.
|
|
278
|
+
*/
|
|
279
|
+
declare function wrapText(text: unknown, max: number, maxLines: number): string[];
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Renders an SVG edge label as a small rounded pill, sized to the label text.
|
|
283
|
+
*
|
|
284
|
+
* Returned as an SVG `<g>` fragment so it can be appended inside an enclosing
|
|
285
|
+
* `<svg>`. Returns the empty string if `label` is falsy.
|
|
286
|
+
*
|
|
287
|
+
* Ported from `resources/doc-studio.jsx` `EdgePill`.
|
|
288
|
+
*/
|
|
289
|
+
/** Pill placement (midpoint of the labelled edge). */
|
|
290
|
+
interface PillPoint {
|
|
291
|
+
readonly lx: number;
|
|
292
|
+
readonly ly: number;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* @param p - Midpoint of the edge (typically from {@link ortho}).
|
|
296
|
+
* @param label - Label text. Empty/undefined yields an empty string.
|
|
297
|
+
* @param err - If true, the label is rendered in the error style.
|
|
298
|
+
*/
|
|
299
|
+
declare function edgePill(p: PillPoint, label: string | undefined, err?: boolean): string;
|
|
300
|
+
|
|
301
|
+
export { type Box, DEFAULT_THEME, type DocumentParts, type DocumentSection, type HtmlRenderer, type HtmlRendererRegistry, type PillPoint, type RenderOptions, type RenderPartsOptions, type Route, type ThemeName, buildThemeVars, edgePill, escapeHtml, globalDefsSvg, houseCss, htmlRenderers, ortho, renderDocument, renderDocumentParts, renderProse, safeColor, safeUrl, themeStyle, themes, wrapText };
|