# AGENTS.md Guidance for AI agents (and humans) working on Vision Start. Read before making changes. --- ## 0. First rule: keep `project-context.md` alive - **Always read `project-context.md` first.** It is the canonical map of the project: structure, features, data model, flow, build/release. Treat it as required reading before any non-trivial change. - **Always keep it updated.** Whenever you add, remove, rename, or materially change files, modules, features, storage keys, config fields, build/release steps, or workflows, update the relevant section of `project-context.md` in the same change set. If your work touches the structure tree, the data model, the storage layout, or the quick-lookup table, those sections must reflect the new state. - Keep it a general overview, not a coding-style guide — that distinction lives here in `AGENTS.md`. --- ## 1. Styling guidance The design language is **soft liquid glass**: lightly translucent matte surfaces, restrained edge highlights, moderate backdrop blur, soft shadows, cyan accents, and iOS-like easing. ### Surface recipe (use consistently) - **Tiles / floating controls / light surfaces:** `liquid-surface` plus `liquid-control` / `liquid-tile` / `liquid-focus` as appropriate. - **Cards / modals / panels:** `liquid-panel liquid-modal-card rounded-3xl` for centered modals; `liquid-drawer` for the settings drawer. - **Inputs:** `liquid-input p-3`; ranges use `liquid-range`. - **Buttons:** `liquid-button` plus one of `liquid-button-primary`, `liquid-button-success`, `liquid-button-secondary`, or `liquid-button-danger`. - **Add/edit surfaces:** add tiles use `liquid-ghost-tile`; tile edit controls use `liquid-edit-toolbar` and `liquid-edit-action`. - **Full-screen modal overlay:** `liquid-modal-backdrop`. - Keep the glass effect subtle: avoid high-opacity white highlights, heavy blur, strong saturation, or oversized glow unless a specific component needs emphasis. ### Color tokens - Accent / focus / selection: cyan via `liquid-focus`, `cyan-400`, `cyan-200`, and `liquid-button-primary` - Confirm/Save: `liquid-button-success` - Cancel/secondary: `liquid-button-secondary` - Destructive: `liquid-button-danger` or red text on `liquid-edit-action` - Tertiary (export/import etc.): `liquid-button-secondary` - Status: online `bg-green-400 text-green-400`, offline `bg-red-400 text-red-400`, pending `bg-slate-400 text-slate-400` - Text: primary `text-white`, secondary `text-slate-300`/`text-slate-400`, muted `text-white/50` (in-app hover states) ### Motion - Use the custom easing tokens defined in `index.css`: `ease-ios` (default), `ease-spring` (toggle knobs, drawer slides), and `ease-liquid` (tile lift and wallpaper transitions). - Standard durations: `duration-150` (buttons), `duration-200` (tiles, toggles, icons), and `duration-300` (drawers/modals). - Prefer the shared liquid classes for hover/press motion. Add custom transforms only when the shared classes do not cover the interaction. - Keep `prefers-reduced-motion` behavior intact when adding animations. ### Tailwind specifics - Tailwind **v4** via `@tailwindcss/vite` and `@tailwindcss/postcss`. Custom easing is in `index.css` under `@theme {}` — add new theme tokens there, not in `tailwind.config.js`. - `tailwind.config.js` has a **safelist** of `w-[Npx]/h-[Npx]` classes because tile/icon sizes are constructed dynamically. If you add new pixel-size classes that are generated at runtime (not literally present in source), add them to the safelist or they will be purged. - Prefer static classes; only build dynamic class strings when unavoidable and then safelist them. ### Components with established conventions to reuse - `Dropdown` (`components/Dropdown.tsx`) for selects — single or multi. Has a glassy look and custom arrow. **Always use it** instead of `` for option picking. - `ToggleSwitch` (`components/ToggleSwitch.tsx`) for boolean toggles — **always use it** instead of checkboxes. - Modal pattern: `liquid-modal-backdrop` + centered `liquid-panel liquid-modal-card rounded-3xl`, close on overlay click via `handleOverlayClick` (`if (e.target === e.currentTarget) onClose()`). - New editors/settings go in `components/configuration/` as a `*Tab.tsx` and are wired into `ConfigurationModal.tsx`. - New options' size presets follow the existing scale: `tiny | small | medium | large` (see `Header.tsx`, `WebsiteTile.tsx`). --- ## 2. New feature guidance A "feature" in this project typically means: a new widget, a new settings option, or a new bit of persisted state. When adding one, follow this checklist. ### A. Types & defaults (in order) 1. **`types.ts`** — add new fields to the relevant interface (usually `Config`, sometimes a nested one like `clock`/`serverWidget`). Create a nested object when a feature has 2+ sub-options (mirror the `clock`/`serverWidget` pattern). 2. **`components/services/ConfigurationService.ts` `DEFAULT_CONFIG`** — add the new field with a sane default so existing users (with a previously-saved `Config`) get it via the `{ ...DEFAULT_CONFIG, ...parsed }` merge on load. 3. Any new standalone collections (not inside `Config`) get their own storage key/helper in `ConfigurationService` (see `userWallpapers`, `wallpaperState` for the pattern). ### B. Settings UI (if the feature is user-configurable) 1. Create `components/configuration/Tab.tsx` and add its entry to the `tabs` array in `components/ConfigurationModal.tsx`. 2. Receive `config` and an `onChange(updates: Partial)` callback; emit partial updates — `App.tsx`'s `handleConfigChange` merges shallowly, so update nested objects as whole units (e.g. `{ clock: { ...config.clock, ...updates } }`). 3. Use `Dropdown` / `ToggleSwitch` / range inputs per the styling section above. ### C. Rendering & wiring 4. Render the feature in `App.tsx` (or a dedicated component imported by `App.tsx`), gated by its `config..enabled` flag when it can be turned off. 5. Mark up new modal/edit surfaces to match existing modals (backdrop click closes, glass card styling). ### D. Persistence — configs must be properly saved 6. `App.tsx` already persists `config` via `ConfigurationService.saveConfig(config)` in a `useEffect`. **Any state added to `Config` is persisted automatically** — do not introduce parallel ad-hoc `localStorage` writes for config fields. 7. Standalone collections (not in `Config`) need explicit save calls — mirror `userWallpapers`: load on mount, save on every mutation, never keep stale copies. 8. Never write to `chrome.storage.local` directly outside `components/utils/StorageLocalManager.ts`. Always check `checkChromeStorageLocalAvailable()` first and gracefully degrade (the app must still run as a plain web page). Throw informative errors when the storage is required but missing. ### E. Export / import must keep working 9. If you add a new `localStorage` key (other than config fields, which are exported/imported automatically), add it to `REQUIRED_LOCAL_STORAGE_KEYS` in `ConfigurationService.ts` so it is included in exports and restored on imports. 10. After adding fields to `Config`, double check the import path: `importConfig` writes each restored key to `localStorage` and returns `{ config, userWallpapers }`. New config sub-objects flow through automatically because they're inside `config`. ### F. Defaults & migrations 11. Default values must be chosen so a fresh install and an upgrade from an older `Config` both behave sensibly (the load path merges onto `DEFAULT_CONFIG`). 12. If a new field changes behavior in a way that existing users' stored data should be preserved differently, handle the migration inside `loadConfig` (and consider bumping the export `version` field in `exportConfig` if the shape changes non-additively). ### G. Style of new tiles/widgets 13. A new widget should follow the existing widget shape: optional/gated by `config..enabled`, positioned with a liquid glass container, and use the surface/duration/easing tokens above. 14. New "editable" tiles/content reuse the `isEditing` mode and the edit/move/delete affordances pattern from `WebsiteTile.tsx` and `CategoryGroup.tsx`. --- ## 3. General engineering rules - **Language:** TypeScript strict mode (`tsconfig.json` enforces `strict`, `noUnusedLocals`, `noUnusedParameters`, `noFallthroughCasesInSwitch`, `noUncheckedSideEffectImports`). Do not loosen these. - **Runtime note:** Preact is the actual runtime (`@preact/preset-vite`), but types and imports use `react`/`@types/react`. Do not introduce React-only APIs without verifying Preact compatibility. - **Path alias:** `@/*` maps to the project root (see `tsconfig.json` `paths`). Source files currently use relative imports (`./`, `../`) — match the surrounding file. - **No comments** in committed code unless explicitly requested. - **Build vs. dev:** Prefer verifying with `npm run build` rather than `npm run dev`. The only npm scripts are `dev`, `build`, `preview`. - **No lint/typecheck script is configured.** Verify TS correctness manually (e.g. `npx tsc --noEmit`), and confirm `npm run build` succeeds before considering a task complete. - **Don't touch `public/icon-metadata.json`** — it's gitignored and fetched at release-build time by `scripts/prepare_release.sh`. Don't commit a local copy. - **Don't reintroduce dead code:** `components/EditModal.tsx` imports `lucide-react` and `./IconPicker`, neither of which is a dependency. It is not wired into the app. Use `WebsiteEditModal.tsx` / `CategoryEditModal.tsx` instead, and add the icon picker inline (as in `WebsiteEditModal.tsx`) rather than reviving `IconPicker`. - **External asset URLs:** any new icons/css/JS pulled from CDNs (e.g. `cdn.jsdelivr.net`) must work offline-or-not without crashing — always provide a fallback (see how `iconService.ts` falls back to Google's S2 favicon service). - **Manifest & extension constraints:** the extension uses Manifest V3 with `script-src 'self'` CSP and only the `storage` permission. Don't introduce inline scripts/eval, and avoid requesting new permissions unless essential — added permissions can disable updates on installed extensions. - **Commits:** never commit unless explicitly asked. Don't touch secrets, don't edit `.gitignore`/`.env.local`/`.claude/`, don't run `git config`. --- ## 4. When in doubt - Read `project-context.md`. - Mirror the nearest existing equivalent (a similar widget, modal, or config tab) before inventing a new pattern. - Favor the established liquid utilities (`liquid-surface`, `liquid-panel`, `liquid-input`, `liquid-button`, `liquid-focus`) over ad-hoc glass class strings. - Surface failing assumptions (CORS, missing chrome.storage, larger-than-4MB wallpapers) with clear errors, matching the style of `StorageLocalManager.ts` and `iconService.ts`. - Update `project-context.md` after your change.