Skip to content

1. The Shell

A page that boots a Kitsune shell, installs the debug module, and prints a diagnostic line for every event and command. Nothing visible yet — we want to see the runtime is alive.

Replace Vite’s default index.html with:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Quill</title>
<link rel="stylesheet" href="./src/quill.css" />
</head>
<body>
<kit-shell name="quill">
<kit-boundary surface="app">
<header>
<h1>Quill</h1>
</header>
<main id="main">
<p>Click anywhere — diagnostics will print to the console.</p>
</main>
</kit-boundary>
<kit-toast-region></kit-toast-region>
</kit-shell>
<script type="module" src="./src/main.ts"></script>
</body>
</html>

Two things stand out:

  • <kit-shell name="quill"> is the application root.
  • <kit-boundary surface="app"> wraps everything. Anything that emits an event from inside it carries surface: 'app' in context.
import '@atheory-ai/kitsune-app'
import '@atheory-ai/kitsune-ui'
import { debugModule } from '@atheory-ai/kitsune-dev'
import { dialogModule, notificationModule } from '@atheory-ai/kitsune-ui'
await customElements.whenDefined('kit-shell')
const shell = document.querySelector('kit-shell')!
shell.modules = [
notificationModule(),
dialogModule(),
debugModule(),
]

Three modules installed:

  • notificationModule() — handles notification.show commands by rendering toasts in <kit-toast-region>.
  • dialogModule() — handles dialog.open and dialog.close commands by id.
  • debugModule() — logs every event and every diagnostic to the console.
:root {
color-scheme: light dark;
--quill-max-width: 64rem;
--quill-pad: 1.25rem;
}
body {
font-family: system-ui, sans-serif;
margin: 0;
background: light-dark(white, oklch(20% 0 250));
color: light-dark(oklch(20% 0 250), oklch(95% 0 0));
}
kit-shell { display: block; }
header {
border-block-end: 1px solid light-dark(oklch(85% 0 0), oklch(30% 0 0));
padding: var(--quill-pad);
}
header h1 { font-size: 1.25rem; margin: 0; }
main {
margin-inline: auto;
max-inline-size: var(--quill-max-width);
padding: var(--quill-pad);
}

color-scheme: light dark plus light-dark() gives Quill automatic dark mode. No JavaScript, no theme switcher.

Open pnpm dev and the browser console. You’ll see lines like:

[kit:diagnostic] command.handler_registered { commandType: 'notification.show' }
[kit:diagnostic] command.handler_registered { commandType: 'dialog.open' }
[kit:diagnostic] module.installed { moduleName: 'kit-notifications' }
[kit:diagnostic] module.installed { moduleName: 'kit-dialog' }
[kit:diagnostic] module.installed { moduleName: 'debug' }
[kit:diagnostic] runtime.started

The runtime is up. No UI to interact with yet, but the plumbing is real.

A list of notes. We’ll wire up boundaries that name each note, so analytics, audit, and any future capability know which note is being interacted with.

Next: Chapter 2 — Boundaries and the List →