@mariozechner/pi-coding-agent 0.44.0 → 0.45.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.
@@ -1,12 +1,61 @@
1
1
  import { Container, fuzzyFilter, getEditorKeybindings, Input, Key, matchesKey, Spacer, Text, } from "@mariozechner/pi-tui";
2
2
  import { theme } from "../theme/theme.js";
3
3
  import { DynamicBorder } from "./dynamic-border.js";
4
+ function isEnabled(enabledIds, id) {
5
+ return enabledIds === null || enabledIds.includes(id);
6
+ }
7
+ function toggle(enabledIds, id) {
8
+ if (enabledIds === null)
9
+ return [id]; // First toggle: start with only this one
10
+ const index = enabledIds.indexOf(id);
11
+ if (index >= 0)
12
+ return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];
13
+ return [...enabledIds, id];
14
+ }
15
+ function enableAll(enabledIds, allIds, targetIds) {
16
+ if (enabledIds === null)
17
+ return null; // Already all enabled
18
+ const targets = targetIds ?? allIds;
19
+ const result = [...enabledIds];
20
+ for (const id of targets) {
21
+ if (!result.includes(id))
22
+ result.push(id);
23
+ }
24
+ return result.length === allIds.length ? null : result;
25
+ }
26
+ function clearAll(enabledIds, allIds, targetIds) {
27
+ if (enabledIds === null) {
28
+ return targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];
29
+ }
30
+ const targets = new Set(targetIds ?? enabledIds);
31
+ return enabledIds.filter((id) => !targets.has(id));
32
+ }
33
+ function move(enabledIds, allIds, id, delta) {
34
+ const list = enabledIds ?? [...allIds];
35
+ const index = list.indexOf(id);
36
+ if (index < 0)
37
+ return list;
38
+ const newIndex = index + delta;
39
+ if (newIndex < 0 || newIndex >= list.length)
40
+ return list;
41
+ const result = [...list];
42
+ [result[index], result[newIndex]] = [result[newIndex], result[index]];
43
+ return result;
44
+ }
45
+ function getSortedIds(enabledIds, allIds) {
46
+ if (enabledIds === null)
47
+ return allIds;
48
+ const enabledSet = new Set(enabledIds);
49
+ return [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];
50
+ }
4
51
  /**
5
52
  * Component for enabling/disabling models for Ctrl+P cycling.
6
53
  * Changes are session-only until explicitly persisted with Ctrl+S.
7
54
  */
8
55
  export class ScopedModelsSelectorComponent extends Container {
9
- items = [];
56
+ modelsById = new Map();
57
+ allIds = [];
58
+ enabledIds = null;
10
59
  filteredItems = [];
11
60
  selectedIndex = 0;
12
61
  searchInput;
@@ -18,27 +67,13 @@ export class ScopedModelsSelectorComponent extends Container {
18
67
  constructor(config, callbacks) {
19
68
  super();
20
69
  this.callbacks = callbacks;
21
- // Group models by provider for organized display
22
- const modelsByProvider = new Map();
23
70
  for (const model of config.allModels) {
24
- const list = modelsByProvider.get(model.provider) ?? [];
25
- list.push(model);
26
- modelsByProvider.set(model.provider, list);
71
+ const fullId = `${model.provider}/${model.id}`;
72
+ this.modelsById.set(fullId, model);
73
+ this.allIds.push(fullId);
27
74
  }
28
- // Build items - group by provider
29
- for (const [provider, models] of modelsByProvider) {
30
- for (const model of models) {
31
- const fullId = `${provider}/${model.id}`;
32
- // If no filter defined, all models are enabled by default
33
- const isEnabled = !config.hasEnabledModelsFilter || config.enabledModelIds.has(fullId);
34
- this.items.push({
35
- fullId,
36
- model,
37
- enabled: isEnabled,
38
- });
39
- }
40
- }
41
- this.filteredItems = this.getSortedItems();
75
+ this.enabledIds = config.hasEnabledModelsFilter ? [...config.enabledModelIds] : null;
76
+ this.filteredItems = this.buildItems();
42
77
  // Header
43
78
  this.addChild(new DynamicBorder());
44
79
  this.addChild(new Spacer(1));
@@ -59,35 +94,29 @@ export class ScopedModelsSelectorComponent extends Container {
59
94
  this.addChild(new DynamicBorder());
60
95
  this.updateList();
61
96
  }
62
- /** Get items sorted with enabled items first */
63
- getSortedItems() {
64
- const enabled = this.items.filter((i) => i.enabled);
65
- const disabled = this.items.filter((i) => !i.enabled);
66
- return [...enabled, ...disabled];
97
+ buildItems() {
98
+ return getSortedIds(this.enabledIds, this.allIds).map((id) => ({
99
+ fullId: id,
100
+ model: this.modelsById.get(id),
101
+ enabled: isEnabled(this.enabledIds, id),
102
+ }));
67
103
  }
68
104
  getFooterText() {
69
- const enabledCount = this.items.filter((i) => i.enabled).length;
70
- const allEnabled = enabledCount === this.items.length;
71
- const countText = allEnabled ? "all enabled" : `${enabledCount}/${this.items.length} enabled`;
72
- const parts = ["Enter toggle", "^A all", "^X clear", "^P provider", "^S save", countText];
73
- if (this.isDirty) {
74
- return theme.fg("dim", ` ${parts.join(" · ")} `) + theme.fg("warning", "(unsaved)");
75
- }
76
- return theme.fg("dim", ` ${parts.join(" · ")}`);
77
- }
78
- updateFooter() {
79
- this.footerText.setText(this.getFooterText());
105
+ const enabledCount = this.enabledIds?.length ?? this.allIds.length;
106
+ const allEnabled = this.enabledIds === null;
107
+ const countText = allEnabled ? "all enabled" : `${enabledCount}/${this.allIds.length} enabled`;
108
+ const parts = ["Enter toggle", "^A all", "^X clear", "^P provider", "Alt+↑↓ reorder", "^S save", countText];
109
+ return this.isDirty
110
+ ? theme.fg("dim", ` ${parts.join(" · ")} `) + theme.fg("warning", "(unsaved)")
111
+ : theme.fg("dim", ` ${parts.join(" · ")}`);
80
112
  }
81
- filterItems(query) {
82
- const sorted = this.getSortedItems();
83
- if (!query) {
84
- this.filteredItems = sorted;
85
- }
86
- else {
87
- this.filteredItems = fuzzyFilter(sorted, query, (item) => `${item.model.id} ${item.model.provider}`);
88
- }
113
+ refresh() {
114
+ const query = this.searchInput.getValue();
115
+ const items = this.buildItems();
116
+ this.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.model.id} ${i.model.provider}`) : items;
89
117
  this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));
90
118
  this.updateList();
119
+ this.footerText.setText(this.getFooterText());
91
120
  }
92
121
  updateList() {
93
122
  this.listContainer.clear();
@@ -97,47 +126,20 @@ export class ScopedModelsSelectorComponent extends Container {
97
126
  }
98
127
  const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible));
99
128
  const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);
100
- // Only show status if there's a filter (not all models enabled)
101
- const allEnabled = this.items.every((i) => i.enabled);
129
+ const allEnabled = this.enabledIds === null;
102
130
  for (let i = startIndex; i < endIndex; i++) {
103
131
  const item = this.filteredItems[i];
104
- if (!item)
105
- continue;
106
132
  const isSelected = i === this.selectedIndex;
107
133
  const prefix = isSelected ? theme.fg("accent", "→ ") : " ";
108
134
  const modelText = isSelected ? theme.fg("accent", item.model.id) : item.model.id;
109
135
  const providerBadge = theme.fg("muted", ` [${item.model.provider}]`);
110
- // Only show checkmarks when there's actually a filter
111
136
  const status = allEnabled ? "" : item.enabled ? theme.fg("success", " ✓") : theme.fg("dim", " ✗");
112
137
  this.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));
113
138
  }
114
139
  // Add scroll indicator if needed
115
140
  if (startIndex > 0 || endIndex < this.filteredItems.length) {
116
- const scrollInfo = theme.fg("muted", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`);
117
- this.listContainer.addChild(new Text(scrollInfo, 0, 0));
118
- }
119
- }
120
- toggleItem(item) {
121
- // If all models are currently enabled (no scope yet), first toggle starts fresh:
122
- // clear all and enable only the selected model
123
- const allEnabled = this.items.every((i) => i.enabled);
124
- if (allEnabled) {
125
- for (const i of this.items) {
126
- i.enabled = false;
127
- }
128
- item.enabled = true;
129
- this.isDirty = true;
130
- this.callbacks.onClearAll();
131
- this.callbacks.onModelToggle(item.fullId, true);
141
+ this.listContainer.addChild(new Text(theme.fg("muted", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0));
132
142
  }
133
- else {
134
- item.enabled = !item.enabled;
135
- this.isDirty = true;
136
- this.callbacks.onModelToggle(item.fullId, item.enabled);
137
- }
138
- // Re-sort and re-filter to move item to correct section
139
- this.filterItems(this.searchInput.getValue());
140
- this.updateFooter();
141
143
  }
142
144
  handleInput(data) {
143
145
  const kb = getEditorKeybindings();
@@ -156,69 +158,84 @@ export class ScopedModelsSelectorComponent extends Container {
156
158
  this.updateList();
157
159
  return;
158
160
  }
161
+ // Alt+Up/Down - Reorder enabled models
162
+ if (matchesKey(data, Key.alt("up")) || matchesKey(data, Key.alt("down"))) {
163
+ const item = this.filteredItems[this.selectedIndex];
164
+ if (item && isEnabled(this.enabledIds, item.fullId)) {
165
+ const delta = matchesKey(data, Key.alt("up")) ? -1 : 1;
166
+ const enabledList = this.enabledIds ?? this.allIds;
167
+ const currentIndex = enabledList.indexOf(item.fullId);
168
+ const newIndex = currentIndex + delta;
169
+ // Only move if within bounds
170
+ if (newIndex >= 0 && newIndex < enabledList.length) {
171
+ this.enabledIds = move(this.enabledIds, this.allIds, item.fullId, delta);
172
+ this.isDirty = true;
173
+ this.selectedIndex += delta;
174
+ this.refresh();
175
+ }
176
+ }
177
+ return;
178
+ }
159
179
  // Toggle on Enter
160
180
  if (matchesKey(data, Key.enter)) {
161
181
  const item = this.filteredItems[this.selectedIndex];
162
182
  if (item) {
163
- this.toggleItem(item);
183
+ const wasAllEnabled = this.enabledIds === null;
184
+ this.enabledIds = toggle(this.enabledIds, item.fullId);
185
+ this.isDirty = true;
186
+ if (wasAllEnabled)
187
+ this.callbacks.onClearAll();
188
+ this.callbacks.onModelToggle(item.fullId, isEnabled(this.enabledIds, item.fullId));
189
+ this.refresh();
164
190
  }
165
191
  return;
166
192
  }
167
193
  // Ctrl+A - Enable all (filtered if search active, otherwise all)
168
194
  if (matchesKey(data, Key.ctrl("a"))) {
169
- const targets = this.searchInput.getValue() ? this.filteredItems : this.items;
170
- for (const item of targets) {
171
- item.enabled = true;
172
- }
195
+ const targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;
196
+ this.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);
173
197
  this.isDirty = true;
174
- this.callbacks.onEnableAll(targets.map((i) => i.fullId));
175
- this.filterItems(this.searchInput.getValue());
176
- this.updateFooter();
198
+ this.callbacks.onEnableAll(targetIds ?? this.allIds);
199
+ this.refresh();
177
200
  return;
178
201
  }
179
202
  // Ctrl+X - Clear all (filtered if search active, otherwise all)
180
203
  if (matchesKey(data, Key.ctrl("x"))) {
181
- const targets = this.searchInput.getValue() ? this.filteredItems : this.items;
182
- for (const item of targets) {
183
- item.enabled = false;
184
- }
204
+ const targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;
205
+ this.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);
185
206
  this.isDirty = true;
186
207
  this.callbacks.onClearAll();
187
- this.filterItems(this.searchInput.getValue());
188
- this.updateFooter();
208
+ this.refresh();
189
209
  return;
190
210
  }
191
211
  // Ctrl+P - Toggle provider of current item
192
212
  if (matchesKey(data, Key.ctrl("p"))) {
193
- const currentItem = this.filteredItems[this.selectedIndex];
194
- if (currentItem) {
195
- const provider = currentItem.model.provider;
196
- const providerItems = this.items.filter((i) => i.model.provider === provider);
197
- const allEnabled = providerItems.every((i) => i.enabled);
198
- const newState = !allEnabled;
199
- for (const item of providerItems) {
200
- item.enabled = newState;
201
- }
213
+ const item = this.filteredItems[this.selectedIndex];
214
+ if (item) {
215
+ const provider = item.model.provider;
216
+ const providerIds = this.allIds.filter((id) => this.modelsById.get(id).provider === provider);
217
+ const allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));
218
+ this.enabledIds = allEnabled
219
+ ? clearAll(this.enabledIds, this.allIds, providerIds)
220
+ : enableAll(this.enabledIds, this.allIds, providerIds);
202
221
  this.isDirty = true;
203
- this.callbacks.onToggleProvider(provider, providerItems.map((i) => i.fullId), newState);
204
- this.filterItems(this.searchInput.getValue());
205
- this.updateFooter();
222
+ this.callbacks.onToggleProvider(provider, providerIds, !allEnabled);
223
+ this.refresh();
206
224
  }
207
225
  return;
208
226
  }
209
227
  // Ctrl+S - Save/persist to settings
210
228
  if (matchesKey(data, Key.ctrl("s"))) {
211
- const enabledIds = this.items.filter((i) => i.enabled).map((i) => i.fullId);
212
- this.callbacks.onPersist(enabledIds);
229
+ this.callbacks.onPersist(this.enabledIds ?? [...this.allIds]);
213
230
  this.isDirty = false;
214
- this.updateFooter();
231
+ this.footerText.setText(this.getFooterText());
215
232
  return;
216
233
  }
217
234
  // Ctrl+C - clear search or cancel if empty
218
235
  if (matchesKey(data, Key.ctrl("c"))) {
219
236
  if (this.searchInput.getValue()) {
220
237
  this.searchInput.setValue("");
221
- this.filterItems("");
238
+ this.refresh();
222
239
  }
223
240
  else {
224
241
  this.callbacks.onCancel();
@@ -232,7 +249,7 @@ export class ScopedModelsSelectorComponent extends Container {
232
249
  }
233
250
  // Pass everything else to search input
234
251
  this.searchInput.handleInput(data);
235
- this.filterItems(this.searchInput.getValue());
252
+ this.refresh();
236
253
  }
237
254
  getSearchInput() {
238
255
  return this.searchInput;
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-models-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any>;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: Set<string>;\n\t/** true if enabledModels setting is defined (empty = all enabled) */\n\thasEnabledModelsFilter: boolean;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called when a model is toggled (session-only, no persist) */\n\tonModelToggle: (modelId: string, enabled: boolean) => void;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[]) => void;\n\t/** Called when user enables all models. Returns list of all model IDs. */\n\tonEnableAll: (allModelIds: string[]) => void;\n\t/** Called when user clears all models */\n\tonClearAll: () => void;\n\t/** Called when user toggles all models for a provider. Returns affected model IDs. */\n\tonToggleProvider: (provider: string, modelIds: string[], enabled: boolean) => void;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container {\n\tprivate items: ModelItem[] = [];\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 15;\n\tprivate isDirty = false;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\t// Group models by provider for organized display\n\t\tconst modelsByProvider = new Map<string, Model<any>[]>();\n\t\tfor (const model of config.allModels) {\n\t\t\tconst list = modelsByProvider.get(model.provider) ?? [];\n\t\t\tlist.push(model);\n\t\t\tmodelsByProvider.set(model.provider, list);\n\t\t}\n\n\t\t// Build items - group by provider\n\t\tfor (const [provider, models] of modelsByProvider) {\n\t\t\tfor (const model of models) {\n\t\t\t\tconst fullId = `${provider}/${model.id}`;\n\t\t\t\t// If no filter defined, all models are enabled by default\n\t\t\t\tconst isEnabled = !config.hasEnabledModelsFilter || config.enabledModelIds.has(fullId);\n\t\t\t\tthis.items.push({\n\t\t\t\t\tfullId,\n\t\t\t\t\tmodel,\n\t\t\t\t\tenabled: isEnabled,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.filteredItems = this.getSortedItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Model Configuration\")), 0, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Session-only. Ctrl+S to save to settings.\"), 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\t/** Get items sorted with enabled items first */\n\tprivate getSortedItems(): ModelItem[] {\n\t\tconst enabled = this.items.filter((i) => i.enabled);\n\t\tconst disabled = this.items.filter((i) => !i.enabled);\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.items.filter((i) => i.enabled).length;\n\t\tconst allEnabled = enabledCount === this.items.length;\n\t\tconst countText = allEnabled ? \"all enabled\" : `${enabledCount}/${this.items.length} enabled`;\n\t\tconst parts = [\"Enter toggle\", \"^A all\", \"^X clear\", \"^P provider\", \"^S save\", countText];\n\t\tif (this.isDirty) {\n\t\t\treturn theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\");\n\t\t}\n\t\treturn theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate updateFooter(): void {\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate filterItems(query: string): void {\n\t\tconst sorted = this.getSortedItems();\n\t\tif (!query) {\n\t\t\tthis.filteredItems = sorted;\n\t\t} else {\n\t\t\tthis.filteredItems = fuzzyFilter(sorted, query, (item) => `${item.model.id} ${item.model.provider}`);\n\t\t}\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\t// Only show status if there's a filter (not all models enabled)\n\t\tconst allEnabled = this.items.every((i) => i.enabled);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", item.model.id) : item.model.id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", ` [${item.model.provider}]`);\n\t\t\t// Only show checkmarks when there's actually a filter\n\t\t\tconst status = allEnabled ? \"\" : item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\t}\n\n\tprivate toggleItem(item: ModelItem): void {\n\t\t// If all models are currently enabled (no scope yet), first toggle starts fresh:\n\t\t// clear all and enable only the selected model\n\t\tconst allEnabled = this.items.every((i) => i.enabled);\n\t\tif (allEnabled) {\n\t\t\tfor (const i of this.items) {\n\t\t\t\ti.enabled = false;\n\t\t\t}\n\t\t\titem.enabled = true;\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.callbacks.onModelToggle(item.fullId, true);\n\t\t} else {\n\t\t\titem.enabled = !item.enabled;\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onModelToggle(item.fullId, item.enabled);\n\t\t}\n\t\t// Re-sort and re-filter to move item to correct section\n\t\tthis.filterItems(this.searchInput.getValue());\n\t\tthis.updateFooter();\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (matchesKey(data, Key.enter)) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tthis.toggleItem(item);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+A - Enable all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"a\"))) {\n\t\t\tconst targets = this.searchInput.getValue() ? this.filteredItems : this.items;\n\t\t\tfor (const item of targets) {\n\t\t\t\titem.enabled = true;\n\t\t\t}\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onEnableAll(targets.map((i) => i.fullId));\n\t\t\tthis.filterItems(this.searchInput.getValue());\n\t\t\tthis.updateFooter();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+X - Clear all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"x\"))) {\n\t\t\tconst targets = this.searchInput.getValue() ? this.filteredItems : this.items;\n\t\t\tfor (const item of targets) {\n\t\t\t\titem.enabled = false;\n\t\t\t}\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.filterItems(this.searchInput.getValue());\n\t\t\tthis.updateFooter();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P - Toggle provider of current item\n\t\tif (matchesKey(data, Key.ctrl(\"p\"))) {\n\t\t\tconst currentItem = this.filteredItems[this.selectedIndex];\n\t\t\tif (currentItem) {\n\t\t\t\tconst provider = currentItem.model.provider;\n\t\t\t\tconst providerItems = this.items.filter((i) => i.model.provider === provider);\n\t\t\t\tconst allEnabled = providerItems.every((i) => i.enabled);\n\t\t\t\tconst newState = !allEnabled;\n\t\t\t\tfor (const item of providerItems) {\n\t\t\t\t\titem.enabled = newState;\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.callbacks.onToggleProvider(\n\t\t\t\t\tprovider,\n\t\t\t\t\tproviderItems.map((i) => i.fullId),\n\t\t\t\t\tnewState,\n\t\t\t\t);\n\t\t\t\tthis.filterItems(this.searchInput.getValue());\n\t\t\t\tthis.updateFooter();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+S - Save/persist to settings\n\t\tif (matchesKey(data, Key.ctrl(\"s\"))) {\n\t\t\tconst enabledIds = this.items.filter((i) => i.enabled).map((i) => i.fullId);\n\t\t\tthis.callbacks.onPersist(enabledIds);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.updateFooter();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.filterItems(\"\");\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.filterItems(this.searchInput.getValue());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"scoped-models-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\nfunction toggle(enabledIds: EnabledIds, id: string): EnabledIds {\n\tif (enabledIds === null) return [id]; // First toggle: start with only this one\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn [...enabledIds, id];\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn result.length === allIds.length ? null : result;\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, allIds: string[], id: string, delta: number): EnabledIds {\n\tconst list = enabledIds ?? [...allIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any>;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: Set<string>;\n\t/** true if enabledModels setting is defined (empty = all enabled) */\n\thasEnabledModelsFilter: boolean;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called when a model is toggled (session-only, no persist) */\n\tonModelToggle: (modelId: string, enabled: boolean) => void;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[]) => void;\n\t/** Called when user enables all models. Returns list of all model IDs. */\n\tonEnableAll: (allModelIds: string[]) => void;\n\t/** Called when user clears all models */\n\tonClearAll: () => void;\n\t/** Called when user toggles all models for a provider. Returns affected model IDs. */\n\tonToggleProvider: (provider: string, modelIds: string[], enabled: boolean) => void;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 15;\n\tprivate isDirty = false;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.hasEnabledModelsFilter ? [...config.enabledModelIds] : null;\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Model Configuration\")), 0, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Session-only. Ctrl+S to save to settings.\"), 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\treturn getSortedIds(this.enabledIds, this.allIds).map((id) => ({\n\t\t\tfullId: id,\n\t\t\tmodel: this.modelsById.get(id)!,\n\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.length ?? this.allIds.length;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled ? \"all enabled\" : `${enabledCount}/${this.allIds.length} enabled`;\n\t\tconst parts = [\"Enter toggle\", \"^A all\", \"^X clear\", \"^P provider\", \"Alt+↑↓ reorder\", \"^S save\", countText];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.model.id} ${i.model.provider}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tconst allEnabled = this.enabledIds === null;\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", item.model.id) : item.model.id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", ` [${item.model.provider}]`);\n\t\t\tconst status = allEnabled ? \"\" : item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Alt+Up/Down - Reorder enabled models\n\t\tif (matchesKey(data, Key.alt(\"up\")) || matchesKey(data, Key.alt(\"down\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = matchesKey(data, Key.alt(\"up\")) ? -1 : 1;\n\t\t\t\tconst enabledList = this.enabledIds ?? this.allIds;\n\t\t\t\tconst currentIndex = enabledList.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < enabledList.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, this.allIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (matchesKey(data, Key.enter)) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst wasAllEnabled = this.enabledIds === null;\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tif (wasAllEnabled) this.callbacks.onClearAll();\n\t\t\t\tthis.callbacks.onModelToggle(item.fullId, isEnabled(this.enabledIds, item.fullId));\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+A - Enable all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"a\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onEnableAll(targetIds ?? this.allIds);\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+X - Clear all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"x\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P - Toggle provider of current item\n\t\tif (matchesKey(data, Key.ctrl(\"p\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.callbacks.onToggleProvider(provider, providerIds, !allEnabled);\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+S - Save/persist to settings\n\t\tif (matchesKey(data, Key.ctrl(\"s\"))) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds ?? [...this.allIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -22,6 +22,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
22
22
  | `protected-paths.ts` | Blocks writes to protected paths (.env, .git/, node_modules/) |
23
23
  | `confirm-destructive.ts` | Confirms before destructive session actions (clear, switch, fork) |
24
24
  | `dirty-repo-guard.ts` | Prevents session changes with uncommitted git changes |
25
+ | `sandbox/` | OS-level sandboxing using `@anthropic-ai/sandbox-runtime` with per-project config |
25
26
 
26
27
  ### Custom Tools
27
28