Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
Zig
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311const std = @import("std");const debug_trace = @import("../shared/debug_trace.zig");const image_attachments = @import("../images/image_attachments.zig");const io_mod = @import("../shared/io.zig");const types = @import("../shared/types.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const paste_blocks = @import("../input/pasted_blocks.zig");const registered_entities = @import("../input/registered_entities.zig");const core_input_runtime = @import("../input/runtime.zig");const entity_spans = @import("../shared/entity_spans.zig");const visual_layout = @import("../../ui/input/visual_layout.zig");const sort_utils = @import("../shared/sort_utils.zig");
pub const ReviewEntry = struct { draft: worker_runtime.QueuedPromptDraft, pasted_blocks: std.ArrayList(paste_blocks.PastedBlock) = .empty, image_tokens: std.ArrayList(entity_spans.ImageTokenSpan) = .empty, dirty: bool = false,
fn deinit(self: *ReviewEntry, alloc: std.mem.Allocator) void { worker_runtime.freeQueuedPromptDraft(alloc, self.draft); paste_blocks.deinitBlocks(alloc, &self.pasted_blocks); self.image_tokens.deinit(alloc); }};
pub const State = struct { entries: []ReviewEntry = &.{}, selected_index: ?usize = null, reason: ?worker_runtime.QueueReviewReason = null, visible: bool = false, selected_dirty: bool = false,
pub fn deinit(self: *State, alloc: std.mem.Allocator) void { self.clear(alloc); }
pub fn clear(self: *State, alloc: std.mem.Allocator) void { for (self.entries) |*entry| entry.deinit(alloc); if (self.entries.len > 0) alloc.free(self.entries); self.* = .{}; }
pub fn active(self: *const State) bool { return self.reason != null; }};
pub const PromptAdmission = enum { enqueue, replaced,};
pub fn Runtime(comptime App: type) type { return struct { pub fn requestCancelAndOpen(app: *App) bool { return openAfterPause(app, app.worker.requestCancelWithQueueReview()); }
pub fn pauseAndOpenAfterModalCancel(app: *App) bool { return openAfterPause(app, app.worker.beginQueueReview(.post_cancel)); }
pub fn routeVertical(app: *App, direction: visual_layout.Direction) !bool { const state = &app.queued_prompt_review; if (state.active()) { if (state.visible) { const index = state.selected_index orelse return false; switch (direction) { .up => { if (index > 0) { if (state.selected_dirty) try stashSelected(app); state.selected_index = index - 1; loadSelected(app) catch |err| { state.selected_index = index; loadSelected(app) catch {}; return err; }; traceNavigation(state, "older"); } }, .down => { if (index + 1 < state.entries.len) { if (state.selected_dirty) try stashSelected(app); state.selected_index = index + 1; loadSelected(app) catch |err| { state.selected_index = index; loadSelected(app) catch {}; return err; }; traceNavigation(state, "newer"); } else { try hideDraft(app); } }, } app.shell.render_requests.request(.footer); return true; }
if (direction == .up and composerIsEmpty(app)) { try loadSelected(app); app.shell.render_requests.request(.footer); return true; } return false; }
if (direction != .up or !composerIsEmpty(app)) return false; if (!app.worker.beginQueueReview(.manual)) return false; _ = ensureOpen(app, .manual) catch |err| { debug_trace.logf("input", "queue review snapshot failed reason=manual err={s}", .{@errorName(err)}); app.shell.render_requests.request(.footer); return true; }; app.shell.render_requests.request(.footer); return true; }
pub fn markVisibleSelectionDirty(app: *App) void { if (!app.queued_prompt_review.visible) return; app.queued_prompt_review.selected_dirty = true; }
pub fn hideVisibleDraft(app: *App) !bool { if (!app.queued_prompt_review.visible) return false; try hideDraft(app); app.shell.render_requests.request(.footer); return true; }
pub fn cancelAllHiddenPostCancelQueued(app: *App) bool { const state = &app.queued_prompt_review; if (!postCancelAllAvailable(app)) return false; const dropped = state.entries.len; app.worker.clearQueuedPrompts( app.alloc, app.input_runtime.kill_ring.images.items, ); app.shell.render_requests.finishSubmittedPromptTransition(); state.clear(app.alloc); app.input_runtime.inputResetState().clearCurrent(app.alloc); paste_blocks.clearBlocks(app.alloc, &app.input_runtime.entities.pasted_blocks); releasePendingImages(app); debug_trace.eventf("input", "queue_review_cancelled_all", .{}, "dropped={d}", .{dropped}); app.shell.render_requests.request(.footer); return true; }
pub fn postCancelAllAvailable(app: *const App) bool { const state = &app.queued_prompt_review; return state.active() and state.reason.? == .post_cancel and !state.visible and composerIsEmpty(app); }
pub fn submitPausedQueueUnchanged(app: *App) !bool { if (!app.queued_prompt_review.active() and app.worker.queueReviewReason() == null) return false; if (!try commitDirtyEntries(app)) return error.QueuedPromptNoLongerAvailable; app.input_runtime.inputResetState().clearCurrent(app.alloc); paste_blocks.clearBlocks(app.alloc, &app.input_runtime.entities.pasted_blocks); releasePendingImages(app); finishReview(app, "unchanged_queue"); return true; }
pub fn deleteEmptyVisibleDraft(app: *App) !bool { const state = &app.queued_prompt_review; if (!state.visible or !composerIsEmpty(app)) return false; const index = state.selected_index orelse return error.QueueReviewSelectionMissing; if (index >= state.entries.len) return error.QueueReviewSelectionMissing;
const turn_id = state.entries[index].draft.turn_id; if (!app.worker.deleteQueuedPromptDraft( app.alloc, turn_id, app.input_runtime.kill_ring.images.items, )) { return error.QueuedPromptNoLongerAvailable; }
var removed = state.entries[index]; const remaining_len = state.entries.len - 1; if (remaining_len == 0) { app.alloc.free(state.entries); state.entries = &.{}; removed.deinit(app.alloc); app.shell.render_requests.finishSubmittedPromptTransition(); debug_trace.eventf("input", "queue_review_draft_deleted", .{ .turn_id = turn_id }, "remaining=0", .{}); app.input_runtime.inputResetState().clearCurrent(app.alloc); paste_blocks.clearBlocks(app.alloc, &app.input_runtime.entities.pasted_blocks); releasePendingImages(app); finishReview(app, "deleted_last_prompt"); return true; }
const next = try app.alloc.alloc(ReviewEntry, remaining_len); var next_index: usize = 0; for (state.entries, 0..) |entry, entry_index| { if (entry_index == index) continue; next[next_index] = entry; next_index += 1; } app.alloc.free(state.entries); state.entries = next; removed.deinit(app.alloc); debug_trace.eventf("input", "queue_review_draft_deleted", .{ .turn_id = turn_id }, "remaining={d}", .{remaining_len});
state.selected_index = @min(index, remaining_len - 1); state.visible = false; state.selected_dirty = false; try loadSelected(app); app.shell.render_requests.request(.footer); return true; }
pub fn promptAdmission( app: *App, review_input: []const u8, review_pasted_blocks: []const paste_blocks.PastedBlock, review_image_tokens: []const entity_spans.ImageTokenSpan, review_skill_tokens: []const registered_entities.SkillTokenSpan, ) !PromptAdmission { const state = &app.queued_prompt_review; if (!state.active() or !state.visible) return .enqueue; const index = state.selected_index orelse return error.QueueReviewSelectionMissing; if (index >= state.entries.len) return error.QueueReviewSelectionMissing;
var pasted_blocks = try dupePastedBlocks( app.alloc, review_pasted_blocks, ); var pasted_blocks_owned = true; defer if (pasted_blocks_owned) paste_blocks.deinitBlocks(app.alloc, &pasted_blocks); var image_tokens: std.ArrayList(entity_spans.ImageTokenSpan) = .empty; defer image_tokens.deinit(app.alloc); try image_tokens.appendSlice(app.alloc, review_image_tokens); try writeSelectedDraft( app, review_input, app.pending_images.items, review_skill_tokens, ); paste_blocks.deinitBlocks( app.alloc, &state.entries[index].pasted_blocks, ); state.entries[index].pasted_blocks = pasted_blocks; pasted_blocks = .empty; pasted_blocks_owned = false; state.entries[index].image_tokens.deinit(app.alloc); state.entries[index].image_tokens = image_tokens; image_tokens = .empty; if (!try commitDirtyEntries(app)) return error.QueuedPromptNoLongerAvailable;
finishReview(app, "edited_prompts"); return .replaced; }
pub fn resumeAfterNewPrompt(app: *App) void { if (!app.queued_prompt_review.active() and app.worker.queueReviewReason() == null) return; finishReview(app, "new_prompt"); }
pub fn reset(app: *App) void { app.queued_prompt_review.clear(app.alloc); }
fn openAfterPause(app: *App, paused: bool) bool { if (!paused) return false; if (app.queued_prompt_review.active()) { app.queued_prompt_review.reason = .post_cancel; return true; } _ = ensureOpen(app, .post_cancel) catch |err| { debug_trace.logf("input", "queue review snapshot failed reason=post_cancel err={s}", .{@errorName(err)}); app.shell.render_requests.request(.footer); return true; }; return true; }
fn ensureOpen(app: *App, reason: worker_runtime.QueueReviewReason) !bool { const drafts = try app.worker.snapshotQueuedPromptDrafts(app.alloc); if (drafts.len == 0) { worker_runtime.freeQueuedPromptDrafts(app.alloc, drafts); return false; } var drafts_owned = true; errdefer if (drafts_owned) worker_runtime.freeQueuedPromptDrafts(app.alloc, drafts);
const entries = try app.alloc.alloc(ReviewEntry, drafts.len); var entries_owned = true; errdefer if (entries_owned) app.alloc.free(entries); for (entries, drafts) |*entry, draft| { entry.* = .{ .draft = draft }; } var tokens_initialized: usize = 0; errdefer for (entries[0..tokens_initialized]) |*entry| { entry.image_tokens.deinit(app.alloc); }; for (entries) |*entry| { const has_review_tokens = if (entry.draft.review_draft) |review| review.image_tokens.len > 0 else false; if (!has_review_tokens) { entry.image_tokens = try imageTokensForQueuedPrompt( app.alloc, entry.draft.reviewInput(), entry.draft.images, ); } tokens_initialized += 1; } for (entries) |*entry| { if (entry.draft.review_draft) |*review| { if (review.pasted_blocks.len > 0) { entry.pasted_blocks = .{ .items = review.pasted_blocks, .capacity = review.pasted_blocks.len, }; review.pasted_blocks = &.{}; } if (review.image_tokens.len > 0) { entry.image_tokens = .{ .items = review.image_tokens, .capacity = review.image_tokens.len, }; review.image_tokens = &.{}; } } } app.alloc.free(drafts); drafts_owned = false;
app.queued_prompt_review.clear(app.alloc); const show_draft = composerIsEmpty(app); app.queued_prompt_review = .{ .entries = entries, .selected_index = entries.len - 1, .reason = reason, .visible = false, }; entries_owned = false; tokens_initialized = 0; if (show_draft) try loadSelected(app); return true; }
fn loadSelected(app: *App) !void { const state = &app.queued_prompt_review; const index = state.selected_index orelse return error.QueueReviewSelectionMissing; if (index >= state.entries.len) return error.QueueReviewSelectionMissing; const entry = &state.entries[index]; const draft = entry.draft; const review_input = draft.reviewInput(); const review_skill_spans = draft.reviewSkillDisplaySpans();
const images = try types.dupeImageAttachmentSlice(app.alloc, draft.images); var images_transferred = false; errdefer if (!images_transferred) types.freeImageAttachmentSlice(app.alloc, images); var skill_tokens = try dupeVisualSkillTokens( app.alloc, review_skill_spans, ); errdefer deinitVisualSkillTokens(app.alloc, &skill_tokens); var prepared_input = try app.input_runtime.textReplacementState().prepare( app.alloc, review_input, ); defer prepared_input.deinit(app.alloc); try app.pending_images.ensureTotalCapacity(app.alloc, images.len);
releasePendingImages(app); if (images.len > 0) { app.pending_images.appendSliceAssumeCapacity(images); app.alloc.free(images); } images_transferred = true; app.input_runtime.textReplacementState().commit(app.alloc, &prepared_input); app.input_runtime.entities.image_tokens.deinit(app.alloc); app.input_runtime.entities.image_tokens = entry.image_tokens; entry.image_tokens = .empty; app.input_runtime.entities.skill_tokens.deinit(app.alloc); app.input_runtime.entities.skill_tokens = skill_tokens; skill_tokens = .empty; paste_blocks.deinitBlocks(app.alloc, &app.input_runtime.entities.pasted_blocks); app.input_runtime.entities.pasted_blocks = entry.pasted_blocks; entry.pasted_blocks = .empty; state.visible = true; state.selected_dirty = false; }
fn stashSelected(app: *App) !void { const state = &app.queued_prompt_review; const index = state.selected_index orelse return error.QueueReviewSelectionMissing; if (index >= state.entries.len) return error.QueueReviewSelectionMissing;
try writeSelectedDraft( app, app.input_runtime.edit_state.input.items, app.pending_images.items, app.input_runtime.entities.skill_tokens.items, ); paste_blocks.deinitBlocks(app.alloc, &state.entries[index].pasted_blocks); state.entries[index].pasted_blocks = app.input_runtime.entities.pasted_blocks; app.input_runtime.entities.pasted_blocks = .empty; state.entries[index].image_tokens.deinit(app.alloc); state.entries[index].image_tokens = app.input_runtime.entities.image_tokens; app.input_runtime.entities.image_tokens = .empty; debug_trace.eventf( "input", "queue_review_draft_stashed", .{ .turn_id = state.entries[index].draft.turn_id }, "prompt_bytes={d}", .{state.entries[index].draft.prompt.len}, ); }
fn writeSelectedDraft( app: *App, prompt: []const u8, images: []const types.ImageAttachment, skill_tokens: []const registered_entities.SkillTokenSpan, ) !void { const state = &app.queued_prompt_review; const index = state.selected_index orelse return error.QueueReviewSelectionMissing; if (index >= state.entries.len) return error.QueueReviewSelectionMissing;
const prompt_copy = try app.alloc.dupe(u8, prompt); errdefer app.alloc.free(prompt_copy); const images_copy = try types.dupeImageAttachmentSlice(app.alloc, images); errdefer types.freeImageAttachmentSlice(app.alloc, images_copy); const skill_display_spans = try dupeSkillDisplaySpans(app.alloc, skill_tokens); errdefer worker_runtime.freeSkillDisplaySpans(app.alloc, skill_display_spans);
const draft = &state.entries[index].draft; types.freeImageAttachmentSlice(app.alloc, draft.images); if (draft.review_draft) |review| { worker_runtime.freeQueueReviewDraft(app.alloc, review); } draft.images = images_copy; draft.review_draft = .{ .input = prompt_copy, .skill_display_spans = skill_display_spans, }; state.entries[index].dirty = true; state.selected_dirty = false; }
fn commitDirtyEntries(app: *App) !bool { const drafts = try buildDirtyDraftsForCommit(app); defer worker_runtime.freeQueuedPromptDrafts(app.alloc, drafts); if (drafts.len == 0) return true; if (!try app.worker.replaceQueuedPromptDrafts(app.alloc, drafts)) return false; debug_trace.eventf("input", "queue_review_batch_committed", .{}, "updated={d}", .{drafts.len}); return true; }
fn buildDirtyDraftsForCommit(app: *App) ![]worker_runtime.QueuedPromptDraft { const state = &app.queued_prompt_review; var dirty_count: usize = 0; for (state.entries) |entry| dirty_count += @intFromBool(entry.dirty); if (dirty_count == 0) return &.{};
const commits = try app.alloc.alloc(worker_runtime.QueuedPromptDraft, dirty_count); var filled: usize = 0; errdefer { for (commits[0..filled]) |draft| worker_runtime.freeQueuedPromptDraft(app.alloc, draft); app.alloc.free(commits); } for (state.entries) |entry| { if (!entry.dirty) continue; const review_input = entry.draft.reviewInput(); const review_skill_spans = entry.draft.reviewSkillDisplaySpans(); const expanded = try paste_blocks.expand( app.alloc, review_input, entry.pasted_blocks.items, ); defer if (expanded.owned) app.alloc.free(expanded.text); const trimmed = std.mem.trim(u8, expanded.text, " \t\r\n"); const prompt_copy = try app.alloc.dupe(u8, trimmed); errdefer app.alloc.free(prompt_copy); const images_copy = try types.dupeImageAttachmentSlice(app.alloc, entry.draft.images); errdefer types.freeImageAttachmentSlice(app.alloc, images_copy); const spans_copy = try projectSkillDisplaySpansForCommit( app.alloc, review_input, expanded.text, entry.pasted_blocks.items, review_skill_spans, ); errdefer worker_runtime.freeSkillDisplaySpans( app.alloc, spans_copy, ); const review_draft = try worker_runtime.dupeQueueReviewDraft( app.alloc, .{ .input = @constCast(review_input), .pasted_blocks = entry.pasted_blocks.items, .image_tokens = entry.image_tokens.items, .skill_display_spans = @constCast(review_skill_spans), }, ); commits[filled] = .{ .turn_id = entry.draft.turn_id, .prompt = prompt_copy, .images = images_copy, .skill_display_spans = spans_copy, .review_draft = review_draft, }; filled += 1; } return commits; }
fn hideDraft(app: *App) !void { if (app.queued_prompt_review.selected_dirty) try stashSelected(app); app.input_runtime.inputResetState().clearCurrent(app.alloc); paste_blocks.clearBlocks(app.alloc, &app.input_runtime.entities.pasted_blocks); releasePendingImages(app); app.queued_prompt_review.visible = false; app.queued_prompt_review.selected_dirty = false; debug_trace.eventf("input", "queue_review_hidden", .{}, "queued={d}", .{app.queued_prompt_review.entries.len}); }
fn finishReview(app: *App, source: []const u8) void { const queued = app.queued_prompt_review.entries.len; app.queued_prompt_review.clear(app.alloc); _ = app.worker.resumeQueueReview(); debug_trace.eventf("input", "queue_review_finished", .{}, "source={s} queued={d}", .{ source, queued }); app.shell.render_requests.request(.footer); }
fn releasePendingImages(app: *App) void { if (comptime @hasDecl(App, "releasePendingImages")) { App.releasePendingImages(app); } else { app.clearPendingImages(); } }
fn composerIsEmpty(app: *const App) bool { return app.input_runtime.edit_state.input.items.len == 0 and app.pending_images.items.len == 0; }
fn traceNavigation(state: *const State, direction: []const u8) void { const index = state.selected_index orelse return; debug_trace.eventf("input", "queue_review_navigate", .{}, "direction={s} index={d} count={d}", .{ direction, index, state.entries.len }); } };}
fn dupePastedBlocks( alloc: std.mem.Allocator, blocks: []const paste_blocks.PastedBlock,) !std.ArrayList(paste_blocks.PastedBlock) { var copy: std.ArrayList(paste_blocks.PastedBlock) = .empty; errdefer paste_blocks.deinitBlocks(alloc, ©); try copy.ensureTotalCapacity(alloc, blocks.len); for (blocks) |block| { const text = try alloc.dupe(u8, block.text); errdefer alloc.free(text); copy.appendAssumeCapacity(.{ .id = block.id, .text = text, .line_count = block.line_count, .span = block.span, }); } return copy;}
fn imageTokensForQueuedPrompt( alloc: std.mem.Allocator, prompt: []const u8, images: []const types.ImageAttachment,) !std.ArrayList(entity_spans.ImageTokenSpan) { var tokens: std.ArrayList(entity_spans.ImageTokenSpan) = .empty; errdefer tokens.deinit(alloc); try tokens.ensureTotalCapacity(alloc, images.len);
for (images) |image| { var match_start: ?usize = null; var match_end: usize = 0; var count: usize = 0; var raw_start: usize = 0; while (raw_start < prompt.len) { const match = image_attachments.matchImagePlaceholder( prompt, raw_start, ) orelse { raw_start += 1; continue; }; if (match.id == image.id) { count += 1; match_start = raw_start; match_end = raw_start + match.length; } raw_start += match.length; } if (count != 1) { debug_trace.logf( "input", "queued image span reconstruction skipped id={d} matches={d}", .{ image.id, count }, ); continue; } tokens.appendAssumeCapacity(.{ .id = image.id, .span = .{ .raw_start = match_start.?, .raw_end = match_end, }, }); } sort_utils.sort( entity_spans.ImageTokenSpan, tokens.items, {}, struct { fn lessThan(_: void, lhs: entity_spans.ImageTokenSpan, rhs: entity_spans.ImageTokenSpan) bool { return lhs.span.raw_start < rhs.span.raw_start; } }.lessThan, ); return tokens;}
fn dupeVisualSkillTokens( alloc: std.mem.Allocator, spans: []const worker_runtime.SkillDisplaySpan,) !std.ArrayList(registered_entities.SkillTokenSpan) { var tokens: std.ArrayList(registered_entities.SkillTokenSpan) = .empty; errdefer deinitVisualSkillTokens(alloc, &tokens); try tokens.ensureTotalCapacity(alloc, spans.len); for (spans) |span| { const name = try alloc.dupe(u8, span.name); errdefer alloc.free(name); const path = try alloc.dupe(u8, span.path); tokens.appendAssumeCapacity(.{ .raw_start = span.raw_start, .raw_end = span.raw_end, .name = name, .path = path, .display_source = span.display_source, .owns_trailing_separator = span.owns_trailing_separator, }); } return tokens;}
fn deinitVisualSkillTokens(alloc: std.mem.Allocator, tokens: *std.ArrayList(registered_entities.SkillTokenSpan)) void { for (tokens.items) |token| { alloc.free(token.name); alloc.free(token.path); } tokens.deinit(alloc); tokens.* = .empty;}
fn dupeSkillDisplaySpans( alloc: std.mem.Allocator, tokens: []const registered_entities.SkillTokenSpan,) ![]worker_runtime.SkillDisplaySpan { if (tokens.len == 0) return &.{}; const spans = try alloc.alloc(worker_runtime.SkillDisplaySpan, tokens.len); var filled: usize = 0; errdefer { for (spans[0..filled]) |span| { alloc.free(span.name); alloc.free(span.path); } alloc.free(spans); } while (filled < tokens.len) : (filled += 1) { const name = try alloc.dupe(u8, tokens[filled].name); errdefer alloc.free(name); const path = try alloc.dupe(u8, tokens[filled].path); spans[filled] = .{ .raw_start = tokens[filled].raw_start, .raw_end = tokens[filled].raw_end, .name = name, .path = path, .display_source = tokens[filled].display_source, .owns_trailing_separator = tokens[filled].owns_trailing_separator, }; } return spans;}
fn projectSkillDisplaySpansForCommit( alloc: std.mem.Allocator, raw_input: []const u8, expanded_text: []const u8, pasted_blocks: []const paste_blocks.PastedBlock, spans: []const worker_runtime.SkillDisplaySpan,) ![]worker_runtime.SkillDisplaySpan { if (spans.len == 0) return &.{};
const trimmed = std.mem.trim(u8, expanded_text, " \t\r\n"); const trim_start = expanded_text.len - std.mem.trimStart(u8, expanded_text, " \t\r\n").len; var projected: std.ArrayList(worker_runtime.SkillDisplaySpan) = .empty; errdefer { for (projected.items) |span| { alloc.free(span.name); alloc.free(span.path); } projected.deinit(alloc); }
for (spans) |span| { const start = paste_blocks.projectOffsetThroughExpansion(raw_input, pasted_blocks, span.raw_start) orelse continue; const end = paste_blocks.projectOffsetThroughExpansion(raw_input, pasted_blocks, span.raw_end) orelse continue; if (start < trim_start or end < start or end - trim_start > trimmed.len) continue; const name = try alloc.dupe(u8, span.name); errdefer alloc.free(name); const path = try alloc.dupe(u8, span.path); errdefer alloc.free(path); try projected.append(alloc, .{ .raw_start = start - trim_start, .raw_end = end - trim_start, .name = name, .path = path, .display_source = span.display_source, .owns_trailing_separator = span.owns_trailing_separator, }); }
if (projected.items.len == 0) { projected.deinit(alloc); return &.{}; } return projected.toOwnedSlice(alloc);}
const kill_ring = @import("../input/kill_ring.zig");const render_request = @import("../../ui/render_request.zig");
const ReviewTestShell = struct { render_requests: render_request.RenderRequestState = .{},};
const ReviewTestApp = struct { alloc: std.mem.Allocator, worker: worker_runtime.WorkerRuntime = .{}, queued_prompt_review: State = .{}, input_runtime: core_input_runtime.Runtime = .{}, pending_images: std.ArrayList(types.ImageAttachment) = .empty, shell: ReviewTestShell = .{},
fn deinit(self: *ReviewTestApp) void { self.queued_prompt_review.deinit(self.alloc); self.clearPendingImages(); self.pending_images.deinit(self.alloc); self.input_runtime.deinit(self.alloc); self.worker.deinit(self.alloc); }
pub fn clearPendingImages(self: *ReviewTestApp) void { self.input_runtime.entities.image_tokens.clearRetainingCapacity(); for (self.pending_images.items) |image| types.freeImageAttachment(self.alloc, image); self.pending_images.clearRetainingCapacity(); }};
fn makeReviewTestPrompt(alloc: std.mem.Allocator, text: []const u8) !worker_runtime.QueuedPrompt { const prompt = try alloc.dupe(u8, text); errdefer alloc.free(prompt); const model = try alloc.dupe(u8, "test/model"); errdefer alloc.free(model); const api_key = try alloc.dupe(u8, "key"); errdefer alloc.free(api_key); const history = try alloc.alloc(types.HistoryTurn, 0); errdefer alloc.free(history); const grants = try alloc.alloc(types.PermissionGrant, 0); return .{ .prompt = prompt, .images = &.{}, .model = model, .api_key = api_key, .permission_mode = .auto, .history = history, .grants = grants, };}
fn openQueueReviewWithSemanticImageTokens(alloc: std.mem.Allocator) !void { var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit();
{ var prompt = try makeReviewTestPrompt(alloc, "[Image #1]"); errdefer worker_runtime.freeQueuedPrompt(alloc, prompt); const images = [_]types.ImageAttachment{.{ .id = 1, .path = @constCast("/tmp/one.png"), .media_type = @constCast("image/png"), }}; prompt.images = try types.dupeImageAttachmentSlice(alloc, &images); const tokens = [_]entity_spans.ImageTokenSpan{.{ .id = 1, .span = .{ .raw_start = 0, .raw_end = "[Image #1]".len }, }}; prompt.review_draft = try worker_runtime.dupeQueueReviewDraft( alloc, .{ .input = prompt.prompt, .image_tokens = @constCast(&tokens), }, ); try app.worker.enqueuePrompt(alloc, prompt); } { var prompt = try makeReviewTestPrompt(alloc, "[Image #2]"); errdefer worker_runtime.freeQueuedPrompt(alloc, prompt); const images = [_]types.ImageAttachment{.{ .id = 2, .path = @constCast("/tmp/two.png"), .media_type = @constCast("image/png"), }}; prompt.images = try types.dupeImageAttachmentSlice(alloc, &images); prompt.authorized_image_catalog = try types.dupeImageAttachmentSlice( alloc, &images, ); try app.worker.enqueuePrompt(alloc, prompt); }
try std.testing.expect(app.worker.beginQueueReview(.manual)); const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.ensureOpen(&app, .manual));}
test "queued prompt review releases semantic image tokens once on allocation failure" { try std.testing.checkAllAllocationFailures( std.testing.allocator, openQueueReviewWithSemanticImageTokens, .{}, );}
test "queued prompt review navigates newest to oldest and stays paused when hidden" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "second queued"));
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings("second queued", app.input_runtime.edit_state.input.items); try std.testing.expectEqual(worker_runtime.QueueReviewReason.manual, app.worker.queueReviewReason().?);
try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings("first queued", app.input_runtime.edit_state.input.items); try std.testing.expect(try rt.routeVertical(&app, .down)); try std.testing.expectEqualStrings("second queued", app.input_runtime.edit_state.input.items);
try std.testing.expect(try rt.routeVertical(&app, .down)); try std.testing.expectEqual(@as(usize, 0), app.input_runtime.edit_state.input.items.len); try std.testing.expect(app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() != null);
try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings("second queued", app.input_runtime.edit_state.input.items); try app.input_runtime.edit_state.input.appendSlice(alloc, " edited"); var placeholder_buf: [64]u8 = undefined; const placeholder = try paste_blocks.formatPlaceholder(&placeholder_buf, 7, 2); try app.input_runtime.edit_state.input.append(alloc, ' '); try app.input_runtime.edit_state.input.appendSlice(alloc, placeholder); try app.input_runtime.entities.pasted_blocks.append(alloc, .{ .id = 7, .text = try alloc.dupe(u8, "pasted\ncontent"), .line_count = 2, }); app.input_runtime.edit_state.cursor = app.input_runtime.edit_state.input.items.len; rt.markVisibleSelectionDirty(&app); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings("first queued", app.input_runtime.edit_state.input.items); try std.testing.expect(try rt.routeVertical(&app, .down)); try std.testing.expectEqualStrings("second queued edited [Pasted text #7, 2 lines]", app.input_runtime.edit_state.input.items); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.pasted_blocks.items.len); try std.testing.expectEqualStrings("pasted\ncontent", app.input_runtime.entities.pasted_blocks.items[0].text);
const worker_drafts = try app.worker.snapshotQueuedPromptDrafts(alloc); defer worker_runtime.freeQueuedPromptDrafts(alloc, worker_drafts); try std.testing.expectEqualStrings("second queued", worker_drafts[1].prompt);}
test "queued prompt review restores compact paste and commits expanded execution" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit();
const placeholder = "[Pasted text #4, 1 line]"; const compact = "before " ++ placeholder ++ " after"; const backing = "x" ** (paste_blocks.large_paste_char_threshold + 1); const expanded = "before " ++ backing ++ " after"; var prompt = try makeReviewTestPrompt(alloc, expanded); errdefer worker_runtime.freeQueuedPrompt(alloc, prompt); var blocks = [_]paste_blocks.PastedBlock{.{ .id = 4, .text = @constCast(backing), .line_count = 1, .span = .{ .raw_start = "before ".len, .raw_end = "before ".len + placeholder.len, }, }}; prompt.review_draft = try worker_runtime.dupeQueueReviewDraft(alloc, .{ .input = @constCast(compact), .pasted_blocks = &blocks, }); try app.worker.enqueuePrompt(alloc, prompt);
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings(compact, app.input_runtime.edit_state.input.items); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.pasted_blocks.items.len); try std.testing.expectEqualStrings( backing, app.input_runtime.entities.pasted_blocks.items[0].text, ); try app.input_runtime.edit_state.input.appendSlice(alloc, " edited"); app.input_runtime.edit_state.cursor = app.input_runtime.edit_state.input.items.len; rt.markVisibleSelectionDirty(&app);
try std.testing.expectEqual( PromptAdmission.replaced, try rt.promptAdmission( &app, app.input_runtime.edit_state.input.items, app.input_runtime.entities.pasted_blocks.items, app.input_runtime.entities.image_tokens.items, app.input_runtime.entities.skill_tokens.items, ), ); const drafts = try app.worker.snapshotQueuedPromptDrafts(alloc); defer worker_runtime.freeQueuedPromptDrafts(alloc, drafts); try std.testing.expectEqualStrings(expanded ++ " edited", drafts[0].prompt); try std.testing.expectEqualStrings( compact ++ " edited", drafts[0].review_draft.?.input, ); try std.testing.expectEqualStrings( backing, drafts[0].review_draft.?.pasted_blocks[0].text, );}
test "queued prompt review direct commit preserves image token provenance" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit();
{ var prompt = try makeReviewTestPrompt(alloc, "inspect [Image #7]"); errdefer worker_runtime.freeQueuedPrompt(alloc, prompt); const images = [_]types.ImageAttachment{.{ .id = 7, .path = @constCast("/tmp/seven.png"), .media_type = @constCast("image/png"), }}; prompt.images = try types.dupeImageAttachmentSlice(alloc, &images); prompt.authorized_image_catalog = try types.dupeImageAttachmentSlice( alloc, &images, ); try app.worker.enqueuePrompt(alloc, prompt); }
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.image_tokens.items.len); try std.testing.expectEqual( PromptAdmission.replaced, try rt.promptAdmission( &app, app.input_runtime.edit_state.input.items, app.input_runtime.entities.pasted_blocks.items, app.input_runtime.entities.image_tokens.items, app.input_runtime.entities.skill_tokens.items, ), );
const drafts = try app.worker.snapshotQueuedPromptDrafts(alloc); defer worker_runtime.freeQueuedPromptDrafts(alloc, drafts); try std.testing.expectEqual(@as(usize, 1), drafts.len); try std.testing.expectEqual(@as(usize, 1), drafts[0].review_draft.?.image_tokens.len); try std.testing.expectEqual(@as(usize, 7), drafts[0].review_draft.?.image_tokens[0].id); try std.testing.expectEqual( @as(usize, "inspect ".len), drafts[0].review_draft.?.image_tokens[0].span.raw_start, ); try std.testing.expectEqual( @as(usize, "inspect [Image #7]".len), drafts[0].review_draft.?.image_tokens[0].span.raw_end, );}
test "one confirmation commits edits stashed across multiple queued prompts" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "second queued"));
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); try app.input_runtime.edit_state.input.appendSlice(alloc, " edited"); app.input_runtime.edit_state.cursor = app.input_runtime.edit_state.input.items.len; rt.markVisibleSelectionDirty(&app); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings("first queued", app.input_runtime.edit_state.input.items);
try std.testing.expectEqual( PromptAdmission.replaced, try rt.promptAdmission( &app, "first queued edited", &.{}, &.{}, &.{}, ), ); try std.testing.expect(!app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() == null);
const drafts = try app.worker.snapshotQueuedPromptDrafts(alloc); defer worker_runtime.freeQueuedPromptDrafts(alloc, drafts); try std.testing.expectEqualStrings("first queued edited", drafts[0].prompt); try std.testing.expectEqualStrings("second queued edited", drafts[1].prompt);}
test "new prompt after a hidden review appends in FIFO order and resumes admission" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "second queued"));
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expect(try rt.hideVisibleDraft(&app)); try std.testing.expect(app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() != null);
try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "new queued")); rt.resumeAfterNewPrompt(&app); try std.testing.expect(!app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() == null);
const drafts = try app.worker.snapshotQueuedPromptDrafts(alloc); defer worker_runtime.freeQueuedPromptDrafts(alloc, drafts); try std.testing.expectEqual(@as(usize, 3), drafts.len); try std.testing.expectEqualStrings("first queued", drafts[0].prompt); try std.testing.expectEqualStrings("second queued", drafts[1].prompt); try std.testing.expectEqualStrings("new queued", drafts[2].prompt);}
test "empty submit resumes a hidden review without changing queued prompts" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "second queued"));
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expect(try rt.hideVisibleDraft(&app)); try std.testing.expect(try rt.submitPausedQueueUnchanged(&app)); try std.testing.expect(!app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() == null);
const drafts = try app.worker.snapshotQueuedPromptDrafts(alloc); defer worker_runtime.freeQueuedPromptDrafts(alloc, drafts); try std.testing.expectEqual(@as(usize, 2), drafts.len); try std.testing.expectEqualStrings("first queued", drafts[0].prompt); try std.testing.expectEqualStrings("second queued", drafts[1].prompt);}
test "backspace deletion removes only the visible empty draft and keeps review paused" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "second queued"));
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); app.input_runtime.inputResetState().clearCurrent(alloc); try std.testing.expect(try rt.deleteEmptyVisibleDraft(&app)); try std.testing.expectEqualStrings("first queued", app.input_runtime.edit_state.input.items); try std.testing.expectEqual(@as(usize, 1), app.worker.queuedPromptCount()); try std.testing.expect(app.worker.queueReviewReason() != null);
app.input_runtime.inputResetState().clearCurrent(alloc); try std.testing.expect(try rt.deleteEmptyVisibleDraft(&app)); try std.testing.expectEqual(@as(usize, 0), app.worker.queuedPromptCount()); try std.testing.expect(!app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() == null);}
test "queued image line kill survives deleting its queue card and yanks verified bytes" { const alloc = std.testing.allocator; const original_bytes = "\x89PNG\r\n\x1a\nqueued image"; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); { var source = try tmp.dir.createFile(std.testing.io, "source.png", .{}); defer source.close(std.testing.io); try source.writeStreamingAll(std.testing.io, original_bytes); } const source_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "source.png"); defer alloc.free(source_path); const root = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "."); defer alloc.free(root); const snapshot_dir = try std.fs.path.join(alloc, &.{ root, "snapshots" }); defer alloc.free(snapshot_dir);
var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); { var prompt = try makeReviewTestPrompt(alloc, "describe [Image #1]"); errdefer worker_runtime.freeQueuedPrompt(alloc, prompt); const images = try alloc.alloc(types.ImageAttachment, 1); var images_owned = true; errdefer if (images_owned) alloc.free(images); images[0] = try image_attachments.loadImageAttachment(alloc, source_path); var image_owned = true; errdefer if (image_owned) { image_attachments.discardImageAttachment(alloc, images[0]); }; images[0].id = 1; try image_attachments.captureImageSnapshot(alloc, &images[0], snapshot_dir); prompt.images = images; image_owned = false; images_owned = false; prompt.authorized_image_catalog = try types.dupeImageAttachmentSlice( alloc, prompt.images, ); try app.worker.enqueuePrompt(alloc, prompt); } try tmp.dir.deleteFile(std.testing.io, "source.png");
const rt = Runtime(ReviewTestApp); try std.testing.expect(try rt.routeVertical(&app, .up)); app.input_runtime.edit_state.cursor = app.input_runtime.edit_state.input.items.len; try std.testing.expect(try app.input_runtime.killRingState( &app.pending_images, ).delete( alloc, .line_start, )); try std.testing.expectEqual(@as(usize, 0), app.pending_images.items.len); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.kill_ring.images.items.len); const retained_snapshot = app.input_runtime.kill_ring.images.items[0].snapshot_path.?;
try std.testing.expect(try rt.deleteEmptyVisibleDraft(&app)); try std.testing.expectEqual(@as(usize, 0), app.worker.queuedPromptCount()); try std.Io.Dir.accessAbsolute(std.testing.io, retained_snapshot, .{});
try std.testing.expectEqual( kill_ring.YankResult{ .inserted = 1 }, try app.input_runtime.killRingState(&app.pending_images).yank( alloc, 2, 4096, ), ); try std.testing.expectEqualStrings("describe [Image #2]", app.input_runtime.edit_state.input.items); try std.testing.expectEqual(@as(usize, 1), app.pending_images.items.len); try std.testing.expectEqual(@as(usize, 2), app.pending_images.items[0].id); var verified = try image_attachments.loadVerifiedSnapshot( alloc, app.pending_images.items[0], .{}, ); defer verified.deinit(alloc); try std.testing.expectEqualSlices(u8, original_bytes, verified.bytes);}
test "hidden post-cancel review can discard every queued prompt with Escape action" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "second queued"));
const rt = Runtime(ReviewTestApp); try std.testing.expect(rt.pauseAndOpenAfterModalCancel(&app)); try std.testing.expectEqual(worker_runtime.QueueReviewReason.post_cancel, app.queued_prompt_review.reason.?); try std.testing.expect(app.queued_prompt_review.visible); try std.testing.expect(try rt.hideVisibleDraft(&app)); try std.testing.expect(rt.postCancelAllAvailable(&app)); try std.testing.expect(rt.cancelAllHiddenPostCancelQueued(&app)); try std.testing.expectEqual(@as(usize, 0), app.worker.queuedPromptCount()); try std.testing.expect(!app.queued_prompt_review.active()); try std.testing.expect(app.worker.queueReviewReason() == null);}
test "post-cancel review opens newest queued prompt with images and skills" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit(); try app.worker.enqueuePrompt(alloc, try makeReviewTestPrompt(alloc, "first queued")); { var second = try makeReviewTestPrompt( alloc, "second $review$review [Image #1]", ); errdefer worker_runtime.freeQueuedPrompt(alloc, second); { const images = try alloc.alloc(types.ImageAttachment, 1); errdefer alloc.free(images); const image_path = try alloc.dupe(u8, "/tmp/review.png"); errdefer alloc.free(image_path); const media_type = try alloc.dupe(u8, "image/png"); errdefer alloc.free(media_type);
const spans = try alloc.alloc(worker_runtime.SkillDisplaySpan, 1); errdefer alloc.free(spans); const skill_name = try alloc.dupe(u8, "review"); errdefer alloc.free(skill_name); const skill_path = try alloc.dupe(u8, "/tmp/review-skill");
images[0] = .{ .id = 1, .path = image_path, .media_type = media_type }; spans[0] = .{ .raw_start = "second ".len, .raw_end = "second $review".len, .name = skill_name, .path = skill_path, }; second.images = images; second.skill_display_spans = spans; } try app.worker.enqueuePrompt(alloc, second); }
const rt = Runtime(ReviewTestApp); try std.testing.expect(rt.requestCancelAndOpen(&app)); try std.testing.expect(app.worker.isCancelRequested()); try std.testing.expectEqual(worker_runtime.QueueReviewReason.post_cancel, app.worker.queueReviewReason().?); try std.testing.expectEqualStrings( "second $review$review [Image #1]", app.input_runtime.edit_state.input.items, ); try std.testing.expectEqual(@as(usize, 1), app.pending_images.items.len); try std.testing.expectEqualStrings("/tmp/review.png", app.pending_images.items[0].path); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.image_tokens.items.len); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.image_tokens.items[0].id); try std.testing.expectEqual( @as(usize, "second $review$review ".len), app.input_runtime.entities.image_tokens.items[0].span.raw_start, ); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.skill_tokens.items.len); try std.testing.expectEqualStrings("review", app.input_runtime.entities.skill_tokens.items[0].name); try std.testing.expectEqual(@as(usize, "second ".len), app.input_runtime.entities.skill_tokens.items[0].raw_start); try std.testing.expectEqual(@as(usize, "second $review".len), app.input_runtime.entities.skill_tokens.items[0].raw_end);
try app.input_runtime.insertionState().insertSlice(alloc, " [Image #1]", .preserve); rt.markVisibleSelectionDirty(&app); try std.testing.expect(try rt.hideVisibleDraft(&app)); try std.testing.expect(try rt.routeVertical(&app, .up)); try std.testing.expectEqualStrings( "second $review$review [Image #1] [Image #1]", app.input_runtime.edit_state.input.items, ); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.image_tokens.items.len); try std.testing.expectEqual( @as(usize, "second $review$review ".len), app.input_runtime.entities.image_tokens.items[0].span.raw_start, ); try std.testing.expectEqual(@as(usize, 1), app.input_runtime.entities.skill_tokens.items.len); try std.testing.expectEqual(@as(usize, "second ".len), app.input_runtime.entities.skill_tokens.items[0].raw_start); try std.testing.expectEqual(@as(usize, "second $review".len), app.input_runtime.entities.skill_tokens.items[0].raw_end);}
test "post-cancel review restores reversed image appearance order with stable ids" { const alloc = std.testing.allocator; var app = ReviewTestApp{ .alloc = alloc }; defer app.deinit();
var prompt = try makeReviewTestPrompt(alloc, "[Image #2] [Image #1]"); errdefer worker_runtime.freeQueuedPrompt(alloc, prompt); const source_images = [_]types.ImageAttachment{ .{ .id = 2, .path = @constCast("/tmp/two.png"), .media_type = @constCast("image/png") }, .{ .id = 1, .path = @constCast("/tmp/one.png"), .media_type = @constCast("image/png") }, }; prompt.images = try types.dupeImageAttachmentSlice(alloc, &source_images); try app.worker.enqueuePrompt(alloc, prompt);
const rt = Runtime(ReviewTestApp); try std.testing.expect(rt.requestCancelAndOpen(&app)); try std.testing.expectEqualStrings("[Image #2] [Image #1]", app.input_runtime.edit_state.input.items); try std.testing.expectEqual(@as(usize, 2), app.pending_images.items.len); try std.testing.expectEqual(@as(usize, 2), app.pending_images.items[0].id); try std.testing.expectEqualStrings("/tmp/two.png", app.pending_images.items[0].path); try std.testing.expectEqual(@as(usize, 1), app.pending_images.items[1].id); try std.testing.expectEqualStrings("/tmp/one.png", app.pending_images.items[1].path);}