Skip to main content

fresh_core/
lib.rs

1use serde::{Deserialize, Serialize};
2
3use ts_rs::TS;
4
5/// Unique identifier for a cursor
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
7#[ts(export)]
8pub struct CursorId(pub usize);
9
10impl CursorId {
11    /// Sentinel value used for inverse events during undo/redo
12    /// This indicates that the event shouldn't move any cursor
13    pub const UNDO_SENTINEL: CursorId = CursorId(usize::MAX);
14}
15
16/// Unique identifier for a split pane (leaf or container)
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
18#[ts(export)]
19pub struct SplitId(pub usize);
20
21/// A split pane that displays a buffer (leaf node in the split tree)
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub struct LeafId(pub SplitId);
24
25impl From<LeafId> for SplitId {
26    fn from(id: LeafId) -> Self {
27        id.0
28    }
29}
30
31/// A split container that holds two children (internal node in the split tree)
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub struct ContainerId(pub SplitId);
34
35impl From<ContainerId> for SplitId {
36    fn from(id: ContainerId) -> Self {
37        id.0
38    }
39}
40
41/// Unique identifier for a buffer
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
43#[serde(transparent)]
44#[derive(TS)]
45#[ts(export)]
46pub struct BufferId(pub usize);
47
48/// Direction of a split
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
50#[ts(export)]
51pub enum SplitDirection {
52    Horizontal,
53    Vertical,
54}
55
56pub mod action;
57pub mod api;
58pub mod command;
59pub mod hooks;
60
61/// Unique identifier for a terminal session
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, TS)]
63#[ts(export)]
64pub struct TerminalId(pub usize);
65
66impl std::fmt::Display for TerminalId {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        write!(f, "Terminal-{}", self.0)
69    }
70}
71
72/// Unique identifier for an editor `Window` — a project-rooted unit
73/// of editor state (file tree, LSP set, splits, buffer set, …) that
74/// the user can switch between as a whole. Modelled on a VS Code
75/// window. See `docs/internal/orchestrator-sessions-design.md`.
76///
77/// Windows are 1-indexed; the editor always boots with id=1 (the
78/// "base" window) so the previous single-root behaviour is the
79/// WindowId(1) special case. Ids are stable within a process and
80/// monotonic — closing a window does not free its id.
81///
82/// Note on naming: Orchestrator presents windows as "agent sessions"
83/// in its UX (matching the parallel-agents domain language), but
84/// internally the editor calls them windows to disambiguate from
85/// Fresh's pre-existing workspace-recovery and config-layer
86/// "session" concepts.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, TS)]
88#[ts(export)]
89pub struct WindowId(pub u64);
90
91impl std::fmt::Display for WindowId {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "Window-{}", self.0)
94    }
95}
96
97/// A terminal identified across the whole editor.
98///
99/// `TerminalId`s are only unique *within* their owning window's
100/// `TerminalManager` — every window numbers its terminals from 0, so two
101/// windows each have a `Terminal-0`. Any layer that resolves a terminal
102/// without already holding its window — most importantly the async
103/// PTY-output messages routed through the main loop — must carry this
104/// `(window, terminal)` pair. Resolving by bare `TerminalId` across
105/// windows is ambiguous: it silently attributes output to whichever
106/// window happens to hold the same local id first.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
108pub struct WindowTerminalId {
109    pub window: WindowId,
110    pub terminal: TerminalId,
111}
112
113impl WindowTerminalId {
114    pub fn new(window: WindowId, terminal: TerminalId) -> Self {
115        Self { window, terminal }
116    }
117}
118
119impl std::fmt::Display for WindowTerminalId {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        write!(f, "{}/{}", self.window, self.terminal)
122    }
123}
124
125pub mod config;
126pub mod file_explorer;
127pub mod file_uri;
128pub mod menu;
129pub mod overlay;
130pub mod plugin_schemas;
131pub mod services;
132pub mod text_property;
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn terminal_id_display_format() {
140        assert_eq!(TerminalId(0).to_string(), "Terminal-0");
141        assert_eq!(TerminalId(42).to_string(), "Terminal-42");
142    }
143
144    #[test]
145    fn window_id_display_format() {
146        assert_eq!(WindowId(1).to_string(), "Window-1");
147        assert_eq!(WindowId(42).to_string(), "Window-42");
148    }
149}