@rse/ase 2.0.6 → 2.0.8
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-api.d.ts +2 -0
- package/dst/ase-agent-definition-core.js +24 -3
- package/dst/ase-agent-fleet.js +2 -1
- package/dst/ase-agent-relay-client.js +3 -4
- package/dst/ase-agent-runtime-engine.js +22 -4
- package/dst/ase-agent-runtime-tool.js +10 -8
- package/dst/ase-agent-runtime.js +8 -4
- package/dst/ase-config-schema.js +4 -1
- package/dst/ase-lib-fetch.js +12 -0
- package/dst/ase-lib-server-cli.js +8 -5
- package/dst/ase-man-catalog.js +1 -1
- package/dst/ase-task-board-diff.js +120 -0
- package/dst/ase-task-board-tui-control.js +8 -5
- package/dst/ase-task-board-tui-model.js +54 -14
- package/dst/ase-task-board-tui-popup.js +41 -8
- package/dst/ase-task-board-tui.js +42 -27
- package/dst/ase-task-board-web-client.css +1 -1
- package/dst/ase-task-board-web-client.html +16 -0
- package/dst/ase-task-board-web-client.js +33 -33
- package/dst/ase-task-board-web-server.js +44 -9
- package/dst/ase-task-board.js +12 -4
- package/dst/ase-task-store-client.js +4 -5
- package/dst/ase-task-store-plugin-gitea.js +3 -3
- package/dst/man/ase-agent-adhoc.md +25 -6
- package/dst/man/ase-agent-condenser.md +7 -0
- package/dst/man/ase-agent-editor.md +7 -0
- package/dst/man/ase-agent-estimator.md +7 -0
- package/dst/man/ase-agent-fleet.md +7 -4
- package/dst/man/ase-agent-implementer.md +7 -0
- package/dst/man/ase-agent-integrator.md +7 -0
- package/dst/man/ase-agent-qualifier.md +7 -0
- package/dst/man/ase-agent-rebooter.md +7 -0
- package/dst/man/ase-agent-researcher.md +7 -0
- package/dst/man/ase-config-init.md +2 -1
- package/dst/man/ase-config.md +5 -0
- package/dst/man/ase-task-board.md +44 -2
- package/package.json +11 -10
- 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/meta/ase-common-task.md +33 -1
- package/plugin/meta/ase-control.md +14 -0
- package/plugin/meta/ase-format-specbook.yaml +9 -9
- package/plugin/meta/ase-skill.md +7 -0
- package/plugin/mods/ase-mods-common.ts +73 -42
- package/plugin/mods/ase-mods-help-hint.ts +1 -1
- package/plugin/mods/ase-mods-task-edit.ts +427 -0
- package/plugin/mods/ase-mods.ts +45 -14
- package/plugin/skills/ase-code-edit/SKILL.md +53 -16
- package/plugin/skills/ase-code-edit/help.md +26 -13
- package/plugin/skills/ase-code-lint/SKILL.md +7 -3
- package/plugin/skills/ase-help-intent/data.md +248 -36
- 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-changelog/SKILL.md +29 -10
- package/plugin/skills/ase-repo-changelog/help.md +5 -1
- package/plugin/skills/ase-repo-commit/SKILL.md +4 -1
- package/plugin/skills/ase-repo-dissect/help.md +2 -1
- package/plugin/skills/ase-repo-stage/SKILL.md +459 -0
- package/plugin/skills/ase-repo-stage/help.md +120 -0
- package/plugin/skills/ase-spec-edit/SKILL.md +20 -12
- package/plugin/skills/ase-spec-edit/help.md +22 -14
- package/plugin/skills/ase-task-edit/SKILL.md +114 -9
- package/plugin/skills/ase-task-edit/help.md +60 -0
- package/plugin/skills/ase-task-grill/SKILL.md +35 -20
- package/plugin/skills/ase-task-grill/help.md +18 -8
- /package/plugin/mods/{ase-mods-evaluate.ts → ase-mods-meta-evaluate.ts} +0 -0
- /package/plugin/mods/{ase-mods-grill.ts → ase-mods-task-grill.ts} +0 -0
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"homepage": "https://ase.tools",
|
|
7
7
|
"repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
|
|
8
8
|
"bugs": { "url": "https://github.com/rse/ase/issues" },
|
|
9
|
-
"version": "2.0.
|
|
9
|
+
"version": "2.0.8",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"globals": "17.14.0",
|
|
32
32
|
"typescript": "6.0.3",
|
|
33
33
|
"eslint-plugin-vue": "10.12.0",
|
|
34
|
-
"vue-eslint-parser": "10.4.
|
|
34
|
+
"vue-eslint-parser": "10.4.2",
|
|
35
35
|
"vue-tsc": "3.3.14",
|
|
36
36
|
|
|
37
37
|
"@rse/stx": "1.1.7",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"shx": "0.4.0",
|
|
40
40
|
"sync-directory": "6.0.6",
|
|
41
41
|
"stylus": "0.64.0",
|
|
42
|
-
"vite": "8.3.
|
|
42
|
+
"vite": "8.3.6",
|
|
43
43
|
"@vitejs/plugin-vue": "6.0.10",
|
|
44
|
-
"vue": "3.5.
|
|
44
|
+
"vue": "3.5.45",
|
|
45
45
|
"mousetrap": "1.6.5",
|
|
46
46
|
"@codemirror/view": "6.43.15",
|
|
47
47
|
"@codemirror/state": "6.7.6",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"valibot": "1.5.0",
|
|
79
79
|
"execa": "10.2.1",
|
|
80
80
|
"mkdirp": "3.0.1",
|
|
81
|
-
"@hapi/hapi": "21.4.
|
|
81
|
+
"@hapi/hapi": "21.4.13",
|
|
82
82
|
"beautiful-mermaid": "1.1.3",
|
|
83
83
|
"@d2lang/d2": "0.1.34",
|
|
84
84
|
"elkjs": "0.12.1",
|
|
@@ -86,8 +86,9 @@
|
|
|
86
86
|
"string-width": "8.3.0",
|
|
87
87
|
"react": "19.3.1",
|
|
88
88
|
"chokidar": "6.0.0",
|
|
89
|
-
"marked": "18.1.
|
|
90
|
-
"
|
|
89
|
+
"marked": "18.1.3",
|
|
90
|
+
"diff": "9.0.0",
|
|
91
|
+
"shiki": "4.6.0",
|
|
91
92
|
"cli-table3": "0.6.5",
|
|
92
93
|
"chalk": "6.0.1",
|
|
93
94
|
"pretty-ms": "10.0.0",
|
|
@@ -97,7 +98,7 @@
|
|
|
97
98
|
"zod": "4.6.7",
|
|
98
99
|
"which": "7.0.0",
|
|
99
100
|
"update-notifier": "7.3.1",
|
|
100
|
-
"semver": "7.8.
|
|
101
|
+
"semver": "7.8.6",
|
|
101
102
|
"shell-quote": "1.12.0",
|
|
102
103
|
"get-stdin": "10.0.0",
|
|
103
104
|
"proper-lockfile": "4.1.2",
|
|
@@ -105,10 +106,10 @@
|
|
|
105
106
|
"p-queue": "10.0.0",
|
|
106
107
|
"pacote": "22.0.1",
|
|
107
108
|
"ofetch": "1.5.1",
|
|
108
|
-
"undici": "8.12.
|
|
109
|
+
"undici": "8.12.4",
|
|
109
110
|
"picomatch": "4.0.7",
|
|
110
111
|
"lru-cache": "11.5.3",
|
|
111
|
-
"@rse/specbook": "1.6.
|
|
112
|
+
"@rse/specbook": "1.6.2",
|
|
112
113
|
"markdownlint": "0.42.0",
|
|
113
114
|
"tail": "2.2.6",
|
|
114
115
|
"source-code-error": "1.2.0",
|
|
@@ -58,6 +58,30 @@ Task Skill Common Steps
|
|
|
58
58
|
|
|
59
59
|
</define>
|
|
60
60
|
|
|
61
|
+
<define name="task-focus">
|
|
62
|
+
|
|
63
|
+
Resolve the plan sections under focus: treat <getopt-option-focus/> as a
|
|
64
|
+
comma-separated list of *section tokens*. Trim every token, then expand
|
|
65
|
+
the synonyms `SPEC` and `WHAT` to `SPECIFICATION`, the synonyms `DES` and
|
|
66
|
+
`HOW` to `DESIGN`, and the synonyms `VERIFY`, `VER`, and `WHEN` to
|
|
67
|
+
`VERIFICATION`, and expand the sentinel `all` (the default) to
|
|
68
|
+
`SPECIFICATION,DESIGN,VERIFICATION`. Set <sections/> to the resulting
|
|
69
|
+
list, *keeping the given order*. The getopt parser validates only the
|
|
70
|
+
*first* token, so you *MUST* validate each token yourself: if any token
|
|
71
|
+
is *empty*, *not* one of the recognized sections or synonyms (matched
|
|
72
|
+
*case-sensitively*), or results in a section already present in
|
|
73
|
+
<sections/>, bind <token/> to that offending token, then only output the
|
|
74
|
+
following <template/> and immediately *STOP* processing the entire
|
|
75
|
+
current skill:
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
⧉ **ASE**: ☻ skill: **<arg1/>**, ▶ ERROR: invalid `--focus` token: **<token/>**
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
Do not output anything else.
|
|
82
|
+
|
|
83
|
+
</define>
|
|
84
|
+
|
|
61
85
|
<define name="task-load-content">
|
|
62
86
|
|
|
63
87
|
<if condition="
|
|
@@ -227,10 +251,18 @@ related to this MCP call except the following <template/>:
|
|
|
227
251
|
custom dialog according to the expanded `custom-dialog`
|
|
228
252
|
definition. You *MUST* closely follow this definition:
|
|
229
253
|
|
|
230
|
-
<expand name="custom-dialog" arg1="--
|
|
254
|
+
<expand name="custom-dialog" arg1="--other">
|
|
231
255
|
<content/>
|
|
232
256
|
</expand>
|
|
233
257
|
|
|
258
|
+
If <result/> matches `OTHER: <text/>`, the user typed a free-text
|
|
259
|
+
request instead of choosing an option: treat it as an implicit
|
|
260
|
+
`DONE`, i.e., set <result>DONE</result>, and let the calling skill
|
|
261
|
+
finish along its `DONE` dispatch (output its `DONE` template).
|
|
262
|
+
Its *STOP* then ends only the skill, *not* the current turn:
|
|
263
|
+
immediately afterwards, handle <text/> as a regular user request,
|
|
264
|
+
outside of any skill, without re-opening this dialog.
|
|
265
|
+
|
|
234
266
|
</define>
|
|
235
267
|
|
|
236
268
|
<define name="task-next-handoff">
|
|
@@ -187,3 +187,17 @@ Control Flow Constructs
|
|
|
187
187
|
the order of their occurrence in <parallel-body/>. This construct is
|
|
188
188
|
expanded to its <parallel-body/>. Do not output anything else.
|
|
189
189
|
|
|
190
|
+
- *IMPORTANT*: You *MUST* honor the following rule for *all*
|
|
191
|
+
concurrently dispatched sub-agents -- whether invoked through
|
|
192
|
+
<parallel/> or through multiple `Agent` tool invocations emitted in
|
|
193
|
+
one single message:
|
|
194
|
+
|
|
195
|
+
The agent tool may run sub-agents in the *background* (e.g.
|
|
196
|
+
*Anthropic Claude Code CLI* ignores `run_in_background: false`) and
|
|
197
|
+
then notifies you of each returned sub-agent individually. While
|
|
198
|
+
these notifications arrive, you *MUST* *NOT* output anything -- no
|
|
199
|
+
progress messages (like `Batch 3 of 8 is back, 5 still running`),
|
|
200
|
+
no intermediate results, and no waiting notices -- but silently wait
|
|
201
|
+
for the remaining sub-agents. Only once *all* of them have returned,
|
|
202
|
+
continue with the processing which consumes their results.
|
|
203
|
+
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
id: DG
|
|
126
126
|
file: 03-REQS-DG-Domain-Glossary.md
|
|
127
127
|
desc: >
|
|
128
|
-
The ubiquitous language of the domain, defining the meaning of each domain term in business language, together with its synonyms, its place in the hierarchy of terms, and the terms it has to be kept apart from, shared consistently across all
|
|
128
|
+
The ubiquitous language of the domain, defining the meaning of each domain term in business language, together with its synonyms, its place in the hierarchy of terms, and the terms it has to be kept apart from, shared consistently across all artifacts.
|
|
129
129
|
optional: true
|
|
130
130
|
children:
|
|
131
131
|
- kind: TERM
|
|
@@ -581,7 +581,7 @@
|
|
|
581
581
|
optional: true
|
|
582
582
|
- name: TRIGGER
|
|
583
583
|
desc: >
|
|
584
|
-
The event that starts the use case, stated as a 1-sentence occurrence (e.g. `The attendee opens the event URL.`), as opposed to the `PRE-CONDITION
|
|
584
|
+
The event that starts the use case, stated as a 1-sentence occurrence (e.g. `The attendee opens the event URL.`), as opposed to the `PRE-CONDITION`, which is a state that has to hold, and matching the `When <trigger>` of the event-driven Functional Requirements the use case exercises.
|
|
585
585
|
optional: true
|
|
586
586
|
- name: POST-CONDITION
|
|
587
587
|
desc: >
|
|
@@ -847,7 +847,7 @@
|
|
|
847
847
|
The kind of the interface, one of:
|
|
848
848
|
- `REST`: HTTP resources addressed by URL paths and HTTP methods.
|
|
849
849
|
- `GraphQL`: A GraphQL schema of queries, mutations, and subscriptions.
|
|
850
|
-
- `gRPC`: Protocol Buffers
|
|
850
|
+
- `gRPC`: Protocol Buffers-based remote procedure calls.
|
|
851
851
|
- `WebSocket`: Messages exchanged over a WebSocket connection.
|
|
852
852
|
- `MQTT`: Messages published to and subscribed from MQTT topics.
|
|
853
853
|
- `MCP`: Tools, resources, and prompts of a Model Context Protocol server.
|
|
@@ -1322,7 +1322,7 @@
|
|
|
1322
1322
|
props:
|
|
1323
1323
|
- name: MAXIMS
|
|
1324
1324
|
desc: >
|
|
1325
|
-
Comma-separated references to the MAXIM objects the principle concretizes (e.g. a `Stateless Services` principle concretizing a `Scale Out over Scale Up` maxim), so that the principles form a hierarchy below the maxims rather than a flat
|
|
1325
|
+
Comma-separated references to the MAXIM objects the principle concretizes (e.g. a `Stateless Services` principle concretizing a `Scale Out over Scale Up` maxim), so that the principles form a hierarchy below the maxims rather than a flat catalog; omitted for a principle standing on its own forces alone.
|
|
1326
1326
|
value: list([[MAXIM:*]])
|
|
1327
1327
|
optional: true
|
|
1328
1328
|
- name: DRIVEN-BY
|
|
@@ -1362,7 +1362,7 @@
|
|
|
1362
1362
|
optional: true
|
|
1363
1363
|
- name: USES
|
|
1364
1364
|
desc: >
|
|
1365
|
-
Comma-separated references to the smaller ARCHITECTURE-PATTERNS objects the pattern is composed of (e.g. a `Saga` using a `Transactional Outbox` for its steps), so that the patterns form a language of related structures rather than a flat
|
|
1365
|
+
Comma-separated references to the smaller ARCHITECTURE-PATTERNS objects the pattern is composed of (e.g. a `Saga` using a `Transactional Outbox` for its steps), so that the patterns form a language of related structures rather than a flat catalog.
|
|
1366
1366
|
value: list([[PATTERN:*]])
|
|
1367
1367
|
optional: true
|
|
1368
1368
|
acyclic: true
|
|
@@ -1663,7 +1663,7 @@
|
|
|
1663
1663
|
desc: >
|
|
1664
1664
|
A single technology component, named with a short (2-5 word) summary (e.g. `UI Framework`, `Web Server`, `Relational Database`) and described in a concise (1-3 sentence) paragraph by how the product is used within the solution and how it fits into the overall technology stack, plus a 1-sentence rationale ("why") for choosing this product over the alternatives.
|
|
1665
1665
|
|
|
1666
|
-
A component states one product in one role on one tier: the product used by several tiers alike belongs to the `Common` tier once, the lock-in
|
|
1666
|
+
A component states one product in one role on one tier: the product used by several tiers alike belongs to the `Common` tier once, the lock-in-carrying trade-off behind the choice is a Design Decisions decision referencing the component through `DECIDES`, and the functional components depending on the product reference it through `USED-BY`.
|
|
1667
1667
|
props:
|
|
1668
1668
|
- name: PRODUCT
|
|
1669
1669
|
desc: >
|
|
@@ -2395,7 +2395,7 @@
|
|
|
2395
2395
|
optional: true
|
|
2396
2396
|
- name: USES
|
|
2397
2397
|
desc: >
|
|
2398
|
-
Comma-separated references to the smaller CODE-PATTERNS objects the pattern is composed of (e.g. an `Endpoint Authorization` using the `Error Propagation` for its rejections), so that the patterns form a language of related shapes rather than a flat
|
|
2398
|
+
Comma-separated references to the smaller CODE-PATTERNS objects the pattern is composed of (e.g. an `Endpoint Authorization` using the `Error Propagation` for its rejections), so that the patterns form a language of related shapes rather than a flat catalog.
|
|
2399
2399
|
value: list([[CODE-PATTERN:*]])
|
|
2400
2400
|
optional: true
|
|
2401
2401
|
acyclic: true
|
|
@@ -2449,8 +2449,8 @@
|
|
|
2449
2449
|
The mandatory, concrete input data or action applied during the test, i.e. the stimulus whose response the `EXPECTED` judges.
|
|
2450
2450
|
- name: EXPECTED
|
|
2451
2451
|
desc: >
|
|
2452
|
-
The mandatory, concrete, observable response of the solution to the `INPUT` (a message shown, a session issued, a submission rejected) that defines a passing test, as opposed to the `POST-CONDITION
|
|
2452
|
+
The mandatory, concrete, observable response of the solution to the `INPUT` (a message shown, a session issued, a submission rejected) that defines a passing test, as opposed to the `POST-CONDITION`, which is the state left behind. When the test case verifies a NON-FUNCTIONAL-REQUIREMENTS object, the expected response restates the `METRIC` threshold of that requirement as the pass criterion (e.g. `p95 latency < 200 ms`) rather than a qualitative phrase (e.g. `remains responsive`).
|
|
2453
2453
|
- name: POST-CONDITION
|
|
2454
2454
|
desc: >
|
|
2455
|
-
The persisted state that must hold after the test completes (e.g. `the authorization token is in state used`), including the state that must not have changed (e.g. `no session token exists`), as opposed to the `EXPECTED
|
|
2455
|
+
The persisted state that must hold after the test completes (e.g. `the authorization token is in state used`), including the state that must not have changed (e.g. `no session token exists`), as opposed to the `EXPECTED`, which is the immediate response, and omitted for a test whose input leaves no state behind.
|
|
2456
2456
|
optional: true
|
package/plugin/meta/ase-skill.md
CHANGED
|
@@ -232,6 +232,13 @@ Skill Identification
|
|
|
232
232
|
⧉ **ASE**: ✪ skill: **<skill-name/>**, ✦ purpose: **<purpose/>**, ▶ status: **skill started**
|
|
233
233
|
</template>
|
|
234
234
|
|
|
235
|
+
You *MUST* output this <template/> *immediately* as the *very first*
|
|
236
|
+
output of the skill, *before* any tool call (including the
|
|
237
|
+
`ase_getopt` call) and *before* any work of the skill, and also when
|
|
238
|
+
the skill was invoked via the `Skill` tool from another skill. Any
|
|
239
|
+
"Do not output anything" instruction of the subsequent steps does
|
|
240
|
+
*NOT* suppress or defer it.
|
|
241
|
+
|
|
235
242
|
Later (but only if ((`$1` is *NOT* equal to `-h` or `--help`) *AND*
|
|
236
243
|
(<ase-guidance-level/> is equal to `normal` or `verbose`))), once
|
|
237
244
|
this skill finally will stop processing, you *MUST* once output the
|
|
@@ -6,20 +6,21 @@
|
|
|
6
6
|
|
|
7
7
|
/* Claude Code function hooks module ("mod", EARLY ACCESS):
|
|
8
8
|
Common parts, shared by the grill table, the decision matrix, the intent
|
|
9
|
-
help, the synopsis form, the manual band, the manpage browser,
|
|
10
|
-
band: the rows of the Markdown tables, the styled characters of the terminal
|
|
9
|
+
help, the synopsis form, the manual band, the manpage browser, the hint band,
|
|
10
|
+
and the task band: the rows of the Markdown tables, the styled characters of the terminal
|
|
11
11
|
drawing, their Markdown conversion, wrapping, and framing, and the contract,
|
|
12
12
|
keys, geometry, and drawing of the master/detail view in the band above the
|
|
13
13
|
prompt. */
|
|
14
14
|
|
|
15
15
|
import type { Elements, Frozen, MatchedEvent, ModelCompleteRequest, ModelForkResult,
|
|
16
|
-
PromptBox, PromptEditInput, PromptFilled, RenderElement } from "claude-code"
|
|
16
|
+
PromptBox, PromptEditInput, PromptFilled, RenderElement, Timer } from "claude-code"
|
|
17
17
|
|
|
18
18
|
/* the services of the engine, bound by the hooking infrastructure for the
|
|
19
19
|
modules (as the engine refuses to load a module passing "$" itself): the
|
|
20
20
|
redrawing, the reading of a plugin file and of the names of the directories
|
|
21
21
|
in a plugin directory, the forked and the plain model completion, the
|
|
22
|
-
waiting,
|
|
22
|
+
waiting, the reading and filling of the prompt, the loading of a task
|
|
23
|
+
(with its entity tag), and the running of a repeating timer */
|
|
23
24
|
export type Engine = {
|
|
24
25
|
invalidate: () => void,
|
|
25
26
|
read: (file: string) => Promise<string>,
|
|
@@ -28,7 +29,9 @@ export type Engine = {
|
|
|
28
29
|
complete: (request: ModelCompleteRequest) => Promise<string>,
|
|
29
30
|
sleep: (ms: number) => Promise<void>,
|
|
30
31
|
prompt: () => Promise<PromptBox>,
|
|
31
|
-
fill: (text: string) => Promise<PromptFilled
|
|
32
|
+
fill: (text: string) => Promise<PromptFilled>,
|
|
33
|
+
load: (id: string) => Promise<string>,
|
|
34
|
+
every: (ms: number, fn: () => void) => Timer
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/* the colors of a picked (resp. selected or focused) item, of a code span,
|
|
@@ -131,7 +134,7 @@ export const cells = (line: string): string[] => {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
/* a single styled character of the terminal drawing */
|
|
134
|
-
export type Char = { ch: string, bold: boolean, color: string | undefined }
|
|
137
|
+
export type Char = { ch: string, bold: boolean, color: string | undefined, italic?: boolean, inverse?: boolean, background?: string }
|
|
135
138
|
|
|
136
139
|
/* extract a section of a manual page (without its heading) */
|
|
137
140
|
export const section = (manual: string, name: string): string =>
|
|
@@ -225,16 +228,21 @@ export const wrap = (chars: Char[], columns: number, indent: number, lead = 0):
|
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
/* a run of equally styled characters */
|
|
228
|
-
type Run = { text: string, bold: boolean, color: string | undefined }
|
|
231
|
+
type Run = { text: string, bold: boolean, color: string | undefined, italic: boolean, inverse: boolean, background: string | undefined }
|
|
229
232
|
|
|
230
233
|
/* split a line of styled characters into runs of equally styled characters */
|
|
231
234
|
export const split = (chars: Char[]): Run[] => {
|
|
232
235
|
const runs: Run[] = []
|
|
233
236
|
for (let i = 0, j = 0; i < chars.length; i = j) {
|
|
234
237
|
const { bold, color } = chars[i]!
|
|
235
|
-
|
|
238
|
+
const italic = chars[i]!.italic ?? false
|
|
239
|
+
const inverse = chars[i]!.inverse ?? false
|
|
240
|
+
const { background } = chars[i]!
|
|
241
|
+
while (j < chars.length && chars[j]!.bold === bold && chars[j]!.color === color &&
|
|
242
|
+
(chars[j]!.italic ?? false) === italic && (chars[j]!.inverse ?? false) === inverse &&
|
|
243
|
+
chars[j]!.background === background)
|
|
236
244
|
j++
|
|
237
|
-
runs.push({ text: chars.slice(i, j).map((char) => char.ch).join(""), bold, color })
|
|
245
|
+
runs.push({ text: chars.slice(i, j).map((char) => char.ch).join(""), bold, color, italic, inverse, background })
|
|
238
246
|
}
|
|
239
247
|
return runs
|
|
240
248
|
}
|
|
@@ -307,15 +315,15 @@ export const frame = (lines: Char[][], columns: number, color: string | undefine
|
|
|
307
315
|
}
|
|
308
316
|
|
|
309
317
|
/* split a horizontal edge of a frame into runs with a title placed after its
|
|
310
|
-
first and another one before its last
|
|
311
|
-
one at the end with precedence), flagged whether they are a title */
|
|
312
|
-
const titled = (line: string, head: string, tail: string): [ string, boolean ][] => {
|
|
313
|
-
const end = tail !== "" && line.length >= tail.length +
|
|
314
|
-
const start = head !== "" && line.length >= head.length + (end !== "" ? end.length +
|
|
315
|
-
const k = end !== "" ? line.length -
|
|
318
|
+
first "lead" and another one before its last "trail" characters (each if it
|
|
319
|
+
fits, the one at the end with precedence), flagged whether they are a title */
|
|
320
|
+
const titled = (line: string, head: string, tail: string, lead: number, trail: number): [ string, boolean ][] => {
|
|
321
|
+
const end = tail !== "" && line.length >= tail.length + trail + 2 ? tail : ""
|
|
322
|
+
const start = head !== "" && line.length >= head.length + lead + trail + (end !== "" ? end.length + 2 : 0) ? head : ""
|
|
323
|
+
const k = end !== "" ? line.length - trail - end.length : line.length
|
|
316
324
|
return [
|
|
317
|
-
...(start !== "" ? [ [ line.slice(0,
|
|
318
|
-
[ line.slice(start !== "" ?
|
|
325
|
+
...(start !== "" ? [ [ line.slice(0, lead), false ], [ start, true ] ] as [ string, boolean ][] : []),
|
|
326
|
+
[ line.slice(start !== "" ? lead + start.length : 0, k), false ],
|
|
319
327
|
...(end !== "" ? [ [ end, true ], [ line.slice(k + end.length), false ] ] as [ string, boolean ][] : [])
|
|
320
328
|
]
|
|
321
329
|
}
|
|
@@ -348,14 +356,15 @@ export type Text = Elements["terminal"]["Text"]
|
|
|
348
356
|
|
|
349
357
|
/* draw a line of styled characters: its styled runs */
|
|
350
358
|
export const paint = (Text: Text, chars: Char[]): (string | RenderElement)[] =>
|
|
351
|
-
split(chars).map(({ text, bold, color }) =>
|
|
352
|
-
bold ||
|
|
359
|
+
split(chars).map(({ text, bold, color, italic, inverse, background }) =>
|
|
360
|
+
bold || italic || inverse || color !== undefined || background !== undefined ?
|
|
361
|
+
Text({ bold, italic, inverse, color, backgroundColor: background, children: text }) : text)
|
|
353
362
|
|
|
354
363
|
/* draw a horizontal edge of the self-drawn frame (dimmed), with titles at its
|
|
355
364
|
start and its end (with their brackets dimmed, an "ASE" prefix before a colon
|
|
356
365
|
in bold, and the rest dimmed) */
|
|
357
|
-
const edge = (Text: Text, line: string, head = "", tail = ""): RenderElement =>
|
|
358
|
-
Text({ children: titled(line, head, tail).flatMap(([ text, label ]) => {
|
|
366
|
+
const edge = (Text: Text, line: string, head = "", tail = "", lead = 2, trail = 2): RenderElement =>
|
|
367
|
+
Text({ children: titled(line, head, tail, lead, trail).flatMap(([ text, label ]) => {
|
|
359
368
|
const m = /^(\[ )?([^:]*)(:.*?)( \])?$/.exec(text)
|
|
360
369
|
if (!label || m === null)
|
|
361
370
|
return [ Text({ ...BORDER, children: text }) ]
|
|
@@ -367,9 +376,10 @@ const edge = (Text: Text, line: string, head = "", tail = ""): RenderElement =>
|
|
|
367
376
|
]
|
|
368
377
|
}) })
|
|
369
378
|
|
|
370
|
-
/* draw a row of a scrollbar: the thumb
|
|
379
|
+
/* draw a row of a scrollbar: the dark grey thumb (an inverse space, as it fills
|
|
380
|
+
the entire cell, unlike a block glyph), the (dimmed) track, or nothing */
|
|
371
381
|
const bar = (Text: Text, mark: Mark | undefined): RenderElement =>
|
|
372
|
-
mark === "thumb" ? Text({ children: "
|
|
382
|
+
mark === "thumb" ? Text({ inverse: true, color: "ansi256(242)", children: " " }) :
|
|
373
383
|
mark === "track" ? Text({ dimColor: true, children: "░" }) : Text({ children: " " })
|
|
374
384
|
|
|
375
385
|
/* the geometry of the band above the prompt: the inner columns of the self-drawn
|
|
@@ -384,7 +394,7 @@ export type Layout = { span: number, body: number, master: number, detail: numbe
|
|
|
384
394
|
and, once focused, by the keys only for a tree taller than the band), all rows of
|
|
385
395
|
the band but the one of the engine's "n more" row (as the band then takes them
|
|
386
396
|
anyway), and the columns of the master view (by the given share) and of the
|
|
387
|
-
detail view (with a separator column between them, and a gap
|
|
397
|
+
detail view (with a separator column between them, and a gap, a separator, and
|
|
388
398
|
a scrollbar column right of the detail view) */
|
|
389
399
|
export const layout = (e: BandRender, share: number, band = BAND): Layout => {
|
|
390
400
|
const span = Math.max(20, e.props.bodyColumns - FRAME.cols)
|
|
@@ -412,10 +422,14 @@ export const wheel = (e: BandScroll, view: View, step?: (by: number) => void): v
|
|
|
412
422
|
|
|
413
423
|
/* draw the band above the prompt: inside the self-drawn frame (with the title
|
|
414
424
|
in its top edge) either a single view of the given rows, a single view of the
|
|
415
|
-
given lines with its scrollbar
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
425
|
+
given lines with its scrollbar (optionally with the first given characters of
|
|
426
|
+
each line in a gutter at the left border, separated by a vertical line joining
|
|
427
|
+
the edges, with the background of a line filling the columns up to its scrollbar),
|
|
428
|
+
or a master view of the given rows (where the given rule rows are horizontal lines
|
|
429
|
+
joining the borders) and, joined by a separator, a detail view of the given lines
|
|
430
|
+
(optionally dimmed) with its scrollbar, every scrollbar (of lines not always
|
|
431
|
+
fitting) separated by a vertical line joining the edges,
|
|
432
|
+
into the bottom edge the given status (if any) at its start and
|
|
419
433
|
the scroll position of the lines (unless they always fit) at its end, and below
|
|
420
434
|
the frame the key hints (followed by blank rows for an overflowing band),
|
|
421
435
|
where the scroll position is clamped, and the page size (for the scroll keys)
|
|
@@ -428,7 +442,8 @@ export const panel = (ui: Elements["terminal"], { span, body, master, detail, ov
|
|
|
428
442
|
rule?: (i: number) => boolean,
|
|
429
443
|
lines?: Char[][],
|
|
430
444
|
dim?: boolean,
|
|
431
|
-
fit?: boolean
|
|
445
|
+
fit?: boolean,
|
|
446
|
+
gutter?: number
|
|
432
447
|
}): RenderElement => {
|
|
433
448
|
const { Box, Text } = ui
|
|
434
449
|
const lines = spec.lines ?? []
|
|
@@ -438,22 +453,38 @@ export const panel = (ui: Elements["terminal"], { span, body, master, detail, ov
|
|
|
438
453
|
view.scroll = Math.max(0, Math.min(view.scroll, lines.length - body))
|
|
439
454
|
const marks = scrollbar(body, lines.length, view.scroll)
|
|
440
455
|
const blank = Text({ children: " " })
|
|
456
|
+
const gutter = solo ? spec.gutter ?? 0 : 0
|
|
457
|
+
const rail = spec.lines !== undefined && spec.fit !== true
|
|
458
|
+
const lead = gutter > 0 ? gutter + 2 : 2
|
|
459
|
+
const trail = rail ? 3 : 2
|
|
441
460
|
const border = (left: string, joint: string, right: string) =>
|
|
442
|
-
left + (
|
|
443
|
-
"─".repeat(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
461
|
+
left + (!rail ? "─".repeat(span + 1) :
|
|
462
|
+
(gutter > 0 ? `${"─".repeat(gutter)}${joint}${"─".repeat(span - 2 - gutter)}` :
|
|
463
|
+
solo ? "─".repeat(span - 1) :
|
|
464
|
+
`${"─".repeat(master + 2)}${joint}${"─".repeat(detail + 2)}`) + `${joint}─`) + right
|
|
465
|
+
const scrolled = (i: number, width: number) => {
|
|
466
|
+
const line = lines[view.scroll + i] ?? []
|
|
467
|
+
const fill = line[0]?.background
|
|
468
|
+
return [
|
|
469
|
+
...(gutter > 0 ? [
|
|
470
|
+
Box({ width: gutter, flexShrink: 0, children: Text({ children: paint(Text, line.slice(0, gutter)) }) }),
|
|
471
|
+
Text({ ...BORDER, children: "│" }),
|
|
472
|
+
Text({ backgroundColor: fill, children: " " })
|
|
473
|
+
] : []),
|
|
474
|
+
Box({ width, flexShrink: 0, children:
|
|
475
|
+
Text({ wrap: "truncate-end", dimColor: spec.dim ?? false,
|
|
476
|
+
children: paint(Text, line.slice(gutter)) }) }),
|
|
477
|
+
...(rail ? [ Text({ backgroundColor: fill, children: " " }), Text({ ...BORDER, children: "│" }) ] :
|
|
478
|
+
[ Text({ children: " " }) ]),
|
|
479
|
+
bar(Text, marks[i])
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
const rows: RenderElement[] = [ edge(Text, border("╭", "┬", "╮"), spec.title, "", lead, trail) ]
|
|
452
483
|
for (let i = 0; i < body; i++) {
|
|
453
484
|
const rule = !solo && spec.lines !== undefined && spec.rule?.(i) === true
|
|
454
485
|
rows.push(Box({ flexDirection: "row", children: [
|
|
455
|
-
Text({ ...BORDER, children: rule ? "├─" : "│ " }),
|
|
456
|
-
...(solo ? scrolled(i, span - 3) : spec.lines === undefined ? [
|
|
486
|
+
Text({ ...BORDER, children: rule ? "├─" : gutter > 0 ? "│" : "│ " }),
|
|
487
|
+
...(solo ? scrolled(i, gutter > 0 ? span - 4 - gutter : span - 3) : spec.lines === undefined ? [
|
|
457
488
|
Box({ width: span, flexShrink: 0, children: spec.rows!(i) ?? blank })
|
|
458
489
|
] : [
|
|
459
490
|
rule ? Text({ ...BORDER, children: `${"─".repeat(master + 1)}┤ ` }) :
|
|
@@ -465,7 +496,7 @@ export const panel = (ui: Elements["terminal"], { span, body, master, detail, ov
|
|
|
465
496
|
] }))
|
|
466
497
|
}
|
|
467
498
|
rows.push(edge(Text, border("╰", "┴", "╯"), spec.status,
|
|
468
|
-
spec.fit === true ? "" : position(body, lines.length, view.scroll)))
|
|
499
|
+
spec.fit === true ? "" : position(body, lines.length, view.scroll), lead, trail))
|
|
469
500
|
rows.push(Box({ paddingLeft: 1, children: Text({ dimColor: true, wrap: "truncate-end", children: spec.hints }) }))
|
|
470
501
|
if (overflow)
|
|
471
502
|
rows.push(blank, blank)
|
|
@@ -29,7 +29,7 @@ const GROUP = /\[(?:[^[\]]|\[[^\]]*\])*\]|\((?:[^()]|\([^)]*\))*\)|\S+/g
|
|
|
29
29
|
const NBSP = String.fromCodePoint(0xA0)
|
|
30
30
|
const SYNTAX = "gray"
|
|
31
31
|
const REFER = "red"
|
|
32
|
-
const INDENT =
|
|
32
|
+
const INDENT = 2
|
|
33
33
|
const TITLE = "[ ⧉ ASE: Show Skill Synopsis ]"
|
|
34
34
|
|
|
35
35
|
/* a hint band: the scroll state of its frame, the skill command as typed,
|