@linxiraos/pi-tui 1.2.1 → 1.3.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/CHANGELOG.md +188 -125
- package/README.md +36 -0
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/app-keybindings.d.ts +18 -8
- package/dist/types/apps/git/diff-pane.d.ts +5 -0
- package/dist/types/apps/git/git-tui.d.ts +16 -2
- package/dist/types/apps/git/state.d.ts +11 -0
- package/dist/types/autocomplete.d.ts +2 -0
- package/dist/types/charts/chart-svg.d.ts +7 -13
- package/dist/types/chat/advisor-message.d.ts +4 -1
- package/dist/types/chat/assistant-message.d.ts +9 -4
- package/dist/types/chat/figure-markdown.d.ts +7 -2
- package/dist/types/chat/messages.d.ts +3 -1
- package/dist/types/chat/read-tool-group.d.ts +2 -2
- package/dist/types/chat/table-chart.d.ts +11 -15
- package/dist/types/chat/tool-execution.d.ts +32 -20
- package/dist/types/chat/user-message.d.ts +3 -2
- package/dist/types/chrome/countdown-timer.d.ts +3 -2
- package/dist/types/chrome/diff.d.ts +2 -3
- package/dist/types/chrome/transcript-container.d.ts +2 -0
- package/dist/types/components/image.d.ts +2 -0
- package/dist/types/components/markdown.d.ts +49 -3
- package/dist/types/components/text.d.ts +2 -0
- package/dist/types/debug-server.d.ts +2 -0
- package/dist/types/keybinding-matchers.d.ts +6 -0
- package/dist/types/multiplexers/tmux.d.ts +1 -0
- package/dist/types/multiplexers/types.d.ts +6 -0
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +24 -12
- package/dist/types/overlays/agent-hub-projection.d.ts +10 -5
- package/dist/types/overlays/annotation-overlay.d.ts +19 -0
- package/dist/types/overlays/approval-dialog.d.ts +79 -0
- package/dist/types/overlays/ask-dialog.d.ts +11 -6
- package/dist/types/overlays/btw-history-panel.d.ts +2 -6
- package/dist/types/overlays/hook-editor.d.ts +7 -2
- package/dist/types/overlays/hook-input.d.ts +12 -4
- package/dist/types/overlays/job-tree.d.ts +50 -0
- package/dist/types/overlays/jobs-panel.d.ts +25 -11
- package/dist/types/overlays/login-dialog.d.ts +8 -3
- package/dist/types/overlays/mcp-add-wizard.d.ts +9 -1
- package/dist/types/overlays/model-browser.d.ts +6 -1
- package/dist/types/overlays/model-picker.d.ts +2 -0
- package/dist/types/overlays/pause-screen.d.ts +5 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +4 -0
- package/dist/types/overlays/report-panel.d.ts +5 -1
- package/dist/types/overlays/reset-usage-selector.d.ts +7 -5
- package/dist/types/overlays/session-account-selector.d.ts +4 -0
- package/dist/types/overlays/session-info-overlay.d.ts +7 -3
- package/dist/types/overlays/session-selector.d.ts +3 -1
- package/dist/types/overlays/usage-dashboard.d.ts +6 -6
- package/dist/types/prompt/composer.d.ts +21 -20
- package/dist/types/prompt/custom-editor.d.ts +20 -10
- package/dist/types/prompt/image-references.d.ts +22 -0
- package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
- package/dist/types/prompt/macos-spelling.d.ts +2 -1
- package/dist/types/prompt/prose-gate.d.ts +3 -0
- package/dist/types/prompt/queued-messages.d.ts +1 -1
- package/dist/types/prompt/typing-word.d.ts +25 -0
- package/dist/types/prompt/welcome.d.ts +26 -6
- package/dist/types/render/hanging-wrap.d.ts +7 -0
- package/dist/types/render/hyperlink.d.ts +2 -0
- package/dist/types/render/render-utils.d.ts +9 -1
- package/dist/types/render/utils.d.ts +1 -1
- package/dist/types/setup/scenes/splash.d.ts +6 -5
- package/dist/types/status-line/context-usage.d.ts +4 -1
- package/dist/types/status-line/host.d.ts +6 -4
- package/dist/types/status-line/startup.d.ts +6 -6
- package/dist/types/status-line/types.d.ts +3 -0
- package/dist/types/stdin-buffer.d.ts +7 -0
- package/dist/types/terminal-capabilities.d.ts +16 -1
- package/dist/types/terminal-multiplexer.d.ts +7 -0
- package/dist/types/terminal.d.ts +8 -4
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme.d.ts +11 -0
- package/dist/types/thinking.d.ts +5 -0
- package/dist/types/tmux.d.ts +39 -4
- package/dist/types/tools/ast-edit.d.ts +2 -2
- package/dist/types/tools/ast-grep.d.ts +2 -2
- package/dist/types/tools/bash.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +53 -4
- package/dist/types/tools/find.d.ts +14 -4
- package/dist/types/tools/glob.d.ts +3 -3
- package/dist/types/tools/grep.d.ts +2 -2
- package/dist/types/tools/grouped-file-output.d.ts +9 -4
- package/dist/types/tools/native-view.d.ts +1 -1
- package/dist/types/tools/read.d.ts +32 -10
- package/dist/types/tools/renderer.d.ts +2 -0
- package/dist/types/tools/task.d.ts +31 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/tui.d.ts +20 -3
- package/package.json +10 -10
- package/src/app-keybindings.d.ts +18 -8
- package/src/app-keybindings.ts +20 -8
- package/src/apps/autoresearch-dashboard.ts +444 -27
- package/src/apps/debug/raw-sse.ts +2 -2
- package/src/apps/git/diff-pane.ts +39 -41
- package/src/apps/git/git-tui.ts +80 -14
- package/src/apps/git/sidebar.ts +10 -1
- package/src/apps/git/state.ts +12 -0
- package/src/autocomplete.d.ts +2 -0
- package/src/autocomplete.ts +21 -19
- package/src/bracketed-paste.ts +9 -2
- package/src/charts/chart-svg.d.ts +7 -13
- package/src/charts/chart-svg.ts +19 -22
- package/src/chat/advisor-message.d.ts +4 -1
- package/src/chat/advisor-message.ts +77 -24
- package/src/chat/assistant-message.d.ts +9 -4
- package/src/chat/assistant-message.ts +127 -31
- package/src/chat/bash-execution.ts +7 -2
- package/src/chat/figure-markdown.d.ts +7 -2
- package/src/chat/figure-markdown.ts +10 -3
- package/src/chat/image-loading.ts +9 -6
- package/src/chat/messages.d.ts +3 -1
- package/src/chat/messages.ts +4 -1
- package/src/chat/read-tool-group.d.ts +2 -2
- package/src/chat/read-tool-group.ts +97 -42
- package/src/chat/svg-figure.ts +3 -1
- package/src/chat/svg-source.ts +6 -3
- package/src/chat/table-chart.d.ts +11 -15
- package/src/chat/table-chart.ts +24 -57
- package/src/chat/tool-execution.d.ts +32 -20
- package/src/chat/tool-execution.ts +103 -36
- package/src/chat/user-message.d.ts +3 -2
- package/src/chat/user-message.ts +54 -34
- package/src/chrome/countdown-timer.ts +3 -3
- package/src/chrome/diff.d.ts +2 -3
- package/src/chrome/diff.ts +4 -9
- package/src/chrome/transcript-container.d.ts +2 -0
- package/src/chrome/transcript-container.ts +212 -28
- package/src/components/editor.ts +12 -12
- package/src/components/image.d.ts +2 -0
- package/src/components/image.ts +54 -20
- package/src/components/markdown.d.ts +49 -3
- package/src/components/markdown.ts +461 -11
- package/src/components/settings-list.ts +2 -4
- package/src/components/text.d.ts +2 -0
- package/src/components/text.ts +11 -1
- package/src/debug-server.d.ts +2 -0
- package/src/debug-server.ts +51 -69
- package/src/hotkeys-markdown.ts +2 -0
- package/src/keybinding-matchers.d.ts +6 -0
- package/src/keybinding-matchers.ts +19 -0
- package/src/multiplexers/tmux.d.ts +1 -0
- package/src/multiplexers/tmux.ts +3 -0
- package/src/multiplexers/types.d.ts +6 -0
- package/src/multiplexers/types.ts +6 -0
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +13 -5
- package/src/native/blobs.d.ts +24 -12
- package/src/native/blobs.ts +56 -7
- package/src/native/reconcile.ts +43 -21
- package/src/overlays/advisor-config.ts +259 -8
- package/src/overlays/agent-hub-projection.d.ts +10 -5
- package/src/overlays/agent-hub-projection.ts +63 -12
- package/src/overlays/annotation-overlay.ts +422 -168
- package/src/overlays/approval-dialog.ts +389 -0
- package/src/overlays/ask-dialog.ts +244 -105
- package/src/overlays/btw-history-panel.ts +208 -147
- package/src/overlays/extensions/extension-list.ts +4 -6
- package/src/overlays/hook-editor.ts +90 -32
- package/src/overlays/hook-input.ts +105 -19
- package/src/overlays/job-tree.ts +177 -0
- package/src/overlays/jobs-panel.ts +222 -142
- package/src/overlays/login-dialog.ts +201 -91
- package/src/overlays/mcp-add-wizard.ts +562 -279
- package/src/overlays/model-browser.ts +32 -7
- package/src/overlays/model-hub.ts +4 -10
- package/src/overlays/model-picker.ts +58 -21
- package/src/overlays/pause-screen.ts +81 -23
- package/src/overlays/plan-review-overlay.ts +16 -0
- package/src/overlays/plugin-selector.ts +17 -1
- package/src/overlays/queue-mode-selector.ts +10 -0
- package/src/overlays/report-panel.ts +6 -2
- package/src/overlays/reset-usage-selector.ts +130 -21
- package/src/overlays/session-account-selector.ts +36 -0
- package/src/overlays/session-info-overlay.ts +199 -76
- package/src/overlays/session-selector.ts +15 -10
- package/src/overlays/settings-selector.ts +3 -0
- package/src/overlays/show-images-selector.ts +1 -0
- package/src/overlays/snapcompact-shape-preview.ts +13 -6
- package/src/overlays/theme-selector.ts +6 -1
- package/src/overlays/thinking-selector.ts +8 -4
- package/src/overlays/tree-selector.ts +117 -2
- package/src/overlays/usage-dashboard.ts +498 -144
- package/src/prompt/composer-cache.ts +1 -1
- package/src/prompt/composer.ts +194 -74
- package/src/prompt/custom-editor.ts +168 -65
- package/src/prompt/image-references.d.ts +22 -0
- package/src/prompt/image-references.ts +79 -2
- package/src/prompt/internal-url-autocomplete.ts +1 -0
- package/src/prompt/macos-spelling.ts +28 -5
- package/src/prompt/prose-gate.ts +3 -0
- package/src/prompt/queued-messages.ts +9 -2
- package/src/prompt/tips.txt +1 -2
- package/src/prompt/typing-word.ts +59 -0
- package/src/prompt/welcome.ts +97 -45
- package/src/render/code-cell.ts +18 -5
- package/src/render/hanging-wrap.d.ts +7 -0
- package/src/render/hanging-wrap.ts +58 -0
- package/src/render/hyperlink.d.ts +2 -0
- package/src/render/hyperlink.ts +5 -0
- package/src/render/render-utils.d.ts +9 -1
- package/src/render/render-utils.ts +16 -2
- package/src/render/utils.d.ts +1 -1
- package/src/render/utils.ts +1 -1
- package/src/render/width-aware-text.ts +6 -5
- package/src/setup/scenes/splash.ts +22 -8
- package/src/status-line/component.ts +23 -10
- package/src/status-line/context-usage.ts +176 -73
- package/src/status-line/footer.ts +18 -35
- package/src/status-line/host.ts +6 -1
- package/src/status-line/segments.ts +16 -5
- package/src/status-line/startup.ts +6 -6
- package/src/status-line/types.ts +1 -0
- package/src/stdin-buffer.d.ts +7 -0
- package/src/stdin-buffer.ts +11 -1
- package/src/terminal-capabilities.d.ts +16 -1
- package/src/terminal-capabilities.ts +66 -28
- package/src/terminal-multiplexer.d.ts +7 -0
- package/src/terminal-multiplexer.ts +14 -0
- package/src/terminal.d.ts +8 -4
- package/src/terminal.ts +160 -46
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +1 -0
- package/src/theme/theme-class.ts +1 -0
- package/src/theme/theme.d.ts +11 -0
- package/src/theme/theme.ts +46 -14
- package/src/thinking.d.ts +5 -0
- package/src/thinking.ts +9 -1
- package/src/tmux.d.ts +39 -4
- package/src/tmux.ts +110 -16
- package/src/tools/ask.ts +20 -9
- package/src/tools/ast-edit.d.ts +2 -2
- package/src/tools/ast-edit.ts +30 -7
- package/src/tools/ast-grep.d.ts +2 -2
- package/src/tools/ast-grep.ts +39 -35
- package/src/tools/bash.d.ts +6 -1
- package/src/tools/bash.ts +15 -9
- package/src/tools/debug.ts +2 -1
- package/src/tools/default-renderer.ts +3 -2
- package/src/tools/edit.d.ts +2 -0
- package/src/tools/edit.ts +100 -21
- package/src/tools/eval.d.ts +53 -4
- package/src/tools/eval.ts +173 -137
- package/src/tools/find.d.ts +14 -4
- package/src/tools/find.ts +71 -18
- package/src/tools/glob.d.ts +3 -3
- package/src/tools/glob.ts +51 -17
- package/src/tools/grep.d.ts +2 -2
- package/src/tools/grep.ts +99 -22
- package/src/tools/grouped-file-output.d.ts +9 -4
- package/src/tools/grouped-file-output.ts +21 -6
- package/src/tools/lsp.ts +30 -18
- package/src/tools/mcp.ts +2 -2
- package/src/tools/native-view.d.ts +1 -1
- package/src/tools/native-view.ts +2 -2
- package/src/tools/read.d.ts +32 -10
- package/src/tools/read.ts +89 -43
- package/src/tools/renderer.d.ts +2 -0
- package/src/tools/renderer.ts +2 -0
- package/src/tools/task.d.ts +31 -0
- package/src/tools/task.ts +40 -7
- package/src/tools/todo.ts +3 -2
- package/src/tools/wait.ts +16 -30
- package/src/tools/web-search.ts +2 -1
- package/src/tools/write.d.ts +2 -0
- package/src/tools/write.ts +36 -12
- package/src/tools/yield.ts +2 -2
- package/src/tui.d.ts +20 -3
- package/src/tui.ts +140 -36
- package/src/utils.ts +5 -3
- package/dist/types/tools/eval-format/code-mode-declarations.d.ts +0 -5
- package/src/tools/eval-format/code-mode-declarations.ts +0 -61
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { ThinkingLevel } from "@linxiraos/pi-agent-core";
|
|
11
11
|
import type { Model } from "@linxiraos/pi-ai";
|
|
12
12
|
import { buildModel } from "@linxiraos/pi-catalog/build";
|
|
13
|
-
import { getModelPricingStatus, modelsAreEqual } from "@linxiraos/pi-catalog/models";
|
|
13
|
+
import { getEffectiveTokenCost, getModelPricingStatus, modelsAreEqual } from "@linxiraos/pi-catalog/models";
|
|
14
14
|
import { modelKind, type ModelKind, type ModelPricingStatus } from "@linxiraos/pi-catalog/types";
|
|
15
15
|
import type { Component } from "../tui";
|
|
16
16
|
import { FuzzyCorpus, fuzzyRank } from "../fuzzy";
|
|
@@ -37,7 +37,14 @@ import {
|
|
|
37
37
|
} from "../keybinding-matchers";
|
|
38
38
|
import { MenuSelection } from "../components/menu-selection";
|
|
39
39
|
import { clampScrollOffset, scrollOffsetForRow } from "../components/scroll-viewport";
|
|
40
|
-
import type {
|
|
40
|
+
import type {
|
|
41
|
+
TspPickerColumn,
|
|
42
|
+
TspPickerGroup,
|
|
43
|
+
TspPickerItem,
|
|
44
|
+
TspPickerMark,
|
|
45
|
+
TspSpan,
|
|
46
|
+
TspText,
|
|
47
|
+
} from "@linxiraos/pi-wire";
|
|
41
48
|
import { col, md, node, row, span, text } from "../native/describe";
|
|
42
49
|
import { pickerFuzzyHits } from "../native/picker";
|
|
43
50
|
import type { NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
@@ -735,7 +742,7 @@ function declaredPricingStatus(model: Model): Exclude<ModelPricingStatus, "fixed
|
|
|
735
742
|
function isFreeModel(model: Model): boolean {
|
|
736
743
|
const declared = declaredPricingStatus(model);
|
|
737
744
|
if (declared !== undefined) return declared === "free";
|
|
738
|
-
const cost = model.cost;
|
|
745
|
+
const cost = model.cost ? getEffectiveTokenCost(model.cost) : undefined;
|
|
739
746
|
const credits = model.factoryDroidCredits;
|
|
740
747
|
return (!cost || (cost.input === 0 && cost.output === 0)) && (credits === undefined || credits === 0);
|
|
741
748
|
}
|
|
@@ -759,7 +766,8 @@ function withCreditBadge(model: Model, price: string): string {
|
|
|
759
766
|
const credits = model.factoryDroidCredits;
|
|
760
767
|
if (credits === undefined) return price;
|
|
761
768
|
const badge = `${formatCostLeg(credits)}×`;
|
|
762
|
-
|
|
769
|
+
const cost = getEffectiveTokenCost(model.cost);
|
|
770
|
+
return cost.input !== 0 || cost.output !== 0 ? `${price} ${badge}` : badge;
|
|
763
771
|
}
|
|
764
772
|
|
|
765
773
|
const PRICING_STATUS_LABELS: Record<Exclude<ModelPricingStatus, "fixed">, { short: string; detail: string }> = {
|
|
@@ -774,7 +782,8 @@ function formatCostPair(model: Model): string {
|
|
|
774
782
|
const declared = declaredPricingStatus(model);
|
|
775
783
|
if (declared !== undefined) return PRICING_STATUS_LABELS[declared].short;
|
|
776
784
|
if (isFreeModel(model)) return "free";
|
|
777
|
-
|
|
785
|
+
const cost = getEffectiveTokenCost(model.cost);
|
|
786
|
+
return withCreditBadge(model, `$${formatCostLeg(cost.input)}/${formatCostLeg(cost.output)}`);
|
|
778
787
|
}
|
|
779
788
|
|
|
780
789
|
/** Detail-pane price fact: `$3/15 per M`, or the declared pricing state in words. */
|
|
@@ -796,19 +805,34 @@ function pickerPrice(model: Model): string {
|
|
|
796
805
|
const declared = declaredPricingStatus(model);
|
|
797
806
|
if (declared !== undefined) return PRICING_STATUS_LABELS[declared].short;
|
|
798
807
|
if (isFreeModel(model)) return "free";
|
|
799
|
-
|
|
808
|
+
const cost = getEffectiveTokenCost(model.cost);
|
|
809
|
+
return withCreditBadge(model, `$${formatCostLeg(cost.input)}·${formatCostLeg(cost.output)}`);
|
|
800
810
|
}
|
|
801
811
|
|
|
802
812
|
/** `$2 in · $10 out · $0.2 cache` for a model preview. */
|
|
803
813
|
function previewPrice(model: Model): string {
|
|
804
814
|
const declared = declaredPricingStatus(model);
|
|
805
815
|
if (declared !== undefined) return PRICING_STATUS_LABELS[declared].detail;
|
|
806
|
-
const cost = model.cost;
|
|
816
|
+
const cost = getEffectiveTokenCost(model.cost);
|
|
807
817
|
const parts = [`$${formatCostLeg(cost.input)} in`, `$${formatCostLeg(cost.output)} out`];
|
|
808
818
|
if (cost.cacheRead > 0) parts.push(`$${formatCostLeg(cost.cacheRead)} cache`);
|
|
809
819
|
return withCreditBadge(model, parts.join(" · "));
|
|
810
820
|
}
|
|
811
821
|
|
|
822
|
+
/**
|
|
823
|
+
* A provider's picker mark: the terminal's logo for it, else its initials
|
|
824
|
+
* (`amazon-bedrock` → `AB`, `anthropic` → `An`) on its own gradient.
|
|
825
|
+
*/
|
|
826
|
+
export function providerMark(providerId: string): TspPickerMark {
|
|
827
|
+
const words = providerId.split(/[-_\s.]+/).filter(word => word.length > 0);
|
|
828
|
+
const word = words[0] ?? providerId;
|
|
829
|
+
const text =
|
|
830
|
+
words.length >= 2
|
|
831
|
+
? `${words[0]!.charAt(0)}${words[1]!.charAt(0)}`.toUpperCase()
|
|
832
|
+
: `${word.charAt(0).toUpperCase()}${word.charAt(1)}`;
|
|
833
|
+
return { text, seed: providerId, icon: `provider:${providerId}` };
|
|
834
|
+
}
|
|
835
|
+
|
|
812
836
|
/** The omp theme token of a thinking level's dot (`thinkingHigh`); none for inherit and auto. */
|
|
813
837
|
export function thinkingDotToken(level: ConfiguredThinkingLevel): string | undefined {
|
|
814
838
|
if (level === ThinkingLevel.Inherit || level === AUTO_THINKING) return undefined;
|
|
@@ -1999,6 +2023,7 @@ export class ModelBrowser implements Component {
|
|
|
1999
2023
|
const value: TspPickerItem = {
|
|
2000
2024
|
id: item.selector,
|
|
2001
2025
|
label: quickRole ? item.id : item.selector,
|
|
2026
|
+
mark: providerMark(item.model.provider),
|
|
2002
2027
|
mono: true,
|
|
2003
2028
|
...(quickRole ? { detail: `${item.model.provider}/${item.model.id}` } : {}),
|
|
2004
2029
|
facts,
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
ModelBrowser,
|
|
57
57
|
type ModelBrowserItem,
|
|
58
58
|
modelSearchText,
|
|
59
|
+
providerMark,
|
|
59
60
|
thinkingDotToken,
|
|
60
61
|
type RoleAssignments,
|
|
61
62
|
resolveRoleAssignments,
|
|
@@ -314,14 +315,6 @@ const MODEL_KIND_LABELS: Record<"all" | ModelKind, string> = {
|
|
|
314
315
|
video: "Video",
|
|
315
316
|
};
|
|
316
317
|
|
|
317
|
-
/** A provider mark's initials: `amazon-bedrock` → `AB`, `anthropic` → `An`. */
|
|
318
|
-
function providerInitials(providerId: string): string {
|
|
319
|
-
const words = providerId.split(/[-_\s.]+/).filter(word => word.length > 0);
|
|
320
|
-
if (words.length >= 2) return `${words[0]!.charAt(0)}${words[1]!.charAt(0)}`.toUpperCase();
|
|
321
|
-
const word = words[0] ?? providerId;
|
|
322
|
-
return `${word.charAt(0).toUpperCase()}${word.charAt(1)}`;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
318
|
const PROVIDER_REFRESH_DEBOUNCE_MS = 120;
|
|
326
319
|
const RECENT_LIMIT = 15;
|
|
327
320
|
/** Accepted compaction limit input, shown beside the field: a scaled base, a fixed trigger, or a percentage. */
|
|
@@ -549,7 +542,7 @@ export class ModelHubComponent implements Component {
|
|
|
549
542
|
}
|
|
550
543
|
|
|
551
544
|
// Reconcile catalogs in the background. This is online discovery only —
|
|
552
|
-
// it must not re-run `!command` credential helpers (F5 / `
|
|
545
|
+
// it must not re-run `!command` credential helpers (F5 / `zetacode models
|
|
553
546
|
// refresh` pass refreshCommandCredentials for that). A --models scope is
|
|
554
547
|
// registry-independent, so the reload would only repeat the hydration
|
|
555
548
|
// above.
|
|
@@ -1335,6 +1328,7 @@ export class ModelHubComponent implements Component {
|
|
|
1335
1328
|
*/
|
|
1336
1329
|
#thinkingOptionsFor(model: Model): ConfiguredThinkingLevel[] {
|
|
1337
1330
|
if (!model.reasoning) return [];
|
|
1331
|
+
if (model.thinking?.unverified) return [ThinkingLevel.Inherit, ThinkingLevel.Off, ...getSupportedEfforts(model)];
|
|
1338
1332
|
return [ThinkingLevel.Inherit, ThinkingLevel.Off, AUTO_THINKING, ...getSupportedEfforts(model)];
|
|
1339
1333
|
}
|
|
1340
1334
|
|
|
@@ -3458,7 +3452,7 @@ export class ModelHubComponent implements Component {
|
|
|
3458
3452
|
break;
|
|
3459
3453
|
case "provider": {
|
|
3460
3454
|
const providerId = entry.providerId ?? entry.label;
|
|
3461
|
-
const mark =
|
|
3455
|
+
const mark = providerMark(providerId);
|
|
3462
3456
|
if (entry.locked) {
|
|
3463
3457
|
const envVars = providerEntry(providerId)?.envVars ?? [];
|
|
3464
3458
|
scopes.push({
|
|
@@ -76,6 +76,8 @@ export interface ModelPickerOptions {
|
|
|
76
76
|
taskModeKeys?: readonly KeyId[];
|
|
77
77
|
/** `provider/id` highlighted and preselected in task mode (current Task subagent model). */
|
|
78
78
|
taskSelector?: string;
|
|
79
|
+
/** Present selection-only chrome; consumers handle the chosen selector without mutating a session. */
|
|
80
|
+
selectOnly?: boolean;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/** Fixed chrome rows: top border, status row, footer, bottom border. */
|
|
@@ -90,9 +92,10 @@ const HEIGHT_FRACTION = 0.4;
|
|
|
90
92
|
const STATUS_HINT = "Session-only switch — role models stay unchanged";
|
|
91
93
|
const QUICK_ROLE_STATUS_HINT = "Quick role switch — applies its model and thinking for this session";
|
|
92
94
|
const TASK_STATUS_HINT = "Task subagent switch — spawned task agents use this model (session-only)";
|
|
95
|
+
const SELECT_ONLY_STATUS_HINT = "Selection only — does not change the session model";
|
|
93
96
|
|
|
94
97
|
/** Footer hint for the active mode; keys resolve at render time so theme/keybinding changes apply. */
|
|
95
|
-
function footerHint(mode: "session" | "role" | "task"): string {
|
|
98
|
+
function footerHint(mode: "session" | "role" | "task" | "selection"): string {
|
|
96
99
|
const upDown = editorKeys("tui.select.up", "tui.select.down");
|
|
97
100
|
const enter = formatKeyHint("enter");
|
|
98
101
|
const close = `${editorKey("tui.select.cancel")} close`;
|
|
@@ -101,6 +104,8 @@ function footerHint(mode: "session" | "role" | "task"): string {
|
|
|
101
104
|
return `${upDown} roles · ${enter} apply role model · type to search · ${close}`;
|
|
102
105
|
case "task":
|
|
103
106
|
return `${upDown} models · ${enter} use for Task subagents · type to search · ${close}`;
|
|
107
|
+
case "selection":
|
|
108
|
+
return `${upDown} models · ${enter} select model · type to search · ${close}`;
|
|
104
109
|
default:
|
|
105
110
|
return `${upDown} models · ${enter} use for this session · type to search · @ quick roles · ${close}`;
|
|
106
111
|
}
|
|
@@ -128,6 +133,7 @@ export class ModelPickerComponent implements Component {
|
|
|
128
133
|
#taskMatchKeys = new Set<string>();
|
|
129
134
|
#taskModeKey: KeyId | undefined;
|
|
130
135
|
#taskSelector: string | undefined;
|
|
136
|
+
#selectOnly: boolean;
|
|
131
137
|
#nativeRoot: { memo: string; node: NativeNode } | undefined;
|
|
132
138
|
#pickerRoot:
|
|
133
139
|
| {
|
|
@@ -151,10 +157,11 @@ export class ModelPickerComponent implements Component {
|
|
|
151
157
|
this.#settings = settings;
|
|
152
158
|
this.#registry = registry;
|
|
153
159
|
this.#scopedModels = scopedModels;
|
|
160
|
+
this.#selectOnly = options.selectOnly ?? false;
|
|
154
161
|
this.#currentSelector = options.currentSelector;
|
|
155
162
|
this.#currentQuickRoleSelector = options.currentQuickRole ? `@${options.currentQuickRole}` : undefined;
|
|
156
163
|
this.#taskSelector = options.taskSelector;
|
|
157
|
-
if (callbacks.onPickTask) {
|
|
164
|
+
if (!this.#selectOnly && callbacks.onPickTask) {
|
|
158
165
|
this.#taskModeKey = options.taskModeKeys?.[0];
|
|
159
166
|
for (const key of options.taskModeKeys ?? []) addKeyAliases(this.#taskMatchKeys, key);
|
|
160
167
|
}
|
|
@@ -165,7 +172,7 @@ export class ModelPickerComponent implements Component {
|
|
|
165
172
|
|
|
166
173
|
this.#browser = new ModelBrowser(settings, {
|
|
167
174
|
currentContextTokens: options.currentContextTokens,
|
|
168
|
-
markOverContext:
|
|
175
|
+
markOverContext: !this.#selectOnly,
|
|
169
176
|
emptyText: () =>
|
|
170
177
|
this.#roleMode
|
|
171
178
|
? ` No quick roles in the ${editorKey("app.model.cycleForward") || formatKeyHint("ctrl+p")} cycle`
|
|
@@ -250,13 +257,13 @@ export class ModelPickerComponent implements Component {
|
|
|
250
257
|
|
|
251
258
|
/** Switch browser content only when a leading `@` changes the search mode. */
|
|
252
259
|
#syncItemsForQuery(query: string, refresh = false): void {
|
|
253
|
-
const roleMode = query.startsWith("@") && !this.#taskMode;
|
|
260
|
+
const roleMode = !this.#selectOnly && query.startsWith("@") && !this.#taskMode;
|
|
254
261
|
const modeChanged = roleMode !== this.#roleMode;
|
|
255
262
|
if (!modeChanged && !refresh) return;
|
|
256
263
|
|
|
257
264
|
this.#roleMode = roleMode;
|
|
258
265
|
this.#browser.setShowProvider(!roleMode);
|
|
259
|
-
this.#browser.setMarkOverContext(!roleMode && !this.#taskMode);
|
|
266
|
+
this.#browser.setMarkOverContext(!this.#selectOnly && !roleMode && !this.#taskMode);
|
|
260
267
|
this.#browser.setPreserveQueryOrder(roleMode);
|
|
261
268
|
const currentSelector = roleMode
|
|
262
269
|
? this.#currentQuickRoleSelector
|
|
@@ -299,20 +306,32 @@ export class ModelPickerComponent implements Component {
|
|
|
299
306
|
this.#browser.setMaxVisible(Math.max(MIN_VISIBLE, listBudget));
|
|
300
307
|
|
|
301
308
|
const inner = Math.max(1, width - 4);
|
|
309
|
+
const selectionOnly = this.#selectOnly;
|
|
302
310
|
const status = this.#configError
|
|
303
311
|
? theme.fg("error", ` ${this.#configError}`)
|
|
304
312
|
: this.#taskMode
|
|
305
313
|
? theme.fg("error", ` ${TASK_STATUS_HINT}`)
|
|
306
|
-
: theme.fg(
|
|
314
|
+
: theme.fg(
|
|
315
|
+
"muted",
|
|
316
|
+
` ${selectionOnly ? SELECT_ONLY_STATUS_HINT : this.#roleMode ? QUICK_ROLE_STATUS_HINT : STATUS_HINT}`,
|
|
317
|
+
);
|
|
307
318
|
|
|
308
319
|
const borderColor: ThemeColor | undefined = this.#taskMode ? "error" : undefined;
|
|
309
|
-
let footer = footerHint(
|
|
320
|
+
let footer = footerHint(
|
|
321
|
+
this.#taskMode ? "task" : this.#roleMode ? "role" : selectionOnly ? "selection" : "session",
|
|
322
|
+
);
|
|
310
323
|
if (this.#taskModeKey !== undefined && !this.#roleMode) {
|
|
311
324
|
footer += ` · ${formatKeyHint(this.#taskModeKey)} ${this.#taskMode ? "session model" : "task model"}`;
|
|
312
325
|
}
|
|
313
326
|
|
|
314
327
|
const out: string[] = [];
|
|
315
|
-
out.push(
|
|
328
|
+
out.push(
|
|
329
|
+
topBorder(
|
|
330
|
+
width,
|
|
331
|
+
this.#taskMode ? "Switch Task Model" : selectionOnly ? "Select Model" : "Switch Model",
|
|
332
|
+
borderColor,
|
|
333
|
+
),
|
|
334
|
+
);
|
|
316
335
|
out.push(row(status, width, borderColor));
|
|
317
336
|
for (const line of this.#browser.render(inner)) {
|
|
318
337
|
out.push(row(line, width, borderColor));
|
|
@@ -359,7 +378,7 @@ export class ModelPickerComponent implements Component {
|
|
|
359
378
|
}
|
|
360
379
|
|
|
361
380
|
#describePicker(): NativeNode {
|
|
362
|
-
const mode = this.#taskMode ? "task" : this.#roleMode ? "role" : "session";
|
|
381
|
+
const mode = this.#selectOnly ? "selection" : this.#taskMode ? "task" : this.#roleMode ? "role" : "session";
|
|
363
382
|
const query = this.#browser.query;
|
|
364
383
|
const view = this.#browser.pickerOrder({
|
|
365
384
|
providers: false,
|
|
@@ -390,13 +409,15 @@ export class ModelPickerComponent implements Component {
|
|
|
390
409
|
? [span(this.#configError, "error")]
|
|
391
410
|
: mode === "task"
|
|
392
411
|
? [span(TASK_STATUS_HINT, "warning")]
|
|
393
|
-
: mode === "
|
|
394
|
-
?
|
|
395
|
-
:
|
|
412
|
+
: mode === "selection"
|
|
413
|
+
? SELECT_ONLY_STATUS_HINT
|
|
414
|
+
: mode === "role"
|
|
415
|
+
? QUICK_ROLE_STATUS_HINT
|
|
416
|
+
: STATUS_HINT;
|
|
396
417
|
const cycleKey = editorKey("app.model.cycleForward") || formatKeyHint("ctrl+p");
|
|
397
418
|
const node = picker(
|
|
398
419
|
{
|
|
399
|
-
title: mode === "task" ? "Switch task model" : "Switch model",
|
|
420
|
+
title: mode === "task" ? "Switch task model" : mode === "selection" ? "Select model" : "Switch model",
|
|
400
421
|
subtitle,
|
|
401
422
|
icon: "cpu",
|
|
402
423
|
noun: mode === "role" ? "roles" : "models",
|
|
@@ -405,7 +426,8 @@ export class ModelPickerComponent implements Component {
|
|
|
405
426
|
preview: "below",
|
|
406
427
|
query,
|
|
407
428
|
cursor,
|
|
408
|
-
placeholder:
|
|
429
|
+
placeholder:
|
|
430
|
+
mode === "task" || mode === "selection" ? "Search models…" : "Search models, @ for quick roles…",
|
|
409
431
|
columns: MODEL_PICKER_COLUMNS,
|
|
410
432
|
...catalogue,
|
|
411
433
|
order: view.order,
|
|
@@ -417,11 +439,17 @@ export class ModelPickerComponent implements Component {
|
|
|
417
439
|
actions: compact([
|
|
418
440
|
pickerAction(
|
|
419
441
|
"use",
|
|
420
|
-
mode === "role"
|
|
442
|
+
mode === "role"
|
|
443
|
+
? "Apply role"
|
|
444
|
+
: mode === "task"
|
|
445
|
+
? "Use for Task subagents"
|
|
446
|
+
: mode === "selection"
|
|
447
|
+
? "Select model"
|
|
448
|
+
: "Use for session",
|
|
421
449
|
"enter",
|
|
422
450
|
{ primary: true },
|
|
423
451
|
),
|
|
424
|
-
this.#taskModeKey !== undefined && mode !== "role"
|
|
452
|
+
this.#taskModeKey !== undefined && mode !== "role" && mode !== "selection"
|
|
425
453
|
? pickerAction("task", "Task model", this.#taskModeKey, { on: mode === "task" })
|
|
426
454
|
: undefined,
|
|
427
455
|
query.length > 0 ? { ...CLOSE_ACTION, label: "Clear search" } : CLOSE_ACTION,
|
|
@@ -435,7 +463,7 @@ export class ModelPickerComponent implements Component {
|
|
|
435
463
|
|
|
436
464
|
/** Root card over the status line, the embedded browser, and the mode's key hints. */
|
|
437
465
|
#describeCard(): NativeNode {
|
|
438
|
-
const mode = this.#taskMode ? "task" : this.#roleMode ? "role" : "session";
|
|
466
|
+
const mode = this.#selectOnly ? "selection" : this.#taskMode ? "task" : this.#roleMode ? "role" : "session";
|
|
439
467
|
const memo = `${mode}\0${this.#configError ?? ""}\0${this.#taskModeKey ?? ""}`;
|
|
440
468
|
if (this.#nativeRoot?.memo === memo) return this.#nativeRoot.node;
|
|
441
469
|
|
|
@@ -443,7 +471,14 @@ export class ModelPickerComponent implements Component {
|
|
|
443
471
|
? span(this.#configError, "error")
|
|
444
472
|
: this.#taskMode
|
|
445
473
|
? span(TASK_STATUS_HINT, "error")
|
|
446
|
-
: span(
|
|
474
|
+
: span(
|
|
475
|
+
mode === "selection"
|
|
476
|
+
? SELECT_ONLY_STATUS_HINT
|
|
477
|
+
: this.#roleMode
|
|
478
|
+
? QUICK_ROLE_STATUS_HINT
|
|
479
|
+
: STATUS_HINT,
|
|
480
|
+
"muted",
|
|
481
|
+
);
|
|
447
482
|
const hints: (NativeHint | undefined)[] = [
|
|
448
483
|
actionHint(["tui.select.up", "tui.select.down"], mode === "role" ? "roles" : "models"),
|
|
449
484
|
{
|
|
@@ -453,20 +488,22 @@ export class ModelPickerComponent implements Component {
|
|
|
453
488
|
? "apply role model"
|
|
454
489
|
: mode === "task"
|
|
455
490
|
? "use for Task subagents"
|
|
456
|
-
:
|
|
491
|
+
: mode === "selection"
|
|
492
|
+
? "select model"
|
|
493
|
+
: "use for this session",
|
|
457
494
|
},
|
|
458
495
|
{ keys: [], label: "type to search" },
|
|
459
496
|
];
|
|
460
497
|
if (mode === "session") hints.push({ keys: ["@"], label: "quick roles" });
|
|
461
498
|
hints.push(actionHint("tui.select.cancel", "close"));
|
|
462
|
-
if (this.#taskModeKey !== undefined && !this.#roleMode) {
|
|
499
|
+
if (this.#taskModeKey !== undefined && !this.#roleMode && mode !== "selection") {
|
|
463
500
|
hints.push({ keys: [this.#taskModeKey], label: this.#taskMode ? "session model" : "task model" });
|
|
464
501
|
}
|
|
465
502
|
// `card` directly: the task-mode tone is a common prop `overlayCard` doesn't take.
|
|
466
503
|
const node = card(
|
|
467
504
|
{
|
|
468
505
|
role: "zeta.overlay.model-picker",
|
|
469
|
-
head:
|
|
506
|
+
head: mode === "task" ? "Switch Task Model" : mode === "selection" ? "Select Model" : "Switch Model",
|
|
470
507
|
tone: this.#taskMode ? "error" : undefined,
|
|
471
508
|
},
|
|
472
509
|
[text([status], { wrap: "word" }), this.#browser, hintsRow(hints)],
|
|
@@ -20,7 +20,7 @@ import { matchesAppInterrupt } from "../keybinding-matchers";
|
|
|
20
20
|
import { formatKeyHint, formatTooltipKey } from "../app-keybindings";
|
|
21
21
|
import { boundKeys, interruptKey } from "../chrome/keybinding-hints";
|
|
22
22
|
import type { NativeNode, NativeUiEvent } from "../native/node";
|
|
23
|
-
import { col, node, row, span, text } from "../native/describe";
|
|
23
|
+
import { col, keyed, node, row, span, text } from "../native/describe";
|
|
24
24
|
import { actionBar, actionButton } from "../native/overlay";
|
|
25
25
|
import { isNativeRendering } from "../native/state";
|
|
26
26
|
|
|
@@ -57,6 +57,14 @@ const BODY_LINES = [
|
|
|
57
57
|
"In-flight calls finish; nothing new starts until you resume.",
|
|
58
58
|
] as const;
|
|
59
59
|
|
|
60
|
+
/** What the hold means, one fact per row of the native sheet: icon, what, detail. */
|
|
61
|
+
const HOLD_FACTS = [
|
|
62
|
+
["users", "Every agent holds", "Main agent, subagents and advisor stop at their next step."],
|
|
63
|
+
["activity", "Running calls finish", "A reply or tool already under way completes; nothing new starts."],
|
|
64
|
+
["message", "Queued messages wait", "Steering and follow-ups already queued send after you resume."],
|
|
65
|
+
["pen", "The repo is yours", "Edit files freely, then tell the agent what changed."],
|
|
66
|
+
] as const;
|
|
67
|
+
|
|
60
68
|
/** Live hold clock, seconds-precise: `0:07`, `12:34`, `1:02:03`. */
|
|
61
69
|
function formatClock(ms: number): string {
|
|
62
70
|
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
|
@@ -176,33 +184,83 @@ export class PauseScreenComponent implements Component, OverlayFocusOwner {
|
|
|
176
184
|
/** A glass sheet titled "Paused" (not the whole pane: the transcript stays visible, frozen). */
|
|
177
185
|
readonly nativeOverlay = { role: "zeta.overlay.pause", size: "md", anchor: "center", head: "Paused" } as const;
|
|
178
186
|
|
|
187
|
+
/**
|
|
188
|
+
* The sheet: a hero (the pause glyph beside the live hold clock and the
|
|
189
|
+
* session it holds), what the hold means as four facts, and a footer that
|
|
190
|
+
* reassures and resumes. The resume keys live in the button's tooltip.
|
|
191
|
+
*/
|
|
179
192
|
describe(): NativeNode {
|
|
180
193
|
if (this.#native) return this.#native;
|
|
181
|
-
const children: NativeNode[] = [];
|
|
182
|
-
if (this.host.sessionName) children.push(text([span(this.host.sessionName, "strong")]));
|
|
183
194
|
const resumeKey = boundKeys("app.interrupt", ["escape"])[0] ?? "escape";
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
const hero = row(
|
|
196
|
+
[
|
|
197
|
+
node("icon", { name: "pause", role: "zeta.pause.glyph" }, undefined, "glyph"),
|
|
198
|
+
col(
|
|
199
|
+
[
|
|
200
|
+
node(
|
|
201
|
+
"elapsed",
|
|
202
|
+
{
|
|
203
|
+
age: Date.now() - this.#startedAt,
|
|
204
|
+
format: "clock",
|
|
205
|
+
role: "zeta.pause.clock",
|
|
206
|
+
title: "Paused for",
|
|
207
|
+
},
|
|
208
|
+
undefined,
|
|
209
|
+
"clock",
|
|
210
|
+
),
|
|
211
|
+
keyed(
|
|
212
|
+
text([span(this.host.sessionName ?? "Every agent in this session", "muted")], {
|
|
213
|
+
role: "zeta.pause.session",
|
|
214
|
+
wrap: "none",
|
|
215
|
+
truncate: "end",
|
|
216
|
+
}),
|
|
217
|
+
"session",
|
|
218
|
+
),
|
|
219
|
+
],
|
|
220
|
+
{ gap: "none", grow: 1, shrink: 1 },
|
|
221
|
+
),
|
|
222
|
+
],
|
|
223
|
+
{ role: "zeta.pause.hero", gap: "md", align: "center" },
|
|
224
|
+
);
|
|
225
|
+
const facts = col(
|
|
226
|
+
HOLD_FACTS.map(([icon, what, detail], index) =>
|
|
227
|
+
keyed(
|
|
228
|
+
row(
|
|
229
|
+
[
|
|
230
|
+
node("icon", { name: icon }),
|
|
231
|
+
col([text([span(what, "strong")]), text([span(detail, "muted")], { wrap: "word" })], {
|
|
232
|
+
gap: "none",
|
|
233
|
+
grow: 1,
|
|
234
|
+
shrink: 1,
|
|
235
|
+
}),
|
|
236
|
+
],
|
|
237
|
+
{ role: "zeta.pause.fact", gap: "md", align: "start" },
|
|
238
|
+
),
|
|
239
|
+
`fact-${index}`,
|
|
240
|
+
),
|
|
195
241
|
),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
242
|
+
{ role: "zeta.pause.facts", gap: "md" },
|
|
243
|
+
);
|
|
244
|
+
this.#native = col(
|
|
245
|
+
[
|
|
246
|
+
keyed(hero, "hero"),
|
|
247
|
+
keyed(facts, "facts"),
|
|
248
|
+
actionBar([
|
|
249
|
+
keyed(
|
|
250
|
+
text([span("Each agent continues where it stopped.", "muted")], { role: "zeta.pause.note" }),
|
|
251
|
+
"note",
|
|
252
|
+
),
|
|
253
|
+
null,
|
|
254
|
+
actionButton("Resume", "resume", {
|
|
255
|
+
keys: resumeKey,
|
|
256
|
+
tone: "accent",
|
|
257
|
+
// Each ` · ` clause ending in two spaces and a key is a keycap: `Resume [esc] · [⏎] · [space]`.
|
|
258
|
+
title: `Resume ${formatTooltipKey(resumeKey)} · ${formatTooltipKey("enter")} · ${formatTooltipKey("space")}`,
|
|
259
|
+
}),
|
|
260
|
+
]),
|
|
261
|
+
],
|
|
262
|
+
{ gap: "md" },
|
|
204
263
|
);
|
|
205
|
-
this.#native = col(children, { gap: "md" });
|
|
206
264
|
return this.#native;
|
|
207
265
|
}
|
|
208
266
|
|
|
@@ -606,6 +606,22 @@ export class PlanReviewOverlay implements Component {
|
|
|
606
606
|
slider.onChange?.(next);
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
+
/** Synchronize an external tier pick with the displayed slider before committing. */
|
|
610
|
+
setSliderIndex(index: number): void {
|
|
611
|
+
if (this.#committed || !this.#slider) return;
|
|
612
|
+
this.#moveSlider(index - this.#sliderIndex);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** Commit an external answer through the same input-locking path as Enter; false when nothing was committed. */
|
|
616
|
+
selectOption(label: string): boolean {
|
|
617
|
+
if (this.#committed) return false;
|
|
618
|
+
const index = this.#options.indexOf(label);
|
|
619
|
+
if (index < 0 || this.#disabled.has(index)) return false;
|
|
620
|
+
this.#selectedIndex = index;
|
|
621
|
+
this.#confirmSelection();
|
|
622
|
+
return this.#committed;
|
|
623
|
+
}
|
|
624
|
+
|
|
609
625
|
#confirmSelection(): void {
|
|
610
626
|
const index = this.#selectedIndex;
|
|
611
627
|
if (index >= 0 && index < this.#options.length && !this.#disabled.has(index)) {
|
|
@@ -11,6 +11,7 @@ import { routeSelectListMouseWithTopBorder } from "../chrome/select-list-mouse-r
|
|
|
11
11
|
import { tuiText } from "../i18n";
|
|
12
12
|
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
|
|
13
13
|
import { SelectListSheet } from "../native/picker";
|
|
14
|
+
import type { TspPickerItem } from "@linxiraos/pi-wire";
|
|
14
15
|
|
|
15
16
|
export interface PluginSelectorCallbacks {
|
|
16
17
|
onSelect: (pluginName: string, marketplace: string, scope?: "user" | "project") => void;
|
|
@@ -36,6 +37,8 @@ export class PluginSelectorComponent extends OverlayPanel {
|
|
|
36
37
|
) {
|
|
37
38
|
super(tuiText("ssTabPlugins", "Plugins"), "zeta.overlay.plugins");
|
|
38
39
|
|
|
40
|
+
// The sheet draws version, install state and scope as badges instead of bracketed label text.
|
|
41
|
+
const badgesByValue = new Map<string, NonNullable<TspPickerItem["badges"]>>();
|
|
39
42
|
const items: SelectItem[] = plugins.map(({ plugin, marketplace, scope }) => {
|
|
40
43
|
// Encode scope into the value so onSelect can recover it without a parallel Map.
|
|
41
44
|
// Format: "name@marketplace" or "name@marketplace#scope"
|
|
@@ -46,6 +49,11 @@ export class PluginSelectorComponent extends OverlayPanel {
|
|
|
46
49
|
const scopeTag = scope
|
|
47
50
|
? ` [${scope === "user" ? tuiText("mcpScopeWordUser", "user") : tuiText("mcpScopeWordProject", "project")}]`
|
|
48
51
|
: "";
|
|
52
|
+
badgesByValue.set(id, [
|
|
53
|
+
...(plugin.version ? [{ text: plugin.version }] : []),
|
|
54
|
+
...(installed ? [{ text: "installed", tone: "success" as const }] : []),
|
|
55
|
+
...(scope ? [{ text: scope, tone: "accent" as const }] : []),
|
|
56
|
+
]);
|
|
49
57
|
|
|
50
58
|
return {
|
|
51
59
|
value: id,
|
|
@@ -86,7 +94,15 @@ export class PluginSelectorComponent extends OverlayPanel {
|
|
|
86
94
|
icon: "plus",
|
|
87
95
|
noun: "plugins",
|
|
88
96
|
confirm: "Install",
|
|
89
|
-
decorate: item =>
|
|
97
|
+
decorate: item => {
|
|
98
|
+
const [name] = splitPluginId(item.value);
|
|
99
|
+
if (!name) return undefined;
|
|
100
|
+
return {
|
|
101
|
+
label: name,
|
|
102
|
+
badges: badgesByValue.get(item.value) ?? [],
|
|
103
|
+
...(item.hint ? { facts: { marketplace: item.hint } } : {}),
|
|
104
|
+
};
|
|
105
|
+
},
|
|
90
106
|
columns: [{ id: "marketplace", format: "dim" }],
|
|
91
107
|
});
|
|
92
108
|
}
|
|
@@ -53,9 +53,19 @@ export class QueueModeSelectorComponent extends OverlayPanel {
|
|
|
53
53
|
this.addChild(this.#selectList);
|
|
54
54
|
this.#sheet = new SelectListSheet(this.#selectList, {
|
|
55
55
|
title: "Queue mode",
|
|
56
|
+
subtitle: "How messages you send while omp works are delivered",
|
|
56
57
|
icon: "list",
|
|
57
58
|
noun: "modes",
|
|
58
59
|
current: [currentMode],
|
|
60
|
+
// The terminal list shows the setting's values; the sheet names them for people.
|
|
61
|
+
decorate: item =>
|
|
62
|
+
item.value === "one-at-a-time"
|
|
63
|
+
? {
|
|
64
|
+
label: "One at a time",
|
|
65
|
+
detail: "Each queued message gets its own turn",
|
|
66
|
+
badges: [{ text: "recommended" }],
|
|
67
|
+
}
|
|
68
|
+
: { label: "All at once", detail: "Queued messages are sent together in one turn" },
|
|
59
69
|
});
|
|
60
70
|
}
|
|
61
71
|
|
|
@@ -129,11 +129,15 @@ export class ReportPanel extends OverlayPanel {
|
|
|
129
129
|
return this.#body.render(Math.max(1, width - 4)).length + TEXT_CHROME_ROWS;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* The sheet body (the overlay is the frame, and its body the scroller): the
|
|
134
|
+
* report as `zeta.report.body`, which the terminal sets on the reading
|
|
135
|
+
* measure, then Close (Esc) as the sheet's footer.
|
|
136
|
+
*/
|
|
133
137
|
override describe(): NativeNode {
|
|
134
138
|
const scroll = this.#scroll;
|
|
135
139
|
if (this.#native && this.#native.scroll === scroll) return this.#native.node;
|
|
136
|
-
const body: NativeNode = { ...col([this.#body]), key: "body", scroll };
|
|
140
|
+
const body: NativeNode = { ...col([this.#body], { role: "zeta.report.body" }), key: "body", scroll };
|
|
137
141
|
const node = col([body, actionBar([null, actionButton("Close", "close", { keys: "escape" })])], { gap: "md" });
|
|
138
142
|
this.#native = { scroll, node };
|
|
139
143
|
return node;
|