Skip to content
Back to skills

Visual Companion

ASecurity

Browser-based visual companion for showing mockups, diagrams, comparisons, and interactive options. Use when the user would understand something better by seeing it than reading it.

  • 10 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 1, 2026
ai-agentsgoshellbashgitapi

Works with

  • terminal
  • cli
  • api

Security analysis

A100/100

Pro scans all 6 files and shows the line behind each finding

Scanned September 1, 2026

npx -y skills add dork-labs/dorkos --skill visual-companion --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Visual Companion?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Visual Companion
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dork-labs-visual-companion/badge)](https://www.skillsdirectory.com/skills/dork-labs-visual-companion)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: visual-companion
description: Browser-based visual companion for showing mockups, diagrams, comparisons, and interactive options. Use when the user would understand something better by seeing it than reading it.
license: Complete terms in LICENSE.txt
---

# Visual Companion

Browser-based visual companion for rendering mockups, diagrams, and interactive options. The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content, the user sees it in their browser and can click to select options.

## When to Use

Decide per-question, not per-session. The test: **would the user understand this better by seeing it than reading it?**

**Use the browser** when the content itself is visual:

- **UI mockups** -- wireframes, layouts, navigation structures, component designs
- **Architecture diagrams** -- system components, data flow, relationship maps
- **Side-by-side visual comparisons** -- comparing two layouts, two color schemes, two design directions
- **Design polish** -- when the question is about look and feel, spacing, visual hierarchy
- **Spatial relationships** -- state machines, flowcharts, entity relationships rendered as diagrams

**Use the terminal** when the content is text or tabular:

- **Requirements and scope questions** -- "what does X mean?", "which features are in scope?"
- **Conceptual A/B/C choices** -- picking between approaches described in words
- **Tradeoff lists** -- pros/cons, comparison tables
- **Technical decisions** -- API design, data modeling, architectural approach selection
- **Clarifying questions** -- anything where the answer is words, not a visual preference

A question _about_ a UI topic is not automatically a visual question. "What kind of wizard do you want?" is conceptual -- use the terminal. "Which of these wizard layouts feels right?" is visual -- use the browser.

## How It Works

The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content to `screen_dir`, the user sees it in their browser and can click to select options. Selections are recorded to `state_dir/events` as JSONL that you read on your next turn.

**Content fragments vs full documents:** If your HTML file starts with `<!DOCTYPE` or `<html`, the server serves it as-is (injecting the helper script). Otherwise, the server automatically wraps your content in the frame template -- adding the header, CSS theme, selection indicator, and all interactive infrastructure. **Write content fragments by default.** Only write full documents when you need complete control over the page.

## Starting a Session

```bash
# Start server with persistence (content saved to project)
scripts/start-server.sh --project-dir /path/to/project

# Returns JSON:
# {"type":"server-started","port":52341,"url":"http://localhost:52341",
#  "screen_dir":"/path/to/project/.dork/visual-companion/12345-1706000000/content",
#  "state_dir":"/path/to/project/.dork/visual-companion/12345-1706000000/state"}
```

Save `screen_dir` and `state_dir` from the response. Tell the user to open the URL.

**Finding connection info:** The server writes its startup JSON to `$STATE_DIR/server-info`. If you launched the server in the background and did not capture stdout, read that file to get the URL and port. When using `--project-dir`, check `<project>/.dork/visual-companion/` for the session directory.

**Note:** Pass the project root as `--project-dir` so content persists in `.dork/visual-companion/` and survives server restarts. Without it, files go to `/tmp` and get cleaned up. Remind the user to add `.dork/` to `.gitignore` if it is not already there.

### Launching (all platforms)

The server must outlive the Bash tool call that starts it. Use `--foreground` combined with `run_in_background: true` on the Bash tool so the process persists across conversation turns.

```bash
# CORRECT — server survives across turns
scripts/start-server.sh --project-dir /path/to/project --foreground
# Set run_in_background: true on the Bash tool call
```

Then on the next turn, find the newest session directory and read `$STATE_DIR/server-info` to get the URL and port:

```bash
# Find newest session
ls <project>/.dork/visual-companion/ | sort -t'-' -k2 -n | tail -1
# Read connection info
cat <project>/.dork/visual-companion/<session>/state/server-info
```

**Why not the default background mode?** The default mode (`start-server.sh` without `--foreground`) backgrounds the server as a child of the Bash process. When the Bash tool call completes, the parent shell exits and the server self-terminates with `"reason":"owner process exited"`. This happens on all platforms — macOS, Linux, and Windows.

If the URL is unreachable from your browser (common in remote/containerized setups), bind a non-loopback host:

```bash
scripts/start-server.sh \
  --project-dir /path/to/project \
  --host 0.0.0.0 \
  --url-host localhost
```

Use `--url-host` to control what hostname is printed in the returned URL JSON.

## The Content Loop

1. **Check server is alive**, then **write HTML** to a new file in `screen_dir`:
   - Before each write, check that `$STATE_DIR/server-info` exists. If it does not (or `$STATE_DIR/server-stopped` exists), the server has shut down -- restart it with `start-server.sh` before continuing. The server auto-exits after 30 minutes of inactivity.
   - Use semantic filenames: `platform.html`, `visual-style.html`, `layout.html`
   - **Never reuse filenames** -- each screen gets a fresh file
   - Use the Write tool -- **never use cat/heredoc** (dumps noise into terminal)
   - Server automatically serves the newest file

2. **Tell user what to expect and end your turn:**
   - Remind them of the URL (every step, not just first)
   - Give a brief text summary of what is on screen (e.g., "Showing 3 layout options for the homepage")
   - Ask them to respond in the terminal: "Take a look and let me know what you think. Click to select an option if you'd like."

3. **On your next turn** -- after the user responds in the terminal:
   - Read `$STATE_DIR/events` if it exists -- this contains the user's browser interactions (clicks, selections) as JSONL
   - Merge with the user's terminal text to get the full picture
   - The terminal message is the primary feedback; `state_dir/events` provides structured interaction data

4. **Iterate or advance** -- if feedback changes the current screen, write a new file (e.g., `layout-v2.html`). Only move to the next question when the current step is validated.

5. **Unload when returning to terminal** -- when the next step does not need the browser (e.g., a clarifying question, a tradeoff discussion), push a waiting screen to clear stale content:

   ```html
   <!-- filename: waiting.html (or waiting-2.html, etc.) -->
   <div style="display:flex;align-items:center;justify-content:center;min-height:60vh">
     <p class="subtitle">Continuing in terminal...</p>
   </div>
   ```

   This prevents the user from staring at a resolved choice while the conversation has moved on. When the next visual question comes up, push a new content file as usual.

6. Repeat until done.

## CSS Classes Available

The frame template provides these CSS classes for content fragments. No `<html>`, CSS, or `<script>` tags needed -- the server provides all of that.

### Options (A/B/C choices)

```html
<div class="options">
  <div class="option" data-choice="a" onclick="toggleSelect(this)">
    <div class="letter">A</div>
    <div class="content">
      <h3>Title</h3>
      <p>Description</p>
    </div>
  </div>
</div>
```

**Multi-select:** Add `data-multiselect` to the container to let users select multiple options. Each click toggles the item. The indicator bar shows the count.

```html
<div class="options" data-multiselect>
  <!-- same option markup -- users can select/deselect multiple -->
</div>
```

### Cards (visual designs)

```html
<div class="cards">
  <div class="card" data-choice="design1" onclick="toggleSelect(this)">
    <div class="card-image"><!-- mockup content --></div>
    <div class="card-body">
      <h3>Name</h3>
      <p>Description</p>
    </div>
  </div>
</div>
```

### Mockup container

```html
<div class="mockup">
  <div class="mockup-header">Preview: Dashboard Layout</div>
  <div class="mockup-body"><!-- your mockup HTML --></div>
</div>
```

### Split view (side-by-side)

```html
<div class="split">
  <div class="mockup"><!-- left --></div>
  <div class="mockup"><!-- right --></div>
</div>
```

### Pros/Cons

```html
<div class="pros-cons">
  <div class="pros">
    <h4>Pros</h4>
    <ul>
      <li>Benefit</li>
    </ul>
  </div>
  <div class="cons">
    <h4>Cons</h4>
    <ul>
      <li>Drawback</li>
    </ul>
  </div>
</div>
```

### Mock elements (wireframe building blocks)

```html
<div class="mock-nav">Logo | Home | About | Contact</div>
<div style="display: flex;">
  <div class="mock-sidebar">Navigation</div>
  <div class="mock-content">Main content area</div>
</div>
<button class="mock-button">Action Button</button>
<input class="mock-input" placeholder="Input field" />
<div class="placeholder">Placeholder area</div>
```

### Typography and sections

- `h2` -- page title
- `h3` -- section heading
- `.subtitle` -- secondary text below title
- `.section` -- content block with bottom margin
- `.label` -- small uppercase label text

## Browser Events Format (JSONL)

When the user clicks options in the browser, their interactions are recorded to `$STATE_DIR/events` as one JSON object per line. The file is cleared automatically when you push a new screen.

```jsonl
{"type":"click","choice":"a","text":"Option A - Simple Layout","timestamp":1706000101}
{"type":"click","choice":"c","text":"Option C - Complex Grid","timestamp":1706000108}
{"type":"click","choice":"b","text":"Option B - Hybrid","timestamp":1706000115}
```

The full event stream shows the user's exploration path -- they may click multiple options before settling. The last `choice` event is typically the final selection, but the pattern of clicks can reveal hesitation or preferences worth asking about.

If `$STATE_DIR/events` does not exist, the user did not interact with the browser -- use only their terminal text.

## Design Tips

- **Scale fidelity to the question** -- wireframes for layout, polish for polish questions
- **Explain the question on each page** -- "Which layout feels more professional?" not just "Pick one"
- **Iterate before advancing** -- if feedback changes current screen, write a new version
- **2-4 options max** per screen
- **Use real content when it matters** -- for a photography portfolio, use actual images (Unsplash). Placeholder content obscures design issues.
- **Keep mockups simple** -- focus on layout and structure, not pixel-perfect design

## File Naming

- Use semantic names: `platform.html`, `visual-style.html`, `layout.html`
- Never reuse filenames -- each screen must be a new file
- For iterations: append version suffix like `layout-v2.html`, `layout-v3.html`
- Server serves newest file by modification time

## Capturing Design Decisions for Specs

When using the visual companion alongside a feature spec (`specs/<slug>/`), capture the design work so implementing agents can consume it without reading raw HTML mockups.

### During the session

- Name screens to match the design question (e.g., `agent-channels-tab.html`, `settings-layout.html`)
- After each round of feedback, note the user's selections and verbal reasoning

### At session end

Write a `04-design-decisions.md` file into the spec directory that captures:

1. **Each design question explored** — what was asked, which screen file showed it
2. **Options presented** — brief description of each option (A, B, C)
3. **What was chosen and why** — the user's selection plus their reasoning
4. **Final design direction** — prose descriptions of the agreed-upon designs, detailed enough that an implementing agent can build from them without seeing the mockups

Example structure:

```markdown
# Design Decisions

Visual companion session: `.dork/visual-companion/<session-id>/`

## 1. [Design Question]

**Screen:** `<filename>.html`
**Options:** A) ... B) ... C) ...
**Chosen:** B — [reasoning from user]

## 2. [Next Design Question]

...

## Final Design Summary

[Prose description of the complete agreed design, suitable for implementation]
```

### Spec frontmatter

Add the session reference to the spec's ideation or specification frontmatter:

```yaml
design-session: .dork/visual-companion/<session-id>
```

This lets implementing agents find the raw HTML mockups if they need visual reference.

### When to skip

If the visual companion was used for a quick one-off question (not part of a spec), skip the design decisions file. This process only applies when the session is part of a spec workflow.

## Shutting Down

```bash
scripts/stop-server.sh $SESSION_DIR
```

If the session used `--project-dir`, content files persist in `.dork/visual-companion/` for later reference. Only `/tmp` sessions get deleted on stop.

## Reference

- Frame template (CSS reference): `scripts/frame-template.html`
- Helper script (client-side): `scripts/helper.js`
- Server implementation: `scripts/server.cjs`
- Start script: `scripts/start-server.sh`
- Stop script: `scripts/stop-server.sh`

Files in this skill

  • SKILL.md13 KB
  • scripts/frame-template.html9 KB
  • scripts/helper.js2.7 KB
  • scripts/server.cjs11.2 KB
  • scripts/start-server.sh4.5 KB
  • scripts/stop-server.sh1.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…