docs: consolidate design documentation and standardize crate English

This commit is contained in:
2026-10-12 14:58:00 +09:00
parent a633a53274
commit 4efb0ace1f
404 changed files with 4556 additions and 11654 deletions
+6 -57
View File
@@ -1,61 +1,10 @@
# tui
## Role
`tui` is a terminal client for a single Worker's Console and the Workspace Dashboard. It handles input editing, display state, and connection-specific screen transitions. It does not own Session persistence or Worker lifecycle decisions.
`tui` implements terminal UI clients for the single-Worker Console and workspace Dashboard surfaces.
## Implementation entry points
## Boundaries
Owns:
- terminal rendering and input handling
- local composer state and UI affordances
- single-Worker Console attach/restore/chat screens
- workspace Dashboard presentation and role-action UI
Does not own:
- durable transcript authority (`session-store`)
- Worker current state (`session-store` worker metadata)
- Worker lifecycle policy (`worker`)
- product CLI ownership (`yoi`)
## Design notes
The TUI should display committed events and Worker snapshots rather than inventing durable state. Local input history and optimistic UI affordances are editing conveniences; they must not become hidden model context.
## See also
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
- [`../../docs/design/worker-session-state.md`](../../docs/design/worker-session-state.md)
## Feature invocation editing
`/` discovers enabled Feature declarations with descriptions and usage. Choose a
name or alias with Tab/Enter, complete its parenthesized arguments, and press
Enter to confirm that selected occurrence as a typed chip. A later Enter sends
the message. Raw slash text and paste content are never dispatched as commands.
Use Alt+Enter beside a Feature chip to rediscover its declaration and reopen its
arguments; Backspace/Delete removes a chip atomically. Typed chips survive local
history, draft restoration, transport retry, and rewind.
Every completion and chip-re-edit query carries a fresh `request_id`. Replies
must echo that ID and match the active query context and Worker target. The first
input/cursor edit cancels the pending input watch; returning to identical text
cannot revive it. Authority snapshots and Worker/view switches discard pending
queries and visible candidates, rather than advancing another counter.
Uncorrelated replies are ignored, even for File queries where the wire field
remains optional for legacy callers.
The declared attachment adapter stages a client-local file in place, replacing
its staging chip with an `UploadedFile` reference when ready. Local file argument
completion runs on the client, not Worker. Alt+Enter beside a failed staging chip
retries with the same upload ID; deleting an unsent chip cancels only its owned
staging resource. Accepted, restored, or history attachments are never deleted by
composer cleanup. Staging blocks Send/Notify and history navigation until it is
ready, retried, or removed. There is no raw `/attach` or `/clear-attachments`
command lane.
Attachment staging remains Backend-only: the existing Standalone transport has
no client-local file-upload staging API. The TUI reports that limitation and
preserves the draft invocation; it never falls back to raw path dispatch.
- [`launch`] / [`LaunchMode`] — typed launch requests and terminal restoration.
- `src/app.rs`: display state built from protocol snapshots and events.
- `src/input.rs` / `src/composer_history.rs`: typed input and local editing history.
- `src/console/`: connection, input-event, and attachment-staging management.
+8
View File
@@ -1,3 +1,11 @@
//! Builds terminal display state from Worker protocol snapshots and events.
//!
//! WorkerStateSnapshot updates current run state; connection state and streaming
//! display do not replace it. Input and run control still target the parent Worker
//! when a SubWorker view is selected. Completion replies apply only when request_id,
//! input watch, connection target, and displayed target match. Editing cancels the
//! watch, so returning to the same text does not revive stale replies.
use std::collections::{HashMap, VecDeque};
use std::path::Path;
use std::time::{Duration, Instant};
+6
View File
@@ -1,3 +1,9 @@
//! Stores per-Workspace local input history as typed Segments.
//!
//! This history supports editing reuse; it is not a Worker's durable Session or model
//! context. Feature invocations and UploadedFile references stay typed during re-editing
//! rather than reverting to strings.
use std::collections::VecDeque;
use std::fs;
use std::io;
+8
View File
@@ -1,3 +1,11 @@
//! Manages connection-specific Console loops and client-local attachment staging.
//!
//! Staging uses Backend transport; completion replaces the corresponding input chip
//! with an UploadedFile reference. Connections own only unsent resources. Deleting
//! drafts does not remove accepted, restored, or historical attachments. Standalone
//! has no upload API, so it retains typed drafts and reports the unsupported operation
//! rather than falling back to sending raw paths.
use std::collections::HashMap;
use std::fmt;
use std::future::Future;
+11 -17
View File
@@ -1,15 +1,9 @@
//! Multi-line input buffer with paste placeholders.
//! Multiline input buffer storing characters and typed references as an Atom sequence.
//!
//! The buffer stores a sequence of [`Atom`]s — each either a single
//! character (including `\n`) or an atomic paste reference. The cursor
//! is an index in `0..=atoms.len()` marking the insertion point between
//! atoms. Paste atoms are indivisible: Backspace deletes the whole
//! placeholder, the cursor can't land "inside" one.
//!
//! Display form: paste atoms render as
//! `[Clipboard #N | X chars, Y lines]`. Submit form: paste atoms expand
//! back to their original captured content so the Worker sees the full
//! pasted text (without the placeholder label).
//! Paste, Feature invocation, and attachment chips are indivisible; the cursor sits
//! between Atoms. Display placeholders are separate from outgoing Segments; pasted
//! content expands to its original body on send. Raw slash text and paste are not
//! inferred to be Feature calls; only confirmed invocations are sent as typed input.
use std::{
cell::RefCell,
@@ -985,7 +979,7 @@ impl InputBuffer {
}
}
/// Delete one word backward — the same span [`move_word_left`] would
/// Delete one word backward — the same span [`Self::move_word_left`] would
/// jump over.
pub fn delete_word_before(&mut self) {
let end = self.cursor;
@@ -1021,7 +1015,7 @@ impl InputBuffer {
}
}
/// Move forward by one word. Mirror of [`move_word_left`].
/// Move forward by one word. Mirror of [`Self::move_word_left`].
pub fn move_word_right(&mut self) {
self.cancel_completion_on_edit();
while self.cursor < self.atoms.len()
@@ -2050,7 +2044,7 @@ mod word_motion_tests {
#[test]
fn script_switch_is_a_word_boundary() {
// 漢字 | ひらがな | ASCII
// Han | Hiragana | ASCII
let mut buf = buf_from("日本語のtest");
buf.cursor = 0;
buf.move_word_right();
@@ -2070,7 +2064,7 @@ mod word_motion_tests {
#[test]
fn halfwidth_katakana_is_treated_as_katakana() {
// 半角カナ「アイウエオ」は5 atom、すべて Katakana 種別。
// Halfwidth kana "アイウエオ" has 5 atoms, all of kind Katakana.
let mut buf = buf_from("アイウエオfoo");
buf.cursor = 0;
buf.move_word_right();
@@ -2078,7 +2072,7 @@ mod word_motion_tests {
buf.move_word_right();
assert_eq!(cursor(&buf), 8); // after "foo"
// 全角と半角のカタカナは同じ Katakana 種別なので1単語につながる。
// Fullwidth and halfwidth katakana share kind Katakana, forming one word.
let mut buf2 = buf_from("カタカナ");
buf2.cursor = 0;
buf2.move_word_right();
@@ -2154,7 +2148,7 @@ mod word_motion_tests {
#[test]
fn delete_word_respects_script_boundary() {
// 「日本語の」末尾から1回削除すると、ひらがな部分「の」だけ消える
// One deletion from the end of "日本語の" removes only the hiragana "の".
let mut buf = buf_from("日本語の");
buf.delete_word_before();
assert_eq!(as_text(&buf), "日本語");
+6
View File
@@ -1,3 +1,5 @@
#![doc = include_str!("../README.md")]
mod app;
mod backend_dashboard;
mod backend_spawn;
@@ -129,6 +131,10 @@ impl Drop for TerminalModeGuard {
}
}
/// Opens Console or Dashboard from a typed launch request.
///
/// Restores raw mode and the alternate screen on both success and error.
/// Emits exit diagnostics after screen restoration, not inside the alternate screen.
pub async fn launch(options: LaunchOptions) -> ExitCode {
let LaunchOptions {
target,