diff --git a/README.md b/README.md index bd7e61f..6121cc5 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,13 @@ and is my daily driver for almost everything. # Features -- **Lightning Fast** TUI with ≤6ms frame times, **low latency** input - handling and smooth **animated scrolling** +- **Lightning Fast** TUI with ≤6ms frame times, **low latency** input handling + and smooth **animated scrolling** - Intuitive UI with **tabs**, **scrollbars** and **palettes** with full **mouse** support for all UI elements - Support for more than **70 programming languages**, **zero configuration** needed, via **tree-sitter** powered syntax highlighting -- **Language Server Protocol** pre configured support for most language - servers +- **Language Server Protocol** pre configured support for most language servers - Powerful **multi-cursor** editing and integrated **clipboard history** - Powerful configurable keybinding system that supports **modal** and **non-modal** editing styles @@ -28,29 +27,26 @@ and is my daily driver for almost everything. - Hybrid rope/piece-table buffer system, edit **very large files** with **thousands of cursors** - Infinite **undo** (at least until you run out of ram) -- Full **unicode** support, including support for the kitty text sizing - protocol +- Full **unicode** support, including support for the kitty text sizing protocol - Plenty of **themes** included and support for vscode themes via the flow-themes project -- Runs on **Linux, FreeBSD, MacOS, Windows and Android** (under termux) - with easy **cross-compilation** to all supported targets +- Runs on **Linux, FreeBSD, MacOS, Windows and Android** (under termux) with + easy **cross-compilation** to all supported targets # Requirements - A modern terminal with **24bit color** and, ideally, **kitty keyboard - protocol** support. **Kitty**, **Foot** and **Ghostty** are the - recommended terminals at this time. **Zellij** also works well. Most - other terminals will work, but likely with reduced functionality. -- **NerdFont** support. Either via terminal font fallback or a patched - font. + protocol** support. **Kitty**, **Foot** and **Ghostty** are the recommended + terminals at this time. **Zellij** also works well. Most other terminals will + work, but likely with reduced functionality. +- **NerdFont** support. Either via terminal font fallback or a patched font. - A **UTF-8** locale # Roadmap -See our [devlog](https://flow-control.dev/devlog/2025/) for on-going -updates from the development team. +See our [devlog](https://flow-control.dev/devlog/2025/) for on-going updates from the development team. ## In Development @@ -67,8 +63,8 @@ updates from the development team. # Download / Install -There is an [installation guide](https://flow-control.dev/installation) on -the main website, and source, release and nightly build binary +There is an [installation guide](https://flow-control.dev/installation) on the +main website, and source, release and nightly build binary [downloads](https://flow-control.dev/downloads). Or check your favorite local system package repository. @@ -86,9 +82,9 @@ Flow builds with zig 0.15.2 at this time. Build with: zig build -Doptimize=ReleaseSafe ``` -Zig will by default build a binary optimized for your specific CPU. If you -get illegal instruction errors add `-Dcpu=baseline` to the build command to -produce a binary with generic CPU support. +Zig will by default build a binary optimized for your specific CPU. If you get +illegal instruction errors add `-Dcpu=baseline` to the build command to produce +a binary with generic CPU support. Thanks to Zig you may also cross-compile from any host to pretty much any @@ -109,8 +105,8 @@ The output binary is: zig-out/bin/flow ``` -It is statically built (by default) and contains all the required -tree-sitter parsers and queries. No additional runtime files are required. +It is statically built (by default) and contains all the required tree-sitter parsers +and queries. No additional runtime files are required. # Running Flow Control @@ -129,9 +125,8 @@ Or if you prefer, let zig install it in your home directory: zig build -Doptimize=ReleaseSafe --prefix ~/.local ``` -Flow Control is a single statically linked binary. No further runtime files -are required. You may install it on another system by simply copying the -binary. +Flow Control is a single statically linked binary. No further runtime files are +required. You may install it on another system by simply copying the binary. ```shell scp zig-out/bin/flow root@otherhost:/usr/local/bin @@ -143,9 +138,8 @@ Files to load may be specifed on the command line: flow fileA.zig fileB.zig ``` -The last file will be opened and the previous files will be placed in -reverse order at the top of the recent files list. Switch to recent files -with Ctrl-e. +The last file will be opened and the previous files will be placed in reverse +order at the top of the recent files list. Switch to recent files with Ctrl-e. Common target line specifiers are supported too: @@ -177,13 +171,13 @@ See `flow --help` for the full list of command line options. A basic user manual is available inside flow. You can open it with the `Open help` command (F1). -It is also available in the website -[documentation](https://flow-control.dev/docs/) section. +It is also available in the website [documentation](https://flow-control.dev/docs/) +section. ## Development Resources -Additional [developer](https://flow-control.dev/docs/#resources) resources -can be found on the Flow Control website at. +Additional [developer](https://flow-control.dev/docs/#resources) resources can +be found on the Flow Control website at. There is also an AI generated developer guide at [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/neurocyte/flow). @@ -192,23 +186,23 @@ Accuracy may vary. Check details against the referenced source code. # Configuration -Configuration is mostly dynamically maintained with various commands in the -UI. It is stored under the standard user configuration path. Usually -`~/.config/flow` on Linux. %APPDATA%\Roaming\flow on Windows. Somewhere -magical on MacOS. +Configuration is mostly dynamically maintained with various commands in the UI. +It is stored under the standard user configuration path. Usually +`~/.config/flow` on Linux. %APPDATA%\Roaming\flow on Windows. Somewhere magical +on MacOS. -There are commands to open the various configuration files, so you don't -have to manually find them. Look for commands starting with `Edit` in the -command palette. +There are commands to open the various configuration files, so you don't have to +manually find them. Look for commands starting with `Edit` in the command +palette. -File types may be configured with the `Edit file type configuration` -command. You can also create a new file type by adding a new `.conf` file -to the `file_type` directory. Have a look at an existing file type to see -what options are available. +File types may be configured with the `Edit file type configuration` command. +You can also create a new file type by adding a new `.conf` file to the +`file_type` directory. Have a look at an existing file type to see what options +are available. -Logs, traces and per-project most recently used file lists are stored in -the standard user application state directory. Usually -`~/.local/state/flow` on Linux and %APPDATA%\Roaming\flow on Windows. +Logs, traces and per-project most recently used file lists are stored in the +standard user application state directory. Usually `~/.local/state/flow` on +Linux and %APPDATA%\Roaming\flow on Windows. # Key bindings and commands @@ -219,16 +213,16 @@ Press `ctrl+shift+p` or `alt+x` to show the command palette. Press `ctrl+F2` to see a full list of all current keybindings and commands. Run the `Edit keybindings` command to save the current keybinding mode to a -file and open it for editing. Save your customized keybinds under a new -name in the same directory to create an entirely new keybinding mode. -Keybinding changes will take effect on restart. +file and open it for editing. Save your customized keybinds under a new name +in the same directory to create an entirely new keybinding mode. Keybinding +changes will take effect on restart. # Terminal configuration -Kitty, Ghostty and most other terminals have default keybindings that -conflict with common editor commands. I highly recommend rebinding them to -keys that are not generally used anywhere else. +Kitty, Ghostty and most other terminals have default keybindings that conflict +with common editor commands. I highly recommend rebinding them to keys that are +not generally used anywhere else. For Kitty rebinding `kitty_mod` is usually enough: ``` diff --git a/src/buffer/reflow.zig b/src/buffer/reflow.zig index 55c1480..7cda022 100644 --- a/src/buffer/reflow.zig +++ b/src/buffer/reflow.zig @@ -1,15 +1,10 @@ pub fn reflow(allocator: std.mem.Allocator, text: []const u8, width: usize) error{ OutOfMemory, WriteFailed }![]u8 { - const len = text.len; - const trailing_ln: bool = (len > 0 and text[len - 1] == '\n'); - const input = if (trailing_ln) text[0 .. len - 1] else text; - const prefix = detect_prefix(input); - const words = try split_words(allocator, input, prefix.len); + const prefix = detect_prefix(text); + const words = try split_words(allocator, text, prefix.len); defer allocator.free(words); var output: std.Io.Writer.Allocating = .init(allocator); const writer = &output.writer; - std.log.info("reflow @{d}", .{width}); - var first = true; var line_len: usize = 0; for (words) |word| { @@ -21,35 +16,30 @@ pub fn reflow(allocator: std.mem.Allocator, text: []const u8, width: usize) erro .begin => { if (first) { try writer.writeAll(prefix.first); - line_len += prefix.first.len; first = false; } else { try writer.writeAll(prefix.continuation); - line_len += prefix.continuation.len; var pad = prefix.first.len - prefix.continuation.len; - while (pad > 0) : (pad -= 1) { + while (pad > 0) : (pad -= 1) try writer.writeByte(' '); - line_len += 1; - } } + line_len += prefix.len; continue :blk .words; }, .words => { - if (line_len > prefix.len) { - if (line_len + word.len + 1 >= width) { - try writer.writeByte('\n'); - line_len = 0; - continue :blk .begin; - } - try writer.writeByte(' '); - line_len += 1; + if (line_len > prefix.len and line_len + word.len + 1 >= width - 1) { + try writer.writeByte('\n'); + line_len = 0; + continue :blk .begin; } + if (line_len > prefix.len) + try writer.writeByte(' '); try writer.writeAll(word); line_len += word.len; }, } } - if (trailing_ln) try writer.writeByte('\n'); + return output.toOwnedSlice(); } @@ -71,10 +61,10 @@ fn detect_prefix(text: []const u8) Prefix { const line1 = lines.next() orelse return .{}; var prefix: []const u8 = line1; var count: usize = 0; - while (lines.next()) |line| if (line.len > 0) { + while (lines.next()) |line| { prefix = lcp(prefix, line); count += 1; - }; + } if (count < 1) return .{ .len = 0, .first = &.{}, diff --git a/src/config.zig b/src/config.zig index 571cb70..fe3a454 100644 --- a/src/config.zig +++ b/src/config.zig @@ -33,7 +33,6 @@ follow_cursor_on_buffer_switch: bool = false, //scroll cursor into view on buffe default_cursor: CursorShape = .default, modes_can_change_cursor: bool = true, enable_auto_save: bool = false, -auto_save_mode: AutoSaveMode = .on_focus_change, limit_auto_save_file_types: ?[]const []const u8 = null, // null means *all* enable_prefix_keyhints: bool = true, enable_auto_find: bool = true, @@ -46,7 +45,7 @@ auto_run_commands: ?[]const []const u8 = &.{"save_session_quiet"}, // a list of indent_size: usize = 4, tab_width: usize = 8, indent_mode: IndentMode = .auto, -reflow_width: usize = 76, +reflow_width: usize = 80, top_bar: []const u8 = "tabs", bottom_bar: []const u8 = "mode file log selection diagnostics keybind branch linenumber clock spacer", @@ -150,12 +149,6 @@ pub const WhitespaceMode = enum { none, }; -pub const AutoSaveMode = enum { - on_input_idle, - on_focus_change, - on_document_change, -}; - pub const CursorShape = enum { default, block_blink, diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 65c4dea..ee77f90 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -649,7 +649,7 @@ pub const Editor = struct { self.highlight_references_pending.deinit(self.allocator); self.handlers.deinit(); self.logger.deinit(); - if (self.buffer) |p| self.retire_buffer(p, meta.written()); + if (self.buffer) |p| self.buffer_manager.retire(p, meta.written()); } pub fn need_render(_: *Self) void { @@ -697,6 +697,7 @@ pub const Editor = struct { defer frame.deinit(); break :blk try self.buffer_manager.open_file(file_path); }; + if (tui.config().enable_auto_save) buffer.enable_auto_save(); return self.open_buffer(file_path, buffer, null); } @@ -712,7 +713,7 @@ pub const Editor = struct { fn open_buffer(self: *Self, file_path: []const u8, new_buf: *Buffer, file_type_: ?[]const u8) !void { const frame = tracy.initZone(@src(), .{ .name = "open_buffer" }); defer frame.deinit(); - errdefer self.retire_buffer(new_buf, null); + errdefer self.buffer_manager.retire(new_buf, null); self.cancel_all_selections(); self.get_primary().reset(); self.file_path = try self.allocator.dupe(u8, file_path); @@ -754,6 +755,8 @@ pub const Editor = struct { self.checked_formatter = false; self.formatter = null; + self.maybe_enable_auto_save(); + const syn = blk: { const frame_ = tracy.initZone(@src(), .{ .name = "create" }); defer frame_.deinit(); @@ -790,6 +793,7 @@ pub const Editor = struct { buffer.file_type_icon = fti; buffer.file_type_color = ftc; } + const auto_save = if (self.buffer) |b| b.is_auto_save() and !b.is_ephemeral() else false; if (buffer_meta) |meta| { const frame_ = tracy.initZone(@src(), .{ .name = "extract_state" }); @@ -797,29 +801,24 @@ pub const Editor = struct { var iter = meta; try self.extract_state(&iter, .none); } - - const auto_save = if (self.buffer) |b| - self.maybe_enable_buffer_auto_save(b) - else - false; - try self.send_editor_open(file_path, new_buf.file_exists, ftn, fti, ftc, auto_save); } - fn maybe_enable_buffer_auto_save(self: *Self, buffer: *Buffer) bool { - if (self.restored_state) return false; - if (!tui.config().enable_auto_save) return false; - const self_file_type = self.file_type orelse return false; + fn maybe_enable_auto_save(self: *Self) void { + const buffer = self.buffer orelse return; + if (self.restored_state) return; + buffer.disable_auto_save(); + if (!tui.config().enable_auto_save) return; + const self_file_type = self.file_type orelse return; enable: { const file_types = tui.config().limit_auto_save_file_types orelse break :enable; for (file_types) |file_type| if (std.mem.eql(u8, file_type, self_file_type.name)) break :enable; - return false; + return; } buffer.enable_auto_save(); - return !buffer.is_ephemeral(); } fn detect_indent_mode(self: *Self, content: []const u8) void { @@ -854,16 +853,11 @@ pub const Editor = struct { } pub const set_editor_tab_width_meta: Meta = .{ .arguments = &.{.integer} }; - fn retire_buffer(self: *const Self, buffer: *Buffer, meta: ?[]const u8) void { - self.buffer_manager.retire(buffer, meta); - auto_save_buffer(buffer, .on_focus_change); - } - fn close(self: *Self) !void { var meta: std.Io.Writer.Allocating = .init(self.allocator); defer meta.deinit(); self.write_state(&meta.writer) catch {}; - if (self.buffer) |b_mut| self.retire_buffer(b_mut, meta.written()); + if (self.buffer) |b_mut| self.buffer_manager.retire(b_mut, meta.written()); self.cancel_all_selections(); self.buffer = null; self.plane.erase(); @@ -2026,11 +2020,10 @@ pub const Editor = struct { fn send_editor_update(self: *const Self, old_root: ?Buffer.Root, new_root: ?Buffer.Root, eol_mode: Buffer.EolMode) !void { _ = try self.handlers.msg(.{ "E", "update" }); - if (self.buffer) |buffer| { - if (self.syntax) |_| if (self.file_path) |file_path| if (old_root != null and new_root != null) - project_manager.did_change(file_path, buffer.lsp_version, try text_from_root(new_root, eol_mode), try text_from_root(old_root, eol_mode), eol_mode) catch {}; - auto_save_buffer(buffer, .on_document_change); - } + if (self.buffer) |buffer| if (self.syntax) |_| if (self.file_path) |file_path| if (old_root != null and new_root != null) + project_manager.did_change(file_path, buffer.lsp_version, try text_from_root(new_root, eol_mode), try text_from_root(old_root, eol_mode), eol_mode) catch {}; + if (self.buffer) |b| if (b.is_auto_save() and !b.is_ephemeral()) + tp.self_pid().send(.{ "cmd", "save_file", .{} }) catch {}; } pub fn vcs_content_update(self: *const Self) !void { @@ -5554,10 +5547,7 @@ pub const Editor = struct { buffer.disable_auto_save(); self.send_editor_auto_save(buffer.is_auto_save()) catch {}; if (buffer.is_auto_save()) - std.log.info("enabled auto save {t}", .{tui.config().auto_save_mode}) - else - std.log.info("disabled auto save", .{}); - auto_save_buffer(buffer, .on_document_change); + tp.self_pid().send(.{ "cmd", "save_file", .{} }) catch {}; } pub const toggle_auto_save_meta: Meta = .{ .description = "Toggle auto save" }; @@ -7145,7 +7135,6 @@ pub const EditorWidget = struct { if (self.focused) self.commands.unregister(); self.focused = false; command.executeName("enter_mode_default", .{}) catch {}; - if (self.editor.buffer) |b| auto_save_buffer(b, .on_focus_change); } pub fn update(self: *Self) void { @@ -7224,7 +7213,6 @@ pub const EditorWidget = struct { } }, }; - if (self.editor.buffer) |b| auto_save_buffer(b, .on_input_idle); return false; } else if (try m.match(.{ "whitespace_mode", tp.extract(&whitespace_mode) })) { self.editor.render_whitespace = whitespace_mode; @@ -7440,24 +7428,3 @@ fn ViewMap(T: type, default: T) type { } }; } - -pub fn auto_save_buffer(b: *Buffer, comptime event: @import("config").AutoSaveMode) void { - const auto_save = switch (tui.config().auto_save_mode) { - .on_input_idle => comptime switch (event) { - .on_input_idle, .on_focus_change => true, - .on_document_change => false, - }, - .on_document_change => comptime switch (event) { - .on_document_change => true, - .on_input_idle, .on_focus_change => false, - }, - .on_focus_change => comptime switch (event) { - .on_focus_change => true, - .on_input_idle, .on_document_change => false, - }, - }; - if (auto_save and b.is_auto_save() and !b.is_ephemeral() and b.is_dirty()) { - tp.self_pid().send(.{ "cmd", "save_buffer", .{ b.get_file_path(), "auto_save" } }) catch {}; - std.log.debug("auto save {t} {s}", .{ event, b.get_file_path() }); - } -} diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index eb6b8c2..6b18f33 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -188,18 +188,10 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { } else if (try m.match(.{ "navigate_complete", tp.extract(&path), tp.extract(&goto_args), tp.null_, tp.null_ })) { cmds.navigate_complete(self, null, path, goto_args, null, null, null) catch |e| return tp.exit_error(e, @errorReturnTrace()); return true; - } else if (try m.match(.{"focus_out"})) { - self.process_focus_out() catch |e| return tp.exit_error(e, @errorReturnTrace()); } return if (try self.floating_views.send(from_, m)) true else self.widgets.send(from_, m); } -fn process_focus_out(self: *Self) error{OutOfMemory}!void { - const buffers = try self.buffer_manager.list_unordered(self.allocator); - defer self.allocator.free(buffers); - for (buffers) |b| ed.auto_save_buffer(b, .on_focus_change); -} - pub fn update(self: *Self) void { self.widgets.update(); self.floating_views.update(); @@ -777,11 +769,8 @@ const cmds = struct { pub const create_new_file_meta: Meta = .{ .description = "New file" }; pub fn save_buffer(self: *Self, ctx: Ctx) Result { - var auto_save: bool = false; var file_path: []const u8 = undefined; - if (ctx.args.match(.{ tp.extract(&file_path), "auto_save" }) catch false) { - auto_save = true; - } else if (!(ctx.args.match(.{tp.extract(&file_path)}) catch false)) + if (!(ctx.args.match(.{tp.extract(&file_path)}) catch false)) return error.InvalidSaveBufferArgument; const buffer = self.buffer_manager.get_buffer_for_file(file_path) orelse return; @@ -797,10 +786,7 @@ const cmds = struct { const logger = log.logger("buffer"); defer logger.deinit(); if (buffer.is_ephemeral()) return logger.print_err("save", "ephemeral buffer, use save as", .{}); - if (!buffer.is_dirty()) { - if (!auto_save) logger.print("no changes to save", .{}); - return; - } + if (!buffer.is_dirty()) return logger.print("no changes to save", .{}); try buffer.store_to_file_and_clean(file_path); } pub const save_buffer_meta: Meta = .{ .arguments = &.{.string} }; diff --git a/src/tui/status/filestate.zig b/src/tui/status/filestate.zig index 3425d59..3b6e8da 100644 --- a/src/tui/status/filestate.zig +++ b/src/tui/status/filestate.zig @@ -83,10 +83,6 @@ pub fn layout(_: *Self, _: *ButtonType) Widget.Layout { } pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { - const auto_save = if (self.auto_save) switch (tui.config().auto_save_mode) { - .on_input_idle, .on_document_change => true, - .on_focus_change => false, - } else false; const style_base = theme.statusbar; const style_label = if (btn.active) theme.editor_cursor else style_base; btn.plane.set_base_style(theme.editor); @@ -103,9 +99,9 @@ pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { if (tui.mini_mode()) |_| render_mini_mode(&btn.plane, theme) else if (self.detailed) - self.render_detailed(&btn.plane, theme, auto_save) + self.render_detailed(&btn.plane, theme) else - self.render_normal(&btn.plane, theme, auto_save); + self.render_normal(&btn.plane, theme); self.render_terminal_title(); return false; } @@ -139,19 +135,19 @@ fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void { // 󱣪 Content save check // 󱑛 Content save cog // 󰆔 Content save all -fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme, auto_save: bool) void { +fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme) void { plane.on_styles(styles.italic); _ = plane.putstr(" ") catch {}; if (self.file_icon.len > 0 and tui.config().show_fileicons) { self.render_file_icon(plane, theme); _ = plane.print(" ", .{}) catch {}; } - _ = plane.putstr(if (!self.file_exists) "󰽂 " else if (auto_save) "󱑛 " else if (self.file_dirty) "󰆓 " else "") catch {}; + _ = plane.putstr(if (!self.file_exists) "󰽂 " else if (self.auto_save) "󱑛 " else if (self.file_dirty) "󰆓 " else "") catch {}; _ = plane.print("{s}", .{self.name}) catch {}; return; } -fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme, auto_save: bool) void { +fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme) void { plane.on_styles(styles.italic); _ = plane.putstr(" ") catch {}; if (self.file_icon.len > 0 and tui.config().show_fileicons) { @@ -171,7 +167,7 @@ fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme, auto_ .tabs => "[⭾ = ␉]", }; - _ = plane.putstr(if (!self.file_exists) "󰽂" else if (auto_save) "󱑛" else if (self.file_dirty) "󰆓" else "󱣪") catch {}; + _ = plane.putstr(if (!self.file_exists) "󰽂" else if (self.auto_save) "󱑛" else if (self.file_dirty) "󰆓" else "󱣪") catch {}; _ = plane.print(" {s}:{d}:{d}", .{ self.name, self.line + 1, self.column + 1 }) catch {}; _ = plane.print(" of {d} lines", .{self.lines}) catch {}; if (self.file_type.len > 0) diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 65f1ce0..50f2816 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -902,10 +902,7 @@ const Tab = struct { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); const buffer_ = buffer_manager.buffer_from_ref(self.buffer_ref); const is_dirty = if (buffer_) |buffer| buffer.is_dirty() else false; - const auto_save = if (buffer_) |buffer| if (buffer.is_auto_save()) switch (tui.config().auto_save_mode) { - .on_input_idle, .on_document_change => true, - .on_focus_change => false, - } else false else false; + const auto_save = if (buffer_) |buffer| buffer.is_auto_save() else false; self.render_padding(plane, .left); if (self.tab_style.file_type_icon) if (buffer_) |buffer| if (buffer.file_type_icon) |icon| { const color_: ?u24 = if (buffer.file_type_color) |color| if (!(color == 0xFFFFFF or color == 0x000000 or color == 0x000001)) color else null else null; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index d6c196e..7c816e3 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -499,6 +499,12 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try m.match(.{"mouse_leave"})) return; + if (try m.match(.{"focus_in"})) + return; + + if (try m.match(.{"focus_out"})) + return; + if (try m.match(.{ "K", tp.more })) return; @@ -508,12 +514,6 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try self.send_widgets(from, m)) return; - if (try m.match(.{"focus_in"})) - return; - - if (try m.match(.{"focus_out"})) - return; - if (try m.match(.{ "exit", tp.more })) { if (try m.match(.{ tp.string, "normal" }) or try m.match(.{ tp.string, "timeout_error", 125, "Operation aborted." }) or