mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
agent: clean up save mode and terminal prompts
- Use `std.meta.fieldNames` for the save mode prompt. - Update terminal prompt functions to accept null-terminated strings. - Respect the resolved save mode in `synthesizeSave`.
This commit is contained in:
@@ -677,7 +677,7 @@ fn setProvider(self: *Agent, credentials: Credentials) !void {
|
||||
_ = completionModels(self, self.allocator);
|
||||
}
|
||||
|
||||
const SaveMode = enum { replace, append };
|
||||
const SaveMode = enum { append, replace };
|
||||
|
||||
const PathAndMode = struct { path: []const u8, mode: SaveMode };
|
||||
|
||||
@@ -818,12 +818,11 @@ fn promptSaveMode(self: *Agent, path: []const u8) ?SaveMode {
|
||||
var header_buf: [256]u8 = undefined;
|
||||
const header = std.fmt.bufPrint(&header_buf, "{s} already exists. Pick save mode:", .{path}) catch
|
||||
"File already exists. Pick save mode:";
|
||||
const labels: []const []const u8 = &.{ "replace", "append" };
|
||||
const idx = Terminal.promptNumberedChoice(header, labels, null) catch {
|
||||
const idx = Terminal.promptNumberedChoice(header, std.meta.fieldNames(SaveMode), 0) catch {
|
||||
self.terminal.printInfo("Save cancelled.", .{});
|
||||
return null;
|
||||
};
|
||||
return if (idx == 0) .replace else .append;
|
||||
return @enumFromInt(idx);
|
||||
}
|
||||
|
||||
fn writeSaveFile(self: *Agent, path: []const u8, mode: SaveMode) !void {
|
||||
@@ -848,18 +847,12 @@ fn failSave(self: *Agent, reason: []const u8) void {
|
||||
|
||||
/// LLM-synthesized `/save`: hand the model the builtin catalog, the full
|
||||
/// conversation, and the deterministic record of what ran, then write the
|
||||
/// idiomatic script it returns. Always replaces the target file.
|
||||
/// idiomatic script it returns.
|
||||
fn synthesizeSave(self: *Agent, arena: std.mem.Allocator, filename: ?[]const u8, prompt: ?[]const u8) void {
|
||||
const provider_client = self.ai_client.?;
|
||||
|
||||
const path: []const u8 = blk: {
|
||||
if (filename) |f| break :blk f;
|
||||
if (self.save_path) |p| break :blk p;
|
||||
break :blk randomSaveFilename(arena) catch |err| {
|
||||
self.terminal.printError("failed to choose save filename: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
};
|
||||
const resolved = self.resolveSavePathAndMode(arena, filename) orelse return;
|
||||
const path = resolved.path;
|
||||
|
||||
self.ensureSystemPrompt() catch return self.failSave("out of memory");
|
||||
|
||||
@@ -922,7 +915,7 @@ fn synthesizeSave(self: *Agent, arena: std.mem.Allocator, filename: ?[]const u8,
|
||||
// The save turn is a meta-action; keep it out of the ongoing conversation.
|
||||
self.rollbackMessages(baseline);
|
||||
|
||||
writeContentFile(path, script, .replace) catch |err| {
|
||||
writeContentFile(path, script, resolved.mode) catch |err| {
|
||||
self.terminal.printError("failed to save {s}: {s}", .{ path, @errorName(err) });
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -905,7 +905,7 @@ pub fn interactiveTty() bool {
|
||||
/// Numbered TTY picker. `default` (if set) marks that row "(default)" and
|
||||
/// makes Enter start on that index. Up/Down moves the active row; Enter
|
||||
/// selects it. Numbered input still works for users who prefer typing.
|
||||
pub fn promptNumberedChoice(header: []const u8, items: []const []const u8, default: ?usize) !usize {
|
||||
pub fn promptNumberedChoice(header: []const u8, items: []const [:0]const u8, default: ?usize) !usize {
|
||||
if (items.len == 0) return error.NoChoice;
|
||||
const valid_default: ?usize = if (default) |d| if (d < items.len) d else null else null;
|
||||
if (interactiveTty()) {
|
||||
@@ -918,7 +918,7 @@ pub fn promptNumberedChoice(header: []const u8, items: []const []const u8, defau
|
||||
}
|
||||
|
||||
/// Line-oriented fallback. Errors with NoChoice after 3 invalid attempts.
|
||||
fn promptNumberedChoiceLine(header: []const u8, items: []const []const u8, default: ?usize) !usize {
|
||||
fn promptNumberedChoiceLine(header: []const u8, items: []const [:0]const u8, default: ?usize) !usize {
|
||||
var stdin_buf: [128]u8 = undefined;
|
||||
var stdin = std.fs.File.stdin().reader(&stdin_buf);
|
||||
|
||||
@@ -1005,7 +1005,7 @@ const RawTerminal = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn promptInteractiveChoice(header: []const u8, items: []const []const u8, default: ?usize) !usize {
|
||||
fn promptInteractiveChoice(header: []const u8, items: []const [:0]const u8, default: ?usize) !usize {
|
||||
var raw = try RawTerminal.enable();
|
||||
defer raw.restore();
|
||||
|
||||
@@ -1046,7 +1046,7 @@ fn moveChoiceRenderStart(line_count: usize) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn renderChoice(header: []const u8, items: []const []const u8, default: ?usize, selected: usize, first_render: bool) void {
|
||||
fn renderChoice(header: []const u8, items: []const [:0]const u8, default: ?usize, selected: usize, first_render: bool) void {
|
||||
if (!first_render) moveChoiceRenderStart(items.len + 2);
|
||||
std.debug.print(ansi.clear_line ++ "{s}\r\n", .{header});
|
||||
for (items, 0..) |item, idx| {
|
||||
|
||||
@@ -82,7 +82,7 @@ pub fn resolveCredentials(opts: Config.Agent, remembered: ?Remembered, allow_pic
|
||||
return .{ .credentials = found[0], .source = .detected };
|
||||
}
|
||||
|
||||
var names: [zenai.provider.default_candidates.len][]const u8 = undefined;
|
||||
var names: [zenai.provider.default_candidates.len][:0]const u8 = undefined;
|
||||
for (found, 0..) |cred, i| names[i] = @tagName(cred.provider);
|
||||
std.debug.print("\n", .{});
|
||||
const idx = Terminal.promptNumberedChoice(" Select a provider:", names[0..found.len], 0) catch {
|
||||
|
||||
Reference in New Issue
Block a user