@rse/ase 2.0.2 → 2.0.4
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/dst/ase-agent-definition-core.js +111 -87
- package/dst/ase-agent-fleet.js +1 -1
- package/dst/ase-agent-runtime-claim.js +5 -3
- package/dst/ase-agent-runtime-engine.js +1 -1
- package/dst/ase-agent-runtime-phase.js +1 -1
- package/dst/ase-agent-runtime-request.js +1 -1
- package/dst/ase-agent-runtime.js +3 -5
- package/dst/ase-artifact.js +3 -3
- package/dst/ase-config-core.js +2 -16
- package/dst/ase-config-schema.js +1 -3
- package/dst/ase-config.js +2 -0
- package/dst/ase-hook.js +13 -13
- package/dst/ase-lib-daemon-cli.js +117 -4
- package/dst/ase-lib-server-cli.js +4 -5
- package/dst/ase-lib-version.js +14 -5
- package/dst/ase-man-catalog.js +229 -0
- package/dst/ase-man-render.js +278 -0
- package/dst/ase-man-tui.js +636 -0
- package/dst/ase-man.js +33 -27
- package/dst/ase-mcp.js +4 -4
- package/dst/ase-service-getopt.js +2 -2
- package/dst/ase-service-otel.js +4 -4
- package/dst/ase-service-skills.js +5 -15
- package/dst/ase-service.js +87 -210
- package/dst/ase-setup-common.js +1 -2
- package/dst/ase-setup-settings.js +6 -9
- package/dst/ase-setup.js +4 -5
- package/dst/ase-spec.js +103 -20
- package/dst/ase-statusline.js +170 -170
- package/dst/ase-task-board-filter.js +5 -4
- package/dst/ase-task-board-flow.js +7 -6
- package/dst/ase-task-board-graph.js +5 -4
- package/dst/ase-task-board-tui-control.js +41 -35
- package/dst/ase-task-board-tui-model.js +185 -167
- package/dst/ase-task-board-tui-popup.js +2 -3
- package/dst/ase-task-board-tui-view.js +74 -70
- package/dst/ase-task-board-web-client.js +27 -27
- package/dst/ase-task-board-web-server.js +2 -2
- package/dst/ase-task-board.js +1 -1
- package/dst/ase-task-format.js +1 -1
- package/dst/ase-task-store-client.js +17 -5
- package/dst/ase-task-store-core.js +5 -7
- package/dst/ase-task-store-issues.js +16 -1
- package/dst/ase-task-store-plugin-ase.js +7 -3
- package/dst/ase-task-store-plugin-gitea.js +9 -32
- package/dst/ase-task-store-plugin-github.js +3 -12
- package/dst/ase-task-store-plugin-gitlab.js +2 -18
- package/dst/ase-task.js +51 -23
- package/dst/ase-util-compat.js +1 -1
- package/dst/ase-util-diagram.js +13 -37
- package/dst/ase.js +5 -5
- package/dst/man/ase-agent-adhoc.md +1165 -0
- package/dst/man/ase-agent-condenser.md +383 -0
- package/dst/man/ase-agent-editor.md +441 -0
- package/dst/man/ase-agent-estimator.md +477 -0
- package/dst/man/ase-agent-fleet.md +358 -0
- package/dst/man/ase-agent-implementer.md +521 -0
- package/dst/man/ase-agent-integrator.md +449 -0
- package/dst/man/ase-agent-qualifier.md +426 -0
- package/dst/man/ase-agent-rebooter.md +384 -0
- package/dst/man/ase-agent-relay.md +262 -0
- package/dst/man/ase-agent-researcher.md +478 -0
- package/dst/man/ase-agent.md +628 -0
- package/dst/man/ase-artifact-list.md +57 -0
- package/dst/man/ase-artifact-name.md +56 -0
- package/dst/man/ase-artifact.md +92 -0
- package/dst/man/ase-config-delete.md +74 -0
- package/dst/man/ase-config-edit.md +83 -0
- package/dst/man/ase-config-get.md +70 -0
- package/dst/man/ase-config-init.md +110 -0
- package/dst/man/ase-config-list.md +68 -0
- package/dst/man/ase-config-set.md +92 -0
- package/dst/man/ase-config.md +426 -0
- package/dst/man/ase-hook-permission-request.md +61 -0
- package/dst/man/ase-hook-pre-tool-use.md +96 -0
- package/dst/man/ase-hook-session-end.md +71 -0
- package/dst/man/ase-hook-session-persona.md +65 -0
- package/dst/man/ase-hook-session-start.md +184 -0
- package/dst/man/ase-hook-stop.md +52 -0
- package/dst/man/ase-hook-user-prompt-submit.md +54 -0
- package/dst/man/ase-hook.md +93 -0
- package/dst/man/ase-man.md +195 -0
- package/dst/man/ase-mcp.md +102 -0
- package/dst/man/ase-service-log.md +59 -0
- package/dst/man/ase-service-send.md +60 -0
- package/dst/man/ase-service-start.md +93 -0
- package/dst/man/ase-service-status.md +58 -0
- package/dst/man/ase-service-stop.md +52 -0
- package/dst/man/ase-service.md +124 -0
- package/dst/man/ase-setup-disable.md +67 -0
- package/dst/man/ase-setup-enable.md +66 -0
- package/dst/man/ase-setup-install.md +116 -0
- package/dst/man/ase-setup-mcp.md +213 -0
- package/dst/man/ase-setup-status.md +101 -0
- package/dst/man/ase-setup-statusline.md +162 -0
- package/dst/man/ase-setup-telemetry.md +166 -0
- package/dst/man/ase-setup-uninstall.md +85 -0
- package/dst/man/ase-setup-update.md +117 -0
- package/dst/man/ase-setup.md +156 -0
- package/dst/man/ase-spec-export.md +99 -0
- package/dst/man/ase-spec-init.md +57 -0
- package/dst/man/ase-spec-lint.md +74 -0
- package/dst/man/ase-spec-preview.md +57 -0
- package/dst/man/ase-spec.md +85 -0
- package/dst/man/ase-statusline.md +330 -0
- package/dst/man/ase-task-assign.md +138 -0
- package/dst/man/ase-task-board.md +651 -0
- package/dst/man/ase-task-delete.md +46 -0
- package/dst/man/ase-task-edit.md +73 -0
- package/dst/man/ase-task-idscheme.md +92 -0
- package/dst/man/ase-task-lifecycle.md +92 -0
- package/dst/man/ase-task-list.md +86 -0
- package/dst/man/ase-task-load.md +48 -0
- package/dst/man/ase-task-newid.md +100 -0
- package/dst/man/ase-task-purge.md +49 -0
- package/dst/man/ase-task-rename.md +69 -0
- package/dst/man/ase-task-request.md +108 -0
- package/dst/man/ase-task-save.md +78 -0
- package/dst/man/ase-task-status.md +79 -0
- package/dst/man/ase-task-store.md +377 -0
- package/dst/man/ase-task-view.md +50 -0
- package/dst/man/ase-task.md +335 -0
- package/dst/man/ase-util-compat.md +38 -0
- package/dst/man/ase-util-diagram.md +140 -0
- package/dst/man/ase-util-meta.md +68 -0
- package/dst/man/ase-util-metric.md +74 -0
- package/dst/man/ase-util-mint.md +85 -0
- package/dst/man/ase-util-worktree.md +109 -0
- package/dst/man/ase-util.md +81 -0
- package/dst/man/ase.md +228 -0
- package/package.json +12 -17
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/agents/ase-code-lint.md +3 -3
- package/plugin/agents/ase-docs-proofread.md +3 -3
- package/plugin/agents/ase-docs-refine.md +3 -3
- package/plugin/agents/ase-spec-lint.md +338 -0
- package/plugin/meta/ase-skill.md +7 -0
- package/plugin/mods/ase-mods.ts +10 -7
- package/plugin/mods/claude-code.d.ts +26 -3
- package/plugin/skills/ase-code-analyze/SKILL.md +18 -10
- package/plugin/skills/ase-code-analyze/help.md +11 -6
- package/plugin/skills/ase-code-lint/SKILL.md +37 -23
- package/plugin/skills/ase-code-lint/help.md +10 -5
- package/plugin/skills/ase-help-intent/data.md +204 -13
- package/plugin/skills/ase-help-skill/catalog.md +1 -0
- package/plugin/skills/ase-meta-workflow/workflow.txt +2 -0
- package/plugin/skills/ase-repo-commit/SKILL.md +1 -0
- package/plugin/skills/ase-repo-merge/SKILL.md +1 -0
- package/plugin/skills/ase-repo-review/SKILL.md +5 -3
- package/plugin/skills/ase-repo-review/help.md +3 -1
- package/plugin/skills/ase-spec-edit/SKILL.md +3 -3
- package/plugin/skills/ase-spec-edit/help.md +1 -1
- package/plugin/skills/ase-spec-lint/SKILL.md +695 -0
- package/plugin/skills/ase-spec-lint/help.md +171 -0
- package/plugin/skills/ase-task-implement/SKILL.md +24 -6
- package/plugin/skills/ase-task-implement/help.md +11 -1
- package/dst/man/ase-agent-adhoc.1 +0 -515
- package/dst/man/ase-agent-condenser.1 +0 -193
- package/dst/man/ase-agent-editor.1 +0 -212
- package/dst/man/ase-agent-estimator.1 +0 -214
- package/dst/man/ase-agent-fleet.1 +0 -185
- package/dst/man/ase-agent-implementer.1 +0 -233
- package/dst/man/ase-agent-integrator.1 +0 -213
- package/dst/man/ase-agent-qualifier.1 +0 -214
- package/dst/man/ase-agent-rebooter.1 +0 -193
- package/dst/man/ase-agent-relay.1 +0 -128
- package/dst/man/ase-agent-researcher.1 +0 -217
- package/dst/man/ase-agent.1 +0 -264
- package/dst/man/ase-artifact-list.1 +0 -38
- package/dst/man/ase-artifact-name.1 +0 -39
- package/dst/man/ase-artifact.1 +0 -56
- package/dst/man/ase-config-delete.1 +0 -45
- package/dst/man/ase-config-edit.1 +0 -48
- package/dst/man/ase-config-get.1 +0 -46
- package/dst/man/ase-config-init.1 +0 -60
- package/dst/man/ase-config-list.1 +0 -42
- package/dst/man/ase-config-set.1 +0 -52
- package/dst/man/ase-config.1 +0 -231
- package/dst/man/ase-hook-permission-request.1 +0 -36
- package/dst/man/ase-hook-pre-tool-use.1 +0 -59
- package/dst/man/ase-hook-session-end.1 +0 -47
- package/dst/man/ase-hook-session-persona.1 +0 -37
- package/dst/man/ase-hook-session-start.1 +0 -81
- package/dst/man/ase-hook-stop.1 +0 -34
- package/dst/man/ase-hook-user-prompt-submit.1 +0 -34
- package/dst/man/ase-hook.1 +0 -51
- package/dst/man/ase-man.1 +0 -67
- package/dst/man/ase-mcp.1 +0 -61
- package/dst/man/ase-service-log.1 +0 -43
- package/dst/man/ase-service-send.1 +0 -47
- package/dst/man/ase-service-start.1 +0 -54
- package/dst/man/ase-service-status.1 +0 -46
- package/dst/man/ase-service-stop.1 +0 -37
- package/dst/man/ase-service.1 +0 -85
- package/dst/man/ase-setup-disable.1 +0 -44
- package/dst/man/ase-setup-enable.1 +0 -45
- package/dst/man/ase-setup-install.1 +0 -73
- package/dst/man/ase-setup-mcp.1 +0 -124
- package/dst/man/ase-setup-status.1 +0 -60
- package/dst/man/ase-setup-statusline.1 +0 -92
- package/dst/man/ase-setup-telemetry.1 +0 -92
- package/dst/man/ase-setup-uninstall.1 +0 -59
- package/dst/man/ase-setup-update.1 +0 -64
- package/dst/man/ase-setup.1 +0 -93
- package/dst/man/ase-spec-export.1 +0 -62
- package/dst/man/ase-spec-lint.1 +0 -40
- package/dst/man/ase-spec-preview.1 +0 -40
- package/dst/man/ase-spec.1 +0 -46
- package/dst/man/ase-statusline.1 +0 -189
- package/dst/man/ase-task-assign.1 +0 -71
- package/dst/man/ase-task-board.1 +0 -384
- package/dst/man/ase-task-delete.1 +0 -35
- package/dst/man/ase-task-edit.1 +0 -46
- package/dst/man/ase-task-idscheme.1 +0 -65
- package/dst/man/ase-task-lifecycle.1 +0 -57
- package/dst/man/ase-task-list.1 +0 -47
- package/dst/man/ase-task-load.1 +0 -36
- package/dst/man/ase-task-newid.1 +0 -61
- package/dst/man/ase-task-purge.1 +0 -37
- package/dst/man/ase-task-rename.1 +0 -44
- package/dst/man/ase-task-request.1 +0 -61
- package/dst/man/ase-task-save.1 +0 -48
- package/dst/man/ase-task-status.1 +0 -55
- package/dst/man/ase-task-store.1 +0 -194
- package/dst/man/ase-task-view.1 +0 -40
- package/dst/man/ase-task.1 +0 -160
- package/dst/man/ase-util-compat.1 +0 -25
- package/dst/man/ase-util-diagram.1 +0 -76
- package/dst/man/ase-util-meta.1 +0 -45
- package/dst/man/ase-util-metric.1 +0 -54
- package/dst/man/ase-util-mint.1 +0 -59
- package/dst/man/ase-util-worktree.1 +0 -72
- package/dst/man/ase-util.1 +0 -50
- package/dst/man/ase.1 +0 -124
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Agentic Software Engineering (ASE)
|
|
3
|
+
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import { marked } from "marked";
|
|
7
|
+
/* the syntax highlighter of the code blocks (via Shiki, with its JavaScript
|
|
8
|
+
regular expression engine, i.e. without WebAssembly, and with an empty
|
|
9
|
+
theme, as only the TextMate scopes of the tokens are used), loaded on
|
|
10
|
+
demand only once */
|
|
11
|
+
let highlighter = null;
|
|
12
|
+
let loading = null;
|
|
13
|
+
export const codeLoad = () => {
|
|
14
|
+
if (loading === null) {
|
|
15
|
+
loading = (async () => {
|
|
16
|
+
const [{ createHighlighterCore }, { createJavaScriptRegexEngine }, { bundledLanguages }] = await Promise.all([
|
|
17
|
+
import("shiki/core"), import("shiki/engine/javascript"), import("shiki/langs")
|
|
18
|
+
]);
|
|
19
|
+
highlighter = await createHighlighterCore({
|
|
20
|
+
themes: [{ name: "ase", type: "dark", settings: [] }],
|
|
21
|
+
langs: [bundledLanguages.shellscript, bundledLanguages.yaml, bundledLanguages.json, bundledLanguages.typescript],
|
|
22
|
+
engine: createJavaScriptRegexEngine()
|
|
23
|
+
});
|
|
24
|
+
})();
|
|
25
|
+
}
|
|
26
|
+
return loading;
|
|
27
|
+
};
|
|
28
|
+
/* the highlighted languages of the code blocks, by their fence info */
|
|
29
|
+
const codeLangs = {
|
|
30
|
+
sh: "shellscript", bash: "shellscript", shell: "shellscript", shellscript: "shellscript",
|
|
31
|
+
yaml: "yaml", yml: "yaml", json: "json", ts: "typescript", typescript: "typescript"
|
|
32
|
+
};
|
|
33
|
+
/* the tones of the code tokens, by the prefixes of their TextMate scopes (first match wins) */
|
|
34
|
+
const codeScopes = [
|
|
35
|
+
["cmt", ["comment"]],
|
|
36
|
+
["str", ["string.quoted", "string.template", "string.unquoted.plain", "string.unquoted.block"]],
|
|
37
|
+
["opt", ["constant.other.option"]],
|
|
38
|
+
["var", ["variable.other.normal", "variable.other.special", "variable.other.positional"]],
|
|
39
|
+
["num", ["constant.numeric", "constant.language"]],
|
|
40
|
+
["key", ["entity.name.tag", "support.type.property-name"]],
|
|
41
|
+
["cmd", ["entity.name.command", "entity.name.function", "support.function"]],
|
|
42
|
+
["kw", ["keyword", "storage"]],
|
|
43
|
+
["punct", ["punctuation"]]
|
|
44
|
+
];
|
|
45
|
+
/* append a span to a line, merging it into the last span of equal attributes */
|
|
46
|
+
const append = (line, span) => {
|
|
47
|
+
const last = line[line.length - 1];
|
|
48
|
+
if (last !== undefined && last.tone === span.tone && last.ref === span.ref && last.match === span.match)
|
|
49
|
+
last.text += span.text;
|
|
50
|
+
else
|
|
51
|
+
line.push({ ...span });
|
|
52
|
+
};
|
|
53
|
+
/* split the text of a code block into lines of syntax-highlighting spans
|
|
54
|
+
(all as plain text for an unknown language or while the highlighter is
|
|
55
|
+
not loaded yet, see "codeLoad") */
|
|
56
|
+
const codeLines = (text, lang) => {
|
|
57
|
+
const name = codeLangs[(lang ?? "").toLowerCase()];
|
|
58
|
+
if (highlighter === null || name === undefined)
|
|
59
|
+
return text.split(/\r?\n/).map((line) => [{ text: line, tone: "text" }]);
|
|
60
|
+
const { tokens } = highlighter.codeToTokens(text, { lang: name, theme: "ase", includeExplanation: "scopeName" });
|
|
61
|
+
return tokens.map((line) => {
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const part of line.flatMap((token) => token.explanation ?? [{ content: token.content, scopes: [] }])) {
|
|
64
|
+
const scopes = part.scopes.map((scope) => scope.scopeName);
|
|
65
|
+
const tone = codeScopes.find(([, prefixes]) => scopes.some((scope) => prefixes.some((prefix) => scope === prefix || scope.startsWith(prefix + "."))))?.[0] ?? "text";
|
|
66
|
+
append(out, { text: part.content, tone });
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
/* register a reference onto a page, if it is referable */
|
|
72
|
+
const refOf = (ctx, id) => {
|
|
73
|
+
if (!ctx.exists(id))
|
|
74
|
+
return undefined;
|
|
75
|
+
ctx.refs.push({ id, line: -1 });
|
|
76
|
+
return ctx.refs.length - 1;
|
|
77
|
+
};
|
|
78
|
+
/* render inline tokens into spans of a base tone, where a code span naming
|
|
79
|
+
a page (a skill by its name, or a command by its name directly followed by
|
|
80
|
+
"(1)") and a link onto the "help.md" of a skill reference this page, and
|
|
81
|
+
a hard line break is a span of a single newline */
|
|
82
|
+
const inline = (tokens, tone, ctx) => {
|
|
83
|
+
const out = [];
|
|
84
|
+
tokens.forEach((token, i) => {
|
|
85
|
+
const next = tokens[i + 1];
|
|
86
|
+
if (token.type === "strong")
|
|
87
|
+
out.push(...inline(token.tokens, "bold", ctx));
|
|
88
|
+
else if (token.type === "em")
|
|
89
|
+
out.push(...inline(token.tokens, "italic", ctx));
|
|
90
|
+
else if (token.type === "del")
|
|
91
|
+
out.push(...inline(token.tokens, tone, ctx));
|
|
92
|
+
else if (token.type === "codespan") {
|
|
93
|
+
const text = token.text;
|
|
94
|
+
const man = next !== undefined && next.type === "text" && next.text.startsWith("(1)");
|
|
95
|
+
out.push({ text: text.replace(/\n/g, " "), tone: "code", ref: refOf(ctx, man ? `${text}.1` : text) });
|
|
96
|
+
}
|
|
97
|
+
else if (token.type === "link") {
|
|
98
|
+
const link = token;
|
|
99
|
+
const spans = inline(link.tokens, "link", ctx);
|
|
100
|
+
const m = /^(?:\.\.\/)?(ase-[a-z0-9-]+)\/help\.md$/.exec(link.href);
|
|
101
|
+
const ref = m !== null && !spans.some((span) => span.ref !== undefined) ? refOf(ctx, m[1]) : undefined;
|
|
102
|
+
out.push(...spans.map((span) => ({ ...span, ref: span.ref ?? ref })));
|
|
103
|
+
}
|
|
104
|
+
else if (token.type === "text") {
|
|
105
|
+
const text = token;
|
|
106
|
+
if (text.tokens !== undefined && text.tokens.length > 0)
|
|
107
|
+
out.push(...inline(text.tokens, tone, ctx));
|
|
108
|
+
else
|
|
109
|
+
out.push({ text: text.text.replace(/\n/g, " "), tone });
|
|
110
|
+
}
|
|
111
|
+
else if (token.type === "br")
|
|
112
|
+
out.push({ text: "\n", tone });
|
|
113
|
+
else if (token.type === "escape" || token.type === "html" || token.type === "image")
|
|
114
|
+
out.push({ text: token.text.replace(/\n/g, " "), tone });
|
|
115
|
+
else
|
|
116
|
+
out.push({ text: token.raw.replace(/\n/g, " "), tone });
|
|
117
|
+
});
|
|
118
|
+
return out;
|
|
119
|
+
};
|
|
120
|
+
/* word-wrap spans onto lines of a width: the spans are split into words
|
|
121
|
+
(glued across adjacent spans, like a code span directly followed by its
|
|
122
|
+
punctuation), which are laid out with single spaces in between, where a
|
|
123
|
+
span of a single newline breaks the line and an overlong word is broken hard */
|
|
124
|
+
const wrap = (spans, width) => {
|
|
125
|
+
const atoms = [];
|
|
126
|
+
let atom = null;
|
|
127
|
+
let space = false;
|
|
128
|
+
for (const span of spans) {
|
|
129
|
+
if (span.text === "\n") {
|
|
130
|
+
atoms.push(null);
|
|
131
|
+
atom = null;
|
|
132
|
+
space = false;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
for (const piece of span.text.split(/(\s+)/)) {
|
|
136
|
+
if (piece === "")
|
|
137
|
+
continue;
|
|
138
|
+
if (/^\s+$/.test(piece)) {
|
|
139
|
+
atom = null;
|
|
140
|
+
space = true;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (atom === null) {
|
|
144
|
+
atom = { parts: [], len: 0, space };
|
|
145
|
+
space = false;
|
|
146
|
+
atoms.push(atom);
|
|
147
|
+
}
|
|
148
|
+
atom.parts.push({ ...span, text: piece });
|
|
149
|
+
atom.len += piece.length;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const lines = [];
|
|
153
|
+
let line = [];
|
|
154
|
+
let col = 0;
|
|
155
|
+
const newline = () => {
|
|
156
|
+
lines.push(line);
|
|
157
|
+
line = [];
|
|
158
|
+
col = 0;
|
|
159
|
+
};
|
|
160
|
+
for (const a of atoms) {
|
|
161
|
+
if (a === null) {
|
|
162
|
+
newline();
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (col > 0 && col + (a.space ? 1 : 0) + a.len > width)
|
|
166
|
+
newline();
|
|
167
|
+
if (col > 0 && a.space) {
|
|
168
|
+
const prev = line[line.length - 1];
|
|
169
|
+
const same = prev.tone === a.parts[0].tone && prev.ref === a.parts[0].ref;
|
|
170
|
+
append(line, same ? { ...prev, text: " " } : { text: " ", tone: "text" });
|
|
171
|
+
col++;
|
|
172
|
+
}
|
|
173
|
+
for (const part of a.parts) {
|
|
174
|
+
let text = part.text;
|
|
175
|
+
while (col + text.length > width) {
|
|
176
|
+
const n = width - col;
|
|
177
|
+
if (n > 0)
|
|
178
|
+
append(line, { ...part, text: text.slice(0, n) });
|
|
179
|
+
text = text.slice(n);
|
|
180
|
+
newline();
|
|
181
|
+
}
|
|
182
|
+
if (text !== "") {
|
|
183
|
+
append(line, { ...part, text });
|
|
184
|
+
col += text.length;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (line.length > 0 || lines.length === 0)
|
|
189
|
+
lines.push(line);
|
|
190
|
+
return lines;
|
|
191
|
+
};
|
|
192
|
+
/* render block tokens into lines of a width, separated by empty lines
|
|
193
|
+
(unless tight, as within the items of a tight list), where list items,
|
|
194
|
+
code blocks, and block quotes are all indented by four columns */
|
|
195
|
+
const blocks = (tokens, width, ctx, tight = false) => {
|
|
196
|
+
const out = [];
|
|
197
|
+
const add = (lines) => {
|
|
198
|
+
if (lines.length === 0)
|
|
199
|
+
return;
|
|
200
|
+
if (out.length > 0 && !tight)
|
|
201
|
+
out.push([]);
|
|
202
|
+
out.push(...lines);
|
|
203
|
+
};
|
|
204
|
+
for (const token of tokens) {
|
|
205
|
+
if (token.type === "space" || token.type === "def")
|
|
206
|
+
continue;
|
|
207
|
+
else if (token.type === "heading") {
|
|
208
|
+
const head = token;
|
|
209
|
+
add(wrap(inline(head.tokens, head.depth === 1 ? "title" : head.depth === 2 ? "head" : "sub", ctx), width));
|
|
210
|
+
}
|
|
211
|
+
else if (token.type === "paragraph" || token.type === "text") {
|
|
212
|
+
const para = token;
|
|
213
|
+
add(wrap(para.tokens !== undefined ? inline(para.tokens, "text", ctx) : [{ text: para.text, tone: "text" }], width));
|
|
214
|
+
}
|
|
215
|
+
else if (token.type === "list") {
|
|
216
|
+
const list = token;
|
|
217
|
+
const lines = [];
|
|
218
|
+
list.items.forEach((item, k) => {
|
|
219
|
+
const bullet = list.ordered ? `${(list.start === "" ? 1 : list.start) + k}. `.padEnd(4) : "● ";
|
|
220
|
+
const body = blocks(item.tokens, Math.max(10, width - bullet.length), ctx, !item.loose);
|
|
221
|
+
if (list.loose && k > 0)
|
|
222
|
+
lines.push([]);
|
|
223
|
+
if (body.length === 0)
|
|
224
|
+
body.push([]);
|
|
225
|
+
body.forEach((line, n) => {
|
|
226
|
+
lines.push([{ text: n === 0 ? bullet : " ".repeat(bullet.length), tone: "bullet" }, ...line]);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
add(lines);
|
|
230
|
+
}
|
|
231
|
+
else if (token.type === "code") {
|
|
232
|
+
const code = token;
|
|
233
|
+
add(codeLines(code.text, code.lang).map((line) => [{ text: " ", tone: "text" }, ...line]));
|
|
234
|
+
}
|
|
235
|
+
else if (token.type === "blockquote")
|
|
236
|
+
add(blocks(token.tokens, Math.max(10, width - 4), ctx)
|
|
237
|
+
.map((line) => [{ text: " │ ", tone: "dim" }, ...line]));
|
|
238
|
+
else if (token.type === "hr")
|
|
239
|
+
add([[{ text: "─".repeat(width), tone: "dim" }]]);
|
|
240
|
+
else
|
|
241
|
+
add(token.raw.replace(/\n+$/, "").split("\n").map((line) => [{ text: line, tone: "dim" }]));
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
};
|
|
245
|
+
/* render the Markdown source of a page (with the id "self") into lines of
|
|
246
|
+
a width, where references onto the page itself are omitted */
|
|
247
|
+
export const renderPage = (md, width, self, exists) => {
|
|
248
|
+
const ctx = { refs: [], exists: (id) => id !== self && exists(id) };
|
|
249
|
+
const lines = blocks(marked.lexer(md.replace(/\t/g, " ")), Math.max(10, width), ctx);
|
|
250
|
+
lines.forEach((line, n) => {
|
|
251
|
+
for (const span of line)
|
|
252
|
+
if (span.ref !== undefined && ctx.refs[span.ref].line < 0)
|
|
253
|
+
ctx.refs[span.ref].line = n;
|
|
254
|
+
});
|
|
255
|
+
return { lines, refs: ctx.refs };
|
|
256
|
+
};
|
|
257
|
+
/* mark the matches of a (global) regular expression within a line */
|
|
258
|
+
export const markMatches = (line, re) => {
|
|
259
|
+
const text = line.map((span) => span.text).join("");
|
|
260
|
+
const marks = new Array(text.length).fill(false);
|
|
261
|
+
let found = false;
|
|
262
|
+
for (const m of text.matchAll(re)) {
|
|
263
|
+
if (m[0].length === 0)
|
|
264
|
+
continue;
|
|
265
|
+
marks.fill(true, m.index, m.index + m[0].length);
|
|
266
|
+
found = true;
|
|
267
|
+
}
|
|
268
|
+
if (!found)
|
|
269
|
+
return line;
|
|
270
|
+
const out = [];
|
|
271
|
+
let pos = 0;
|
|
272
|
+
for (const span of line) {
|
|
273
|
+
for (let i = 0; i < span.text.length; i++)
|
|
274
|
+
append(out, { ...span, text: span.text[i], match: marks[pos + i] });
|
|
275
|
+
pos += span.text.length;
|
|
276
|
+
}
|
|
277
|
+
return out;
|
|
278
|
+
};
|