docs: consolidate design documentation and standardize crate English
This commit is contained in:
+6
-57
@@ -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.
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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), "日本語");
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user