From f1c2aa84664b0f1ec27cdd4e5c7caa2648fb1ff1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Jan 2026 19:42:24 +0000 Subject: [PATCH] Add first-launch onboarding message Display a brief welcome message in grey text on first launch, explaining: - Cmd+N/W for terminal management - Cmd+Enter for focus toggle - Hold Escape to interrupt agents - architect hook command for AI agent integration The message is shown once and tracked via onboarding_shown in persistence.toml. https://claude.ai/code/session_013AMHvCsisuoq8T8BroZKs4 --- docs/configuration.md | 2 ++ src/app/runtime.zig | 24 ++++++++++++++++++++++++ src/config.zig | 4 ++++ 3 files changed, 30 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 2354879..f4df752 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -193,6 +193,7 @@ Auto-managed runtime state. Do not edit manually unless troubleshooting. ```toml font_size = 14 +onboarding_shown = true [window] width = 1440 @@ -212,6 +213,7 @@ terminals = [ | Field | Description | |-------|-------------| | `font_size` | Current font size (adjusted with `Cmd++`/`Cmd+-`) | +| `onboarding_shown` | Whether the first-launch onboarding message has been displayed | | `[window]` | Last window position and dimensions | | `terminals` | Working directories for each terminal (ordered by session index) | diff --git a/src/app/runtime.zig b/src/app/runtime.zig index ca9598b..b04daa4 100644 --- a/src/app/runtime.zig +++ b/src/app/runtime.zig @@ -526,6 +526,30 @@ pub fn run() !void { // Always spawn at least the first terminal try sessions[0].ensureSpawnedWithLoop(&loop); + // Show onboarding message on first launch + if (!persistence.onboarding_shown) { + if (sessions[0].stream) |*stream| { + // Grey text using ANSI bright black (90m), reset with 0m + const onboarding_msg = + "\x1b[90m" ++ + "Welcome to Architect!\n" ++ + "\n" ++ + " Cmd+N New terminal Cmd+W Close terminal\n" ++ + " Cmd+Enter Toggle focus Hold Escape Interrupt agent\n" ++ + "\n" ++ + "Hook AI agents: architect hook claude | codex | gemini\n" ++ + "\x1b[0m\n"; + stream.nextSlice(onboarding_msg) catch |err| { + log.warn("Failed to display onboarding message: {}", .{err}); + }; + sessions[0].markDirty(); + } + persistence.onboarding_shown = true; + persistence.save(allocator) catch |err| { + log.warn("Failed to save persistence after onboarding: {}", .{err}); + }; + } + init_count = sessions.len; const session_ui_info = try allocator.alloc(ui_mod.SessionUiInfo, grid_layout.max_terminals); diff --git a/src/config.zig b/src/config.zig index 5a697f8..be0eb2b 100644 --- a/src/config.zig +++ b/src/config.zig @@ -236,11 +236,13 @@ pub const Persistence = struct { window: WindowConfig = .{}, font_size: c_int = 14, terminal_paths: std.ArrayListUnmanaged([]const u8) = .{}, + onboarding_shown: bool = false, const TomlPersistenceV2 = struct { window: WindowConfig = .{}, font_size: c_int = 14, terminals: ?[]const []const u8 = null, + onboarding_shown: bool = false, }; const TomlPersistenceV1 = struct { @@ -283,6 +285,7 @@ pub const Persistence = struct { defer result.deinit(); persistence.window = result.value.window; persistence.font_size = result.value.font_size; + persistence.onboarding_shown = result.value.onboarding_shown; if (result.value.terminals) |paths| { for (paths) |path| { @@ -339,6 +342,7 @@ pub const Persistence = struct { pub fn serializeToWriter(self: Persistence, writer: anytype) !void { // Write font_size first (top-level scalar) try writer.print("font_size = {d}\n", .{self.font_size}); + try writer.print("onboarding_shown = {}\n", .{self.onboarding_shown}); // Write terminals array before any sections if (self.terminal_paths.items.len > 0) {