summaryrefslogtreecommitdiffhomepage
path: root/src/features/workspaces/ui/HomeTileGrid.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-07 16:12:58 +0900
committerAdam Malczewski <[email protected]>2026-09-07 16:12:58 +0900
commit83fb6e3ce7f80a9c287764df26b6245e0945a941 (patch)
treeafe8a0046b40a45a85f3bc56a4bcd4c93a6f36c7 /src/features/workspaces/ui/HomeTileGrid.svelte
parent754450b539818f9ad94c9586888973b0edd74b68 (diff)
downloaddispatch-web-83fb6e3ce7f80a9c287764df26b6245e0945a941.tar.gz
dispatch-web-83fb6e3ce7f80a9c287764df26b6245e0945a941.zip
feat(workspaces): rework tile-grid visuals — Start-screen polish
The home canvas now reads as a real Windows 8 Start screen instead of a cramped, edge-hugging list: - Geometry: the wide unit is DERIVED from the canvas height (4 rows fill it: unit=height/5 clamped to 112-184px, gap=unit/9, margin=unit/3 clamped 24-64px) — tiles are no longer tiny, and the tile field always carries a proportional outer margin (never compressed against the left edge). Tall mode keeps fluid columns with a taller 148px row + 12px gap. - Tiles take a per-workspace SATURATED tone (new pure tileTone(id): FNV-1a hash → one of the six theme-semantic colors) with tone-content text — the Win8 colored-tile identity. Starred is now a ★ badge (a tile keeps its own color), not a gray-tint override. - The tile face is the Win8 layout: small uppercase slug caption at the top, a bold span-aware title (lg → 3xl across 1×1 → 4×2), and a single-line stat row pinned to the bottom (count compacts to 'conv.' on 1×1 tiles, truncation-safe so narrow tiles never clip). - Selection is a high-contrast offset ring (visible on any tile color); tiles lift on hover; the pan arrow is a solid elevated circle. - Header gains a 'WORKSPACES' kicker; empty/loading states are centered and styled for the Start-screen canvas. Persisted data (order + spans only) and all interaction semantics (drag/FLIP, long-press, action bar, resize picker, create popover) are untouched. Tests: geometry derivation/clamping, tileTone determinism, wide-vs-compact count label — 1664 green.
Diffstat (limited to 'src/features/workspaces/ui/HomeTileGrid.svelte')
-rw-r--r--src/features/workspaces/ui/HomeTileGrid.svelte41
1 files changed, 31 insertions, 10 deletions
diff --git a/src/features/workspaces/ui/HomeTileGrid.svelte b/src/features/workspaces/ui/HomeTileGrid.svelte
index e29b9b1..ceae133 100644
--- a/src/features/workspaces/ui/HomeTileGrid.svelte
+++ b/src/features/workspaces/ui/HomeTileGrid.svelte
@@ -2,9 +2,25 @@
import type { WorkspaceEntry } from "@dispatch/wire";
import { nextDragPhase, type DragEvent, type DragPhase } from "../logic/drag-controller";
import type { GridGeometry, Placement, Span } from "../logic/tile-layout";
+ import { tileTone, type TileTone } from "../logic/view-model";
import type { WorkspaceStore } from "../store.svelte";
import WorkspaceTile from "./WorkspaceTile.svelte";
+ /**
+ * The Start-screen tile tones (static classes — Tailwind/DaisyUI must see
+ * the literal `bg-*`/`text-*-content` names to emit them). A tile is its
+ * tone's SATURATED color with the tone's `-content` text, like Win8's
+ * colored tiles on the dark Start canvas.
+ */
+ const TONE_CLASSES: Record<TileTone, string> = {
+ primary: "bg-primary text-primary-content border-primary",
+ secondary: "bg-secondary text-secondary-content border-secondary",
+ accent: "bg-accent text-accent-content border-accent",
+ info: "bg-info text-info-content border-info",
+ success: "bg-success text-success-content border-success",
+ warning: "bg-warning text-warning-content border-warning",
+ };
+
let {
geometry,
placements,
@@ -222,7 +238,7 @@
class="h-full overscroll-contain"
class:overflow-x-auto={geometry.mode === "wide"}
class:overflow-y-auto={geometry.mode === "tall"}
- style={`scroll-snap-type: ${geometry.mode === "wide" ? "x" : "y"} proximity;`}
+ style={`padding: ${geometry.margin}px; scroll-snap-type: ${geometry.mode === "wide" ? "x" : "y"} proximity;`}
onscroll={updateScroll}
onwheel={handleWheel}
>
@@ -233,12 +249,12 @@
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_click_events_have_key_events -->
<div
data-tile-id={placement.id}
- class={`flex select-none flex-col overflow-hidden rounded-box border transition-colors ${
- selectedId === placement.id ? "border-primary/50 ring-2 ring-primary" : ""
+ class={`flex select-none flex-col overflow-hidden rounded-box border shadow-md transition-[transform,box-shadow] duration-150 hover:-translate-y-0.5 hover:shadow-xl ${
+ TONE_CLASSES[tileTone(placement.id)]
} ${
- ws.starred
- ? "border-primary/40 bg-primary/20"
- : "border-base-content/10 bg-base-200"
+ selectedId === placement.id
+ ? "ring-4 ring-base-content/80 ring-offset-2 ring-offset-base-100"
+ : "border-transparent"
} ${phase.kind === "dragging" && phase.id === placement.id ? "opacity-40" : ""}`}
style={`${wrapperStyle(placement)} scroll-snap-align: start;`}
role="button"
@@ -277,17 +293,22 @@
</div>
{#if canPan}
- <!-- Pan arrow + thin scroll-position indicator (wide geometry only). -->
+ <!-- Pan arrow + thin scroll-position indicator (wide geometry only).
+ Both sit INSIDE the canvas' outer margin. -->
<button
type="button"
- class="btn btn-circle btn-sm absolute right-3 bottom-3 z-10 border-base-content/20 bg-base-100/80 shadow-lg"
+ class="btn btn-circle absolute z-10 border-none bg-base-100 text-base-content shadow-xl"
+ style={`right: ${Math.max(12, geometry.margin / 2)}px; bottom: ${Math.max(12, geometry.margin / 2)}px;`}
aria-label="Pan right"
title="Pan one screen sideways"
onclick={pan}
>
→
</button>
- <div class="absolute inset-x-0 bottom-0 z-10 h-0.5 bg-base-content/10">
+ <div
+ class="absolute right-0 bottom-0 left-0 z-10 h-0.5 bg-base-content/10"
+ style={`margin: 0 ${geometry.margin}px;`}
+ >
<div
class="h-full bg-primary transition-[width] duration-100"
style={`width: ${Math.round(scrollProgress * 100)}%;`}
@@ -299,7 +320,7 @@
{#if phase.kind === "dragging"}
<!-- The drag ghost follows the pointer (transient; never a data change). -->
<div
- class="pointer-events-none fixed z-50 rounded-box border border-primary bg-primary/30 px-3 py-2 text-sm font-bold shadow-2xl"
+ class="pointer-events-none fixed z-50 rounded-box border border-base-content/25 bg-base-100/95 px-3 py-2 text-sm font-bold shadow-2xl"
style={`left: ${phase.x + 10}px; top: ${phase.y + 10}px;`}
>
{workspacesById.get(phase.id)?.title ?? phase.id}