Skip to content

feat: implement TTY mode, cclip deletion, and modernize dependencies#23

Merged
Mjoyufull merged 10 commits intodevfrom
feat/updates
Feb 3, 2026
Merged

feat: implement TTY mode, cclip deletion, and modernize dependencies#23
Mjoyufull merged 10 commits intodevfrom
feat/updates

Conversation

@Mjoyufull
Copy link
Owner

@Mjoyufull Mjoyufull commented Feb 1, 2026

Summary

This PR implements several significant improvements to fsel, including a high-performance TTY mode for in-terminal app launching, clipboard entry deletion in cclip mode, and a major dependency modernization to Ratatui 0.30.

  • I'm a clown who can't code 🤡
  • I did basic linting

Changes

App Launcher & TTY Mode

  • New Feature: Added -t, --tty flag and support for terminal_launcher = "tty" in config.
  • Process Replacement: In TTY mode, fsel now replaces itself with the target application using exec, providing a seamless terminal experience.
  • Improved Launch Logic: Moved TTY handling earlier in the launch sequence to bypass unnecessary environment prefixes (uwsm/systemd) and ensure usage history/frecency are recorded before the process is replaced.

Clipboard Management (cclip)

  • Entry Deletion: Implemented clipboard entry deletion via the Alt+Delete keybind.
  • Selection Preservation: Fixed a UI bug where the selection would reset to the top after deletion; the selection now stays at the same physical position (selecting the next item).

Modernization & Performance

  • Ratatui 0.30: Updated to the latest Ratatui workspace structure.
  • Layout Optimization: Enabled the layout-cache feature in Cargo.toml to improve UI responsiveness by caching repeated layout calculations.
  • Dependency Bumps: Updated tokio (1.49), directories (6.0), dirs (6.0), nucleo-matcher (0.3.1), and unicode-width (0.2.2).
  • CLI Fixes: Resolved a compilation error caused by a duplicate prefix_depth field and corrected the -t shorthand parsing.

Testing

  1. TTY Mode: Run fsel -tty, select a terminal app (e.g., htop), and verify it takes over the current terminal session.
  2. cclip Deletion: Run fsel --cclip, select an entry, and press Alt+Delete. Verify the entry is removed and the selection remains at the current index.
  3. Layout Perf: Verified cargo check passes with layout-cache enabled.
  4. Build: Confirmed clean build and clippy with Ratatui 0.30.

Breaking Changes

  • Process Replacement: In TTY mode, the fsel process is replaced. This is intended behavior for terminal launchers but differs from the detached GUI launch model.

Related Issues

N/A (Feature implementations and refinements)

This commit introduces significant features and optimizations across fsel:

App Launcher & TTY Mode:
- Implement `-t, --tty` flag and `terminal_launcher = "tty"` config option.
- Use `CommandExt::exec` to replace the fsel process with the target app
  when in TTY mode, ensuring seamless terminal transitions.
- Refine launch logic to record history and frecency BEFORE process
  replacement and bypass environment-specific prefixes (uwsm, systemd) in
  TTY mode.
- Fix -t shorthand parsing in CLI options.

Clipboard Management (cclip):
- Add entry deletion support via new [cclip_delete](cci:1://file:///home/chris/projects/code/fsel/src/ui/keybinds.rs:228:4-231:5) keybind (Alt+Delete).
- Implement [delete_item](cci:1://file:///home/chris/projects/code/fsel/src/modes/cclip/select.rs:155:0-167:1) helper calling `cclip delete <ID>`.
- Fix selection jump bug: preserve selection index and scroll offset
  after deletion, ensuring the selection stays at the same physical
  position.

Performance & Dependency Updates:
- Update Ratatui to v0.30 and enable the `layout-cache` feature for
  faster layout calculations and UI rendering.
- Bump core dependencies: tokio (1.49), directories (6.0), dirs (6.0),
  nucleo-matcher (0.3.1), and unicode-width (0.2.2).
- Clean up duplicate `prefix_depth` field in CLI options.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added TTY mode (-t/--tty) to launch terminal apps in the current terminal session; supports terminal_launcher = "tty"
    • Added Alt+Delete keybinding to delete selected clipboard entries while preserving selection and scroll
    • Reintroduced --nosway option to control Sway integration
  • Documentation

    • Updated README and usage docs with TTY examples, --nosway, and clipboard deletion behavior

Walkthrough

Adds TTY launch mode and clipboard-entry deletion; migrates serialization from bincode→postcard, time handling from chrono→time, and directory lookup from dirs→directories; updates dependencies and documentation; replaces config-rs flow with manual TOML loading and introduces a ConfigError type.

Changes

Cohort / File(s) Summary
Build / Dependencies
Cargo.toml
Replaced bincode with postcard; removed chrono, config, walkdir; added postcard, time; upgraded directories (5→6), ratatui (0.29→0.30), tokio (1.0→1.49), unicode-width (0.2.0→0.2.2).
Documentation
README.md, USAGE.md
Documented new TTY mode (-t/--tty, terminal_launcher = "tty"), added TTY examples, exposed --nosway option, and documented Alt+Delete clipboard deletion and behavior.
CLI
src/cli.rs
Added public tty: bool to Opts, parse -t/--tty flag and clear terminal_launcher when TTY selected; help text updated.
Configuration
src/config.rs
Removed config-rs builder flow; implemented manual TOML load with XDG/default fallback, env var overrides, added public ConfigError (Io, Toml), expanded FselConfig with dmenu, cclip, app_launcher sections and flattened general/ui/layout.
Serialization Migration
src/core/cache.rs, src/core/database.rs, src/modes/cclip/mod.rs
Switched (de)serialization from bincode::serialize/deserialize to postcard::to_allocvec/from_bytes for caches, frecency, pinned apps, tag metadata, and history.
Time Migration / Logging
src/core/debug_logger.rs
Replaced chrono timestamping with time::OffsetDateTime (local fallback) and format_description for filenames and log entries (ms precision).
Directory Lookup
src/modes/app_launcher/run.rs, src/modes/app_launcher/search.rs
Replaced dirs::home_dir() with `directories::UserDirs::new().map(
TTY Mode Execution
src/modes/app_launcher/launch.rs
Added empty-command preflight; when cli.tty && app.is_terminal, perform pre-exec validation, update history/frecency, then exec(2) to replace current process (short-circuits normal spawn), propagating exec errors.
Clipboard Deletion & Keybinds
src/modes/cclip/select.rs, src/modes/cclip/run.rs, src/ui/keybinds.rs
Added delete_item(rowid) helper; handle Alt+Delete to delete selected entry, reload history while preserving selection/scroll, show confirmation or error; added cclip_delete keybind defaulting to Alt+Delete and parsing for Delete.
ANSI Stripping
src/ui/dmenu_ui.rs
Replaced regex-based ANSI stripping with strip_ansi_escapes when escape sequences detected.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant AppLauncher
    participant DB
    participant OS

    User->>CLI: invoke fsel (args, maybe -t)
    CLI->>AppLauncher: launch(command, opts)
    alt tty mode (opts.tty && app.is_terminal)
        AppLauncher->>DB: record history & frecency
        DB-->>AppLauncher: ok
        AppLauncher->>AppLauncher: validate executable
        AppLauncher->>OS: exec(target)  -- replaces current process
        OS-->>AppLauncher: error (if exec fails)
    else normal mode
        AppLauncher->>OS: spawn(target)  -- child process created
        OS-->>AppLauncher: child pid
        AppLauncher->>DB: update history/frecency
        DB-->>AppLauncher: ok
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Poem

🐰 Postcard snug, the clock retold in time,
TTY hops in to run where shells align,
A clip deleted with an Alt-key beat,
Home paths found, ANSI stripped neat,
A tiny rabbit nods — the patches shine. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main changes: TTY mode implementation, cclip deletion feature, and dependency modernization.
Description check ✅ Passed The description comprehensively covers all major changes including TTY mode, cclip deletion, dependency updates, testing steps, and breaking changes.
Docstring Coverage ✅ Passed Docstring coverage is 82.05% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/updates

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/cli.rs`:
- Around line 114-115: The detailed help output has two lines using leading
spaces instead of the tree-drawing characters, causing inconsistent formatting;
update the help text where the entries for "-V, --version" and "-t, --tty" are
defined (look for the help string or function that constructs those lines, e.g.,
the constant or function that includes the literal " -V, --version              
Show version info" and " -t, --tty                    Launch in current terminal
(TTY mode)") and replace the leading spaces with the appropriate tree characters
(use "├─" for the middle item and "└─" for the last item, and ensure vertical
connector "│" alignment matches surrounding lines) so the ASCII tree matches the
rest of the help output.

In `@src/modes/cclip/run.rs`:
- Around line 1062-1063: The delete_item call currently ignores failures; update
the super::select::delete_item(&rowid) handling to match the success branch by
reporting errors to the user: on Ok(()) keep
ui.set_temp_message(format!("Deleted entry {}", rowid)); and on Err(e) call
ui.set_temp_message(...) with a clear error message (e.g. "Failed to delete
entry {rowid}: {e}") so failures (command not found, permission denied, etc.)
surface to the user.
- Around line 1065-1097: When reloading clipboard history after a deletion, keep
the active tag filter (cli.cclip_tag) instead of using
super::scan::get_clipboard_history() unfiltered: call the same filtering logic
used on initial load (the branch that checks cli.cclip_tag) — either call the
filtered loader if one exists or filter updated_items in place by retaining only
items whose tags contain the selected tag (check cclip_item.tags) before mapping
into Item; ensure you reference cli.cclip_tag,
super::scan::get_clipboard_history(), and the cclip_item.tags field so the
reload presents the same filtered view the user was previously seeing.
🧹 Nitpick comments (1)
src/modes/cclip/run.rs (1)

1070-1088: Consider extracting duplicate item reconstruction logic.

This item reconstruction block (converting CclipItem to Item with formatting) is repeated in at least three places: here (deletion), tag application (lines 1301-1319), and tag removal (lines 1390-1408). Extracting this to a helper function would improve maintainability.

@Mjoyufull Mjoyufull self-assigned this Feb 1, 2026
@Mjoyufull
Copy link
Owner Author

you always have an issue with everything i do bro damn, how about a "good work" every once in a while damn

Mjoyufull and others added 5 commits February 1, 2026 22:37
fixing help text styling

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Report failures from cclip delete command to the user UI
- Maintain the active tag filter when reloading history after deletion
- Ensure UI selection and scroll preservation logic remains robust
Major overhaul to significantly reduce binary size, dependency count, and resolve security audits.

Key Changes:
- Replaced `bincode` (unmaintained) with `postcard` 1.1 for all database/cache serialization.
- Replaced `chrono` with `time` 0.3 for lighter debug logging.
- Replaced `config` crate with lightweight direct TOML parsing + env handling.
- Replaced `regex` with `strip-ansi-escapes` for zero-overhead ANSI stripping.
- Replaced `dirs` with `directories` crate equivalents.
- optimization: Slimmed `tokio` features to only used components.
- security: Resolved `atomic-polyfill` audit warning by disabling unused `heapless` features in postcard.
- fix(cli): Normalize [terminal_launcher](cci:1://file:///home/chris/projects/code/fsel/src/config.rs:189:0-192:1) config to empty string when "tty" mode is detected to prevent misuse.

Stats:
- Dependencies: Reduced from ~317 to 229 (-88 deps)
- Binary Size: Reduced from ~4.5MB to 2.7MB can you belieave that

BREAKING CHANGE:
- Database format changed from bincode to postcard. Existing history and cache will be reset/rebuilt on next launch. i think
@Mjoyufull
Copy link
Owner Author

Aight so check it, i went thru and purged hella bloat cause this shi was gettin rediculous. We droppd from like 317 dependecies down to 229 and the binary size sittin at 2.7MB now (was pushin 5MB before).

Deep dive on what i changed:

  1. Serialization rewrite: We was using bincode but that shi dead/unmaintained and flaggin security warnings. I switched everything (cache, history, cclip) over to postcard 1.1. NOTE: This a breaking change for the DB format, so everyones cache/history finna reset on update. It is what it is tho, way safer and faster now.

  2. Config handling: The config crate heavy asl for no reason, pulling in json/yaml deps when we only usin TOML. I rewrote src/config.rs to just use serde + toml directly and verify env vars manually.

  3. Logging: We was pulling in the whole chrono crate just to format timestamps in debug logs?? Swapped that out for time crate, simple fix.

  4. Misc Cleanups:

    • Nuked regex completely (replaced with strip-ansi-escapes cause we only stripping color codes anyway).
    • Removed walkdir (wasnt even usin it).
    • Consolidated dirs into directories crate so we consistent.
    • Slimmed down tokio features in Cargo.toml to just the essentials (rt-multi-thread, process, sync, etc).
  5. Fixes:

    • Audit was buggin about atomic-polyfill (deprecated). Turns out postcard brings that in via heapless default features. I disabled default features on postcard to kill that warning.
    • Fixed cli.rs logic so if you set terminal_launcher = "tty", it actually clears the string so we dont try to run "tty" as a command later on type shi.

Codebase way cleaner now ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/cli.rs`:
- Around line 262-263: The CLI path should mirror the config handling by
clearing terminal_launcher instead of setting the "fsel-tty-placeholder"; when
detecting tty mode (check cli.tty and app.is_terminal before the early exec()),
set terminal_launcher to an empty/None value (same semantics used in the config
branch) rather than assigning the placeholder string so both code paths are
consistent and the intent is explicit (reference symbols: cli.tty,
app.is_terminal, terminal_launcher, exec()).
🧹 Nitpick comments (2)
src/core/debug_logger.rs (1)

51-52: Consider using compile-time format descriptions for slight efficiency gain.

The current approach parses format descriptions at runtime. While the .unwrap() is safe here since the format strings are known-valid, using time::macros::format_description! would be slightly more efficient by parsing at compile time.

♻️ Optional: Use compile-time format descriptions
+use time::macros::format_description;
+
+// At module level or as constants:
+const DATETIME_FORMAT: &[time::format_description::FormatItem<'static>] = 
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]");
+const FILENAME_FORMAT: &[time::format_description::FormatItem<'static>] = 
+    format_description!("[year][month][day]-[hour][minute][second]");

// Then use:
-        time::OffsetDateTime::now_local().unwrap_or_else(|_| time::OffsetDateTime::now_utc())
-            .format(&time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]").unwrap()).unwrap()
+        time::OffsetDateTime::now_local().unwrap_or_else(|_| time::OffsetDateTime::now_utc())
+            .format(&DATETIME_FORMAT).unwrap()
src/modes/app_launcher/launch.rs (1)

37-48: Consider extracting history/frecency recording to a helper function.

The history and frecency recording logic is duplicated between the TTY path (lines 37-48) and the non-TTY path (lines 141-152). While the placement differs by necessity (before exec vs. after spawn), the logic is identical.

♻️ Suggested helper function
fn record_launch(db: &std::sync::Arc<redb::Database>, app_name: &str, history: i64) -> Result<()> {
    let value = history + 1;
    let write_txn = db.begin_write()?;
    {
        let mut table = write_txn.open_table(crate::core::cache::HISTORY_TABLE)?;
        table.insert(app_name, value)?;
    }
    write_txn.commit()?;

    if let Err(e) = crate::core::database::record_access(db, app_name) {
        eprintln!("Warning: Failed to update frecency: {}", e);
    }
    Ok(())
}

Then use it in both paths:

-        // Record history and frecency BEFORE exec since we disappear after
-        let value = app.history + 1;
-        let write_txn = db.begin_write()?;
-        {
-            let mut table = write_txn.open_table(crate::core::cache::HISTORY_TABLE)?;
-            table.insert(app.name.as_str(), value)?;
-        }
-        write_txn.commit()?;
-
-        if let Err(e) = crate::core::database::record_access(db, &app.name) {
-            eprintln!("Warning: Failed to update frecency: {}", e);
-        }
+        // Record history and frecency BEFORE exec since we disappear after
+        record_launch(db, &app.name, app.history)?;

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@Cargo.toml`:
- Line 38: Update the ratatui dependency features in Cargo.toml so the crossterm
variant is explicit for version 0.30: replace the current features entry for the
ratatui dependency (ratatui = { version = "0.30", default-features = false,
features = ["crossterm", "layout-cache"] }) to use the version-specific feature
name "crossterm_0_29" instead of "crossterm", keeping "layout-cache" and
default-features = false unchanged.

In `@README.md`:
- Around line 207-213: The README's "TTY mode" paragraph uses "etc" without the
required period; update the text in the TTY mode section (the paragraph
mentioning "TTY mode" and examples like `fsel -t`, `fsel --tty`, and
`terminal_launcher = "tty"`) to change "etc" to "etc." so the abbreviation
follows American English punctuation conventions.
🧹 Nitpick comments (3)
src/core/debug_logger.rs (2)

25-30: Consider using compile-time format description macro.

The time crate provides time::macros::format_description! for compile-time validation, which eliminates runtime parsing and the associated .unwrap() calls.

♻️ Suggested refactor using compile-time macro
+use time::macros::format_description;
+
+// At module level or as consts:
+const FILENAME_FMT: &[time::format_description::FormatItem<'static>] =
+    format_description!("[year][month][day]-[hour][minute][second]");

Then at the call site:

 let now = time::OffsetDateTime::now_local().unwrap_or_else(|_| time::OffsetDateTime::now_utc());
-let timestamp = now
-    .format(
-        &time::format_description::parse("[year][month][day]-[hour][minute][second]").unwrap(),
-    )
-    .unwrap();
+let timestamp = now.format(&FILENAME_FMT).unwrap();

55-63: Duplicated format string; consider extracting to a constant.

This exact format string is repeated at line 253 for log_session_end(). Extracting it to a shared constant (ideally using the compile-time macro) would improve maintainability.

♻️ Suggested extraction
use time::macros::format_description;

const TIMESTAMP_FMT: &[time::format_description::FormatItem<'static>] =
    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]");

Then use &TIMESTAMP_FMT in both locations.

Cargo.toml (1)

28-29: Document time 0.3 local-offset platform constraints before deployment.

time crate's local-offset feature has well-documented platform-specific behavior on supported targets:

  • Linux: requires process to be single-threaded (checked via /proc/self/stat); returns IndeterminateOffset error in multi-threaded contexts
  • BSD & other Unix: safe implementation gracefully returns error; local offset detection only succeeds if timezone is configured via /etc/localtime backed by /usr/share/zoneinfo

Ensure error handling is in place for calls to OffsetDateTime::now_local() and similar APIs, especially if multi-threading is enabled on Linux or custom timezone setups are used on BSD systems. The unsound escape hatch --cfg unsound_local_offset should not be used (explicitly unsound, potential segfaults).

directories 6.0 is compatible; its changes are limited to dependency updates.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/config.rs (1)

275-357: ⚠️ Potential issue | 🟡 Minor

Don’t silently ignore a missing explicit config path.
If the user passes --config, falling back to defaults hides mistakes; returning a NotFound error is safer.

🔧 Suggested fix
-        let config_path = if let Some(path) = cli_config_path {
+        let cli_provided = cli_config_path.is_some();
+        let config_path = if let Some(path) = cli_config_path {
             Some(path)
         } else if let Some(proj_dirs) = ProjectDirs::from("", "", "fsel") {
             let mut p = proj_dirs.config_dir().to_path_buf();
             p.push("config.toml");
             Some(p)
         } else {
             None
         };
 
         // Load config from file or use defaults
         let mut cfg: FselConfig = if let Some(ref path) = config_path {
             if path.exists() {
                 let contents = fs::read_to_string(path)?;
                 toml::from_str(&contents)?
+            } else if cli_provided {
+                return Err(std::io::Error::new(
+                    std::io::ErrorKind::NotFound,
+                    format!("Config file not found: {}", path.display()),
+                )
+                .into());
             } else {
                 FselConfig::default()
             }
         } else {
             FselConfig::default()
         };
src/modes/app_launcher/launch.rs (1)

15-55: ⚠️ Potential issue | 🟡 Minor

Guard against empty launch commands before TTY exec.
commands[0] will panic if the command string is empty/whitespace; return a clear error instead.

🔧 Suggested fix
-    let commands = shell_words::split(&app.command)?;
+    let commands = shell_words::split(&app.command)?;
+    if commands.is_empty() {
+        return Err(eyre::eyre!("Empty command for app '{}'", app.name));
+    }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/config.rs`:
- Around line 308-363: The comment claims full parity with the config-rs
Environment source but the code only overrides fields under cfg.general and
cfg.ui (e.g., terminal_launcher, match_mode, highlight_color); add equivalent
env var overrides for the other config sections (cfg.layout, cfg.dmenu,
cfg.cclip, cfg.app_launcher) by reading their respective FSEL_* vars and
parsing/assigning them using the same pattern
(env::var(...).parse().unwrap_or(...)/direct assignment) so environment-driven
settings work for layout/dmenu/cclip/app_launcher, or if this omission is
intentional, update the comment at the top of the block to clearly state only
general and ui are overridden to avoid misleading users.

In `@src/modes/app_launcher/launch.rs`:
- Around line 30-57: The code currently commits history/frecency before calling
process::Command.exec(), so failed execs still count; fix by pre-validating the
target executable (resolve commands[0] on PATH and check it is
accessible/executable) before opening write_txn and inserting into
HISTORY_TABLE, and only perform write_txn.commit() and call
crate::core::database::record_access(db, &app.name) after validation succeeds;
alternatively implement a best‑effort rollback by catching the exec error (the
Err from exec.exec()) and removing the inserted history entry (using write_txn
or a new write transaction to delete app.name from HISTORY_TABLE and adjust
app.history) and attempting to revert frecency via crate::core::database APIs so
failed execs do not persist as launches.

…recording failed execs

- Add FSEL_* environment variable overrides for layout, dmenu, cclip and the legacy
  app_launcher section. Environment values now parse/assign into the corresponding
  config fields (including Option<T> fields for mode-specific settings), matching
  the pattern used for general/ui.
- In app_launcher launch flow, validate/resolve the target executable (PATH search
  and executable-bit check) before opening the write transaction and updating
  HISTORY_TABLE/frecency. Use the resolved path for exec so failed execs are not
  recorded as successful launches.
- Clarify comments; preserve prior behavior and fallbacks for panel sizing/position.

i think i made the right choice here but i could be wrong
@Mjoyufull Mjoyufull merged commit a03d814 into dev Feb 3, 2026
1 check passed
@Mjoyufull Mjoyufull deleted the feat/updates branch February 3, 2026 05:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant