Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .changeset/design-mode-voice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": minor
---

Add an experimental `kilo design` command for voice-steered live design iteration. It runs one in-process Kilo session driven by a continuous voice loop: speak (or type, in fake-voice mode) and finalized turns dispatch to the agent while edits stream back into the browser. Press Escape to interrupt and keep listening. The command is hidden from release builds while it's in development.
18 changes: 18 additions & 0 deletions packages/kilo-design-fixture/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Design canvas fixture

This is a throwaway static site used as the Browser Canvas for `kilo design`. It
is intentionally tiny so design requests have something concrete to act on.

When asked to make a visual change, edit these files only:

- `public/index.html` — the page markup
- `public/styles.css` — the styling (CSS custom properties live in `:root`)

Guidelines:

- Prefer editing the CSS variables in `:root` (e.g. `--brand`, `--bg`, `--fg`,
`--radius`, `--space`) when a request is about color, spacing, or shape.
- Keep changes small and focused on what was asked — this is a live, hot-reloading
canvas, so each edit should be immediately visible.
- Do not add a build step, framework, or dependencies. Plain HTML + CSS only.
- Do not touch `serve.ts` or anything outside `public/` unless explicitly asked.
10 changes: 10 additions & 0 deletions packages/kilo-design-fixture/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "@kilocode/design-fixture",
"version": "0.0.0",
"private": true,
"description": "Throwaway Browser Canvas fixture for `kilo design` (zero-install, Bun dev server with live reload)",
"type": "module",
"scripts": {
"dev": "bun run serve.ts"
}
}
48 changes: 48 additions & 0 deletions packages/kilo-design-fixture/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Nimbus — design canvas</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<header class="nav">
<div class="brand">Nimbus</div>
<nav class="links">
<a href="#">Product</a>
<a href="#">Pricing</a>
<a href="#">Docs</a>
<a class="cta" href="#">Get started</a>
</nav>
</header>

<main>
<section class="hero">
<h1 class="hero-title">Ship interfaces at the speed of thought.</h1>
<p class="hero-sub">Nimbus turns rough ideas into polished product UI — no pixel-pushing required.</p>
<div class="hero-actions">
<a class="btn primary" href="#">Start free</a>
<a class="btn ghost" href="#">Watch demo</a>
</div>
</section>

<section class="cards">
<article class="card">
<h3>Fast</h3>
<p>Edits land the instant you describe them.</p>
</article>
<article class="card">
<h3>Flexible</h3>
<p>Every token is yours to shape and re-shape.</p>
</article>
<article class="card">
<h3>Friendly</h3>
<p>Talk to it like a teammate, not a tool.</p>
</article>
</section>
</main>

<footer class="foot">Built with Nimbus · design canvas fixture</footer>
</body>
</html>
132 changes: 132 additions & 0 deletions packages/kilo-design-fixture/public/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
:root {
--brand: #ec4899;
--brand-ink: #ffffff;
--bg: #000000;
--fg: #0f172a;
--muted: #64748b;
--card-bg: #f8fafc;
--border: #e2e8f0;
--radius: 12px;
--space: 16px;
}

* {
box-sizing: border-box;
}

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: var(--fg);
background: var(--bg);
line-height: 1.5;
}

.nav {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space) 32px;
border-bottom: 1px solid var(--border);
}

.brand {
font-weight: 700;
font-size: 20px;
color: var(--brand);
}

.links {
display: flex;
gap: 20px;
align-items: center;
}

.links a {
color: var(--muted);
text-decoration: none;
font-size: 14px;
}

.links .cta {
background: var(--brand);
color: var(--brand-ink);
padding: 8px 14px;
border-radius: var(--radius);
}

.hero {
max-width: 760px;
margin: 80px auto 48px;
padding: 0 24px;
text-align: center;
}

.hero-title {
font-size: 72px;
line-height: 1.1;
margin: 0 0 16px;
}

.hero-sub {
font-size: 18px;
color: var(--muted);
margin: 0 0 28px;
}

.hero-actions {
display: flex;
gap: 12px;
justify-content: center;
}

.btn {
padding: 12px 20px;
border-radius: var(--radius);
text-decoration: none;
font-weight: 600;
font-size: 15px;
}

.btn.primary {
background: var(--brand);
color: var(--brand-ink);
}

.btn.ghost {
border: 1px solid var(--border);
color: var(--fg);
}

.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space);
max-width: 900px;
margin: 0 auto 64px;
padding: 0 24px;
}

.card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 24px;
}

.card h3 {
margin: 0 0 8px;
}

.card p {
margin: 0;
color: var(--muted);
}

.foot {
text-align: center;
color: var(--muted);
font-size: 13px;
padding: 24px;
border-top: 1px solid var(--border);
}
88 changes: 88 additions & 0 deletions packages/kilo-design-fixture/serve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Zero-dependency dev server for the Design Mode canvas fixture.
//
// Serves ./public with live reload: a tiny WebSocket pushes a "reload" whenever
// a file under ./public changes, so edits the design agent makes show up in the
// browser instantly — the visible half of the speak → edit → see loop.

import { watch } from "fs"
import path from "path"

const root = path.join(import.meta.dir, "public")
const port = Number(process.env.PORT ?? 4321)

const LIVE_RELOAD = `
<script>
(() => {
const connect = () => {
const ws = new WebSocket(\`ws://\${location.host}/__lr\`)
ws.onmessage = (e) => { if (e.data === "reload") location.reload() }
ws.onclose = () => setTimeout(connect, 600)
}
connect()
})()
</script>
`

const MIME: Record<string, string> = {
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".svg": "image/svg+xml",
".png": "image/png",
".jpg": "image/jpeg",
".json": "application/json",
}

const server = Bun.serve({
port,
async fetch(req, server) {
const url = new URL(req.url)
if (url.pathname === "/__lr") {
if (server.upgrade(req)) return undefined as unknown as Response
return new Response("expected websocket", { status: 426 })
}

const rel = url.pathname === "/" ? "/index.html" : url.pathname
const file = Bun.file(path.join(root, rel))
if (!(await file.exists())) return new Response("not found", { status: 404 })

const ext = path.extname(rel)
if (ext === ".html") {
const html = (await file.text()).replace("</body>", `${LIVE_RELOAD}</body>`)
return new Response(html, { headers: { "content-type": MIME[".html"] } })
}
return new Response(file, { headers: { "content-type": MIME[ext] ?? "application/octet-stream" } })
},
websocket: {
open(ws) {
ws.subscribe("lr")
},
message() {},
close(ws) {
ws.unsubscribe("lr")
},
},
})

let debounce: ReturnType<typeof setTimeout> | undefined
watch(root, { recursive: true }, () => {
clearTimeout(debounce)
debounce = setTimeout(() => server.publish("lr", "reload"), 80)
})

const fixtureDir = import.meta.dir
process.stdout.write(
[
``,
` Design canvas → http://localhost:${port}`,
``,
` In another terminal, from the repo root, run:`,
``,
` ./bin/kilodev design --voice local --url http://localhost:${port} --dir ${fixtureDir}`,
``,
` (use --voice fake to drive it by typing instead of talking)`,
``,
` Watching ${path.relative(process.cwd(), root) || root} for changes — edits hot-reload.`,
``,
].join("\n") + "\n",
)
4 changes: 4 additions & 0 deletions packages/kilo-voice-sidecar/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.build/
.swiftpm/
*.xcodeproj
DerivedData/
28 changes: 28 additions & 0 deletions packages/kilo-voice-sidecar/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// swift-tools-version:5.9
import PackageDescription

let package = Package(
name: "kilo-voice-sidecar",
platforms: [
.macOS(.v13)
],
targets: [
.executableTarget(
name: "kilo-voice-sidecar",
path: "Sources/kilo-voice-sidecar",
exclude: ["Info.plist"],
linkerSettings: [
// Embed an Info.plist into the binary so macOS sees the
// microphone / speech-recognition usage strings. Without this a
// SwiftPM CLI has no usage descriptions and the TCC permission
// request never resolves (the mic silently fails).
.unsafeFlags([
"-Xlinker", "-sectcreate",
"-Xlinker", "__TEXT",
"-Xlinker", "__info_plist",
"-Xlinker", "Sources/kilo-voice-sidecar/Info.plist",
])
]
)
]
)
16 changes: 16 additions & 0 deletions packages/kilo-voice-sidecar/Sources/kilo-voice-sidecar/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>ai.kilo.voice-sidecar</string>
<key>CFBundleName</key>
<string>kilo-voice-sidecar</string>
<key>CFBundleDisplayName</key>
<string>Kilo Voice Sidecar</string>
<key>NSMicrophoneUsageDescription</key>
<string>Kilo Design Mode uses the microphone so you can steer design changes by voice.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>Kilo Design Mode transcribes your speech to drive live design edits.</string>
</dict>
</plist>
Loading