feat(proto): SurfaceSpec, Group, Workspace, status view types

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 21:12:29 +07:00
parent 114922aaf8
commit c8ba401023
2 changed files with 106 additions and 0 deletions
+2
View File
@@ -2,7 +2,9 @@ pub mod codec;
pub mod ids;
pub mod layout;
pub mod message;
pub mod workspace;
pub use ids::{GroupId, SurfaceId, WorkspaceId};
pub use layout::{LayoutNode, Orient};
pub use message::{Cmd, Envelope, ErrorBody, Evt};
pub use workspace::{Group, SurfaceSpec, SurfaceView, Workspace, WorkspaceView};
+104
View File
@@ -0,0 +1,104 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::ids::{GroupId, SurfaceId, WorkspaceId};
use crate::layout::LayoutNode;
/// Everything needed to (re)create a panel's process.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SurfaceSpec {
pub command: String,
#[serde(default)]
pub args: Vec<String>,
pub cwd: String,
#[serde(default)]
pub agent_label: Option<String>,
pub cols: u16,
pub rows: u16,
#[serde(default)]
pub autostart: bool,
}
/// A colored, ordered collection of workspaces.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Group {
pub id: GroupId,
pub name: String,
pub color: String,
pub order: u32,
}
/// Persisted workspace: structure only (no live process state).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Workspace {
pub id: WorkspaceId,
pub path: String,
pub name: String,
#[serde(default)]
pub group_id: Option<GroupId>,
pub order: u32,
#[serde(default)]
pub unread: bool,
/// None = empty workspace (no panels yet).
#[serde(default)]
pub layout: Option<LayoutNode>,
pub surfaces: HashMap<SurfaceId, SurfaceSpec>,
}
/// Per-surface view in `status` — spec plus live lifecycle flag.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SurfaceView {
pub spec: SurfaceSpec,
/// true = has a live actor/PTY; false = stopped (in tree, no process).
pub running: bool,
}
/// Workspace view in `status` / `workspace_changed`: structure + per-surface state.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WorkspaceView {
pub id: WorkspaceId,
pub path: String,
pub name: String,
pub group_id: Option<GroupId>,
pub order: u32,
pub unread: bool,
pub layout: Option<LayoutNode>,
pub surfaces: HashMap<SurfaceId, SurfaceView>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn surface_spec_round_trips() {
let s = SurfaceSpec {
command: "claude".into(),
args: vec![],
cwd: "/tmp".into(),
agent_label: Some("claude".into()),
cols: 80,
rows: 24,
autostart: false,
};
let j = serde_json::to_string(&s).unwrap();
let back: SurfaceSpec = serde_json::from_str(&j).unwrap();
assert_eq!(back, s);
}
#[test]
fn workspace_round_trips_with_empty_layout() {
let w = Workspace {
id: WorkspaceId("w_1".into()),
path: "/tmp/p".into(),
name: "p".into(),
group_id: None,
order: 0,
unread: false,
layout: None,
surfaces: HashMap::new(),
};
let j = serde_json::to_string(&w).unwrap();
let back: Workspace = serde_json::from_str(&j).unwrap();
assert_eq!(back, w);
}
}