diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..d367935 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,34 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "studio-api", + "runtimeExecutable": "dotnet", + "runtimeArgs": [ + "run", + "--project", + "src/backend/Tap.Studio/Tap.Studio.csproj", + "-p:SkipTapUiBuild=true", + "--", + "--Studio:WorkspaceRoot=samples/sample-workspace", + "--Studio:Port=5298" + ], + "port": 5298 + }, + { + "name": "studio-ui", + "runtimeExecutable": "env", + "runtimeArgs": [ + "STUDIO_API_URL=http://localhost:5298", + "scripts/studio-ui-dev.sh" + ], + "port": 5297 + }, + { + "name": "studio-aspire", + "runtimeExecutable": "sh", + "runtimeArgs": ["-c", "echo 'attaching to aspire-managed studio on 55446'; while :; do sleep 3600; done"], + "port": 55446 + } + ] +} diff --git a/.claude/skills/mantine/SKILL.md b/.claude/skills/mantine/SKILL.md new file mode 100644 index 0000000..46abd32 --- /dev/null +++ b/.claude/skills/mantine/SKILL.md @@ -0,0 +1,179 @@ +--- +name: mantine +description: "Mantine v9 React component library cheat sheet for the Tap Studio UI (src/ui-studio/). USE WHEN: writing or editing React components under src/ui-studio/, asking about Mantine v9 props, color-scheme, theme tokens, AppShell, forms, modals, notifications, tabs, badges, layout. DO NOT USE FOR: the standalone Tap Inspector UI (src/ui-inspector/) which uses its own CSS, or for non-React code. INVOKES: file edits, build commands. The Studio UI is Mantine-only — drop in raw
/CSS only when there's no Mantine equivalent." +--- + +# Mantine v9 skill (Tap Studio) + +The Tap Studio UI (`src/ui-studio/`) is built entirely on **Mantine 9.2.1** + **@tabler/icons-react**. There is no Tailwind, no shadcn, no base-ui — only Mantine. Custom CSS modules are reserved for the variable-input painted overlay and a couple of helper styles. Everything else uses Mantine components and theme tokens. + +This skill is the working reference: idiomatic patterns we already use, v9-specific gotchas, and where to look for more. + +## When to invoke + +Invoke this skill any time the work touches `src/ui-studio/src/**` and involves form layouts, dialogs, tab bars, color-scheme behaviour, or new component scaffolding. For pure data/state work (Zustand store, API client, TypeScript types) you don't need it. + +## Quick links + +- **Mantine docs**: https://mantine.dev/getting-started/ +- **Component index**: https://mantine.dev/core/anchor/ (replace `anchor` with the component name) +- **Tabler icons**: https://tabler.io/icons (search → import `IconXxx` from `@tabler/icons-react`) +- **v8→v9 changelog**: https://mantine.dev/changelog/9-0-0/ +- **Theme object types**: `node_modules/@mantine/core/lib/core/MantineProvider/theme/...` +- **Reference app**: `/Users/p7e/code/philbir/dreamr/src/ui-inspector` — uses Mantine 8 but most patterns are stable. + +## Live docs via the Context7 MCP + +The repo ships a project-scoped MCP at `/.mcp.json` that registers **Context7**. Use it to pull current prop signatures + code snippets without opening a browser: + +1. `mcp__context7__resolve-library-id` with `libraryName: "mantine"` → returns `/mantinedev/mantine` (and version variants). +2. `mcp__context7__get-library-docs` with that ID + a focused `topic` (e.g. `"Collapse component"`, `"TagsInput onChange"`, `"AppShell Header"`). + +Context7 is the canonical source for v9 props (when in doubt about `expanded` vs `in`, ask it first). For Tabler icons, `mcp__context7__resolve-library-id` with `"@tabler/icons-react"` works the same way. + +## Files that pin our setup + +| File | Role | +|---|---| +| `src/ui-studio/src/main.tsx` | `MantineProvider` + `ColorSchemeScript` + `Notifications` + `ModalsProvider`. | +| `src/ui-studio/src/theme.ts` | Custom `tap` color tuple + `defaultRadius` + component defaults. | +| `src/ui-studio/postcss.config.cjs` | `postcss-preset-mantine` + breakpoint variables. | +| `src/ui-studio/src/styles/index.css` | The *only* hand-rolled global CSS (method colors + mono font stack). | +| `src/ui-studio/src/workspace/useTheme.ts` | Thin wrapper over `useMantineColorScheme`. | + +## v9-specific gotchas you will hit + +1. **`Collapse` uses `expanded`, not `in`.** v8 used `in`; v9 renamed it. + ```tsx + + ``` +2. **`MultiSelect` cannot create new tags** — for chip-style inputs (auth scopes, tags) use **`TagsInput`**. +3. **`Select` defaults to `allowDeselect={true}`.** For "must-pick-one" fields, set `allowDeselect={false}` so users can't click the selected option to clear it. +4. **`Select` accepts string or `{value,label}[]`** — when passing `readonly` arrays (e.g. tuples), cast: `data={METHODS as unknown as string[]}`. +5. **`Tabs` value is `string | null`** — keep state as `useState('default-tab')`. +6. **`Modal.onClose` runs on any close (Esc, backdrop, X)** — your cleanup logic goes there, not on the "Cancel" button. +7. **`Notifications`** is a *component*, not a hook — mount it once at the root (already done in `main.tsx`). Use `notifications.show({…})` to fire. +8. **`useDisclosure`** returns `[opened, { open, close, toggle }]` — destructure the second element by name, not index. +9. **CSS variables are prefixed `--mantine-color-*`** (e.g. `--mantine-color-tap-6`). The `c` prop accepts shortcut names: `c="tap"`, `c="dimmed"`, `c="red"`. +10. **``** is inline by default — use `` for multi-line code blocks. +11. **The `loading` prop** on `Button` shows a spinner and disables the button — don't double-disable manually. + +## Idiomatic patterns from our codebase + +### Forms — typed local state, no `@mantine/form` + +We don't use `@mantine/form` (yet). Editors keep `spec` and `savedSpec` via `useState` and PUT a typed DTO to the server. See `src/editors/AuthEditor.tsx`. The pattern: + +```tsx +const [spec, setSpec] = useState(null) +const [savedSpec, setSavedSpec] = useState(null) +const dirty = useMemo(() => JSON.stringify(spec) !== JSON.stringify(savedSpec), [spec, savedSpec]) + +function update(key: K, value: AuthSpec[K]) { + setSpec((cur) => cur ? { ...cur, [key]: value } : cur) +} +``` + +If a future editor needs validation, switch to `useForm()` from `@mantine/form` — but be consistent within the editor. + +### Layout primitives we lean on + +- **``** — vertical form fields, dialog bodies, panel content. +- **``** — inline row of controls (toolbar, header right side). +- **``** — two side-by-side inputs (e.g. Name + Type). +- **``** — the kind picker cards in CreateNewDialog. +- **``** with `header / navbar / main` — the top-level shell in `App.tsx`. +- **``** wherever content might overflow. Set `flex={1}` for vertical fill. +- **`` + `}>`** — every editor's section navigation. + +### Color usage + +- Brand: `color="tap"` (primary color is set in the theme). +- Per-kind chips in editor headers (`EditorShell`): + - Request → `tap` + - API → `teal` + - Auth → `orange` + - Environment → `grape` + - Workspace → `tap` +- Action states: `green` (success), `red` (error/destructive), `yellow` (warn/secret), `gray` (muted). +- Don't use raw hex codes in component code — pick from theme colors or use `c="dimmed"`. + +### Indicating dirty / counts on tabs + +```tsx + + Headers {count > 0 && {count}} + +``` + +### Modals via the provider + +Use the imperative API only when there's no useful body — for actual forms (Add Workspace, Create New) prefer `` directly. Confirmation dialogs: + +```tsx +modals.openConfirmModal({ + title: 'Delete request?', + children: This can't be undone., + labels: { confirm: 'Delete', cancel: 'Keep' }, + confirmProps: { color: 'red' }, + onConfirm: () => doDelete(), +}) +``` + +### Notifications + +```tsx +import { notifications } from '@mantine/notifications' +notifications.show({ title: 'Saved', message: 'Request updated.', color: 'green', autoClose: 2500 }) +notifications.show({ title: 'Save failed', message: e.message, color: 'red' }) +``` + +### Tabler icons + +- Always import from `@tabler/icons-react`: `import { IconSend, IconLock } from '@tabler/icons-react'`. +- Default `size={14}` inside `Tabs.Tab` and toolbar buttons; `size={16}` for header icons; `size={20}` for empty-state hero icons. +- Stroke width is `2` by default. Use `stroke={1.5}` for big/decorative icons so they don't look heavy. + +### Variable-token highlighting + +`src/editors/VariableInput.tsx` wraps `TextInput` with a painted overlay that colorizes `{{var}}` and `${{secret}}` tokens. Use it anywhere a value can carry templates (URLs, auth fields, env values). Plain `TextInput` is fine for non-templated fields like `Name`. + +## Migration anti-patterns (don't do these) + +- ❌ Importing from `@base-ui-components/react` — that package has been removed. +- ❌ Inlining CSS in `style={{}}` for layout — use ``, ``, or `mb="md"` props. Inline `style` is OK for one-off width tweaks and Mantine CSS-variable references (e.g. `color: 'var(--mantine-color-tap-6)'`). +- ❌ Creating new `.module.css` files — Mantine's `styles` prop + `style` prop cover 95% of cases; the remaining 5% goes through theme overrides in `theme.ts`. +- ❌ Using `document.documentElement.dataset.theme` — Mantine owns the color scheme via `useMantineColorScheme`. +- ❌ Wrapping every input in a manual `