@shivaedev/work-board 0.6.1 → 0.7.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/CHANGELOG.md +19 -2
- package/README.md +10 -0
- package/dist/cli.js +1 -1
- package/dist/page/style.d.ts.map +1 -1
- package/dist/page/style.js +25 -1
- package/dist/page/style.js.map +1 -1
- package/dist/render/board.d.ts.map +1 -1
- package/dist/render/board.js +5 -2
- package/dist/render/board.js.map +1 -1
- package/dist/render/callouts.d.ts +3 -0
- package/dist/render/callouts.d.ts.map +1 -0
- package/dist/render/callouts.js +27 -0
- package/dist/render/callouts.js.map +1 -0
- package/dist/render/markdown.d.ts.map +1 -1
- package/dist/render/markdown.js +4 -1
- package/dist/render/markdown.js.map +1 -1
- package/dist/render/visual.d.ts +3 -0
- package/dist/render/visual.d.ts.map +1 -0
- package/dist/render/visual.js +90 -0
- package/dist/render/visual.js.map +1 -0
- package/dist/render/visualDocuments.d.ts +5 -0
- package/dist/render/visualDocuments.d.ts.map +1 -0
- package/dist/render/visualDocuments.js +57 -0
- package/dist/render/visualDocuments.js.map +1 -0
- package/dist/render/visualFields.d.ts +12 -0
- package/dist/render/visualFields.d.ts.map +1 -0
- package/dist/render/visualFields.js +13 -0
- package/dist/render/visualFields.js.map +1 -0
- package/dist/search/entries.d.ts.map +1 -1
- package/dist/search/entries.js +4 -1
- package/dist/search/entries.js.map +1 -1
- package/package.json +4 -2
- package/src/cli.ts +1 -1
- package/src/page/style.ts +26 -1
- package/src/render/board.ts +5 -2
- package/src/render/callouts.ts +29 -0
- package/src/render/markdown.ts +4 -1
- package/src/render/visual.ts +100 -0
- package/src/render/visualDocuments.ts +68 -0
- package/src/render/visualFields.ts +17 -0
- package/src/search/entries.ts +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
### Changed
|
|
6
6
|
|
|
7
|
-
- Prepare concrete step 11 visual-document source examples for discussion; the
|
|
8
|
-
proposed block conventions are not implemented or approved yet.
|
|
9
7
|
- Record the approved Platform-first live client direction and its actual shared HTTP/browser fixture evidence; Work Board adopts that foundation in 0.5.0 below.
|
|
10
8
|
|
|
9
|
+
## 0.7.0 - 2026-10-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Read-only metric, progress and authored timeline Markdown directives parsed by
|
|
14
|
+
`remark-directive` and validated with Effect Schema. Keep Markdown labels/source
|
|
15
|
+
links, explicit unknown values and native labeled progress with text alternatives.
|
|
16
|
+
Retain invalid/unsupported source with diagnostics and bound each component to
|
|
17
|
+
64 KiB and each timeline to 100 entries. Nested composition remains deferred.
|
|
18
|
+
- GitHub NOTE/TIP/IMPORTANT/WARNING/CAUTION callouts, with comparisons remaining
|
|
19
|
+
ordinary GFM tables. The project brief uses the agreed source conventions.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Use the same remark parser/directive grammar for legacy board segmentation,
|
|
24
|
+
rendering and search; remove Satteri. Headings inside directive containers do
|
|
25
|
+
not become board columns. Keep native live/history ownership and local-only
|
|
26
|
+
reading without source writes or acceptance inference.
|
|
27
|
+
|
|
11
28
|
## 0.6.1 - 2026-10-06
|
|
12
29
|
|
|
13
30
|
### Changed
|
package/README.md
CHANGED
|
@@ -402,3 +402,13 @@ server-rendered Markdown, with locally served Mermaid for ordinary `mermaid`
|
|
|
402
402
|
fences. Existing local links, images, heading anchors, footnotes, raw-HTML details
|
|
403
403
|
and Shiki code highlighting remain available without React client state. Mermaid
|
|
404
404
|
rendering and inspection need JavaScript; its source remains readable without it.
|
|
405
|
+
|
|
406
|
+
### Visual document directives
|
|
407
|
+
|
|
408
|
+
Use `:::metric{value="50" unit="documents"}`, `:::progress{completed="3" total="8"}`
|
|
409
|
+
and `:::timeline` containers with Markdown labels and linked sources. Components
|
|
410
|
+
render on the server and remain readable without JavaScript. Unknown counts stay
|
|
411
|
+
unknown, invalid components retain their source with a diagnostic, and timelines
|
|
412
|
+
preserve authored order. Recorded values do not establish verified acceptance.
|
|
413
|
+
See [the complete source conventions](./docs/vision/delivery/visual-document-examples.md).
|
|
414
|
+
GitHub alert blockquotes render as callouts; comparisons remain ordinary GFM tables.
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
|
3
3
|
import { Effect, Layer, Option, Path } from "effect";
|
|
4
4
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { HOST, serveBoard } from "./serve.js";
|
|
6
|
-
const VERSION = "0.
|
|
6
|
+
const VERSION = "0.7.0";
|
|
7
7
|
const serve = Effect.fn("WorkBoard.serve")(function* (input) {
|
|
8
8
|
const path = yield* Path.Path;
|
|
9
9
|
return yield* Layer.launch(serveBoard({ home: Option.getOrUndefined(input.home), port: input.port, root: path.resolve(input.dir) }));
|
package/dist/page/style.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AA0EA,eAAO,MAAM,KAAK,QAYN,CAAC"}
|
package/dist/page/style.js
CHANGED
|
@@ -54,5 +54,29 @@ figure.diagram[data-state="failed"] .diagram-source { display: block; }
|
|
|
54
54
|
[data-diagram-error] { color: var(--danger); font-size: 0.875em; }
|
|
55
55
|
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }
|
|
56
56
|
`;
|
|
57
|
-
|
|
57
|
+
const documents = `
|
|
58
|
+
.visual-document, .visual-callout { border: 1px solid var(--border); border-radius: 12px; padding: 1em 1.2em; margin: 1.2em 0; overflow-wrap: anywhere; }
|
|
59
|
+
.visual-document > :last-child, .visual-callout > :last-child { margin-bottom: 0; }
|
|
60
|
+
.visual-label { font-weight: 650; margin-top: 0; }
|
|
61
|
+
.visual-value { font-size: 1.35em; font-variant-numeric: tabular-nums; }
|
|
62
|
+
.visual-document progress { display: block; width: 100%; height: 1em; accent-color: var(--link); }
|
|
63
|
+
.visual-timeline-entries { border-left: 2px solid var(--border); padding-left: 1.6em; }
|
|
64
|
+
.visual-timeline-entries > li { padding: .3em 0 .7em .2em; }
|
|
65
|
+
.visual-callout { border-left: 4px solid var(--link); background: var(--surface); }
|
|
66
|
+
.visual-warning, .visual-caution { border-left-color: #b97720; }
|
|
67
|
+
.visual-diagnostic { font-weight: 600; color: var(--muted); }
|
|
68
|
+
@media print { .visual-document, .visual-callout { break-inside: avoid; } }
|
|
69
|
+
`;
|
|
70
|
+
export const style = theme
|
|
71
|
+
+ layout
|
|
72
|
+
+ prose
|
|
73
|
+
+ board
|
|
74
|
+
+ diagrams
|
|
75
|
+
+ readingStyles()
|
|
76
|
+
+ searchStyles
|
|
77
|
+
+ metadataStyles
|
|
78
|
+
+ workStyle()
|
|
79
|
+
+ attentionStyle()
|
|
80
|
+
+ visualStyles
|
|
81
|
+
+ documents;
|
|
58
82
|
//# sourceMappingURL=style.js.map
|
package/dist/page/style.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Bb,CAAC;AAEF,MAAM,KAAK,GAAG;;;;;;;;;;;;CAYb,CAAC;AAEF,MAAM,QAAQ,GAAG;;;;;;;;CAQhB,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GACjB,KAAK,
|
|
1
|
+
{"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Bb,CAAC;AAEF,MAAM,KAAK,GAAG;;;;;;;;;;;;CAYb,CAAC;AAEF,MAAM,QAAQ,GAAG;;;;;;;;CAQhB,CAAC;AAEF,MAAM,SAAS,GAAG;;;;;;;;;;;;CAYjB,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GACjB,KAAK;MACH,MAAM;MACN,KAAK;MACL,KAAK;MACL,QAAQ;MACR,aAAa,EAAE;MACf,YAAY;MACZ,cAAc;MACd,SAAS,EAAE;MACX,cAAc,EAAE;MAChB,YAAY;MACZ,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/render/board.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/render/board.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AA0CD,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,KAAG,KAexC,CAAC;AAEF,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,KAAK,KAAG,SAAS,KAAK,EAC2E,CAAC"}
|
package/dist/render/board.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import remarkDirective from "remark-directive";
|
|
2
|
+
import remarkGfm from "remark-gfm";
|
|
3
|
+
import remarkParse from "remark-parse";
|
|
4
|
+
import { unified } from "unified";
|
|
2
5
|
const textOf = (node) => {
|
|
3
6
|
if ("value" in node) {
|
|
4
7
|
return node.value;
|
|
@@ -36,7 +39,7 @@ const place = (draft, node, source, closing) => {
|
|
|
36
39
|
const joined = (blocks) => blocks.join("\n\n");
|
|
37
40
|
export const boardOf = (source) => {
|
|
38
41
|
const draft = { definitions: [], footer: undefined, intro: [], sections: [], title: "" };
|
|
39
|
-
const root =
|
|
42
|
+
const root = unified().use(remarkParse).use(remarkGfm).use(remarkDirective).parse(source);
|
|
40
43
|
const nodes = root.type === "root" ? root.children : [];
|
|
41
44
|
const lastHeading = nodes.findLastIndex((node) => node.type === "heading");
|
|
42
45
|
nodes.forEach((node, index) => {
|
package/dist/render/board.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board.js","sourceRoot":"","sources":["../../src/render/board.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"board.js","sourceRoot":"","sources":["../../src/render/board.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callouts.d.ts","sourceRoot":"","sources":["../../src/render/callouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAKlC,wBAAgB,QAAQ,KACG,MAAM,IAAI,UAsBpC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { visit as visitNodes } from "unist-util-visit";
|
|
2
|
+
const alerts = new Set(["NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION"]);
|
|
3
|
+
export function callouts() {
|
|
4
|
+
return function transform(tree) {
|
|
5
|
+
visitNodes(tree, "blockquote", (node) => {
|
|
6
|
+
const [first] = node.children;
|
|
7
|
+
const text = first?.type === "paragraph" ? first.children.at(0) : undefined;
|
|
8
|
+
if (text?.type !== "text") {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const match = /^\[!(?<kind>[A-Z]+)\](?:\n|$)/u.exec(text.value);
|
|
12
|
+
const named = match?.groups?.kind;
|
|
13
|
+
if (!(named && alerts.has(named))) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const kind = named.toLowerCase();
|
|
17
|
+
text.value = text.value.slice(match?.[0].length ?? 0);
|
|
18
|
+
node.children.unshift({
|
|
19
|
+
children: [{ type: "text", value: named[0] + kind.slice(1) }],
|
|
20
|
+
data: { hProperties: { className: ["visual-label"] } },
|
|
21
|
+
type: "paragraph",
|
|
22
|
+
});
|
|
23
|
+
node.data = { ...node.data, hName: "aside", hProperties: { className: ["visual-callout", `visual-${kind}`], role: "note" } };
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=callouts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callouts.js","sourceRoot":"","sources":["../../src/render/callouts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;AAE3E,MAAM,UAAU,QAAQ;IACvB,OAAO,SAAS,SAAS,CAAC,IAAU;QACnC,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;YACvC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5E,IAAI,IAAI,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,OAAO;YACR,CAAC;YACD,MAAM,KAAK,GAAG,gCAAgC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,KAAK,GAAG,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC;YAClC,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACnC,OAAO;YACR,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;YACtD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACrB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,IAAI,EAAE,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,cAAc,CAAC,EAAE,EAAE;gBACtD,IAAI,EAAE,WAAW;aACjB,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,gBAAgB,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;QAC9H,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/render/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/render/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAWhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,2GAyBzB,CAAC"}
|
package/dist/render/markdown.js
CHANGED
|
@@ -3,7 +3,10 @@ import { createElement } from "react";
|
|
|
3
3
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
4
|
import { MarkdownAsync } from "react-markdown";
|
|
5
5
|
import rehypeRaw from "rehype-raw";
|
|
6
|
+
import remarkDirective from "remark-directive";
|
|
6
7
|
import remarkGfm from "remark-gfm";
|
|
8
|
+
import { callouts } from "#render/callouts.ts";
|
|
9
|
+
import { visualDocuments } from "#render/visualDocuments.ts";
|
|
7
10
|
import { codeBlocks } from "./code.js";
|
|
8
11
|
import { documentLinks } from "./documentLinks.js";
|
|
9
12
|
import { RenderFailed } from "./failed.js";
|
|
@@ -27,7 +30,7 @@ export const renderMarkdown = Effect.fn("WorkBoard.renderMarkdown")(function* (s
|
|
|
27
30
|
children: source,
|
|
28
31
|
components: { img: ({ node: _node, ...props }) => createElement("img", { ...props, loading: "lazy" }) },
|
|
29
32
|
rehypePlugins: plugins,
|
|
30
|
-
remarkPlugins: [remarkGfm],
|
|
33
|
+
remarkPlugins: [remarkGfm, remarkDirective, visualDocuments, callouts],
|
|
31
34
|
})),
|
|
32
35
|
});
|
|
33
36
|
return html;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/render/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAc,EAAE,UAAyB,EAAE;IACzH,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IACvC,MAAM,OAAO,GAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACpE,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACrC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC;QAC7C,GAAG,EAAE,KAAK,IAAI,EAAE,CACf,oBAAoB,CACnB,MAAM,aAAa,CAAC;YACnB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE;YACvG,aAAa,EAAE,OAAO;YACtB,aAAa,EAAE,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/render/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAc,EAAE,UAAyB,EAAE;IACzH,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IACvC,MAAM,OAAO,GAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACpE,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACrC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC;QAC7C,GAAG,EAAE,KAAK,IAAI,EAAE,CACf,oBAAoB,CACnB,MAAM,aAAa,CAAC;YACnB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE;YACvG,aAAa,EAAE,OAAO;YACtB,aAAa,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,QAAQ,CAAC;SACtE,CAAC,CACF;KACF,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACb,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../src/render/visual.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAgF/D,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,QAiBpD"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { MetricFields, ProgressFields } from "./visualFields.js";
|
|
3
|
+
function textOf(node) {
|
|
4
|
+
if ("value" in node) {
|
|
5
|
+
return node.value;
|
|
6
|
+
}
|
|
7
|
+
return "children" in node ? node.children.map((child) => textOf(child)).join("") : "";
|
|
8
|
+
}
|
|
9
|
+
function paragraph(value, className) {
|
|
10
|
+
return { children: [{ type: "text", value }], data: { hProperties: { className: className ? [className] : [] } }, type: "paragraph" };
|
|
11
|
+
}
|
|
12
|
+
function hasLink(node) {
|
|
13
|
+
if (node.type === "link") {
|
|
14
|
+
return node.url.trim().length > 0;
|
|
15
|
+
}
|
|
16
|
+
if (node.type === "linkReference") {
|
|
17
|
+
return node.identifier.trim().length > 0;
|
|
18
|
+
}
|
|
19
|
+
return "children" in node && node.children.some((child) => hasLink(child));
|
|
20
|
+
}
|
|
21
|
+
function provenance(node) {
|
|
22
|
+
return node.children.some((child) => child.type === "paragraph" && textOf(child).toLowerCase().startsWith("source:") && hasLink(child));
|
|
23
|
+
}
|
|
24
|
+
function fields(schema, node) {
|
|
25
|
+
const parsed = Schema.decodeUnknownOption(schema, { onExcessProperty: "error" })(node.attributes ?? {});
|
|
26
|
+
if (Option.isNone(parsed)) {
|
|
27
|
+
throw new Error("Invalid or unknown attributes.");
|
|
28
|
+
}
|
|
29
|
+
return parsed.value;
|
|
30
|
+
}
|
|
31
|
+
function metric(node) {
|
|
32
|
+
const { unit, value } = fields(MetricFields, node);
|
|
33
|
+
if (!provenance(node)) {
|
|
34
|
+
throw new Error("Name a source with a Source: Markdown link.");
|
|
35
|
+
}
|
|
36
|
+
node.children.splice(1, 0, paragraph(value === undefined || value === "unknown" ? "Not recorded" : `${value} ${unit}`, "visual-value"));
|
|
37
|
+
}
|
|
38
|
+
function progress(node, label) {
|
|
39
|
+
const { completed, total } = fields(ProgressFields, node);
|
|
40
|
+
if (!provenance(node)) {
|
|
41
|
+
throw new Error("Name a source with a Source: Markdown link.");
|
|
42
|
+
}
|
|
43
|
+
const known = typeof completed === "number" && typeof total === "number";
|
|
44
|
+
if (known && completed > total) {
|
|
45
|
+
throw new Error("Completed count exceeds total.");
|
|
46
|
+
}
|
|
47
|
+
function display(value) {
|
|
48
|
+
return typeof value === "number" ? String(value) : "Not recorded";
|
|
49
|
+
}
|
|
50
|
+
const tally = `Recorded tally: ${display(completed)} of ${display(total)}`;
|
|
51
|
+
node.children.splice(1, 0, paragraph(tally + (known && total > 0 ? "" : ". Percentage unavailable."), "visual-value"));
|
|
52
|
+
if (known && total > 0) {
|
|
53
|
+
node.children.splice(2, 0, {
|
|
54
|
+
children: [{ type: "text", value: tally }],
|
|
55
|
+
data: { hName: "progress", hProperties: { ariaLabel: `${label}: ${tally}`, max: String(total), value: String(completed) } },
|
|
56
|
+
type: "paragraph",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function timeline(node) {
|
|
61
|
+
fields(Schema.Struct({}), node);
|
|
62
|
+
const lists = node.children.filter((child) => child.type === "list");
|
|
63
|
+
const [list] = lists;
|
|
64
|
+
if (lists.length !== 1 || !list?.ordered || list.children.length === 0 || list.children.length > 100) {
|
|
65
|
+
throw new Error("Use one ordered list with 1–100 entries.");
|
|
66
|
+
}
|
|
67
|
+
if (list.children.some((item) => !hasLink(item) || textOf(item).trim().length === 0)) {
|
|
68
|
+
throw new Error("Each timeline entry needs text and a source link.");
|
|
69
|
+
}
|
|
70
|
+
list.data = { ...list.data, hProperties: { className: ["visual-timeline-entries"] } };
|
|
71
|
+
}
|
|
72
|
+
export function renderVisual(node) {
|
|
73
|
+
const [first] = node.children;
|
|
74
|
+
if (first?.type !== "paragraph" || textOf(first).trim().length === 0 || textOf(first).toLowerCase().startsWith("source:")) {
|
|
75
|
+
throw new Error("Start with a Markdown label paragraph.");
|
|
76
|
+
}
|
|
77
|
+
const label = textOf(first).trim();
|
|
78
|
+
if (node.name === "metric") {
|
|
79
|
+
metric(node);
|
|
80
|
+
}
|
|
81
|
+
if (node.name === "progress") {
|
|
82
|
+
progress(node, label);
|
|
83
|
+
}
|
|
84
|
+
if (node.name === "timeline") {
|
|
85
|
+
timeline(node);
|
|
86
|
+
}
|
|
87
|
+
first.data = { ...first.data, hProperties: { className: ["visual-label"] } };
|
|
88
|
+
node.data = { ...node.data, hName: "section", hProperties: { ariaLabel: label, className: ["visual-document", `visual-${node.name}`] } };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=visual.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual.js","sourceRoot":"","sources":["../../src/render/visual.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualDocuments.d.ts","sourceRoot":"","sources":["../../src/render/visualDocuments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAe,MAAM,OAAO,CAAC;AAsC/C,wBAAgB,eAAe,KACJ,MAAM,IAAI,EAAE,MAAM;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,UA4BvE"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { SKIP, visit as visitNodes } from "unist-util-visit";
|
|
2
|
+
import { renderVisual } from "./visual.js";
|
|
3
|
+
const names = new Set(["metric", "progress", "timeline"]);
|
|
4
|
+
function isDirective(node) {
|
|
5
|
+
return node.type === "containerDirective" || node.type === "leafDirective" || node.type === "textDirective";
|
|
6
|
+
}
|
|
7
|
+
function nested(node) {
|
|
8
|
+
return isDirective(node) || ("children" in node && node.children.some((child) => nested(child)));
|
|
9
|
+
}
|
|
10
|
+
function failureOf(node, source) {
|
|
11
|
+
try {
|
|
12
|
+
if (!names.has(node.name)) {
|
|
13
|
+
throw new Error(`Unknown visual directive ${node.name}.`);
|
|
14
|
+
}
|
|
15
|
+
if (node.type !== "containerDirective") {
|
|
16
|
+
throw new Error("Use a ::: container directive.");
|
|
17
|
+
}
|
|
18
|
+
const length = new TextEncoder().encode(source.slice(node.position?.start.offset, node.position?.end.offset)).byteLength;
|
|
19
|
+
if (length > 65_536) {
|
|
20
|
+
throw new Error("Visual directive exceeds 64 KiB.");
|
|
21
|
+
}
|
|
22
|
+
if (node.children.some((child) => nested(child))) {
|
|
23
|
+
throw new Error("Nested visual directives are not supported.");
|
|
24
|
+
}
|
|
25
|
+
renderVisual(node);
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return error instanceof Error ? error.message : String(error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function visualDocuments() {
|
|
33
|
+
return function transform(tree, file) {
|
|
34
|
+
const source = String(file.value);
|
|
35
|
+
visitNodes(tree, ["containerDirective", "leafDirective", "textDirective"], (directive, index, parent) => {
|
|
36
|
+
if (!isDirective(directive) || index === undefined || parent === undefined) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const failure = failureOf(directive, source);
|
|
40
|
+
if (failure === undefined) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const original = source.slice(directive.position?.start.offset, directive.position?.end.offset);
|
|
44
|
+
if (directive.type === "textDirective") {
|
|
45
|
+
parent.children.splice(index, 1, { type: "text", value: original });
|
|
46
|
+
return [SKIP, index + 1];
|
|
47
|
+
}
|
|
48
|
+
parent.children.splice(index, 1, {
|
|
49
|
+
children: [{ type: "text", value: `Visual component could not be rendered: ${failure}` }],
|
|
50
|
+
data: { hProperties: { className: ["visual-diagnostic"] } },
|
|
51
|
+
type: "paragraph",
|
|
52
|
+
}, { type: "code", value: original });
|
|
53
|
+
return [SKIP, index + 2];
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=visualDocuments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualDocuments.js","sourceRoot":"","sources":["../../src/render/visualDocuments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const MetricFields: Schema.Struct<{
|
|
3
|
+
readonly unit: Schema.String;
|
|
4
|
+
readonly value: Schema.optional<Schema.Union<readonly [Schema.decodeTo<Schema.Finite, Schema.String, never, never>, Schema.Literal<"unknown">]>>;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const ProgressFields: Schema.Struct<{
|
|
7
|
+
readonly completed: Schema.optional<Schema.Union<readonly [Schema.decodeTo<Schema.Finite, Schema.String, never, never>, Schema.Literal<"unknown">]>>;
|
|
8
|
+
readonly total: Schema.optional<Schema.Union<readonly [Schema.decodeTo<Schema.Finite, Schema.String, never, never>, Schema.Literal<"unknown">]>>;
|
|
9
|
+
}>;
|
|
10
|
+
export type MetricFields = typeof MetricFields.Type;
|
|
11
|
+
export type ProgressFields = typeof ProgressFields.Type;
|
|
12
|
+
//# sourceMappingURL=visualFields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualFields.d.ts","sourceRoot":"","sources":["../../src/render/visualFields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwB,MAAM,QAAQ,CAAC;AAMtD,eAAO,MAAM,YAAY;;;EAGvB,CAAC;AACH,eAAO,MAAM,cAAc;;;EAGzB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAC;AACpD,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Schema, SchemaTransformation } from "effect";
|
|
2
|
+
const NumberValue = Schema.String.check(Schema.isPattern(/\S/u)).pipe(Schema.decodeTo(Schema.Finite, SchemaTransformation.numberFromString));
|
|
3
|
+
const Count = NumberValue.check(Schema.makeFilter((value) => (Number.isSafeInteger(value) && value >= 0) || "Expected a nonnegative safe integer"));
|
|
4
|
+
const UnknownValue = Schema.Literal("unknown");
|
|
5
|
+
export const MetricFields = Schema.Struct({
|
|
6
|
+
unit: Schema.String.check(Schema.isPattern(/\S/u)),
|
|
7
|
+
value: Schema.optional(Schema.Union([NumberValue, UnknownValue])),
|
|
8
|
+
});
|
|
9
|
+
export const ProgressFields = Schema.Struct({
|
|
10
|
+
completed: Schema.optional(Schema.Union([Count, UnknownValue])),
|
|
11
|
+
total: Schema.optional(Schema.Union([Count, UnknownValue])),
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=visualFields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualFields.js","sourceRoot":"","sources":["../../src/render/visualFields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAEtD,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAC7I,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,IAAI,qCAAqC,CAAC,CAAC,CAAC;AACpJ,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;CACjE,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/D,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;CAC3D,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/search/entries.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/search/entries.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAWD,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,SAAI,EAAE,KAAK,cAAoB,GAAG,OAAO,CAAC,SAAS,KAAK,EAAE,CAAC,CA2B/I"}
|
package/dist/search/entries.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { MarkdownAsync } from "react-markdown";
|
|
2
2
|
import rehypeRaw from "rehype-raw";
|
|
3
|
+
import remarkDirective from "remark-directive";
|
|
3
4
|
import remarkGfm from "remark-gfm";
|
|
4
5
|
import { fileUrl } from "#files/url.ts";
|
|
5
6
|
import { boardOf } from "#render/board.ts";
|
|
7
|
+
import { callouts } from "#render/callouts.ts";
|
|
6
8
|
import { documentHeadings } from "#render/documentHeadings.ts";
|
|
9
|
+
import { visualDocuments } from "#render/visualDocuments.ts";
|
|
7
10
|
import { searchCollector } from "./collector.js";
|
|
8
11
|
import { linkCollector } from "./linkCollector.js";
|
|
9
12
|
function fragmentLocation(source, fragment, bodyLine, seen) {
|
|
@@ -31,7 +34,7 @@ export async function entriesOf(source, file, home, bodyLine = 1, links = new Se
|
|
|
31
34
|
await MarkdownAsync({
|
|
32
35
|
children: board?.definitions ? `${fragment}\n\n${board.definitions}` : fragment,
|
|
33
36
|
rehypePlugins: [rehypeRaw, headings.plugin, plugin, linkCollector(links)],
|
|
34
|
-
remarkPlugins: [remarkGfm],
|
|
37
|
+
remarkPlugins: [remarkGfm, remarkDirective, visualDocuments, callouts],
|
|
35
38
|
});
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.js","sourceRoot":"","sources":["../../src/search/entries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAWnD,SAAS,gBAAgB,CAAC,MAAc,EAAE,QAAgB,EAAE,QAAgB,EAAE,IAAyB;IACtG,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,IAAY,EAAE,IAAa,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,IAAI,GAAG,EAAU;IACnH,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,IAAI,YAAY,GAAuB,QAAQ,CAAC;IAChD,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,eAAe,CAC1C,IAAI,EACJ,GAAG,EAAE,CAAC,YAAY,EAClB,GAAG,EAAE,CAAC,cAAc,CACpB,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjD,MAAM,SAAS,GAAG,KAAK;QACtB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC;QACtI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACZ,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,IAAI,QAAQ,EAAE,CAAC;YACd,cAAc,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YAC7C,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC9F,MAAM,aAAa,CAAC;gBACnB,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,QAAQ,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ;gBAC/E,aAAa,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;gBACzE,aAAa,EAAE,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"entries.js","sourceRoot":"","sources":["../../src/search/entries.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/work-board",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Serve a folder of markdown files as a live local page that updates in place when a file changes",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/work-board#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -46,13 +46,15 @@
|
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@shivaedev/effect-contract": "0.4.0",
|
|
49
|
+
"mdast-util-directive": "3.1.0",
|
|
49
50
|
"mermaid": "11.16.1",
|
|
50
51
|
"react": "19.2.8",
|
|
51
52
|
"react-dom": "19.2.8",
|
|
52
53
|
"react-markdown": "10.1.0",
|
|
53
54
|
"rehype-raw": "7.0.0",
|
|
55
|
+
"remark-directive": "4.0.0",
|
|
54
56
|
"remark-gfm": "4.0.1",
|
|
55
|
-
"
|
|
57
|
+
"remark-parse": "11.0.0",
|
|
56
58
|
"shiki": "4.4.2",
|
|
57
59
|
"unified": "11.0.5",
|
|
58
60
|
"unist-util-visit": "5.0.0",
|
package/src/cli.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Effect, Layer, Option, Path } from "effect";
|
|
|
4
4
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { HOST, serveBoard } from "./serve.ts";
|
|
6
6
|
|
|
7
|
-
const VERSION = "0.
|
|
7
|
+
const VERSION = "0.7.0";
|
|
8
8
|
|
|
9
9
|
const serve = Effect.fn("WorkBoard.serve")(function* (input: { readonly dir: string; readonly port: number; readonly home: Option.Option<string> }) {
|
|
10
10
|
const path = yield* Path.Path;
|
package/src/page/style.ts
CHANGED
|
@@ -58,5 +58,30 @@ figure.diagram[data-state="failed"] .diagram-source { display: block; }
|
|
|
58
58
|
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }
|
|
59
59
|
`;
|
|
60
60
|
|
|
61
|
+
const documents = `
|
|
62
|
+
.visual-document, .visual-callout { border: 1px solid var(--border); border-radius: 12px; padding: 1em 1.2em; margin: 1.2em 0; overflow-wrap: anywhere; }
|
|
63
|
+
.visual-document > :last-child, .visual-callout > :last-child { margin-bottom: 0; }
|
|
64
|
+
.visual-label { font-weight: 650; margin-top: 0; }
|
|
65
|
+
.visual-value { font-size: 1.35em; font-variant-numeric: tabular-nums; }
|
|
66
|
+
.visual-document progress { display: block; width: 100%; height: 1em; accent-color: var(--link); }
|
|
67
|
+
.visual-timeline-entries { border-left: 2px solid var(--border); padding-left: 1.6em; }
|
|
68
|
+
.visual-timeline-entries > li { padding: .3em 0 .7em .2em; }
|
|
69
|
+
.visual-callout { border-left: 4px solid var(--link); background: var(--surface); }
|
|
70
|
+
.visual-warning, .visual-caution { border-left-color: #b97720; }
|
|
71
|
+
.visual-diagnostic { font-weight: 600; color: var(--muted); }
|
|
72
|
+
@media print { .visual-document, .visual-callout { break-inside: avoid; } }
|
|
73
|
+
`;
|
|
74
|
+
|
|
61
75
|
export const style =
|
|
62
|
-
theme
|
|
76
|
+
theme
|
|
77
|
+
+ layout
|
|
78
|
+
+ prose
|
|
79
|
+
+ board
|
|
80
|
+
+ diagrams
|
|
81
|
+
+ readingStyles()
|
|
82
|
+
+ searchStyles
|
|
83
|
+
+ metadataStyles
|
|
84
|
+
+ workStyle()
|
|
85
|
+
+ attentionStyle()
|
|
86
|
+
+ visualStyles
|
|
87
|
+
+ documents;
|
package/src/render/board.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { RootContent } from "mdast";
|
|
2
|
-
import
|
|
2
|
+
import remarkDirective from "remark-directive";
|
|
3
|
+
import remarkGfm from "remark-gfm";
|
|
4
|
+
import remarkParse from "remark-parse";
|
|
5
|
+
import { unified } from "unified";
|
|
3
6
|
|
|
4
7
|
export interface Section {
|
|
5
8
|
readonly heading: string;
|
|
@@ -58,7 +61,7 @@ const joined = (blocks: readonly string[]): string => blocks.join("\n\n");
|
|
|
58
61
|
|
|
59
62
|
export const boardOf = (source: string): Board => {
|
|
60
63
|
const draft: Draft = { definitions: [], footer: undefined, intro: [], sections: [], title: "" };
|
|
61
|
-
const root =
|
|
64
|
+
const root = unified().use(remarkParse).use(remarkGfm).use(remarkDirective).parse(source);
|
|
62
65
|
const nodes = root.type === "root" ? root.children : [];
|
|
63
66
|
const lastHeading = nodes.findLastIndex((node) => node.type === "heading");
|
|
64
67
|
nodes.forEach((node, index) => {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Root } from "mdast";
|
|
2
|
+
import { visit as visitNodes } from "unist-util-visit";
|
|
3
|
+
|
|
4
|
+
const alerts = new Set(["NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION"]);
|
|
5
|
+
|
|
6
|
+
export function callouts() {
|
|
7
|
+
return function transform(tree: Root) {
|
|
8
|
+
visitNodes(tree, "blockquote", (node) => {
|
|
9
|
+
const [first] = node.children;
|
|
10
|
+
const text = first?.type === "paragraph" ? first.children.at(0) : undefined;
|
|
11
|
+
if (text?.type !== "text") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const match = /^\[!(?<kind>[A-Z]+)\](?:\n|$)/u.exec(text.value);
|
|
15
|
+
const named = match?.groups?.kind;
|
|
16
|
+
if (!(named && alerts.has(named))) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const kind = named.toLowerCase();
|
|
20
|
+
text.value = text.value.slice(match?.[0].length ?? 0);
|
|
21
|
+
node.children.unshift({
|
|
22
|
+
children: [{ type: "text", value: named[0] + kind.slice(1) }],
|
|
23
|
+
data: { hProperties: { className: ["visual-label"] } },
|
|
24
|
+
type: "paragraph",
|
|
25
|
+
});
|
|
26
|
+
node.data = { ...node.data, hName: "aside", hProperties: { className: ["visual-callout", `visual-${kind}`], role: "note" } };
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/render/markdown.ts
CHANGED
|
@@ -3,8 +3,11 @@ import { createElement } from "react";
|
|
|
3
3
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
4
|
import { MarkdownAsync } from "react-markdown";
|
|
5
5
|
import rehypeRaw from "rehype-raw";
|
|
6
|
+
import remarkDirective from "remark-directive";
|
|
6
7
|
import remarkGfm from "remark-gfm";
|
|
7
8
|
import type { PluggableList } from "unified";
|
|
9
|
+
import { callouts } from "#render/callouts.ts";
|
|
10
|
+
import { visualDocuments } from "#render/visualDocuments.ts";
|
|
8
11
|
import { codeBlocks } from "./code.ts";
|
|
9
12
|
import type { documentHeadings } from "./documentHeadings.ts";
|
|
10
13
|
import { documentLinks } from "./documentLinks.ts";
|
|
@@ -38,7 +41,7 @@ export const renderMarkdown = Effect.fn("WorkBoard.renderMarkdown")(function* (s
|
|
|
38
41
|
children: source,
|
|
39
42
|
components: { img: ({ node: _node, ...props }) => createElement("img", { ...props, loading: "lazy" }) },
|
|
40
43
|
rehypePlugins: plugins,
|
|
41
|
-
remarkPlugins: [remarkGfm],
|
|
44
|
+
remarkPlugins: [remarkGfm, remarkDirective, visualDocuments, callouts],
|
|
42
45
|
}),
|
|
43
46
|
),
|
|
44
47
|
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import type { Paragraph, RootContent } from "mdast";
|
|
3
|
+
import type { ContainerDirective } from "mdast-util-directive";
|
|
4
|
+
import { MetricFields, ProgressFields } from "./visualFields.ts";
|
|
5
|
+
|
|
6
|
+
function textOf(node: RootContent): string {
|
|
7
|
+
if ("value" in node) {
|
|
8
|
+
return node.value;
|
|
9
|
+
}
|
|
10
|
+
return "children" in node ? node.children.map((child) => textOf(child)).join("") : "";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function paragraph(value: string, className?: string): Paragraph {
|
|
14
|
+
return { children: [{ type: "text", value }], data: { hProperties: { className: className ? [className] : [] } }, type: "paragraph" };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function hasLink(node: RootContent): boolean {
|
|
18
|
+
if (node.type === "link") {
|
|
19
|
+
return node.url.trim().length > 0;
|
|
20
|
+
}
|
|
21
|
+
if (node.type === "linkReference") {
|
|
22
|
+
return node.identifier.trim().length > 0;
|
|
23
|
+
}
|
|
24
|
+
return "children" in node && node.children.some((child) => hasLink(child));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function provenance(node: ContainerDirective): boolean {
|
|
28
|
+
return node.children.some((child) => child.type === "paragraph" && textOf(child).toLowerCase().startsWith("source:") && hasLink(child));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function fields<A>(schema: Schema.ConstraintDecoder<A, never>, node: ContainerDirective): A {
|
|
32
|
+
const parsed = Schema.decodeUnknownOption(schema, { onExcessProperty: "error" })(node.attributes ?? {});
|
|
33
|
+
if (Option.isNone(parsed)) {
|
|
34
|
+
throw new Error("Invalid or unknown attributes.");
|
|
35
|
+
}
|
|
36
|
+
return parsed.value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function metric(node: ContainerDirective) {
|
|
40
|
+
const { unit, value } = fields(MetricFields, node);
|
|
41
|
+
if (!provenance(node)) {
|
|
42
|
+
throw new Error("Name a source with a Source: Markdown link.");
|
|
43
|
+
}
|
|
44
|
+
node.children.splice(1, 0, paragraph(value === undefined || value === "unknown" ? "Not recorded" : `${value} ${unit}`, "visual-value"));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function progress(node: ContainerDirective, label: string) {
|
|
48
|
+
const { completed, total } = fields(ProgressFields, node);
|
|
49
|
+
if (!provenance(node)) {
|
|
50
|
+
throw new Error("Name a source with a Source: Markdown link.");
|
|
51
|
+
}
|
|
52
|
+
const known = typeof completed === "number" && typeof total === "number";
|
|
53
|
+
if (known && completed > total) {
|
|
54
|
+
throw new Error("Completed count exceeds total.");
|
|
55
|
+
}
|
|
56
|
+
function display(value: number | "unknown" | undefined) {
|
|
57
|
+
return typeof value === "number" ? String(value) : "Not recorded";
|
|
58
|
+
}
|
|
59
|
+
const tally = `Recorded tally: ${display(completed)} of ${display(total)}`;
|
|
60
|
+
node.children.splice(1, 0, paragraph(tally + (known && total > 0 ? "" : ". Percentage unavailable."), "visual-value"));
|
|
61
|
+
if (known && total > 0) {
|
|
62
|
+
node.children.splice(2, 0, {
|
|
63
|
+
children: [{ type: "text", value: tally }],
|
|
64
|
+
data: { hName: "progress", hProperties: { ariaLabel: `${label}: ${tally}`, max: String(total), value: String(completed) } },
|
|
65
|
+
type: "paragraph",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function timeline(node: ContainerDirective) {
|
|
71
|
+
fields(Schema.Struct({}), node);
|
|
72
|
+
const lists = node.children.filter((child) => child.type === "list");
|
|
73
|
+
const [list] = lists;
|
|
74
|
+
if (lists.length !== 1 || !list?.ordered || list.children.length === 0 || list.children.length > 100) {
|
|
75
|
+
throw new Error("Use one ordered list with 1–100 entries.");
|
|
76
|
+
}
|
|
77
|
+
if (list.children.some((item) => !hasLink(item) || textOf(item).trim().length === 0)) {
|
|
78
|
+
throw new Error("Each timeline entry needs text and a source link.");
|
|
79
|
+
}
|
|
80
|
+
list.data = { ...list.data, hProperties: { className: ["visual-timeline-entries"] } };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function renderVisual(node: ContainerDirective) {
|
|
84
|
+
const [first] = node.children;
|
|
85
|
+
if (first?.type !== "paragraph" || textOf(first).trim().length === 0 || textOf(first).toLowerCase().startsWith("source:")) {
|
|
86
|
+
throw new Error("Start with a Markdown label paragraph.");
|
|
87
|
+
}
|
|
88
|
+
const label = textOf(first).trim();
|
|
89
|
+
if (node.name === "metric") {
|
|
90
|
+
metric(node);
|
|
91
|
+
}
|
|
92
|
+
if (node.name === "progress") {
|
|
93
|
+
progress(node, label);
|
|
94
|
+
}
|
|
95
|
+
if (node.name === "timeline") {
|
|
96
|
+
timeline(node);
|
|
97
|
+
}
|
|
98
|
+
first.data = { ...first.data, hProperties: { className: ["visual-label"] } };
|
|
99
|
+
node.data = { ...node.data, hName: "section", hProperties: { ariaLabel: label, className: ["visual-document", `visual-${node.name}`] } };
|
|
100
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Root, RootContent } from "mdast";
|
|
2
|
+
import type { ContainerDirective, LeafDirective, TextDirective } from "mdast-util-directive";
|
|
3
|
+
import { SKIP, visit as visitNodes } from "unist-util-visit";
|
|
4
|
+
import { renderVisual } from "./visual.ts";
|
|
5
|
+
|
|
6
|
+
type Directive = ContainerDirective | LeafDirective | TextDirective;
|
|
7
|
+
const names = new Set(["metric", "progress", "timeline"]);
|
|
8
|
+
|
|
9
|
+
function isDirective(node: { readonly type: string }): node is Directive {
|
|
10
|
+
return node.type === "containerDirective" || node.type === "leafDirective" || node.type === "textDirective";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function nested(node: RootContent): boolean {
|
|
14
|
+
return isDirective(node) || ("children" in node && node.children.some((child) => nested(child)));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function failureOf(node: Directive, source: string): string | undefined {
|
|
18
|
+
try {
|
|
19
|
+
if (!names.has(node.name)) {
|
|
20
|
+
throw new Error(`Unknown visual directive ${node.name}.`);
|
|
21
|
+
}
|
|
22
|
+
if (node.type !== "containerDirective") {
|
|
23
|
+
throw new Error("Use a ::: container directive.");
|
|
24
|
+
}
|
|
25
|
+
const length = new TextEncoder().encode(source.slice(node.position?.start.offset, node.position?.end.offset)).byteLength;
|
|
26
|
+
if (length > 65_536) {
|
|
27
|
+
throw new Error("Visual directive exceeds 64 KiB.");
|
|
28
|
+
}
|
|
29
|
+
if (node.children.some((child) => nested(child))) {
|
|
30
|
+
throw new Error("Nested visual directives are not supported.");
|
|
31
|
+
}
|
|
32
|
+
renderVisual(node);
|
|
33
|
+
return undefined;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return error instanceof Error ? error.message : String(error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function visualDocuments() {
|
|
40
|
+
return function transform(tree: Root, file: { readonly value: unknown }) {
|
|
41
|
+
const source = String(file.value);
|
|
42
|
+
visitNodes(tree, ["containerDirective", "leafDirective", "textDirective"], (directive, index, parent) => {
|
|
43
|
+
if (!isDirective(directive) || index === undefined || parent === undefined) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const failure = failureOf(directive, source);
|
|
47
|
+
if (failure === undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const original = source.slice(directive.position?.start.offset, directive.position?.end.offset);
|
|
51
|
+
if (directive.type === "textDirective") {
|
|
52
|
+
parent.children.splice(index, 1, { type: "text", value: original });
|
|
53
|
+
return [SKIP, index + 1] as const;
|
|
54
|
+
}
|
|
55
|
+
parent.children.splice(
|
|
56
|
+
index,
|
|
57
|
+
1,
|
|
58
|
+
{
|
|
59
|
+
children: [{ type: "text", value: `Visual component could not be rendered: ${failure}` }],
|
|
60
|
+
data: { hProperties: { className: ["visual-diagnostic"] } },
|
|
61
|
+
type: "paragraph",
|
|
62
|
+
},
|
|
63
|
+
{ type: "code", value: original },
|
|
64
|
+
);
|
|
65
|
+
return [SKIP, index + 2] as const;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Schema, SchemaTransformation } from "effect";
|
|
2
|
+
|
|
3
|
+
const NumberValue = Schema.String.check(Schema.isPattern(/\S/u)).pipe(Schema.decodeTo(Schema.Finite, SchemaTransformation.numberFromString));
|
|
4
|
+
const Count = NumberValue.check(Schema.makeFilter((value) => (Number.isSafeInteger(value) && value >= 0) || "Expected a nonnegative safe integer"));
|
|
5
|
+
const UnknownValue = Schema.Literal("unknown");
|
|
6
|
+
|
|
7
|
+
export const MetricFields = Schema.Struct({
|
|
8
|
+
unit: Schema.String.check(Schema.isPattern(/\S/u)),
|
|
9
|
+
value: Schema.optional(Schema.Union([NumberValue, UnknownValue])),
|
|
10
|
+
});
|
|
11
|
+
export const ProgressFields = Schema.Struct({
|
|
12
|
+
completed: Schema.optional(Schema.Union([Count, UnknownValue])),
|
|
13
|
+
total: Schema.optional(Schema.Union([Count, UnknownValue])),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type MetricFields = typeof MetricFields.Type;
|
|
17
|
+
export type ProgressFields = typeof ProgressFields.Type;
|
package/src/search/entries.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { MarkdownAsync } from "react-markdown";
|
|
2
2
|
import rehypeRaw from "rehype-raw";
|
|
3
|
+
import remarkDirective from "remark-directive";
|
|
3
4
|
import remarkGfm from "remark-gfm";
|
|
4
5
|
import { fileUrl } from "#files/url.ts";
|
|
5
6
|
import { boardOf } from "#render/board.ts";
|
|
7
|
+
import { callouts } from "#render/callouts.ts";
|
|
6
8
|
import { documentHeadings } from "#render/documentHeadings.ts";
|
|
9
|
+
import { visualDocuments } from "#render/visualDocuments.ts";
|
|
7
10
|
import { searchCollector } from "./collector.ts";
|
|
8
11
|
import { linkCollector } from "./linkCollector.ts";
|
|
9
12
|
|
|
@@ -46,7 +49,7 @@ export async function entriesOf(source: string, file: string, home: boolean, bod
|
|
|
46
49
|
await MarkdownAsync({
|
|
47
50
|
children: board?.definitions ? `${fragment}\n\n${board.definitions}` : fragment,
|
|
48
51
|
rehypePlugins: [rehypeRaw, headings.plugin, plugin, linkCollector(links)],
|
|
49
|
-
remarkPlugins: [remarkGfm],
|
|
52
|
+
remarkPlugins: [remarkGfm, remarkDirective, visualDocuments, callouts],
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
55
|
}
|