Thomas Zhang пре 3 месеци
родитељ
комит
4e8a0a2a4e

+ 2 - 1
specs/DESIGN.md

@@ -7,7 +7,8 @@ Component-level design docs live under `specs/designs/`, organized by category:
 ```
 specs/designs/
 ├── 00-foundation/     — core server infrastructure (server lifecycle, auth, config)
-└── 01-data/           — external data sources (Git, Kubernetes)
+├── 01-data/           — external data sources (Git, Kubernetes)
+└── 10-layout/         — overall UI designs
 ```
 
 ---

+ 32 - 0
specs/designs/10-layout/detail.md

@@ -0,0 +1,32 @@
+# Detail Page
+
+## Overall Styling Guideline
+
+Detail page should have different sections. Each section should use a glass pane as container with a large thin text as title.
+
+For listing properties, use the property table.
+
+| Component      | Path                                                  |
+| -------------- | ----------------------------------------------------- |
+| Glass Pane     | src/lib/components/common/glass-pane/pane.svelte      |
+| Property Table | src/lib/components/common/property-table/table.svelte |
+
+## Management Tabs
+
+Management tabs should be present for managed resources. Tabs should include: status, resources, changes, definitions.
+
+### Status Tab
+
+List all status conditions of the resource.
+
+### Resources Tab
+
+List child resources of the deployment (pods, deployments, services).
+
+### Changes Tab
+
+List all pending PRs and the commit history of the resource.
+
+### Definitions Tab
+
+Show the K8s custom resource of the current resource.

+ 79 - 0
specs/designs/10-layout/form.md

@@ -0,0 +1,79 @@
+# Form Page
+
+## Overall Styling Guideline
+
+Form pages create and edit entities. A single shared `<XForm>` component handles both flows; whether it acts as create or edit is derived from whether a starting value is provided.
+
+The form is rendered inside a centered, max-width container. Form fields are grouped into glass-pane cards, one per logical panel.
+
+| Component  | Path                                               |
+| ---------- | -------------------------------------------------- |
+| Glass Pane | `src/lib/components/common/glass-pane/pane.svelte` |
+| Card       | `src/lib/components/common/card/card.svelte`       |
+| Field      | `src/lib/components/controls/field/`               |
+| IconPicker | `src/lib/components/forms/icon-picker/`            |
+
+## Component Props
+
+```ts
+type Props = {
+	value?: XFormValue;
+	cancelHref: string;
+	onsubmit: (value?: XFormValue) => void;
+};
+```
+
+`isEdit` is derived from whether `value` is provided — undefined → create, defined → edit. The edit wrapper converts the loaded entity to a form value via `to{X}FormValue` before passing it in.
+
+## Page Header
+
+```
+New {Entity} | Edit {Entity}             [Cancel] [Create | Save Changes]
+{mode}                                              (edit mode only)
+```
+
+- Title: `New {Entity}` (create) or `Edit {Entity}` (edit)
+- Mode subtitle: shown only in edit mode, small capitalized text, reflecting the locked mode
+- Cancel button: outline, navigates to `cancelHref`
+- Submit button: primary; label is `Create` (create) or `Save Changes` (edit); shows a spinner and is disabled while submitting
+
+The same Cancel + Submit pair is rendered again at the bottom of the form, right-aligned.
+
+## Basic Information Panel
+
+The first panel in every form. The icon selector sits on the left as a square sidebar; basic identity fields stack on the right.
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ ┌────────┐  Name                                            │
+│ │  icon  │  [                   ]                           │
+│ │selector│  Mode                                            │
+│ └────────┘  [External | Managed]                            │
+│             …entity-specific fields…                        │
+└─────────────────────────────────────────────────────────────┘
+```
+
+- **Icon**: `<IconPicker>` over the entity's icon registry
+- **Name**: text, maxlength 64
+- **Mode** (entities with both `external` and `managed` variants): segmented toggle — `External` | `Managed`. Disabled in edit mode (mode is fixed once a resource is created). A description below the toggle explains what each mode means and updates as the user toggles.
+
+Entity-specific basic fields (description, type selectors, etc.) follow underneath. Optional sub-sections may be nested via `<Optional>`.
+
+## Submit
+
+`onsubmit` is invoked when the submit button is clicked. The form itself does not navigate or surface errors — the wrapper page is expected to call the API, navigate to the entity's detail page on success, and surface failures via the notification store.
+
+Each entity provides a `validate{X}FormValue` helper in its `types.ts` that converts a form value to the entity type and returns a list of validation error messages.
+
+## Wrapper Files
+
+Each form is mounted via thin route wrappers:
+
+- `routes/{entity}/new/+page.svelte` — create wrapper, sets the breadcrumb to `[New {Entity}]`
+- `routes/{entity}/[id]/edit/+page.server.ts` — loads the entity (404 if not found)
+- `routes/{entity}/[id]/edit/+page.svelte` — edit wrapper, converts the loaded entity via `to{X}FormValue`, passes it to the form, and sets the breadcrumb to the entity name
+
+## Page Name
+
+- Create: `New {Entity} - LocoStack`
+- Edit: `Edit {entity.name} - LocoStack`

+ 27 - 0
specs/designs/10-layout/list.md

@@ -0,0 +1,27 @@
+# Entity List Page
+
+## Overview
+
+Card grid list of an entity (agents, tools, models, ...).
+
+## Toolbar
+
+- Search input (name match, left side)
+- Filters toggle button (right next to the search input)
+- Spacer
+- "New" button (right side, link to the entity creation page)
+
+## Filter Panel
+
+Hidden by default, revealed by the Filters toggle button. Each row has a fixed-width label and a set of pill buttons.
+
+## Result Count
+
+```
+{filtered} of {total} {entity}        (when filtered)
+{total} {entity}(s)                   (when not filtered)
+```
+
+## Card Grid
+
+Standard responsive grid. Each card is a glass pane, link to its detail page.

+ 99 - 0
specs/designs/10-layout/style.md

@@ -0,0 +1,99 @@
+# Visual Style
+
+## Aesthetic
+
+**Slick, professional, and dark.** The overall tone is a premium developer tool — confident and minimal, not playful. Near-black background with light glassmorphism foreground elements. Glass surfaces are restrained: medium blur, medium opacity. The background includes an aurora-like shape with sharp gradient stops to make the glass effect stand out.
+
+Reference feel: closer to a high-end terminal or a fintech dashboard than a consumer SaaS product. Every surface should feel intentional and precise.
+
+## Component Library
+
+**shadcn-svelte** is the component library. Components are installed into `$lib/components/controls/` via the shadcn-svelte CLI and customized in place. Do not wrap shadcn components in extra abstraction layers — edit them directly when customization is needed.
+
+shadcn-svelte uses **bits-ui** for headless primitives and is fully compatible with Svelte 5 Runes.
+
+## Background
+
+- Near-black base with a subtle aurora gradient layer.
+- Subtle depth is achieved through both the background aurora and the glass layers.
+
+## Glass Surfaces
+
+Glass is used for foreground elements only (sidebar, top bar, cards, modals). It should feel like matte frosted glass over black — not colorful, not warm.
+
+| Surface           | Background               | Blur         | Border                             |
+| ----------------- | ------------------------ | ------------ | ---------------------------------- |
+| Sidebar / top bar | `rgba(255,255,255,0.04)` | `blur(12px)` | `1px solid rgba(255,255,255,0.08)` |
+| Cards / panels    | `rgba(255,255,255,0.05)` | `blur(8px)`  | `1px solid rgba(255,255,255,0.09)` |
+| Modals / popovers | `rgba(255,255,255,0.07)` | `blur(16px)` | `1px solid rgba(255,255,255,0.12)` |
+| Input fields      | `rgba(255,255,255,0.05)` | none         | `1px solid rgba(255,255,255,0.10)` |
+
+Keep colored tints subtle on glass surfaces. Accent color should remain focused on interactive elements.
+
+## Color Palette
+
+| Role           | Value                         | Usage                                    |
+| -------------- | ----------------------------- | ---------------------------------------- |
+| Background     | `oklch(0.145 0.008 326)`      | Page background                          |
+| Surface-1      | `rgba(255,255,255,0.04)`      | Sidebar, top bar                         |
+| Surface-2      | `rgba(255,255,255,0.05)`      | Cards, panels                            |
+| Surface-3      | `rgba(255,255,255,0.07)`      | Modals, dropdowns                        |
+| Border         | `rgba(255,255,255,0.08–0.12)` | All surface edges                        |
+| Text primary   | `rgba(255,255,255,0.92)`      | Body text, headings                      |
+| Text secondary | `rgba(255,255,255,0.45)`      | Labels, captions, placeholders           |
+| Text muted     | `rgba(255,255,255,0.25)`      | Disabled states, timestamps              |
+| Accent         | `oklch(0.586 0.256 295.614)`  | Primary actions, active nav, focus rings |
+| Accent hover   | `oklch(0.496 0.265 301.924)`  | Button hover, active states              |
+| Accent subtle  | `rgba(99,102,241,0.15)`       | Selected row backgrounds, badge fills    |
+| Destructive    | `#ef4444` (red-500)           | Delete actions, error states             |
+
+## Status Colors
+
+Desaturated by default, full opacity only on active/hover. Never use raw saturated colors as backgrounds.
+
+| State            | Color                  | Badge fill              |
+| ---------------- | ---------------------- | ----------------------- |
+| Running / merged | `#22c55e` (green-500)  | `rgba(34,197,94,0.12)`  |
+| Pending / open   | `#3b82f6` (blue-500)   | `rgba(59,130,246,0.12)` |
+| Degraded / draft | `#eab308` (yellow-500) | `rgba(234,179,8,0.12)`  |
+| Stopped / error  | `#ef4444` (red-500)    | `rgba(239,68,68,0.12)`  |
+
+## Typography
+
+- Font: Inter Variable for UI text, monospace for code/YAML blocks
+- Custom font loading is enabled for Inter Variable
+- Headings: `rgba(255,255,255,0.92)`, medium weight (500)
+- Body: `rgba(255,255,255,0.80)`, regular weight
+- Line height: 1.5 for body, 1.2 for headings
+
+## Elevation & Depth
+
+Three levels, achieved through glass opacity and border strength alone — no shadows or glows:
+
+1. **Base** — sidebar, top bar (`Surface-1`, faintest border)
+2. **Raised** — cards, form panels (`Surface-2`, slightly stronger border)
+3. **Floating** — modals, popovers, dropdowns (`Surface-3`, strongest border, higher blur)
+
+Avoid box shadows. If separation is needed, increase border opacity by one step rather than adding a shadow.
+
+## Interactive States
+
+- **Default**: muted text, transparent background
+- **Hover**: text brightens to primary (`0.92`), surface tint `rgba(255,255,255,0.04)` added
+- **Active / selected**: accent subtle background (`rgba(99,102,241,0.15)`), accent text color
+- **Focus**: `2px solid #6366f1` outline with `2px` offset — no soft glow rings
+- **Disabled**: text at `0.25` opacity, no pointer events
+
+## Icons
+
+Lucide via `lucide-svelte`. Size `16px` inline, `20px` for standalone actions. Color `rgba(255,255,255,0.45)` at rest, `rgba(255,255,255,0.92)` on hover/active. Stroke width `1.5px`.
+
+## Motion
+
+Minimal. Transitions only where they carry meaning:
+
+- Sidebar collapse/expand: `width 200ms ease`
+- Dropdown / popover open: `opacity 120ms ease, transform 120ms ease` (subtle upward fade-in)
+- Toasts / notifications: `transform 200ms ease` slide in from edge
+
+No looping animations, no decorative transitions on hover. Loading states use a simple opacity pulse, not a spinner, for inline elements; a spinner only for full-panel blocking loads.