Computer panel: match measured WorkClaw geometry (from capture)
Forensic capture of the live app's Computer panel gave exact ground truth, correcting my prior commit (which trusted a reviewer's measurements): - Dock is content-width (~380px) centered by a w-full wrapper, NOT stretched full-width — my last change had wrongly stretched it. Reverts to the real centered-pill behaviour. - Dark tiles (#1f1f1f) gain the measured grounding drop in --shadow-dock-tile (0 4.65px 9.35px rgba(0,0,0,0.2)); previously flat (hairline + inset only). - AppIcon cells fixed to 68px / 8px gap; grid gap 34px×16px; icons 34px; the dock now uses the same nested tile component as the grid. - White brand-tile shadow + tile nesting were already exact (capture confirms). Regenerated computer-home baseline. typecheck/lint/86 unit/31 E2E/6 visual green. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
355a6f23b9
commit
36087c30f5
@@ -174,7 +174,8 @@
|
|||||||
--shadow-popover: 0 16px 40px -12px #0009;
|
--shadow-popover: 0 16px 40px -12px #0009;
|
||||||
--shadow-dialog: 0 32px 80px -24px #000000b3;
|
--shadow-dialog: 0 32px 80px -24px #000000b3;
|
||||||
--shadow-edge: -3px 0 14px -4px #0006; /* panel left edge */
|
--shadow-edge: -3px 0 14px -4px #0006; /* panel left edge */
|
||||||
--shadow-dock-tile: 0 0 0 1px #ffffff14, inset 0 1px 0 #ffffff2e;
|
--shadow-dock-tile: 0 0 0 1px #ffffff14, 0 4.65px 9.35px #0003,
|
||||||
|
inset 0 1px 0 #ffffff2e; /* white hairline + grounding drop + top inset (WorkClaw) */
|
||||||
--shadow-cta: inset 0 1px 0 #ffffff40, 0 3px 10px -3px #f965658c; /* coral CTA */
|
--shadow-cta: inset 0 1px 0 #ffffff40, 0 3px 10px -3px #f965658c; /* coral CTA */
|
||||||
--shadow-launcher: inset 0 1px 0 #ffffff38, 0 5px 14px -3px #f965657a; /* header Computer launcher */
|
--shadow-launcher: inset 0 1px 0 #ffffff38, 0 5px 14px -3px #f965657a; /* header Computer launcher */
|
||||||
--shadow-screen:
|
--shadow-screen:
|
||||||
|
|||||||
@@ -13,15 +13,16 @@ interface HomeScreenProps {
|
|||||||
onOpen: (app: AppId, origin: DOMRect) => void;
|
onOpen: (app: AppId, origin: DOMRect) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WorkClaw's measured "physical icon" shadow — a dark hairline + two soft
|
/* WorkClaw's measured white-tile "physical icon" shadow (verified from prod):
|
||||||
drops + a bright top inset. Tuned for the white brand tiles (on dark glass
|
dark hairline + two soft drops + a bright top inset. Reads on the white
|
||||||
tiles a dark drop is invisible, so those keep the white-bevel token). */
|
brand tiles. Dark tiles use --shadow-dock-tile (white hairline + grounding
|
||||||
|
drop + top inset). */
|
||||||
const TILE_SHADOW =
|
const TILE_SHADOW =
|
||||||
"0 0 0 1px rgba(0,0,0,0.04), 0 1px 1.5px rgba(0,0,0,0.08), 0 4.65px 9.35px rgba(0,0,0,0.07), inset 0 1px 0 rgba(255,255,255,0.5)";
|
"0 0 0 1px rgba(0,0,0,0.04), 0 1px 1.5px rgba(0,0,0,0.08), 0 4.65px 9.35px rgba(0,0,0,0.07), inset 0 1px 0 rgba(255,255,255,0.5)";
|
||||||
|
|
||||||
/* The innermost visual tile (56px, 16px radius). Brand tiles use the official
|
/* The innermost visual tile (56px, 16px radius). Brand tiles use the official
|
||||||
service mark on a white squircle with the 4-layer shadow; the rest get the
|
service mark on a white squircle; the rest get the coral-gradient glyph on a
|
||||||
coral-gradient glyph on a glass tile with the dark-surface white bevel. */
|
#1f1f1f tile. Icons are 34px (measured). */
|
||||||
function tileSurface(app: AppId) {
|
function tileSurface(app: AppId) {
|
||||||
if (app === "browser" || app === "slack") {
|
if (app === "browser" || app === "slack") {
|
||||||
const src = app === "browser" ? "/services/chrome.svg" : "/services/slack.svg";
|
const src = app === "browser" ? "/services/chrome.svg" : "/services/slack.svg";
|
||||||
@@ -42,15 +43,16 @@ function tileSurface(app: AppId) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="relative flex size-14 items-center justify-center rounded-2xl bg-black/50 shadow-dock-tile backdrop-blur-sm select-none">
|
<div className="relative flex size-14 items-center justify-center rounded-2xl bg-[#1f1f1f] shadow-dock-tile select-none">
|
||||||
<GradientGlyph icon={APP_ICON[app]} size={28} />
|
<GradientGlyph icon={APP_ICON[app]} size={34} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WorkClaw's nested tile DOM: cell (stacks tile + label) → button (press
|
/* WorkClaw's AppIcon: a 68px cell (8px gap to label) → button (press target,
|
||||||
target, scales to 0.92) → positioning context → opacity-fade layer →
|
scales to 0.92) → positioning context → opacity-fade layer → visual tile.
|
||||||
visual tile. The button wraps only the tile; the label sits outside it. */
|
The button wraps only the tile; the label sits outside it. Used for both the
|
||||||
|
grid and the dock. */
|
||||||
function Tile({
|
function Tile({
|
||||||
app,
|
app,
|
||||||
label,
|
label,
|
||||||
@@ -61,14 +63,14 @@ function Tile({
|
|||||||
onOpen: (app: AppId, origin: DOMRect) => void;
|
onOpen: (app: AppId, origin: DOMRect) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="group relative z-0 flex flex-col items-center gap-1.5 hover:z-20 focus-within:z-20">
|
<div className="group relative z-0 flex w-[68px] flex-col items-center gap-2 hover:z-20 focus-within:z-20">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
onClick={(e: MouseEvent<HTMLButtonElement>) =>
|
onClick={(e: MouseEvent<HTMLButtonElement>) =>
|
||||||
onOpen(app, e.currentTarget.getBoundingClientRect())
|
onOpen(app, e.currentTarget.getBoundingClientRect())
|
||||||
}
|
}
|
||||||
className="cursor-pointer transition-transform duration-[0.1s] ease-out-app active:scale-[0.92]"
|
className="cursor-pointer transition-transform duration-100 ease-out active:scale-[0.92]"
|
||||||
>
|
>
|
||||||
<div className="relative size-14 rounded-2xl">
|
<div className="relative size-14 rounded-2xl">
|
||||||
<div className="absolute inset-0 transition-opacity duration-[120ms] ease-linear">
|
<div className="absolute inset-0 transition-opacity duration-[120ms] ease-linear">
|
||||||
@@ -83,40 +85,30 @@ function Tile({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The measured home screen: app grid over the wallpaper, frosted dock. */
|
/** The measured home screen: app grid over the wallpaper, frosted dock. The
|
||||||
|
* dock is content-width (≈380px) and centered by its w-full wrapper — not
|
||||||
|
* stretched (verified against prod). */
|
||||||
export function HomeScreen({ agentName, onOpen }: HomeScreenProps) {
|
export function HomeScreen({ agentName, onOpen }: HomeScreenProps) {
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-1 flex-col justify-between p-5">
|
<div className="relative flex flex-1 flex-col justify-between pt-5">
|
||||||
<div className="grid grid-cols-4 gap-3" aria-label={`${agentName}'s apps`}>
|
<div
|
||||||
|
className="grid grid-cols-4 gap-x-4 gap-y-[34px] px-3"
|
||||||
|
aria-label={`${agentName}'s apps`}
|
||||||
|
>
|
||||||
{HOME_GRID.map((tile) => (
|
{HOME_GRID.map((tile) => (
|
||||||
<Tile
|
<Tile key={tile.app} app={tile.app} label={tile.label} onOpen={onOpen} />
|
||||||
key={tile.app}
|
|
||||||
app={tile.app}
|
|
||||||
label={tile.label}
|
|
||||||
onOpen={onOpen}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex w-full items-end justify-center px-3 pb-8">
|
||||||
<div
|
<div
|
||||||
aria-label="Dock"
|
aria-label="Dock"
|
||||||
className="flex items-end justify-center gap-5 rounded-3xl bg-white/[0.07] px-6 pt-4 pb-3 shadow-dock-capsule ring-1 ring-white/[0.04] ring-inset backdrop-blur-[40px] backdrop-saturate-150"
|
className="flex items-end justify-center gap-5 rounded-3xl bg-white/[0.07] px-6 pt-4 pb-3 shadow-dock-capsule ring-1 ring-white/[0.04] ring-inset backdrop-blur-[40px] backdrop-saturate-150"
|
||||||
>
|
>
|
||||||
{HOME_DOCK.map((tile) => (
|
{HOME_DOCK.map((tile) => (
|
||||||
<div key={tile.app} className="flex flex-col items-center gap-1.5">
|
<Tile key={tile.app} app={tile.app} label={tile.label} onOpen={onOpen} />
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label={tile.label}
|
|
||||||
onClick={(e) =>
|
|
||||||
onOpen(tile.app, e.currentTarget.getBoundingClientRect())
|
|
||||||
}
|
|
||||||
className="flex size-14 items-center justify-center rounded-2xl bg-black shadow-dock-tile transition-transform duration-[0.1s] ease-out-app active:scale-[0.92]"
|
|
||||||
>
|
|
||||||
<GradientGlyph icon={APP_ICON[tile.app]} size={34} />
|
|
||||||
</button>
|
|
||||||
<span className="text-[11px] text-white/80">{tile.label}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 195 KiB |
Reference in New Issue
Block a user