@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.
Files changed (236) hide show
  1. package/dst/ase-agent-definition-core.js +111 -87
  2. package/dst/ase-agent-fleet.js +1 -1
  3. package/dst/ase-agent-runtime-claim.js +5 -3
  4. package/dst/ase-agent-runtime-engine.js +1 -1
  5. package/dst/ase-agent-runtime-phase.js +1 -1
  6. package/dst/ase-agent-runtime-request.js +1 -1
  7. package/dst/ase-agent-runtime.js +3 -5
  8. package/dst/ase-artifact.js +3 -3
  9. package/dst/ase-config-core.js +2 -16
  10. package/dst/ase-config-schema.js +1 -3
  11. package/dst/ase-config.js +2 -0
  12. package/dst/ase-hook.js +13 -13
  13. package/dst/ase-lib-daemon-cli.js +117 -4
  14. package/dst/ase-lib-server-cli.js +4 -5
  15. package/dst/ase-lib-version.js +14 -5
  16. package/dst/ase-man-catalog.js +229 -0
  17. package/dst/ase-man-render.js +278 -0
  18. package/dst/ase-man-tui.js +636 -0
  19. package/dst/ase-man.js +33 -27
  20. package/dst/ase-mcp.js +4 -4
  21. package/dst/ase-service-getopt.js +2 -2
  22. package/dst/ase-service-otel.js +4 -4
  23. package/dst/ase-service-skills.js +5 -15
  24. package/dst/ase-service.js +87 -210
  25. package/dst/ase-setup-common.js +1 -2
  26. package/dst/ase-setup-settings.js +6 -9
  27. package/dst/ase-setup.js +4 -5
  28. package/dst/ase-spec.js +103 -20
  29. package/dst/ase-statusline.js +170 -170
  30. package/dst/ase-task-board-filter.js +5 -4
  31. package/dst/ase-task-board-flow.js +7 -6
  32. package/dst/ase-task-board-graph.js +5 -4
  33. package/dst/ase-task-board-tui-control.js +41 -35
  34. package/dst/ase-task-board-tui-model.js +185 -167
  35. package/dst/ase-task-board-tui-popup.js +2 -3
  36. package/dst/ase-task-board-tui-view.js +74 -70
  37. package/dst/ase-task-board-web-client.js +27 -27
  38. package/dst/ase-task-board-web-server.js +2 -2
  39. package/dst/ase-task-board.js +1 -1
  40. package/dst/ase-task-format.js +1 -1
  41. package/dst/ase-task-store-client.js +17 -5
  42. package/dst/ase-task-store-core.js +5 -7
  43. package/dst/ase-task-store-issues.js +16 -1
  44. package/dst/ase-task-store-plugin-ase.js +7 -3
  45. package/dst/ase-task-store-plugin-gitea.js +9 -32
  46. package/dst/ase-task-store-plugin-github.js +3 -12
  47. package/dst/ase-task-store-plugin-gitlab.js +2 -18
  48. package/dst/ase-task.js +51 -23
  49. package/dst/ase-util-compat.js +1 -1
  50. package/dst/ase-util-diagram.js +13 -37
  51. package/dst/ase.js +5 -5
  52. package/dst/man/ase-agent-adhoc.md +1165 -0
  53. package/dst/man/ase-agent-condenser.md +383 -0
  54. package/dst/man/ase-agent-editor.md +441 -0
  55. package/dst/man/ase-agent-estimator.md +477 -0
  56. package/dst/man/ase-agent-fleet.md +358 -0
  57. package/dst/man/ase-agent-implementer.md +521 -0
  58. package/dst/man/ase-agent-integrator.md +449 -0
  59. package/dst/man/ase-agent-qualifier.md +426 -0
  60. package/dst/man/ase-agent-rebooter.md +384 -0
  61. package/dst/man/ase-agent-relay.md +262 -0
  62. package/dst/man/ase-agent-researcher.md +478 -0
  63. package/dst/man/ase-agent.md +628 -0
  64. package/dst/man/ase-artifact-list.md +57 -0
  65. package/dst/man/ase-artifact-name.md +56 -0
  66. package/dst/man/ase-artifact.md +92 -0
  67. package/dst/man/ase-config-delete.md +74 -0
  68. package/dst/man/ase-config-edit.md +83 -0
  69. package/dst/man/ase-config-get.md +70 -0
  70. package/dst/man/ase-config-init.md +110 -0
  71. package/dst/man/ase-config-list.md +68 -0
  72. package/dst/man/ase-config-set.md +92 -0
  73. package/dst/man/ase-config.md +426 -0
  74. package/dst/man/ase-hook-permission-request.md +61 -0
  75. package/dst/man/ase-hook-pre-tool-use.md +96 -0
  76. package/dst/man/ase-hook-session-end.md +71 -0
  77. package/dst/man/ase-hook-session-persona.md +65 -0
  78. package/dst/man/ase-hook-session-start.md +184 -0
  79. package/dst/man/ase-hook-stop.md +52 -0
  80. package/dst/man/ase-hook-user-prompt-submit.md +54 -0
  81. package/dst/man/ase-hook.md +93 -0
  82. package/dst/man/ase-man.md +195 -0
  83. package/dst/man/ase-mcp.md +102 -0
  84. package/dst/man/ase-service-log.md +59 -0
  85. package/dst/man/ase-service-send.md +60 -0
  86. package/dst/man/ase-service-start.md +93 -0
  87. package/dst/man/ase-service-status.md +58 -0
  88. package/dst/man/ase-service-stop.md +52 -0
  89. package/dst/man/ase-service.md +124 -0
  90. package/dst/man/ase-setup-disable.md +67 -0
  91. package/dst/man/ase-setup-enable.md +66 -0
  92. package/dst/man/ase-setup-install.md +116 -0
  93. package/dst/man/ase-setup-mcp.md +213 -0
  94. package/dst/man/ase-setup-status.md +101 -0
  95. package/dst/man/ase-setup-statusline.md +162 -0
  96. package/dst/man/ase-setup-telemetry.md +166 -0
  97. package/dst/man/ase-setup-uninstall.md +85 -0
  98. package/dst/man/ase-setup-update.md +117 -0
  99. package/dst/man/ase-setup.md +156 -0
  100. package/dst/man/ase-spec-export.md +99 -0
  101. package/dst/man/ase-spec-init.md +57 -0
  102. package/dst/man/ase-spec-lint.md +74 -0
  103. package/dst/man/ase-spec-preview.md +57 -0
  104. package/dst/man/ase-spec.md +85 -0
  105. package/dst/man/ase-statusline.md +330 -0
  106. package/dst/man/ase-task-assign.md +138 -0
  107. package/dst/man/ase-task-board.md +651 -0
  108. package/dst/man/ase-task-delete.md +46 -0
  109. package/dst/man/ase-task-edit.md +73 -0
  110. package/dst/man/ase-task-idscheme.md +92 -0
  111. package/dst/man/ase-task-lifecycle.md +92 -0
  112. package/dst/man/ase-task-list.md +86 -0
  113. package/dst/man/ase-task-load.md +48 -0
  114. package/dst/man/ase-task-newid.md +100 -0
  115. package/dst/man/ase-task-purge.md +49 -0
  116. package/dst/man/ase-task-rename.md +69 -0
  117. package/dst/man/ase-task-request.md +108 -0
  118. package/dst/man/ase-task-save.md +78 -0
  119. package/dst/man/ase-task-status.md +79 -0
  120. package/dst/man/ase-task-store.md +377 -0
  121. package/dst/man/ase-task-view.md +50 -0
  122. package/dst/man/ase-task.md +335 -0
  123. package/dst/man/ase-util-compat.md +38 -0
  124. package/dst/man/ase-util-diagram.md +140 -0
  125. package/dst/man/ase-util-meta.md +68 -0
  126. package/dst/man/ase-util-metric.md +74 -0
  127. package/dst/man/ase-util-mint.md +85 -0
  128. package/dst/man/ase-util-worktree.md +109 -0
  129. package/dst/man/ase-util.md +81 -0
  130. package/dst/man/ase.md +228 -0
  131. package/package.json +12 -17
  132. package/plugin/.claude-plugin/plugin.json +1 -1
  133. package/plugin/.codex-plugin/plugin.json +1 -1
  134. package/plugin/.github/plugin/plugin.json +1 -1
  135. package/plugin/agents/ase-code-lint.md +3 -3
  136. package/plugin/agents/ase-docs-proofread.md +3 -3
  137. package/plugin/agents/ase-docs-refine.md +3 -3
  138. package/plugin/agents/ase-spec-lint.md +338 -0
  139. package/plugin/meta/ase-skill.md +7 -0
  140. package/plugin/mods/ase-mods.ts +10 -7
  141. package/plugin/mods/claude-code.d.ts +26 -3
  142. package/plugin/skills/ase-code-analyze/SKILL.md +18 -10
  143. package/plugin/skills/ase-code-analyze/help.md +11 -6
  144. package/plugin/skills/ase-code-lint/SKILL.md +37 -23
  145. package/plugin/skills/ase-code-lint/help.md +10 -5
  146. package/plugin/skills/ase-help-intent/data.md +204 -13
  147. package/plugin/skills/ase-help-skill/catalog.md +1 -0
  148. package/plugin/skills/ase-meta-workflow/workflow.txt +2 -0
  149. package/plugin/skills/ase-repo-commit/SKILL.md +1 -0
  150. package/plugin/skills/ase-repo-merge/SKILL.md +1 -0
  151. package/plugin/skills/ase-repo-review/SKILL.md +5 -3
  152. package/plugin/skills/ase-repo-review/help.md +3 -1
  153. package/plugin/skills/ase-spec-edit/SKILL.md +3 -3
  154. package/plugin/skills/ase-spec-edit/help.md +1 -1
  155. package/plugin/skills/ase-spec-lint/SKILL.md +695 -0
  156. package/plugin/skills/ase-spec-lint/help.md +171 -0
  157. package/plugin/skills/ase-task-implement/SKILL.md +24 -6
  158. package/plugin/skills/ase-task-implement/help.md +11 -1
  159. package/dst/man/ase-agent-adhoc.1 +0 -515
  160. package/dst/man/ase-agent-condenser.1 +0 -193
  161. package/dst/man/ase-agent-editor.1 +0 -212
  162. package/dst/man/ase-agent-estimator.1 +0 -214
  163. package/dst/man/ase-agent-fleet.1 +0 -185
  164. package/dst/man/ase-agent-implementer.1 +0 -233
  165. package/dst/man/ase-agent-integrator.1 +0 -213
  166. package/dst/man/ase-agent-qualifier.1 +0 -214
  167. package/dst/man/ase-agent-rebooter.1 +0 -193
  168. package/dst/man/ase-agent-relay.1 +0 -128
  169. package/dst/man/ase-agent-researcher.1 +0 -217
  170. package/dst/man/ase-agent.1 +0 -264
  171. package/dst/man/ase-artifact-list.1 +0 -38
  172. package/dst/man/ase-artifact-name.1 +0 -39
  173. package/dst/man/ase-artifact.1 +0 -56
  174. package/dst/man/ase-config-delete.1 +0 -45
  175. package/dst/man/ase-config-edit.1 +0 -48
  176. package/dst/man/ase-config-get.1 +0 -46
  177. package/dst/man/ase-config-init.1 +0 -60
  178. package/dst/man/ase-config-list.1 +0 -42
  179. package/dst/man/ase-config-set.1 +0 -52
  180. package/dst/man/ase-config.1 +0 -231
  181. package/dst/man/ase-hook-permission-request.1 +0 -36
  182. package/dst/man/ase-hook-pre-tool-use.1 +0 -59
  183. package/dst/man/ase-hook-session-end.1 +0 -47
  184. package/dst/man/ase-hook-session-persona.1 +0 -37
  185. package/dst/man/ase-hook-session-start.1 +0 -81
  186. package/dst/man/ase-hook-stop.1 +0 -34
  187. package/dst/man/ase-hook-user-prompt-submit.1 +0 -34
  188. package/dst/man/ase-hook.1 +0 -51
  189. package/dst/man/ase-man.1 +0 -67
  190. package/dst/man/ase-mcp.1 +0 -61
  191. package/dst/man/ase-service-log.1 +0 -43
  192. package/dst/man/ase-service-send.1 +0 -47
  193. package/dst/man/ase-service-start.1 +0 -54
  194. package/dst/man/ase-service-status.1 +0 -46
  195. package/dst/man/ase-service-stop.1 +0 -37
  196. package/dst/man/ase-service.1 +0 -85
  197. package/dst/man/ase-setup-disable.1 +0 -44
  198. package/dst/man/ase-setup-enable.1 +0 -45
  199. package/dst/man/ase-setup-install.1 +0 -73
  200. package/dst/man/ase-setup-mcp.1 +0 -124
  201. package/dst/man/ase-setup-status.1 +0 -60
  202. package/dst/man/ase-setup-statusline.1 +0 -92
  203. package/dst/man/ase-setup-telemetry.1 +0 -92
  204. package/dst/man/ase-setup-uninstall.1 +0 -59
  205. package/dst/man/ase-setup-update.1 +0 -64
  206. package/dst/man/ase-setup.1 +0 -93
  207. package/dst/man/ase-spec-export.1 +0 -62
  208. package/dst/man/ase-spec-lint.1 +0 -40
  209. package/dst/man/ase-spec-preview.1 +0 -40
  210. package/dst/man/ase-spec.1 +0 -46
  211. package/dst/man/ase-statusline.1 +0 -189
  212. package/dst/man/ase-task-assign.1 +0 -71
  213. package/dst/man/ase-task-board.1 +0 -384
  214. package/dst/man/ase-task-delete.1 +0 -35
  215. package/dst/man/ase-task-edit.1 +0 -46
  216. package/dst/man/ase-task-idscheme.1 +0 -65
  217. package/dst/man/ase-task-lifecycle.1 +0 -57
  218. package/dst/man/ase-task-list.1 +0 -47
  219. package/dst/man/ase-task-load.1 +0 -36
  220. package/dst/man/ase-task-newid.1 +0 -61
  221. package/dst/man/ase-task-purge.1 +0 -37
  222. package/dst/man/ase-task-rename.1 +0 -44
  223. package/dst/man/ase-task-request.1 +0 -61
  224. package/dst/man/ase-task-save.1 +0 -48
  225. package/dst/man/ase-task-status.1 +0 -55
  226. package/dst/man/ase-task-store.1 +0 -194
  227. package/dst/man/ase-task-view.1 +0 -40
  228. package/dst/man/ase-task.1 +0 -160
  229. package/dst/man/ase-util-compat.1 +0 -25
  230. package/dst/man/ase-util-diagram.1 +0 -76
  231. package/dst/man/ase-util-meta.1 +0 -45
  232. package/dst/man/ase-util-metric.1 +0 -54
  233. package/dst/man/ase-util-mint.1 +0 -59
  234. package/dst/man/ase-util-worktree.1 +0 -72
  235. package/dst/man/ase-util.1 +0 -50
  236. 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
+ };