This commit is contained in:
@@ -21,8 +21,10 @@ binary is still a scaffold, while the isolated native UI spikes now exercise an
|
|||||||
interactive 20/60/20 workspace backed by six live comparison surfaces, and the
|
interactive 20/60/20 workspace backed by six live comparison surfaces, and the
|
||||||
root workspace contains the first bounded local PTY, runtime actor, VT engine,
|
root workspace contains the first bounded local PTY, runtime actor, VT engine,
|
||||||
and capability-gated workspace command boundaries. The GPUI slice shows a
|
and capability-gated workspace command boundaries. The GPUI slice shows a
|
||||||
responsive one-, three-, or five-lane work band—five on a 3440 px ultrawide—with
|
responsive one-, three-, or five-panel workspace—five on a 3440 px ultrawide—
|
||||||
one styled actor-owned VT session and five deterministic comparison surfaces.
|
where every panel owns its own context, work surface, and decision shelf. One
|
||||||
|
panel contains a styled actor-owned VT session and five comparison surfaces
|
||||||
|
remain deterministic.
|
||||||
Retained-history navigation is wired. We are still validating terminal text
|
Retained-history navigation is wired. We are still validating terminal text
|
||||||
selection and mouse input, standalone runtime IPC/durability, ACP integration,
|
selection and mouse input, standalone runtime IPC/durability, ACP integration,
|
||||||
packaging, and usage-data contracts before a large implementation.
|
packaging, and usage-data contracts before a large implementation.
|
||||||
|
|||||||
@@ -80,14 +80,14 @@ durability step. See decisions 0004 and 0005.
|
|||||||
|
|
||||||
## Default workspace composition
|
## Default workspace composition
|
||||||
|
|
||||||
The default desktop workspace has three horizontal bands: the top 20% holds pane,
|
The default desktop workspace is a responsive horizontal row of self-contained
|
||||||
agent, tool, context, and goal state; the middle 60% holds responsive vertical
|
vertical panels: one panel in compact windows, three at normal desktop widths,
|
||||||
work lanes; and the bottom 20% is the answer, choice, chat, and approval shelf.
|
and five on a 3440 px ultrawide. Inside every panel, the top 20% holds that pane's
|
||||||
The work region shows one lane in compact windows, three at normal desktop
|
agent, tool, context, goal, target, and status; the middle 60% holds its terminal
|
||||||
widths, and five on a 3440 px ultrawide. The selected lane alone owns keyboard
|
or work surface; and the bottom 20% holds that pane's answer, choices, chat, and
|
||||||
input. Six surfaces continue updating so performance comparisons remain
|
approval boundary. The selected panel alone owns keyboard input. Six surfaces
|
||||||
meaningful, with a sliding visible window keeping the selected pane onscreen.
|
continue updating so performance comparisons remain meaningful, with a sliding
|
||||||
See decisions 0008 and 0009.
|
visible window keeping the selected pane onscreen. See decisions 0008 and 0009.
|
||||||
|
|
||||||
We should evaluate, not blindly copy, WezTerm, Zellij, RMUX, tmux, and cmux. The
|
We should evaluate, not blindly copy, WezTerm, Zellij, RMUX, tmux, and cmux. The
|
||||||
first spike must compare a reusable terminal crate with a small first-party layer.
|
first spike must compare a reusable terminal crate with a small first-party layer.
|
||||||
|
|||||||
@@ -66,15 +66,14 @@ surface change never silently launches a process, moves the pane to another
|
|||||||
host, or changes which agent owns the session. Unsupported surfaces are shown as
|
host, or changes which agent owns the session. Unsupported surfaces are shown as
|
||||||
unavailable rather than simulated.
|
unavailable rather than simulated.
|
||||||
|
|
||||||
The default workspace has three horizontal bands: the top 20% shows context,
|
The default workspace is a responsive row of self-contained vertical panels:
|
||||||
pane/agent state, tools, and goal; the middle 60% is a responsive set of vertical
|
one panel in compact windows, three at normal desktop widths, and up to five on
|
||||||
work lanes; and the bottom 20% is the decision shelf. The work band shows one
|
an ultrawide display. Every panel has its own 20% context region, 60% terminal or
|
||||||
lane in compact windows, three at normal desktop widths, and up to five on an
|
work surface, and 20% decision shelf. One panel owns keyboard input at a time,
|
||||||
ultrawide display. One lane owns keyboard input at a time, and selecting a lane
|
and selecting it never pauses the others. Explicit split trees and dashboard
|
||||||
never pauses the others. Explicit split trees and dashboard layouts remain
|
layouts remain supported, but they do not displace this cmux-style default.
|
||||||
supported, but they do not displace this terminal-first default.
|
|
||||||
|
|
||||||
An optional decision shelf sits below the active work surface. It may show an
|
An optional decision shelf sits below each pane's work surface. It may show an
|
||||||
answer draft, two or three concrete choices, why each was suggested, and the
|
answer draft, two or three concrete choices, why each was suggested, and the
|
||||||
capability each choice would need. Suggestions are inert data. Choosing one may
|
capability each choice would need. Suggestions are inert data. Choosing one may
|
||||||
prepare a typed workspace plan, but any command, file mutation, credential use,
|
prepare a typed workspace plan, but any command, file mutation, credential use,
|
||||||
|
|||||||
+2
-2
@@ -118,8 +118,8 @@ who already have the final cargo-watch release installed.
|
|||||||
blocked OSC 52 behavior, key encoding, application-cursor mode, and resize.
|
blocked OSC 52 behavior, key encoding, application-cursor mode, and resize.
|
||||||
- `lumbridge-core` tests capability-gated split/select/rename/close commands,
|
- `lumbridge-core` tests capability-gated split/select/rename/close commands,
|
||||||
idempotent request IDs, close-tree promotion, and atomic agentic setup plans.
|
idempotent request IDs, close-tree promotion, and atomic agentic setup plans.
|
||||||
- The GPUI slice tests its key-event adapter, responsive one/three/five-lane
|
- The GPUI slice tests its key-event adapter, responsive one/three/five-panel
|
||||||
geometry, selected-pane lane windowing, 20/60/20 geometry-to-PTY sizing, xterm
|
geometry, selected-panel windowing, per-panel 20/60/20 PTY sizing, xterm
|
||||||
256-color conversion, styled-run coalescing, cursor-run boundaries, and
|
256-color conversion, styled-run coalescing, cursor-run boundaries, and
|
||||||
scrollback shortcut routing. It renders one real actor-owned VT session while
|
scrollback shortcut routing. It renders one real actor-owned VT session while
|
||||||
five surfaces continue their deterministic background workload.
|
five surfaces continue their deterministic background workload.
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ from macOS and Linux and the hard gates pass.
|
|||||||
The current programs establish dependency, build, launch, and interaction
|
The current programs establish dependency, build, launch, and interaction
|
||||||
baselines. GPUI now routes a real PTY through a VT engine and keyboard encoder,
|
baselines. GPUI now routes a real PTY through a VT engine and keyboard encoder,
|
||||||
paints coalesced styled cell runs and cursor shapes, and derives PTY rows/columns
|
paints coalesced styled cell runs and cursor shapes, and derives PTY rows/columns
|
||||||
from its responsive one/three/five-lane 60% terminal band when the window
|
from the 60% terminal region inside each responsive one/three/five-panel layout
|
||||||
changes. Retained-history page/top/bottom navigation is wired. Text selection,
|
when the window changes. Retained-history page/top/bottom navigation is wired. Text selection,
|
||||||
mouse modes, a native Markdown editor, and one isolated browser child remain.
|
mouse modes, a native Markdown editor, and one isolated browser child remain.
|
||||||
|
|
||||||
## Ubuntu baseline — metal, 2026-08-31
|
## Ubuntu baseline — metal, 2026-08-31
|
||||||
@@ -87,9 +87,10 @@ fallback.
|
|||||||
## Measurement semantics
|
## Measurement semantics
|
||||||
|
|
||||||
Both renderers retain the same all-deterministic six-surface action stream for
|
Both renderers retain the same all-deterministic six-surface action stream for
|
||||||
comparison. GPUI now presents a 20/60/20 workspace whose middle band shows one,
|
comparison. GPUI now presents one, three, or five vertical panels, each with its
|
||||||
three, or five vertical work lanes while one terminal fixture is replaced with a
|
own 20/60/20 context/work/decision composition, while one terminal fixture is
|
||||||
real actor-owned VT session and five deterministic surfaces keep running. Counters
|
replaced with a real actor-owned VT session and five deterministic surfaces keep
|
||||||
|
running. Counters
|
||||||
separate external PTY batches/lines from total model updates. The GPUI footer
|
separate external PTY batches/lines from total model updates. The GPUI footer
|
||||||
reports dispatch-to-element-build p50/p95 over a bounded 256-sample window. It
|
reports dispatch-to-element-build p50/p95 over a bounded 256-sample window. It
|
||||||
is deliberately not called key-to-present or frame-present latency: neither
|
is deliberately not called key-to-present or frame-present latency: neither
|
||||||
|
|||||||
+21
-20
@@ -10,14 +10,14 @@ framework decision gate, not a decorative dashboard.
|
|||||||
## Current-run audit
|
## Current-run audit
|
||||||
|
|
||||||
The first 2026-08-31 GPUI baseline proved six-surface density but gave every
|
The first 2026-08-31 GPUI baseline proved six-surface density but gave every
|
||||||
pane equal visual priority. The current slice replaces that dashboard with three
|
pane equal visual priority. The current slice replaces that dashboard with one,
|
||||||
horizontal bands: 20% context and pane selection, a 60% responsive work region,
|
three, or five cmux-style vertical panels according to available width. Every
|
||||||
and a 20% decision shelf. The work region shows one, three, or five vertical
|
panel owns its own 20% context region, 60% work surface, and 20% decision shelf.
|
||||||
lanes according to available width. Keyboard focus, command-palette input, live
|
Keyboard focus, command-palette input, live PTY input, styled VT cells, cursor
|
||||||
PTY input, styled VT cells, cursor shapes, geometry-driven resize, and retained-
|
shapes, geometry-driven resize, and retained-history navigation are wired.
|
||||||
history navigation are wired. Remaining visible gaps are terminal text selection
|
Remaining visible gaps are terminal text selection and mouse modes, real
|
||||||
and mouse modes, real decision-shelf actions, usage-detail provenance, and end-to-
|
decision-shelf actions, usage-detail provenance, and end-to-end platform
|
||||||
end platform accessibility/IME.
|
accessibility/IME.
|
||||||
|
|
||||||
Screenshots for the audit are stored outside Git under
|
Screenshots for the audit are stored outside Git under
|
||||||
`~/shots/2026-08/lumbridge-ui-audit/`. Accessibility and IME correctness cannot
|
`~/shots/2026-08/lumbridge-ui-audit/`. Accessibility and IME correctness cannot
|
||||||
@@ -42,8 +42,8 @@ research, not source assets for Lumbridge. We adapt these interaction patterns:
|
|||||||
cards;
|
cards;
|
||||||
- a quiet top tab strip for mixed terminal, Markdown, browser, and review
|
- a quiet top tab strip for mixed terminal, Markdown, browser, and review
|
||||||
surfaces;
|
surfaces;
|
||||||
- a primary work region with a small responsive number of equal vertical lanes,
|
- a small responsive number of self-contained vertical panels, instead of
|
||||||
instead of either one ultrawide terminal or a dense equal-card dashboard;
|
either one ultrawide terminal or a dense equal-card dashboard;
|
||||||
- narrow contextual tools, such as files, review, or Buzz, that can collapse
|
- narrow contextual tools, such as files, review, or Buzz, that can collapse
|
||||||
when the terminal needs the space;
|
when the terminal needs the space;
|
||||||
- usage and agent state at the edge of the workspace rather than in modal
|
- usage and agent state at the edge of the workspace rather than in modal
|
||||||
@@ -60,20 +60,21 @@ surface rather than a hosted Lumbridge control plane.
|
|||||||
palette shortcut.
|
palette shortcut.
|
||||||
2. The sidebar starts with a compact `ATTENTION` group, followed by workspaces
|
2. The sidebar starts with a compact `ATTENTION` group, followed by workspaces
|
||||||
and remote hosts. It does not become a notification feed.
|
and remote hosts. It does not become a notification feed.
|
||||||
3. A compact top 20% context band keeps all pane identities and states visible;
|
3. The top 20% of every visible panel keeps that pane's identity, surfaces,
|
||||||
the selected pane receives the accent treatment.
|
execution target, context, goal, and state visible; the selected panel
|
||||||
4. A waiting pane uses an amber semantic label in both the context band and
|
receives the accent treatment.
|
||||||
attention sidebar. The label always includes words such as `NEEDS INPUT`.
|
4. A waiting pane uses an amber semantic label in both its panel context region
|
||||||
5. The middle 60% gives one, three, or five terminal/work lanes uninterrupted
|
and the attention sidebar. The label always includes words such as
|
||||||
height and retains target, harness state, dimensions, and surface tabs. The
|
`NEEDS INPUT`.
|
||||||
selected lane alone owns keyboard input.
|
5. The middle 60% of every panel gives its terminal or work surface uninterrupted
|
||||||
|
height. The selected panel alone owns keyboard input.
|
||||||
6. The footer groups connection state, selected-harness identity, usage-window
|
6. The footer groups connection state, selected-harness identity, usage-window
|
||||||
provenance, and burn forecast into readable regions.
|
provenance, and burn forecast into readable regions.
|
||||||
7. The selected pane has a quiet surface strip for Terminal, Browser, Tools,
|
7. The selected pane has a quiet surface strip for Terminal, Browser, Tools,
|
||||||
Context, Goal, and Review. Switching surfaces keeps one pane identity.
|
Context, Goal, and Review. Switching surfaces keeps one pane identity.
|
||||||
8. A compact decision shelf may show local-analyst choices below the work
|
8. Every panel has a compact decision shelf below its work surface. It labels
|
||||||
surface. It labels its model/provider, trace scope, and whether suggestions
|
whether suggestions are inert or review is required; no choice is visually
|
||||||
are inert; no choice is visually conflated with an approved action.
|
conflated with an approved action.
|
||||||
|
|
||||||
## Keyboard task flow
|
## Keyboard task flow
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
# 0008: The default workspace is a primary-terminal 3×1 composition
|
# 0008: Every workspace panel uses a terminal-first 3×1 composition
|
||||||
|
|
||||||
Status: accepted for the native vertical slice.
|
Status: accepted for the native vertical slice.
|
||||||
|
|
||||||
Lumbridge defaults to three horizontal bands in one column. The top 20% exposes
|
Every Lumbridge workspace panel defaults to three horizontal regions in one
|
||||||
pane, agent, tools, context, goal, target, and attention state. The middle 60%
|
column. The top 20% exposes that pane's agent, tools, context, goal, target, and
|
||||||
is the terminal-first work region. The bottom 20% holds answers, choices, chat,
|
attention state. The middle 60% is that pane's terminal-first work surface. The
|
||||||
approvals, and the local-analyst boundary.
|
bottom 20% is that pane's answer, choice, chat, approval, and local-analyst
|
||||||
|
boundary.
|
||||||
|
|
||||||
This composition favors the region where engineers type and read for most of the
|
This composition keeps the information needed to understand and steer a process
|
||||||
day. It keeps context and decisions visible without making every surface a small
|
attached to the same visual object as its terminal. The workspace may contain a
|
||||||
dashboard card. The middle region may contain a responsive number of vertical
|
responsive number of these panels under decision 0009; the selected panel alone
|
||||||
work lanes under decision 0009; the selected lane alone owns keyboard input.
|
owns keyboard input.
|
||||||
|
|
||||||
The framework spike retains six updating surfaces even when five are offscreen.
|
The framework spike retains six updating surfaces even when one is outside the
|
||||||
That preserves the earlier streaming and reducer workload for performance
|
five-panel ultrawide window. That preserves the earlier streaming and reducer
|
||||||
comparison. Hidden surfaces do not receive keyboard input merely because they
|
workload for performance comparison. Hidden panels do not receive keyboard input
|
||||||
continue to update.
|
merely because they continue to update.
|
||||||
|
|
||||||
Window and lane geometry determine each terminal's rows and columns. The UI
|
Window and panel geometry determine each terminal's rows and columns. The UI
|
||||||
resizes the terminal engine and PTY through the bounded runtime actor; it does
|
resizes the terminal engine and PTY through the bounded runtime actor; it does
|
||||||
not resize only the text view or infer terminal wrapping after the fact.
|
not resize only the text view or infer terminal wrapping after the fact.
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
# 0009: The primary work region uses responsive vertical lanes
|
# 0009: The workspace uses responsive self-contained vertical panels
|
||||||
|
|
||||||
Status: accepted for the native vertical slice.
|
Status: accepted for the native vertical slice.
|
||||||
|
|
||||||
The middle 60% work region shows one vertical lane in compact windows, three at
|
The workspace shows one vertical panel in compact windows, three at normal
|
||||||
normal desktop widths, and five when at least 2800 px remains after the sidebar.
|
desktop widths, and five when at least 2800 px remains after the sidebar. A
|
||||||
A 3440 px ultrawide therefore presents five lanes. A sliding window over the six
|
3440 px ultrawide therefore presents five panels. A sliding window over the six
|
||||||
comparison panes always keeps the selected pane visible.
|
comparison panes always keeps the selected pane visible.
|
||||||
|
|
||||||
This is not a return to the original 2×3 dashboard. Every lane receives the full
|
This is not a return to the original 2×3 dashboard and it is not one shared
|
||||||
work-region height, the surrounding context and decision bands remain stable,
|
20/60/20 frame sliced only through the middle. Every panel receives the full
|
||||||
and only the selected lane owns keyboard input. Clicking a lane selects it. Pane
|
workspace height and independently contains its own 20% context region, 60%
|
||||||
processes and output continue independently of selection.
|
terminal/work surface, and 20% decision shelf. Only the selected panel owns
|
||||||
|
keyboard input. Clicking a panel selects it. Pane processes and output continue
|
||||||
|
independently of selection.
|
||||||
|
|
||||||
Terminal geometry is computed from the actual width of one lane, including gaps
|
Terminal geometry is computed from the width of one panel and the height of its
|
||||||
and insets. The selected real PTY is resized through the runtime actor when the
|
middle 60% region, including gaps and content insets. The selected real PTY is
|
||||||
responsive lane count or window bounds change. On the 3440×1440 reference
|
resized through the runtime actor when the responsive panel count or window
|
||||||
display the current fixed-cell spike derives approximately 71 columns by 40
|
bounds change. On the 3440×1440 reference display the current fixed-cell spike
|
||||||
rows per lane.
|
derives approximately 71 columns by 42 rows per panel.
|
||||||
|
|
||||||
The first actor slice still owns one real PTY and five deterministic comparison
|
The first actor slice still owns one real PTY and five deterministic comparison
|
||||||
surfaces. Promoting the runtime boundary from one actor to a pane-indexed session
|
surfaces. Promoting the runtime boundary from one actor to a pane-indexed session
|
||||||
registry is required before all five visible terminal lanes can own independent
|
registry is required before all five visible terminal panels can own independent
|
||||||
real processes.
|
real processes.
|
||||||
|
|||||||
+3
-2
@@ -24,8 +24,9 @@ shared model retain the all-deterministic mode for like-for-like framework
|
|||||||
comparison. GPUI feeds raw output through `lumbridge-terminal` and sends encoded
|
comparison. GPUI feeds raw output through `lumbridge-terminal` and sends encoded
|
||||||
keyboard input and terminal protocol replies through the bounded runtime actor.
|
keyboard input and terminal protocol replies through the bounded runtime actor.
|
||||||
Its visual adapter coalesces VT cells into native styled runs, paints cursor
|
Its visual adapter coalesces VT cells into native styled runs, paints cursor
|
||||||
shapes, and resizes the engine and PTY from a responsive one/three/five-lane
|
shapes, and resizes the engine and PTY from the middle 60% of a responsive
|
||||||
middle 60% work band. Retained-history navigation is wired; text selection and
|
one/three/five-panel workspace. Every panel owns separate context and decision
|
||||||
|
regions. Retained-history navigation is wired; text selection and
|
||||||
mouse input remain intentionally unfinished.
|
mouse input remain intentionally unfinished.
|
||||||
|
|
||||||
Build independently:
|
Build independently:
|
||||||
|
|||||||
+215
-275
@@ -39,11 +39,11 @@ const SIDEBAR_WIDTH: f32 = 248.0;
|
|||||||
const APP_HEADER_HEIGHT: f32 = 48.0;
|
const APP_HEADER_HEIGHT: f32 = 48.0;
|
||||||
const TAB_BAR_HEIGHT: f32 = 38.0;
|
const TAB_BAR_HEIGHT: f32 = 38.0;
|
||||||
const APP_FOOTER_HEIGHT: f32 = 32.0;
|
const APP_FOOTER_HEIGHT: f32 = 32.0;
|
||||||
const TERMINAL_CHROME_HEIGHT: f32 = 72.0;
|
const TERMINAL_CONTENT_VERTICAL_INSET: f32 = 24.0;
|
||||||
const TERMINAL_HORIZONTAL_INSET: f32 = 32.0;
|
const TERMINAL_HORIZONTAL_INSET: f32 = 32.0;
|
||||||
const TERMINAL_CELL_WIDTH: f32 = 8.4;
|
const TERMINAL_CELL_WIDTH: f32 = 8.4;
|
||||||
const TERMINAL_CELL_HEIGHT: f32 = 18.0;
|
const TERMINAL_CELL_HEIGHT: f32 = 18.0;
|
||||||
const WORK_LANE_GAP: f32 = 4.0;
|
const WORK_PANEL_GAP: f32 = 4.0;
|
||||||
const TERMINAL_ROW_STEP: u16 = 2;
|
const TERMINAL_ROW_STEP: u16 = 2;
|
||||||
const TERMINAL_COLUMN_STEP: u16 = 10;
|
const TERMINAL_COLUMN_STEP: u16 = 10;
|
||||||
|
|
||||||
@@ -78,7 +78,6 @@ struct LumbridgeShell {
|
|||||||
terminal_snapshot: TerminalSnapshot,
|
terminal_snapshot: TerminalSnapshot,
|
||||||
last_runtime_sequence: u64,
|
last_runtime_sequence: u64,
|
||||||
root_focus: FocusHandle,
|
root_focus: FocusHandle,
|
||||||
pane_focus: [FocusHandle; 6],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
@@ -119,10 +118,6 @@ impl LiveRuntimeStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn is_fault(&self) -> bool {
|
|
||||||
matches!(self, Self::Fault(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn is_terminal(&self) -> bool {
|
const fn is_terminal(&self) -> bool {
|
||||||
matches!(self, Self::Exited(_) | Self::Fault(_))
|
matches!(self, Self::Exited(_) | Self::Fault(_))
|
||||||
}
|
}
|
||||||
@@ -280,14 +275,15 @@ fn dim_color(color: u32) -> u32 {
|
|||||||
fn terminal_dimensions_for_window(window_size: Size<Pixels>) -> TerminalDimensions {
|
fn terminal_dimensions_for_window(window_size: Size<Pixels>) -> TerminalDimensions {
|
||||||
let width = f32::from(window_size.width);
|
let width = f32::from(window_size.width);
|
||||||
let height = f32::from(window_size.height);
|
let height = f32::from(window_size.height);
|
||||||
let lane_count = visible_lane_count(window_size);
|
let panel_count = visible_panel_count(window_size);
|
||||||
let workspace_height =
|
let workspace_height =
|
||||||
(height - APP_HEADER_HEIGHT - TAB_BAR_HEIGHT - APP_FOOTER_HEIGHT).max(0.0);
|
(height - APP_HEADER_HEIGHT - TAB_BAR_HEIGHT - APP_FOOTER_HEIGHT).max(0.0);
|
||||||
let terminal_height = (workspace_height * 0.60 - TERMINAL_CHROME_HEIGHT).max(0.0);
|
let terminal_height =
|
||||||
|
(workspace_height * 0.60 - TERMINAL_CONTENT_VERTICAL_INSET).max(0.0);
|
||||||
let workspace_width = (width - SIDEBAR_WIDTH).max(0.0);
|
let workspace_width = (width - SIDEBAR_WIDTH).max(0.0);
|
||||||
let lane_gaps = WORK_LANE_GAP * lane_count.saturating_sub(1) as f32;
|
let panel_gaps = WORK_PANEL_GAP * panel_count.saturating_sub(1) as f32;
|
||||||
let lane_width = ((workspace_width - lane_gaps).max(0.0) / lane_count as f32).max(0.0);
|
let panel_width = ((workspace_width - panel_gaps).max(0.0) / panel_count as f32).max(0.0);
|
||||||
let terminal_width = (lane_width - TERMINAL_HORIZONTAL_INSET).max(0.0);
|
let terminal_width = (panel_width - TERMINAL_HORIZONTAL_INSET).max(0.0);
|
||||||
let rows = (terminal_height / TERMINAL_CELL_HEIGHT)
|
let rows = (terminal_height / TERMINAL_CELL_HEIGHT)
|
||||||
.floor()
|
.floor()
|
||||||
.clamp(2.0, f32::from(u16::MAX));
|
.clamp(2.0, f32::from(u16::MAX));
|
||||||
@@ -303,7 +299,7 @@ fn terminal_dimensions_for_window(window_size: Size<Pixels>) -> TerminalDimensio
|
|||||||
.expect("geometry clamps terminal dimensions above zero")
|
.expect("geometry clamps terminal dimensions above zero")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visible_lane_count(window_size: Size<Pixels>) -> usize {
|
fn visible_panel_count(window_size: Size<Pixels>) -> usize {
|
||||||
let workspace_width = (f32::from(window_size.width) - SIDEBAR_WIDTH).max(0.0);
|
let workspace_width = (f32::from(window_size.width) - SIDEBAR_WIDTH).max(0.0);
|
||||||
if workspace_width >= 2_800.0 {
|
if workspace_width >= 2_800.0 {
|
||||||
5
|
5
|
||||||
@@ -314,8 +310,12 @@ fn visible_lane_count(window_size: Size<Pixels>) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visible_lane_range(total: usize, selected: usize, lane_count: usize) -> std::ops::Range<usize> {
|
fn visible_panel_range(
|
||||||
let count = lane_count.clamp(1, total.max(1)).min(total);
|
total: usize,
|
||||||
|
selected: usize,
|
||||||
|
panel_count: usize,
|
||||||
|
) -> std::ops::Range<usize> {
|
||||||
|
let count = panel_count.clamp(1, total.max(1)).min(total);
|
||||||
let start = selected
|
let start = selected
|
||||||
.saturating_sub(count / 2)
|
.saturating_sub(count / 2)
|
||||||
.min(total.saturating_sub(count));
|
.min(total.saturating_sub(count));
|
||||||
@@ -363,11 +363,6 @@ impl RenderTiming {
|
|||||||
|
|
||||||
impl LumbridgeShell {
|
impl LumbridgeShell {
|
||||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let pane_focus = std::array::from_fn(|index| {
|
|
||||||
cx.focus_handle()
|
|
||||||
.tab_index(index as isize + 1)
|
|
||||||
.tab_stop(true)
|
|
||||||
});
|
|
||||||
let root_focus = cx.focus_handle();
|
let root_focus = cx.focus_handle();
|
||||||
window.focus(&root_focus);
|
window.focus(&root_focus);
|
||||||
|
|
||||||
@@ -433,7 +428,6 @@ impl LumbridgeShell {
|
|||||||
terminal_snapshot,
|
terminal_snapshot,
|
||||||
last_runtime_sequence: 0,
|
last_runtime_sequence: 0,
|
||||||
root_focus,
|
root_focus,
|
||||||
pane_focus,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,83 +701,6 @@ impl LumbridgeShell {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pane_selector(
|
|
||||||
pane: &PaneState,
|
|
||||||
selected: bool,
|
|
||||||
runtime_status: LiveRuntimeStatus,
|
|
||||||
focus: FocusHandle,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> gpui::AnyElement {
|
|
||||||
let id = pane.id();
|
|
||||||
let needs_input = pane.needs_input();
|
|
||||||
let external = pane.output_source() == OutputSource::External;
|
|
||||||
let label = if external {
|
|
||||||
runtime_status.badge()
|
|
||||||
} else {
|
|
||||||
match pane.kind() {
|
|
||||||
SurfaceKind::Terminal => pane.status().label(),
|
|
||||||
_ => pane.fixture().badge,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let state_color = if needs_input || (external && runtime_status.is_fault()) {
|
|
||||||
ATTENTION
|
|
||||||
} else if matches!(pane.kind(), SurfaceKind::Terminal) {
|
|
||||||
SUCCESS
|
|
||||||
} else {
|
|
||||||
ACCENT
|
|
||||||
};
|
|
||||||
|
|
||||||
div()
|
|
||||||
.id(("pane", id.index()))
|
|
||||||
.track_focus(&focus)
|
|
||||||
.tab_index(id.index() as isize + 1)
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.gap_3()
|
|
||||||
.h_full()
|
|
||||||
.min_w(px(172.0))
|
|
||||||
.px_3()
|
|
||||||
.bg(rgb(if selected { PANEL_ACTIVE } else { PANEL_ALT }))
|
|
||||||
.border_1()
|
|
||||||
.border_color(rgb(if selected || needs_input {
|
|
||||||
state_color
|
|
||||||
} else {
|
|
||||||
BORDER_QUIET
|
|
||||||
}))
|
|
||||||
.rounded(px(5.0))
|
|
||||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
|
||||||
shell.select_pane(id, window, cx);
|
|
||||||
}))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.min_w_0()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.truncate()
|
|
||||||
.text_sm()
|
|
||||||
.text_color(rgb(TEXT))
|
|
||||||
.child(if external {
|
|
||||||
"Terminal · local shell"
|
|
||||||
} else {
|
|
||||||
pane.fixture().title
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.mt_1()
|
|
||||||
.truncate()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(rgb(MUTED))
|
|
||||||
.child(pane.fixture().target.to_owned()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(div().text_xs().text_color(rgb(state_color)).child(label))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn terminal_run(run: TerminalPaintRun) -> gpui::AnyElement {
|
fn terminal_run(run: TerminalPaintRun) -> gpui::AnyElement {
|
||||||
let cursor = run.cursor;
|
let cursor = run.cursor;
|
||||||
div()
|
div()
|
||||||
@@ -851,13 +768,7 @@ impl LumbridgeShell {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn work_surface(
|
fn pane_context(&self, pane: &PaneState, selected: bool) -> gpui::AnyElement {
|
||||||
&self,
|
|
||||||
pane: &PaneState,
|
|
||||||
selected: bool,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> gpui::AnyElement {
|
|
||||||
let pane_id = pane.id();
|
|
||||||
let external = pane.output_source() == OutputSource::External;
|
let external = pane.output_source() == OutputSource::External;
|
||||||
let status = if external {
|
let status = if external {
|
||||||
self.runtime_status.badge()
|
self.runtime_status.badge()
|
||||||
@@ -909,6 +820,90 @@ impl LumbridgeShell {
|
|||||||
.child(label)
|
.child(label)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.size_full()
|
||||||
|
.min_h_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.px_3()
|
||||||
|
.py_2()
|
||||||
|
.bg(rgb(if selected { PANEL_ACTIVE } else { PANEL_ALT }))
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(rgb(BORDER))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_between()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.min_w_0()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.truncate()
|
||||||
|
.text_sm()
|
||||||
|
.text_color(rgb(TEXT))
|
||||||
|
.child(pane.fixture().title),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.mt_1()
|
||||||
|
.truncate()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(rgb(MUTED))
|
||||||
|
.child(detail),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_none()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(rgb(if pane.needs_input() {
|
||||||
|
ATTENTION
|
||||||
|
} else if selected {
|
||||||
|
SUCCESS
|
||||||
|
} else {
|
||||||
|
MUTED
|
||||||
|
}))
|
||||||
|
.child(surface_status),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.h(px(28.0))
|
||||||
|
.flex_none()
|
||||||
|
.mt_2()
|
||||||
|
.overflow_hidden()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(rgb(BORDER_QUIET))
|
||||||
|
.children(tabs),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_between()
|
||||||
|
.mt_2()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(rgb(MUTED))
|
||||||
|
.child("TOOLS · CONTEXT · GOAL")
|
||||||
|
.child(if pane.needs_input() {
|
||||||
|
"NEEDS INPUT"
|
||||||
|
} else if selected {
|
||||||
|
"KEYBOARD OWNER"
|
||||||
|
} else {
|
||||||
|
"RUNNING"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pane_work_surface(&self, pane: &PaneState) -> gpui::AnyElement {
|
||||||
|
let external = pane.output_source() == OutputSource::External;
|
||||||
let content = if external {
|
let content = if external {
|
||||||
self.terminal_view()
|
self.terminal_view()
|
||||||
} else {
|
} else {
|
||||||
@@ -927,63 +922,15 @@ impl LumbridgeShell {
|
|||||||
};
|
};
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.id(("work-lane", pane_id.index()))
|
|
||||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
|
||||||
shell.select_pane(pane_id, window, cx);
|
|
||||||
}))
|
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
.min_w_0()
|
.min_w_0()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.bg(rgb(PANEL))
|
.bg(rgb(BG))
|
||||||
.border_1()
|
.border_y_1()
|
||||||
.border_color(rgb(if selected { ACCENT } else { BORDER }))
|
.border_color(rgb(BORDER))
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.h(px(36.0))
|
|
||||||
.flex_none()
|
|
||||||
.px_3()
|
|
||||||
.bg(rgb(PANEL_ALT))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.min_w_0()
|
|
||||||
.text_sm()
|
|
||||||
.child(div().flex_none().child(pane.fixture().title))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.truncate()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(rgb(MUTED))
|
|
||||||
.child(detail),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex_none()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(rgb(if selected { SUCCESS } else { MUTED }))
|
|
||||||
.child(surface_status),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.h(px(28.0))
|
|
||||||
.flex_none()
|
|
||||||
.px_1()
|
|
||||||
.bg(rgb(PANEL_ALT))
|
|
||||||
.border_t_1()
|
|
||||||
.border_color(rgb(BORDER_QUIET))
|
|
||||||
.children(tabs),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
@@ -995,22 +942,36 @@ impl LumbridgeShell {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decision_region(&self) -> gpui::AnyElement {
|
fn pane_decision_region(&self, pane: &PaneState) -> gpui::AnyElement {
|
||||||
let choice = |label: &'static str, detail: &'static str| {
|
let choice = |label: &'static str, detail: &'static str, attention: bool| {
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.items_center()
|
||||||
.min_w(px(200.0))
|
.justify_between()
|
||||||
.px_3()
|
.min_w_0()
|
||||||
.py_2()
|
.px_2()
|
||||||
.rounded(px(5.0))
|
.py_1()
|
||||||
|
.rounded(px(4.0))
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(rgb(BORDER))
|
.border_color(rgb(if attention { ATTENTION } else { BORDER }))
|
||||||
.bg(rgb(PANEL_ALT))
|
.bg(rgb(PANEL_ALT))
|
||||||
.text_sm()
|
.text_xs()
|
||||||
.text_color(rgb(TEXT))
|
.text_color(rgb(TEXT))
|
||||||
.child(label)
|
.child(label)
|
||||||
.child(div().mt_1().text_xs().text_color(rgb(MUTED)).child(detail))
|
.child(div().truncate().text_color(rgb(MUTED)).child(detail))
|
||||||
|
};
|
||||||
|
let choices = if pane.needs_input() {
|
||||||
|
[
|
||||||
|
("Review request", "inspect scope", true),
|
||||||
|
("Steer…", "edit response", false),
|
||||||
|
("Dismiss", "leave inert", false),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
("Continue", "keep moving", false),
|
||||||
|
("Review plan", "inspect commands", false),
|
||||||
|
("Ask…", "refine prompt", false),
|
||||||
|
]
|
||||||
};
|
};
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -1018,9 +979,11 @@ impl LumbridgeShell {
|
|||||||
.size_full()
|
.size_full()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.px_3()
|
.px_2()
|
||||||
.py_2()
|
.py_2()
|
||||||
.bg(rgb(PANEL))
|
.bg(rgb(PANEL))
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(rgb(BORDER))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -1030,23 +993,78 @@ impl LumbridgeShell {
|
|||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.text_color(rgb(MUTED))
|
.text_color(rgb(MUTED))
|
||||||
.child("DECISION SHELF · answer, choices, approvals"),
|
.child("DECISION SHELF"),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.text_color(rgb(ATTENTION))
|
.text_color(rgb(if pane.needs_input() {
|
||||||
.child("LOCAL ANALYST OFF · suggestions inert"),
|
ATTENTION
|
||||||
|
} else {
|
||||||
|
MUTED
|
||||||
|
}))
|
||||||
|
.child(if pane.needs_input() {
|
||||||
|
"REVIEW REQUIRED"
|
||||||
|
} else {
|
||||||
|
"SUGGESTIONS INERT"
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.flex_col()
|
||||||
.gap_2()
|
.gap_1()
|
||||||
.mt_2()
|
.mt_2()
|
||||||
.child(choice("Continue", "Keep the selected agent moving"))
|
.children(choices.map(|(label, detail, attention)| {
|
||||||
.child(choice("Review plan", "Inspect capability-scoped commands"))
|
choice(label, detail, attention)
|
||||||
.child(choice("Ask…", "Refine before any action")),
|
})),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspace_panel(
|
||||||
|
&self,
|
||||||
|
pane: &PaneState,
|
||||||
|
selected: bool,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> gpui::AnyElement {
|
||||||
|
let pane_id = pane.id();
|
||||||
|
div()
|
||||||
|
.id(("workspace-panel", pane_id.index()))
|
||||||
|
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||||
|
shell.select_pane(pane_id, window, cx);
|
||||||
|
}))
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.size_full()
|
||||||
|
.min_w_0()
|
||||||
|
.min_h_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.border_1()
|
||||||
|
.border_color(rgb(if selected { ACCENT } else { BORDER }))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.h(relative(0.20))
|
||||||
|
.flex_none()
|
||||||
|
.min_h_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(self.pane_context(pane, selected)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.h(relative(0.60))
|
||||||
|
.flex_none()
|
||||||
|
.min_h_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(self.pane_work_surface(pane)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.h(relative(0.20))
|
||||||
|
.flex_none()
|
||||||
|
.min_h_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(self.pane_decision_region(pane)),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
@@ -1140,6 +1158,7 @@ impl LumbridgeShell {
|
|||||||
impl Render for LumbridgeShell {
|
impl Render for LumbridgeShell {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let attention = self.model.pane(PaneId::ClaudeUi);
|
let attention = self.model.pane(PaneId::ClaudeUi);
|
||||||
|
let panel_count = visible_panel_count(window.bounds().size);
|
||||||
let sidebar = div()
|
let sidebar = div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
@@ -1303,117 +1322,38 @@ impl Render for LumbridgeShell {
|
|||||||
.px_3()
|
.px_3()
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.text_color(rgb(MUTED))
|
.text_color(rgb(MUTED))
|
||||||
.child("1 interactive VT · 5 deterministic · 1 waiting"),
|
.child(format!(
|
||||||
|
"{panel_count} visible panels · 1 interactive VT · 1 waiting"
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let pane_selectors = self
|
let panel_range = visible_panel_range(
|
||||||
.model
|
|
||||||
.panes()
|
|
||||||
.iter()
|
|
||||||
.map(|pane| {
|
|
||||||
Self::pane_selector(
|
|
||||||
pane,
|
|
||||||
self.model.selected_pane() == pane.id(),
|
|
||||||
self.runtime_status.clone(),
|
|
||||||
self.pane_focus[pane.id().index()].clone(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let selected = self.model.pane(self.model.selected_pane());
|
|
||||||
let lane_count = visible_lane_count(window.bounds().size);
|
|
||||||
let lane_range = visible_lane_range(
|
|
||||||
PaneId::ALL.len(),
|
PaneId::ALL.len(),
|
||||||
self.model.selected_pane().index(),
|
self.model.selected_pane().index(),
|
||||||
lane_count,
|
panel_count,
|
||||||
);
|
);
|
||||||
let work_lanes = lane_range
|
let workspace_panels = panel_range
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let pane = self.model.pane(PaneId::ALL[index]);
|
let pane = self.model.pane(PaneId::ALL[index]);
|
||||||
div()
|
div()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_w_0()
|
.min_w_0()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.child(self.work_surface(
|
.child(self.workspace_panel(
|
||||||
pane,
|
pane,
|
||||||
pane.id() == self.model.selected_pane(),
|
pane.id() == self.model.selected_pane(),
|
||||||
cx,
|
cx,
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let workspace_stack = div()
|
let workspace_row = div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.gap_1()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_w_0()
|
.min_w_0()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(
|
.children(workspace_panels);
|
||||||
div()
|
|
||||||
.h(relative(0.20))
|
|
||||||
.flex_none()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.min_h_0()
|
|
||||||
.overflow_hidden()
|
|
||||||
.px_3()
|
|
||||||
.py_2()
|
|
||||||
.bg(rgb(PANEL))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.text_xs()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_color(rgb(MUTED))
|
|
||||||
.child("CONTEXT · panes, agents, tools, goal"),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_color(rgb(if selected.needs_input() {
|
|
||||||
ATTENTION
|
|
||||||
} else {
|
|
||||||
SUCCESS
|
|
||||||
}))
|
|
||||||
.child(if selected.needs_input() {
|
|
||||||
"NEEDS INPUT · decision below"
|
|
||||||
} else {
|
|
||||||
"WORKSPACE HEALTHY"
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.mt_2()
|
|
||||||
.flex_1()
|
|
||||||
.min_h_0()
|
|
||||||
.overflow_hidden()
|
|
||||||
.children(pane_selectors),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.h(relative(0.60))
|
|
||||||
.flex_none()
|
|
||||||
.flex()
|
|
||||||
.gap_1()
|
|
||||||
.min_h_0()
|
|
||||||
.overflow_hidden()
|
|
||||||
.children(work_lanes),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.h(relative(0.20))
|
|
||||||
.flex_none()
|
|
||||||
.min_h_0()
|
|
||||||
.overflow_hidden()
|
|
||||||
.child(self.decision_region()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let counters = self.model.counters();
|
let counters = self.model.counters();
|
||||||
let footer_left = format!(
|
let footer_left = format!(
|
||||||
@@ -1511,7 +1451,7 @@ impl Render for LumbridgeShell {
|
|||||||
.min_h_0()
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(tabs)
|
.child(tabs)
|
||||||
.child(workspace_stack),
|
.child(workspace_row),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@@ -1658,7 +1598,7 @@ mod tests {
|
|||||||
use super::{
|
use super::{
|
||||||
KeyModifiers, TerminalEngine, TerminalEngineOptions, TerminalKey, TerminalScroll,
|
KeyModifiers, TerminalEngine, TerminalEngineOptions, TerminalKey, TerminalScroll,
|
||||||
indexed_terminal_color, terminal_dimensions_for_window, terminal_key_from_parts,
|
indexed_terminal_color, terminal_dimensions_for_window, terminal_key_from_parts,
|
||||||
terminal_paint_rows, terminal_scroll_from_parts, visible_lane_count, visible_lane_range,
|
terminal_paint_rows, terminal_scroll_from_parts, visible_panel_count, visible_panel_range,
|
||||||
};
|
};
|
||||||
use gpui::{px, size};
|
use gpui::{px, size};
|
||||||
|
|
||||||
@@ -1685,24 +1625,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn terminal_geometry_tracks_the_middle_sixty_percent() {
|
fn terminal_geometry_tracks_middle_sixty_percent_per_panel() {
|
||||||
let dimensions = terminal_dimensions_for_window(size(px(1500.0), px(960.0)));
|
let dimensions = terminal_dimensions_for_window(size(px(1500.0), px(960.0)));
|
||||||
assert_eq!(dimensions.rows(), 24);
|
assert_eq!(dimensions.rows(), 26);
|
||||||
assert_eq!(dimensions.columns(), 45);
|
assert_eq!(dimensions.columns(), 45);
|
||||||
|
|
||||||
let ultrawide = size(px(3440.0), px(1440.0));
|
let ultrawide = size(px(3440.0), px(1440.0));
|
||||||
assert_eq!(visible_lane_count(ultrawide), 5);
|
assert_eq!(visible_panel_count(ultrawide), 5);
|
||||||
let dimensions = terminal_dimensions_for_window(ultrawide);
|
let dimensions = terminal_dimensions_for_window(ultrawide);
|
||||||
assert_eq!(dimensions.rows(), 40);
|
assert_eq!(dimensions.rows(), 42);
|
||||||
assert_eq!(dimensions.columns(), 71);
|
assert_eq!(dimensions.columns(), 71);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lane_window_keeps_the_selected_pane_visible() {
|
fn panel_window_keeps_the_selected_pane_visible() {
|
||||||
assert_eq!(visible_lane_range(6, 0, 5), 0..5);
|
assert_eq!(visible_panel_range(6, 0, 5), 0..5);
|
||||||
assert_eq!(visible_lane_range(6, 5, 5), 1..6);
|
assert_eq!(visible_panel_range(6, 5, 5), 1..6);
|
||||||
assert_eq!(visible_lane_range(6, 3, 3), 2..5);
|
assert_eq!(visible_panel_range(6, 3, 3), 2..5);
|
||||||
assert_eq!(visible_lane_range(6, 4, 1), 4..5);
|
assert_eq!(visible_panel_range(6, 4, 1), 4..5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user