tiletopia/src-tauri/src/lib.rs
megaproxy d951c360ae Replace token-usage panel with per-pane context-fill indicator
For a subscription user, lifetime token totals + a $ estimate aren't
actionable; how full each session's context window is right now is. So:

- Removed the UsagePanel, the titlebar 💰 chip, and Ctrl+Shift+U.
- Repurposed the transcript reader (src-tauri/src/usage.rs): get_pane_context
  returns each recent session's CURRENT context occupancy = the last
  assistant turn's input + cache_read + cache_creation tokens (the prompt
  size), instead of lifetime sums. Same UNC/$HOME/cache/recency machinery.
- src/lib/usage.ts now holds context helpers (window inference 200k vs 1M by
  whether occupancy already exceeds 200k, % , green→amber→red ramp, label).
- App polls get_pane_context (15s, visibility-gated) into a cwd→context map
  exposed via orchestration; each LeafPane looks itself up by leaf.cwd and
  renders a slim fill bar + % in its header (hidden for non-claude/unmatched
  panes).

Also fixes the narrow-pane toolbar: a ResizeObserver sets leaf--narrow /
leaf--xnarrow width tiers; the label shrinks first, split buttons / status /
secondary chips drop out by tier, and the close × + context indicator stay
pinned right and visible down to the 180px min width.

tsc clean (apart from the not-yet-installed xterm addons). Rust builds on
the Windows host; needs runtime verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:43:06 +01:00

117 lines
4.9 KiB
Rust

//! Library entry point. `main.rs` calls `run()`.
mod commands;
mod creds;
mod hosts;
mod mcp;
mod mcp_policy;
mod pty;
mod usage;
mod window_state;
use std::sync::Arc;
// `Manager` trait must be in scope to call `.app_handle()` on the `&Window`
// passed to the `on_window_event` closure below. Same pattern as the
// `Emitter` trait needed for `.emit()` (see 2026-05-26 PR-1 session log).
use tauri::Manager;
use crate::mcp::{McpServerHandle, McpState, PendingActions};
use crate::pty::PtyManager;
use crate::window_state::{PendingInits, WindowsState, MAIN_WINDOW_LABEL};
pub fn run() {
let _ = tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.with_writer(std::io::stderr)
.try_init();
// keyring-core 1.x requires explicit store registration before any
// Entry::new() call. We're Windows-only so the Credential Manager
// backend is the only choice. Failure here means SSH passwords won't
// be retrievable — log and continue (host configs still work without
// saved passwords; users just see the prompt and type it manually).
match windows_native_keyring_store::Store::new() {
Ok(store) => keyring_core::set_default_store(store),
Err(e) => tracing::warn!("keyring store init failed: {e}"),
}
// PtyManager and McpState are shared with the MCP server, so register
// them as Arc<T> rather than the plain T. Tauri commands access them
// via `tauri::State<'_, Arc<T>>` and deref / clone as needed.
let ptys: Arc<PtyManager> = Arc::new(PtyManager::new());
let mcp_state: Arc<tokio::sync::RwLock<McpState>> =
Arc::new(tokio::sync::RwLock::new(McpState::default()));
// Pending action registry — separate managed state so mcp_action_reply can
// grab it without needing to lock McpState or reach into TileService.
let pending_actions: Arc<PendingActions> = Arc::new(PendingActions::default());
// Cross-window workspace aggregator: every window pushes its tab list
// here; backend debounces + writes the merged envelope to workspace.json.
let windows_state: Arc<WindowsState> = Arc::new(WindowsState::default());
// Pane-transfer pending-init registry: source window stashes a payload
// keyed by the new window's label; target window pulls it during mount.
let pending_inits: Arc<PendingInits> = Arc::new(PendingInits::default());
let windows_state_for_event = Arc::clone(&windows_state);
let pending_inits_for_event = Arc::clone(&pending_inits);
tauri::Builder::default()
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_opener::init())
.manage(ptys)
.manage(mcp_state)
.manage(McpServerHandle::default())
.manage(pending_actions)
.manage(windows_state)
.manage(pending_inits)
.manage(usage::UsageCache::default())
.on_window_event(move |window, event| {
// When a non-main window closes, drop its workspaces from the
// aggregator AND any unconsumed pending-init payload so neither
// resurrect on next launch. Matches Chrome-style "closing a
// detached window discards its tabs" intent.
if let tauri::WindowEvent::CloseRequested { .. } = event {
let label = window.label().to_string();
if label != MAIN_WINDOW_LABEL {
pending_inits_for_event.by_label.lock().remove(&label);
windows_state_for_event
.forget(window.app_handle().clone(), &label);
}
}
})
.invoke_handler(tauri::generate_handler![
commands::list_distros,
commands::spawn_pane,
commands::write_to_pane,
commands::resize_pane,
commands::kill_pane,
commands::mark_pane_transferring,
commands::claim_pane,
commands::get_pane_ring,
commands::create_pane_window,
commands::take_pending_window_init,
commands::push_window_workspaces,
commands::save_workspace,
commands::load_workspace,
commands::list_ssh_hosts,
commands::save_ssh_hosts,
commands::set_host_password,
commands::delete_host_password,
commands::has_host_password,
commands::mcp_start,
commands::mcp_stop,
commands::mcp_status,
commands::mcp_regenerate_token,
commands::mcp_update_state,
commands::mcp_action_reply,
commands::mcp_policy_load,
commands::mcp_policy_save,
commands::mcp_hard_deny_labels,
usage::get_pane_context,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}