@dsh-cc/tui 0.5.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/LICENSE +201 -0
- package/README.md +144 -0
- package/lib/bash-history.d.ts +31 -0
- package/lib/bash-history.d.ts.map +1 -0
- package/lib/bash-history.js +94 -0
- package/lib/bash-history.js.map +1 -0
- package/lib/compact-fold.d.ts +35 -0
- package/lib/compact-fold.d.ts.map +1 -0
- package/lib/compact-fold.js +60 -0
- package/lib/compact-fold.js.map +1 -0
- package/lib/components/arg-completers.d.ts +22 -0
- package/lib/components/arg-completers.d.ts.map +1 -0
- package/lib/components/arg-completers.js +75 -0
- package/lib/components/arg-completers.js.map +1 -0
- package/lib/components/code-theme.d.ts +25 -0
- package/lib/components/code-theme.d.ts.map +1 -0
- package/lib/components/code-theme.js +137 -0
- package/lib/components/code-theme.js.map +1 -0
- package/lib/components/completion.d.ts +50 -0
- package/lib/components/completion.d.ts.map +1 -0
- package/lib/components/completion.js +279 -0
- package/lib/components/completion.js.map +1 -0
- package/lib/components/diff-card.d.ts +29 -0
- package/lib/components/diff-card.d.ts.map +1 -0
- package/lib/components/diff-card.js +175 -0
- package/lib/components/diff-card.js.map +1 -0
- package/lib/components/diff-hunks.d.ts +44 -0
- package/lib/components/diff-hunks.d.ts.map +1 -0
- package/lib/components/diff-hunks.js +188 -0
- package/lib/components/diff-hunks.js.map +1 -0
- package/lib/components/editor-history.d.ts +23 -0
- package/lib/components/editor-history.d.ts.map +1 -0
- package/lib/components/editor-history.js +29 -0
- package/lib/components/editor-history.js.map +1 -0
- package/lib/components/markdown-theme.d.ts +11 -0
- package/lib/components/markdown-theme.d.ts.map +1 -0
- package/lib/components/markdown-theme.js +29 -0
- package/lib/components/markdown-theme.js.map +1 -0
- package/lib/components/masked-input.d.ts +24 -0
- package/lib/components/masked-input.d.ts.map +1 -0
- package/lib/components/masked-input.js +168 -0
- package/lib/components/masked-input.js.map +1 -0
- package/lib/components/overlay-host.d.ts +31 -0
- package/lib/components/overlay-host.d.ts.map +1 -0
- package/lib/components/overlay-host.js +128 -0
- package/lib/components/overlay-host.js.map +1 -0
- package/lib/components/overlays.d.ts +94 -0
- package/lib/components/overlays.d.ts.map +1 -0
- package/lib/components/overlays.js +450 -0
- package/lib/components/overlays.js.map +1 -0
- package/lib/components/provider-box.d.ts +23 -0
- package/lib/components/provider-box.d.ts.map +1 -0
- package/lib/components/provider-box.js +164 -0
- package/lib/components/provider-box.js.map +1 -0
- package/lib/components/read-group.d.ts +51 -0
- package/lib/components/read-group.d.ts.map +1 -0
- package/lib/components/read-group.js +105 -0
- package/lib/components/read-group.js.map +1 -0
- package/lib/components/root-bash.d.ts +36 -0
- package/lib/components/root-bash.d.ts.map +1 -0
- package/lib/components/root-bash.js +112 -0
- package/lib/components/root-bash.js.map +1 -0
- package/lib/components/root-types.d.ts +42 -0
- package/lib/components/root-types.d.ts.map +1 -0
- package/lib/components/root-types.js +8 -0
- package/lib/components/root-types.js.map +1 -0
- package/lib/components/root-utils.d.ts +22 -0
- package/lib/components/root-utils.d.ts.map +1 -0
- package/lib/components/root-utils.js +45 -0
- package/lib/components/root-utils.js.map +1 -0
- package/lib/components/root.d.ts +25 -0
- package/lib/components/root.d.ts.map +1 -0
- package/lib/components/root.js +357 -0
- package/lib/components/root.js.map +1 -0
- package/lib/components/theme.d.ts +61 -0
- package/lib/components/theme.d.ts.map +1 -0
- package/lib/components/theme.js +99 -0
- package/lib/components/theme.js.map +1 -0
- package/lib/components/transcript.d.ts +43 -0
- package/lib/components/transcript.d.ts.map +1 -0
- package/lib/components/transcript.js +203 -0
- package/lib/components/transcript.js.map +1 -0
- package/lib/components/working-line.d.ts +35 -0
- package/lib/components/working-line.d.ts.map +1 -0
- package/lib/components/working-line.js +66 -0
- package/lib/components/working-line.js.map +1 -0
- package/lib/effort-catalog.d.ts +27 -0
- package/lib/effort-catalog.d.ts.map +1 -0
- package/lib/effort-catalog.js +36 -0
- package/lib/effort-catalog.js.map +1 -0
- package/lib/export-markdown.d.ts +18 -0
- package/lib/export-markdown.d.ts.map +1 -0
- package/lib/export-markdown.js +124 -0
- package/lib/export-markdown.js.map +1 -0
- package/lib/harness/approval-preview.d.ts +33 -0
- package/lib/harness/approval-preview.d.ts.map +1 -0
- package/lib/harness/approval-preview.js +179 -0
- package/lib/harness/approval-preview.js.map +1 -0
- package/lib/harness/boot-banner.d.ts +14 -0
- package/lib/harness/boot-banner.d.ts.map +1 -0
- package/lib/harness/boot-banner.js +89 -0
- package/lib/harness/boot-banner.js.map +1 -0
- package/lib/harness/driver-agent.d.ts +78 -0
- package/lib/harness/driver-agent.d.ts.map +1 -0
- package/lib/harness/driver-agent.js +314 -0
- package/lib/harness/driver-agent.js.map +1 -0
- package/lib/harness/driver-agents.d.ts +20 -0
- package/lib/harness/driver-agents.d.ts.map +1 -0
- package/lib/harness/driver-agents.js +123 -0
- package/lib/harness/driver-agents.js.map +1 -0
- package/lib/harness/driver-approvals.d.ts +38 -0
- package/lib/harness/driver-approvals.d.ts.map +1 -0
- package/lib/harness/driver-approvals.js +277 -0
- package/lib/harness/driver-approvals.js.map +1 -0
- package/lib/harness/driver-bash.d.ts +15 -0
- package/lib/harness/driver-bash.d.ts.map +1 -0
- package/lib/harness/driver-bash.js +73 -0
- package/lib/harness/driver-bash.js.map +1 -0
- package/lib/harness/driver-catalog.d.ts +84 -0
- package/lib/harness/driver-catalog.d.ts.map +1 -0
- package/lib/harness/driver-catalog.js +256 -0
- package/lib/harness/driver-catalog.js.map +1 -0
- package/lib/harness/driver-ctx.d.ts +462 -0
- package/lib/harness/driver-ctx.d.ts.map +1 -0
- package/lib/harness/driver-ctx.js +11 -0
- package/lib/harness/driver-ctx.js.map +1 -0
- package/lib/harness/driver-hud.d.ts +31 -0
- package/lib/harness/driver-hud.d.ts.map +1 -0
- package/lib/harness/driver-hud.js +204 -0
- package/lib/harness/driver-hud.js.map +1 -0
- package/lib/harness/driver-live.d.ts +36 -0
- package/lib/harness/driver-live.d.ts.map +1 -0
- package/lib/harness/driver-live.js +44 -0
- package/lib/harness/driver-live.js.map +1 -0
- package/lib/harness/driver-modal.d.ts +52 -0
- package/lib/harness/driver-modal.d.ts.map +1 -0
- package/lib/harness/driver-modal.js +78 -0
- package/lib/harness/driver-modal.js.map +1 -0
- package/lib/harness/driver-mode.d.ts +35 -0
- package/lib/harness/driver-mode.d.ts.map +1 -0
- package/lib/harness/driver-mode.js +84 -0
- package/lib/harness/driver-mode.js.map +1 -0
- package/lib/harness/driver-pickers.d.ts +48 -0
- package/lib/harness/driver-pickers.d.ts.map +1 -0
- package/lib/harness/driver-pickers.js +249 -0
- package/lib/harness/driver-pickers.js.map +1 -0
- package/lib/harness/driver-promote.d.ts +29 -0
- package/lib/harness/driver-promote.d.ts.map +1 -0
- package/lib/harness/driver-promote.js +36 -0
- package/lib/harness/driver-promote.js.map +1 -0
- package/lib/harness/driver-queue.d.ts +19 -0
- package/lib/harness/driver-queue.d.ts.map +1 -0
- package/lib/harness/driver-queue.js +281 -0
- package/lib/harness/driver-queue.js.map +1 -0
- package/lib/harness/driver-run-local.d.ts +32 -0
- package/lib/harness/driver-run-local.d.ts.map +1 -0
- package/lib/harness/driver-run-local.js +366 -0
- package/lib/harness/driver-run-local.js.map +1 -0
- package/lib/harness/driver-sessions.d.ts +28 -0
- package/lib/harness/driver-sessions.d.ts.map +1 -0
- package/lib/harness/driver-sessions.js +386 -0
- package/lib/harness/driver-sessions.js.map +1 -0
- package/lib/harness/driver.d.ts +22 -0
- package/lib/harness/driver.d.ts.map +1 -0
- package/lib/harness/driver.js +459 -0
- package/lib/harness/driver.js.map +1 -0
- package/lib/harness/preset.d.ts +32 -0
- package/lib/harness/preset.d.ts.map +1 -0
- package/lib/harness/preset.js +34 -0
- package/lib/harness/preset.js.map +1 -0
- package/lib/harness/session-list.d.ts +65 -0
- package/lib/harness/session-list.d.ts.map +1 -0
- package/lib/harness/session-list.js +91 -0
- package/lib/harness/session-list.js.map +1 -0
- package/lib/harness/shell-output.d.ts +44 -0
- package/lib/harness/shell-output.d.ts.map +1 -0
- package/lib/harness/shell-output.js +90 -0
- package/lib/harness/shell-output.js.map +1 -0
- package/lib/harness/statusline-cc-transcript.d.ts +51 -0
- package/lib/harness/statusline-cc-transcript.d.ts.map +1 -0
- package/lib/harness/statusline-cc-transcript.js +191 -0
- package/lib/harness/statusline-cc-transcript.js.map +1 -0
- package/lib/harness/statusline-command.d.ts +54 -0
- package/lib/harness/statusline-command.d.ts.map +1 -0
- package/lib/harness/statusline-command.js +161 -0
- package/lib/harness/statusline-command.js.map +1 -0
- package/lib/harness/statusline-payload.d.ts +79 -0
- package/lib/harness/statusline-payload.d.ts.map +1 -0
- package/lib/harness/statusline-payload.js +93 -0
- package/lib/harness/statusline-payload.js.map +1 -0
- package/lib/harness/statusline-settings.d.ts +47 -0
- package/lib/harness/statusline-settings.d.ts.map +1 -0
- package/lib/harness/statusline-settings.js +57 -0
- package/lib/harness/statusline-settings.js.map +1 -0
- package/lib/harness/statusline-wiring.d.ts +45 -0
- package/lib/harness/statusline-wiring.d.ts.map +1 -0
- package/lib/harness/statusline-wiring.js +247 -0
- package/lib/harness/statusline-wiring.js.map +1 -0
- package/lib/harness/usage-view.d.ts +84 -0
- package/lib/harness/usage-view.d.ts.map +1 -0
- package/lib/harness/usage-view.js +199 -0
- package/lib/harness/usage-view.js.map +1 -0
- package/lib/harness/worktree-exit.d.ts +105 -0
- package/lib/harness/worktree-exit.d.ts.map +1 -0
- package/lib/harness/worktree-exit.js +168 -0
- package/lib/harness/worktree-exit.js.map +1 -0
- package/lib/history.d.ts +50 -0
- package/lib/history.d.ts.map +1 -0
- package/lib/history.js +139 -0
- package/lib/history.js.map +1 -0
- package/lib/index.d.ts +63 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +54 -0
- package/lib/index.js.map +1 -0
- package/lib/input.d.ts +158 -0
- package/lib/input.d.ts.map +1 -0
- package/lib/input.js +400 -0
- package/lib/input.js.map +1 -0
- package/lib/invariant.d.ts +14 -0
- package/lib/invariant.d.ts.map +1 -0
- package/lib/invariant.js +20 -0
- package/lib/invariant.js.map +1 -0
- package/lib/mode-cycle.d.ts +22 -0
- package/lib/mode-cycle.d.ts.map +1 -0
- package/lib/mode-cycle.js +32 -0
- package/lib/mode-cycle.js.map +1 -0
- package/lib/model-catalog.d.ts +22 -0
- package/lib/model-catalog.d.ts.map +1 -0
- package/lib/model-catalog.js +45 -0
- package/lib/model-catalog.js.map +1 -0
- package/lib/packaged-preset.d.ts +32 -0
- package/lib/packaged-preset.d.ts.map +1 -0
- package/lib/packaged-preset.js +94 -0
- package/lib/packaged-preset.js.map +1 -0
- package/lib/plugin.d.ts +11 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +88 -0
- package/lib/plugin.js.map +1 -0
- package/lib/profile.d.ts +4 -0
- package/lib/profile.d.ts.map +1 -0
- package/lib/profile.js +7 -0
- package/lib/profile.js.map +1 -0
- package/lib/project-sessions.d.ts +29 -0
- package/lib/project-sessions.d.ts.map +1 -0
- package/lib/project-sessions.js +60 -0
- package/lib/project-sessions.js.map +1 -0
- package/lib/project.d.ts +75 -0
- package/lib/project.d.ts.map +1 -0
- package/lib/project.js +196 -0
- package/lib/project.js.map +1 -0
- package/lib/provider-actions.d.ts +37 -0
- package/lib/provider-actions.d.ts.map +1 -0
- package/lib/provider-actions.js +417 -0
- package/lib/provider-actions.js.map +1 -0
- package/lib/provider-command.d.ts +109 -0
- package/lib/provider-command.d.ts.map +1 -0
- package/lib/provider-command.js +220 -0
- package/lib/provider-command.js.map +1 -0
- package/lib/provider-detail.d.ts +15 -0
- package/lib/provider-detail.d.ts.map +1 -0
- package/lib/provider-detail.js +201 -0
- package/lib/provider-detail.js.map +1 -0
- package/lib/provider-flow.d.ts +191 -0
- package/lib/provider-flow.d.ts.map +1 -0
- package/lib/provider-flow.js +192 -0
- package/lib/provider-flow.js.map +1 -0
- package/lib/provider-presets.d.ts +32 -0
- package/lib/provider-presets.d.ts.map +1 -0
- package/lib/provider-presets.js +105 -0
- package/lib/provider-presets.js.map +1 -0
- package/lib/provider-read.d.ts +63 -0
- package/lib/provider-read.d.ts.map +1 -0
- package/lib/provider-read.js +115 -0
- package/lib/provider-read.js.map +1 -0
- package/lib/provider-settings.d.ts +30 -0
- package/lib/provider-settings.d.ts.map +1 -0
- package/lib/provider-settings.js +65 -0
- package/lib/provider-settings.js.map +1 -0
- package/lib/resume-target.d.ts +59 -0
- package/lib/resume-target.d.ts.map +1 -0
- package/lib/resume-target.js +143 -0
- package/lib/resume-target.js.map +1 -0
- package/lib/slash.d.ts +44 -0
- package/lib/slash.d.ts.map +1 -0
- package/lib/slash.js +81 -0
- package/lib/slash.js.map +1 -0
- package/lib/state/driver-types.d.ts +505 -0
- package/lib/state/driver-types.d.ts.map +1 -0
- package/lib/state/driver-types.js +8 -0
- package/lib/state/driver-types.js.map +1 -0
- package/lib/state/driver-usage-types.d.ts +59 -0
- package/lib/state/driver-usage-types.d.ts.map +1 -0
- package/lib/state/driver-usage-types.js +8 -0
- package/lib/state/driver-usage-types.js.map +1 -0
- package/lib/statusline.d.ts +63 -0
- package/lib/statusline.d.ts.map +1 -0
- package/lib/statusline.js +97 -0
- package/lib/statusline.js.map +1 -0
- package/lib/store/pickers.d.ts +34 -0
- package/lib/store/pickers.d.ts.map +1 -0
- package/lib/store/pickers.js +111 -0
- package/lib/store/pickers.js.map +1 -0
- package/lib/store/provider-panel.d.ts +107 -0
- package/lib/store/provider-panel.d.ts.map +1 -0
- package/lib/store/provider-panel.js +151 -0
- package/lib/store/provider-panel.js.map +1 -0
- package/lib/store/question.d.ts +21 -0
- package/lib/store/question.d.ts.map +1 -0
- package/lib/store/question.js +59 -0
- package/lib/store/question.js.map +1 -0
- package/lib/store/queue.d.ts +27 -0
- package/lib/store/queue.d.ts.map +1 -0
- package/lib/store/queue.js +34 -0
- package/lib/store/queue.js.map +1 -0
- package/lib/store/session.d.ts +73 -0
- package/lib/store/session.d.ts.map +1 -0
- package/lib/store/session.js +172 -0
- package/lib/store/session.js.map +1 -0
- package/lib/store/subagents.d.ts +16 -0
- package/lib/store/subagents.d.ts.map +1 -0
- package/lib/store/subagents.js +44 -0
- package/lib/store/subagents.js.map +1 -0
- package/lib/store/todos.d.ts +35 -0
- package/lib/store/todos.d.ts.map +1 -0
- package/lib/store/todos.js +68 -0
- package/lib/store/todos.js.map +1 -0
- package/lib/store/usage.d.ts +15 -0
- package/lib/store/usage.d.ts.map +1 -0
- package/lib/store/usage.js +27 -0
- package/lib/store/usage.js.map +1 -0
- package/lib/store/views.d.ts +419 -0
- package/lib/store/views.d.ts.map +1 -0
- package/lib/store/views.js +3 -0
- package/lib/store/views.js.map +1 -0
- package/lib/store.d.ts +19 -0
- package/lib/store.d.ts.map +1 -0
- package/lib/store.js +10 -0
- package/lib/store.js.map +1 -0
- package/lib/terminal/lease.d.ts +33 -0
- package/lib/terminal/lease.d.ts.map +1 -0
- package/lib/terminal/lease.js +99 -0
- package/lib/terminal/lease.js.map +1 -0
- package/lib/tool-card.d.ts +75 -0
- package/lib/tool-card.d.ts.map +1 -0
- package/lib/tool-card.js +109 -0
- package/lib/tool-card.js.map +1 -0
- package/lib/tool-verbs.d.ts +12 -0
- package/lib/tool-verbs.d.ts.map +1 -0
- package/lib/tool-verbs.js +29 -0
- package/lib/tool-verbs.js.map +1 -0
- package/lib/transcript.d.ts +40 -0
- package/lib/transcript.d.ts.map +1 -0
- package/lib/transcript.js +413 -0
- package/lib/transcript.js.map +1 -0
- package/lib/working-line.d.ts +44 -0
- package/lib/working-line.d.ts.map +1 -0
- package/lib/working-line.js +56 -0
- package/lib/working-line.js.map +1 -0
- package/package.json +72 -0
- package/presets/cc/.dsh-cc-managed.json +5 -0
- package/presets/cc/agent.cordis.yml +524 -0
- package/presets/cc/preset.yml +3 -0
- package/scripts/stage-preset.mjs +26 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver-backed slash argument completers injected into the TUI autocomplete
|
|
3
|
+
* provider. `/model` completes from the live model catalog in both forms the
|
|
4
|
+
* arg path accepts (`provider/id`, plus a bare `id` when unique across the
|
|
5
|
+
* catalog — mirroring parseModelChoice); `/effort` completes live effort
|
|
6
|
+
* levels; `/permissions` completes the five CC rule-engine modes; `/resume`
|
|
7
|
+
* completes session short ids.
|
|
8
|
+
* Candidates are fetched per completion request, so catalog/session staleness
|
|
9
|
+
* is bounded by the driver calls themselves.
|
|
10
|
+
* @module @dsh-cc/tui/components/arg-completers
|
|
11
|
+
*/
|
|
12
|
+
import { PERMISSION_COMMAND_MODES } from '@dsh-cc/command-permissions';
|
|
13
|
+
import { PROVIDER_SUBCOMMANDS } from "../slash.js";
|
|
14
|
+
import { shortenSession } from "../statusline.js";
|
|
15
|
+
/**
|
|
16
|
+
* Build the per-command argument completer map handed to
|
|
17
|
+
* {@link TuiAutocompleteProvider}. Only commands with meaningful arguments are
|
|
18
|
+
* registered; every other slash command stays at the provider's null fallback.
|
|
19
|
+
*/
|
|
20
|
+
export function buildArgCompleters(driver) {
|
|
21
|
+
return {
|
|
22
|
+
model: async () => {
|
|
23
|
+
const catalog = await driver.loadModelCatalog();
|
|
24
|
+
// A bare id is only offered when it resolves unambiguously —
|
|
25
|
+
// parseModelChoice rejects an id shared by multiple providers.
|
|
26
|
+
const idCounts = new Map();
|
|
27
|
+
for (const entry of catalog)
|
|
28
|
+
idCounts.set(entry.id, (idCounts.get(entry.id) ?? 0) + 1);
|
|
29
|
+
const items = [];
|
|
30
|
+
for (const entry of catalog) {
|
|
31
|
+
const key = `${entry.provider}/${entry.id}`;
|
|
32
|
+
items.push({
|
|
33
|
+
value: key,
|
|
34
|
+
label: key,
|
|
35
|
+
...entry.name.length === 0 ? {} : { description: entry.name },
|
|
36
|
+
});
|
|
37
|
+
if (idCounts.get(entry.id) === 1) {
|
|
38
|
+
items.push({
|
|
39
|
+
value: entry.id,
|
|
40
|
+
label: entry.id,
|
|
41
|
+
...entry.name.length === 0 ? {} : { description: entry.name },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return items;
|
|
46
|
+
},
|
|
47
|
+
effort: async () => {
|
|
48
|
+
// The driver folds in the trailing reserved `default` entry and returns
|
|
49
|
+
// [] when no model (or no resolvable levels) — no dead-end completions.
|
|
50
|
+
const efforts = await driver.loadModelEfforts();
|
|
51
|
+
return efforts.map(level => ({ value: level, label: level }));
|
|
52
|
+
},
|
|
53
|
+
permissions: async () => {
|
|
54
|
+
return PERMISSION_COMMAND_MODES.map(mode => ({ value: mode, label: mode }));
|
|
55
|
+
},
|
|
56
|
+
resume: async () => {
|
|
57
|
+
const sessions = await driver.listSessions();
|
|
58
|
+
// Newest first, mirroring the /resume switcher overlay ordering.
|
|
59
|
+
const sorted = sessions.slice().sort((a, b) => b.createdAt - a.createdAt);
|
|
60
|
+
return sorted.map(session => {
|
|
61
|
+
const short = shortenSession(session.id);
|
|
62
|
+
return {
|
|
63
|
+
value: short,
|
|
64
|
+
label: short,
|
|
65
|
+
...short === session.id ? {} : { description: session.id },
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
provider: async () => {
|
|
70
|
+
// Static subcommand table (§4.1); route ids come from the overlay itself.
|
|
71
|
+
return PROVIDER_SUBCOMMANDS.map(sub => ({ value: sub, label: sub }));
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=arg-completers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-completers.js","sourceRoot":"","sources":["../../src/components/arg-completers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntax highlighting for fenced code blocks. highlight.js (common-languages
|
|
3
|
+
* subset, pinned ^11) tokenizes source into an HTML string of
|
|
4
|
+
* `<span class="hljs-...">` elements; a tiny stack-based walker converts that
|
|
5
|
+
* HTML into ANSI-styled lines. Styles never bleed across rows: every SGR
|
|
6
|
+
* opener is closed at the line end and reopened on the next line. Unknown or
|
|
7
|
+
* unsupported languages fall back to the unstyled source split by newlines.
|
|
8
|
+
*
|
|
9
|
+
* We import `highlight.js/lib/common` (not the full 190-language bundle) — it
|
|
10
|
+
* ships ~36 common languages and resolves cleanly under the repo's ESM
|
|
11
|
+
* `moduleResolution: bundler` tsconfig (the package's `exports` map points the
|
|
12
|
+
* `./lib/common` subpath at the ESM build with sibling `.d.ts` files).
|
|
13
|
+
* @module @dsh-cc/tui/components/code-theme
|
|
14
|
+
*/
|
|
15
|
+
import { type Theme } from './theme.ts';
|
|
16
|
+
/**
|
|
17
|
+
* Highlight `code` (written in `lang`) and return ANSI-styled lines.
|
|
18
|
+
*
|
|
19
|
+
* - Styles come from the injected `theme` (default: the built-in palette).
|
|
20
|
+
* - Unknown or unsupported `lang` → plain `code.split('\n')` (never throws).
|
|
21
|
+
* - Empty `code` → `['']`.
|
|
22
|
+
* - The returned array length always matches `code.split('\n').length`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function highlightCodeAnsi(code: string, lang?: string, theme?: Theme): string[];
|
|
25
|
+
//# sourceMappingURL=code-theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-theme.d.ts","sourceRoot":"","sources":["../../src/components/code-theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAgB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAA;AAyGrD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,KAAoB,GAAG,MAAM,EAAE,CASpG"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntax highlighting for fenced code blocks. highlight.js (common-languages
|
|
3
|
+
* subset, pinned ^11) tokenizes source into an HTML string of
|
|
4
|
+
* `<span class="hljs-...">` elements; a tiny stack-based walker converts that
|
|
5
|
+
* HTML into ANSI-styled lines. Styles never bleed across rows: every SGR
|
|
6
|
+
* opener is closed at the line end and reopened on the next line. Unknown or
|
|
7
|
+
* unsupported languages fall back to the unstyled source split by newlines.
|
|
8
|
+
*
|
|
9
|
+
* We import `highlight.js/lib/common` (not the full 190-language bundle) — it
|
|
10
|
+
* ships ~36 common languages and resolves cleanly under the repo's ESM
|
|
11
|
+
* `moduleResolution: bundler` tsconfig (the package's `exports` map points the
|
|
12
|
+
* `./lib/common` subpath at the ESM build with sibling `.d.ts` files).
|
|
13
|
+
* @module @dsh-cc/tui/components/code-theme
|
|
14
|
+
*/
|
|
15
|
+
import hljs from 'highlight.js/lib/common';
|
|
16
|
+
import { defaultTheme } from "./theme.js";
|
|
17
|
+
/**
|
|
18
|
+
* Map of highlight.js scope (the class name with the `hljs-` prefix stripped)
|
|
19
|
+
* to an SGR style, derived from the injected theme. Kept intentionally small —
|
|
20
|
+
* this is a terminal, not an IDE. Unmapped scopes render unstyled.
|
|
21
|
+
*/
|
|
22
|
+
function styleByScope(theme) {
|
|
23
|
+
return {
|
|
24
|
+
keyword: theme.warning,
|
|
25
|
+
literal: theme.accent,
|
|
26
|
+
number: theme.accent,
|
|
27
|
+
// No dedicated string color → strings fall back to the accent.
|
|
28
|
+
string: theme.accent,
|
|
29
|
+
comment: theme.muted,
|
|
30
|
+
title: theme.bold,
|
|
31
|
+
function: theme.bold,
|
|
32
|
+
'function_': theme.bold,
|
|
33
|
+
'title.function_': theme.bold,
|
|
34
|
+
built_in: theme.accent,
|
|
35
|
+
attr: theme.italic,
|
|
36
|
+
attribute: theme.italic,
|
|
37
|
+
type: theme.accent,
|
|
38
|
+
class: theme.bold,
|
|
39
|
+
meta: theme.italic,
|
|
40
|
+
regexp: theme.accent,
|
|
41
|
+
variable: theme.italic,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Decode the HTML entities highlight.js emits back to raw source characters. */
|
|
45
|
+
function decodeEntities(s) {
|
|
46
|
+
// `&` MUST be decoded last so a literal `&` in source (emitted as
|
|
47
|
+
// `&amp;`) round-trips correctly; a single-pass global replace does not
|
|
48
|
+
// re-scan replaced text, so this is safe.
|
|
49
|
+
return s
|
|
50
|
+
.replace(/</g, '<')
|
|
51
|
+
.replace(/>/g, '>')
|
|
52
|
+
.replace(/"/g, '"')
|
|
53
|
+
.replace(/'/g, "'")
|
|
54
|
+
.replace(/'/g, "'")
|
|
55
|
+
.replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(Number(d)))
|
|
56
|
+
.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
|
|
57
|
+
.replace(/&/g, '&');
|
|
58
|
+
}
|
|
59
|
+
/** Resolve the innermost open span to an SGR style (undefined = unstyled). */
|
|
60
|
+
function styleForStackTop(stack, styles) {
|
|
61
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
62
|
+
const attr = stack[i];
|
|
63
|
+
if (!attr)
|
|
64
|
+
continue;
|
|
65
|
+
for (const part of attr.split(/\s+/)) {
|
|
66
|
+
const scope = part.startsWith('hljs-') ? part.slice(5) : part;
|
|
67
|
+
const style = styles[scope];
|
|
68
|
+
if (style)
|
|
69
|
+
return style;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Convert highlight.js HTML (only `<span class="hljs-...">`, `</span>`, and
|
|
76
|
+
* entity-escaped text) into ANSI-styled lines. The returned array length
|
|
77
|
+
* always equals the number of source lines; ANSI codes never span lines.
|
|
78
|
+
*/
|
|
79
|
+
function htmlToAnsiLines(html, styles) {
|
|
80
|
+
const lines = [];
|
|
81
|
+
let current = '';
|
|
82
|
+
const stack = [];
|
|
83
|
+
// The only `<` characters in hljs output are tag starts; text `<` is `<`.
|
|
84
|
+
const parts = html.split(/(<\/?span\b[^>]*>)/);
|
|
85
|
+
for (const part of parts) {
|
|
86
|
+
if (part === '')
|
|
87
|
+
continue;
|
|
88
|
+
if (part === '</span>') {
|
|
89
|
+
stack.pop();
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (part.startsWith('<span')) {
|
|
93
|
+
const m = /class="([^"]*)"/.exec(part);
|
|
94
|
+
stack.push(m ? m[1] : '');
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// Text node: decode entities, then split on newlines so each line opens
|
|
98
|
+
// and closes its own SGR codes (no cross-line bleed).
|
|
99
|
+
const text = decodeEntities(part);
|
|
100
|
+
const segs = text.split('\n');
|
|
101
|
+
for (let i = 0; i < segs.length; i++) {
|
|
102
|
+
if (i > 0) {
|
|
103
|
+
lines.push(current);
|
|
104
|
+
current = '';
|
|
105
|
+
}
|
|
106
|
+
const seg = segs[i];
|
|
107
|
+
if (!seg)
|
|
108
|
+
continue;
|
|
109
|
+
const style = styleForStackTop(stack, styles);
|
|
110
|
+
current += style ? style(seg) : seg;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
lines.push(current);
|
|
114
|
+
return lines;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Highlight `code` (written in `lang`) and return ANSI-styled lines.
|
|
118
|
+
*
|
|
119
|
+
* - Styles come from the injected `theme` (default: the built-in palette).
|
|
120
|
+
* - Unknown or unsupported `lang` → plain `code.split('\n')` (never throws).
|
|
121
|
+
* - Empty `code` → `['']`.
|
|
122
|
+
* - The returned array length always matches `code.split('\n').length`.
|
|
123
|
+
*/
|
|
124
|
+
export function highlightCodeAnsi(code, lang, theme = defaultTheme) {
|
|
125
|
+
if (code === '')
|
|
126
|
+
return [''];
|
|
127
|
+
if (!lang || !hljs.getLanguage(lang))
|
|
128
|
+
return code.split('\n');
|
|
129
|
+
try {
|
|
130
|
+
const result = hljs.highlight(code, { language: lang, ignoreIllegals: true });
|
|
131
|
+
return htmlToAnsiLines(result.value, styleByScope(theme));
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return code.split('\n');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=code-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-theme.js","sourceRoot":"","sources":["../../src/components/code-theme.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI autocomplete provider: slash-command suggestions + `@`-file completion.
|
|
3
|
+
* Implements pi-tui's `AutocompleteProvider` interface so the vendored Editor
|
|
4
|
+
* can drive it via `setAutocompleteProvider`. Slash suggestions come from an
|
|
5
|
+
* injected command catalog (local + harness); `@`-paths come from OUR OWN
|
|
6
|
+
* workspace walk (recursive readdir, no `fd` dependency).
|
|
7
|
+
* @module @dsh-cc/tui/components/completion
|
|
8
|
+
*/
|
|
9
|
+
import type { AutocompleteItem, AutocompleteProvider, AutocompleteSuggestions } from '@dsh-cc/pi-tui';
|
|
10
|
+
/** A command in the injected catalog. */
|
|
11
|
+
export interface CommandItem {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly description?: string;
|
|
14
|
+
readonly argumentHint?: string;
|
|
15
|
+
}
|
|
16
|
+
/** A workspace walk returns relative POSIX paths (no leading ./). */
|
|
17
|
+
export type WorkspaceWalk = (cwd: string, signal: AbortSignal) => string[];
|
|
18
|
+
/**
|
|
19
|
+
* Per-command argument completer: given the current argument word (may be
|
|
20
|
+
* empty) and the request's AbortSignal, return candidate items. The provider
|
|
21
|
+
* prefix-filters the returned values against the argument word, so a completer
|
|
22
|
+
* may return the full candidate list and let the provider narrow it.
|
|
23
|
+
*/
|
|
24
|
+
export type ArgCompleter = (prefix: string, signal: AbortSignal) => AutocompleteItem[] | Promise<AutocompleteItem[]>;
|
|
25
|
+
/** Argument completers keyed by slash-command name (lowercase). */
|
|
26
|
+
export type ArgCompleterMap = Readonly<Record<string, ArgCompleter>>;
|
|
27
|
+
/**
|
|
28
|
+
* Autocomplete provider for the TUI composer. Handles `/command` suggestions
|
|
29
|
+
* (prefix-filtered from an injected catalog) and `@file` completion (fuzzy
|
|
30
|
+
* prefix match against a workspace walk rooted at `cwd`).
|
|
31
|
+
*/
|
|
32
|
+
export declare class TuiAutocompleteProvider implements AutocompleteProvider {
|
|
33
|
+
readonly triggerCharacters: string[];
|
|
34
|
+
private readonly commands;
|
|
35
|
+
private readonly cwd;
|
|
36
|
+
private readonly walk;
|
|
37
|
+
private readonly argCompleters;
|
|
38
|
+
constructor(commands: readonly CommandItem[], cwd: string, walk?: WorkspaceWalk, argCompleters?: ArgCompleterMap);
|
|
39
|
+
getSuggestions(lines: string[], cursorLine: number, cursorCol: number, options: {
|
|
40
|
+
signal: AbortSignal;
|
|
41
|
+
force?: boolean;
|
|
42
|
+
}): Promise<AutocompleteSuggestions | null>;
|
|
43
|
+
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
44
|
+
lines: string[];
|
|
45
|
+
cursorLine: number;
|
|
46
|
+
cursorCol: number;
|
|
47
|
+
};
|
|
48
|
+
shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/components/completion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,uBAAuB,EACxB,MAAM,gBAAgB,CAAA;AAGvB,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,KAChB,gBAAgB,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;AAErD,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAA;AAuHpE;;;;GAIG;AACH,qBAAa,uBAAwB,YAAW,oBAAoB;IAClE,QAAQ,CAAC,iBAAiB,WAAa;IAEvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;gBAG7C,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,aAA2B,EACjC,aAAa,GAAE,eAAoB;IAQ/B,cAAc,CAClB,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAChD,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAmE1C,eAAe,CACb,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IA+D7D,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;CAS7F"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI autocomplete provider: slash-command suggestions + `@`-file completion.
|
|
3
|
+
* Implements pi-tui's `AutocompleteProvider` interface so the vendored Editor
|
|
4
|
+
* can drive it via `setAutocompleteProvider`. Slash suggestions come from an
|
|
5
|
+
* injected command catalog (local + harness); `@`-paths come from OUR OWN
|
|
6
|
+
* workspace walk (recursive readdir, no `fd` dependency).
|
|
7
|
+
* @module @dsh-cc/tui/components/completion
|
|
8
|
+
*/
|
|
9
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
10
|
+
import { join, relative, sep } from 'node:path';
|
|
11
|
+
import { fuzzyFilter } from '@dsh-cc/pi-tui';
|
|
12
|
+
const MAX_WALK_RESULTS = 200;
|
|
13
|
+
const MAX_WALK_DEPTH = 8;
|
|
14
|
+
/** Directory names pruned from the walk unconditionally. */
|
|
15
|
+
const PRUNED_DIRS = new Set(['.git', 'node_modules']);
|
|
16
|
+
/**
|
|
17
|
+
* Path delimiters that bound a token. Mirrors pi-tui's PATH_DELIMITERS so token
|
|
18
|
+
* extraction is consistent with the vendored provider's expectations.
|
|
19
|
+
*/
|
|
20
|
+
const TOKEN_DELIMITERS = new Set([' ', '\t', '"', "'", '=']);
|
|
21
|
+
function isPathDelimiter(ch) {
|
|
22
|
+
return ch !== undefined && TOKEN_DELIMITERS.has(ch);
|
|
23
|
+
}
|
|
24
|
+
function toPosix(p) {
|
|
25
|
+
return p.split(sep).join('/');
|
|
26
|
+
}
|
|
27
|
+
/** Find the start of the token (after the last path/space delimiter). */
|
|
28
|
+
function tokenStart(text) {
|
|
29
|
+
for (let i = text.length - 1; i >= 0; i -= 1) {
|
|
30
|
+
if (isPathDelimiter(text[i]))
|
|
31
|
+
return i + 1;
|
|
32
|
+
}
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Recursive readdir walk from `cwd`, returning relative POSIX paths.
|
|
37
|
+
* Directory entries carry a trailing '/'. Prunes `.git` and `node_modules`;
|
|
38
|
+
* caps depth at {@link MAX_WALK_DEPTH} and total results at
|
|
39
|
+
* {@link MAX_WALK_RESULTS}. Honors the AbortSignal between directories.
|
|
40
|
+
* Dotfiles are included only when the user has already typed a leading dot in
|
|
41
|
+
* the prefix (so `@.` surfaces them but `@src` does not).
|
|
42
|
+
*/
|
|
43
|
+
function defaultWalk(cwd, signal) {
|
|
44
|
+
const results = [];
|
|
45
|
+
const walk = (dir, depth) => {
|
|
46
|
+
if (signal.aborted)
|
|
47
|
+
return;
|
|
48
|
+
if (results.length >= MAX_WALK_RESULTS)
|
|
49
|
+
return;
|
|
50
|
+
if (depth > MAX_WALK_DEPTH)
|
|
51
|
+
return;
|
|
52
|
+
let entries;
|
|
53
|
+
try {
|
|
54
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
if (signal.aborted)
|
|
61
|
+
return;
|
|
62
|
+
if (results.length >= MAX_WALK_RESULTS)
|
|
63
|
+
return;
|
|
64
|
+
if (PRUNED_DIRS.has(entry.name))
|
|
65
|
+
continue;
|
|
66
|
+
const full = join(dir, entry.name);
|
|
67
|
+
const rel = toPosix(relative(cwd, full));
|
|
68
|
+
if (entry.isDirectory()) {
|
|
69
|
+
results.push(`${rel}/`);
|
|
70
|
+
walk(full, depth + 1);
|
|
71
|
+
}
|
|
72
|
+
else if (entry.isSymbolicLink()) {
|
|
73
|
+
// Resolve the symlink target to classify dir-vs-file; follow with a
|
|
74
|
+
// guard against broken links (treat as a file if unreadable).
|
|
75
|
+
try {
|
|
76
|
+
if (statSync(full).isDirectory()) {
|
|
77
|
+
results.push(`${rel}/`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
results.push(rel);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
results.push(rel);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
results.push(rel);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
walk(cwd, 0);
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Filter a walk tree down to entries whose relative path starts with the given
|
|
97
|
+
* prefix (case-insensitive on the path, segment-aware). Directory entries are
|
|
98
|
+
* indicated by a trailing '/'. Returns AutocompleteItem values: directories
|
|
99
|
+
* keep the trailing '/' in both value and label; files get a trailing space
|
|
100
|
+
* via applyCompletion (not here). Directories sort before files.
|
|
101
|
+
*/
|
|
102
|
+
function buildPathItems(tree, rawPrefix, isAtPrefix) {
|
|
103
|
+
const prefixLower = rawPrefix.toLowerCase();
|
|
104
|
+
const matched = [];
|
|
105
|
+
for (const rel of tree) {
|
|
106
|
+
const posixRel = rel.replace(/\\/g, '/');
|
|
107
|
+
if (!posixRel.toLowerCase().startsWith(prefixLower))
|
|
108
|
+
continue;
|
|
109
|
+
const isDir = posixRel.endsWith('/');
|
|
110
|
+
const clean = isDir ? posixRel.slice(0, -1) : posixRel;
|
|
111
|
+
const name = clean.split('/').pop() ?? clean;
|
|
112
|
+
matched.push({ rel: posixRel, isDir, name });
|
|
113
|
+
}
|
|
114
|
+
matched.sort((a, b) => {
|
|
115
|
+
if (a.isDir && !b.isDir)
|
|
116
|
+
return -1;
|
|
117
|
+
if (!a.isDir && b.isDir)
|
|
118
|
+
return 1;
|
|
119
|
+
return a.rel.localeCompare(b.rel);
|
|
120
|
+
});
|
|
121
|
+
return matched.slice(0, MAX_WALK_RESULTS).map(({ rel, isDir, name }) => {
|
|
122
|
+
const pathValue = isDir ? rel : rel; // dir already has trailing '/'
|
|
123
|
+
return {
|
|
124
|
+
value: isAtPrefix ? `@${pathValue}` : pathValue,
|
|
125
|
+
label: isDir ? `${name}/` : name,
|
|
126
|
+
description: isDir ? rel.slice(0, -1) : rel,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Autocomplete provider for the TUI composer. Handles `/command` suggestions
|
|
132
|
+
* (prefix-filtered from an injected catalog) and `@file` completion (fuzzy
|
|
133
|
+
* prefix match against a workspace walk rooted at `cwd`).
|
|
134
|
+
*/
|
|
135
|
+
export class TuiAutocompleteProvider {
|
|
136
|
+
triggerCharacters = ['/', '@'];
|
|
137
|
+
commands;
|
|
138
|
+
cwd;
|
|
139
|
+
walk;
|
|
140
|
+
argCompleters;
|
|
141
|
+
constructor(commands, cwd, walk = defaultWalk, argCompleters = {}) {
|
|
142
|
+
this.commands = commands;
|
|
143
|
+
this.cwd = cwd;
|
|
144
|
+
this.walk = walk;
|
|
145
|
+
this.argCompleters = argCompleters;
|
|
146
|
+
}
|
|
147
|
+
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
148
|
+
const currentLine = lines[cursorLine] ?? '';
|
|
149
|
+
const before = currentLine.slice(0, cursorCol);
|
|
150
|
+
// --- @-file completion -------------------------------------------------
|
|
151
|
+
const start = tokenStart(before);
|
|
152
|
+
const token = before.slice(start);
|
|
153
|
+
if (token.startsWith('@')) {
|
|
154
|
+
const rawPrefix = token.slice(1); // strip '@'
|
|
155
|
+
// The walk itself honors the signal; call it even when already aborted so
|
|
156
|
+
// the provider reports a clean null (and tests can observe the call).
|
|
157
|
+
const tree = this.walk(this.cwd, options.signal);
|
|
158
|
+
if (options.signal.aborted)
|
|
159
|
+
return null;
|
|
160
|
+
const items = buildPathItems(tree, rawPrefix, true);
|
|
161
|
+
if (items.length === 0)
|
|
162
|
+
return null;
|
|
163
|
+
return { items, prefix: token };
|
|
164
|
+
}
|
|
165
|
+
// --- /command completion ----------------------------------------------
|
|
166
|
+
// Only at the start of the first line, with no space yet.
|
|
167
|
+
if (cursorLine === 0 && before.startsWith('/') && !before.includes(' ')) {
|
|
168
|
+
const cmdPrefix = before.slice(1);
|
|
169
|
+
const filtered = fuzzyFilter(this.commands.map(c => ({ name: c.name, description: c.description, argumentHint: c.argumentHint })), cmdPrefix, c => c.name);
|
|
170
|
+
const items = filtered.map(c => {
|
|
171
|
+
const hint = c.argumentHint;
|
|
172
|
+
const desc = c.description ?? '';
|
|
173
|
+
const fullDesc = hint ? (desc ? `${hint} — ${desc}` : hint) : desc;
|
|
174
|
+
return {
|
|
175
|
+
value: c.name,
|
|
176
|
+
label: c.name,
|
|
177
|
+
...fullDesc.length === 0 ? {} : { description: fullDesc },
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
if (items.length === 0)
|
|
181
|
+
return null;
|
|
182
|
+
return { items, prefix: before };
|
|
183
|
+
}
|
|
184
|
+
// --- /command argument completion --------------------------------------
|
|
185
|
+
// The editor's trigger pattern requires the trigger token to end the line,
|
|
186
|
+
// so it never fires once a space follows the command — argument suggestions
|
|
187
|
+
// are reachable only through explicit Tab. Dispatch to the per-command
|
|
188
|
+
// completer when one is registered; without one, stay at null (argument
|
|
189
|
+
// territory keeps the pre-completion behavior). An argument token starting
|
|
190
|
+
// with '@' never reaches here: the @-file branch above wins by design.
|
|
191
|
+
if (cursorLine === 0) {
|
|
192
|
+
const argMatch = /^\/(\S+)\s(.*)$/.exec(before);
|
|
193
|
+
if (argMatch !== null) {
|
|
194
|
+
const completer = this.argCompleters[argMatch[1].toLowerCase()];
|
|
195
|
+
if (completer === undefined)
|
|
196
|
+
return null;
|
|
197
|
+
const args = argMatch[2];
|
|
198
|
+
const argPrefix = args.slice(tokenStart(args));
|
|
199
|
+
const candidates = await completer(argPrefix, options.signal);
|
|
200
|
+
if (options.signal.aborted)
|
|
201
|
+
return null;
|
|
202
|
+
const prefixLower = argPrefix.toLowerCase();
|
|
203
|
+
const items = candidates.filter(item => item.value.toLowerCase().startsWith(prefixLower));
|
|
204
|
+
if (items.length === 0)
|
|
205
|
+
return null;
|
|
206
|
+
return { items, prefix: argPrefix };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
212
|
+
const currentLine = lines[cursorLine] ?? '';
|
|
213
|
+
const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
|
|
214
|
+
const afterCursor = currentLine.slice(cursorCol);
|
|
215
|
+
// Slash command: replace `/partial` with `/name` + a separating space
|
|
216
|
+
// (but only if the text after the cursor doesn't already begin with one,
|
|
217
|
+
// to avoid doubling spaces when completing mid-line).
|
|
218
|
+
if (prefix.startsWith('/') && beforePrefix.trim() === '') {
|
|
219
|
+
const separator = afterCursor.startsWith(' ') || afterCursor.startsWith('\t') ? '' : ' ';
|
|
220
|
+
const newLine = `${beforePrefix}/${item.value}${separator}${afterCursor}`;
|
|
221
|
+
const newLines = [...lines];
|
|
222
|
+
newLines[cursorLine] = newLine;
|
|
223
|
+
return {
|
|
224
|
+
lines: newLines,
|
|
225
|
+
cursorLine,
|
|
226
|
+
cursorCol: beforePrefix.length + 1 + item.value.length + separator.length, // +1 for '/'
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// @-file: replace `@partial` with `@relpath` (+ trailing '/' kept open for dirs,
|
|
230
|
+
// + trailing space for files).
|
|
231
|
+
if (prefix.startsWith('@')) {
|
|
232
|
+
const isDir = item.label.endsWith('/');
|
|
233
|
+
const suffix = isDir ? '' : ' ';
|
|
234
|
+
const newLine = `${beforePrefix}${item.value}${suffix}${afterCursor}`;
|
|
235
|
+
const newLines = [...lines];
|
|
236
|
+
newLines[cursorLine] = newLine;
|
|
237
|
+
return {
|
|
238
|
+
lines: newLines,
|
|
239
|
+
cursorLine,
|
|
240
|
+
cursorCol: beforePrefix.length + item.value.length + suffix.length,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// Slash-command argument: replace only the current argument word, keeping
|
|
244
|
+
// the command name and everything before it intact. The prefix is exactly
|
|
245
|
+
// the word being replaced (see the argument branch in getSuggestions), so
|
|
246
|
+
// the generic before/after slicing already preserves the command; only the
|
|
247
|
+
// separator differs from plain insertion.
|
|
248
|
+
if (cursorLine === 0 && currentLine.startsWith('/') && beforePrefix.includes(' ')) {
|
|
249
|
+
const separator = afterCursor.startsWith(' ') || afterCursor.startsWith('\t') ? '' : ' ';
|
|
250
|
+
const newLine = `${beforePrefix}${item.value}${separator}${afterCursor}`;
|
|
251
|
+
const newLines = [...lines];
|
|
252
|
+
newLines[cursorLine] = newLine;
|
|
253
|
+
return {
|
|
254
|
+
lines: newLines,
|
|
255
|
+
cursorLine,
|
|
256
|
+
cursorCol: beforePrefix.length + item.value.length + separator.length,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
// Fallback: plain insertion.
|
|
260
|
+
const newLine = `${beforePrefix}${item.value}${afterCursor}`;
|
|
261
|
+
const newLines = [...lines];
|
|
262
|
+
newLines[cursorLine] = newLine;
|
|
263
|
+
return {
|
|
264
|
+
lines: newLines,
|
|
265
|
+
cursorLine,
|
|
266
|
+
cursorCol: beforePrefix.length + item.value.length,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
|
|
270
|
+
const currentLine = lines[cursorLine] ?? '';
|
|
271
|
+
const before = currentLine.slice(0, cursorCol);
|
|
272
|
+
// Don't force-trigger while typing a slash command at the start of a line.
|
|
273
|
+
if (before.trimStart().startsWith('/') && !before.trimStart().includes(' ')) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/components/completion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAM/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AA0B5C,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAC5B,MAAM,cAAc,GAAG,CAAC,CAAA;AAExB,4DAA4D;AAC5D,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAErD;;;GAGG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAE5D,SAAS,eAAe,CAAC,EAAsB;IAC7C,OAAO,EAAE,KAAK,SAAS,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AACrD,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC/B,CAAC;AAED,yEAAyE;AACzE,SAAS,UAAU,CAAC,IAAY;IAC9B,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IAC5C,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAAC,GAAW,EAAE,MAAmB;IACnD,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,KAAa,EAAQ,EAAE;QAChD,IAAI,MAAM,CAAC,OAAO;YAAE,OAAM;QAC1B,IAAI,OAAO,CAAC,MAAM,IAAI,gBAAgB;YAAE,OAAM;QAC9C,IAAI,KAAK,GAAG,cAAc;YAAE,OAAM;QAClC,IAAI,OAAO,CAAA;QACX,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,OAAM;QACR,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAM;YAC1B,IAAI,OAAO,CAAC,MAAM,IAAI,gBAAgB;gBAAE,OAAM;YAC9C,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,SAAQ;YACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YAClC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;gBACvB,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;YACvB,CAAC;iBAAM,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;gBAClC,oEAAoE;gBACpE,8DAA8D;gBAC9D,IAAI,CAAC;oBACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;wBACjC,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;oBACzB,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;oBACnB,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACnB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACnB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACZ,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CACrB,IAAuB,EACvB,SAAiB,EACjB,UAAmB;IAEnB,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;IAC3C,MAAM,OAAO,GAAoD,EAAE,CAAA;IACnE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QACxC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,SAAQ;QAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;QACtD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpB,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC,CAAA;QAClC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,CAAA;QACjC,OAAO,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IACF,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE;QACrE,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA,CAAC,+BAA+B;QACnE,OAAO;YACL,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;YAC/C,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI;YAChC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;SAC5C,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IACzB,iBAAiB,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAEtB,QAAQ,CAAwB;IAChC,GAAG,CAAQ;IACX,IAAI,CAAe;IACnB,aAAa,CAAiB;IAE/C,YACE,QAAgC,EAChC,GAAW,EACX,OAAsB,WAAW,EACjC,gBAAiC,EAAE;QAEnC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,KAAe,EACf,UAAkB,EAClB,SAAiB,EACjB,OAAiD;QAEjD,MAAM,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;QAC3C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;QAE9C,0EAA0E;QAC1E,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,CAAC,YAAY;YAC7C,0EAA0E;YAC1E,sEAAsE;YACtE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;YAChD,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAA;YACvC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;YACnD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;YACnC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QACjC,CAAC;QAED,yEAAyE;QACzE,0DAA0D;QAC1D,IAAI,UAAU,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxE,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACjC,MAAM,QAAQ,GAAG,WAAW,CAC1B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,EACpG,SAAS,EACT,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACZ,CAAA;YACD,MAAM,KAAK,GAAuB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBACjD,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAA;gBAC3B,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,IAAI,EAAE,CAAA;gBAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;gBAClE,OAAO;oBACL,KAAK,EAAE,CAAC,CAAC,IAAI;oBACb,KAAK,EAAE,CAAC,CAAC,IAAI;oBACb,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE;iBAC1D,CAAA;YACH,CAAC,CAAC,CAAA;YACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;YACnC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;QAClC,CAAC;QAED,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,uEAAuE;QACvE,wEAAwE;QACxE,2EAA2E;QAC3E,uEAAuE;QACvE,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC/C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACtB,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC,CAAA;gBAChE,IAAI,SAAS,KAAK,SAAS;oBAAE,OAAO,IAAI,CAAA;gBACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAA;gBACzB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;gBAC9C,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;gBAC7D,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO,IAAI,CAAA;gBACvC,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;gBAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAA;gBACzF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,IAAI,CAAA;gBACnC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;YACrC,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAED,eAAe,CACb,KAAe,EACf,UAAkB,EAClB,SAAiB,EACjB,IAAsB,EACtB,MAAc;QAEd,MAAM,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;QAC3C,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;QACpE,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAEhD,sEAAsE;QACtE,yEAAyE;QACzE,sDAAsD;QACtD,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACzD,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;YACxF,MAAM,OAAO,GAAG,GAAG,YAAY,IAAI,IAAI,CAAC,KAAK,GAAG,SAAS,GAAG,WAAW,EAAE,CAAA;YACzE,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;YAC3B,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAA;YAC9B,OAAO;gBACL,KAAK,EAAE,QAAQ;gBACf,UAAU;gBACV,SAAS,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,aAAa;aACzF,CAAA;QACH,CAAC;QAED,iFAAiF;QACjF,+BAA+B;QAC/B,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;YACtC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;YAC/B,MAAM,OAAO,GAAG,GAAG,YAAY,GAAG,IAAI,CAAC,KAAK,GAAG,MAAM,GAAG,WAAW,EAAE,CAAA;YACrE,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;YAC3B,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAA;YAC9B,OAAO;gBACL,KAAK,EAAE,QAAQ;gBACf,UAAU;gBACV,SAAS,EAAE,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;aACnE,CAAA;QACH,CAAC;QAED,0EAA0E;QAC1E,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,0CAA0C;QAC1C,IAAI,UAAU,KAAK,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAClF,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;YACxF,MAAM,OAAO,GAAG,GAAG,YAAY,GAAG,IAAI,CAAC,KAAK,GAAG,SAAS,GAAG,WAAW,EAAE,CAAA;YACxE,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;YAC3B,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAA;YAC9B,OAAO;gBACL,KAAK,EAAE,QAAQ;gBACf,UAAU;gBACV,SAAS,EAAE,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM;aACtE,CAAA;QACH,CAAC;QAED,6BAA6B;QAC7B,MAAM,OAAO,GAAG,GAAG,YAAY,GAAG,IAAI,CAAC,KAAK,GAAG,WAAW,EAAE,CAAA;QAC5D,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;QAC3B,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAA;QAC9B,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,UAAU;YACV,SAAS,EAAE,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;SACnD,CAAA;IACH,CAAC;IAED,2BAA2B,CAAC,KAAe,EAAE,UAAkB,EAAE,SAAiB;QAChF,MAAM,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;QAC3C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;QAC9C,2EAA2E;QAC3E,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5E,OAAO,KAAK,CAAA;QACd,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render structured FileDiff data as ANSI-styled hunk lines for the
|
|
3
|
+
* transcript. Hunks come from {@link computeHunks} (line-level LCS with a
|
|
4
|
+
* size-capped prefix/suffix fallback). Large unchanged runs between hunks
|
|
5
|
+
* collapse to a single dim `… N unchanged lines …` marker; small runs render
|
|
6
|
+
* as dim context so adjacent hunks read as one cluster. Changed lines carry a
|
|
7
|
+
* right-aligned gutter line number (old-side number on `-` lines, new-side on
|
|
8
|
+
* `+` lines). Output is capped so a single tool row never blows the
|
|
9
|
+
* transcript line budget, and truncation respects hunk boundaries: a hunk
|
|
10
|
+
* that fits in the remaining budget is never split in half.
|
|
11
|
+
* @module @dsh-cc/tui/components/diff-card
|
|
12
|
+
*/
|
|
13
|
+
import type { FileDiff } from '../tool-card.ts';
|
|
14
|
+
import { type Theme } from './theme.ts';
|
|
15
|
+
/** Default cap on emitted lines for a single tool row's diffs. */
|
|
16
|
+
export declare const DEFAULT_DIFF_LINE_CAP = 60;
|
|
17
|
+
/**
|
|
18
|
+
* Render FileDiff[] as ANSI-styled lines: a bold per-file header with a tag,
|
|
19
|
+
* then gutter-numbered removals (error role) and additions (success role)
|
|
20
|
+
* grouped into hunks, with muted context and `… N unchanged lines …` collapse
|
|
21
|
+
* markers between them. Capped at `maxLines` (default 60); a muted trailer is
|
|
22
|
+
* appended when content is cut, and the cut lands on a hunk boundary.
|
|
23
|
+
*
|
|
24
|
+
* Indentation (two leading spaces) is applied here so callers can drop the
|
|
25
|
+
* returned lines directly beneath a tool row's head line. Styles come from
|
|
26
|
+
* the injected `theme` (default: the built-in palette).
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderDiffLines(diffs: readonly FileDiff[], maxLines?: number, theme?: Theme): string[];
|
|
29
|
+
//# sourceMappingURL=diff-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-card.d.ts","sourceRoot":"","sources":["../../src/components/diff-card.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE/C,OAAO,EAAgB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAA;AAErD,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,KAAK,CAAA;AA4JvC;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,QAAQ,GAAE,MAA8B,EACxC,KAAK,GAAE,KAAoB,GAC1B,MAAM,EAAE,CAaV"}
|