@shivaedev/work-board 0.0.0 → 0.2.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 +51 -0
- package/LICENSE +21 -0
- package/README.md +157 -2
- package/dist/board.d.ts +8 -0
- package/dist/board.d.ts.map +1 -0
- package/dist/board.js +27 -0
- package/dist/board.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -0
- package/dist/files/changes.d.ts +17 -0
- package/dist/files/changes.d.ts.map +1 -0
- package/dist/files/changes.js +38 -0
- package/dist/files/changes.js.map +1 -0
- package/dist/files/home.d.ts +11 -0
- package/dist/files/home.d.ts.map +1 -0
- package/dist/files/home.js +33 -0
- package/dist/files/home.js.map +1 -0
- package/dist/files/list.d.ts +9 -0
- package/dist/files/list.d.ts.map +1 -0
- package/dist/files/list.js +38 -0
- package/dist/files/list.js.map +1 -0
- package/dist/http/assets.d.ts +15 -0
- package/dist/http/assets.d.ts.map +1 -0
- package/dist/http/assets.js +32 -0
- package/dist/http/assets.js.map +1 -0
- package/dist/http/events.d.ts +5 -0
- package/dist/http/events.d.ts.map +1 -0
- package/dist/http/events.js +17 -0
- package/dist/http/events.js.map +1 -0
- package/dist/http/loopback.d.ts +4 -0
- package/dist/http/loopback.d.ts.map +1 -0
- package/dist/http/loopback.js +22 -0
- package/dist/http/loopback.js.map +1 -0
- package/dist/http/page.d.ts +9 -0
- package/dist/http/page.d.ts.map +1 -0
- package/dist/http/page.js +43 -0
- package/dist/http/page.js.map +1 -0
- package/dist/http/respond.d.ts +8 -0
- package/dist/http/respond.d.ts.map +1 -0
- package/dist/http/respond.js +15 -0
- package/dist/http/respond.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/page/board.d.ts +4 -0
- package/dist/page/board.d.ts.map +1 -0
- package/dist/page/board.js +32 -0
- package/dist/page/board.js.map +1 -0
- package/dist/page/client.d.ts +2 -0
- package/dist/page/client.d.ts.map +1 -0
- package/dist/page/client.js +91 -0
- package/dist/page/client.js.map +1 -0
- package/dist/page/diagrams.d.ts +2 -0
- package/dist/page/diagrams.d.ts.map +1 -0
- package/dist/page/diagrams.js +73 -0
- package/dist/page/diagrams.js.map +1 -0
- package/dist/page/document.d.ts +4 -0
- package/dist/page/document.d.ts.map +1 -0
- package/dist/page/document.js +9 -0
- package/dist/page/document.js.map +1 -0
- package/dist/page/escape.d.ts +2 -0
- package/dist/page/escape.d.ts.map +1 -0
- package/dist/page/escape.js +3 -0
- package/dist/page/escape.js.map +1 -0
- package/dist/page/nav.d.ts +3 -0
- package/dist/page/nav.d.ts.map +1 -0
- package/dist/page/nav.js +22 -0
- package/dist/page/nav.js.map +1 -0
- package/dist/page/shell.d.ts +2 -0
- package/dist/page/shell.d.ts.map +1 -0
- package/dist/page/shell.js +11 -0
- package/dist/page/shell.js.map +1 -0
- package/dist/page/style.d.ts +2 -0
- package/dist/page/style.d.ts.map +1 -0
- package/dist/page/style.js +75 -0
- package/dist/page/style.js.map +1 -0
- package/dist/page/swap.d.ts +2 -0
- package/dist/page/swap.d.ts.map +1 -0
- package/dist/page/swap.js +123 -0
- package/dist/page/swap.js.map +1 -0
- package/dist/page/updated.d.ts +2 -0
- package/dist/page/updated.d.ts.map +1 -0
- package/dist/page/updated.js +2 -0
- package/dist/page/updated.js.map +1 -0
- package/dist/render/board.d.ts +20 -0
- package/dist/render/board.d.ts.map +1 -0
- package/dist/render/board.js +54 -0
- package/dist/render/board.js.map +1 -0
- package/dist/render/code.d.ts +10 -0
- package/dist/render/code.d.ts.map +1 -0
- package/dist/render/code.js +47 -0
- package/dist/render/code.js.map +1 -0
- package/dist/render/failed.d.ts +9 -0
- package/dist/render/failed.d.ts.map +1 -0
- package/dist/render/failed.js +4 -0
- package/dist/render/failed.js.map +1 -0
- package/dist/render/footnotes.d.ts +19 -0
- package/dist/render/footnotes.d.ts.map +1 -0
- package/dist/render/footnotes.js +23 -0
- package/dist/render/footnotes.js.map +1 -0
- package/dist/render/highlighter.d.ts +13 -0
- package/dist/render/highlighter.d.ts.map +1 -0
- package/dist/render/highlighter.js +11 -0
- package/dist/render/highlighter.js.map +1 -0
- package/dist/render/markdown.d.ts +5 -0
- package/dist/render/markdown.d.ts.map +1 -0
- package/dist/render/markdown.js +15 -0
- package/dist/render/markdown.js.map +1 -0
- package/dist/serve.d.ts +9 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +9 -0
- package/dist/serve.js.map +1 -0
- package/package.json +62 -3
- package/src/board.ts +44 -0
- package/src/cli.ts +24 -0
- package/src/files/changes.ts +71 -0
- package/src/files/home.ts +37 -0
- package/src/files/list.ts +55 -0
- package/src/http/assets.ts +38 -0
- package/src/http/events.ts +29 -0
- package/src/http/loopback.ts +29 -0
- package/src/http/page.ts +54 -0
- package/src/http/respond.ts +18 -0
- package/src/index.ts +1 -0
- package/src/page/board.ts +37 -0
- package/src/page/client.ts +90 -0
- package/src/page/diagrams.ts +72 -0
- package/src/page/document.ts +10 -0
- package/src/page/escape.ts +3 -0
- package/src/page/nav.ts +28 -0
- package/src/page/shell.ts +12 -0
- package/src/page/style.ts +79 -0
- package/src/page/swap.ts +122 -0
- package/src/page/updated.ts +1 -0
- package/src/render/board.ts +82 -0
- package/src/render/code.ts +54 -0
- package/src/render/failed.ts +3 -0
- package/src/render/footnotes.ts +32 -0
- package/src/render/highlighter.ts +17 -0
- package/src/render/markdown.ts +16 -0
- package/src/serve.ts +16 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export const swap = `
|
|
2
|
+
const containers = new Set(["DIV", "SECTION", "ARTICLE", "HEADER", "FOOTER", "MAIN", "NAV"]);
|
|
3
|
+
|
|
4
|
+
const structural = (element) => [...element.childNodes].every((node) => node.nodeType === 1 || node.textContent.trim() === "");
|
|
5
|
+
|
|
6
|
+
const pristine = new WeakMap();
|
|
7
|
+
|
|
8
|
+
export const remember = (element) => {
|
|
9
|
+
pristine.set(element, element.outerHTML);
|
|
10
|
+
if (containers.has(element.tagName)) for (const child of element.children) remember(child);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const normalized = (element) => {
|
|
14
|
+
const copy = element.cloneNode(true);
|
|
15
|
+
for (const node of [copy, ...copy.querySelectorAll("*")]) {
|
|
16
|
+
if (node.tagName === "DETAILS") node.removeAttribute("open");
|
|
17
|
+
if (node.matches("figure.diagram")) {
|
|
18
|
+
node.querySelector(".diagram-svg")?.remove();
|
|
19
|
+
node.dataset.state = "pending";
|
|
20
|
+
delete node.dataset.error;
|
|
21
|
+
}
|
|
22
|
+
if (node.matches("[data-modified]")) node.textContent = "";
|
|
23
|
+
}
|
|
24
|
+
return copy.outerHTML;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const signature = (element) => {
|
|
28
|
+
if (!pristine.has(element)) pristine.set(element, normalized(element));
|
|
29
|
+
return pristine.get(element);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const sameShell = (old, next) =>
|
|
33
|
+
old.tagName === next.tagName && containers.has(old.tagName) && old.cloneNode(false).outerHTML === next.cloneNode(false).outerHTML;
|
|
34
|
+
|
|
35
|
+
const matches = (a, b) => {
|
|
36
|
+
let start = 0;
|
|
37
|
+
while (start < a.length && start < b.length && a[start] === b[start]) start++;
|
|
38
|
+
let tail = 0;
|
|
39
|
+
while (tail < a.length - start && tail < b.length - start && a[a.length - 1 - tail] === b[b.length - 1 - tail]) tail++;
|
|
40
|
+
const n = a.length - start - tail;
|
|
41
|
+
const m = b.length - start - tail;
|
|
42
|
+
const table = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
43
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
44
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
45
|
+
table[i][j] = a[start + i] === b[start + j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const pairs = Array.from({ length: start }, (_, index) => [index, index]);
|
|
49
|
+
for (let i = 0, j = 0; i < n && j < m; ) {
|
|
50
|
+
if (a[start + i] === b[start + j]) pairs.push([start + i++, start + j++]);
|
|
51
|
+
else if (table[i + 1][j] >= table[i][j + 1]) i++;
|
|
52
|
+
else j++;
|
|
53
|
+
}
|
|
54
|
+
for (let index = tail; index > 0; index--) pairs.push([a.length - index, b.length - index]);
|
|
55
|
+
return [...pairs, [a.length, b.length]];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const patch = (current, incoming) => {
|
|
59
|
+
if (!structural(current) || !structural(incoming)) {
|
|
60
|
+
current.replaceChildren(...incoming.childNodes);
|
|
61
|
+
for (const child of current.children) remember(child);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const olds = [...current.children];
|
|
65
|
+
const news = [...incoming.children];
|
|
66
|
+
const signatures = news.map((next) => next.outerHTML);
|
|
67
|
+
const arrive = (index) => {
|
|
68
|
+
remember(news[index]);
|
|
69
|
+
return news[index];
|
|
70
|
+
};
|
|
71
|
+
let i = 0;
|
|
72
|
+
let j = 0;
|
|
73
|
+
for (const [kept, arrived] of matches(olds.map(signature), signatures)) {
|
|
74
|
+
const anchor = olds[kept] ?? null;
|
|
75
|
+
for (; i < kept && j < arrived; i++, j++) {
|
|
76
|
+
if (sameShell(olds[i], news[j])) {
|
|
77
|
+
patch(olds[i], news[j]);
|
|
78
|
+
pristine.set(olds[i], signatures[j]);
|
|
79
|
+
} else olds[i].replaceWith(arrive(j));
|
|
80
|
+
}
|
|
81
|
+
for (; i < kept; i++) olds[i].remove();
|
|
82
|
+
for (; j < arrived; j++) current.insertBefore(arrive(j), anchor);
|
|
83
|
+
i++;
|
|
84
|
+
j++;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const keysOf = (root) => {
|
|
89
|
+
const seen = new Map();
|
|
90
|
+
return [...root.querySelectorAll("details")].map((details) => {
|
|
91
|
+
const base =
|
|
92
|
+
details.dataset.key ??
|
|
93
|
+
(details.closest(".item")?.querySelector("h3")?.textContent ?? "") + "|" + (details.querySelector("summary")?.textContent ?? "");
|
|
94
|
+
const count = seen.get(base) ?? 0;
|
|
95
|
+
seen.set(base, count + 1);
|
|
96
|
+
return [base + "#" + count, details];
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const carryDrawings = (drawings, figures) => {
|
|
101
|
+
if (drawings.length !== figures.length) return;
|
|
102
|
+
figures.forEach((figure, index) => {
|
|
103
|
+
const drawing = drawings[index];
|
|
104
|
+
if (figure.dataset.state !== "pending" || drawing === null || drawing.isConnected) return;
|
|
105
|
+
figure.prepend(drawing);
|
|
106
|
+
figure.dataset.state = "redrawing";
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const swap = (current, incoming) => {
|
|
111
|
+
const before = keysOf(current);
|
|
112
|
+
const open = new Map(before.map(([key, details]) => [key, details.open]));
|
|
113
|
+
const existing = new Set(before.map(([, details]) => details));
|
|
114
|
+
const drawings = [...current.querySelectorAll("figure.diagram")].map((figure) => figure.querySelector(".diagram-svg"));
|
|
115
|
+
patch(current, incoming);
|
|
116
|
+
carryDrawings(drawings, [...current.querySelectorAll("figure.diagram")]);
|
|
117
|
+
for (const [key, details] of keysOf(current)) {
|
|
118
|
+
const was = open.get(key);
|
|
119
|
+
if (was !== undefined && !existing.has(details)) details.open = was;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
`;
|
|
123
|
+
//# sourceMappingURL=swap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swap.js","sourceRoot":"","sources":["../../src/page/swap.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyHnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updated.d.ts","sourceRoot":"","sources":["../../src/page/updated.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,GAAI,UAAU,MAAM,KAAG,MAAwE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updated.js","sourceRoot":"","sources":["../../src/page/updated.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,oCAAoC,QAAQ,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface Section {
|
|
2
|
+
readonly title: string;
|
|
3
|
+
readonly heading: string;
|
|
4
|
+
readonly notes: string;
|
|
5
|
+
readonly items: ReadonlyArray<string>;
|
|
6
|
+
}
|
|
7
|
+
export interface Board {
|
|
8
|
+
readonly title: string;
|
|
9
|
+
readonly intro: string;
|
|
10
|
+
readonly sections: ReadonlyArray<Section>;
|
|
11
|
+
readonly footer: string;
|
|
12
|
+
readonly definitions: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const boardOf: (source: string) => Board;
|
|
15
|
+
export interface Count {
|
|
16
|
+
readonly title: string;
|
|
17
|
+
readonly count: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const countsOf: (board: Board) => ReadonlyArray<Count>;
|
|
20
|
+
//# sourceMappingURL=board.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/render/board.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC7B;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,aAAa,CAAC,KAAK,CACsE,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { markdownToMdast } from "satteri";
|
|
2
|
+
const textOf = (node) => {
|
|
3
|
+
if ("value" in node) {
|
|
4
|
+
return node.value;
|
|
5
|
+
}
|
|
6
|
+
return "children" in node ? node.children.map((child) => textOf(child)).join("") : "";
|
|
7
|
+
};
|
|
8
|
+
const isSection = (node) => node.type === "heading" && node.depth === 2;
|
|
9
|
+
const place = (draft, node, source, closing) => {
|
|
10
|
+
const section = draft.sections.at(-1);
|
|
11
|
+
if (node.type === "definition" || node.type === "footnoteDefinition") {
|
|
12
|
+
draft.definitions.push(source);
|
|
13
|
+
}
|
|
14
|
+
else if (draft.footer !== undefined) {
|
|
15
|
+
draft.footer.push(source);
|
|
16
|
+
}
|
|
17
|
+
else if (node.type === "heading" && node.depth === 1 && draft.title === "") {
|
|
18
|
+
draft.title = source;
|
|
19
|
+
}
|
|
20
|
+
else if (isSection(node)) {
|
|
21
|
+
draft.sections.push({ title: textOf(node), heading: source, notes: [], items: [] });
|
|
22
|
+
}
|
|
23
|
+
else if (section === undefined) {
|
|
24
|
+
draft.intro.push(source);
|
|
25
|
+
}
|
|
26
|
+
else if (node.type === "heading" && node.depth === 3) {
|
|
27
|
+
section.items.push([source]);
|
|
28
|
+
}
|
|
29
|
+
else if (node.type === "thematicBreak" && closing) {
|
|
30
|
+
draft.footer = [];
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
(section.items.at(-1) ?? section.notes).push(source);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const joined = (blocks) => blocks.join("\n\n");
|
|
37
|
+
export const boardOf = (source) => {
|
|
38
|
+
const draft = { title: "", intro: [], sections: [], footer: undefined, definitions: [] };
|
|
39
|
+
const root = markdownToMdast(source);
|
|
40
|
+
const nodes = root.type === "root" ? root.children : [];
|
|
41
|
+
const lastHeading = nodes.findLastIndex((node) => node.type === "heading");
|
|
42
|
+
nodes.forEach((node, index) => {
|
|
43
|
+
place(draft, node, source.slice(node.position?.start.offset, node.position?.end.offset), index > lastHeading);
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
title: draft.title,
|
|
47
|
+
intro: joined(draft.intro),
|
|
48
|
+
sections: draft.sections.map((section) => ({ ...section, notes: joined(section.notes), items: section.items.map(joined) })),
|
|
49
|
+
footer: joined(draft.footer ?? []),
|
|
50
|
+
definitions: joined(draft.definitions),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export const countsOf = (board) => board.sections.flatMap((section) => (section.items.length === 0 ? [] : [{ title: section.title, count: section.items.length }]));
|
|
54
|
+
//# sourceMappingURL=board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board.js","sourceRoot":"","sources":["../../src/render/board.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Element } from "hast";
|
|
2
|
+
import { type Highlighter as Shiki } from "shiki";
|
|
3
|
+
export declare const codeBlocks: (highlighter: Shiki) => {
|
|
4
|
+
name: string;
|
|
5
|
+
element: {
|
|
6
|
+
filter: string[];
|
|
7
|
+
visit: (pre: Readonly<Element>) => Promise<import("hast").RootContent | undefined>;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../src/render/code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,MAAM,CAAC;AAEpD,OAAO,EAA0C,KAAK,WAAW,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC;AAiC1F,eAAO,MAAM,UAAU,GAAI,aAAa,KAAK;;;;;;CAkB1C,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineHastPlugin } from "satteri";
|
|
2
|
+
import { bundledLanguages } from "shiki";
|
|
3
|
+
import { THEMES } from "./highlighter.js";
|
|
4
|
+
const languageOf = (code) => {
|
|
5
|
+
const classes = code.properties.className;
|
|
6
|
+
const named = Array.isArray(classes) ? classes.find((name) => String(name).startsWith("language-")) : undefined;
|
|
7
|
+
return named === undefined ? undefined : String(named).slice("language-".length);
|
|
8
|
+
};
|
|
9
|
+
const textOf = (node) => {
|
|
10
|
+
if (node.type === "text") {
|
|
11
|
+
return node.value;
|
|
12
|
+
}
|
|
13
|
+
return node.type === "element" ? node.children.map(textOf).join("") : "";
|
|
14
|
+
};
|
|
15
|
+
const diagram = (source) => ({
|
|
16
|
+
type: "element",
|
|
17
|
+
tagName: "figure",
|
|
18
|
+
properties: { className: ["diagram"], dataState: "pending" },
|
|
19
|
+
children: [{ type: "element", tagName: "pre", properties: { className: ["diagram-source"] }, children: [{ type: "text", value: source }] }],
|
|
20
|
+
});
|
|
21
|
+
const isBundled = (language) => Object.hasOwn(bundledLanguages, language);
|
|
22
|
+
const highlighted = async (highlighter, source, language) => {
|
|
23
|
+
if (!isBundled(language)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
await highlighter.loadLanguage(language);
|
|
27
|
+
return highlighter.codeToHast(source, { lang: language, themes: THEMES, defaultColor: false }).children[0];
|
|
28
|
+
};
|
|
29
|
+
export const codeBlocks = (highlighter) => defineHastPlugin({
|
|
30
|
+
name: "work-board-code",
|
|
31
|
+
element: {
|
|
32
|
+
filter: ["pre"],
|
|
33
|
+
visit: async (pre) => {
|
|
34
|
+
const [code] = pre.children;
|
|
35
|
+
if (code?.type !== "element" || code.tagName !== "code") {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const language = languageOf(code);
|
|
39
|
+
const source = textOf(code);
|
|
40
|
+
if (language === "mermaid") {
|
|
41
|
+
return diagram(source);
|
|
42
|
+
}
|
|
43
|
+
return language === undefined ? undefined : await highlighted(highlighter, source, language);
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/render/code.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAwB,gBAAgB,EAA6B,MAAM,OAAO,CAAC;AAC1F,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,UAAU,GAAG,CAAC,IAAa,EAAsB,EAAE;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChH,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AAClF,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,IAAoB,EAAU,EAAE;IAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1E,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,MAAc,EAAW,EAAE,CAAC,CAAC;IAC7C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,QAAQ;IACjB,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE;IAC5D,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;CAC3I,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,CAAC,QAAgB,EAA+B,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;AAE/G,MAAM,WAAW,GAAG,KAAK,EAAE,WAAkB,EAAE,MAAc,EAAE,QAAgB,EAAE,EAAE;IAClF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC5G,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,WAAkB,EAAE,EAAE,CAChD,gBAAgB,CAAC;IAChB,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,KAAK,CAAC;QACf,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC;YAC5B,IAAI,IAAI,EAAE,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;gBACzD,OAAO,SAAS,CAAC;YAClB,CAAC;YACD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC5B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;YACxB,CAAC;YACD,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9F,CAAC;KACD;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const RenderFailed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
2
|
+
readonly _tag: "RenderFailed";
|
|
3
|
+
} & Readonly<A>;
|
|
4
|
+
export declare class RenderFailed extends RenderFailed_base<{
|
|
5
|
+
readonly cause: unknown;
|
|
6
|
+
}> {
|
|
7
|
+
}
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=failed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed.d.ts","sourceRoot":"","sources":["../../src/render/failed.ts"],"names":[],"mappings":";;;AAEA,qBAAa,YAAa,SAAQ,kBAAiC;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;CAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed.js","sourceRoot":"","sources":["../../src/render/failed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,OAAO,YAAa,SAAQ,IAAI,CAAC,WAAW,CAAC,cAAc,CAA8B;CAAG"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Element } from "hast";
|
|
2
|
+
export declare const footnoteIds: (prefix: string) => {
|
|
3
|
+
name: string;
|
|
4
|
+
element: {
|
|
5
|
+
filter: string[];
|
|
6
|
+
visit: (element: Readonly<Element>) => {
|
|
7
|
+
position?: import("satteri").Position | undefined;
|
|
8
|
+
type: "element";
|
|
9
|
+
tagName: string;
|
|
10
|
+
children: import("hast").ElementContent[];
|
|
11
|
+
content?: import("hast").Root | undefined;
|
|
12
|
+
data?: import("hast").ElementData | undefined;
|
|
13
|
+
properties: {
|
|
14
|
+
[k: string]: string | number | boolean | (string | number)[] | null | undefined;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=footnotes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footnotes.d.ts","sourceRoot":"","sources":["../../src/render/footnotes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAmBpC,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM;;;;;;;;;;;;;;;;CAYvC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineHastPlugin } from "satteri";
|
|
2
|
+
const FOOTNOTE_IDS = /^(?:user-content-fn|footnote-label)/;
|
|
3
|
+
const SCOPED = new Set(["id", "href", "ariaDescribedBy"]);
|
|
4
|
+
const scoped = (prefix, value) => {
|
|
5
|
+
if (typeof value === "string" && FOOTNOTE_IDS.test(value)) {
|
|
6
|
+
return `${prefix}${value}`;
|
|
7
|
+
}
|
|
8
|
+
if (typeof value === "string" && value.startsWith("#") && FOOTNOTE_IDS.test(value.slice(1))) {
|
|
9
|
+
return `#${prefix}${value.slice(1)}`;
|
|
10
|
+
}
|
|
11
|
+
return Array.isArray(value) ? value.map((each) => (typeof each === "string" ? String(scoped(prefix, each)) : each)) : value;
|
|
12
|
+
};
|
|
13
|
+
export const footnoteIds = (prefix) => defineHastPlugin({
|
|
14
|
+
name: "work-board-footnote-ids",
|
|
15
|
+
element: {
|
|
16
|
+
filter: ["a", "h2", "li"],
|
|
17
|
+
visit: (element) => ({
|
|
18
|
+
...element,
|
|
19
|
+
properties: Object.fromEntries(Object.entries(element.properties).map(([name, value]) => [name, SCOPED.has(name) ? scoped(prefix, value) : value])),
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=footnotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footnotes.js","sourceRoot":"","sources":["../../src/render/footnotes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,YAAY,GAAG,qCAAqC,CAAC;AAI3D,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAE1D,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,KAAY,EAAS,EAAE;IACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7F,OAAO,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC7H,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,EAAE,CAC7C,gBAAgB,CAAC;IAChB,IAAI,EAAE,yBAAyB;IAC/B,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;QACzB,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACpB,GAAG,OAAO;YACV,UAAU,EAAE,MAAM,CAAC,WAAW,CAC7B,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnH;SACD,CAAC;KACF;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Context, Layer } from "effect";
|
|
2
|
+
import { type Highlighter as Shiki } from "shiki";
|
|
3
|
+
import { RenderFailed } from "./failed.ts";
|
|
4
|
+
export declare const THEMES: {
|
|
5
|
+
readonly light: "github-light";
|
|
6
|
+
readonly dark: "github-dark";
|
|
7
|
+
};
|
|
8
|
+
declare const Highlighter_base: Context.ServiceClass<Highlighter, "WorkBoard/Highlighter", Shiki>;
|
|
9
|
+
export declare class Highlighter extends Highlighter_base {
|
|
10
|
+
static readonly layer: Layer.Layer<Highlighter, RenderFailed, never>;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=highlighter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlighter.d.ts","sourceRoot":"","sources":["../../src/render/highlighter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAqB,KAAK,WAAW,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,eAAO,MAAM,MAAM;;;CAA0D,CAAC;;AAE9E,qBAAa,WAAY,SAAQ,gBAA8D;IAC9F,MAAM,CAAC,QAAQ,CAAC,KAAK,gDAQnB;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { createHighlighter } from "shiki";
|
|
3
|
+
import { RenderFailed } from "./failed.js";
|
|
4
|
+
export const THEMES = { light: "github-light", dark: "github-dark" };
|
|
5
|
+
export class Highlighter extends Context.Service()("WorkBoard/Highlighter") {
|
|
6
|
+
static layer = Layer.effect(Highlighter)(Effect.acquireRelease(Effect.tryPromise({
|
|
7
|
+
try: () => createHighlighter({ themes: Object.values(THEMES), langs: [] }),
|
|
8
|
+
catch: (cause) => new RenderFailed({ cause }),
|
|
9
|
+
}), (highlighter) => Effect.sync(() => highlighter.dispose())));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=highlighter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlighter.js","sourceRoot":"","sources":["../../src/render/highlighter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAA6B,MAAM,OAAO,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,CAAC,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAW,CAAC;AAE9E,MAAM,OAAO,WAAY,SAAQ,OAAO,CAAC,OAAO,EAAsB,CAAC,uBAAuB,CAAC;IAC9F,MAAM,CAAU,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAChD,MAAM,CAAC,cAAc,CACpB,MAAM,CAAC,UAAU,CAAC;QACjB,GAAG,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC1E,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC;KAC7C,CAAC,EACF,CAAC,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CACzD,CACD,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { RenderFailed } from "./failed.ts";
|
|
3
|
+
import { Highlighter } from "./highlighter.ts";
|
|
4
|
+
export declare const renderMarkdown: (source: string, idPrefix?: any) => Effect.Effect<string, RenderFailed, Highlighter>;
|
|
5
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/render/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,eAAO,MAAM,cAAc,sFAQzB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { markdownToHtml } from "satteri";
|
|
3
|
+
import { codeBlocks } from "./code.js";
|
|
4
|
+
import { RenderFailed } from "./failed.js";
|
|
5
|
+
import { footnoteIds } from "./footnotes.js";
|
|
6
|
+
import { Highlighter } from "./highlighter.js";
|
|
7
|
+
export const renderMarkdown = Effect.fn("WorkBoard.renderMarkdown")(function* (source, idPrefix = "") {
|
|
8
|
+
const highlighter = yield* Highlighter;
|
|
9
|
+
const { html } = yield* Effect.tryPromise({
|
|
10
|
+
try: async () => markdownToHtml(source, { hastPlugins: idPrefix === "" ? [codeBlocks(highlighter)] : [codeBlocks(highlighter), footnoteIds(idPrefix)] }),
|
|
11
|
+
catch: (cause) => new RenderFailed({ cause }),
|
|
12
|
+
});
|
|
13
|
+
return html;
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +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,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAc,EAAE,QAAQ,GAAG,EAAE;IAC3G,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IACvC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACzC,GAAG,EAAE,KAAK,IAAI,EAAE,CACf,cAAc,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QACxI,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC;KAC7C,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACb,CAAC,CAAC,CAAC"}
|
package/dist/serve.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { type BoardOptions } from "./board.ts";
|
|
3
|
+
export declare const HOST = "127.0.0.1";
|
|
4
|
+
export interface ServeOptions extends BoardOptions {
|
|
5
|
+
readonly port: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const listenOn: (port: number) => Layer.Layer<import("effect/unstable/http/Etag").Generator | import("effect/unstable/http/HttpPlatform").HttpPlatform | import("effect/unstable/http/HttpServer").HttpServer | import("@effect/platform-node/NodeServices").NodeServices, import("effect/unstable/http/HttpServerError").ServeError, never>;
|
|
8
|
+
export declare const serveBoard: (options: ServeOptions) => Layer.Layer<import("effect/unstable/http/Etag").Generator | import("effect/unstable/http/HttpPlatform").HttpPlatform | import("effect/unstable/http/HttpServer").HttpServer | import("@effect/platform-node/NodeServices").NodeServices, import("./files/home.ts").HomeMissing | import("./http/assets.ts").MermaidMissing | import("effect/PlatformError").PlatformError | import("./render/failed.ts").RenderFailed | import("effect/unstable/http/HttpServerError").ServeError, never>;
|
|
9
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,IAAI,cAAc,CAAC;AAEhC,MAAM,WAAW,YAAa,SAAQ,YAAY;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,+SAA8F,CAAC;AAEpI,eAAO,MAAM,UAAU,GAAI,SAAS,YAAY,8dACgE,CAAC"}
|
package/dist/serve.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { NodeHttpServer } from "@effect/platform-node";
|
|
3
|
+
import { Layer } from "effect";
|
|
4
|
+
import { HttpRouter } from "effect/unstable/http";
|
|
5
|
+
import { boardLayer } from "./board.js";
|
|
6
|
+
export const HOST = "127.0.0.1";
|
|
7
|
+
export const listenOn = (port) => NodeHttpServer.layer(createServer, { host: HOST, port, disablePreemptiveShutdown: true });
|
|
8
|
+
export const serveBoard = (options) => HttpRouter.serve(boardLayer(options), { disableLogger: true }).pipe(Layer.provideMerge(listenOn(options.port)));
|
|
9
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAqB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,CAAC,MAAM,IAAI,GAAG,WAAW,CAAC;AAMhC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC,CAAC;AAEpI,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAqB,EAAE,EAAE,CACnD,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,73 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/work-board",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Serve a folder of markdown files as a live local page that updates in place when a file changes",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/ShivaeDev/platform.git",
|
|
9
10
|
"directory": "packages/work-board"
|
|
10
11
|
},
|
|
12
|
+
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/work-board#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ShivaeDev/platform/issues"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=24"
|
|
18
|
+
},
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"bin": {
|
|
21
|
+
"work-board": "./dist/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"source": "./src/index.ts",
|
|
33
|
+
"import": "./dist/index.js",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
11
38
|
"publishConfig": {
|
|
12
|
-
"access": "public"
|
|
39
|
+
"access": "public",
|
|
40
|
+
"provenance": true
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"mermaid": "11.16.1",
|
|
44
|
+
"satteri": "0.9.5",
|
|
45
|
+
"shiki": "4.4.2"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@effect/platform-node": "4.0.0-rc.112",
|
|
49
|
+
"@effect/platform-node-shared": "4.0.0-rc.112",
|
|
50
|
+
"effect": "4.0.0-rc.112"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@effect/platform-node": "4.0.0-rc.112",
|
|
54
|
+
"@effect/platform-node-shared": "4.0.0-rc.112",
|
|
55
|
+
"@types/hast": "3.0.5",
|
|
56
|
+
"@types/mdast": "4.0.4",
|
|
57
|
+
"@types/node": "24.10.1",
|
|
58
|
+
"@typescript/native": "npm:typescript@7.0.2",
|
|
59
|
+
"effect": "4.0.0-rc.112",
|
|
60
|
+
"happy-dom": "20.11.2",
|
|
61
|
+
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
62
|
+
"vitest": "4.1.9"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "node --eval \"import('node:fs').then(({ rmSync }) => rmSync('dist', { force: true, recursive: true }))\" && tsc6 --project tsconfig.build.json",
|
|
66
|
+
"check": "biome check .",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:package": "node scripts/test-package.mjs",
|
|
69
|
+
"typecheck": "tsc --noEmit",
|
|
70
|
+
"typecheck:compat": "tsc6 --noEmit",
|
|
71
|
+
"ready": "pnpm check && pnpm typecheck && pnpm typecheck:compat && pnpm test && pnpm build && pnpm test:package"
|
|
13
72
|
}
|
|
14
73
|
}
|
package/src/board.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Effect, type FileSystem, Layer, type Path } from "effect";
|
|
2
|
+
import { HttpRouter, type HttpServerRequest, type HttpServerResponse } from "effect/unstable/http";
|
|
3
|
+
import { watchChanges } from "./files/changes.ts";
|
|
4
|
+
import { homeIn } from "./files/home.ts";
|
|
5
|
+
import { ASSETS, MERMAID_ROUTE, mermaidFile, mermaidRoot } from "./http/assets.ts";
|
|
6
|
+
import { events } from "./http/events.ts";
|
|
7
|
+
import { loopbackOnly } from "./http/loopback.ts";
|
|
8
|
+
import { page } from "./http/page.ts";
|
|
9
|
+
import { respond } from "./http/respond.ts";
|
|
10
|
+
import { Highlighter } from "./render/highlighter.ts";
|
|
11
|
+
|
|
12
|
+
export interface BoardOptions {
|
|
13
|
+
readonly root: string;
|
|
14
|
+
readonly home?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type Services = Highlighter | FileSystem.FileSystem | Path.Path;
|
|
18
|
+
type Handler<E> = (request: HttpServerRequest.HttpServerRequest) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, Services>;
|
|
19
|
+
|
|
20
|
+
const routes = (options: BoardOptions) =>
|
|
21
|
+
HttpRouter.use((router) =>
|
|
22
|
+
Effect.gen(function* () {
|
|
23
|
+
const home = yield* homeIn(options.root, options.home);
|
|
24
|
+
const mermaid = yield* mermaidRoot();
|
|
25
|
+
const changes = yield* watchChanges(options.root);
|
|
26
|
+
const context = yield* Effect.context<Services>();
|
|
27
|
+
const serve = <E>(route: HttpRouter.PathInput, handler: Handler<E>) =>
|
|
28
|
+
router.add(
|
|
29
|
+
"GET",
|
|
30
|
+
route,
|
|
31
|
+
loopbackOnly((request) => Effect.provideContext(handler(request), context)),
|
|
32
|
+
);
|
|
33
|
+
yield* serve("/events", events(changes));
|
|
34
|
+
for (const [route, body, contentType] of ASSETS) {
|
|
35
|
+
yield* serve(route, () => Effect.succeed(respond(body, contentType)));
|
|
36
|
+
}
|
|
37
|
+
yield* serve(MERMAID_ROUTE, mermaidFile(mermaid));
|
|
38
|
+
const pages = page({ root: options.root, home }, changes);
|
|
39
|
+
yield* serve("/", pages);
|
|
40
|
+
yield* serve("/*", pages);
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const boardLayer = (options: BoardOptions) => routes(options).pipe(Layer.provide(Highlighter.layer));
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
3
|
+
import { Effect, Layer, Option, Path } from "effect";
|
|
4
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
5
|
+
import { HOST, serveBoard } from "./serve.ts";
|
|
6
|
+
|
|
7
|
+
const VERSION = "0.2.0";
|
|
8
|
+
|
|
9
|
+
const serve = Effect.fn("WorkBoard.serve")(function* (input: { readonly dir: string; readonly port: number; readonly home: Option.Option<string> }) {
|
|
10
|
+
const path = yield* Path.Path;
|
|
11
|
+
return yield* Layer.launch(serveBoard({ root: path.resolve(input.dir), home: Option.getOrUndefined(input.home), port: input.port }));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const workBoard = Command.make(
|
|
15
|
+
"work-board",
|
|
16
|
+
{
|
|
17
|
+
dir: Argument.directory("dir", { mustExist: true }).pipe(Argument.withDescription("The folder of markdown files to serve")),
|
|
18
|
+
port: Flag.integer("port").pipe(Flag.withDefault(4747), Flag.withDescription(`The port to listen on at ${HOST}`)),
|
|
19
|
+
home: Flag.string("home").pipe(Flag.optional, Flag.withDescription("The file shown as a board at /, relative to the folder")),
|
|
20
|
+
},
|
|
21
|
+
serve,
|
|
22
|
+
).pipe(Command.withDescription("Serve a folder of markdown files as a live page that updates in place when a file changes."));
|
|
23
|
+
|
|
24
|
+
Command.run(workBoard, { version: VERSION }).pipe(Effect.provide(NodeServices.layer), NodeRuntime.runMain);
|