Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 17 additions & 2 deletions .agents/skills/adapt/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: adapt
description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
user-invocable: true
argument-hint: "[target] [context (mobile, tablet, print...)]"
argument-hint: '[target] [context (mobile, tablet, print...)]'
---

Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
Expand Down Expand Up @@ -44,25 +44,29 @@ Create context-appropriate strategy:
### Mobile Adaptation (Desktop → Mobile)

**Layout Strategy**:

- Single column instead of multi-column
- Vertical stacking instead of side-by-side
- Full-width components instead of fixed widths
- Bottom navigation instead of top/side navigation

**Interaction Strategy**:

- Touch targets 44x44px minimum (not hover-dependent)
- Swipe gestures where appropriate (lists, carousels)
- Bottom sheets instead of dropdowns
- Thumbs-first design (controls within thumb reach)
- Larger tap areas with more spacing

**Content Strategy**:

- Progressive disclosure (don't show everything at once)
- Prioritize primary content (secondary content in tabs/accordions)
- Shorter text (more concise)
- Larger text (16px minimum)

**Navigation Strategy**:

- Hamburger menu or bottom navigation
- Reduce navigation complexity
- Sticky headers for context
Expand All @@ -71,12 +75,14 @@ Create context-appropriate strategy:
### Tablet Adaptation (Hybrid Approach)

**Layout Strategy**:

- Two-column layouts (not single or three-column)
- Side panels for secondary content
- Master-detail views (list + detail)
- Adaptive based on orientation (portrait vs landscape)

**Interaction Strategy**:

- Support both touch and pointer
- Touch targets 44x44px but allow denser layouts than phone
- Side navigation drawers
Expand All @@ -85,19 +91,22 @@ Create context-appropriate strategy:
### Desktop Adaptation (Mobile → Desktop)

**Layout Strategy**:

- Multi-column layouts (use horizontal space)
- Side navigation always visible
- Multiple information panels simultaneously
- Fixed widths with max-width constraints (don't stretch to 4K)

**Interaction Strategy**:

- Hover states for additional information
- Keyboard shortcuts
- Right-click context menus
- Drag and drop where helpful
- Multi-select with Shift/Cmd

**Content Strategy**:

- Show more information upfront (less progressive disclosure)
- Data tables with many columns
- Richer visualizations
Expand All @@ -106,12 +115,14 @@ Create context-appropriate strategy:
### Print Adaptation (Screen → Print)

**Layout Strategy**:

- Page breaks at logical points
- Remove navigation, footer, interactive elements
- Black and white (or limited color)
- Proper margins for binding

**Content Strategy**:

- Expand shortened content (show full URLs, hidden sections)
- Add page numbers, headers, footers
- Include metadata (print date, page title)
Expand All @@ -120,12 +131,14 @@ Create context-appropriate strategy:
### Email Adaptation (Web → Email)

**Layout Strategy**:

- Narrow width (600px max)
- Single column only
- Inline CSS (no external stylesheets)
- Table-based layouts (for email client compatibility)

**Interaction Strategy**:

- Large, obvious CTAs (buttons not text links)
- No hover states (not reliable)
- Deep links to web app for complex interactions
Expand All @@ -137,6 +150,7 @@ Apply changes systematically:
### Responsive Breakpoints

Choose appropriate breakpoints:

- Mobile: 320px-767px
- Tablet: 768px-1023px
- Desktop: 1024px+
Expand Down Expand Up @@ -175,6 +189,7 @@ Choose appropriate breakpoints:
**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.

**NEVER**:

- Hide core functionality on mobile (if it matters, make it work)
- Assume desktop = powerful device (consider accessibility, older machines)
- Use different information architecture across contexts (confusing)
Expand All @@ -195,4 +210,4 @@ Test thoroughly across contexts:
- **Edge cases**: Very small screens (320px), very large screens (4K)
- **Slow connections**: Test on throttled network

Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.
Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.
23 changes: 21 additions & 2 deletions .agents/skills/clarify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: clarify
description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
user-invocable: true
argument-hint: "[target]"
argument-hint: '[target]'
---

Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
Expand Down Expand Up @@ -50,97 +50,115 @@ Create a strategy for clearer communication:
Refine text across these common areas:

### Error Messages

**Bad**: "Error 403: Forbidden"
**Good**: "You don't have permission to view this page. Contact your admin for access."

**Bad**: "Invalid input"
**Good**: "Email addresses need an @ symbol. Try: name@example.com"

**Principles**:

- Explain what went wrong in plain language
- Suggest how to fix it
- Don't blame the user
- Include examples when helpful
- Link to help/support if applicable

### Form Labels & Instructions

**Bad**: "DOB (MM/DD/YYYY)"
**Good**: "Date of birth" (with placeholder showing format)

**Bad**: "Enter value here"
**Good**: "Your email address" or "Company name"

**Principles**:

- Use clear, specific labels (not generic placeholders)
- Show format expectations with examples
- Explain why you're asking (when not obvious)
- Put instructions before the field, not after
- Keep required field indicators clear

### Button & CTA Text

**Bad**: "Click here" | "Submit" | "OK"
**Good**: "Create account" | "Save changes" | "Got it, thanks"

**Principles**:

- Describe the action specifically
- Use active voice (verb + noun)
- Match user's mental model
- Be specific ("Save" is better than "OK")

### Help Text & Tooltips

**Bad**: "This is the username field"
**Good**: "Choose a username. You can change this later in Settings."

**Principles**:

- Add value (don't just repeat the label)
- Answer the implicit question ("What is this?" or "Why do you need this?")
- Keep it brief but complete
- Link to detailed docs if needed

### Empty States

**Bad**: "No items"
**Good**: "No projects yet. Create your first project to get started."

**Principles**:

- Explain why it's empty (if not obvious)
- Show next action clearly
- Make it welcoming, not dead-end

### Success Messages

**Bad**: "Success"
**Good**: "Settings saved! Your changes will take effect immediately."

**Principles**:

- Confirm what happened
- Explain what happens next (if relevant)
- Be brief but complete
- Match the user's emotional moment (celebrate big wins)

### Loading States

**Bad**: "Loading..." (for 30+ seconds)
**Good**: "Analyzing your data... this usually takes 30-60 seconds"

**Principles**:

- Set expectations (how long?)
- Explain what's happening (when it's not obvious)
- Show progress when possible
- Offer escape hatch if appropriate ("Cancel")

### Confirmation Dialogs

**Bad**: "Are you sure?"
**Good**: "Delete 'Project Alpha'? This can't be undone."

**Principles**:

- State the specific action
- Explain consequences (especially for destructive actions)
- Use clear button labels ("Delete project" not "Yes")
- Don't overuse confirmations (only for risky actions)

### Navigation & Wayfinding

**Bad**: Generic labels like "Items" | "Things" | "Stuff"
**Good**: Specific labels like "Your projects" | "Team members" | "Settings"

**Principles**:

- Be specific and descriptive
- Use language users understand (not internal jargon)
- Make hierarchy clear
Expand All @@ -158,6 +176,7 @@ Every piece of copy should follow these rules:
6. **Be consistent**: Use same terms throughout (don't vary for variety)

**NEVER**:

- Use jargon without explanation
- Blame users ("You made an error" → "This field is required")
- Be vague ("Something went wrong" without explanation)
Expand All @@ -179,4 +198,4 @@ Test that copy improvements work:
- **Consistency**: Does it match terminology elsewhere?
- **Tone**: Is it appropriate for the situation?

Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.
Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.
9 changes: 5 additions & 4 deletions .claude/skills/launch-video/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: launch-video
description: Use when creating a launch, release, or announcement video for the Spool desktop app from real screen recordings. Covers the capture pipeline (Electron + native macOS window recording), the HyperFrames composition layout, common trailer-vs-PPT pitfalls, and the first-frame poster trick for social media. Invoke when the user mentions release video, launch video, announcement video, trailer, demo video, or wants to ship a video for a Spool version bump.
user-invocable: true
argument-hint: "[version (e.g. v0.5.0)]"
argument-hint: '[version (e.g. v0.5.0)]'
---

Build a release video for a Spool version. The output is a 1080p MP4 plus a poster JPG, intended for X / Twitter. Runtime scales with feature count: 20–40s, 3–8 feature beats + brand outro, optionally a stylised capstone scene before the outro for releases whose payoff is a tangible outcome the captured clips can't show on their own.
Expand All @@ -15,6 +15,7 @@ A release video for Spool has two halves running in parallel on screen:
2. **Right two-thirds** — the real Spool app, recorded as a native macOS window. The camera (CSS `transform-origin + scale` on the `.window`) zooms into the specific UI region for each feature.

Focus on the active feature is carried by one of two devices:

- **Amber annotation rectangles** inside `.window` that track the camera — good for "look at this strip" callouts. Cheap and precise when the feature is a single bounded region.
- **Spotlight mask** — an SVG overlay that dims everything except 1–2 transparent "holes" punched through. Softer falloff means small coordinate drift disappears into the dim instead of reading as a misaligned outline. Dual holes let you keep the preview bright while a control on the right side lights up in sync with a click. See `references/spotlight.md`.

Expand All @@ -41,7 +42,7 @@ Read these references before you start composing. They are not optional — each
- Records one `.mov` per feature via `recordNativeWindow()` from `helpers/native-window-capture.ts`
- Uses `cursorClick(window, selector, opts)` / `cursorTo(...)` / `cursorPark(x, y)` from the same helper instead of bare `.click()` so the cursor's path is visibly filmed
- Outputs to `videos/spool-vX.Y.Z/assets/live/` (gitignored)
- If your release gates a feature behind a Vite flag, build the app with `VITE_FEATURE_<NAME>=1 pnpm --filter @spool/app run build:electron` *before* running the spec — `import.meta.env.VITE_FEATURE_<NAME>` is inlined at build time, not read at runtime
- If your release gates a feature behind a Vite flag, build the app with `VITE_FEATURE_<NAME>=1 pnpm --filter @spool/app run build:electron` _before_ running the spec — `import.meta.env.VITE_FEATURE_<NAME>` is inlined at build time, not read at runtime
- Bump `--global-timeout` if pacing is relaxed: Playwright's default 300s isn't enough for 7-beat spec runs (~50s of `screencapture -V` plus warmup)
4. **Copy `videos/launch-template/` to `videos/spool-vX.Y.Z/`.**
5. **Customise the composition** (`index.html`):
Expand All @@ -67,8 +68,8 @@ Read these references before you start composing. They are not optional — each

## Hard rules

- **Synthetic cursor is allowed but must track real input.** A floating GSAP cursor that arrives at random places looks uncanny — that rule from prior releases stands. What's *not* uncanny is the cursor-overlay technique in `references/cursor-overlay.md`: a DOM cursor injected into the page that follows Playwright's actual `mouse.move()` and pulses on `mousedown`. If a scene's whole point is a click, draw the cursor. Don't draw a cursor for scenes where nothing's being clicked.
- **Chain state across clips. Don't reset between recordings.** If clip N ends with the UI in some configuration, clip N+1 starts there. Pre-clip state resets (clicking around to clean up between recordings) cause a visible "flash" at the clipCut boundary even though both clips show valid app pixels. If a later beat needs a specific look, get to it *inside* an earlier recording (the user sees the click) or design the demo flow so the natural state at each beat is already what you want.
- **Synthetic cursor is allowed but must track real input.** A floating GSAP cursor that arrives at random places looks uncanny — that rule from prior releases stands. What's _not_ uncanny is the cursor-overlay technique in `references/cursor-overlay.md`: a DOM cursor injected into the page that follows Playwright's actual `mouse.move()` and pulses on `mousedown`. If a scene's whole point is a click, draw the cursor. Don't draw a cursor for scenes where nothing's being clicked.
- **Chain state across clips. Don't reset between recordings.** If clip N ends with the UI in some configuration, clip N+1 starts there. Pre-clip state resets (clicking around to clean up between recordings) cause a visible "flash" at the clipCut boundary even though both clips show valid app pixels. If a later beat needs a specific look, get to it _inside_ an earlier recording (the user sees the click) or design the demo flow so the natural state at each beat is already what you want.
- **First frame must be the full hero state.** Brand mark + panel 1 + UI window all visible at `t=0`, not faded in. Twitter's auto-thumbnail grabs this frame.
- **Annotations and spotlights live inside `.window`**, not on the outer canvas. They must move with the camera transform.
- **Spotlight coords must be verified by overlay on the rendered frame.** Eyeballing them off a grid on a raw `.mov` is consistently 5–15% off. See `pitfalls.md` on the drawbox verification loop.
Expand Down
31 changes: 18 additions & 13 deletions .claude/skills/launch-video/references/capture.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ const PROJECTS: ProjectSeed[] = [
name: 'spool',
total: 79,
leadSessions: [
{ title: 'Audit the share flow in a fresh worktree', source: 'claude', iso: '2026-05-13T15:12:00Z' },
{
title: 'Audit the share flow in a fresh worktree',
source: 'claude',
iso: '2026-05-13T15:12:00Z',
},
// ... more titles per project
],
fillerSources: ['claude', 'codex'],
Expand Down Expand Up @@ -68,18 +72,19 @@ const PROJECTS = [/* per-release seed */]
const OUT_DIR = path.join(__dirname, '../../../videos/spool-vX.Y.Z/assets/live')

test('record release clips', async () => {
test.setTimeout(540_000) // ~9min — relaxed pacing across 7 beats can take longer than the default 30s test timeout
test.setTimeout(540_000) // ~9min — relaxed pacing across 7 beats can take longer than the default 30s test timeout

const ctx = await launchDemoApp(PROJECTS)
await setDemoWindowBounds(ctx, 1080, 740)
await waitForDemoSync(ctx.window)
await installCursorOverlay(ctx.window) // synthetic cursor for the rest of the spec
await cursorPark(ctx.window, 120, 360) // park off to the side before clip 1
await installCursorOverlay(ctx.window) // synthetic cursor for the rest of the spec
await cursorPark(ctx.window, 120, 360) // park off to the side before clip 1

await recordNativeWindow(ctx.app, path.join(OUT_DIR, '01-home.mov'), 4.0, async () => {
await ctx.window.waitForTimeout(500) // settle so the cursor is in the first filmed frame
await ctx.window.waitForTimeout(500) // settle so the cursor is in the first filmed frame
await cursorClick(ctx.window, '[data-testid="<feature-gated-element>"]', {
preClickPause: 260, postClickPause: 280,
preClickPause: 260,
postClickPause: 280,
})
// ... drive the rest of the scene
})
Expand All @@ -99,7 +104,7 @@ pnpm --filter @spool/app exec playwright test e2e/release-capture.spec.ts \

`--global-timeout` is required for relaxed-pacing releases — Playwright's config default is 300s and a 7-beat spec with relaxed `postClickPause` values can easily exceed that. `--retries=0` because a flaky capture (Electron failing to focus, screencapture permission glitch) on first run can spawn a second Electron whose window overlaps the first — the capture then records the wrong app state. Better to fail loudly and re-run by hand than auto-retry and ship wrong frames.

If the feature being demoed is behind a build-time flag (e.g. `VITE_FEATURE_<NAME>`), build the app with the flag *before* running the spec — `import.meta.env.VITE_FEATURE_<NAME>` is inlined at build time:
If the feature being demoed is behind a build-time flag (e.g. `VITE_FEATURE_<NAME>`), build the app with the flag _before_ running the spec — `import.meta.env.VITE_FEATURE_<NAME>` is inlined at build time:

```bash
VITE_FEATURE_<NAME>=1 pnpm --filter @spool/app run build:electron
Expand All @@ -109,12 +114,12 @@ If the feature is also gated by a runtime opt-in stored in `agents.json` (e.g. a

## Capture-time constants

| | Value | Why |
|---|---|---|
| Window logical size | 1080×740 | Matches app default (`packages/app/src/main/index.ts`); the composition assumes this aspect |
| Theme | dark forced | `nativeTheme.themeSource = 'dark'` in `setDemoWindowBounds()` |
| GPU | disabled | `ELECTRON_DISABLE_GPU=1` for deterministic frames |
| `screencapture` cursor | off | Default; don't add `-C`. The real OS cursor doesn't move with Playwright, so capturing it would film a stationary system arrow in some corner. Use the synthetic DOM cursor from `cursor-overlay.md` instead |
| | Value | Why |
| ---------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Window logical size | 1080×740 | Matches app default (`packages/app/src/main/index.ts`); the composition assumes this aspect |
| Theme | dark forced | `nativeTheme.themeSource = 'dark'` in `setDemoWindowBounds()` |
| GPU | disabled | `ELECTRON_DISABLE_GPU=1` for deterministic frames |
| `screencapture` cursor | off | Default; don't add `-C`. The real OS cursor doesn't move with Playwright, so capturing it would film a stationary system arrow in some corner. Use the synthetic DOM cursor from `cursor-overlay.md` instead |

## Common capture issues

Expand Down
Loading
Loading