Fix workspace accumulation, tab-close popover, scrollbars, drag ghost
- window_state.rs: persist only the main window's workspaces. The aggregator flattened every window's tabs into the saved file; main then adopted the whole blob on launch, so detached windows' ephemeral tabs (and Pane N drag-out artifacts) accumulated without bound. - TabStrip: portal the close-confirm popover to <body> with fixed, viewport-clamped positioning so the horizontally-scrolling strip can't clip it and it never runs off a window edge. - styles.css: make themed ::-webkit-scrollbar global, not just xterm viewport. - LeafPane: B1 drag-out ghost chip (portal, edge-pinned, orange detach state). - App.tsx: moveToNewWindow waits briefly for pane registration instead of failing instantly on an in-flight spawn/adopt. - gitignore cargo-test.lo*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bea6cf2977
commit
e6d0040021
9 changed files with 224 additions and 95 deletions
|
|
@ -66,29 +66,20 @@ impl WindowsState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Build the on-disk envelope by concatenating every window's
|
||||
/// workspaces in stable label order (main first when present, then
|
||||
/// the rest sorted alphabetically by label — deterministic so the
|
||||
/// file diff stays stable across no-op saves).
|
||||
/// Build the on-disk envelope from ONLY the main window's workspaces.
|
||||
///
|
||||
/// Detached windows are ephemeral — their tabs are discarded on close
|
||||
/// (Chrome-style), and only the main window's tabs are meant to survive
|
||||
/// a restart. Persisting every window's workspaces (the original design)
|
||||
/// let detached windows' tabs — and the `Pane N` adopt-targets from
|
||||
/// drag-out — leak into the saved file; on the next launch the main
|
||||
/// window loaded the whole blob and adopted them all, so they
|
||||
/// accumulated without bound. Keying the persisted set to the main label
|
||||
/// makes detached state structurally unable to pollute it.
|
||||
fn build_envelope(&self) -> Value {
|
||||
let map = self.per_window.lock();
|
||||
let mut keys: Vec<&String> = map.keys().collect();
|
||||
keys.sort_by(|a, b| {
|
||||
// main first, then alpha
|
||||
match (a.as_str(), b.as_str()) {
|
||||
(MAIN_WINDOW_LABEL, _) => std::cmp::Ordering::Less,
|
||||
(_, MAIN_WINDOW_LABEL) => std::cmp::Ordering::Greater,
|
||||
(x, y) => x.cmp(y),
|
||||
}
|
||||
});
|
||||
let mut workspaces: Vec<Value> = Vec::new();
|
||||
for k in keys {
|
||||
if let Some(list) = map.get(k) {
|
||||
for w in list {
|
||||
workspaces.push(w.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let workspaces: Vec<Value> =
|
||||
map.get(MAIN_WINDOW_LABEL).cloned().unwrap_or_default();
|
||||
serde_json::json!({
|
||||
"version": 2,
|
||||
"workspaces": workspaces,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue