@bastani/atomic 0.9.11-alpha.9 → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/README.md +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/subagent-relay.ts +8 -5
- package/dist/builtin/mcp/CHANGELOG.md +10 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +20 -0
- package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
- package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/notification-content.ts +14 -9
- package/dist/builtin/subagents/src/runs/background/notify.ts +20 -4
- package/dist/builtin/web-access/CHANGELOG.md +20 -0
- package/dist/builtin/web-access/content-tools.ts +1 -0
- package/dist/builtin/web-access/flat-string.ts +37 -0
- package/dist/builtin/web-access/github-api.ts +2 -1
- package/dist/builtin/web-access/github-extract.ts +3 -2
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +57 -0
- package/dist/builtin/workflows/builtin/goal-ledger.ts +6 -4
- package/dist/builtin/workflows/builtin/goal-runner.ts +2 -1
- package/dist/builtin/workflows/builtin/goal.ts +1 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +8 -9
- package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +2 -4
- package/dist/builtin/workflows/builtin/ralph-models.ts +4 -4
- package/dist/builtin/workflows/builtin/ralph-runner.ts +5 -8
- package/dist/builtin/workflows/builtin/ralph.ts +2 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +14 -8
- package/dist/builtin/workflows/src/durable/completed-inspection.ts +10 -11
- package/dist/builtin/workflows/src/durable/resume-catalog.ts +1 -1
- package/dist/builtin/workflows/src/durable/resume-eligibility.ts +42 -0
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +36 -37
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +10 -2
- package/dist/builtin/workflows/src/engine/run.ts +1 -0
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +12 -8
- package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +5 -2
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1966 -880
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +6 -3
- package/dist/builtin/workflows/src/extension/postmortem-deps.ts +3 -2
- package/dist/builtin/workflows/src/extension/render-result.ts +4 -11
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +17 -20
- package/dist/builtin/workflows/src/extension/runtime.ts +3 -3
- package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +1 -1
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +3 -7
- package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +21 -24
- package/dist/builtin/workflows/src/extension/workflow-resume-picker-rows.ts +38 -26
- package/dist/builtin/workflows/src/extension/workflow-resume-shadow.ts +2 -1
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +56 -64
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -2
- package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +0 -5
- package/dist/builtin/workflows/src/extension/workflow-targets.ts +17 -20
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +1 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +24 -51
- package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +7 -29
- package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +5 -13
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +4 -13
- package/dist/builtin/workflows/src/runs/background/quit.ts +4 -3
- package/dist/builtin/workflows/src/runs/background/run-inspect.ts +8 -8
- package/dist/builtin/workflows/src/runs/background/status.ts +2 -1
- package/dist/builtin/workflows/src/runs/background/workflow-lifecycle-aggregate.ts +2 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
- package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +23 -8
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +6 -2
- package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +5 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +56 -10
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +6 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +137 -6
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +2 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +2 -0
- package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +159 -10
- package/dist/builtin/workflows/src/shared/flat-string.ts +33 -0
- package/dist/builtin/workflows/src/shared/graph-store-snapshot.ts +144 -0
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +2 -3
- package/dist/builtin/workflows/src/shared/run-id.ts +36 -0
- package/dist/builtin/workflows/src/shared/store-internal.ts +32 -3
- package/dist/builtin/workflows/src/shared/store-observation.ts +10 -0
- package/dist/builtin/workflows/src/shared/store-public-types.ts +8 -0
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +14 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +4 -0
- package/dist/builtin/workflows/src/shared/types.ts +2 -0
- package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +261 -0
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +39 -23
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +26 -9
- package/dist/builtin/workflows/src/tui/graph-view-constants.ts +3 -0
- package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +95 -98
- package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -20
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +160 -13
- package/dist/builtin/workflows/src/tui/layout.ts +8 -2
- package/dist/builtin/workflows/src/tui/node-card.ts +57 -45
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -2
- package/dist/builtin/workflows/src/tui/prompt-card-render.ts +291 -21
- package/dist/builtin/workflows/src/tui/prompt-card.ts +1 -1
- package/dist/builtin/workflows/src/tui/renderers.ts +0 -1
- package/dist/builtin/workflows/src/tui/run-detail.ts +52 -35
- package/dist/builtin/workflows/src/tui/run-identity-rows.ts +97 -0
- package/dist/builtin/workflows/src/tui/session-overlays.ts +24 -3
- package/dist/builtin/workflows/src/tui/session-picker.ts +94 -41
- package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +190 -26
- package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +52 -18
- package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +9 -5
- package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +44 -4
- package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +3 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -2
- package/dist/builtin/workflows/src/tui/status-list.ts +67 -45
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -6
- package/dist/builtin/workflows/src/tui/widget.ts +49 -40
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +15 -10
- package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +21 -8
- package/dist/core/agent-session-accessors.d.ts.map +1 -1
- package/dist/core/agent-session-accessors.js +5 -0
- package/dist/core/agent-session-accessors.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +5 -0
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-compaction.js +3 -0
- package/dist/core/agent-session-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +2 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-message-queue.d.ts +3 -7
- package/dist/core/agent-session-message-queue.d.ts.map +1 -1
- package/dist/core/agent-session-message-queue.js +27 -13
- package/dist/core/agent-session-message-queue.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +5 -3
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +6 -3
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-post-tool-compaction.js +11 -2
- package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +5 -3
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +4 -0
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +4 -0
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage-backends.d.ts.map +1 -1
- package/dist/core/auth-storage-backends.js +12 -1
- package/dist/core/auth-storage-backends.js.map +1 -1
- package/dist/core/bounded-model-refresh.d.ts +14 -0
- package/dist/core/bounded-model-refresh.d.ts.map +1 -0
- package/dist/core/bounded-model-refresh.js +38 -0
- package/dist/core/bounded-model-refresh.js.map +1 -0
- package/dist/core/context-tool-pairing.d.ts +4 -4
- package/dist/core/context-tool-pairing.js +4 -4
- package/dist/core/context-tool-pairing.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +26 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/messages.d.ts +18 -6
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +46 -18
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-refresh-timeout.d.ts +9 -0
- package/dist/core/model-refresh-timeout.d.ts.map +1 -0
- package/dist/core/model-refresh-timeout.js +9 -0
- package/dist/core/model-refresh-timeout.js.map +1 -0
- package/dist/core/model-runtime-snapshot.d.ts +3 -0
- package/dist/core/model-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/model-runtime-snapshot.js +59 -0
- package/dist/core/model-runtime-snapshot.js.map +1 -1
- package/dist/core/model-runtime.d.ts +7 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +72 -17
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +4 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +1 -1
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +2 -2
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +1 -1
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +2 -1
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-history.d.ts.map +1 -1
- package/dist/core/session-manager-history.js +2 -2
- package/dist/core/session-manager-history.js.map +1 -1
- package/dist/core/session-manager-types.d.ts +3 -0
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main-app-mode.d.ts +1 -0
- package/dist/main-app-mode.d.ts.map +1 -1
- package/dist/main-app-mode.js +3 -0
- package/dist/main-app-mode.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +8 -3
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.d.ts +7 -2
- package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js +13 -4
- package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-editor.d.ts +3 -1
- package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-editor.js +2 -2
- package/dist/modes/interactive/components/chat-session-host-editor.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts +3 -0
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +5 -4
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +16 -2
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +35 -5
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +0 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +25 -35
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.js +2 -1
- package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +1 -1
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +4 -1
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-pause.d.ts +3 -1
- package/dist/modes/interactive/interactive-pause.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-pause.js +5 -1
- package/dist/modes/interactive/interactive-pause.js.map +1 -1
- package/dist/modes/interactive/interactive-queueing.js +3 -3
- package/dist/modes/interactive/interactive-queueing.js.map +1 -1
- package/dist/modes/interactive-engine/engine-health.d.ts +6 -0
- package/dist/modes/interactive-engine/engine-health.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-health.js +15 -1
- package/dist/modes/interactive-engine/engine-health.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts +1 -0
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +7 -1
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js +5 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +23 -7
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +3 -1
- package/docs/index.md +10 -0
- package/docs/keybindings.md +1 -1
- package/docs/models.md +2 -0
- package/docs/providers.md +7 -3
- package/docs/quickstart.md +7 -1
- package/docs/session-format.md +2 -0
- package/docs/termux.md +3 -1
- package/docs/usage.md +3 -2
- package/docs/workflows.md +82 -46
- package/npm-shrinkwrap.json +59 -27
- package/package.json +3 -3
- package/dist/builtin/workflows/src/tui/toast.ts +0 -107
|
@@ -6,41 +6,273 @@ import type { GraphTheme } from "./graph-theme.js";
|
|
|
6
6
|
import { createPromptSelectList } from "./prompt-card-select.js";
|
|
7
7
|
import type { PromptCardState } from "./prompt-card-state.js";
|
|
8
8
|
import { graphemeParts } from "./prompt-card-text.js";
|
|
9
|
+
import { renderRunIdentityRows } from "./run-identity-rows.js";
|
|
10
|
+
import { statusColor, statusIcon } from "./status-helpers.js";
|
|
11
|
+
|
|
12
|
+
export interface PromptCardIdentity {
|
|
13
|
+
readonly runId: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly meta?: string;
|
|
16
|
+
}
|
|
9
17
|
|
|
10
18
|
export interface PromptCardRenderOpts {
|
|
11
19
|
readonly state: PromptCardState;
|
|
12
20
|
readonly theme: GraphTheme;
|
|
13
21
|
readonly width: number;
|
|
14
22
|
readonly cursorOn: boolean;
|
|
23
|
+
/** Optional run attribution; absent preserves the legacy prompt card. */
|
|
24
|
+
readonly identity?: PromptCardIdentity;
|
|
25
|
+
/** Maximum rows to emit. Small budgets use a reduced, closed prompt surface. */
|
|
26
|
+
readonly maxRows?: number;
|
|
27
|
+
/** Wrapped question-row offset used by scrollable attached prompt surfaces. */
|
|
28
|
+
readonly messageOffset?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PromptCardLayout {
|
|
32
|
+
readonly lines: string[];
|
|
33
|
+
readonly totalQuestionRows: number;
|
|
34
|
+
readonly visibleQuestionRows: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function renderPromptIdentityBanner(identity: PromptCardIdentity, theme: GraphTheme, width: number): string[] {
|
|
38
|
+
const innerWidth = Math.max(20, width - 2);
|
|
39
|
+
const borderColor = theme.border;
|
|
40
|
+
const bg = "";
|
|
41
|
+
const identityRows = renderRunIdentityRows({
|
|
42
|
+
runId: identity.runId,
|
|
43
|
+
name: identity.name,
|
|
44
|
+
meta: identity.meta,
|
|
45
|
+
glyph: statusIcon("awaiting_input"),
|
|
46
|
+
glyphColor: statusColor("awaiting_input", theme),
|
|
47
|
+
theme,
|
|
48
|
+
width: innerWidth,
|
|
49
|
+
idIndent: 1,
|
|
50
|
+
idGap: 1,
|
|
51
|
+
nameIndent: 4,
|
|
52
|
+
});
|
|
53
|
+
return [
|
|
54
|
+
makeBorderTop(borderColor, " AWAITING INPUT ", theme, innerWidth, bg),
|
|
55
|
+
...identityRows.map((row) => makePaddedRow(bg, borderColor, innerWidth, row)),
|
|
56
|
+
makeBorderBottom(borderColor, innerWidth, bg),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function renderPromptRunIdBanner(identity: PromptCardIdentity, theme: GraphTheme, width: number): string[] {
|
|
61
|
+
const banner = renderPromptIdentityBanner(identity, theme, width);
|
|
62
|
+
// The canonical identity renderer appends the workflow-name row last, after
|
|
63
|
+
// every wrapped run-id row. Remove only that row for the middle ladder rung.
|
|
64
|
+
return [...banner.slice(0, -2), banner.at(-1)!];
|
|
15
65
|
}
|
|
16
66
|
|
|
67
|
+
const STANDARD_PROMPT_FIXED_ROWS = 5;
|
|
68
|
+
const NORMAL_SPACING_ROWS = 3;
|
|
69
|
+
|
|
17
70
|
/**
|
|
18
|
-
* Render the prompt
|
|
19
|
-
*
|
|
71
|
+
* Render the prompt surface as an optional attribution banner followed by
|
|
72
|
+
* the existing prompt UI. The banner contains identity only; the prompt
|
|
73
|
+
* question, response field, and hints remain in their existing box.
|
|
20
74
|
*/
|
|
21
75
|
export function renderPromptCard(opts: PromptCardRenderOpts): string[] {
|
|
76
|
+
return renderPromptCardLayout(opts).lines;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Render a card and report its scrollable question window in question-row units. */
|
|
80
|
+
export function renderPromptCardLayout(opts: PromptCardRenderOpts): PromptCardLayout {
|
|
22
81
|
const { state, theme, width } = opts;
|
|
23
82
|
const innerWidth = Math.max(20, width - 2);
|
|
24
83
|
const borderColor = theme.border;
|
|
25
84
|
const bg = "";
|
|
85
|
+
const maxRows = opts.maxRows === undefined ? undefined : Math.max(0, Math.floor(opts.maxRows));
|
|
86
|
+
const messageOffset = Math.max(0, Math.floor(opts.messageOffset ?? 0));
|
|
87
|
+
const unattributed = renderPromptBodyBlock(
|
|
88
|
+
state,
|
|
89
|
+
theme,
|
|
90
|
+
innerWidth,
|
|
91
|
+
opts.cursorOn,
|
|
92
|
+
borderColor,
|
|
93
|
+
bg,
|
|
94
|
+
maxRows,
|
|
95
|
+
messageOffset,
|
|
96
|
+
);
|
|
97
|
+
if (opts.identity === undefined) return unattributed;
|
|
98
|
+
|
|
99
|
+
const banners = [
|
|
100
|
+
renderPromptIdentityBanner(opts.identity, theme, width),
|
|
101
|
+
renderPromptRunIdBanner(opts.identity, theme, width),
|
|
102
|
+
];
|
|
103
|
+
for (const banner of banners) {
|
|
104
|
+
const promptBudget = maxRows === undefined ? undefined : Math.max(0, maxRows - banner.length);
|
|
105
|
+
const attributedPrompt = renderPromptBodyBlock(
|
|
106
|
+
state,
|
|
107
|
+
theme,
|
|
108
|
+
innerWidth,
|
|
109
|
+
opts.cursorOn,
|
|
110
|
+
borderColor,
|
|
111
|
+
bg,
|
|
112
|
+
promptBudget,
|
|
113
|
+
messageOffset,
|
|
114
|
+
);
|
|
115
|
+
const minimumQuestionRows = attributedPrompt.totalQuestionRows > 0 ? 1 : 0;
|
|
116
|
+
const bannerFits =
|
|
117
|
+
maxRows === undefined ||
|
|
118
|
+
(banner.length + minimumCompletePromptRows(state) <= maxRows &&
|
|
119
|
+
attributedPrompt.visibleQuestionRows >= minimumQuestionRows &&
|
|
120
|
+
attributedPrompt.visibleQuestionRows >= unattributed.visibleQuestionRows);
|
|
121
|
+
if (!bannerFits) continue;
|
|
26
122
|
|
|
123
|
+
return {
|
|
124
|
+
lines: [
|
|
125
|
+
...banner,
|
|
126
|
+
...attributedPrompt.lines.map((line, index) =>
|
|
127
|
+
index === 0 ? makeBorderTop(borderColor, "", theme, innerWidth, bg) : line,
|
|
128
|
+
),
|
|
129
|
+
],
|
|
130
|
+
totalQuestionRows: attributedPrompt.totalQuestionRows,
|
|
131
|
+
visibleQuestionRows: attributedPrompt.visibleQuestionRows,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return unattributed;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function renderPromptBodyBlock(
|
|
138
|
+
state: PromptCardState,
|
|
139
|
+
theme: GraphTheme,
|
|
140
|
+
innerWidth: number,
|
|
141
|
+
cursorOn: boolean,
|
|
142
|
+
borderColor: string,
|
|
143
|
+
bg: string,
|
|
144
|
+
maxRows: number | undefined,
|
|
145
|
+
messageOffset: number,
|
|
146
|
+
): PromptCardLayout {
|
|
147
|
+
const messageRows = wrapText(state.prompt.message, innerWidth - 4);
|
|
148
|
+
const totalQuestionRows = messageRows.length;
|
|
149
|
+
if (maxRows !== undefined && maxRows === 0) {
|
|
150
|
+
return { lines: [], totalQuestionRows, visibleQuestionRows: 0 };
|
|
151
|
+
}
|
|
152
|
+
if (maxRows !== undefined && maxRows < minimumCompletePromptRows(state)) {
|
|
153
|
+
return renderReducedPromptBlock(state, theme, innerWidth, borderColor, bg, maxRows, messageRows, messageOffset);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const desiredResponseRows = responseRowCount(state);
|
|
157
|
+
const minimumResponseRows = minimumResponseRowCount(state);
|
|
158
|
+
const normallySpaced =
|
|
159
|
+
maxRows === undefined ||
|
|
160
|
+
maxRows >= STANDARD_PROMPT_FIXED_ROWS + NORMAL_SPACING_ROWS + desiredResponseRows + totalQuestionRows;
|
|
161
|
+
let messageCount = totalQuestionRows;
|
|
162
|
+
let responseCount = desiredResponseRows;
|
|
163
|
+
if (maxRows !== undefined) {
|
|
164
|
+
const fixedRows = STANDARD_PROMPT_FIXED_ROWS + (normallySpaced ? NORMAL_SPACING_ROWS : 0);
|
|
165
|
+
const variableBudget = maxRows - fixedRows;
|
|
166
|
+
messageCount = totalQuestionRows > 0 ? 1 : 0;
|
|
167
|
+
responseCount = minimumResponseRows;
|
|
168
|
+
let remaining = variableBudget - messageCount - responseCount;
|
|
169
|
+
const responseExtra = Math.min(remaining, Math.max(0, desiredResponseRows - responseCount));
|
|
170
|
+
responseCount += responseExtra;
|
|
171
|
+
remaining -= responseExtra;
|
|
172
|
+
messageCount += Math.min(remaining, Math.max(0, totalQuestionRows - messageCount));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const safeMessageOffset = Math.min(messageOffset, Math.max(0, totalQuestionRows - messageCount));
|
|
27
176
|
const lines: string[] = [];
|
|
28
177
|
lines.push(makeBorderTop(borderColor, " AWAITING INPUT ", theme, innerWidth, bg));
|
|
29
|
-
lines.push(makePaddedRow(bg, borderColor, innerWidth, ""));
|
|
30
|
-
for (const messageLine of
|
|
178
|
+
if (normallySpaced) lines.push(makePaddedRow(bg, borderColor, innerWidth, ""));
|
|
179
|
+
for (const messageLine of messageRows.slice(safeMessageOffset, safeMessageOffset + messageCount)) {
|
|
31
180
|
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${paint(messageLine, theme.text)}`));
|
|
32
181
|
}
|
|
33
|
-
lines.push(makePaddedRow(bg, borderColor, innerWidth, ""));
|
|
182
|
+
if (normallySpaced) lines.push(makePaddedRow(bg, borderColor, innerWidth, ""));
|
|
34
183
|
|
|
35
|
-
const fieldLines = renderResponseFieldBox(state, theme, innerWidth - 4,
|
|
36
|
-
for (const
|
|
37
|
-
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${
|
|
184
|
+
const fieldLines = renderResponseFieldBox(state, theme, innerWidth - 4, cursorOn, responseCount);
|
|
185
|
+
for (const fieldLine of fieldLines) {
|
|
186
|
+
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${fieldLine}`));
|
|
38
187
|
}
|
|
39
188
|
|
|
40
|
-
lines.push(makePaddedRow(bg, borderColor, innerWidth, ""));
|
|
189
|
+
if (normallySpaced) lines.push(makePaddedRow(bg, borderColor, innerWidth, ""));
|
|
41
190
|
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${renderHints(state.prompt.kind, theme)}`));
|
|
42
191
|
lines.push(makeBorderBottom(borderColor, innerWidth, bg));
|
|
43
|
-
return lines;
|
|
192
|
+
return { lines, totalQuestionRows, visibleQuestionRows: messageCount };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function minimumCompletePromptRows(state: PromptCardState): number {
|
|
196
|
+
return STANDARD_PROMPT_FIXED_ROWS + minimumResponseRowCount(state) + 1;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function renderReducedPromptBlock(
|
|
200
|
+
state: PromptCardState,
|
|
201
|
+
theme: GraphTheme,
|
|
202
|
+
innerWidth: number,
|
|
203
|
+
borderColor: string,
|
|
204
|
+
bg: string,
|
|
205
|
+
maxRows: number,
|
|
206
|
+
messageRows: readonly string[],
|
|
207
|
+
messageOffset: number,
|
|
208
|
+
): PromptCardLayout {
|
|
209
|
+
const totalQuestionRows = messageRows.length;
|
|
210
|
+
const compactHint = compactSurfaceHint(state.prompt.kind);
|
|
211
|
+
if (maxRows === 1) {
|
|
212
|
+
return {
|
|
213
|
+
lines: [paint(`AWAITING INPUT · ${compactHint}`, theme.textMuted, { bold: true })],
|
|
214
|
+
totalQuestionRows,
|
|
215
|
+
visibleQuestionRows: 0,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (maxRows === 2) {
|
|
219
|
+
return {
|
|
220
|
+
lines: [
|
|
221
|
+
makeBorderTop(borderColor, ` AWAITING INPUT · ${compactHint} `, theme, innerWidth, bg),
|
|
222
|
+
makeBorderBottom(borderColor, innerWidth, bg),
|
|
223
|
+
],
|
|
224
|
+
totalQuestionRows,
|
|
225
|
+
visibleQuestionRows: 0,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const availableBodyRows = maxRows - 2;
|
|
230
|
+
const supportingRows = availableBodyRows >= 3 ? 2 : Math.max(0, availableBodyRows - 1);
|
|
231
|
+
const messageCount = Math.min(totalQuestionRows, Math.max(1, availableBodyRows - supportingRows));
|
|
232
|
+
const safeMessageOffset = Math.min(messageOffset, Math.max(0, totalQuestionRows - messageCount));
|
|
233
|
+
const title = maxRows < 5 ? ` AWAITING INPUT · ${compactHint} ` : " AWAITING INPUT ";
|
|
234
|
+
const lines = [makeBorderTop(borderColor, title, theme, innerWidth, bg)];
|
|
235
|
+
for (const messageLine of messageRows.slice(safeMessageOffset, safeMessageOffset + messageCount)) {
|
|
236
|
+
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${paint(messageLine, theme.text)}`));
|
|
237
|
+
}
|
|
238
|
+
if (availableBodyRows >= 2) {
|
|
239
|
+
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${renderReducedResponse(state, theme)}`));
|
|
240
|
+
}
|
|
241
|
+
if (availableBodyRows >= 3) {
|
|
242
|
+
lines.push(makePaddedRow(bg, borderColor, innerWidth, ` ${renderHints(state.prompt.kind, theme)}`));
|
|
243
|
+
}
|
|
244
|
+
lines.push(makeBorderBottom(borderColor, innerWidth, bg));
|
|
245
|
+
return { lines, totalQuestionRows, visibleQuestionRows: messageCount };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function compactSurfaceHint(kind: PendingPrompt["kind"]): string {
|
|
249
|
+
switch (kind) {
|
|
250
|
+
case "confirm":
|
|
251
|
+
return "y Yes";
|
|
252
|
+
case "select":
|
|
253
|
+
return "↑↓ Choose";
|
|
254
|
+
case "custom":
|
|
255
|
+
return "enter";
|
|
256
|
+
default:
|
|
257
|
+
return "enter Submit";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function renderReducedResponse(state: PromptCardState, theme: GraphTheme): string {
|
|
262
|
+
switch (state.prompt.kind) {
|
|
263
|
+
case "confirm":
|
|
264
|
+
return paint("response yes / no", theme.textMuted, { bold: true });
|
|
265
|
+
case "select":
|
|
266
|
+
return paint(`response ${state.prompt.choices?.[state.selectedIndex] ?? "(no choices)"}`, theme.textMuted, {
|
|
267
|
+
bold: true,
|
|
268
|
+
});
|
|
269
|
+
case "input":
|
|
270
|
+
return paint(`response ❯ ${state.rawText}`, theme.textMuted, { bold: true });
|
|
271
|
+
case "editor":
|
|
272
|
+
return paint("response editor", theme.textMuted, { bold: true });
|
|
273
|
+
case "custom":
|
|
274
|
+
return paint("response", theme.textMuted, { bold: true });
|
|
275
|
+
}
|
|
44
276
|
}
|
|
45
277
|
|
|
46
278
|
function makeBorderTop(color: string, label: string, theme: GraphTheme, innerWidth: number, bg: string): string {
|
|
@@ -67,11 +299,30 @@ function wrapText(text: string, width: number): string[] {
|
|
|
67
299
|
return wrapTextWithAnsi(text, width);
|
|
68
300
|
}
|
|
69
301
|
|
|
302
|
+
function responseRowCount(state: PromptCardState): number {
|
|
303
|
+
switch (state.prompt.kind) {
|
|
304
|
+
case "select": {
|
|
305
|
+
const choiceCount = state.prompt.choices?.length ?? 0;
|
|
306
|
+
const visibleChoices = Math.max(1, Math.min(5, choiceCount));
|
|
307
|
+
return visibleChoices + (choiceCount > visibleChoices ? 1 : 0);
|
|
308
|
+
}
|
|
309
|
+
case "editor":
|
|
310
|
+
return 6;
|
|
311
|
+
default:
|
|
312
|
+
return 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function minimumResponseRowCount(state: PromptCardState): number {
|
|
317
|
+
return state.prompt.kind === "select" && (state.prompt.choices?.length ?? 0) > 1 ? 2 : 1;
|
|
318
|
+
}
|
|
319
|
+
|
|
70
320
|
function renderResponseFieldBox(
|
|
71
321
|
state: PromptCardState,
|
|
72
322
|
theme: GraphTheme,
|
|
73
323
|
usable: number,
|
|
74
324
|
cursorOn: boolean,
|
|
325
|
+
maxContentRows: number,
|
|
75
326
|
): string[] {
|
|
76
327
|
const boxWidth = Math.max(4, usable);
|
|
77
328
|
const contentWidth = Math.max(1, boxWidth - 2);
|
|
@@ -80,7 +331,7 @@ function renderResponseFieldBox(
|
|
|
80
331
|
const labelText = paint(label, theme.textMuted, { bold: true });
|
|
81
332
|
const labelW = visibleWidth(labelText);
|
|
82
333
|
const topFill = Math.max(0, boxWidth - labelW - 2);
|
|
83
|
-
const rows = renderResponseField(state, theme, contentWidth, cursorOn);
|
|
334
|
+
const rows = renderResponseField(state, theme, contentWidth, cursorOn, maxContentRows);
|
|
84
335
|
return [
|
|
85
336
|
paint("╭", borderColor) + labelText + paint(`${"─".repeat(topFill)}╮`, borderColor),
|
|
86
337
|
...rows.map((row) => makeFieldRow(row, contentWidth, borderColor)),
|
|
@@ -94,16 +345,22 @@ function makeFieldRow(content: string, width: number, borderColor: string): stri
|
|
|
94
345
|
return paint("│", borderColor) + padded + paint("│", borderColor);
|
|
95
346
|
}
|
|
96
347
|
|
|
97
|
-
function renderResponseField(
|
|
348
|
+
function renderResponseField(
|
|
349
|
+
state: PromptCardState,
|
|
350
|
+
theme: GraphTheme,
|
|
351
|
+
usable: number,
|
|
352
|
+
cursorOn: boolean,
|
|
353
|
+
maxRows: number,
|
|
354
|
+
): string[] {
|
|
98
355
|
switch (state.prompt.kind) {
|
|
99
356
|
case "confirm":
|
|
100
357
|
return [renderConfirmRow(state, theme, usable)];
|
|
101
358
|
case "select":
|
|
102
|
-
return renderSelectRows(state, theme, usable);
|
|
359
|
+
return renderSelectRows(state, theme, usable, maxRows);
|
|
103
360
|
case "input":
|
|
104
361
|
return [renderInputRow(state, theme, usable, cursorOn)];
|
|
105
362
|
case "editor":
|
|
106
|
-
return renderEditorRows(state, theme, usable, cursorOn);
|
|
363
|
+
return renderEditorRows(state, theme, usable, cursorOn, maxRows);
|
|
107
364
|
case "custom":
|
|
108
365
|
return [padToUsable("", usable)];
|
|
109
366
|
}
|
|
@@ -123,12 +380,14 @@ function renderConfirmRow(state: PromptCardState, theme: GraphTheme, usable: num
|
|
|
123
380
|
return padToUsable(row, usable);
|
|
124
381
|
}
|
|
125
382
|
|
|
126
|
-
function renderSelectRows(state: PromptCardState, theme: GraphTheme, usable: number): string[] {
|
|
383
|
+
function renderSelectRows(state: PromptCardState, theme: GraphTheme, usable: number, maxRows: number): string[] {
|
|
127
384
|
const choices = state.prompt.choices ?? [];
|
|
128
385
|
if (choices.length === 0) {
|
|
129
386
|
return [padToUsable(paint("(no choices)", theme.dim), usable)];
|
|
130
387
|
}
|
|
131
|
-
const
|
|
388
|
+
const rowBudget = Math.max(1, Math.floor(maxRows));
|
|
389
|
+
const scrolls = choices.length > Math.min(5, rowBudget);
|
|
390
|
+
const maxVisible = Math.max(1, Math.min(5, choices.length, rowBudget - (scrolls ? 1 : 0)));
|
|
132
391
|
const list = createPromptSelectList(state, theme, maxVisible);
|
|
133
392
|
return list.render(usable).map((line) => padToUsable(line, usable));
|
|
134
393
|
}
|
|
@@ -141,8 +400,19 @@ function renderInputRow(state: PromptCardState, theme: GraphTheme, usable: numbe
|
|
|
141
400
|
return padToUsable(paint("❯ ", theme.accent) + withCursor, usable);
|
|
142
401
|
}
|
|
143
402
|
|
|
144
|
-
function renderEditorRows(
|
|
145
|
-
|
|
403
|
+
function renderEditorRows(
|
|
404
|
+
state: PromptCardState,
|
|
405
|
+
theme: GraphTheme,
|
|
406
|
+
usable: number,
|
|
407
|
+
cursorOn: boolean,
|
|
408
|
+
maxRows: number,
|
|
409
|
+
): string[] {
|
|
410
|
+
const rowBudget = Math.max(1, Math.floor(maxRows));
|
|
411
|
+
if (rowBudget === 1 && state.editorSubmitFocused) {
|
|
412
|
+
return [padToUsable(renderEditorSubmitAction(true, theme), usable)];
|
|
413
|
+
}
|
|
414
|
+
const editorRows = rowBudget === 1 ? 1 : rowBudget - 1;
|
|
415
|
+
const includeSubmit = rowBudget > 1;
|
|
146
416
|
const allLines = state.rawText.split("\n");
|
|
147
417
|
// Find the line + column the caret currently sits on.
|
|
148
418
|
let acc = 0;
|
|
@@ -159,10 +429,10 @@ function renderEditorRows(state: PromptCardState, theme: GraphTheme, usable: num
|
|
|
159
429
|
caretLine = i + 1;
|
|
160
430
|
caretCol = 0;
|
|
161
431
|
}
|
|
162
|
-
const start = Math.max(0, Math.min(caretLine - Math.floor(
|
|
432
|
+
const start = Math.max(0, Math.min(caretLine - Math.floor(editorRows / 2), allLines.length - editorRows));
|
|
163
433
|
const safeStart = Math.max(0, start);
|
|
164
434
|
const rows: string[] = [];
|
|
165
|
-
for (let i = 0; i <
|
|
435
|
+
for (let i = 0; i < editorRows; i++) {
|
|
166
436
|
const lineIdx = safeStart + i;
|
|
167
437
|
const lineText = allLines[lineIdx] ?? "";
|
|
168
438
|
const isCaretLine = !state.editorSubmitFocused && lineIdx === caretLine;
|
|
@@ -174,7 +444,7 @@ function renderEditorRows(state: PromptCardState, theme: GraphTheme, usable: num
|
|
|
174
444
|
const prefix = paint(isCaretLine ? "❯ " : " ", isCaretLine ? theme.accent : theme.dim);
|
|
175
445
|
rows.push(padToUsable(prefix + withCursor, usable));
|
|
176
446
|
}
|
|
177
|
-
rows.push(padToUsable(renderEditorSubmitAction(state.editorSubmitFocused, theme), usable));
|
|
447
|
+
if (includeSubmit) rows.push(padToUsable(renderEditorSubmitAction(state.editorSubmitFocused, theme), usable));
|
|
178
448
|
return rows;
|
|
179
449
|
}
|
|
180
450
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export { handlePromptCardInput, isPromptEscapeInput } from "./prompt-card-input.js";
|
|
9
|
-
export type { PromptCardRenderOpts } from "./prompt-card-render.js";
|
|
9
|
+
export type { PromptCardIdentity, PromptCardRenderOpts } from "./prompt-card-render.js";
|
|
10
10
|
export { renderPromptCard } from "./prompt-card-render.js";
|
|
11
11
|
export type { PromptCardAction, PromptCardState } from "./prompt-card-state.js";
|
|
12
12
|
export { createPromptCardState, defaultResponseFor } from "./prompt-card-state.js";
|
|
@@ -12,4 +12,3 @@ export { renderHeader } from "./header.js";
|
|
|
12
12
|
export { computeLayout, NODE_H, NODE_W } from "./layout.js";
|
|
13
13
|
export { renderNodeCard } from "./node-card.js";
|
|
14
14
|
export { fmtDuration, statusColor, statusIcon } from "./status-helpers.js";
|
|
15
|
-
export { renderToasts } from "./toast.js";
|
|
@@ -24,10 +24,10 @@ import type { FlatBandBadge } from "./chat-surface.js";
|
|
|
24
24
|
import { renderRoundedBox } from "./chat-surface.js";
|
|
25
25
|
import { BOLD, hexToAnsi, RESET } from "./color-utils.js";
|
|
26
26
|
import type { GraphTheme } from "./graph-theme.js";
|
|
27
|
+
import { wrapIdentifierLines } from "./run-identity-rows.js";
|
|
27
28
|
import { fmtDuration, statusColor, statusIcon } from "./status-helpers.js";
|
|
28
29
|
import { truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
29
30
|
|
|
30
|
-
const SHORT_ID_LEN = 6;
|
|
31
31
|
const STAGE_NAME_COL = 14;
|
|
32
32
|
const KEY_COL = 14;
|
|
33
33
|
|
|
@@ -56,10 +56,11 @@ export function renderRunDetail(detail: RunDetail, opts: RenderRunDetailOpts = {
|
|
|
56
56
|
|
|
57
57
|
function renderPlain(detail: RunDetail, now: number, width: number): string {
|
|
58
58
|
const out: string[] = [];
|
|
59
|
-
|
|
60
|
-
const sid = shortId(detail.runId);
|
|
61
59
|
const stateBadge = stateLabel(detail);
|
|
62
60
|
|
|
61
|
+
out.push(...renderIdentifierRows(detail.runId, width - 2));
|
|
62
|
+
out.push("");
|
|
63
|
+
|
|
63
64
|
for (const [k, v] of summaryRows(detail, now)) {
|
|
64
65
|
if (v === undefined) continue;
|
|
65
66
|
const value = truncateToWidth(v, Math.max(1, width - 4 - KEY_COL), "…");
|
|
@@ -89,18 +90,10 @@ function renderPlain(detail: RunDetail, now: number, width: number): string {
|
|
|
89
90
|
out.push("");
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
const hint =
|
|
94
|
-
detail.status === "paused"
|
|
95
|
-
? ` ▸ workflow resume id=${sid} continue workflow `
|
|
96
|
-
: ` ▸ workflow interrupt id=${sid} cancel `;
|
|
97
|
-
out.push(truncateToWidth(hint, width - 2, "…"));
|
|
98
|
-
} else {
|
|
99
|
-
out.push(truncateToWidth(` ▸ workflow resume id=${sid} reopen graph `, width - 2, "…"));
|
|
100
|
-
}
|
|
93
|
+
out.push(...renderDetailHintRows(detail, width - 2));
|
|
101
94
|
|
|
102
95
|
return renderRoundedBox({
|
|
103
|
-
title: `RUN ${
|
|
96
|
+
title: `RUN ${detail.name} ${stateBadge}`,
|
|
104
97
|
bodyLines: out,
|
|
105
98
|
width,
|
|
106
99
|
});
|
|
@@ -115,9 +108,10 @@ function renderThemed(detail: RunDetail, now: number, theme: GraphTheme, width:
|
|
|
115
108
|
const muted = hexToAnsi(theme.textMuted);
|
|
116
109
|
const dim = hexToAnsi(theme.dim);
|
|
117
110
|
const text = hexToAnsi(theme.text);
|
|
118
|
-
const accent = hexToAnsi(theme.accent);
|
|
119
111
|
|
|
120
|
-
|
|
112
|
+
out.push(...renderIdentifierRows(detail.runId, width - 2, theme));
|
|
113
|
+
out.push("");
|
|
114
|
+
|
|
121
115
|
const badges = stateBadges(detail, theme);
|
|
122
116
|
|
|
123
117
|
for (const [k, v] of summaryRows(detail, now)) {
|
|
@@ -151,25 +145,11 @@ function renderThemed(detail: RunDetail, now: number, theme: GraphTheme, width:
|
|
|
151
145
|
out.push("");
|
|
152
146
|
}
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
const hint =
|
|
156
|
-
detail.status === "paused"
|
|
157
|
-
? ` ${dim}▸${RESET} ${accent}workflow resume id=${sid}${RESET}${dim} continue workflow${RESET} `
|
|
158
|
-
: ` ${dim}▸${RESET} ${accent}workflow interrupt id=${sid}${RESET}${dim} cancel${RESET} `;
|
|
159
|
-
out.push(truncateToWidth(hint, width - 2, "…"));
|
|
160
|
-
} else {
|
|
161
|
-
out.push(
|
|
162
|
-
truncateToWidth(
|
|
163
|
-
` ${dim}▸${RESET} ${accent}workflow resume id=${sid}${RESET}${dim} reopen graph${RESET} `,
|
|
164
|
-
width - 2,
|
|
165
|
-
"…",
|
|
166
|
-
),
|
|
167
|
-
);
|
|
168
|
-
}
|
|
148
|
+
out.push(...renderDetailHintRows(detail, width - 2, theme));
|
|
169
149
|
|
|
170
150
|
const badgeText = badges.length > 0 ? ` ${badges.map((b) => b.text).join(" ")}` : "";
|
|
171
151
|
return renderRoundedBox({
|
|
172
|
-
title: `RUN ${
|
|
152
|
+
title: `RUN ${detail.name}${badgeText}`,
|
|
173
153
|
bodyLines: out,
|
|
174
154
|
accent: theme.accent,
|
|
175
155
|
theme,
|
|
@@ -389,16 +369,53 @@ function statePlain(detail: RunDetail): string {
|
|
|
389
369
|
// Tiny formatters
|
|
390
370
|
// ---------------------------------------------------------------------------
|
|
391
371
|
|
|
392
|
-
function shortId(id: string): string {
|
|
393
|
-
return id.length > SHORT_ID_LEN ? id.slice(0, SHORT_ID_LEN) : id;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
372
|
function pad(s: string, n: number): string {
|
|
397
373
|
const width = visibleWidth(s);
|
|
398
374
|
if (width >= n) return s;
|
|
399
375
|
return s + " ".repeat(n - width);
|
|
400
376
|
}
|
|
401
377
|
|
|
378
|
+
function renderIdentifierRows(id: string, width: number, theme?: GraphTheme): string[] {
|
|
379
|
+
const rows = wrapIdentifierLines(id, width, " run id ", " ");
|
|
380
|
+
if (!theme) return rows.map((row) => `${row.prefix}${row.chunk}`);
|
|
381
|
+
const muted = hexToAnsi(theme.textMuted);
|
|
382
|
+
const accent = hexToAnsi(theme.accent);
|
|
383
|
+
return rows.map((row, index) => {
|
|
384
|
+
const prefix = index === 0 ? `${muted}${row.prefix}${RESET}` : row.prefix;
|
|
385
|
+
return `${prefix}${accent}${row.chunk}${RESET}`;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function renderDetailHintRows(detail: RunDetail, width: number, theme?: GraphTheme): string[] {
|
|
390
|
+
const prefix =
|
|
391
|
+
detail.endedAt === undefined
|
|
392
|
+
? detail.status === "paused"
|
|
393
|
+
? " ▸ workflow resume id="
|
|
394
|
+
: " ▸ workflow interrupt id="
|
|
395
|
+
: " ▸ workflow resume id=";
|
|
396
|
+
const suffix =
|
|
397
|
+
detail.endedAt === undefined
|
|
398
|
+
? detail.status === "paused"
|
|
399
|
+
? " continue workflow "
|
|
400
|
+
: " cancel "
|
|
401
|
+
: " reopen graph ";
|
|
402
|
+
const continuation = " ";
|
|
403
|
+
const rows = wrapIdentifierLines(detail.runId, width, prefix, continuation);
|
|
404
|
+
const last = rows[rows.length - 1]!;
|
|
405
|
+
if (visibleWidth(`${last.prefix}${last.chunk}${suffix}`) <= width) {
|
|
406
|
+
last.chunk += suffix;
|
|
407
|
+
} else {
|
|
408
|
+
rows.push({
|
|
409
|
+
prefix: continuation,
|
|
410
|
+
chunk: truncateToWidth(suffix.trimStart(), Math.max(1, width - visibleWidth(continuation)), "…"),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
if (!theme) return rows.map((row) => `${row.prefix}${row.chunk}`);
|
|
414
|
+
const dim = hexToAnsi(theme.dim);
|
|
415
|
+
const accent = hexToAnsi(theme.accent);
|
|
416
|
+
return rows.map((row) => `${dim}${row.prefix}${RESET}${accent}${row.chunk}${RESET}`);
|
|
417
|
+
}
|
|
418
|
+
|
|
402
419
|
function formatTime(ms: number): string {
|
|
403
420
|
const d = new Date(ms);
|
|
404
421
|
const hh = String(d.getUTCHours()).padStart(2, "0");
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Shared two-row run identity rendering for workflow cards and attribution banners. */
|
|
2
|
+
|
|
3
|
+
import { BOLD, hexToAnsi, RESET } from "./color-utils.js";
|
|
4
|
+
import type { GraphTheme } from "./graph-theme.js";
|
|
5
|
+
import { visibleWidth } from "./text-helpers.js";
|
|
6
|
+
|
|
7
|
+
export interface RunIdentityRowsOpts {
|
|
8
|
+
/** Full run UUID; this value is never shortened. */
|
|
9
|
+
readonly runId: string;
|
|
10
|
+
/** Workflow display name shown on the second row. */
|
|
11
|
+
readonly name: string;
|
|
12
|
+
/** Optional metadata appended to the workflow name. */
|
|
13
|
+
readonly meta?: string;
|
|
14
|
+
/** Status glyph shown on the first row. */
|
|
15
|
+
readonly glyph: string;
|
|
16
|
+
/** Hex colour for the status glyph in themed output. */
|
|
17
|
+
readonly glyphColor?: string;
|
|
18
|
+
/** Hex colour for metadata in themed output. */
|
|
19
|
+
readonly metaColor?: string;
|
|
20
|
+
/** Omit for plain output. */
|
|
21
|
+
readonly theme?: GraphTheme;
|
|
22
|
+
/** Visible width available to the identity rows. Omit to disable wrapping. */
|
|
23
|
+
readonly width?: number;
|
|
24
|
+
/** Leading spaces before the glyph. Defaults to the widget's three cells. */
|
|
25
|
+
readonly idIndent?: number;
|
|
26
|
+
/** Spaces between the glyph and the full run id. Defaults to two cells. */
|
|
27
|
+
readonly idGap?: number;
|
|
28
|
+
/** Leading spaces before the workflow name. Defaults to five cells. */
|
|
29
|
+
readonly nameIndent?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IdentifierLine {
|
|
33
|
+
prefix: string;
|
|
34
|
+
chunk: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hard-wrap an identifier without changing any of its characters. Prefixes
|
|
39
|
+
* count against each row's visible-cell budget, but are not part of the id.
|
|
40
|
+
*/
|
|
41
|
+
export function wrapIdentifierLines(
|
|
42
|
+
id: string,
|
|
43
|
+
width: number,
|
|
44
|
+
firstPrefix: string,
|
|
45
|
+
continuationPrefix: string,
|
|
46
|
+
): IdentifierLine[] {
|
|
47
|
+
const rows: IdentifierLine[] = [];
|
|
48
|
+
let remaining = id;
|
|
49
|
+
let first = true;
|
|
50
|
+
while (remaining.length > 0 || rows.length === 0) {
|
|
51
|
+
const prefix = first ? firstPrefix : continuationPrefix;
|
|
52
|
+
const budget = Math.max(1, width - visibleWidth(prefix));
|
|
53
|
+
let chunk = "";
|
|
54
|
+
for (const character of remaining) {
|
|
55
|
+
if (chunk.length > 0 && visibleWidth(`${chunk}${character}`) > budget) break;
|
|
56
|
+
chunk += character;
|
|
57
|
+
}
|
|
58
|
+
if (chunk.length === 0) chunk = remaining[0] ?? "";
|
|
59
|
+
rows.push({ prefix, chunk });
|
|
60
|
+
remaining = remaining.slice(chunk.length);
|
|
61
|
+
first = false;
|
|
62
|
+
}
|
|
63
|
+
return rows;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Render the canonical identity shape shared by the background widget and
|
|
68
|
+
* awaiting-input attribution banner. The caller supplies the status glyph so
|
|
69
|
+
* surfaces can preserve their existing run-state semantics.
|
|
70
|
+
*/
|
|
71
|
+
export function renderRunIdentityRows(opts: RunIdentityRowsOpts): string[] {
|
|
72
|
+
const idIndent = Math.max(0, opts.idIndent ?? 3);
|
|
73
|
+
const idGap = Math.max(0, opts.idGap ?? 2);
|
|
74
|
+
const nameIndent = Math.max(0, opts.nameIndent ?? 5);
|
|
75
|
+
const idPrefix = `${" ".repeat(idIndent)}${opts.glyph}${" ".repeat(idGap)}`;
|
|
76
|
+
const continuationPrefix = " ".repeat(Math.max(0, idIndent + idGap - 1));
|
|
77
|
+
const availableWidth = opts.width === undefined ? Number.POSITIVE_INFINITY : Math.max(1, opts.width);
|
|
78
|
+
const chunks = wrapIdentifierLines(opts.runId, availableWidth, idPrefix, continuationPrefix);
|
|
79
|
+
const themed = opts.theme !== undefined;
|
|
80
|
+
const glyph = themed ? `${hexToAnsi(opts.glyphColor ?? opts.theme.text)}${opts.glyph}${RESET}` : opts.glyph;
|
|
81
|
+
const id = themed ? (text: string) => `${hexToAnsi(opts.theme!.accent)}${text}${RESET}` : (text: string) => text;
|
|
82
|
+
const name = themed ? `${hexToAnsi(opts.theme.text)}${BOLD}${opts.name}${RESET}` : opts.name;
|
|
83
|
+
const metaColor = opts.metaColor ?? opts.theme?.dim;
|
|
84
|
+
const meta =
|
|
85
|
+
themed && opts.meta !== undefined && opts.meta.length > 0
|
|
86
|
+
? ` · ${hexToAnsi(metaColor ?? opts.theme.textMuted)}${opts.meta}${RESET}`
|
|
87
|
+
: opts.meta !== undefined && opts.meta.length > 0
|
|
88
|
+
? ` · ${opts.meta}`
|
|
89
|
+
: "";
|
|
90
|
+
|
|
91
|
+
const rows = chunks.map((chunk, index) => {
|
|
92
|
+
if (index > 0) return `${chunk.prefix}${id(chunk.chunk)}`;
|
|
93
|
+
return `${" ".repeat(idIndent)}${glyph}${" ".repeat(idGap)}${id(chunk.chunk)}`;
|
|
94
|
+
});
|
|
95
|
+
rows.push(`${" ".repeat(nameIndent)}${name}${meta}`);
|
|
96
|
+
return rows;
|
|
97
|
+
}
|