# 画面の設計 ## 画面の人の発言は右に寄せる(#185) 部屋の画面で、画面の人の発言だけを右端に寄せる。セッション(AI)の発言と通知の畳みは左のままである。チャットアプリと同じ見え方であり、誰の発言かを位置で見分けられるようにする(Master 要望、2026-09-27)。変わるのは画面の見え方だけであり、部屋・ログ・端末への入力・保存形式は変えない。 - **判定は名前で行う。** 発言者の名前が、この画面が名乗っている名前(アカウントの名前、無ければ `human`)と一致する行を右に寄せる。ログから読み戻した行は `own` を持たず(「[部屋のログ](1-room.md#部屋のログ)」)、ライブの行と読み戻した行で寄せ方を食い違わせないには、行そのものが運ぶもので決めるしか無い。#193 でログがアカウントも運ぶようになったが、寄せ方はそれを読まない:アカウント id で決めれば、画面の人のアカウントを作り直した後や、別の画面から同じ config を開いた場合に、自分の言った行が左へ移る。ライブの行だけ `own` で決めると、同じ行がトピックを選び直したときに反対側へ移る。拒否が返した発言の描画も同じ規則による。 - **名前で決めることの代償を受け入れる。** 寄せ方が言うのは「この画面の名前で言われた行」であって「この画面の接続が言った行」ではない。この画面と同じ名前で入った別の参加者の行も右に出る。改名すれば、旧名で言った行は描き直した時点で左へ戻る。接続で決める `own`(「[部屋プロトコル](1-room.md#部屋プロトコルアプリ--サイドカー)」)は色の梯子が読むままであり、寄せ方にだけ使わない。 - **通知は常に左である。** 畳まれる行(`mcp` アカウントの行、#193)は、画面の名前が何であれ畳みに入り、右には寄せない。 - **行は塊ごと寄せる。** 本文は左から読む向きのまま、塊の幅は行の長さに合わせる(上限は他の行と同じ `46em`)。#225 までは色の線が寄せた側(右端)へ移り、名前と時刻の見出しも右から並んでいた。 - **右の行は丸印も名前も持たない(#225)。** 他の行の左の色線は頭文字の丸印に代わったが(下記「会話面の見た目」)、右に寄せた行には丸印を置かず、名前も出さない。位置と吹き出しの色(#187)が誰の行かを既に言っている。宛先があれば `@宛先` を吹き出しの頭に、時刻は吹き出しの足に置く。 - **続けて送った行は一塊である(#225)。** 右の行が間に何も挟まずに続く間は吹き出しを詰めて並べ、時刻はその連なりの最後の一つにだけ出す。日の区切り線や他の参加者の行が挟まれば、連なりはそこで切れる。判定は画面の描き方だけで行い(`.message.mine` が次の `.message.mine` を持つか)、行やログには何も足さない。 ## 画面の人の色は描いた行にもその場で届く(#189) 画面の人のアカウントの色を変えて保存すると、アプリを再起動しなくても、既に描かれている画面の人の行(ライブ・読み戻しとも)と、この後の発言の両方がその場で新しい色になる(Master 要望、2026-09-27)。部屋のログの保存形式(当時の五欄)は変えない。 - **この後の発言は席の宣言し直しで変わる。** 画面の人のアカウントを保存すると `room_join` を呼び直し、名前・色・アカウントを席に宣言し直す(#40 以来の経路)。発言の `hue` は席から刻まれるため、保存の後に言った行は新しい色で届く。 - **描いた行は一つの値を指す。** 行の色は描いた時点で行に固定されていたため、宣言し直しは既に描いた行へ届かなかった。画面の人の行(`isMine` が右に寄せる行)だけは、行ごとの色ではなく部屋に置いた一つの値を指し、その値はアカウントの保存と起動時に画面が書く。画面の人の色は部屋に聞かずとも画面が知っている——アカウントの色である——ため、この一組だけがこの形を取れる。 - **読み戻した行も同じ色になる。** ログは `hue` を持たないが(「[部屋のログ](1-room.md#部屋のログ)」)、画面の人の行はログではなくアカウントから色を得る。宣言が無ければ自分の accent であり、色の梯子(下記)を自分として読んだ結果である。#187 の「ライブと読み戻しで同じ色」は、画面の人の行についてこれで成り立つ。 - **名前で決めることの代償は寄せ方と同じである。** どの行が画面の人の行かは名前で決まるため(上記「画面の人の発言は右に寄せる」)、この画面と同じ名前で入った別の参加者の行も、右に寄るのと同じくこの画面の色で出る。 - **AI アカウントの色は対象外である。** その席の宣言はセッション起動時の `hello` にあり、変えた色は次の起動から届く。 ## 会話面の見た目(#225) 2026-10-02 の対話で描いた試作に沿う(Master「大分いい感じ」)。変えたのは見た目だけであり、部屋・ログ・保存形式・送る中身は変えない。 **誰の行かは頭文字の丸印で言う。** 行の左端の色線に代えて、発言者の名前の一文字目を丸の中に置く。丸の地は発言者の色をページの地に薄く混ぜたもの、文字は発言者の色そのものである——吹き出しの地(#187)と同じ作り方であり、ライトとダークの両方で文字が読める。ライトでは #235 で混ぜ方が変わった(下記「ライトの色」)。色は色の梯子(下記「参加者ごとの色」)のままであり、参加者パネルの行も同じ丸印を出す(#253、下記「参加者パネル」)。丸印は読み上げから外す(名前が隣に在る)。通知の畳み(#169 / #193)の見出しにも同じ丸印を置く。 **画像を持つアカウントの丸印は、頭文字の代わりにその画像を出す(#236)。** 画像は丸く切り抜き、丸の大きさいっぱいに出す。地の薄い色は画像の下に残るため、角の透けた画像は発言者の色の上に載る。どの行の丸印も、その行のアカウント id で画像を引く——名前では引かない。アカウントを持たない話者、画像を持たないアカウント、読み込めなかった画像は頭文字のままである。画像の持ち方と置き場所は「[アカウントは画像を持てる](3-accounts.md#アカウントは画像を持てる236)」。 **アプリ自身の発言の丸印は、頭文字の代わりにアプリのアイコンを出す(#340)。** 利用上限の知らせ(#294)など、発言者 `Pullcept(自動通知)` でアカウントを持たない発言である。マスターの希望(2026-10-08 の部屋)。決めるのは部屋が発言に付けた印(`from_app`、「[部屋のログ](1-room.md#部屋のログ)」)であり、名前ではない——`Pullcept` と名乗る参加者の行は頭文字のままである。印はログにも残るため、読み戻した行と拒否が返した行も同じアイコンになる。絵はタイトルバーと同じ `src-tauri/app-icon.svg`——`src-tauri/icons/` の全部を作る元の絵(#246)——であり、新しい絵は作らない。丸に切り抜かず、丸の中に収まるよう内側に余白を取って丸ごと出す。絵は自分の端まで描かれており、切り抜けば吹き出しの角が欠けるためである。地は他の丸印と同じく発言者の色の薄い地である。 **丸印の大きさは会話面の文字サイズに連れる(#250、#258)。** 直径と頭文字は丸印自身の `em` で持ち、会話面の文字サイズ(下記「会話面の文字サイズ」)と一緒に動く。既定の文字サイズでの直径はおよそ 2.66rem(頭文字 `1.265em`、直径はその `2.1em`)である。#258 で、それまでの直径 2.31rem(頭文字 `1.1em`)から、直径と頭文字をともに 1.15 倍にした。Master「今の会話のアイコンサイズをアカウントのアイコンサイズに。で会話のアイコンもう少しだけ大きくできる?」(2026-10-03)。1.15 倍は「もう少しだけ」を受けた AI の判断である。参加者パネルの丸印は、変える前の会話面の丸印の大きさを受け継いだ(下記「参加者パネル」)——#260 から、その関係(パネル = 会話面の 1/1.15)は UI の倍率と会話面の文字サイズのどの組でも保たれる。 **行の見出しは本文と同じ大きさで、名前は参加者の色で書く(#258)。** 名前・宛先・時刻の一行(`.message .meta`)は本文と同じ文字の大きさであり、自分の吹き出しの下の時刻も同じである。それまでは見出しが本文の `0.78em`、吹き出しの下の時刻が `0.72em` だった。名前はその参加者の色(`--speaker`、アカウントで選んだ色、選んでいなければ名前から導いた色)で書く。それまでは `--fg` だった。時刻は `--muted` のままである。Master「チャット欄の名前の部分のフォントサイズを文章のフォントサイズと同じにしてほしいのと。アカウント設定で設定した色にしてほしいんだけど。」、続けて「時刻とあて先も文章と同じサイズがいいかも。」、自分の吹き出しの下の時刻について「そっちもそろえちゃって!」(2026-10-03)。宛先は #452 で、アットマークと名前を間を空けずにつないだ形にし、その宛先の人の色で書くようにした(それまでは「→ 宛先」を発言者の色で書いていた)。宛先が複数なら一人ずつ空白で区切って並べる。色は名前の色と同じ規則で引く——この画面の人なら自分の色、表示中のトピックに居る人ならその人の色、居なければそのアカウントの色、どれでもなければ名前から導いた色である。見出しは宛先の並び(`to`)から描くもので、送る本文の中の @名前 が宛先へ変わる仕組みとは関係しない。Master「矢印の部分を@にしたいんだよ」「できるなら頼む!」(2026-10-17)。 **行間を広げる。** 行と行の間を `0.75rem` から `1.15rem` へ、本文の行の高さを `1.6` から `1.7` へ広げた。 **日付は区切り線で言う。** 日が変わるところに、左右に線を引いた日付の行を一つ置く。各行の時刻は時と分だけになった——読み戻した行も同じである(#48 以来の「時刻には日付も出す」は区切り線へ移った)。日付の言い方はトピックの一覧と同じであり(今日 / 9月28日、今年でなければ年を付ける)、時刻の全文は時刻のツールチップに出る。区切り線は描いた時点の「今日」で言うため、新しい区切り線を描くたびに画面上の区切り線をすべて言い直す。開いたまま日をまたいでも「今日」が二つにならない。 **画面の人の行は一塊にする。** 上記「画面の人の発言は右に寄せる」の二項目(#225)。 **上の帯は開いているトピックの名前と人数を言う。** タイトルバーの、ボタンの間の空いていた場所に置く(AI 判断)。名前はトピックの一覧の行と同じ語で出し(題の無い行は「未記入」)、索引にまだ無いトピック——起動や「新規」の後、最初の発言の前——は「新しいトピック」と出す。人数は部屋の名簿の人数、つまり今そのトピックに繋がっている者の数であり、起動していないアカウントと席を取らない MCP サーバ(#193)は数えない。名前は参加者パネルのものであり、帯には出さない。 ## タイトルバーは窓の最上段である(#252) 窓に Windows の標準の枠は無く、画面のタイトルバーが窓の最上段に立つ。それまでは標準の枠の最上段(アイコンと三つのボタン)の下に、このタイトルバーが二段目として並んでいた。Master「あとアプリの二段目のメニューバーを最上段に入れつつ最上段の全高をちょっとだけ上げて、色もサイドバーに合わせられる?」、続けて「じゃあ全高はそのままで」(2026-10-03)。枠を外した仕組みと、Tauri の側で確かめたことは「[窓の枠は画面が描き、題は残す](5-development.md#窓の枠は画面が描き題は残す)」。 **一段にまとめ、高さは変えない。** 帯の高さは二段目だった頃のタイトルバーの高さのままである(Master「じゃあ全高はそのままで」)。消えたのは標準の枠の一段であり、その分だけ会話面と左右の面が縦に広がる。 **並びは、左端にアイコン、右端に窓の三つのボタン。** 間に置く操作——トピックの開閉、トピック名と人数、端末、設定、アカウントの開閉——の並びと働きは変えていない。アイコンは `src-tauri/app-icon.svg`、つまりタスクバーのアイコンを作る元の絵そのものであり(#246)、帯のボタンの線画と同じ大きさで出す。飾りであって読み上げには出さない。窓の名前は題が持つ(#248)。 **地はサイドバーと同じ `--surface`、下の境は `--line`。** 帯と左右の面が一つの枠として部屋を囲む。 **窓の三つのボタンも #221 のボタンである(AI 判断)。** 最小化・最大化(元に戻す)・閉じるは線画だけで描き、枠も地も持たず、カーソルの下で `--hover` が出る。違うのは二点である。一つは、帯の高さいっぱいに伸び、帯の上端と右端に接していること。カーソルを窓の右上の角へ投げれば閉じるに当たる——標準の枠のボタンと同じ置き方である。帯の高さはほかのボタンが決めており、端まで伸ばしても帯は高くならない。もう一つは、閉じるだけがカーソルの下で赤(`--close-ground`、字は `--close-ink`)になること。標準の枠の閉じると同じ合図であり、ライトとダークで同じ値である。最大化は窓が最大化している間「元に戻す」と名乗り、絵も重なった二つの四角に変わる。名前と絵は窓の大きさが変わるたびに窓へ問い直す——帯のダブルクリック、Win+↑、画面の上端への引き寄せも最大化するが、どれもこのボタンを通らない。 **帯の空いているところを掴んで窓を動かせる。ダブルクリックで最大化と元に戻すが入れ替わる。** トピック名と人数の上、アイコンの上も掴める。ボタンの上では掴まず、押したボタンが働く。 **トピック名はダブルクリックで名前を変えられる(#469)。** 名前の上だけはダブルクリックが最大化にならず、その場に一覧の行と同じ欄が開く。Enter で決定、Escape と欄の外を押すと取消、空のままなら変えない——一覧の行の名前変更と同じ三つの出口であり、同じ欄を使う。名前の上を押して引けば、帯のほかの所と同じく窓が動く。まだ一覧に載っていない「新しいトピック」は直せない(直す行がまだ無い)。 **閉じるは今のアプリの終了の流れを通る。** 下記「アプリの終了」。 **失ったもの。** Windows 11 で最大化ボタンにカーソルを乗せると出るスナップの配置の一覧は出ない(受け入れた損失、AI 判断)。キーボードの Win+矢印と、窓を画面の端へ引くスナップは、標準の枠と同じ経路を通る。理由は「[窓の枠は画面が描き、題は残す](5-development.md#窓の枠は画面が描き題は残す)」。 ## 二つの地色 面には地色が二つある。`--bg`(ライトで `#ffffff`)が窓の地であり、`--surface`(同 `#fbfbf9`、#235 までは `#f4f4f7`)が面そのものの色味である。ボタンはそのどちらも自分の地として持たない。 添付の札・ダイアログのログと注記・宛先の候補の選択中の項目は、`--surface` ではなく `--tint`(ライトで `#f4f4f7`、ダークで `#202026`)で塗る。#235 で面の地が窓の地にほぼ並ぶ色になり、`--surface` で塗った札は白の上で見えなくなるためである。値は #235 までの `--surface` のままであり、見た目は変わっていない。 **ボタンは枠も塗りも持たない(#221、Master 判断 2026-10-02)。** 画面のボタンは線画のアイコンだけで描き、普段は枠も塗りも無い。カーソルを乗せたときだけ薄い塗り(`--hover`)が出る。参考は Claude Desktop のタイトルバーのボタンである。対象は画面に載るボタンのすべて——タイトルバー、トピックの一覧(行・「新規」・削除)、参加者パネル(見出しの追加・行・行の操作とそのメニューの項目・セッション ID のコピー)、端末面のヘッダ(タブ・タブの閉じる・面を畳む)——である。 **名前とキーはツールチップで出す。** 語を持たないボタンは、何をするかを `title`(ツールチップ)と `aria-label`(読み上げ)で言う。キーを持つ操作は名前の後ろに括弧で添える——「トピックの開閉(Ctrl+B)」「送信(Enter)」。キーを持たない操作には何も添えない。 **設定・添付・送信の線画は試作の形である(#234、Master 判断 2026-10-02)。** タイトルバーの「設定」は歯車、添付は斜めに傾いたクリップ、送信は紙飛行機で描く。三つとも 2026-10-02 の試作が使った Tabler Icons(outline、MIT ライセンス)の `settings` / `paperclip` / `send` を、その 24 の格子から画面の 16 の格子へ写したものであり、ライセンスの表記は `NOTICE.txt` に置く。#221 は「設定」を三本のスライダー、送信を上向きの矢印で描き、#223 は添付を立てたクリップで描いていた。スライダーにしたのは参加者パネルの行の編集の ⚙ と分けるためだったが、その ⚙ は #224 で行のメニューへ移り、行には無い。メニューの「編集」の歯車は自前の線画のまま残っており、「設定」の歯車とは形が違う。 **送信だけは塗る。** `送信` は 塗ったボタンとして残す。画面が前に出す操作を一つに絞るためである。他のボタンはすべてカーソルを待ってから現れる。 **送信の地はアプリのアイコンの青である(#258)。** 地はライトとダークのどちらでも `#1e9be8`——タスクバーのアイコン(`src-tauri/app-icon.svg`、#246)の青——であり、線画は白である。Master「さらに細かいんだけど、pullceptのアイコンの青色に。送信ボタンも合わせることって可能?」(2026-10-03)。それまでは `--accent`(ライト `#185fa5`、ダーク `#7aa7d8`)で塗り、線画は `--bg` だった。色は専用のトークン(`--send-ground` / `--send-ink`)に置き、`--accent` は変えない——参加者ごとの色は専用の明度・彩度トークンで描く(下記「参加者ごとの色」)。カーソルの下では地を黒へ 15% 寄せ、ライトとダークのどちらでも少し濃くなる。それまでは `--fg` へ寄せており、ダークでは明るくなっていた——地が両方の面で同じ一色になったため、寄せる先も一つに固定した。 **カーソルの塗りは地の上に重ねる影であって、色ではない。** カーソルの塗り `--hover` は半透明の黒(ダークでは白)であり、`:root` とダークの両方に定義する。ボタンが `--bg` の上にあっても `--surface` の上にあっても、同じだけ持ち上がる。旧規則(下記)が枠で守っていた「操作はどの面に載っていても一つの形である」は、この形で保たれる。選択の塗り `--selected` も #235 までは同じ作りだったが、ライトでは Claude デスクトップのサイドバーの選ばれた行の色 `#edece8` になった(下記「ライトの色」)。ライトの二つの地はどちらもほぼ白であり、一つの色でも両方の上で同じに読める。ダークは半透明の白のままである。 **選択は塗りで言う。** 選択中の端末タブ、参加者パネルの選択中の行、トピックの一覧の選択中の行(と、索引に無いトピックに居る間の「新規」)は `--selected` で塗る。`--hover` より一段濃く、カーソルの下の行が選ばれた行と読まれないようにする。端末タブは加えて文字色が `--muted` から参加者自身の色へ変わる。三つは同じ選択を同じ手で描く(#75)。設定の項目の列の、開いている項目の印も同じ塗りである。 **欄は枠を残す(#221、AI 判断)。** 入力欄・テキストエリア・選択肢は `--bg` を地に持ち、`--line` の枠で輪郭を持つ。ボタンは線画そのものが操作であることを言うが、空の欄には言うものが無い——枠を外すと、どこへ打ち込むのかが地と見分けられなくなる。 **ダイアログの語のボタンはこの規則の外にある(#221、AI 判断)。** アカウントの窓・設定・確認のダイアログの `取消` `決定` `終了` `削除` `閉じる` `保存` などは語で答えるボタンであり、これまでどおり枠を持ち、行為を担う一つ(`決定`、危険な側の答え)は塗る。語は枠が無ければダイアログの本文と見分けがつかない。枠を外せるのは、線画が自分で操作であることを言うからである。 **状態の札は操作ではなく表示であり、塗ってよい。** 「待機」などの札はこの規則に縛られない。 **旧規則(2026-08-24 〜 #221)。** それまでは、入力欄・選択肢・テキストエリア・ボタン、そして選択中の端末タブはすべて `--bg` を背景に持ち、`--line` の枠で輪郭を持っていた(実機指示、2026-08-24)。操作を地から分けるのは枠であって塗りではない、という規則である。理由は、塗りで分けると同じボタンがタイトルバーでは白の上の灰色、参加者パネルでは灰色の上の灰色になり、操作の見え方が「どの面に載っているか」で変わることだった。選択中の端末タブも同じ理由で枠(未選択の `transparent` から `--line` へ)と文字色で選択を言い、参加者パネルの選択中の行と同じ「枠のある窓の地の箱」になっていた(#75)。その前は逆を向いており、パネルの地が `--surface` なので選択された行は `--bg` で浮き、端末ヘッダの地が `--bg` なので選択されたタブは `--surface` で沈んでいた。#221 はボタンと選択から枠を外したが、旧規則の理由は捨てていない——塗りを地の色ではなく地に重ねる影にしたのは、どの面の上でも同じ持ち上がり方をさせるためである。欄だけは同じ理由のまま枠に残る。 **`--surface` は面の地に残る。** 参加者パネルとトピックの一覧と設定の項目の列の地である。面自身の地は変えない——揃えるのは面の中の操作であって、面ではない。 **ライトの色(#235)。** ライトの色づかいは 2026-10-02 の二枚目の試作(画面全体の案)に寄せ、左右の列を Claude デスクトップ(ライト)のサイドバーの色にした(Master「Cで、ダークは今のままでいいかな一旦」)。サイドバーの三色(面・選ばれた行・境の線)は Master が貼った画面写真の画素から拾った値である。ダークは変えていない。ライトのために足したトークンは、ダークでは #235 までと同じ見た目になる値を持つ。角の丸みも変えていない(Master「丸みは変えない方がいいね」)。 | トークン・場所 | ライト(#235) | #235 まで | |---|---|---| | `--muted` | `#73726c` | `#6b6b76` | | `--accent` | `#185fa5` | `#3a6ea5` | | `--line`(列の境など) | `#e2e2e1` | `#d8d8de` | | `--surface`(トピックの一覧・参加者パネルの地) | `#fbfbf9` | `#f4f4f7` | | `--selected`(選ばれた行。枠なし) | `#edece8` | 黒 10% | | `--hover` | 黒 4% | 黒 6% | | `--composer-line`(入力欄の枠) | 黒 20% | `--line` | | 状態の札(考え中…・出力中) | 地 `--ok-ground` `#eaf3de`、文字 `--ok` `#3b6d11` | 文字 `#2e7a4e`、地はその 14% | | それ以外の札(未起動など) | 地 `--badge-ground` `#f1efe8`、文字 `--muted` | 地は `--muted` の 14% | | 頭文字の丸 | 地は参加者の色 13% を `--bg` に、文字は参加者の色 75% を黒に混ぜる | 地 22%、文字は参加者の色そのもの | | 自分の発言の吹き出し | 地は参加者の色 9% を `--bg` に、縁は 35% を `--bg` に混ぜた 1px の線 | 地 14%、縁なし | - **`--hover` を薄くした(AI 判断)。** 「選択はカーソルより一段濃い」(下記「選択は塗りで言う」)を保つためである。黒 6% のままだと、`#fbfbf9` の上のカーソルの塗りが `#edece8` とほぼ同じ色になり、カーソルの下の行が選ばれた行と読まれる。 - **吹き出しの縁は箱の内側に描く。** 枠ではなく内側の影で 1px の線を引くため、吹き出しの寸法は変わらない。ダークでは縁の色が吹き出しの地と同じ値になり、線は見えない。 - **「起動失敗」の札は変えていない。** 両方のテーマで、`--danger` を薄く混ぜた地のままである。 **端末のカーソルは文字色で描く(#445)。** 端末のテーマは背景に `--bg`、文字に `--fg` を渡し、カーソルにも `--fg`、カーソルの下の文字に `--bg` を渡す。カーソルの色を渡さないと xterm は `#ffffff` で描くため、ライトテーマの地の上では見えなくなる(Master 実機、2026-10-17「pullceptのターミナルにカーソルとか表示されない」)。Claude Code も Codex も、入力欄では本物のカーソルを出している。 **色は直値で書かない。** 「白」はライトテーマでの見え方であって指定ではない。ダークテーマでは `--bg` は `#17171a` であり、`#ffffff` と書けばそこで壊れる。指定は常にトークンで書く。 ## 入力欄(#222) **一つの囲みに本文と操作列を収める(Master 判断 2026-10-02、モックアップで合意)。** 入力欄は角の丸い一つの箱であり、上に本文を書く欄、その下に操作列を持つ。操作列は左にアイコンのボタン、右に `送信` を置く。それまでは枠付きの大きなテキストエリアと、その右に独立した `送信` が並んでいた。 **枠は箱が持ち、本文の欄は持たない。** 箱そのものが欄であり、「欄は枠を残す」(上記「二つの地色」)は箱の枠で満たす。箱の枠は `--composer-line` であり、ライトでは `--line` より濃い(#235、上記「ライトの色」)。中の本文の欄(#354 からはエディター、下記「入力欄はコードを書けるエディターである」)は枠も地も持たない——枠が二重になれば、どれが打ち込む先かがかえって読めなくなる。本文に焦点がある間は箱の枠が `--accent` に変わり、打ち込む先を示す役を本文の欄の縁から引き継ぐ。 **操作列のボタンは #221 の形に従う。** 左のボタンは枠も塗りも無い線画であり、カーソルの下でだけ `--hover` が出る。名前とキーはツールチップで出す。`送信` だけが塗りのボタンとして右端に残る(「送信だけは塗る」)。 **宛先のボタンは `@` を打つだけである。** 押すとカーソル位置に `@` が入り、`@` キーを打ったのと同じ候補が開く。カーソルが語に接しているときは前に空白を一つ入れる——語の途中の `@` では候補が開かないためである。候補から選んだ名前が宛先になる経路は `@` キーと同じであり(#204 / #206)、ボタンが別の経路で宛先を持たせることはない。押しても本文から焦点は外れない。 **添付は名前の札で本文の上に並ぶ(#223、Master 判断 2026-10-02、モックアップで合意)。** 添付したファイルは、箱の中の本文の欄の上に、ファイルの名前の札として一つずつ並ぶ。札の右端の `✕`(「外す」)で外せる。添付は本文と一緒に出ていくものであるため、本文と同じ箱に入れる。送ると札は消え、本文の後ろに保存先のパスが書かれる(「[添付は保存したファイルのパスで届く](1-room.md#添付は保存したファイルのパスで届く223)」)。床に拒まれたときと送れなかったときは、本文と一緒に札も戻る。 - **入口は三つである。** 操作列の左の組の先頭の添付のボタン(斜めのクリップの線画、「添付」)がシステムのファイルの窓を開き、選んだファイルを札にする。一度に何個でも選べる。入力欄へファイルを落としても札になる。本文に焦点がある間の `Ctrl+V` は、クリップボードがファイルや画像を持ち、**文字を持たないときだけ**札にする——Excel やブラウザでコピーしたものは文字の横に選択範囲の画像も持っており、それを貼るのは文字を貼ることである。 - **落とす先は入力欄だけである。** ドロップは webview のページへは届かず、Tauri が受けてパスと落ちた位置を知らせる(`dragDropEnabled`、既定で有効)。画面は位置が入力欄(`#composer`)の上かどうかで決め、他の場所に落としたものは何もしない。ファイルを入力欄の上に持っている間は、箱の枠が `--accent` になり、地がうっすら色づく。 - **札は表示であり、`✕` は操作である。** 札は `--tint` で塗る(上記「二つの地色」の「状態の札は操作ではなく表示であり、塗ってよい」)。`✕` は #221 の形——枠も塗りも無い線画で、カーソルの下でだけ `--hover` が出る。長い名前は札の幅(18rem)で末尾を省略し、`✕` は押し出されない。全文はツールチップに出る。札も `✕` も UI 全体の軸であり、会話面の文字サイズに従わない。 - **押しても本文から焦点は外れない。** 添付のボタンと `✕` は宛先のボタンと同じく、押下で焦点を奪わない。ファイルの窓を閉じた後とドロップの後は本文に焦点を戻す。 - **画像の札は名前の代わりに縮小した絵を出す(#318)。** ボタンで選んだ画像と貼り付けた画像(png / jpg / jpeg / gif / webp)は、3.5rem 四方に切り抜いた絵の札になり、`✕` は絵の右上の角に丸い地で載る。名前はツールチップと読み上げに残る。絵を押すと、会話の絵と同じ大きな表示が開く(下記「添付は会話の中に絵と札で出る」)。`✕` は外すだけで、大きな表示は開かない。ドロップした画像は送るまで名前の札のままである——ドロップはパスで届き、画面は添付フォルダの外のファイルを読まないためである(「[添付は保存したファイルのパスで届く](1-room.md#添付は保存したファイルのパスで届く223)」)。 **キーの案内は小さく出す。** 「@ 宛先 / Enter 送信 / Shift+Enter 改行」を操作列のボタンの間に、小さく `--muted` で出す。それまではテキストエリアの placeholder に書かれており、一文字目を打った時点で消えていた。案内は一行の高さに収め、幅が足りないときは収まらない案内を末尾から一つずつまるごと落とす——一つの案内を途中で切った形は出さない。placeholder は「部屋に発言する」だけになった。 **#222 が変えたのは見た目と配置だけである。** 送る中身(添付を除く。添付は上記、#223)、部屋への渡し方、`@` の候補・Enter で送信・Shift+Enter で改行の挙動はそのままである。会話面の文字サイズに従うのは今も本文の欄だけであり、操作列は UI 全体の軸に残る(下記「会話面の文字サイズ」)。 ## 添付は会話の中に絵と札で出る(#318) 添付した画像やファイルを、会話面の中でそのまま見られるようにする(Master 要望 2026-10-07。見本は Claude デスクトップ)。変えたのは見え方だけであり、発言の本文・ログ・端末へ届く文字は変えない(「[添付は保存したファイルのパスで届く](1-room.md#添付は保存したファイルのパスで届く223)」)。 **`添付:` の段は、パスの文字ではなく絵と札で出す。** 発言の本文の末尾に `添付:` の段があり、そのパスがすべてこの部屋の添付フォルダの下を指すとき、画面は段の文字を出さず、本文の下に段の並びのまま添付を並べる。画面の人の行では吹き出しの中に右寄せで、他の参加者の行では名前の下に左寄せで並ぶ。添付だけの発言は本文の行を持たない。 - **画像は縮小した絵で出す。** png / jpg / jpeg / gif / webp を、8rem 四方に切り抜いた絵(`object-fit: cover`)で出す。大きさは絵が届く前から決まっているため、絵が届いても行の高さは変わらず、読んでいる位置が押し動かされない(AI 判断)。読めなかった絵・壊れていた絵は、何も言わずにそのファイルの札に替わる。 - **それ以外は名前と種類の札で出す。** 札は `--tint` で塗り、名前の後ろに拡張子を大文字で `--muted` に小さく添える(拡張子が無ければ「ファイル」)。長い名前は札の幅(18rem)で末尾を省略し、全文はツールチップに出る。札は押しても何も開かない——開く先を画面が決めれば、添付フォルダの外へ出る口になりうるためである。 - **添付フォルダの外を指す段は、文字のまま出す。** AI が書いた自分のパスや、引用された段がそれに当たる。読み替えないパスを隠すことはしない。その文字のパスは、本文のほかのパスと同じくリンクになる(下記「発言の URL とパスは押して開ける」、#349)。 - **画面が読みにいくのは、添付フォルダの中のファイルだけである。** 判定はアプリの側で、パスを実体へ解決してから行う(「[添付は保存したファイルのパスで届く](1-room.md#添付は保存したファイルのパスで届く223)」の #318 の項)。 **絵を押すと、窓を暗くして大きく見せる。** 会話の絵と入力欄の画像の札のどちらからも開く。窓全体を暗くし、絵をその真ん中に、絵そのものの大きさで出す——窓と周りの余白(3rem)に収まらないときだけ縮める。右上に `✕`(「閉じる」)を置き、`✕`・絵の周りの暗い所を押す・`Esc` のどれでも閉じる。閉じれば焦点は開く前の場所へ戻る(入力欄の札から開いたときは本文の欄)。開くときは暗がりと絵がふわっと現れ、OS の「動きを減らす」がオンなら一度に出る(下記「動き」と同じ線)。新しいライブラリは足していない。 ## 三つのバッククォートで囲んだ部分はコードの枠で出る(#348) 発言の本文と入力欄で、`` ``` `` で囲んだ部分をコードの枠で見えるようにする(Master 依頼 2026-10-09。見本は Claude デスクトップの入力欄)。入力欄の側は #354 でエディターに作り替えた(下記「入力欄はコードを書けるエディターである」)。この節の入力欄についての記述のうち、文字の後ろの層・等幅の書体・Enter の扱いは #354 で置き換わっている。変えたのは見え方だけであり、発言の本文・部屋の記録・各席の端末へ打ち込まれる文字は変えない。送る本文は `` ``` `` の行を含め、打ったとおりである。 **どこが枠になるかは CommonMark のコードフェンスに合わせる。** 行頭(空白 3 つまで可)に `` ``` `` 以上の連続したバッククォートがある行で枠が始まり、同じ数以上のバッククォートだけの行(後ろの空白は可)で閉じる。開く行のバッククォートの後ろにバッククォートがあれば枠にしない——文の途中の `` ``` `` や、一行の中で閉じる `` ```x``` `` は枠にならない。**閉じていない `` ``` `` は、そこから最後までを枠にする。** 判定は一つの関数(`findCodeFences`)で行い、発言と入力欄の両方がそれを使う——同じ文が二つの場所で違う形に出ないためである。 **開く `` ``` `` の後ろは、一語の言語名のときだけ言語名とみなす(#352)。** 一語とは、英字・数字と `-` `_` `+` `.` `#` だけでできた、空白を含まない語である(`ts`・`c++`・`c#`・`objective-c` など。前後の空白は除く)。それ以外の文が続いていれば、それは枠の一行目の中身である——`` ```あ。。。 `` は「あ。。。」で始まるコードの枠になり、`` ```python title `` も「python title」が一行目の枠になる。この読み分けは枠の始まりと終わりを変えない。どちらの場合も枠はその行で開き、閉じる行・入力欄の枠・Enter の扱い・`添付:` の読み分けは言語名のある枠と同じである。CommonMark は後ろの文の最初の語を言語名とするが、日本語の文を書いた人の意図と合わないため、ここだけ外した(Master の実機確認 2026-10-09 による)。 **発言では、枠の中を等幅の文字で、改行と空白をそのまま描く。** 枠は `--code-line` の線と `--code-ground` の地を持つ。この二つは本文色(`--fg`)を薄く混ぜた色であり、会話の地にも自分の吹き出しの地にも、ライトにもダークにも一組で乗る。`` ``` `` の行そのものは描かない。開く行に言語名があれば、コードの上に小さく `--muted` で添え、コードの中には入れない。枠の幅を超える行は折り返さず、枠の中で横に送る——コードの並びを崩さないためである。自分の吹き出しは、そのような行があっても部屋の幅を超えない。枠の外の文は `linkifyText` へ渡り(#431 からは Markdown として読んだ後の地の文が渡る。下記「発言は Markdown として描く」)、URL とパスがリンクになる(下記「発言の URL とパスは押して開ける」)。枠の中はリンクにしない。HTML としては読まない(文字と、色分けの `span` だけで描く)。**枠の中は色分けする(#354)。** 開く行の言語名を使い、言語名が無いか、知らない言語名なら自動で判定する。色分けは入力欄と同じ関数(`highlightedCode`、`src/composer.ts`)で行い、同じコードは発言でも入力欄でも同じ色になる。 **入力欄の枠は #354 のエディターが描く。** #348 は入力欄をテキストエリアのままにし、文字の後ろの層(`#input-fences`)で枠を敷き、枠がある間は入力欄全体を等幅にしていた(Master 判断 2026-10-09、案 B)。開いた `` ``` `` の中の Enter は改行で、#352 は閉じていない枠の空の行での Enter に閉じる `` ``` `` を入れさせていた。この三つは #354 で取り除いた。今の形は下記「入力欄はコードを書けるエディターである」。 **枠の中に書いた `添付:` は添付として読まない。** 閉じていない `` ``` `` の後ろに `添付:` の段があっても、それは枠の中の文字であり、コードとして描く(「添付は会話の中に絵と札で出る」の読み替えはしない)。アプリが添付を付けた発言でも、本文が閉じていない `` ``` `` で終わっていれば、添付のパスは枠の中の文字として出る——書いた人の文字と、アプリが後ろに付けた段を、本文からは見分けられないためである(AI 判断。#348 のレビューの指摘による)。 ## 入力欄はコードを書けるエディターである(#354) 入力欄を、Codex デスクトップアプリの入力欄のようにコードを書ける欄に作り替えた(Master 判断 2026-10-09、案 C)。テキストエリアをやめ、エディターの部品(Tiptap。中身は ProseMirror)を置いた(`#input`、`src/composer.ts`)。手本にしたのは確かめた動きだけであり、ほかの製品のコードは写していない。 **`` ``` `` を打つと、その行がコードの枠になる。** 行頭で三つ目のバッククォートを打った時点で、バッククォートは消え、その行が枠になる。行の後ろに文字があれば、それも枠の中身になる。枠の中の文字は等幅で、折り返す(入力欄の幅を超える行で横に送ると、カーソルが見えなくなるため。AI 判断)。枠の外の文は会話面と同じ書体のままである。 **枠の左上に言語のボタンを置く。** 最初は「自動」で、判定した言語があれば「自動(JavaScript)」のように括弧の中に出す。判定は打つ手が止まって少ししてからやり直す。押すと、検索欄のついた言語の一覧が開く(一覧の先頭が「自動」)。検索欄に打つと、言語の名前・識別子・別名で絞り込む。上下の矢印で選び、Enter か押下で決め、Esc か一覧の外を押すと閉じる。決めると枠の色分けがその言語に変わり、ボタンは言語の名前になる。言語は highlight.js のよく使う言語の組(37 種)であり、色分けと判定はこの組で行う。 **枠の中は打っているあいだも色分けする。** 言語を決めていなければ自動で判定する。言語を決めていない 5,000 文字を超えるコードは判定せず、色を付けない——判定は組のすべての言語でコード全体を読むため、入力欄ではキーを打つたびに重くなる(AI 判断)。言語を決めたコードは長さによらず色を付ける。色は画面の地の色に合わせてライトとダークで一組ずつ持つ(`--hl-*`、`src/styles.css`)。発言の枠も同じ色で描く。 **キー操作は Codex に合わせる。** - **Enter はいつでも送信である。** 枠の中でも、空の行でも送る。日本語入力の変換を確定する Enter と、宛先の候補が開いているときの Enter(候補を選ぶ)は除く。 - **Shift+Enter は改行である。** 枠の中ではコードの次の行になり、枠からは抜けない。枠の外では次の行になる。 - **下矢印**: 枠の最後の行(折り返した行は見えている最後の行)で押すと、枠の下の行に出る。下に行が無ければ空の行を作る。 - **上矢印**: 枠のすぐ下の行の最初の行で押すと、枠の最後の行の末尾に戻る。本文の先頭にある枠の最初の行で押すと、枠の上に空の行を作ってそこへ出る——そうしないと、枠より前に書く道が無いため(AI 判断)。 - **Backspace**: 枠の最後の空の行で押すと、その行が枠から出て、枠の下の行になる(カーソルもそこへ移る)。枠全体が空なら、枠が消えて空の行になる。中身のある枠の先頭で押しても枠は消えない——すぐ上が空の行ならその行を消して枠を上へ寄せ、そうでなければ何もしない。枠の途中の空の行では、普段どおり行をつなぐ——そこで枠を抜けると枠が二つに割れるため(AI 判断)。 - #353 で入れた「閉じていない枠の空の行で Enter を押すと枠を抜ける」動きは取り除いた。Enter は常に送信である。 **送る中身は今までと同じ Markdown の文字列である。** エディターの中は行と枠の並びであり、送るときに文字列へ直す。枠の外の行はそのまま一行ずつ、枠は `` ```言語名 `` で始まり `` ``` `` で終わる(言語が「自動」なら `` ``` `` だけ)。枠の中の空の行も残る。コードの中に閉じる `` ``` `` と読まれる行があるときは、それより長いバッククォートで囲み、受け取った側でもコードが切れないようにする。部屋のフレーム・ログ・各席の端末へ打つ文字の決まりは変えていない。送れなかったときと床に拒まれたときに入力欄へ戻す文字列も、同じ規則(`findCodeFences`)で行と枠に読み戻す。 **貼り付けは文字だけを読む。** 枠の外へ貼った文字に `` ``` `` の枠があれば枠として入り、空の行も残る。ブラウザなどからの貼り付けでも HTML は読まず、文字だけを使う。枠の中へ貼った文字はそのままコードになる。VS Code からコピーした文字を枠の外へ貼ると、VS Code の言語の枠として入る(Tiptap の動き)。コピーは、一行や一つの枠の中だけならその文字を、行と枠をまたぐなら送るときと同じ文字列をクリップボードへ置く。 **今の入力欄にある機能はすべて残す。** `@` で開く宛先の候補(候補はカーソルのある行のカーソルより前で探す)、添付(ボタン・画像の貼り付け・ファイルのドロップ、送るときの `添付:` の段)、送れなかったときに本文と札を戻すこと、日本語入力、会話面の文字サイズ、ダークテーマは変えていない。元に戻す(Ctrl+Z)とやり直し(Ctrl+Y / Ctrl+Shift+Z)はエディターの履歴で行う。送った後と戻した後の書き換えは履歴に載せない。 **箱の高さは中身に合わせて伸びる。** 空のときはテキストエリアの `rows="2"` と同じ二行の高さで、書くほど伸び、窓の高さの 4 割を超えると中でスクロールする。テキストエリアのような手で高さを変えるつまみは無くした(AI 判断)。placeholder(「部屋に発言する」)は、本文が空の一行のときだけ欄の外側の要素が描く(`#input[data-empty]`)——エディターの中身を placeholder のために描き直さないためである。 **使うライブラリのライセンス。** 入力欄が使うライブラリと、それらが使うライブラリはすべて MIT か BSD-3-Clause である(GPL など縛りの強いものは入れていない)。採用したバージョンごとの名前・ライセンス・著作権表示とライセンス本文は `THIRD-PARTY-NOTICES.txt` にまとめ、インストーラーに `NOTICE.txt` と一緒に同梱する(`src-tauri/tauri.conf.json` の `bundle.resources`)。このファイルは `scripts/third-party-notices.mjs` が `package-lock.json` と各パッケージの LICENSE から作る。 | パッケージ | 版 | ライセンス | |---|---|---| | @tiptap/core, @tiptap/pm, @tiptap/extension-code-block, @tiptap/extension-code-block-lowlight, @tiptap/extension-document, @tiptap/extension-paragraph, @tiptap/extension-text, @tiptap/extensions | 3.31.4 | MIT | | lowlight | 3.3.0 | MIT | | highlight.js | 11.11.2 | BSD-3-Clause | | prosemirror-model / -state / -view / -transform / -commands / -keymap / -history / -inputrules / -schema-list / -dropcursor / -gapcursor / -changeset / -tables | 1.25.12 / 1.4.4 / 1.42.6 / 1.12.2 / 1.7.2 / 1.2.3 / 1.5.1 / 1.5.1 / 1.5.1 / 1.8.4 / 1.4.1 / 2.4.4 / 1.8.5 | MIT | | orderedmap, rope-sequence, w3c-keyname | 2.1.1, 1.3.4, 2.2.8 | MIT | | devlop, dequal | 1.1.0, 2.0.3 | MIT | | @types/hast, @types/unist(型の定義だけで、画面には入らない) | 3.0.5, 3.0.3 | MIT | ## 発言の URL とパスは押して開ける(#349) 部屋の発言の本文に出てくる URL とファイルパスを、押せるリンクとして描く(Master 要望 2026-10-09「pullceptのチャット欄でURLとかファイルパスとかリンクさせたい」)。変えたのは見え方と押したときの動きだけであり、発言の本文・ログ・端末へ届く文字は変えない。入力欄と、添付の絵と札(#318)は対象外である。札は今も押して何も開かない。 **リンクになるものは二つである。** - **URL は `http://` と `https://` で始まるものだけ。** 大文字と小文字は問わない。`file:`・`javascript:` などほかの scheme はリンクにしない。 - **パスは Windows の絶対パスだけ。** ドライブのパス(`C:\…`、`D:/…`)と UNC(`\\server\share\…`)である。相対パスはリンクにしない。`\\.\` と `\\?\` で始まるもの(装置と verbatim の形)もリンクにしない。 - **英数字の直後からは始めない。** 語の途中や、URL やほかのパスの中にあるドライブ名を、別のリンクとして拾わないためである。 **リンクは一続きの文字であり、空白の手前で終わる。** パスに使えない文字(`< > " | * ? :`)、バッククォート(コードとして書かれたパス)、文を区切る和文の約物 `、。「」『』【】` でも終わる。名前にはまず現れず、パスの直後に続けて書いた文には必ず現れるためである(AI 判断)。それ以外の全角文字はパスの一部として扱う。そのため `C:\メモ.mdを開いて` のように空白を置かずに文を続けると `を開いて` までがパスになり、押すと「見つかりません」になる。 - **末尾の句読点・引用符はリンクに含めない。** `. , ; : ! ? ' " * >` と `。、,.:;!?〉》」』】` である。 - **閉じ括弧は、リンクの中に対になる開き括弧が無いときだけ外す。** `) ) ] ] } }` が対象である。`…/Foo_(bar)` の括弧はリンクに残り、`(C:\資料)` の括弧は文へ戻る。 **押すと開く。** 押すのはマウスのクリックだけであり、右クリック・長押し・キーボードの挙動は増やしていない。リンクの上で文字を選ぶドラッグでは開かない。 - **URL は既定のブラウザで開く。** - **フォルダはエクスプローラーで開く。** - **ファイルは、その種類を開く既定のアプリで開く。** - **開くと実行されるファイルは、実行せずに場所を示す。** プログラム・スクリプト・インストーラー・ショートカットなど(`.exe`・`.bat`・`.cmd`・`.ps1`・`.vbs`・`.js`・`.msi`・`.lnk`・`.py`・`.reg`・`.iso` など)は、エクスプローラーでそのフォルダを開き、ファイルを選んだ状態にする。発言はだれでも書けるものであり、押すことは実行するという判断ではないためである。一覧は `crates/post-link` の `RUNS_WHEN_OPENED` にある。拡張子は Windows と同じく、名前の末尾の `.` と空白を除いてから読む。 - **存在しないパスは開かず、ステータス行に「見つかりません: <パス>」と出す。** ほかの理由で開けなかったときも、その理由がステータス行に出る。 **何を開くかはアプリの側で決め直す。** 画面のリンクの切り出しは文字の読み方であり、守りではない。押したリンクはアプリの command(`open_post_url` / `open_post_path`)へ渡り、scheme、パスの形、存在するか、開くと実行されるかをそこで判定してから `tauri-plugin-opener` で開く。画面の capability(`opener:default`)は広げていない。UNC のパスは存在を確かめるのにサーバーの答えを待つことがあるため、確かめるのは画面の処理の外で行う。 **リンクはアクセント色(`--accent`)に下線で描く。** 発言者の色と分けるためである。`--accent` はダークで値を持ち替えるため、明暗のどちらも同じ規則で描ける。ポインタを載せると下線が太くなる。長いパスは本文と同じ所で折り返す。 **リンクを作るのは、地の文を受け取って描く部品を返す関数 `linkifyText` である(`src/main.ts`)。** 改行と空白は、本文を `textContent` で描いていたときのまま返る(`.message .body` は `white-space: pre-wrap`)。本文の一部を別の形で描くもの(``` の枠など)は、その外側の文字だけをこの関数へ通せる。 ## HTML の枠を静的にプレビューする(#436) 明示的な `html` fence は、本文ではコードを描かずファイル名の一行だけを出す。この一行は角丸のカードで、ファイルアイコン・名前・「開く ›」を並べ、ホバーで色が変わる(#439)。名前は、開く行の `html` の後の文字(`` ```html report.html `` なら `report.html`)、無ければ HTML の `