const std = @import("std");const question_prompt = @import("../agent/question_prompt.zig");const input_completion_runtime = @import("input_completion_runtime.zig");const input_queue_runtime = @import("input_queue_runtime.zig");const app_commands = @import("app_commands.zig");const app_lifecycle = @import("app_lifecycle.zig");const app_permission_runtime = @import("app_permission_runtime.zig");const app_session_runtime = @import("app_session_runtime.zig");const terminal_ui_projection = @import("../terminal/ui_projection.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const model_capabilities = @import("../config/model_capabilities.zig");const picker_state = @import("../input/picker_state.zig");const core_input_runtime = @import("../input/runtime.zig");const command_specs = @import("../slash_commands/command_specs.zig");const model_cache_runtime = @import("model_cache_runtime.zig");const debug_trace = @import("../shared/debug_trace.zig");const diff_mod = @import("../output/diff.zig");const io_mod = @import("../shared/io.zig");const text_utils = @import("../shared/text_utils.zig");const permission_request = @import("../permissions/permission_request.zig");const sandbox = @import("../permissions/sandbox.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const types = @import("../shared/types.zig");const subagent_domain = @import("../subagent/domain.zig");const subagent_projection = @import("../subagent/ui_projection.zig");const file_index = @import("../workspace/file_index.zig");const activity_runtime = @import("../output/activity_runtime.zig");const transcript_presentation = @import("../output/transcript_presentation.zig");const event_loop = @import("../../ui/event_loop.zig");const surface_frame = @import("../../ui/footer/surface_frame.zig");const surface_invalidation = @import("../../ui/footer/surface_invalidation.zig");const interaction_state = @import("../../ui/footer/interaction_state.zig");const approval_prompt = @import("../permissions/approval_prompt.zig");const render_input = @import("../../ui/footer/render_input.zig");const input_presentation = @import("../../ui/footer/input_presentation.zig");const footer_viewport = @import("../../ui/footer/viewport.zig");const render_request = @import("../../ui/render_request.zig");const ui_input = @import("../../ui/input/runtime.zig");const input_visual_layout = @import("../../ui/input/visual_layout.zig");const registered_entities = @import("../input/registered_entities.zig");const approval_screen = @import("../../ui/approval_screen.zig");const models_screen = @import("../../ui/models_screen.zig");const resume_screen = @import("../../ui/resume_screen.zig");const skills_screen = @import("../../ui/skills_screen.zig");const help_screen = @import("../../ui/help_screen.zig");const settings_screen = @import("../../ui/settings_screen.zig");const full_transcript_screen = @import("../../ui/full_transcript_screen.zig");const render_engine = @import("../../ui/render_engine.zig");const build_checkpoint = @import("../../ui/render_engine/build_checkpoint.zig");const shell_runtime = @import("../../ui/shell_runtime.zig");const shimmer_runtime = @import("../../ui/transcript/shimmer_runtime.zig");const ui_subagents = @import("../../ui/subagent/controller.zig");const ui_terminal = @import("../../ui/terminal/terminal.zig");const vt_emulator = @import("../terminal/engine.zig");const transcript_painter = @import("../../ui/transcript/painter.zig");const command_output_runtime = @import("../../ui/transcript/command_output_runtime.zig");const resume_projection = @import("../../ui/transcript/resume_projection.zig");const transcript_runtime = @import("../../ui/transcript/runtime.zig");const ui_render = @import("../../ui/render.zig");const assistant_pacer = @import("../../ui/assistant/pacer.zig"); fn set_transcript_assistant_tail_writable( runtime: *transcript_runtime.TranscriptRuntime, writable: bool,) void { if (runtime.transcript_release.assistant_tail_writable == writable) return; runtime.transcript_release = runtime.transcript_release.with_assistant_tail_writable(writable); debug_trace.logf( "scroll", "assistant tail writability changed writable={s}", .{if (writable) "true" else "false"}, );} pub const VisualEpochResetTrigger = enum { native_clear_probe, ctrl_l,}; const FrameAttemptResult = struct { shadow_state: render_engine.terminal_diff.ShadowCommitState, animation_visible: bool, yolo_warning_visible: bool = false, fn is_committed(self: FrameAttemptResult) bool { return self.shadow_state.is_committed(); }}; const InlineRenderReconciliation = struct { alternate_screen_owns_rendering: bool = false, terminal_transition: render_engine.terminal_diff.FrameTerminalTransition = .none,}; const PreparedChildConversation = struct { runtime: transcript_runtime.TranscriptRuntime, diff_entries: std.ArrayList(diff_mod.DiffEntry), next_diff_id: u32,}; fn encodeSelectedChildDisplayName( alloc: std.mem.Allocator, raw_name: []const u8,) error{OutOfMemory}!text_utils.EncodedText { return text_utils.encodeTerminalSafe( alloc, raw_name, std.math.maxInt(usize), );} /// Couples the physical terminal buffer with the logical presentation that/// owns retry invalidations. Ctrl-X keeps one terminal shadow with native/// primary/alternate buffers while main and child conversations retain/// independent render queues.const SurfaceFrameShell = struct { output: *transcript_runtime.TranscriptRuntime, invalidation_owner: *transcript_runtime.TranscriptRuntime, shadow_vt: ?*vt_emulator.Grid, committed_frame_layout: render_engine.frame_layout.CommittedLayoutSnapshot, history_reset_uses_ris: bool, fn init( output: *transcript_runtime.TranscriptRuntime, invalidation_owner: *transcript_runtime.TranscriptRuntime, committed_frame_layout: render_engine.frame_layout.CommittedLayoutSnapshot, ) SurfaceFrameShell { return .{ .output = output, .invalidation_owner = invalidation_owner, .shadow_vt = output.shadow_vt, .committed_frame_layout = committed_frame_layout, .history_reset_uses_ris = output.history_reset_uses_ris, }; } pub fn frameSink( self: *SurfaceFrameShell, ) render_engine.terminal_diff.FrameSink { return self.output.frameSink(); } pub fn recordFrameInvalidation( self: *SurfaceFrameShell, range: render_engine.paint_plan.FrameInvalidationRange, ) !void { try self.invalidation_owner.recordFrameInvalidation(range); }}; const RenderReconciliation = union(enum) { inline_render: InlineRenderReconciliation, file_approval_screen, skills_screen, models_screen, resume_screen, help_screen, settings_screen, frame_result: FrameAttemptResult,}; const QueuedCardProjection = struct { cards: []render_input.QueuedPromptCard = &.{}, row_count: u16 = 0, editor_active: bool = false, fn deinit(self: *QueuedCardProjection, alloc: std.mem.Allocator) void { for (self.cards) |card| alloc.free(card.bytes); if (self.cards.len > 0) alloc.free(self.cards); self.* = .{}; }}; fn composerInputAppearance(comptime App: type, app: *App) render_input.InputAppearance { return if (app.shell.maxxing_mode == .minimal) .lines else app.input_runtime.input_appearance;} fn composerPrefixStyle(comptime App: type, app: *App) input_presentation.ComposerPrefixStyle { return if (app.shell.maxxing_mode == .minimal) .rail else .arrow;} // Queued prompts stay collapsed behind their summary row until the review is// opened; only then does the banner expand into one card per queued prompt.fn buildQueuedCardProjection(comptime App: type, app: *App) !QueuedCardProjection { if (comptime !@hasField(App, "queued_prompt_review")) return .{}; const review_entries = app.queued_prompt_review.entries; if (!app.queued_prompt_review.visible or !app.queued_prompt_review.active() or review_entries.len == 0) return .{}; const draft_count = review_entries.len; const cards = try app.alloc.alloc(render_input.QueuedPromptCard, draft_count); var built: usize = 0; errdefer { for (cards[0..built]) |card| app.alloc.free(card.bytes); app.alloc.free(cards); } const selected_turn_id: ?u64 = blk: { const selected_index = app.queued_prompt_review.selected_index orelse break :blk null; if (selected_index >= review_entries.len) break :blk null; break :blk review_entries[selected_index].draft.turn_id; }; var total_rows: u16 = 0; var editor_active = false; while (built < draft_count) : (built += 1) { const draft = review_entries[built].draft; const editing = selected_turn_id != null and draft.turn_id == selected_turn_id.?; if (editing) { const source = input_visual_layout.Source{ .input = app.input_runtime.edit_state.input.items, .cursor = app.input_runtime.edit_state.cursor, .terminal_cols = app.shell.layout.cols, .images = app.pending_images.items, .pasted_blocks = app.input_runtime.entities.pasted_blocks.items, .image_tokens = app.input_runtime.entities.image_tokens.items, .skill_tokens = app.input_runtime.entities.skill_tokens.items, }; const summary = input_visual_layout.summarize(source, null); const row_count: u16 = @intCast(@min(summary.total_rows, std.math.maxInt(u16))); cards[built] = .{ .bytes = try app.alloc.dupe(u8, ""), .row_count = @max(row_count, 1), .editing = true, }; total_rows +|= cards[built].row_count; editor_active = true; continue; } const review_input = draft.reviewInput(); const review_skill_spans = draft.reviewSkillDisplaySpans(); var skill_tokens: []registered_entities.SkillTokenSpan = if (review_skill_spans.len > 0) try app.alloc.alloc(registered_entities.SkillTokenSpan, review_skill_spans.len) else &.{}; defer if (skill_tokens.len > 0) app.alloc.free(skill_tokens); for (review_skill_spans, 0..) |span, i| { skill_tokens[i] = .{ .raw_start = span.raw_start, .raw_end = span.raw_end, .name = span.name, .path = span.path, .display_source = span.display_source, .owns_trailing_separator = span.owns_trailing_separator, }; } const bytes = try input_presentation.composeQueuedPromptCard( app.alloc, .{ .input = review_input, .cursor = 0, .terminal_cols = app.shell.layout.cols, .images = draft.images, .pasted_blocks = review_entries[built].pasted_blocks.items, .image_tokens = review_entries[built].image_tokens.items, .skill_tokens = skill_tokens, }, composerInputAppearance(App, app), composerPrefixStyle(App, app), ); const row_count: u16 = @intCast(@min( std.mem.count(u8, bytes, "\n"), std.math.maxInt(u16), )); cards[built] = .{ .bytes = bytes, .row_count = row_count }; total_rows +|= row_count; } return .{ .cards = cards, .row_count = total_rows, .editor_active = editor_active };} noinline fn approvalScreenNeedsClear( screen_active: bool, request: permission_request.PermissionRequest, layout: types.Layout, commit: ?interaction_state.ApprovalScreenCommit,) bool { if (!screen_active) return true; const prior = commit orelse return true; return prior.request_id != request.id or prior.rows != layout.rows or prior.cols != layout.cols;} pub fn Runtime(comptime App: type) type { return struct { fn appendDeferredHistoryForVisualEpoch( ctx: *anyopaque, finished: *const types.FinishedPrompt, ) anyerror!assistant_pacer.DeferredFinishCommit { const app: *App = @ptrCast(@alignCast(ctx)); return switch (try app_session_runtime.Runtime(App).appendFinishedPromptForVisualEpoch( app, finished.*, )) { .uncommitted => .uncommitted, .committed, .committed_degraded => .committed, }; } pub fn resetVisualEpoch( app: *App, trigger: VisualEpochResetTrigger, ) !bool { const welcome = try ui_render.welcomeMessage(app.alloc); defer app.alloc.free(welcome); const committed = try app.pacer.discardDeferredPresentationForVisualEpoch( app.alloc, .{ .ctx = app, .append_history = appendDeferredHistoryForVisualEpoch, }, ); if (!committed) { switch (trigger) { .native_clear_probe => debug_trace.logf( "native_clear", "native_clear_recovery_skipped reason=history_uncommitted", .{}, ), .ctrl_l => debug_trace.logf( "render", "visual_epoch_reset_skipped trigger=ctrl_l reason=history_uncommitted", .{}, ), } return false; } try app.shell.resetVisualEpoch(app.alloc, welcome); try app.shell.requestTerminalReset(&app.metrics); switch (trigger) { .native_clear_probe => debug_trace.logf( "native_clear", "native_clear_recovery_requested", .{}, ), .ctrl_l => debug_trace.logf( "render", "visual_epoch_reset_requested trigger=ctrl_l", .{}, ), } return true; } pub fn applyThemeUpdate(app: *App, light: bool, rgb: ?ui_render.TerminalRgb) !void { if (!ui_render.themeNeedsUpdate(light, rgb)) return; const prior_light = ui_render.is_light; app.shell.retintEntriesForTheme(app.alloc, prior_light, light) catch |err| { debug_trace.logf("theme", "theme_transcript_retint_failed err={s}", .{@errorName(err)}); return; }; app.pacer.rethemeInlineCode(light); ui_render.initTheme(light, rgb); app.shell.setCommandOutputRenderPolicy(shellStyles()); try app.shell.requestTerminalReset(&app.metrics); app.shell.render_requests.request(.transcript); } pub fn shellStyles() transcript_runtime.Styles { return .{ .system_notice_label_style = ui_render.system_notice_label_style, .system_notice_text_style = ui_render.system_notice_text_style, .reset_style = ui_render.reset_style, .dim_style = ui_render.dim_style, .red_style = ui_render.red_style, .cancelled_text_style = ui_render.hint_style, .notice_information_style = ui_render.system_notice_label_style, .notice_success_style = ui_render.green_style, .notice_warning_style = ui_render.warning_style, .notice_error_style = ui_render.red_style, .notice_cancelled_style = ui_render.dim_style, .code_highlight_theme = if (ui_render.is_light) .light else .dark, }; } var model_completions_buf: [32][]const u8 = undefined; var effort_picker_values_buf: [types.ReasoningEffort.max_options + 1]types.ReasoningEffort = undefined; var effort_picker_labels_buf: [types.ReasoningEffort.max_options + 1][]const u8 = undefined; var fast_picker_labels_buf: [2][]const u8 = undefined; var file_completions_buf: [input_completion_runtime.file_picker_completion_cap]file_index.SearchResult = undefined; var file_match_spans_buf: [input_completion_runtime.file_picker_completion_cap * file_index.max_path_len]file_index.MatchSpan = undefined; var file_path_storage_buf: [input_completion_runtime.file_picker_path_storage_cap]u8 = undefined; noinline fn footerContext( app: *App, upgrade_status_buf: *[64]u8, shimmer_pos: i16, queued_cards: *const QueuedCardProjection, ) render_input.RenderContext { const queue_preview = app.worker.queuePreview(); const model_query = app.input_runtime.picker.activeModelPickerQuery(&app.input_runtime.edit_state); const pending_model = if (app.input_runtime.picker.hasPendingModelPickerSelection()) app.input_runtime.picker.model_picker_pending_model.items else null; var model_picker_stage: picker_state.ModelPickerStage = .model; var picker_items: []const []const u8 = &.{}; var picker_index: usize = 0; var picker_window_start: usize = 0; var picker_anchor: usize = 0; if (model_query) |picker_query| { model_picker_stage = picker_query.stage; picker_anchor = picker_query.token_start; switch (picker_query.stage) { .model => { const count = input_completion_runtime.CompletionRuntime(App).modelPickerCompletions(app, picker_query.query, &model_completions_buf); picker_items = model_completions_buf[0..count]; picker_index = input_completion_runtime.CompletionRuntime(App).modelPickerIndex(app, picker_items); picker_window_start = input_completion_runtime.CompletionRuntime(App).modelPickerWindowStart(app, count, picker_index); }, .effort => { const target = if (app.input_runtime.picker.hasPendingModelPickerSelection()) app.input_runtime.picker.model_picker_pending_model.items else app.selected_model.items; const capabilities = model_capabilities.resolveForApp(App, app, target); const effort_count = model_capabilities.reasoningEffortOptionCount(capabilities); for (0..effort_count) |i| { effort_picker_values_buf[i] = model_capabilities.reasoningEffortAtIndex(capabilities, i); effort_picker_labels_buf[i] = effort_picker_values_buf[i].displayLabel(); } const count = picker_state.filterCompletionLabels(picker_query.query, effort_picker_labels_buf[0..effort_count], effort_picker_labels_buf[0..]); picker_items = effort_picker_labels_buf[0..count]; picker_index = app.input_runtime.picker.model_picker_effort_index; picker_window_start = app.input_runtime.picker.model_picker_effort_window_start; }, .fast => { for (picker_state.model_picker_fast_options, 0..) |option, i| fast_picker_labels_buf[i] = option; const count = picker_state.filterCompletionLabels(picker_query.query, fast_picker_labels_buf[0..], fast_picker_labels_buf[0..]); picker_items = fast_picker_labels_buf[0..count]; picker_index = app.input_runtime.picker.model_picker_fast_index; picker_window_start = app.input_runtime.picker.model_picker_fast_window_start; }, } } const file_query = if (model_query == null) app.input_runtime.picker.activeFilePickerQuery(&app.input_runtime.edit_state) else null; var file_items: []const file_index.SearchResult = &.{}; var file_anchor: usize = 0; var file_selection_index: usize = 0; if (file_query) |fq| { file_anchor = fq.at_offset; const count = app.fileCompletions(fq.query, &file_completions_buf, &file_match_spans_buf, &file_path_storage_buf) catch |err| failed: { debug_trace.logf("render", "file picker render search failed err={s}", .{@errorName(err)}); break :failed 0; }; file_items = file_completions_buf[0..count]; file_selection_index = app.input_runtime.picker.file_completion_index; } const inline_completion = input_completion_runtime.CompletionRuntime(App).visibleInlineCompletion(app); const visible_model = pending_model orelse app.selected_model.items; const visible_capabilities = model_capabilities.resolveForApp(App, app, visible_model); const model_supports_fast = visible_capabilities.supports_fast_mode; const model_supports_effort = visible_capabilities.reasoning_efforts.len > 0; const visible_effort = if (pending_model != null and model_supports_effort) pendingPickerEffort(app, visible_model, model_query, app.input_runtime.picker.model_picker_effort_index) else if (model_capabilities.reasoningEffortSupported(visible_capabilities, app.effort)) app.effort else .auto; const visible_fast_mode = if (pending_model != null and model_supports_fast) pendingPickerFastMode(model_query, app.input_runtime.picker.model_picker_fast_index) else app.fast_mode; const upgrade_label = app.upgrader.statusLabel(upgrade_status_buf); const yolo_warning_active = if (comptime @hasField(App, "permission_state") and @hasField(App, "permission_engine")) app_permission_runtime.Runtime(App).yoloWarningActive(app) else false; const settings_snapshot = app_commands.settingsCatalogSnapshot(app); const now_ms = io_mod.milliTimestamp(); var visible_stream = app.stream; if (!visible_stream.active) { if (app.pacer.completedAssistantPresentationTokenProgress()) |progress| { visible_stream.token_progress = progress; } } return .{ .slash_registry = app.slashRegistry(), .stream = visible_stream, .completed_assistant_presentation_tail = app.pacer.hasCompletedAssistantPresentationTail(), .writing_response = app.pacer.hasPending(), .has_api_key = app.auth.credentialSource() != null, .model = visible_model, .pending_images = app.pending_images.items, .input_appearance = app.input_runtime.input_appearance, .maxxing_mode = app.shell.maxxing_mode, .permission_mode = if (comptime @hasField(App, "permission_engine")) app.permission_engine.mode else .ask,Lines 1–500 of 6411. Load more
const std = @import("std"); const question_prompt = @import("../agent/question_prompt.zig"); const input_completion_runtime = @import("input_completion_runtime.zig"); const input_queue_runtime = @import("input_queue_runtime.zig"); const app_commands = @import("app_commands.zig"); const app_lifecycle = @import("app_lifecycle.zig"); const app_permission_runtime = @import("app_permission_runtime.zig"); const app_session_runtime = @import("app_session_runtime.zig"); const terminal_ui_projection = @import("../terminal/ui_projection.zig"); const worker_runtime = @import("../agent/worker_runtime.zig"); const auth_runtime = @import("../auth/auth_runtime.zig"); const model_capabilities = @import("../config/model_capabilities.zig"); const picker_state = @import("../input/picker_state.zig"); const core_input_runtime = @import("../input/runtime.zig"); const command_specs = @import("../slash_commands/command_specs.zig"); const model_cache_runtime = @import("model_cache_runtime.zig"); const debug_trace = @import("../shared/debug_trace.zig"); const diff_mod = @import("../output/diff.zig"); const io_mod = @import("../shared/io.zig"); const text_utils = @import("../shared/text_utils.zig"); const permission_request = @import("../permissions/permission_request.zig"); const sandbox = @import("../permissions/sandbox.zig"); const skill_runtime = @import("../skills/skill_runtime.zig"); const types = @import("../shared/types.zig"); const subagent_domain = @import("../subagent/domain.zig"); const subagent_projection = @import("../subagent/ui_projection.zig"); const file_index = @import("../workspace/file_index.zig"); const activity_runtime = @import("../output/activity_runtime.zig"); const transcript_presentation = @import("../output/transcript_presentation.zig"); const event_loop = @import("../../ui/event_loop.zig"); const surface_frame = @import("../../ui/footer/surface_frame.zig"); const surface_invalidation = @import("../../ui/footer/surface_invalidation.zig"); const interaction_state = @import("../../ui/footer/interaction_state.zig"); const approval_prompt = @import("../permissions/approval_prompt.zig"); const render_input = @import("../../ui/footer/render_input.zig"); const input_presentation = @import("../../ui/footer/input_presentation.zig"); const footer_viewport = @import("../../ui/footer/viewport.zig"); const render_request = @import("../../ui/render_request.zig"); const ui_input = @import("../../ui/input/runtime.zig"); const input_visual_layout = @import("../../ui/input/visual_layout.zig"); const registered_entities = @import("../input/registered_entities.zig"); const approval_screen = @import("../../ui/approval_screen.zig"); const models_screen = @import("../../ui/models_screen.zig"); const resume_screen = @import("../../ui/resume_screen.zig"); const skills_screen = @import("../../ui/skills_screen.zig"); const help_screen = @import("../../ui/help_screen.zig"); const settings_screen = @import("../../ui/settings_screen.zig"); const full_transcript_screen = @import("../../ui/full_transcript_screen.zig"); const render_engine = @import("../../ui/render_engine.zig"); const build_checkpoint = @import("../../ui/render_engine/build_checkpoint.zig"); const shell_runtime = @import("../../ui/shell_runtime.zig"); const shimmer_runtime = @import("../../ui/transcript/shimmer_runtime.zig"); const ui_subagents = @import("../../ui/subagent/controller.zig"); const ui_terminal = @import("../../ui/terminal/terminal.zig"); const vt_emulator = @import("../terminal/engine.zig"); const transcript_painter = @import("../../ui/transcript/painter.zig"); const command_output_runtime = @import("../../ui/transcript/command_output_runtime.zig"); const resume_projection = @import("../../ui/transcript/resume_projection.zig"); const transcript_runtime = @import("../../ui/transcript/runtime.zig"); const ui_render = @import("../../ui/render.zig"); const assistant_pacer = @import("../../ui/assistant/pacer.zig"); fn set_transcript_assistant_tail_writable( runtime: *transcript_runtime.TranscriptRuntime, writable: bool, ) void { if (runtime.transcript_release.assistant_tail_writable == writable) return; runtime.transcript_release = runtime.transcript_release.with_assistant_tail_writable(writable); debug_trace.logf( "scroll", "assistant tail writability changed writable={s}", .{if (writable) "true" else "false"}, ); } pub const VisualEpochResetTrigger = enum { native_clear_probe, ctrl_l, }; const FrameAttemptResult = struct { shadow_state: render_engine.terminal_diff.ShadowCommitState, animation_visible: bool, yolo_warning_visible: bool = false, fn is_committed(self: FrameAttemptResult) bool { return self.shadow_state.is_committed(); } }; const InlineRenderReconciliation = struct { alternate_screen_owns_rendering: bool = false, terminal_transition: render_engine.terminal_diff.FrameTerminalTransition = .none, }; const PreparedChildConversation = struct { runtime: transcript_runtime.TranscriptRuntime, diff_entries: std.ArrayList(diff_mod.DiffEntry), next_diff_id: u32, }; fn encodeSelectedChildDisplayName( alloc: std.mem.Allocator, raw_name: []const u8, ) error{OutOfMemory}!text_utils.EncodedText { return text_utils.encodeTerminalSafe( alloc, raw_name, std.math.maxInt(usize), ); } /// Couples the physical terminal buffer with the logical presentation that /// owns retry invalidations. Ctrl-X keeps one terminal shadow with native /// primary/alternate buffers while main and child conversations retain /// independent render queues. const SurfaceFrameShell = struct { output: *transcript_runtime.TranscriptRuntime, invalidation_owner: *transcript_runtime.TranscriptRuntime, shadow_vt: ?*vt_emulator.Grid, committed_frame_layout: render_engine.frame_layout.CommittedLayoutSnapshot, history_reset_uses_ris: bool, fn init( output: *transcript_runtime.TranscriptRuntime, invalidation_owner: *transcript_runtime.TranscriptRuntime, committed_frame_layout: render_engine.frame_layout.CommittedLayoutSnapshot, ) SurfaceFrameShell { return .{ .output = output, .invalidation_owner = invalidation_owner, .shadow_vt = output.shadow_vt, .committed_frame_layout = committed_frame_layout, .history_reset_uses_ris = output.history_reset_uses_ris, }; } pub fn frameSink( self: *SurfaceFrameShell, ) render_engine.terminal_diff.FrameSink { return self.output.frameSink(); } pub fn recordFrameInvalidation( self: *SurfaceFrameShell, range: render_engine.paint_plan.FrameInvalidationRange, ) !void { try self.invalidation_owner.recordFrameInvalidation(range); } }; const RenderReconciliation = union(enum) { inline_render: InlineRenderReconciliation, file_approval_screen, skills_screen, models_screen, resume_screen, help_screen, settings_screen, frame_result: FrameAttemptResult, }; const QueuedCardProjection = struct { cards: []render_input.QueuedPromptCard = &.{}, row_count: u16 = 0, editor_active: bool = false, fn deinit(self: *QueuedCardProjection, alloc: std.mem.Allocator) void { for (self.cards) |card| alloc.free(card.bytes); if (self.cards.len > 0) alloc.free(self.cards); self.* = .{}; } }; fn composerInputAppearance(comptime App: type, app: *App) render_input.InputAppearance { return if (app.shell.maxxing_mode == .minimal) .lines else app.input_runtime.input_appearance; } fn composerPrefixStyle(comptime App: type, app: *App) input_presentation.ComposerPrefixStyle { return if (app.shell.maxxing_mode == .minimal) .rail else .arrow; } // Queued prompts stay collapsed behind their summary row until the review is // opened; only then does the banner expand into one card per queued prompt. fn buildQueuedCardProjection(comptime App: type, app: *App) !QueuedCardProjection { if (comptime !@hasField(App, "queued_prompt_review")) return .{}; const review_entries = app.queued_prompt_review.entries; if (!app.queued_prompt_review.visible or !app.queued_prompt_review.active() or review_entries.len == 0) return .{}; const draft_count = review_entries.len; const cards = try app.alloc.alloc(render_input.QueuedPromptCard, draft_count); var built: usize = 0; errdefer { for (cards[0..built]) |card| app.alloc.free(card.bytes); app.alloc.free(cards); } const selected_turn_id: ?u64 = blk: { const selected_index = app.queued_prompt_review.selected_index orelse break :blk null; if (selected_index >= review_entries.len) break :blk null; break :blk review_entries[selected_index].draft.turn_id; }; var total_rows: u16 = 0; var editor_active = false; while (built < draft_count) : (built += 1) { const draft = review_entries[built].draft; const editing = selected_turn_id != null and draft.turn_id == selected_turn_id.?; if (editing) { const source = input_visual_layout.Source{ .input = app.input_runtime.edit_state.input.items, .cursor = app.input_runtime.edit_state.cursor, .terminal_cols = app.shell.layout.cols, .images = app.pending_images.items, .pasted_blocks = app.input_runtime.entities.pasted_blocks.items, .image_tokens = app.input_runtime.entities.image_tokens.items, .skill_tokens = app.input_runtime.entities.skill_tokens.items, }; const summary = input_visual_layout.summarize(source, null); const row_count: u16 = @intCast(@min(summary.total_rows, std.math.maxInt(u16))); cards[built] = .{ .bytes = try app.alloc.dupe(u8, ""), .row_count = @max(row_count, 1), .editing = true, }; total_rows +|= cards[built].row_count; editor_active = true; continue; } const review_input = draft.reviewInput(); const review_skill_spans = draft.reviewSkillDisplaySpans(); var skill_tokens: []registered_entities.SkillTokenSpan = if (review_skill_spans.len > 0) try app.alloc.alloc(registered_entities.SkillTokenSpan, review_skill_spans.len) else &.{}; defer if (skill_tokens.len > 0) app.alloc.free(skill_tokens); for (review_skill_spans, 0..) |span, i| { skill_tokens[i] = .{ .raw_start = span.raw_start, .raw_end = span.raw_end, .name = span.name, .path = span.path, .display_source = span.display_source, .owns_trailing_separator = span.owns_trailing_separator, }; } const bytes = try input_presentation.composeQueuedPromptCard( app.alloc, .{ .input = review_input, .cursor = 0, .terminal_cols = app.shell.layout.cols, .images = draft.images, .pasted_blocks = review_entries[built].pasted_blocks.items, .image_tokens = review_entries[built].image_tokens.items, .skill_tokens = skill_tokens, }, composerInputAppearance(App, app), composerPrefixStyle(App, app), ); const row_count: u16 = @intCast(@min( std.mem.count(u8, bytes, "\n"), std.math.maxInt(u16), )); cards[built] = .{ .bytes = bytes, .row_count = row_count }; total_rows +|= row_count; } return .{ .cards = cards, .row_count = total_rows, .editor_active = editor_active }; } noinline fn approvalScreenNeedsClear( screen_active: bool, request: permission_request.PermissionRequest, layout: types.Layout, commit: ?interaction_state.ApprovalScreenCommit, ) bool { if (!screen_active) return true; const prior = commit orelse return true; return prior.request_id != request.id or prior.rows != layout.rows or prior.cols != layout.cols; } pub fn Runtime(comptime App: type) type { return struct { fn appendDeferredHistoryForVisualEpoch( ctx: *anyopaque, finished: *const types.FinishedPrompt, ) anyerror!assistant_pacer.DeferredFinishCommit { const app: *App = @ptrCast(@alignCast(ctx)); return switch (try app_session_runtime.Runtime(App).appendFinishedPromptForVisualEpoch( app, finished.*, )) { .uncommitted => .uncommitted, .committed, .committed_degraded => .committed, }; } pub fn resetVisualEpoch( app: *App, trigger: VisualEpochResetTrigger, ) !bool { const welcome = try ui_render.welcomeMessage(app.alloc); defer app.alloc.free(welcome); const committed = try app.pacer.discardDeferredPresentationForVisualEpoch( app.alloc, .{ .ctx = app, .append_history = appendDeferredHistoryForVisualEpoch, }, ); if (!committed) { switch (trigger) { .native_clear_probe => debug_trace.logf( "native_clear", "native_clear_recovery_skipped reason=history_uncommitted", .{}, ), .ctrl_l => debug_trace.logf( "render", "visual_epoch_reset_skipped trigger=ctrl_l reason=history_uncommitted", .{}, ), } return false; } try app.shell.resetVisualEpoch(app.alloc, welcome); try app.shell.requestTerminalReset(&app.metrics); switch (trigger) { .native_clear_probe => debug_trace.logf( "native_clear", "native_clear_recovery_requested", .{}, ), .ctrl_l => debug_trace.logf( "render", "visual_epoch_reset_requested trigger=ctrl_l", .{}, ), } return true; } pub fn applyThemeUpdate(app: *App, light: bool, rgb: ?ui_render.TerminalRgb) !void { if (!ui_render.themeNeedsUpdate(light, rgb)) return; const prior_light = ui_render.is_light; app.shell.retintEntriesForTheme(app.alloc, prior_light, light) catch |err| { debug_trace.logf("theme", "theme_transcript_retint_failed err={s}", .{@errorName(err)}); return; }; app.pacer.rethemeInlineCode(light); ui_render.initTheme(light, rgb); app.shell.setCommandOutputRenderPolicy(shellStyles()); try app.shell.requestTerminalReset(&app.metrics); app.shell.render_requests.request(.transcript); } pub fn shellStyles() transcript_runtime.Styles { return .{ .system_notice_label_style = ui_render.system_notice_label_style, .system_notice_text_style = ui_render.system_notice_text_style, .reset_style = ui_render.reset_style, .dim_style = ui_render.dim_style, .red_style = ui_render.red_style, .cancelled_text_style = ui_render.hint_style, .notice_information_style = ui_render.system_notice_label_style, .notice_success_style = ui_render.green_style, .notice_warning_style = ui_render.warning_style, .notice_error_style = ui_render.red_style, .notice_cancelled_style = ui_render.dim_style, .code_highlight_theme = if (ui_render.is_light) .light else .dark, }; } var model_completions_buf: [32][]const u8 = undefined; var effort_picker_values_buf: [types.ReasoningEffort.max_options + 1]types.ReasoningEffort = undefined; var effort_picker_labels_buf: [types.ReasoningEffort.max_options + 1][]const u8 = undefined; var fast_picker_labels_buf: [2][]const u8 = undefined; var file_completions_buf: [input_completion_runtime.file_picker_completion_cap]file_index.SearchResult = undefined; var file_match_spans_buf: [input_completion_runtime.file_picker_completion_cap * file_index.max_path_len]file_index.MatchSpan = undefined; var file_path_storage_buf: [input_completion_runtime.file_picker_path_storage_cap]u8 = undefined; noinline fn footerContext( app: *App, upgrade_status_buf: *[64]u8, shimmer_pos: i16, queued_cards: *const QueuedCardProjection, ) render_input.RenderContext { const queue_preview = app.worker.queuePreview(); const model_query = app.input_runtime.picker.activeModelPickerQuery(&app.input_runtime.edit_state); const pending_model = if (app.input_runtime.picker.hasPendingModelPickerSelection()) app.input_runtime.picker.model_picker_pending_model.items else null; var model_picker_stage: picker_state.ModelPickerStage = .model; var picker_items: []const []const u8 = &.{}; var picker_index: usize = 0; var picker_window_start: usize = 0; var picker_anchor: usize = 0; if (model_query) |picker_query| { model_picker_stage = picker_query.stage; picker_anchor = picker_query.token_start; switch (picker_query.stage) { .model => { const count = input_completion_runtime.CompletionRuntime(App).modelPickerCompletions(app, picker_query.query, &model_completions_buf); picker_items = model_completions_buf[0..count]; picker_index = input_completion_runtime.CompletionRuntime(App).modelPickerIndex(app, picker_items); picker_window_start = input_completion_runtime.CompletionRuntime(App).modelPickerWindowStart(app, count, picker_index); }, .effort => { const target = if (app.input_runtime.picker.hasPendingModelPickerSelection()) app.input_runtime.picker.model_picker_pending_model.items else app.selected_model.items; const capabilities = model_capabilities.resolveForApp(App, app, target); const effort_count = model_capabilities.reasoningEffortOptionCount(capabilities); for (0..effort_count) |i| { effort_picker_values_buf[i] = model_capabilities.reasoningEffortAtIndex(capabilities, i); effort_picker_labels_buf[i] = effort_picker_values_buf[i].displayLabel(); } const count = picker_state.filterCompletionLabels(picker_query.query, effort_picker_labels_buf[0..effort_count], effort_picker_labels_buf[0..]); picker_items = effort_picker_labels_buf[0..count]; picker_index = app.input_runtime.picker.model_picker_effort_index; picker_window_start = app.input_runtime.picker.model_picker_effort_window_start; }, .fast => { for (picker_state.model_picker_fast_options, 0..) |option, i| fast_picker_labels_buf[i] = option; const count = picker_state.filterCompletionLabels(picker_query.query, fast_picker_labels_buf[0..], fast_picker_labels_buf[0..]); picker_items = fast_picker_labels_buf[0..count]; picker_index = app.input_runtime.picker.model_picker_fast_index; picker_window_start = app.input_runtime.picker.model_picker_fast_window_start; }, } } const file_query = if (model_query == null) app.input_runtime.picker.activeFilePickerQuery(&app.input_runtime.edit_state) else null; var file_items: []const file_index.SearchResult = &.{}; var file_anchor: usize = 0; var file_selection_index: usize = 0; if (file_query) |fq| { file_anchor = fq.at_offset; const count = app.fileCompletions(fq.query, &file_completions_buf, &file_match_spans_buf, &file_path_storage_buf) catch |err| failed: { debug_trace.logf("render", "file picker render search failed err={s}", .{@errorName(err)}); break :failed 0; }; file_items = file_completions_buf[0..count]; file_selection_index = app.input_runtime.picker.file_completion_index; } const inline_completion = input_completion_runtime.CompletionRuntime(App).visibleInlineCompletion(app); const visible_model = pending_model orelse app.selected_model.items; const visible_capabilities = model_capabilities.resolveForApp(App, app, visible_model); const model_supports_fast = visible_capabilities.supports_fast_mode; const model_supports_effort = visible_capabilities.reasoning_efforts.len > 0; const visible_effort = if (pending_model != null and model_supports_effort) pendingPickerEffort(app, visible_model, model_query, app.input_runtime.picker.model_picker_effort_index) else if (model_capabilities.reasoningEffortSupported(visible_capabilities, app.effort)) app.effort else .auto; const visible_fast_mode = if (pending_model != null and model_supports_fast) pendingPickerFastMode(model_query, app.input_runtime.picker.model_picker_fast_index) else app.fast_mode; const upgrade_label = app.upgrader.statusLabel(upgrade_status_buf); const yolo_warning_active = if (comptime @hasField(App, "permission_state") and @hasField(App, "permission_engine")) app_permission_runtime.Runtime(App).yoloWarningActive(app) else false; const settings_snapshot = app_commands.settingsCatalogSnapshot(app); const now_ms = io_mod.milliTimestamp(); var visible_stream = app.stream; if (!visible_stream.active) { if (app.pacer.completedAssistantPresentationTokenProgress()) |progress| { visible_stream.token_progress = progress; } } return .{ .slash_registry = app.slashRegistry(), .stream = visible_stream, .completed_assistant_presentation_tail = app.pacer.hasCompletedAssistantPresentationTail(), .writing_response = app.pacer.hasPending(), .has_api_key = app.auth.credentialSource() != null, .model = visible_model, .pending_images = app.pending_images.items, .input_appearance = app.input_runtime.input_appearance, .maxxing_mode = app.shell.maxxing_mode, .permission_mode = if (comptime @hasField(App, "permission_engine")) app.permission_engine.mode else .ask,