Kotoba

Where to start

What Kotoba isWho she is and where she runs, in one page
InstallingOne package, two commands
First runA key, a model, her first words
The approval gateHow she asks before she acts
The two voice modesLocal voice, or the agent tunnel
The soul fileChange who she is
RoadmapWhat grows next, and what was cut on purpose

Or a section

↑↓ move openesc close124 pages
All pages

The web app3 of 12

The main screen

About 6 minutes to read

On this page

/ redirects to /app, and /app is the whole product. There is no landing page in this repository: opening Kotoba in a browser means opening the companion (app/page.tsx:1-14).

Screenshot placeholder: /app mid-call — the wordmark top left, a green LIVE badge top right, the rounded stage card in the middle with her face on the room backdrop and a "Kotoba" name plate in its bottom-left corner, and the row of round sticker buttons under it with the status pill above them.

What is stacked, and in what order

She is drawn inside one rounded card — the "stage" — and everything else orbits it.

LayerWhat it isWhere
Backdropa still PNG of her room, plus a slow warm-light drift and fourteen drifting dust motescomponents/RoomBackdrop.tsx
Avatarthe Live2D model on a PixiJS WebGL canvas, pointer-events: nonecomponents/Live2DCanvas.tsx:87-89
Stage chipsthe working… pill, the name plate, the notice pill, the "calling…" veilCompanionExperience.tsx:715-841
Dockthe status pill and the round control buttons, below the card, not over itcomponents/UIOverlay.tsx

Two more surfaces are portalled to <body> rather than drawn inside the card, because the stage has a CSS transform on it and a position: fixed child inside a transformed ancestor is trapped: the approval card (components/panels/InputRequestPanel.tsx:43-46, 156-157) and the file viewer (components/panels/FilesPanel.tsx:162-163, 224).

The Live2D runtime is loaded in the browser only. /app renders the whole experience with ssr: false, because it touches WebGL, window, and a client-only voice provider (app/app/page.tsx:1-8, 23-25).

Before she is drawn

A full-screen loader covers /app while the model loads. It comes down when the model reports ready, and it comes down instantly — with no "Ready!" fade — when the model failed or when no model is installed at all (components/CallLoader.tsx:1-10, 51, CompanionExperience.tsx:626-628). The loader eats every click, so a model that will never load must not leave it up.

The badge and the status line

Both read from one decider, so they cannot contradict each other or the notice pill (lib/call-status.ts).

The badge, top right (lib/call-status.ts:27-30):

BadgeMeaning
OFFLINEno call
RINGINGconnecting
LIVEconnected
VOICE DOWNconnected, but voice is broken — the session is alive and typing still works

The status pill above the controls (lib/call-status.ts:34-45), in precedence order:

  1. Focused — working… — work talks over everything.
  2. Tap the mic to call Kotoba
  3. Calling…
  4. Mic blocked — she can't hear you, but you can type
  5. Voice is down — she can't hear you, but you can type
  6. You're muted — she can't hear you
  7. Her voice is down — talk or type, captions still work
  8. You're connected — just talk

Losing her hearing outranks mute, because unmuting would fix nothing. Mute outranks losing her speech, because mute is the half you can act on.

The controls

Before a call there are two buttons (UIOverlay.tsx:145-154): Call Kotoba and Settings. Settings is reachable before a call on purpose — configure her, then call.

During a call the row is (UIOverlay.tsx:156-198):

ButtonLabel in the markupNotes
TerminalToggle terminalpulses while she is working and the panel is shut; green dot when there are rows to read
FilesToggle filesyellow dot when a file is new or edited and unopened
ReportToggle reportonly rendered when a report exists; pulses until opened
TranscriptToggle transcript
SettingsSettings
MicMute microphone / Unmute microphonea real mute — the call stays live. While ringing it is a disabled spinner labelled Connecting.
LeaveLeave callhangs up

Mute and Leave are separate controls, divided from the panel buttons by a vertical rule.

How the panels share the space

Two classes of panel (lib/store.ts:178, 297-312):

  • Small: transcript, terminal, files. They can be open together. Terminal and Files share one fixed-width rail on the left of the stage and split its height between them; the transcript opens on the right.
  • Big: report and settings. Opening one closes everything else, and only one big panel can be open at a time.

The stage card shrinks as panels open, so she is never pushed off-screen (CompanionExperience.tsx:684-694). Widths, from the source: the left rail is min(30vw, 340px), the transcript min(34vw, 360px), Settings min(38vw, 480px), the report min(52vw, 680px).

The notice pill

One notice surface, top right of the stage, so there is never a second visual language for "something is wrong". Which failure gets to use it is decided in one place (lib/notices.ts:78-91), in this order:

  1. The events channel is down. Approval cards ride that channel, so while it is down nothing else on screen can be trusted: "Can't reach the backend right now — retrying. Approvals and live progress are paused until it's back."
  2. Voice. A call that cannot hear or speak is what a person notices first.
  3. No model installed. Names the folder the backend actually reads and the route back to the face step. Calls, chat and work all still run.
  4. The model failed to load. Distinct from absence: something that is installed failed to fetch or parse.

A fatal notice is red, the rest are yellow, and a notice with a dismiss control shows a × (CompanionExperience.tsx:766-808).

The stage chips

  • working… — a purple pill, top left of the stage, whenever a background job is running (CompanionExperience.tsx:715-740).
  • Name plate — bottom left, a dot that pulses green while she is speaking, beside the word Kotoba (CompanionExperience.tsx:741-764, 899-901).
  • calling… — a dark veil with a red pill over the whole stage while ringing (CompanionExperience.tsx:810-841), and the card itself shakes (CompanionExperience.tsx:696).

The call shell writes the name Kotoba literally. If you renamed her during setup or in Settings → Personality, the name plate, the transcript's speaker label, the "Call Kotoba" button and the "Tap the mic to call Kotoba" status line all keep saying Kotoba (CompanionExperience.tsx:763, TranscriptPanel.tsx:229, UIOverlay.tsx:147, lib/call-status.ts:36). The rename itself is real — it is written to the backend and it is what she is told she is called, and the setup wizard's own screens use it. This is the shell's labels, not her.

Helper chibis and the plan tab

Two more fixed surfaces appear only when there is something to show: the row of helper chibis in the bottom-left corner when she delegates work (components/SubagentChibis.tsx), and the plan tab in the bottom-right corner when a work run has a task list (components/panels/TaskTab.tsx). Both are described in The plan and the helpers.

What ending a call clears

Leaving a call, or losing the connection, wipes the terminal rows, the emotion, the report title, any open input card, the helper chibis and the plan (lib/store.ts:293-296). Files are deliberately kept — they are on disk. The face is reset to neutral, because a call that ended on sad used to open the next one still wearing it.