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
24 changes: 22 additions & 2 deletions docs/0-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre
- アカウントごとの端末(リストの行で選ぶ、行からの終了、切り替えで残るスクロールバック)
- 行のライフサイクル操作の記号表示(開始 ▶️ / 終了 ❌ / 編集 ⚙️、語は `aria-label` と `title`、幅を固定した列)と、終了の確認ダイアログ(webview 内の `<dialog>`、取消に焦点、Escape は取消)
- 診断面(アカウントごとの CLI の端末表示と入力)
- 診断面のヘッダ(窓の名前「端末」、開いている端末のタブ、行との連動、面を畳む `✕`、端末の文字サイズ)と、面全体の角丸・一つの地・詰めた枠の余白。終了した端末はタブの `✕` で捨てる
- Windows CI(`npm ci` と Rust コンパイル確認)、Release 公開時の CD

### 実機で確認済み(2026-08-21)
Expand All @@ -276,6 +277,7 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre
- アカウントごとの端末と終了の実機確認。2 セッションでの出力の分離、入力の宛先、切り替え後のスクロールバック、終了後の席の解放と再起動はいずれも実装済みであり、CI の型検査は通っているが、実機での操作は未確認である。
- 一つのリストとアカウントのモーダルの実機確認。種別ごとのグループと件数、オフラインの表示、行からの終了、モーダルの決定と取消、本人のアカウントへの移行はいずれも実装済みで、CI の型検査とテストは通っているが、実機での操作は未確認である。`hello` の `account_id` が実際の部屋で名簿へ届くところも未計測である。
- 会話面の文字サイズのうち、ヘッダのピッカー、再起動後の保持、上下限の実機確認。いずれも実装済みで CI の型検査は通っているが、実機での操作は未確認である。キー操作(`Ctrl` + `=` / `-` / `0`)と、効く範囲が会話面に留まること(参加者パネル・診断面・端末・入力欄が動かないこと)は 2026-08-24 の実機観察で確認済みである。後者が webview 自身のズームも閉じている——キーを受ければ寸法は無条件に動くため、動いたこと自体は既定動作を止められた証拠にならないが、会話面以外が一緒に動かなかった以上、webview のズームは走っていない。
- 診断面のヘッダの実機確認。タブと行の連動、`✕` で面が畳まれセッションが残ること、終了したタブの `✕` での破棄、端末の文字サイズの梯子と再起動後の保持、桁数と行数がセッションへ伝わることはいずれも実装済みで CI の型検査は通っているが、実機での操作は未確認である。ヘッダの存在・角丸・枠の見た目は 2026-08-24 に実機で観察され、そこから色を敷かないこと・窓の名前「端末」・枠の余白を詰めることの三点が指示として戻っている。三点を反映した後の見た目は再度未確認である。
- 行の記号表示と終了の確認ダイアログの実機確認。三つの記号が Windows のフォントで揃って出ること、幅を固定した列で本人の行の名前が出ること、ダイアログの取消・終了・Escape はいずれも実装済みで CI の型検査は通っているが、実機での操作は未確認である。
- 会話ログの永続化と観測 UI
- plugin としての allowlist 掲載(配布の第二段階)
Expand Down Expand Up @@ -364,7 +366,7 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre

会話面(`#room`)の文字サイズを、読む側が変えられる。

**効く範囲は会話面だけである。** 参加者パネル・診断面・端末・入力欄の寸法は動かない。範囲は宣言ではなく置き場所で成立させる——寸法は `#room` 自身に載るカスタムプロパティであり、残る四つはこの要素の子孫ではないため継承経路が無い。`:root` に載せれば四つとも巻き込む。端末は特に巻き込めない。xterm.js は自前の寸法を持ち、そこから桁数と行数を計算するため、上から降ってきた寸法は表示だけでなく CLI が使える幅を変える。
**効く範囲は会話面だけである。** 参加者パネル・診断面・端末・入力欄の寸法は動かない。端末は自分自身の寸法を別に持つが、それはこの軸ではない(下記「診断面」)。範囲は宣言ではなく置き場所で成立させる——寸法は `#room` 自身に載るカスタムプロパティであり、残る四つはこの要素の子孫ではないため継承経路が無い。`:root` に載せれば四つとも巻き込む。端末は特に巻き込めない。xterm.js は自前の寸法を持ち、そこから桁数と行数を計算するため、上から降ってきた寸法は表示だけでなく CLI が使える幅を変える。

**既定値は現在の見た目のままである。** `1rem`、つまりブラウザ既定の `16px` である。`.message .body` には `font-size` が無く、`body` にも `html` にも `:root` にも無いため、会話面はこれまでずっと既定で出ていた。まわり(タイトルバー `0.85rem` / 参加者パネル `0.72rem` 〜 `0.78rem` / 発言者名と時刻 `0.78rem` / 端末 `13px`)が寸法を与えられている中で、会話面だけが縮められていない。「チャットの文字が大きい」(実機観察、2026-08-23)はここから来ている。それでも既定は動かさない——設定していない画面が勝手に変わらないことを優先する。面どうしを揃え直すことは別の判断であり、この節が入れるのは変えられる仕組みのほうである。

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

一つの端末を共有しない。共有すると、稼働中の全セッションの出力が一枚の画面へ書き込まれる。CLI はカーソル移動と部分再描画で出力するため、二つの再描画が重なった後の画面から片方を分離することはできない。「最後に起動したものが見える」のではなく、全部が重なる(#57)。

終了した端末は、その場では消さない。落ちる直前の出力が、なぜ落ちたかを読む唯一の材料である。別のアカウントが選ばれた時点で捨てる。同じアカウントを再起動した場合も、前回の端末は新しい端末に置き換わる。
**診断面は窓である。** 上端にヘッダを持ち、面全体が角丸の枠として畳まれる(#68)。角丸は面(`#diagnostics`)が持ち、端末(`#terminal`)は上側の角を手放す。値は `8px` で、ヘッダの上端と面の下端の両方に同じ値が出る。端末が自前の `4px` を持っていた形は、角の無い枠の中に角のある矩形を描いていた。ヘッダが載せるのは、窓の名前「端末」と、開いている端末のタブ、端末の文字サイズ、面を畳む `✕` である。名前は左端に置き、タブはその後ろに並ぶ。何も走っていないときにヘッダが自分について言うのはこの名前だけになる。

**ヘッダに色を敷かない。** 窓は全体が一つの地であり、それはページの地(`--bg`)であってパネルの色味(`--surface`)ではない(実機観察、2026-08-24)。帯であった頃の名残として面が `--surface` を持っていたが、それはヘッダを一色の帯として端末の上に置く形だった。ヘッダであることは位置と中身が言う。窓と会話面を分けるのは枠線と角丸である。

**枠と端末の間の余白は詰める。** `0.15rem` である(実機観察、2026-08-24)。ここは余白ではなく端末が使っていない面積であり、CLI の桁数と行数は残った寸法から計算される。削った分は端末の面積になる。`0` にはしない——xterm はカーソルと選択を自分の箱の縁まで描くため、枠に密着させると縁が枠線の上へ乗る。

**タブと参加者の行は連動する。** どちらを押しても同じ端末が画面に出る。同じ選択を二つの面が持つ形であり、#59 が名簿から外した重複とは逆を向く判断である(下記「受容したトレードオフ」)。タブが答えるのは「いまどの端末が開いているか」であり、行はそれを並べて読むことでしか答えない。二つは一つの描画から同時に出す。連動を実装の約束にせず、構造で成立させるためである。

**`✕` は面を畳む。セッションは終わらない。** 端末もリスナもスクロールバックも残り、開き直せば同じタブが並ぶ。ヘッダの `端末` ボタンは残す。畳まれた状態から開く手は `✕` の側に無く、二つは同じ操作の入口と出口である。セッションを終わらせる手は参加者の行の `終了` のままであり、端末側へは移さない。

**終了した端末は、タブが閉じられるまで残る。** 落ちる直前の出力が、なぜ落ちたかを読む唯一の材料である。「読んだ」の合図は、以前は「別のアカウントを選ぶ」という暗黙の選択だった(#57)。終了したタブに `✕` が付いたことで、その合図は明示の操作へ移る(#68)。合図そのものは失われず、置き場所が変わる。走っているセッションのタブには `✕` を出さない。出せば「セッションの終了」と読まれ、行の `終了` を端末側へ移さないという制約を破る。同じアカウントを再起動した場合は、前回の端末が新しい端末に置き換わる。

**端末の文字サイズは端末自身の寸法である。** 寸法の軸は三本あり、いずれも独立している——会話面(#60)、端末(#68)、UI 全体(#66)。互いに相対で乗らない。これは表示だけの話ではない。xterm.js は `fontSize` から桁数と行数を計算するため、変えるたびに `fit` を呼び、セッションへ新しい寸法を伝える。

保持は `localStorage`(`pullcept.terminal-font-size`)、段は離散の梯子(`9`〜`24px`)でその両端が上下限である。#60 と同じ形である。既定は `13px`——これまで端末が出ていた寸法であり、設定していない画面は変わらない。表示は `px` である。#60 が百分率で出すのは、会話面が何かに対して読まれるものではないためであり、端末は CLI の描く枠に対して読まれ、何桁入るかを決めるのはこの数そのものだからである。梯子に無い値が保存されていた場合は既定へ戻す(#60 と同じ理由)。

寸法は画面ごとの設定であって、セッションの属性ではない。画面上のすべての端末が同じ寸法で出る。隠れている端末は次に表示されたときに測り直す——隠れた枠には測る寸法が無いためであり、そのセッションへ新しい桁数が伝わるのもその時点である。

**終了はアカウントごとに行う。** 参加者のリストのその行から、そのアカウントのセッションを終了する。席の解放は別途要らない——`RoomSeats` は稼働の有無を PTY へ問うため、終了すれば席は空き、そのまま再起動できる(上記「アカウント」)。

Expand Down Expand Up @@ -607,6 +625,8 @@ CI が実行するもの:
| Node サイドカー方式 (b) | プロセスが増える |
| channel の早期採用 | experimental 仕様の変更がアプリへ波及する |
| 第一段階の dev フラグ運用 | 起動ごとに警告バナーが出る。一般配布は不可 |
| 端末のタブと参加者の行が同じ選択を持つ(#68) | 一つの選択を二つの面が描く。#59 が名簿から外した重複と逆を向く |
| 終了した端末をタブが閉じられるまで残す(#68) | 放置されたタブのぶん、スクロールバックがメモリに残る。#57 は「読んだら消える」でこれを避けていた |

## 位置づけ

Expand Down
55 changes: 54 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,61 @@
<!-- One frame, holding a terminal per running account. All but the
selected one are hidden rather than closed, so a session keeps
running and keeps filling its own scrollback while another is
being watched (#57). -->
being watched (#57).

A window, since #68: it carries a header of its own and the
rounding is on this element rather than on the terminal inside
it, so the frame that is folded and unfolded is one shape. -->
<aside id="diagnostics" hidden>
<!-- The window's header. Its name, then three things in the order
they are reached for: which terminal is on the glass, how large
it is drawn, and folding the whole pane away.

No fill of its own. It is the same ground as the rest of the
window, and what says it is a header is where it sits and what
is in it (Master, 実機, 2026-08-24).

The tabs and the participant rows are two renderings of one
selection, and they move together — either one puts the same
terminal on the glass. That is the duplication #59 removed from
the roster, chosen deliberately here (#68): a tab strip answers
"which terminals are open" at the pane being looked at, which
the rows answer only by being read alongside it.

✕ folds the pane; it does not end anything. The session under
the terminal keeps running and its tab comes back with the
pane. Ending a session is 終了 on the row, and it stays there —
one click from a control that merely changes what is showing is
how a slip ends a session mid-answer (#57 / #71). -->
<div id="terminal-head">
<!-- The window's name, at the left where a window's name goes. The
tabs follow it, so the strip reads as this window's tabs; it
is also what the header says about itself when nothing is
running and there are no tabs to show. Same word as the title
bar's button, which is the control that opens this. -->
<span class="pane-title">端末</span>
<!-- `group`, not `tablist`. A tab strip that carries a close
control on some of its tabs is two controls per tab, and the
tablist pattern has one; the rows in the panel already mark
the same selection with `aria-pressed`, and matching them is
worth more than a role whose keyboard contract this does not
implement. -->
<div id="terminal-tabs" role="group" aria-label="端末のタブ"></div>
<!-- The terminal's own size, and only the terminal's. It is one of
three independent size axes — the conversation (#60), this,
and the whole UI (#66) — and none of the three is expressed
relative to another. This one moves xterm's `fontSize`, which
is what the CLI's columns and rows are computed from, so a
change here re-fits the pane and tells the session. -->
<select
id="terminal-font-size"
aria-label="端末の文字サイズ"
title="端末の文字サイズ"
></select>
<button id="diagnostics-close" type="button" aria-label="端末を閉じる" title="端末を閉じる">
</button>
</div>
<div id="terminal" aria-label="セッションの端末"></div>
</aside>

Expand Down
Loading