mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-08 17:31:56 -10:00
shell-redesign V1 attempt-3 Phase 3 BOUNCE-FIX follow-on: floating toggle + vellum rail + lighter icons
Founder direction 2026-05-06 — three refinements:
1. Collapsed Explorer: floating toggle, no whitespace column
Previous: collapsed Explorer rendered as a 12px aside with bg-background;
the whitespace column read as empty UI clutter.
Fix: when collapsed, render ONLY a floating chevron-right button
positioned fixed at left=3.5rem (just past the rail edge) +
top=5.5rem. No aside container, no background column. Tabs and
canvas reflow to fill (anchorLeft drops to 3rem = rail-only).
data-explorer-collapsed='true' attribute on the floating button for
runtime probing.
2. Rail with vellum surface
Previous: rail used `bg-background` (solid paper-cream).
Fix: rail now uses .vellum class (rgba(255,255,255,0.4) +
backdrop-blur(8px)) — same translucent treatment as the
topology-graph Explorer overlay. Paper-grid dots visible through
the rail surface. Reads as a paper sheet layered over the canvas
per universal-shell.md L48 sheets-of-vellum aesthetic.
3. Icons lighter line weight
Previous: lucide icons rendered at default strokeWidth=2 (heavy
line). Read as too dense against the vellum-translucent rail.
Fix: strokeWidth=1.25 on all rail destination + chat icons + the
Explorer toggle chevrons. Closer to 1px architectural drafting
feel matching the 1px ghost border doctrine.
- Widened RailIconSpec.icon ComponentType to accept strokeWidth.
- Lighter hover bg (stone-200/60 instead of stone-200) so hover
reads gentler against vellum.
Visual verification at /topology 1440x900:
- Rail: vellum-translucent; paper-grid dots show through; icons lighter.
- Explorer expanded: paper-grid dots show through; vellum overlay light.
- Click chevron-left: Explorer collapses; ONLY a floating chevron-right
button visible at rail edge; no aside column; tabs reflow to anchor=3rem;
canvas extends fully.
- Click floating chevron: Explorer re-expands.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
ed08ff2376
commit
bbe6250b97
2 files changed
+59
-37
No files matched your search
@@ -116,7 +116,8 @@ interface RailIconSpec {
|
||||
id: string;
|
||||
label: string;
|
||||
to: string;
|
||||
icon: ComponentType<{ className?: string }>;
|
||||
// lucide-react icons accept SVG props (strokeWidth, color, size, etc).
|
||||
icon: ComponentType<{ className?: string; strokeWidth?: number | string }>;
|
||||
/** Path prefix used for active-state matching. */
|
||||
activeWhen: (pathname: string) => boolean;
|
||||
testId: string;
|
||||
@@ -247,10 +248,13 @@ function Rail({
|
||||
"focus-visible:outline focus-visible:outline-2 focus-visible:outline-stone-900 focus-visible:outline-offset-2",
|
||||
active
|
||||
? "bg-stone-900 text-stone-50"
|
||||
: "text-stone-700 hover:bg-stone-200 hover:text-stone-900",
|
||||
: "text-stone-700 hover:bg-stone-200/60 hover:text-stone-900",
|
||||
)}
|
||||
>
|
||||
<Icon className="h-5 w-5" />
|
||||
{/* Lighter icon line weight (founder direction 2026-05-06):
|
||||
stroke-width 1.25 (default lucide is 2) for an architectural
|
||||
drafting feel that matches the 1px ghost border doctrine. */}
|
||||
<Icon className="h-5 w-5" strokeWidth={1.25} />
|
||||
{active && (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
@@ -268,7 +272,11 @@ function Rail({
|
||||
className={cn(
|
||||
// V1 border weight doctrine (universal-shell.md L39–L48):
|
||||
// 1px outline-variant ghost line for inter-region edges.
|
||||
"bg-background border-outline-variant flex shrink-0",
|
||||
// Vellum surface (founder direction 2026-05-06): same translucent
|
||||
// treatment as the topology-graph Explorer overlay, so the rail
|
||||
// reads as a paper sheet layered over the canvas (sheets-of-vellum
|
||||
// aesthetic per universal-shell.md L48).
|
||||
"vellum border-outline-variant flex shrink-0",
|
||||
vertical
|
||||
? "w-12 flex-col items-center border-r py-2 gap-1"
|
||||
: "w-full flex-row items-center border-b px-2 gap-1 overflow-x-auto",
|
||||
@@ -395,13 +403,12 @@ function AppShellInner({ children }: AppShellProps) {
|
||||
// - overlay: padding = 0 (content extends behind translucent explorer);
|
||||
// tab bar is sticky/positioned at left=anchor independently.
|
||||
// 21rem = rail (3rem) + explorer (18rem).
|
||||
// 21rem (rail 3 + explorer 18) when explorer fully open; 6rem
|
||||
// (rail 3 + collapsed-explorer-stub 3) when explorer collapsed; 3rem
|
||||
// (rail only) when no explorer for the destination.
|
||||
const explorerAnchorLeft = isWideLayout && explorerVisible
|
||||
? desktopExplorerOpen
|
||||
? "21rem"
|
||||
: "6rem"
|
||||
// 21rem (rail 3 + explorer 18) when explorer fully open. When
|
||||
// collapsed: 3rem (rail only) — the floating chevron toggle floats
|
||||
// over the canvas and doesn't claim layout space. When no explorer
|
||||
// for the destination: 3rem (rail only).
|
||||
const explorerAnchorLeft = isWideLayout && explorerVisible && desktopExplorerOpen
|
||||
? "21rem"
|
||||
: "3rem";
|
||||
const workspaceLeftOffset = isWideLayout
|
||||
? isTopologyOverlay
|
||||
|
||||
@@ -587,12 +587,37 @@ export function Explorer({
|
||||
// vellum-heavy (70%) was too dense.
|
||||
const isOverlay = overlayMode === "overlay";
|
||||
const isCollapsed = desktopMode === "hidden";
|
||||
|
||||
// When collapsed at desktop: render ONLY a floating toggle button
|
||||
// at left=rail-edge (no aside container behind it). The Explorer
|
||||
// surface tree is unmounted; the canvas + tabs reflow to fill the
|
||||
// freed width. Founder direction 2026-05-06.
|
||||
if (isCollapsed) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
data-testid="explorer-edge-toggle"
|
||||
data-explorer-collapsed="true"
|
||||
aria-label="Expand explorer"
|
||||
onClick={onDesktopToggle}
|
||||
className={cn(
|
||||
"hidden lg:flex fixed top-[5.5rem] left-[3.5rem] z-30 h-8 w-8 items-center justify-center",
|
||||
"rounded-full border border-outline-variant bg-background/90 text-stone-700",
|
||||
"shadow-[0_2px_8px_rgba(41,37,36,0.08)] backdrop-blur-sm transition-colors",
|
||||
"hover:bg-stone-100 hover:text-stone-900",
|
||||
)}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" strokeWidth={1.5} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<aside
|
||||
data-testid="explorer"
|
||||
data-surface={surface}
|
||||
data-explorer-mode={overlayMode}
|
||||
data-explorer-collapsed={isCollapsed}
|
||||
data-explorer-collapsed="false"
|
||||
className={cn(
|
||||
// V1 border weight doctrine (universal-shell.md L39–L48):
|
||||
// 1px outline-variant ghost line for inter-region edges.
|
||||
@@ -606,44 +631,34 @@ export function Explorer({
|
||||
open ? "translate-x-0" : "-translate-x-full",
|
||||
// Desktop (>=lg): persistent column at 280px (lg:w-72) per
|
||||
// universal-shell.md L34. Positioned absolutely after the 48px
|
||||
// rail. When collapsed: thin 12px column with just the
|
||||
// chevron-right re-open toggle visible (founder direction
|
||||
// 2026-05-06 — no way to expand otherwise without page refresh).
|
||||
desktopMode === "full" && "lg:absolute lg:top-0 lg:bottom-0 lg:left-12 lg:w-72 lg:max-w-none lg:translate-x-0",
|
||||
desktopMode === "hidden" && "lg:absolute lg:top-0 lg:bottom-0 lg:left-12 lg:w-12 lg:max-w-none lg:translate-x-0 bg-background",
|
||||
// rail.
|
||||
"lg:absolute lg:top-0 lg:bottom-0 lg:left-12 lg:w-72 lg:max-w-none lg:translate-x-0",
|
||||
)}
|
||||
>
|
||||
<div className="relative flex h-full w-full flex-col">
|
||||
{/* Collapse / expand toggle — always reachable. When the
|
||||
explorer is collapsed (desktopMode='hidden'), the entire
|
||||
aside shrinks to a 12px column showing only this toggle so
|
||||
the user can re-expand without refreshing. Founder direction
|
||||
2026-05-06. */}
|
||||
<button
|
||||
type="button"
|
||||
data-testid="explorer-edge-toggle"
|
||||
aria-label={isCollapsed ? "Expand explorer" : "Collapse explorer"}
|
||||
aria-label="Collapse explorer"
|
||||
onClick={onDesktopToggle}
|
||||
className={cn(
|
||||
"hidden lg:flex absolute z-10 h-8 w-8 items-center justify-center rounded-full border border-stone-300 bg-background/90 text-stone-700",
|
||||
"hidden lg:flex absolute z-10 h-8 w-8 items-center justify-center rounded-full border border-outline-variant bg-background/90 text-stone-700",
|
||||
"shadow-[0_2px_8px_rgba(41,37,36,0.08)] backdrop-blur-sm transition-colors hover:bg-stone-100 hover:text-stone-900",
|
||||
isCollapsed ? "left-1/2 top-3 -translate-x-1/2" : "right-2 top-3",
|
||||
"right-2 top-3",
|
||||
)}
|
||||
>
|
||||
{isCollapsed ? <ChevronRight className="h-4 w-4" /> : <ChevronLeft className="h-4 w-4" />}
|
||||
<ChevronLeft className="h-4 w-4" strokeWidth={1.5} />
|
||||
</button>
|
||||
|
||||
{!isCollapsed && (
|
||||
<SurfaceBody
|
||||
surface={surface}
|
||||
rigs={rigs}
|
||||
psMap={psMap}
|
||||
selection={selection}
|
||||
onSelect={onSelect}
|
||||
onClose={onClose}
|
||||
currentRigId={currentRigId}
|
||||
/>
|
||||
)}
|
||||
<SurfaceBody
|
||||
surface={surface}
|
||||
rigs={rigs}
|
||||
psMap={psMap}
|
||||
selection={selection}
|
||||
onSelect={onSelect}
|
||||
onClose={onClose}
|
||||
currentRigId={currentRigId}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user