Skip to content
Merged
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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,15 @@ npm run tauri dev

右の参加者パネルは**一つのリスト**です。部屋にいる参加者と、まだ起動していないアカウント(「未起動」)が、種別ごとのグループに分かれて並びます(`user — 1` / `AI — 2` のように、件数も出ます)。名前はその参加者の色で出ます。居ない相手は色を保ったまま薄くなり、点が塗りから輪郭に変わります。自分の行には「(あなた)」が付きます。

端末を持っているアカウントの行を押すと、その端末が診断面に出ます。行の「終了」でそのアカウントのセッションを終了できます。終了は二度押しです——一度目でボタンが「本当に終了」に変わり、数秒で戻ります。未起動のアカウントの行に端末の操作は出ません。
端末を持っているアカウントの行を押すと、その端末が診断面に出ます。

行の右側に操作が二つ並びます。**一つ目がセッションの開始と終了、二つ目が編集です。** 位置は行の状態で変わりません。種別 `AI` のアカウントには、未起動なら「開始」、稼働中なら「終了」が同じ場所に出ます。種別 `user`(人間)のアカウントは起動しないので、一つ目は空のままです——その行の「編集」も他の行と同じ位置に並びます。

「開始」は一度押しです。「終了」は二度押しで、一度目でボタンが「本当に終了」に変わり、数秒で戻ります。取り消せない操作だけが二度押しです。

「開始」を押してから起動が返るまで、その行の「開始」は押せなくなり、名前の隣に「起動中」と出ます。起動に失敗した場合は「起動失敗」と出て、理由はマウスを乗せると読めます(全文は画面下の status 行に出ます)。この表示は、次にそのアカウントの「開始」を押すまで残ります。

未起動のアカウントの行に端末の操作は出ません。

リストの下にセッションの裏の値(部屋ソケット、セッションの生死、接続方法、起動コマンド、作業ディレクトリ、開始時刻、ウィンドウ)が出ます。部屋ソケット以外は、選んでいる端末のアカウントのものです。宛先に選べるのは部屋に居る参加者だけで、未起動のアカウントは出ません。

Expand All @@ -78,7 +86,7 @@ npm run tauri dev

この端末は表示と操作のためのものです。ここに映る内容が部屋の発言になることはありません。部屋に並ぶのは channel 経由の発言だけです。

アカウントを選び「セッション参加」を押すと、次の 2 つが行われます。
アカウントの行の「開始」を押すと、次の 2 つが行われます。

1. そのアカウントの作業ディレクトリの `.mcp.json` へ、部屋のサイドカーを `pullcept-room-<slug>-<hash>` という名前で登録します。**既存の内容はマージして保持します**が、あなたのリポジトリのファイルを書き換える操作です。登録鍵はアカウント id から決まります。アカウントごとに分けているのは同じ作業ディレクトリへ 2 つのセッションを立てたときに互いの登録を潰さないためで、名前ではなく id から導くのは改名で鍵が動かないようにするためです。前回の起動で書かれたエントリ(もう繋がらないポートを指しているもの)は、このとき掃除します。
2. `--dangerously-load-development-channels server:pullcept-room-<slug>-<hash>` を付けて CLI を PTY 上の対話セッションとして起動します。
Expand Down
47 changes: 41 additions & 6 deletions docs/0-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,32 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre

**行の見え方。** 名前はその参加者の色で表示する。オフラインの行は色を保ったまま薄くし、点を塗りから輪郭へ変える。灰色に落として色を捨てない——色はアカウントの同一性を担っており、居ないだけで別人に見えるのは誤りである。行が示す補足は、この画面の本人なら「(あなた)」、席の無いアカウントなら「未起動」、セッションが終了して出力だけ残っているなら「終了」である。アバターは持たない。

**行にアカウントの操作が乗る。** 端末を持つアカウントの行は、クリックで診断面の表示をその端末へ切り替える。行には「編集」(下記のモーダルを開く)が付き、稼働中であれば「終了」が付く。行の鍵がアカウント id であることが、行に操作を載せられる条件である——名前で突き合わせると、二つのアカウントが同じ名前を名乗った時点で別の行に当たる。
**行にアカウントの操作が乗る。** 端末を持つアカウントの行は、クリックで診断面の表示をその端末へ切り替える。行の鍵がアカウント id であることが、行に操作を載せられる条件である——名前で突き合わせると、二つのアカウントが同じ名前を名乗った時点で別の行に当たる。

端末を持たないアカウントに端末の操作は出ない。未起動のアカウントに出せる端末の操作は無い(#57 の条件を維持)。
**セッションの開始と終了は、同じ列の同じ場所にある。** 一つのライフサイクルの両端であり、離して置く理由が無い。終了が行に来たのは #57 だが、開始は会話面の上のランチャ行に残っていた(#62 で移した。下記「ランチャ行は無い」)。

**行の操作は名前の後ろの二列である。一列目がライフサイクル、二列目が編集。** 列は行の状態で動かない。以前の並びは「編集」「終了」であり、終了が稼働中の行にしか出ないため、未起動の行では編集が左へずれていた。

- 一列目 = 「開始」または「終了」。種別 `ai` のアカウントの行にだけ出る。
- 二列目 = 「編集」。アカウントを持つ行には常に出る。
- **列は中身の有無に依らず幅を持つ。** ライフサイクルを持たない行——種別 `user`、および端末の操作を持たない行——も列を閉じない。閉じれば編集がその行だけ左へ寄る。

armed の「本当に終了」は「終了」より広く、その幅は名前の側へ伸びる。編集の位置は動かない。ライフサイクルの列を armed の幅に固定する形は採らない——パネルは 16.5rem しかなく、実測では本人の行の名前(「マスター」+「(あなた)」)が出なくなる。四秒間の一行のために、リスト全行の名前の幅を払うことになる。

端末を持たないアカウントに端末の操作は出ない。未起動のアカウントに出せる端末の操作は無い(#57 の条件を維持)。開始はこれに含まれない——端末の操作ではなく、端末を作る操作である。

**種別 `user` の行にライフサイクルは無い。** 人間は起動されるものではなく、`start_session` が既に拒否している(上記「アカウント」)。列は空のまま残り、その行の編集は他の行と同じ位置に並ぶ。

**開始は 2 段階にしない。** 終了が二度押しなのは取り消せないためである(下記「診断面」)。開始は取り消せる——間違えて始めたセッションは、その場所を引き継ぐ終了が終わらせる。取り消せる操作に arm を付けると、undo が効く失敗を防ぐために全ての意図的な操作が二度押しになる。

**起動中と起動失敗は行が読める。** 押した直後から起動が返るまで、その行の開始は押せなくなる。状態の語は名前の隣の note に入る——「未起動」「終了」と同じ場所であり、note は状態を、ボタンは行為を言う。

| note | 行の状態 |
|---|---|
| 起動中 | 開始を押し、起動がまだ返っていない |
| 起動失敗 | 直前の起動が失敗した。理由は `title`、全文は status 行 |

起動に失敗した端末は破棄される(下記「診断面」)。行に残さなければ、押したこと自体が画面から消え、行は押される前と同じ「未起動」へ戻る。この記録は次にそのアカウントの開始が押された時点で消える——失敗が消えるのは、同じ問いがもう一度立てられた時である。

**宛先の選択肢は増えていない。** オフラインの行は部屋の名簿に居ないため、宛先には出ない。届かない相手を名指せる状態は、宛先を名簿から引くという判断(#43)の理由に反する。

Expand Down Expand Up @@ -380,7 +403,7 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre

実際に起動する行は画面に表示する。書いた行と走る行が違う以上、結果を見せるほうが説明より安い。表示は起動オプションの欄の隣、つまりモーダルの中に置く。編集している欄の隣であることが条件であり、欄が移れば表示も移る。名前の欄を編集しても表示は動かない。エントリ名がアカウント id から決まるためであり、これは要点である——起動しているのはアカウントであって、改名はそれを別のものにしない。

表示は省略記号で切らずに折り返す。切れる末尾はアプリが統合した channel エントリであり、そこが「書いた行と走る行が違う」ことの中身である。一行のランチャ行には縦の余裕が無かったが、モーダルにはある。
表示は省略記号で切らずに折り返す。切れる末尾はアプリが統合した channel エントリであり、そこが「書いた行と走る行が違う」ことの中身である。一行のランチャ行には縦の余裕が無かったが、モーダルにはある(ランチャ行そのものは #62 で無くなった。下記「ランチャ行は無い」)

**実測済み(2026-08-21)**: 複数の channel サーバを同時に有効化した状態で往復が成立した。`--print` での確認は無意味である(channel の読み込み自体が走らないため)ことも実測しており、確認は対話セッションでのみ可能。

Expand Down Expand Up @@ -408,7 +431,7 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre

本人のアカウントは削除できず、種別も `user` から変えられない。部屋に居ることが「ここに居る」ことそのものであり、削除すれば居るための何かが無くなる。種別を変えれば、本人が AI の見出しの下に並び、その名前で CLI を起動する選択肢が出る。

種別 `user` のアカウントは起動しない。人間の下に spawn する CLI は無く、`start_session` が拒否する。ランチャの選択肢にも出さない——拒否が唯一の知る手段になるのは、拒否の使い方として誤りである。稼働中のアカウントの種別は変更できない。そのセッションが起動した作業ディレクトリと起動オプションを、動いている最中に捨てることになるためである。
種別 `user` のアカウントは起動しない。人間の下に spawn する CLI は無く、`start_session` が拒否する。その行に開始も出さない——拒否が唯一の知る手段になるのは、拒否の使い方として誤りである。稼働中のアカウントの種別は変更できない。そのセッションが起動した作業ディレクトリと起動オプションを、動いている最中に捨てることになるためである。

**名前は人間が読んで選ぶものである。** UUID などの機械的な一意化で置き換えない。宛先の選択肢に出るのはこの名前であり、そこから 1 名を選べることが目的である。id が機械的な一意性を担うのは、名前をその責務から解放するためである。新規アカウントの既定名は既存と重ならない番号を取る——既定値が同じ名前の参加者を並べるのでは、#40 の欠陥を既定で再現することになる。

Expand All @@ -428,7 +451,19 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre

削除はモーダルの中にあり、終了と同じ 2 クリックを取る。一度目は武装するだけで、二度目が実行する。稼働中のアカウントと、この画面の本人のアカウントは削除を拒否する。

**ランチャ行に残すのはアカウントの選択と参加のみである。** 「どのアカウントで参加するか」と「このアカウントの設定を編集する」は別の行為であり、種別が増えたことで分ける理由がさらに増えた。名前・色・作業ディレクトリ・起動オプションはモーダルにある。タイトルバーが持っていた本人の名前と色の欄も外した——本人がアカウントになった以上、同じ 2 値を編集する面が二つ並ぶことになる。`+` はアカウントが並ぶリストの見出しの隣に置く。作るものが現れる場所の隣であって、起動する行の上ではない。
#### ランチャ行は無い

会話面の上に、アカウントの選択と参加だけを持つ行があった。#59 が名前・色・作業ディレクトリ・起動オプションをモーダルへ移した結果、残ったのは `<select>` と「セッション参加」の二つである。#62 で開始が参加者の行へ移り、この行に残る仕事は無くなった。

選択のためだけに残さない。**どのアカウントを起動するかは、押された行が答える。** 選択という行為そのものが不要になっており、選択肢を出す面を残せば、行と select という二つの「どのアカウントか」が並ぶことになる。

削除して失うものが無いことは参照から確認できる。`accountEl.value` を読むのは `selectedAccount`(開始の呼び出し元)と `renderAccountOptions`(その select 自身の再描画)だけであり、いずれもランチャ内部で閉じていた。パネル下部のセッションの値は選択ではなく画面に出ている端末に従う(上記「参加者パネル」)。

ランチャのボタンが持っていた起動中の `disabled` と、その失敗の表示は、行が引き継ぐ(上記「参加者パネル」)。ボタンは押された行のものになり、どのアカウントが起動しているかを言えるようになった——一つしかないボタンには言えなかったことである。

削除すると会話面が縦に広がる。

`+` はアカウントが並ぶリストの見出しの隣に置く。作るものが現れる場所の隣である。タイトルバーが持っていた本人の名前と色の欄も外した(#59)——本人がアカウントになった以上、同じ 2 値を編集する面が二つ並ぶことになる。

#### 一つのアカウントは一つの部屋に一席まで

Expand Down Expand Up @@ -497,7 +532,7 @@ CI が実行するもの:

2 枚のアートボードを並べ、その差分をロードマップとして読む形にしてある。

- **次の一手** — 現在の実装に参加者パネルと宛先を足した、実装可能な範囲。両方とも実装済みである。この 1 枚と実装の差は、操作ボタンのうち未実装のリサイズとミュート(端末と終了は実装済みで、参加者のリストの各行に載っている)、宛先の見た目(削除可能なチップ)、そして #53 / #57 / #59 で入ったアカウントまわり(種別ごとのグループ、行の編集と終了、モーダル、ランチャ行の縮小)である。宛先は `<select>` で機能的に等価であり、形の差は追っていない。アカウントの側はキャンバスが実装より大きく遅れており、差は #46 で追う。
- **次の一手** — 現在の実装に参加者パネルと宛先を足した、実装可能な範囲。両方とも実装済みである。この 1 枚と実装の差は、操作ボタンのうち未実装のリサイズとミュート(端末と終了は実装済みで、参加者のリストの各行に載っている)、宛先の見た目(削除可能なチップ)、そして #53 / #57 / #59 / #62 で入ったアカウントまわり(種別ごとのグループ、行の開始・終了・編集、モーダル、ランチャ行の廃止)である。宛先は `<select>` で機能的に等価であり、形の差は追っていない。アカウントの側はキャンバスが実装より大きく遅れており、差は #46 で追う。
- **全体像** — ルーム一覧 / ダイレクト / 参加者・セッション状態パネル

色と寸法は `src/styles.css` の実値をそのまま使っている(`--accent: #3a6ea5` / `--surface: #f4f4f7` / 角丸 4・6px / system-ui + Yu Gothic UI)。参加者ごとの色は accent と同じ彩度・明度で色相だけ変えたもの。絵と実装が同じトークンの上に乗るため、キャンバスの値をそのまま実装へ移せる。
Expand Down
18 changes: 7 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@
<button id="toggle-diagnostics" type="button" aria-expanded="false">診断</button>
</header>

<!-- Which account joins, and the join. Nothing else: choosing who to
join as and editing what that account is are two acts, and the row
that did both had to grow a field per attribute (#59). The
attributes are in the dialog; making an account is the + beside the
list it appears in. -->
<div id="launcher">
<select id="account-select" aria-label="参加するアカウント"></select>
<button id="start-session" type="button">セッション参加</button>
</div>

<div id="body">
<div id="conversation">
<main id="room" aria-live="polite"></main>
Expand Down Expand Up @@ -92,7 +82,13 @@
Grouped by the kind declared when the account was made. An account
that is not running is still someone, so it is listed dimmed rather
than left out (#53); it is still not offered as an addressee,
because a name that cannot be reached is not one worth naming. -->
because a name that cannot be reached is not one worth naming.

The row carries the session's whole lifecycle. 開始 was on a row of
its own above the conversation until #62, which put the two ends of
one thing on two surfaces; 終了 had already moved here (#57). The
row is two fixed columns after the name — the lifecycle, then 編集 —
so neither moves when a session starts or ends. -->
<aside id="participants" aria-label="参加者">
<div class="panel-head">
<h2 class="panel-title">参加者</h2>
Expand Down
Loading