diff options
| author | t <t@tjp.lol> | 2026-06-15 17:08:00 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-15 17:18:20 -0600 |
| commit | 9f8719929dbd1326d9b327885c4da319c6d2673c (patch) | |
| tree | cb7cd72f0be46c099e9831364ba7d5cbedebeb09 /src/tui_app.zig | |
| parent | d9d4131cb321a9ce29b000cd7aed8ced9a18efc1 (diff) | |
anthropic credentials in oauth providers (eg copilot)
Diffstat (limited to 'src/tui_app.zig')
| -rw-r--r-- | src/tui_app.zig | 50 |
1 files changed, 44 insertions, 6 deletions
diff --git a/src/tui_app.zig b/src/tui_app.zig index 69eb8fa..f9c58fe 100644 --- a/src/tui_app.zig +++ b/src/tui_app.zig @@ -1008,6 +1008,17 @@ pub const App = struct { /// `ModelRegistry`, and `panto.Agent` (all owned by `main`). The model labels /// and provider/model wire strings the rebuilt config borrows therefore stay /// valid for the whole session. +const ModelPickerRow = struct { + def_index: usize, + item: SelectorItem, +}; + +fn modelPickerRowLessThan(_: void, a: ModelPickerRow, b: ModelPickerRow) bool { + const by_label = std.mem.order(u8, a.item.label, b.item.label); + if (by_label != .eq) return by_label == .lt; + return std.mem.lessThan(u8, a.item.detail, b.item.detail); +} + pub const SelectorController = struct { alloc: std.mem.Allocator, app: *App, @@ -1024,6 +1035,8 @@ pub const SelectorController = struct { /// Built selector item list for the model picker (label = "provider:alias", /// detail = "<wire> <knobs>"). Owned: labels/details are allocated here. model_items: []SelectorItem, + /// Parallel to `model_items`: source registry index for each SORTED row. + model_entry_indices: []usize = &.{}, /// Backing storage for the model item label/detail strings. model_strings: std.ArrayList([]u8) = .empty, /// Reasoning picker items for the CURRENTLY OPEN reasoning picker, rebuilt @@ -1089,6 +1102,7 @@ pub const SelectorController = struct { for (self.model_strings.items) |s| self.alloc.free(s); self.model_strings.deinit(self.alloc); self.alloc.free(self.model_items); + self.alloc.free(self.model_entry_indices); self.alloc.free(self.reasoning_items); self.alloc.free(self.model_label); self.alloc.destroy(self); @@ -1097,16 +1111,22 @@ pub const SelectorController = struct { /// Build the model picker items from every `models.toml` definition. fn buildModelItems(self: *SelectorController) !void { const a = self.alloc; - var items: std.ArrayList(SelectorItem) = .empty; - defer items.deinit(a); - for (self.defs.entries.items) |d| { + var rows: std.ArrayList(ModelPickerRow) = .empty; + defer rows.deinit(a); + for (self.defs.entries.items, 0..) |d, def_index| { const label = try std.fmt.allocPrint(a, "{s}:{s}", .{ d.provider, d.alias }); try self.model_strings.append(a, label); const detail = try formatModelDetail(a, d); try self.model_strings.append(a, detail); - try items.append(a, .{ .label = label, .detail = detail }); + try rows.append(a, .{ .def_index = def_index, .item = .{ .label = label, .detail = detail } }); + } + std.mem.sort(ModelPickerRow, rows.items, {}, modelPickerRowLessThan); + self.model_items = try a.alloc(SelectorItem, rows.items.len); + self.model_entry_indices = try a.alloc(usize, rows.items.len); + for (rows.items, 0..) |row, i| { + self.model_items[i] = row.item; + self.model_entry_indices[i] = row.def_index; } - self.model_items = try items.toOwnedSlice(a); } /// Open the model picker, preselecting the current model. @@ -1191,7 +1211,8 @@ pub const SelectorController = struct { } fn applyModel(self: *SelectorController, index: usize) !void { - const d = self.defs.entries.items[index]; + if (index >= self.model_entry_indices.len) return; + const d = self.defs.entries.items[self.model_entry_indices[index]]; const ref: config_file.ModelRef = .{ .provider = d.provider, .model = d.alias }; const new_provider = config_file.buildProviderConfig(self.file_cfg, self.defs, ref) catch |err| { try self.statusf("[model switch failed: {s}]", .{@errorName(err)}); @@ -2314,6 +2335,23 @@ test "formatModelDetail: shows wire model + non-default knobs" { } } +test "model picker rows sort by provider:alias and keep mapping" { + var rows = [_]ModelPickerRow{ + .{ .def_index = 2, .item = .{ .label = "openai:gpt-4o", .detail = "gpt-4o" } }, + .{ .def_index = 0, .item = .{ .label = "anthropic:sonnet", .detail = "claude-sonnet-4" } }, + .{ .def_index = 1, .item = .{ .label = "anthropic:haiku", .detail = "claude-haiku-4" } }, + }; + + std.mem.sort(ModelPickerRow, rows[0..], {}, modelPickerRowLessThan); + + try testing.expectEqualStrings("anthropic:haiku", rows[0].item.label); + try testing.expectEqual(@as(usize, 1), rows[0].def_index); + try testing.expectEqualStrings("anthropic:sonnet", rows[1].item.label); + try testing.expectEqual(@as(usize, 0), rows[1].def_index); + try testing.expectEqualStrings("openai:gpt-4o", rows[2].item.label); + try testing.expectEqual(@as(usize, 2), rows[2].def_index); +} + test "toggleToolCollapse flips every tool component globally" { const alloc = testing.allocator; const h = try Harness.make(alloc); |
