Skip to content

feat: 編み図の一覧に縮小画像と更新日時を出す - #124

Merged
K0mork merged 2 commits into
mainfrom
feat/chart-list-thumbnails
Oct 6, 2026
Merged

K0mork merged 2 commits into
mainfrom
feat/chart-list-thumbnails

Conversation

@K0mork

@K0mork K0mork commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

変更内容

「編み図」パネルの一覧の各項目に、盤面の縮小画像、寸法、更新日時を出すようにしました。Web版とiOS版の両方に入ります。

  • 縮小画像(64px角の枠): 各セルを記号の色で塗ったモザイクです。複数のセルにまたがる記号は占めるセルをすべて塗り、空きのセルと「白くする」は白にします。長辺が96セルを超える盤面は数セルを1画素にまとめ、色を面積で平均します(1000×1000なら11×11セルで1画素)。縦横比を保って枠に収め、盤面の外は枠の地色で見分けます。
  • 寸法: 「20×20」を「20段×20目」にしました。
  • 更新日時: 今日は「今日 14:05」、昨日は「昨日 9:30」、今年は「10月6日 14:05」、それより前は「2025年10月6日 14:05」と書きます。編集して自動保存されると、開いている一覧でも縮小画像と更新日時が新しくなります。
  • ボタンの配置: 名前変更・複製・削除のボタンを、縮小画像と名前の下の段に同じ幅で並べました。以前は名前の右に詰めていたため、狭い画面では1つのボタンが幅45px前後でした。今はスマホ幅で1つ100px前後あります。高さは以前と同じ(Web版34px、iOS版44px)です。
  • 読み上げ: 項目のボタンは「名前、20段×20目、更新 今日 14:05」と読みます。開いている編み図にはaria-currentを付けます。縮小画像は読み上げません。名前を先頭に置いたので、iOSのUIテストが名前の前方一致で項目を探す処理もそのまま使えます。

EditorView.tsxの変更は一覧の部分を新しいui/DocumentList.tsxへ差し替えた数行だけです。#115 や #120 と同じ箇所の大きな書き換えを避けるため、新しい処理は新しいファイルに分けました。

レビュー後の修正

  • 縮小画像を覚えておく置き場(ThumbnailCache)が、最初に受け取ったセル配列の参照を持ち続け、一覧を読み直すたびに古いセル配列(1000×1000で1件4MB)が残っていました。比較を編み図のID・寸法・更新日時だけにし、セル配列を持たないようにしました。同じセル配列の参照で更新日時だけが変わったときに古い縮小画像を返していた条件も無くしました。一覧から消えた編み図は従来どおり捨て、覚えておく数に上限(200件、最近使っていないものから捨てる)も設けました。
  • 一覧を開いたまま日付が変わると「今日」が残っていたので、次の0時を過ぎたときと、画面に戻ったとき(スリープ中はタイマーが遅れるため)に描き直すようにしました。
  • 長辺が63〜96画素の縮小画像は枠(内側62px)より大きく、pixelatedで縮めると等倍の画面で段や目が抜けて見えました。枠より大きいときだけ滑らかに縮めます。枠より小さい縮小画像は従来どおりセルの境目をぼかさずに拡大します。
  • thumbnail.test.tsの所要時間のコメントを、実測(手元のNode.jsで1回25〜28ms、およそ26ms)に合わせました。

決めたこと

Issue で「決める」とされていた点は、次のように決めました。

  1. 縮小画像は保存しない。一覧に出すときに、保存済みのセル配列から作る。
    • 理由: 一覧の元になるlistDocumentsは、起動時と一覧の更新時に、すでに全編み図のセル配列を読み込んでいます。そのため、縮小画像を保存しても「全部の盤面を読み込む」費用は減りません。減らせるのは画像を作る計算だけです。その計算は、記号を描かずにセル配列を1回なめるだけにしました。1000×1000を埋めた盤面でも、手元のNode.jsで1件およそ26msです。
    • 重くならないようにする工夫: 縮小画像は画面に入った項目から作ります(IntersectionObserver)。編み図が多くても、一覧を開いた時点では見えている数件分しか計算しません。作った画像は、編み図ごとに更新日時が変わるまで覚えておきます。一覧を閉じて開き直したり、別の編み図の保存で一覧が描き直されたりしても、作り直しません。
    • 保存しない利点: IndexedDBの記録も.knitも形式が変わらないので、DBの版上げも移行も要りません。既存のデータと、Web版・iOS版の.knitの互換に影響しません。DBの版は変えていないので、DBの版を変えていない feat: 最後のバックアップ日時を表示し、.knitの書き出しを勧める #120 の設定ストアへの追加とも衝突しません。保存した縮小画像と盤面が食い違うこと(保存の失敗や旧版での編集)も起こりません。
  2. .knitには含めない。 1と同じ理由です。復元した編み図は、一覧に出たときにセル配列から作ります。
  3. 縮小画像はモザイクにする。 一覧では記号を読ませるのではなく、どの編み図かを見分けられればよいためです。Issue の「記号の色が分かる程度」に合わせました。記号の形は出さないので、同じ色の表目と裏目は見分けられません。
  4. 日時は「今日」「昨日」を使う。 一覧で短く読めるようにしました。年をまたぐ日も「昨日」になります。書式は feat: 最後のバックアップ日時を表示し、.knitの書き出しを勧める #120 の「2026年10月6日 14:05」とそろえました。
  5. 旧形式のセルは盤面として読み込むときと同じに扱う。 記号IDだけを持つセルは黒の記号として描きます。未知の記号と盤面からはみ出す記号はBoardと同じく描きません。

実行した確認

  • npm run typecheck: 成功
  • npm test: 30ファイル・191件成功(レビュー後の修正の時点)
  • npm run build: 成功
  • npm run check:dist: 成功
  • npm run test:e2e: chromium-mobile、webkit-mobile、chromium-desktopで106件成功、2件は既存のスキップ。1回目は、今回触れていないPNGの太線のテスト(editor.spec.ts)がwebkit-mobileで1回だけ失敗しました。単独の再実行(webkit-mobileで8回)と全体の再実行では成功しています。
  • iOS Web: tsc -p tsconfig.app.json --noEmit 成功、vitest run --config vite.config.ts 3ファイル・8件成功
  • xcodegen generate --spec ios/project.yml: プロジェクトの差分なし
  • Simulator向けのアプリのビルド(CODE_SIGNING_ALLOWED=NO)とios/scripts/check-app-bundle.sh: 成功
  • 追加したテスト:
    • model/thumbnail.test.ts(作成、面積平均、旧形式、1000×1000を250ms以内、覚えておく置き場がセル配列を持たないこと・更新日時や寸法が変われば作り直すこと・消えたIDを捨てること・上限)
    • storage/thumbnailCompatibility.test.ts(以前の版の記録と旧形式のセル、IndexedDBの記録と.knitの項目が変わらないこと、Web/iOS共有fixtureの縮小画像が盤面として読み込んだ結果と一致すること)
    • ui/documentListText.test.ts(次の0時までの時間を含む)
    • ui/DocumentList.test.tsx(開いたまま0時を過ぎたときと画面に戻ったときの書き直し、枠より大きい縮小画像だけ滑らかに縮めることを含む)
    • tests/e2e/document-list.spec.ts(縮小画像と更新日時が出て、編集後に新しくなること。長い名前でもボタンが項目に収まり、押せる大きさで重ならないこと。1000段×250目の縮小画像が縦横比を保って枠に収まり、滑らかに縮めること。20×20はpixelatedで拡大すること)
  • 枠の指定を誤って縮小画像が枠からはみ出す状態にすると、最後のE2Eが失敗することを確かめました(実装中に実際にこの不具合があり、直しました)。
  • 画面の確認: 色のある30×30、1000×1000、縦長の60×20、前年の編み図を並べ、次の幅でスクリーンショットを撮って確かめました。
    • スマホ幅: iPhone 14のWebKit、Pixel 7のChromium、幅320px
    • デスクトップ幅: 1280px
  • Simulator: 停止中のiPhone 16とiPad(第10世代)のSimulatorにこのビルドを入れました。端末内にあった既存の編み図(以前の版で保存したもの)の一覧に縮小画像と更新日時が出ることを確かめ、確認後に停止しました。編集やデータの削除はしていません。

スクリーンショットは手元で確認済みですが、PRへの添付は未了です。

できなかった確認

  • iOSのSimulatorのテスト一式、アプリ更新の復元、Release Archiveは、Swiftとビルド設定を変えていないため手元では実行していません。PRのCIに任せます。
  • 手元のNode.jsは26、Xcodeは27で、指定の版やCIとは違います。
  • 実機では確かめていません。

懸念

  • 将来、一覧の読み込みでセル配列を読まないように変える場合は、縮小画像を保存する方式に切り替える必要があります(そのときはDBの版上げと移行が要ります)。
  • 同じ色の表目と裏目のように、色が同じで記号だけが違う模様は、縮小画像では見分けられません。

Closes #92

🤖 Generated with Claude Code

The chart panel now shows a small color mosaic of each board, its size,
and when it was last saved. Thumbnails are built from the saved cells
when the list is shown and are not stored, so the IndexedDB records and
the .knit format stay unchanged. The rename, duplicate and delete
buttons move to their own row so they keep a usable width on phones.

Closes #92

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 6, 2026 17:29
@K0mork K0mork added the enhancement New feature or request label Oct 6, 2026

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@K0mork K0mork left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

レビュー(8d95cfe)

CI

CI and deploy Pages はすべて成功しました(ci-gate 成功。deploy・smoke はPRなのでスキップ)。

  • web: Vitest 30ファイル・184件成功、Playwright 106件成功・2件スキップ(再試行なし)
  • ios_web、release_archive、ios (iPhone 17)、ios (iPad (A16))、app_update (iPhone 17)、app_update (iPad (A16)): 成功
  • CodeQL、app_store_docs、changes: 成功

iOSのUIテストは、一覧の項目を label BEGINSWITH "M2切替A" などの前方一致で探し、作成ボタンを app.buttons["新しい編み図"] の完全一致で探します。読み上げ文は名前で始まり、作成ボタンの名前とも完全一致しないので、どちらにも影響はありません(Simulatorのジョブも成功)。

確かめたこと(問題なし)

  • 縮小画像の作り方は Board の読み込みと同じです。旧形式のセルは boardFromDocument と同じ条件で黒の記号として読み、未知の記号と盤面からはみ出す記号は描きません。複数のセルにまたがる記号は、占めるセルをすべて数えます。
  • 面積平均では、端の画素の面積と空きのセル(白)も数えます。壊れたデータで記号が重なっても、面積を超えて数えません。
  • IntersectionObserver が無い環境では、すぐに描きます。
  • 日時は端末の時刻で書きます。「昨日」は前日の正午を基準にして求めるので、夏時間の切り替え日でも誤りません。年をまたぐ日もテストで確かめています。datetime 属性はISO 8601です。
  • IndexedDBの記録、DBの版、.knit の形式は変えていません。互換テスト(thumbnailCompatibility.test.ts)もあります。
  • packedのセル配列のまま処理しており、セルをDOMにしていません。
  • ボタンの高さは --compact-tap-size(Web 34px/iOS 44px)を保っています。E2Eで、長い名前でもボタンが幅64px以上を保ち、重ならないことを確かめています。
  • 差分、PR本文、コミットメッセージ、開発ログに機密情報はありません。Closes #92 とラベル enhancement があります。開発ログは書式どおりで、CIの結果とも合っています。

指摘

  1. 中: ThumbnailCache が古いセル配列を持ち続ける。 キャッシュの項目は最初の cells(ArrayBuffer)を保持します。get で更新日時が一致したときは、その古い参照を差し替えずに返します。名前変更・複製・削除・新規作成・復元のあとの refreshDocuments() は listDocuments() を読み直すので、すべての編み図が新しいArrayBufferになります。その結果、縮小画像を作った編み図ごとに、古いセル配列が1つずつ余分にメモリに残ります。1000×1000では1件4MBで、キャッシュはモジュールに置かれているため、一覧を閉じても解放されません。比較を id・rows・cols・updatedAt だけにして、項目に cells を持たせないのがよいと思います。少なくとも、一致したときに参照を差し替えてください。
  2. 低: テストのコメントと実測値が合っていない。 thumbnail.test.ts のコメントは「手元では数ms」ですが、PR本文と開発ログでは「およそ26ms」です。どちらかに合わせてください。
  3. 低(参考): 一覧を開いたまま日付が変わると、再描画されるまで「今日」の表示が残ります。また、長辺が65〜96セルの盤面は、image-rendering: pixelated のため、等倍の画面では縮小時に一部の段や目が抜けて見えます。どちらも実害は小さく、このPRで直さなくてもよいと思います。

満たしていない完了条件

  • Issue #92 の完了条件「iPhoneとiPadで表示を確かめ、スクリーンショットをPRに付ける」と、AGENTS.mdの「UI変更のスクリーンショットをPRに含める」が未了です(本文にも「添付は未了」とあります)。スマホ幅とデスクトップ幅、iPhoneとiPadのスクリーンショットの添付が必要です。

マージはしていません。

The cache kept the first cell ArrayBuffer of every chart, so each list
reload left one stale copy per chart (4 MB at 1000x1000). Compare only
the id, size and update time, cap the cache at 200 entries, redraw the
list when the date changes, and scale thumbnails larger than the frame
smoothly so rows are not dropped.

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

K0mork commented Oct 6, 2026

Copy link
Copy Markdown
Owner Author

レビュー指摘の修正(2aed27b)

  • 中: ThumbnailCache が古いセル配列を持ち続ける → 比較をID・寸法・更新日時だけにし、セル配列を持たないようにしました。同じセル配列の参照で更新日時だけが変わったときに古い縮小画像を返していた条件も無くしました。一覧から消えた編み図は従来どおり捨て、覚えておく数に上限(200件、最近使っていないものから)を設けました。Vitestで、置き場から受け取ったセル配列をたどれないこと、更新日時・寸法が変われば作り直すこと、消えたIDが残らないこと、上限を確かめています(修正前の実装では参照・更新日時・上限のテストが失敗することも確認)。
  • 低: 所要時間の食い違い → 実測は1000×1000で1回25〜28ms(手元のNode.js)だったので、テストのコメントを「およそ26ms」に合わせました。
  • 低: 日付が変わっても「今日」が残る → 次の0時を過ぎたときと、画面に戻ったとき(スリープ中はタイマーが遅れるため)に描き直します。
  • 低: 長辺65〜96セルで段や目が抜ける → 枠(内側62px)より大きい縮小画像だけimage-rendering: autoで滑らかに縮めます。小さい盤面は従来どおりpixelatedで拡大します。E2Eで両方の描き方を確かめています。

確認

  • 手元: npm run typecheck、npm test(30ファイル・191件)、npm run build、npm run check:dist、npm run test:e2e(106件成功・2件は既存のスキップ)、iOS Webのtypecheckとテスト(3ファイル・8件)がすべて成功。
  • CI(2aed27b): ci-gate 成功。web、ios_web、release_archive、ios (iPhone 17)、ios (iPad (A16))、app_update (iPhone 17)、app_update (iPad (A16))、app_store_docs、changes、CodeQL がすべて成功(deploy・smoke はPRなのでスキップ)。

残っていること

  • スマホ幅・デスクトップ幅、iPhone・iPadのスクリーンショットのPRへの添付は未了です。

マージはしていません。

@K0mork
K0mork merged commit 736dd6d into main Oct 6, 2026
15 checks passed
@K0mork
K0mork deleted the feat/chart-list-thumbnails branch October 6, 2026 23:34
@K0mork

K0mork commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

デプロイ確認

main の e1a2001(#120 のマージ。#118・#123・#115・#124・#126・#120 をすべて含む)の CI and deploy Pages(run 37560031792)が成功しました。

  • ci-gate: 成功
  • deploy: 成功(github-pages 環境の最新のデプロイが e1a2001)
  • smoke: 成功(本番の HTML が配信物と一致し、参照する /assets/ などが取れること)
  • そのほかのジョブ(web、ios_web、ios(iPhone 17・iPad (A16))、app_update(両端末)、release_archive)もすべて成功

途中の 736dd6d(#124)と 2bde0a3(#126)の run は、iOS の UI テストの失敗で ci-gate が通らず、配信されていません。前者は #115 と #123 の組み合わせで壊れたテスト(#126 で修正)、後者は Simulator の一時的な失敗です。

本番(https://knittingeditor.com/)での確認

Playwright で、Android Chrome 相当(Pixel 7)、iPhone Safari 相当(iPhone 14、WebKit)、デスクトップ幅(1440×900)の Chromium と WebKit の4通りで確かめました。どれも成功し、ページのエラーは出ませんでした。

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1] 編み図の一覧に縮小画像と更新日時を表示する

2 participants