Skip to content

v3.3.0 — プレビューの複数選択+まとめて移動 / Desktop の更新通知とアセットダウンロード補助 - #3

Merged
bafv4 merged 5 commits into
mainfrom
dev
Aug 29, 2026
Merged

bafv4 merged 5 commits into
mainfrom
dev

Conversation

@bafv4

@bafv4 bafv4 commented Aug 28, 2026

Copy link
Copy Markdown
Owner

v3.3.0 のリリース範囲。機能 2 件+クリーンアップ 2 件+バージョン更新(5 コミット / 21 ファイル / +1589 −132)。

1. プレビュー: エリアの複数選択とまとめて移動(1179d2f, 9ebfaf8)

2. Desktop: 起動時の更新通知+リリースアセットのダウンロード補助(33384b2, d816881)

CLAUDE.md「自動更新なし」の範囲内での実装です。自動適用はしません(適用=インストーラ実行や exe 差し替えはユーザー操作)。

  • 起動時に 1 回だけ GitHub Releases の最新版をチェックし、新版があればタブ上部にバナーを表示。オフラインや API 失敗は console.warn のみで起動を妨げません。プレリリースは通知対象外。
  • 「ダウンロード」で配布形態に合うアセットを実行ファイルと同じフォルダへ保存(進捗 % 表示付き)。
    • Windows: uninstall.exe の有無(NSIS)/ Program Files 配下(MSI)で判定し、それ以外はポータブル exe。ポータブルはリネームされ得るためファイル名に依存せず、判別不能時もポータブルに倒します。
    • macOS: /Applications 配下なら dmg、それ以外はポータブル zip。.app 内で動作中はバンドルの外へ保存。
    • 書き込み不可(Program Files 等)なら OS のダウンロードフォルダへフォールバックし、実際の保存先を表示。
  • ダウンロードは Rust(reqwest / rustls)でストリーミング。.part → rename なので中断で壊れたファイルが残りません。URL は自リポジトリの releases/download/ 接頭辞のみ許可(webview からの悪用防止)。
  • 実装は src-tauri/src/update.rs に分離。ExternalLink を ui/ に共有化したため、AboutModal の 8 リンクも Tauri webview で確実に開くようになりました(target=_blank は webview で環境依存の無反応になるため opener 経由に)。

検証

  • cargo check / cargo test 2 件(semver 比較・アセット選択)/ tsc / eslint(error 0、既存 warning 2 件のみ)/ vite build 通過。ビルド成果物に v3.3.0 が埋め込まれることを確認。
  • Web バンドルへの Tauri コード漏れなし(desktopUpdate は遅延チャンク、index チャンクに opener/update 参照 0)。Web 実機でバナー非表示・GitHub への fetch 0・コンソールエラー 0。
  • 複数選択のアルゴリズムは実装関数(boundingBox/snapMove/floorCell)を合成して 5 ケース検証(剛体性・外接矩形中央への磁着・個別エリア中央では磁着しないこと・境界クランプ・広い選択の寸法表示)。store の選択追跡(preparing 削除時の index 詰め直し)も実測確認。
  • コードレビュー: 複数選択は 8 アングルレビューで 10 件検出し全件修正済み。/simplify 4 アングルの指摘 19 件も適用済み。

レビュー時の注意

  • Tauri 実機での更新バナー〜ダウンロードの動作確認は未実施です(開発環境の制約)。package.json のバージョンを一時的に 3.2.0 未満へ下げて pnpm tauri dev を起動すると、通知〜ダウンロードまで再現できます。
  • 実ポインタ操作での複数選択ドラッグの目視確認も未実施(アルゴリズム検証と型・lint のみ)。マージ前に dev サーバで 2 エリアの Ctrl+クリック → ドラッグを一度確認することを推奨します。
  • リリースアセットの命名は update.rs の pick_asset と結合しています(release.yml に注記済み)。ポータブル版のファイル名を変えると更新通知がアセットを見つけられなくなります。
  • v3.3.0 タグを push すると release.yml がドラフトリリースを生成します。

🤖 Generated with Claude Code

bafv4 and others added 5 commits August 28, 2026 23:04
Ctrl/⌘+クリックで main / locked / preparing を複数選択でき、選択中のどのエリアから
ドラッグしても選択全体がまとめて動く。スナップと中央ぞろえの指示線は個々のエリアでは
なく**選択全体の外接矩形**の辺・中央で判定する。リサイズハンドルは単独選択時のみ。

- 移動は「floor した外接矩形の変位」を整数化してから全メンバーへ同量加算する剛体移動。
  相対位置が 1px も崩れず、±MAX_DIMENSION のクランプ境界でも移動量側を制限して
  グループが変形しない。座標系の不変条件(整数のみ)は従来どおり store 側で担保。
- store に moveAreas を追加し、グループ移動は 1 回の set() でまとめて反映
  (pointermove ごとに N 回の再レンダリング・永続化が走らないように)。
- Shift はトグルに使わない。従来の「ドラッグ中のスナップ無効」に割当済みで、
  両方に割り当てると Shift+ドラッグ開始が不能になるため(レビューで検出し修正)。
- 複数選択のメンバーを移動なしでクリックしたら単独選択へ縮退(開始閾値 3px)。
  preparing の選択参照は index ベースのため、削除で別エリアに付け替わらないよう
  配列が縮んだら preparing の選択を落とす。
- boundingBox は「座標・スケールは core/coords.ts に集約」の規約に従い core に配置。
- 検証: tsc / eslint 0 エラー。コードレビュー(8 アングル)で 10 件検出し全件修正。
  実装関数(boundingBox/snapMove/floorCell)を合成したアルゴリズム検証 5 件通過
  (剛体性・bbox 中央への磁着・個別エリア中央では磁着しないこと・境界クランプ・
  広い選択の寸法表示)。ブラウザペイン非表示の制約で実ポインタ操作の目視は未実施。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
起動時に GitHub Releases の最新版を 1 回チェックし、新しいバージョンがあれば
タブ上部の横断バナーとトーストで通知する。「ダウンロード」で配布形態に合う
アセットを**実行ファイルと同じフォルダ**へ保存する(進捗 % 表示付き)。
自動適用はしない(CLAUDE.md「自動更新なし」の範囲内。適用はユーザー操作)。

- 配布形態を自動判定してアセットを選ぶ。Windows は uninstall.exe の有無
  (NSIS)と Program Files 配下か(MSI)で判定し、**それ以外はポータブル exe**。
  ポータブルはリネームされ得るためファイル名に依存せず、判別不能時も
  ポータブルに倒す。macOS は /Applications 配下なら dmg、それ以外は
  ポータブル zip。アセット命名は release.yml の実物(v3.2.0)で照合。
- ダウンロードは Rust 側(reqwest / rustls)でストリーミング。`.part` に
  書いてから rename するので中断で壊れたファイルが残らない。実行フォルダが
  書き込み不可(Program Files 等)なら OS のダウンロードフォルダへ
  フォールバック。macOS は .app の中に埋もれないよう .app を含むフォルダへ。
- URL は Rust 側で自リポジトリの releases/download/ 接頭辞のみ許可
  (webview からの任意ダウンロードへの悪用防止)。ファイル名も検証。
- チェック失敗(オフライン・レート制限)は console.warn のみで起動を妨げない。
  プレリリースタグは semver 3 要素に解釈できないため通知対象外。
- リリースページのリンクは opener 経由(Tauri webview の target=_blank は
  環境依存で無反応のため)。権限追加は不要(opener:default が openUrl /
  revealItemInDir を含むことをプラグイン実物で確認)。
- 既知の制限: Tauri 実機での表示・ダウンロードの確認は未実施(この環境では
  tauri dev を起動できない)。package.json のバージョンを一時的に下げて
  起動すると通知〜ダウンロードまで再現できる。
- 検証: cargo check / cargo test(semver 比較・アセット選択の 2 件)通過。
  tsc / eslint 0 エラー、vite build 成功。Web バンドルへの漏れなし
  (desktopUpdate は遅延チャンク、index チャンクに tauri/opener 参照 0)。
  Web 実機でバナー非表示・GitHub への fetch 0・コンソールエラー 0 を確認。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/simplify レビュー(4 アングル)の指摘の適用(プレビュー側)。

- エリア選択を WallPreview のローカル state から store(ui.selectedAreas)へ移す。
  preparing の選択は index 参照のため、削除時に「配列が縮んだら preparing の選択を
  全部落とす」という応急処置を入れていたが、removePreparing が削除と同じ set() 内で
  index を詰め直す形に変えた(removeBackgroundLayer が selectedBackgroundLayerId を
  直すのと同じ流儀)。{#1, #3} 選択中に #1 を消しても #3 の選択が正しく追跡される。
- AreaTarget を store の公用語として export し、AreaMove = AreaTarget & {x,y} に。
  WallPreview 側の AreaRef は AreaTarget | layer の合成になり、moveAreas への
  変換に使っていた再タグ付け switch(到達不能な layer 分岐込み)が消えた。
- selectedRefs の identity を安定化(何も落ちなければ入力をそのまま返す)。
  ドラッグ中は layout.preparing の identity が毎フレーム変わるため、従来は
  選択が不変でも Set の再構築とハンドラの再生成が毎イベント走っていた。
- initDragDerived を items 配列を直接受ける形に、二重ネストの if を一本化、
  クリック縮退ガードの冗長条件(pressedRef が含意する mode/件数)を削除。
- 検証: tsc / eslint 0 エラー。dev サーバで store を直接呼び、removePreparing の
  選択詰め直し({#0,#2} 選択で #0 削除 → 旧 #2 を index 1 として追跡)、
  空選択の DEFAULT_AREA_SELECTION への縮退、reset 時の初期化を確認。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/simplify レビュー(4 アングル)の指摘の適用(更新機能側)。

- Rust の更新機能一式(約 380 行、lib.rs の 44%)を src-tauri/src/update.rs へ分離。
  lib.rs は自身のヘッダが定義する「ファイル I/O コマンドの最小表面積」に戻る。
  併せて #[allow(unreachable_code)] 等 3 つのサプレッションを撤去し、
  detect_install_kind / pick_asset / update_dest_dir を OS ごとの cfg 付き関数定義に分割。
- 効率: reqwest::Client を OnceLock で共有(TLS 設定とプールを check/download で再利用)、
  ダウンロードを 1MB BufWriter に(8〜16KB チャンク直書きだと 100MB 級で数千 syscall)、
  sync_all を削除(.part → rename で完全性は担保済み。再取得可能な成果物に fsync は
  不要で、async ワーカーを数百 ms〜数秒ブロックしていた)、書き込み可否プローブを
  .part 作成自体に統合。
- ExternalLink を ui/ に昇格して adapter の openExternalUrl 経由に。Tauri webview では
  a[target=_blank] が環境依存で無反応になるため、バナーだけでなく AboutModal の
  8 リンク(Desktop でも表示される)が確実に開くようになる。
- バナーの更新トーストを削除(直下に出るバナーと同文の二重通知)。付随する
  toastShown / dismissedVersion グローバルと update.toastAvailable キー(ja/en)も削除。
  サイズ表示は新設の core/format.ts formatBytes に統一。
- release.yml にアセット命名が update.rs の pick_asset と結合している旨を注記
  (リネームすると更新通知がアセットを見つけられなくなる契約を両端に明記)。
- 検証: cargo check / cargo test 2 件 / tsc / eslint(error 0)/ vite build 通過。
  Web バンドルへの漏れなし(index チャンクに opener/update 参照 0)。Web 実機で
  AboutModal のリンクが openExternalUrl 経由で開くこと、コンソールエラー 0 を確認。
  Tauri 実機での動作確認は引き続き未実施。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
プレビューの複数選択とまとめて移動、Desktop の更新通知+リリースアセットの
ダウンロード補助、および /simplify リファクタ 2 件をリリース範囲とする。

- package.json / src-tauri/Cargo.toml を 3.3.0 に更新。tauri.conf.json は
  package.json 参照なので変更不要。Cargo.lock は cargo check で追随済み。
- 検証: cargo check 通過。vite build の成果物に v3.3.0 が埋め込まれることを確認。

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

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
wall-maker Ready Ready Preview Aug 28, 2026 2:31pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01e4462741

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/update.rs
Comment on lines +148 to +149
InstallKind::Installed => find_asset(assets, |n| n.ends_with("-setup.exe"))
.or_else(|| find_asset(assets, |n| n.ends_with(".msi"))),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve MSI installs when selecting the update asset

For Windows installations detected through the Program Files check—specifically the MSI case without uninstall.exe—kind is still only Installed, so this branch always selects the NSIS -setup.exe while both formats are published by .github/workflows/release.yml. An MSI user will therefore be directed into a different installer technology, which can create separate installation/uninstall registrations instead of upgrading the existing MSI installation; retain NSIS versus MSI in the detected install kind and select the matching asset.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/update.rs
Comment on lines +262 to +264
if file_name.is_empty()
|| file_name.chars().any(|c| c == '/' || c == '\\' || c == ':')
|| file_name.contains("..")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bind the destination filename to the release asset URL

The IPC caller supplies file_name independently of url, and this validation only rejects traversal characters. Consequently, a caller can provide a valid repository release URL together with the basename of any existing file in the executable directory (or downloads fallback), after which lines 330–332 delete that file and replace it with the downloaded asset. This is especially risky for a portable executable kept alongside user files; verify that the filename is the decoded final asset component of the approved URL or otherwise derive it server-side.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/update.rs
Comment on lines +303 to +305
let chunk = resp.chunk().await.map_err(|e| {
let _ = fs::remove_file(&part);
format!("ダウンロード中に切断されました: {e}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Close the temporary file before removing it on failure

On Windows, if the connection is interrupted, this cleanup attempts to remove .part while both writer and file still hold the file open. Windows rejects deletion of an open file, the error is discarded, and unwinding closes the handles only after the removal attempt, so every failed download leaves the partial file behind despite the stated cleanup guarantee. Drop the writer/file before removal or use a guard whose cleanup runs after the handles close; the write and flush error paths have the same ordering issue.

Useful? React with 👍 / 👎.

@bafv4
bafv4 merged commit 38dab2b into main Aug 29, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — 01e44627 Deployed Aug 28, 2026 by vercel[bot]
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.

1 participant