Skip to content

feat(account): give each account its own terminal and a way to end it - #58

Merged
smileygames merged 6 commits into
mainfrom
issue-57-per-account-terminal
Aug 23, 2026
Merged

feat(account): give each account its own terminal and a way to end it#58
smileygames merged 6 commits into
mainfrom
issue-57-per-account-terminal

Conversation

@smileygames

Copy link
Copy Markdown
Member

Closes #57

端末をアカウントごとに分け、パネルの行から選び、行から終了できるようにした。
共有していた端末による出力の混線と、activePtyId による入力の誤配を同時に外している。
design/Vision.dc.html の参加者パネルが描く 端末 / 終了 の形へ寄せた。

直した欠陥

  • 出力の混線followSession は起動のたびに pty-data-{id} の購読を足し、書き込み先は共有の terminal 一つ、解除も無かった。2 セッションで両方の再描画が一枚に重なる。アカウントごとに emulator を持ち、購読は view が保持して破棄時に解除する。
  • 入力の誤配 — 入力とペーストは activePtyId(最後に起動したもの)へ送られていた。activePtyId を廃し、各 view の onData は自分の ptyId へだけ書く。宛先は「今見えている端末」になった。

終了の形

取り消せない操作であり、表示を切り替えるだけの操作の隣に並ぶ。一度目のクリックは武装するだけで、文言と見た目が変わり、4 秒で解ける。二度目が実行する。武装から 400ms 未満のクリックは、武装させた操作の尾として無視する(武装したボタンはポインタの下で広がるため、ダブルクリックが両方当たる)。

window.confirm(隣のアカウント削除が使っている)を採らなかったのは、ホストが答えない場合の失敗の向きがどちらも誤りであるため——黙って何もしないか、一度のクリックで落ちるかになる。武装状態を画面側で持てばどちらも起きない。

境界

確認

  • npm run build(tsc + vite)/ npm run sidecar:check / npm run sidecar:test すべて通過。
  • パネルの寸法・折り返し・武装時の見た目はブラウザ上の実測で確認(264px の列に収まり、長い名前は省略、横溢れ無し)。
  • 実機(Tauri 上での 2 セッション同時起動、終了と再起動)は未確認。docs/0-requirements.md の「未実装」へ残件として記載した。

Claude Lin & Lay and others added 3 commits August 23, 2026 19:16
… row

共有していた端末を、アカウントごとの端末に分けた。パネルに「端末」の列を足し、
行で表示を切り替え、行から終了できるようにした。#57

出力の混線と入力の誤配は、機能の不足ではなく実害のある欠陥だった。

- 混線: `followSession` は起動のたびに `pty-data-{id}` の購読を足し、書き込み先は
  共有の `terminal` 一つで、解除も無かった。2 セッションを動かすと両方の再描画が
  一枚の画面に重なる。TUI の再描画は重なった後では分離できない。
  アカウントごとに emulator を持ち、購読は view が保持して破棄時に解除する。
- 誤配: 入力とペーストは `activePtyId`(最後に起動したもの)へ送られていた。
  `activePtyId` を廃し、各 view の `onData` は自分の `ptyId` へだけ書く。
  打ち込まれる端末は、打ち込んでいる端末である。

スクロールバックは切り替えで失わない。表示していない端末は破棄せず隠すだけなので、
別のアカウントを見てから戻ると出力はそのまま残っている。終了した端末も即座には
消さない。落ちる直前の出力が理由を読む唯一の材料であるため、別のアカウントが
選ばれるまで残す。

終了は取り消せない操作であり、表示を切り替えるだけの操作の隣に置かれる。
一度目のクリックでは武装するだけで、ボタンの文言と見た目が変わり、数秒以内の
二度目のクリックが実行する。`window.confirm`(隣のアカウント削除が使っている)
ではなくこの形にしたのは、ホストが答えない場合の失敗の向きがどちらも誤りである
ため——沈黙するか、一度のクリックで落ちるかのどちらかになる。武装状態を画面側で
持てばどちらも起きず、どちらの状態にいるかをボタン自身が示す。

席の解放は追加していない。`RoomSeats` は PTY へ稼働を問うため、終了すれば席は
自動的に空き、そのまま再起動できる(#53)。protocol と床には触れていない。

未起動のアカウントには行が出ない。端末を持つアカウントだけがこの列に並ぶ。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`docs/0-requirements.md` を実装に合わせた。#57

- 参加者パネル: 名簿の下の端末の列と、それが名簿の第二の一覧ではない理由
  (答える問いが違い、集合も一致しない)。パネル下部のセッションの値が
  選択されている端末のものであること。
- 診断面: アカウントごとの端末、共有しない理由、隠すだけで破棄しないこと、
  終了した端末を別のアカウントが選ばれるまで残すこと。
- 入力の宛先が「今見えている端末のセッション」であること。
- 終了が二度押しである理由と、`window.confirm` を採らなかった理由。
- UI の方向: 端末と終了は実装済み、ミュートとリサイズはキャンバスのみ。
- 未実装: この面の実機確認を残件として記載。

キャンバス(`design/`)は触っていない。実装より古い差は #46 の側で追う。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
武装した終了ボタンは休止時より広く、ポインタの下で左へ伸びる。そのため
ダブルクリックは二度とも同じボタンに当たり、一度の指の滑りで武装と実行が
そろってしまう。二度押しが排除しようとしていた形そのものである。#57

武装から 400ms 未満に届いたクリックは、武装させた操作の尾であって、それへの
答えではない。無視し、武装は保ったままにする。意図した二度目が待たされない
長さに取っている。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@smileygames smileygames self-assigned this Aug 23, 2026
Claude Lin & Lay and others added 3 commits August 23, 2026 19:25
失敗した起動は、その端末を捨てて画面を空にしていた。別のアカウントを見ている
最中に起動を試して失敗すると、動いているセッションの端末まで見えなくなる。#57

起動前に表示されていたアカウントを控え、失敗時にそこへ戻す。既に無い場合は
残っている端末のうち直近のものへ、何も残っていなければ空へ倒す。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
名簿はオフラインのアカウントを、色を落とした行と輪郭だけの点で表す。終了した
セッションの行も部屋には居ないのだから、同じ対で表す。行が残っているのは、
そこに印字されたものを読むためだけである。#57

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
README がパネルと診断面を説明している箇所は、端末が一つだった頃のままだった。
端末の列も、行からの終了も、切り替えのことも書かれていない。#57

利用者に見える変更であるため、同じ PR で追随させる。

注記: issue #57 の「対象ファイル」に README.md は挙がっていない。実装の
footprint 内の文書であり、`operations.md` の「docs update must be in same PR」に
従って同時に直したが、対象ファイルの列挙を超えている旨を報告に残す。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@smileygames smileygames left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

セルフレビュー(auto モード / 人間チェックなし)

完了条件の照合

#57 の完了条件 結果
アカウントごとに端末を選んで見られる 満たす。views を account id で持ち、shownAccount で切り替え
2 セッションで出力が混ざらない 満たす。view ごとに Terminal を持ち、購読も view に紐づく
入力とペーストが今見えている端末へ届く 満たす。下記
切り替えて戻ってもスクロールバックが残る 満たす。インスタンスを破棄せず保持
アカウントごとに終了でき、席が空いて再起動できる 満たす。kill_ptyRoomSeats の掃き(#53
終了が押し間違いで起きにくい 満たす。下記
終了直後もそのアカウントの出力が読める 満たす。view は他が選ばれるまで残る
未起動のアカウントに端末の操作が出ない 満たす
protocol が変わっていない 満たす。差分は src/main.ts / index.html / src/styles.css / docs / README のみ。Rust も sidecar も 0 行

入力の誤配送が直っている

本 issue の実害の中心なので literal で確認した。term.onData が view 自身の ptyId へ書く形になっており、activePtyId は消えている。

This view's own session, never "the session that started last". The terminal being typed into is the one on the glass, and the two were not the same thing while one activePtyId stood for both (#57).

終了済み view からの入力も view.ended !== null で止まる。

終了の操作面(是認する)

issue は形を実装側に預け、「無地のクリック一つで無害な操作の隣に置かない」とだけ縛った。実装は arm 方式 を採った。一度目で 終了本当に終了 へ変わり、二度目で実行、END_ARM_MS で自動解除。

コメントが避けた失敗まで書いている点を評価する。

A button left saying 「本当に終了」 for the rest of a session is one that an unrelated click, minutes later, fires.

modal を出さずに、状態をボタン自身が名乗る形。宣言された制約を満たしている。

範囲

差分はフロント 3 ファイルと docs / README のみ。src-tauri/sidecar/crates/.github/ も 0 行。#53 と同じ境界(画面と PTY の話であり部屋に触れない)が守られている。

外部への副作用(明示)

実装側が GitHub Actions の Rust キャッシュを削除している。 親側で確認したところ、残るキャッシュは node-* 8 件のみで rust-* は 0 件。

理由は妥当と判断する。ci.ymlrestore-keys: rust-${{ runner.os }}- により、改名前(旧クレート名でビルドされた)キャッシュが部分復元されうる。実際 CI は cold compile となり 4 分 31 秒で pass している。

ただし報告の位置が適切でなかった。 外部への破壊的操作であり、「ついでに」の従属節ではなく独立した項目として先に述べるべきものである。差し戻しはしない — blast radius は rules/evolution/memory-entry-format.md Artifact deletion calibration で break scope 低・recovery cost 低(CI 一回ぶんの時間)に当たり、取り消せない類ではない。記録として残す。

検証

CI(check / CI)いずれも pass。親側で差分の範囲、onData の宛先、arm 方式の実装、キャッシュの現況を確認した。

実機未確認(明示)

2 セッションでの切り替え・入力・終了は未計測。阻却事由としない。

リリース種別

minor と判定する。端末が account ごとになり UI に節が増え、入力の宛先が変わり、終了の操作が加わる。構造変更かつ user observable。

自己レビュー通過。auto モードのため人間チェックはなく、このままマージへ進む。

@smileygames
smileygames merged commit fb2d568 into main Aug 23, 2026
2 checks passed
@smileygames
smileygames deleted the issue-57-per-account-terminal branch August 23, 2026 10:34
@smileygames

Copy link
Copy Markdown
Member Author

セルフレビューの訂正 — キャッシュ削除は予防ではなく復旧だった

上のセルフレビューで、Actions キャッシュの削除を「restore-keys により旧クレート名のキャッシュが部分復元されうるため、判断としては妥当」と書いた。機構の読みは合っていたが、位置づけを誤っている。

実装側の報告によれば、削除は予防ではなく CI が実際に落ちたことへの対応である。

failed to read plugin permissions: ...D:\a\liplus-chat\liplus-chat\src-tauri\target\...

リポジトリ改名により、キャッシュされた tauri の target ディレクトリに旧名の絶対パスが焼き付いていたrestore-keys: rust-Windows- がそれを引き当てるため、この PR の差分とは無関係に main も以後の全 PR も赤くなる状態だった。

したがって「ついでの掃除」ではなく、赤を消すために必要な操作だった。報告の位置についての指摘は取り下げる。外部への副作用として記録する意義は変わらないが、判断そのものは正しく、先に述べるべき「独立した項目」というより、この PR を通すための前提条件だった。

残る脆さ

restore-keys: rust-Windows- は残っており、再度リポジトリを改名すれば同じことが起きる。 実装側は「対象ファイル集合を超えるうえ、改名は繰り返し起きる引き金ではない」として ci.yml を硬化させなかった。この判断は妥当と考える。記録として残す。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(account): give each account its own terminal and a way to end it

1 participant