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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ You are working on **Dogwalker**: a free, cross-platform (macOS/Windows/Linux) E

## Project status

v1.5.0 is in development.
v1.5.1 is released.

## Invariants — do not violate without explicit human sign-off

Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@

An infinite canvas for AI coding agents: real terminals as nodes on a zoomable 2D surface. Put them on a leash — wire terminals together and your agents talk to each other through a structured protocol.

*macOS · Windows · Linux*
<p>
<a href="https://github.com/caribeedu/dogwalker/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/caribeedu/dogwalker?label=release&labelColor=1b1b23&color=e8b565"></a>
<a href="https://github.com/caribeedu/dogwalker/actions/workflows/build.yml"><img alt="Build" src="https://img.shields.io/github/actions/workflow/status/caribeedu/dogwalker/build.yml?label=build&labelColor=1b1b23&color=e8b565"></a>
<a href="#license"><img alt="License: MIT" src="https://img.shields.io/github/license/caribeedu/dogwalker?labelColor=1b1b23&color=e8b565"></a>
</p>

<p>
<a href="#install"><img alt="macOS — Homebrew" src="https://img.shields.io/badge/macOS-Homebrew-e8b565?logo=apple&logoColor=white&labelColor=1b1b23"></a>
<a href="#install"><img alt="Windows — Scoop" src="https://img.shields.io/badge/Windows-Scoop-e8b565?logo=windows&logoColor=white&labelColor=1b1b23"></a>
<a href="#install"><img alt="Linux — AUR" src="https://img.shields.io/badge/Linux-AUR-e8b565?logo=archlinux&logoColor=white&labelColor=1b1b23"></a>
</p>

[Why](#why) · [Features](#features) · [Install](#install) · [Quick start](#quick-start) · [Compatibility](#compatibility) · [Architecture](#architecture) · [Documents](#documents) · [Privacy](#privacy) · [License](#license)

Expand Down Expand Up @@ -70,6 +80,8 @@ Grab the installer for your OS from the [latest release](https://github.com/cari

Unsigned for now: Windows SmartScreen ("More info → Run anyway") and macOS Gatekeeper (right-click → Open, or `xattr -dr com.apple.quarantine Dogwalker.app`) will warn on first launch. Code signing + notarization are tracked for a later release.

Packaged Windows/macOS builds **auto-update** from the latest release. Package-manager installs — Homebrew (macOS), Scoop (Windows), AUR (Linux) — are set up under [`packaging/`](packaging/); the `brew` / `scoop` / AUR commands land here once their taps are published.

### From source

**Requirements:** Node.js 20+ and `git`.
Expand Down
12 changes: 12 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes, newest first. Dogwalker is a free, local, cross-platform
canvas for AI coding agents.

## 1.5.1

- Themed scrollbars and checkboxes: both now follow the Dogwalker design system
and recolor with the theme (custom `--dw-*`-driven styling instead of the
browser defaults).
- The dev palette sits centered just below the common palette instead of the
top-left corner.
- README gains status badges (release / build / license) and per-OS install
badges (macOS · Homebrew, Windows · Scoop, Linux · AUR).
- Panel polish: section headings share one consistent style, plus minor label
tidy-ups.

## 1.5.0

- Every CLI verb accepts `--json`: append it to any command (`list`, `check`,
Expand Down
21 changes: 21 additions & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The path from empty repo to public release, one version at a time. Each version
| [v1.4.0](#v140--app-themes-open-composer--schema-tree) | App themes, open composer & schema tree | The UI is themeable, the composer @mentions terminal recipients, and schemas edit as a tree |
| [v1.4.1](#v141--theme-tokenization--composer-polish) | Theme tokenization & composer polish | App themes recolor the whole chrome; composer tints @mentions inline |
| [v1.5.0](#v150--universal---json--codemirror-schema-editor) | Universal --json & CodeMirror schema editor | Every CLI verb speaks `--json`; contracts edit their schema in a linted CodeMirror |
| [v1.5.1](#v151--design-system-polish) | Design-system polish | Themed scrollbars/checkboxes, dev palette placement, README badges |

---

Expand Down Expand Up @@ -546,6 +547,26 @@ The first feature minor after the 1.4 polish.

---

## v1.5.1 — Design-system polish

A small polish patch.

- **Themed scrollbars & checkboxes.** Both are restyled to the `--dw-*` design
tokens (custom webkit scrollbars; `appearance: none` checkboxes with a drawn,
accent-filled tick), so they match the chrome and recolor with the theme instead
of using the browser defaults.
- **Dev palette placement.** The dev-only palette moves from the top-left corner to
centered just below the common palette.
- **README badges.** Status badges (release / build / license) and per-OS install
badges (macOS · Homebrew, Windows · Scoop, Linux · AUR).
- Panel section headings collapse onto one consistent `.dw-section-head` style.

**Exit criteria**
- Scrollbars and checkboxes look native to Dogwalker and change with the theme.
- The dev palette no longer overlaps the top-left chrome.

---

## After v1 (parked, unscheduled)

Recurring ideas deliberately not on the path: community preset/skill sharing,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dogwalker",
"productName": "Dogwalker",
"version": "1.5.0",
"version": "1.5.1",
"description": "Walk all your agents at once — an infinite canvas for AI coding agents.",
"main": ".vite/build/main.js",
"private": true,
Expand Down
31 changes: 12 additions & 19 deletions src/app/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function PresetsSection() {
setName(''); setCommand(''); setIcon(DEFAULT_PRESET_ICON);
refresh();
};
return <div className="dw-section"><div className="dw-section-head"><h2>Presets</h2></div>
return <div className="dw-section"><h2 className="dw-section-head">Presets</h2>
<p className="dw-settings-hint">Reusable terminal launch commands. Pick an icon for the terminal palette; built-in commands are read-only but can be duplicated or deleted.</p>
<div className="dw-routine-form">
<IconPicker value={icon} onChange={setIcon} />
Expand Down Expand Up @@ -219,7 +219,7 @@ function RolesSection() {
const refresh = () => void window.dw.listRoles().then((roles) => { setItems(roles); window.dispatchEvent(new Event('dw:roles-changed')); });
useEffect(() => { refresh(); }, []);
const add = async () => { if (!name.trim()) return; await window.dw.createRole({ name, instructions }); setName(''); setInstructions(''); refresh(); };
return <div className="dw-section"><div className="dw-section-head"><h2>Roles</h2></div>
return <div className="dw-section"><h2 className="dw-section-head">Roles</h2>
<p className="dw-settings-hint">Reusable Markdown instructions. Assign one in a terminal header.</p>
<div className="dw-routine-form"><input placeholder="Role name" value={name} onChange={(e) => setName(e.target.value)} /><textarea rows={4} placeholder="Instructions for this role…" value={instructions} onChange={(e) => setInstructions(e.target.value)} /><button className="dw-btn-primary" onClick={() => void add()}>+ Add role</button></div>
<div className="dw-routine-list">{items.map((r) => <div className="dw-routine-card" key={r.id}><div className="dw-routine-info"><div className="dw-routine-title">{r.name}</div><div className="dw-routine-prompt-preview">{r.instructions || 'No instructions yet.'}</div></div><div className="dw-routine-actions"><button className="dw-btn-small" onClick={() => { const name = window.prompt('Role name', r.name); const instructions = window.prompt('Instructions', r.instructions); if (name !== null && instructions !== null) void window.dw.updateRole(r.id, { name, instructions }).then(refresh); }}>Edit</button><button className="dw-btn-small" onClick={() => void window.dw.createRole({ name: r.name + ' copy', instructions: r.instructions }).then(refresh)}>Duplicate</button><button className="dw-btn-small dw-btn-danger" onClick={() => void window.dw.deleteRole(r.id).then(refresh)}>Delete</button></div></div>)}</div>
Expand Down Expand Up @@ -297,8 +297,8 @@ function ContractForm({ initial, submitLabel, onSubmit, onCancel }: {
<label className="dw-contract-label">JSON Schema the peer's answer must match</label>
<SchemaEditor value={schemaText} onChange={setSchemaText} />
<div className="dw-routine-row">
<label className="dw-routine-every">attempts<input type="number" min={1} value={attempts} onChange={(e) => setAttempts(Number(e.target.value))} /></label>
<label className="dw-routine-every">timeout<input type="number" min={1} value={timeoutSec} onChange={(e) => setTimeoutSec(Number(e.target.value))} />s</label>
<label className="dw-routine-every">Attempts<input type="number" min={1} value={attempts} onChange={(e) => setAttempts(Number(e.target.value))} /></label>
<label className="dw-routine-every">Timeout<input type="number" min={1} value={timeoutSec} onChange={(e) => setTimeoutSec(Number(e.target.value))} />s</label>
</div>
<label className="dw-contract-label">Rejection prompt — re-sent to the peer after a failed attempt</label>
<textarea rows={2} placeholder="Please return valid JSON matching the schema." value={rejectionPrompt} onChange={(e) => setRejectionPrompt(e.target.value)} />
Expand All @@ -319,7 +319,7 @@ function ContractsSection() {
const [nonce, setNonce] = useState(0);
const refresh = () => void window.dw.listContracts().then(setItems);
useEffect(() => { refresh(); }, []);
return <div className="dw-section"><div className="dw-section-head"><h2>Contracts</h2></div>
return <div className="dw-section"><h2 className="dw-section-head">Contracts</h2>
<p className="dw-settings-hint">A JSON Schema an <code>ask --contract</code> answer must validate against. The broker re-asks the peer up to the attempt budget; when they run out, the asker receives the fallback value.</p>
<ContractForm
key={`create-${nonce}`}
Expand Down Expand Up @@ -365,8 +365,8 @@ function WorkspacesSection({
}: Props) {
return (
<div className="dw-section">
<div className="dw-section-head">
<h2>Workspaces</h2>
<div className="dw-section-head-row">
<h2 className="dw-section-head">Workspaces</h2>
<button className="dw-btn-primary" onClick={onCreate}>
+ New
</button>
Expand Down Expand Up @@ -431,9 +431,7 @@ function RoutinesSection({ activeId }: { activeId: string }) {

return (
<div className="dw-section">
<div className="dw-section-head">
<h2>Routines</h2>
</div>
<h2 className="dw-section-head">Routines</h2>
<p className="dw-settings-hint">
A scheduled prompt for an agent. Chain steps with <code>&&</code> (or new
lines) — each waits for the agent's turn to finish before the next.
Expand All @@ -456,7 +454,7 @@ function RoutinesSection({ activeId }: { activeId: string }) {
))}
</select>
<label className="dw-routine-every">
every
Every
<input
type="number"
min={5}
Expand Down Expand Up @@ -528,15 +526,12 @@ function SettingsSection({ themes, settings, activeThemeName, onUpdateSettings }
const lightThemes = themes.filter((t) => t.appearance === 'light');
return (
<div className="dw-section">
<div className="dw-section-head">
<h2>Theme</h2>
<span className="dw-active-theme">active: {activeThemeName}</span>
</div>
<h2 className="dw-section-head">Theme</h2>
<p className="dw-settings-hint">
One theme colors everything — the terminals and the whole interface
(background, sidebar, menus, notes, leashes, buttons and icons).
</p>

<span className="dw-active-theme">Active: {activeThemeName}</span>
<div className="dw-theme-grid">
{themes.map((t) => (
<button
Expand Down Expand Up @@ -565,9 +560,7 @@ function SettingsSection({ themes, settings, activeThemeName, onUpdateSettings }
Follow system light/dark
</label>

<div className="dw-section-head" style={{ marginTop: 8 }}>
<h2>Notifications</h2>
</div>
<h2 className="dw-section-head" style={{ marginTop: 8 }}>Notifications</h2>
<label className="dw-toggle-row">
<input
type="checkbox"
Expand Down
96 changes: 88 additions & 8 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,77 @@ body,
font-size: 13px;
}

/* ---- Design system: scrollbars (themed) -------------------------------- */
* {
scrollbar-width: thin;
scrollbar-color: var(--dw-border-strong) transparent;
}

::-webkit-scrollbar {
width: 12px;
height: 12px;
}

::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
background: transparent;
}

::-webkit-scrollbar-thumb {
background: var(--dw-border-strong);
border: 3px solid transparent;
background-clip: padding-box;
border-radius: var(--dw-r-pill);
}

::-webkit-scrollbar-thumb:hover {
background: var(--dw-text-faint);
background-clip: padding-box;
}

/* ---- Design system: checkboxes (themed) -------------------------------- */
input[type='checkbox'] {
appearance: none;
-webkit-appearance: none;
flex-shrink: 0;
position: relative;
width: 16px;
height: 16px;
margin: 0;
border: 1px solid var(--dw-border-strong);
border-radius: var(--dw-r-sm);
background: var(--dw-surface-2);
cursor: pointer;
transition: background 0.12s ease, border-color 0.12s ease;
}

input[type='checkbox']:hover {
border-color: var(--dw-accent);
}

input[type='checkbox']:checked {
background: var(--dw-accent);
border-color: var(--dw-accent);
}

/* Drawn checkmark so it inherits the theme (ink readable on the accent fill). */
input[type='checkbox']:checked::after {
content: '';
position: absolute;
left: 4.5px;
top: 1px;
width: 4px;
height: 8px;
border: solid var(--dw-on-accent);
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}

input[type='checkbox']:focus-visible {
outline: none;
box-shadow: var(--dw-ring);
}

/* ---- App shell ---------------------------------------------------------- */
.dw-app {
height: 100%;
Expand Down Expand Up @@ -879,8 +950,10 @@ body,
/* ---- Dev toolbar (dev only) --------------------------------------------- */
.dw-devbar {
position: absolute;
top: 8px;
left: 8px;
/* Sits centered just below the common palette (top: 12px + its ~48px height). */
top: 68px;
left: 50%;
transform: translateX(-50%);
z-index: 15;
display: flex;
align-items: center;
Expand Down Expand Up @@ -1312,7 +1385,7 @@ body,
}

.dw-panel-body {
padding: 24px 28px;
padding: 24px 56px;
overflow-y: auto;
}

Expand All @@ -1324,8 +1397,8 @@ body,

.dw-panel-close {
position: absolute;
top: 12px;
right: 14px;
top: 22px;
right: 26px;
width: 30px;
height: 30px;
border: none;
Expand All @@ -1342,16 +1415,21 @@ body,
}

.dw-section-head {
margin: 0 0 18px;
font-size: 18px;
color: var(--dw-text);
}

/* A section heading paired with an action on the right (e.g. Workspaces → New). */
.dw-section-head-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 18px;
}

.dw-section-head h2 {
.dw-section-head-row .dw-section-head {
margin: 0;
font-size: 18px;
color: var(--dw-text);
}

.dw-ws-grid {
Expand Down Expand Up @@ -2543,6 +2621,8 @@ body,
.dw-active-theme {
color: var(--dw-text-muted);
font-size: 12px;
margin-bottom: 14px;
display: block;
}

.dw-theme-grid {
Expand Down
Loading