Skip to content

feat: 最後のバックアップ日時を表示し、.knitの書き出しを勧める - #120

Merged
K0mork merged 4 commits into
mainfrom
feat/backup-reminder
Oct 7, 2026
Merged

K0mork merged 4 commits into
mainfrom
feat/backup-reminder

Conversation

@K0mork

@K0mork K0mork commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

変更内容

  • 編み図ごとに、最後に.knitを書き出した日時を記録し、「保存」パネルのバックアップ欄に「この編み図の最後のバックアップ:2026年10月6日 14:05」(未書き出しなら「この編み図はまだバックアップしていません。」)と表示します。「この編み図」は開いている編み図、「全データ」はその時点の全編み図の日時を記録します。
  • 変更があるのに長く書き出していないとき、または一度に多く編集したときに、道具列と盤面の間へ書き出しを勧める帯を1段出します。帯の「書き出す」で開いている編み図の.knitを書き出し、「あとで」で一定期間出しません。
  • Web版とiOS版の両方に入ります(共通コードpackages/editor-core)。使い方ページ(Web・iOS)のバックアップの節に説明を1段落足しました。ios/docs/WEB_SYNC.mdに共通ファイルとWeb版の扱いを書きました。

レビュー後の修正(a0d189c)

  • なぞり描きの途中で帯が出て盤面がずれる不具合を直しました。 指・ポインタを画面に置いている間(1本指の描画、2本指の移動・拡大、範囲選択の途中を含む)は帯を出すまでの待ち時間を止め、すべて離してから2.5秒を数え直します。画面全体のpointerdown・pointerup・pointercancelを見るので、BoardCanvasは変えていません(盤面はポインタを捕まえるので、離した通知は盤面の外でも届きます)。
  • 書き出しを取りやめても記録される不具合を、Web版で直しました。 EditorPlatform.saveFileは{ saved }を返します。savedは保存・共有を終えたらtrue、取りやめたらfalse、hostが結果を返さないときはundefinedです。falseのときは日時を記録せず、帯も残します。iPhone・iPadのSafariでは、確認ダイアログの「閉じる」・背景のタップ・Escapeと、共有シートの取り消しでfalseになります。
  • iOS版のネイティブ側は iOS版: .knitの書き出しを取りやめたことをWebへ返し、最後のバックアップとして記録しない #122 に分けました。 Swift側は取り消しを知っていますが、変える箇所(finishExport、completionWithItemsHandler、cleanupPendingExport)がPR #121の変更とそのまま重なるためです。iOS版はこれまでどおり書き出しを始めた日時を記録し、iOSの使い方ページにもそう書きました(「保存画面を途中で閉じた場合も含みます」)。
  • ui/BackupReminder.tsxを中身のコンポーネント名に合わせてui/BackupReminderBar.tsxへ改名しました。
  • E2Eで帯が「出ない」ことをwaitForTimeout(300)の後に確かめていた箇所は、「保存」パネルに日時が出る(記録を読み終えた)のを待ち、パネルを閉じた同じ描画で帯が無いことを確かめる形にしました。そのため、「あとで」の記録も読み終えてから日時を出すようにしています。

決めたこと

利用者が不在のため、Issue本文と既存UIに合わせて次のように決めました。数値はいずれも定数1か所で変えられます(packages/editor-core/state/backupReminder.ts・useBackupReminder.ts)。

  • 記録の単位: 編み図ごと。「全データ」の書き出しは全編み図の日時を更新する。
  • 保存先: IndexedDBの設定ストアにlastBackupAt:<編み図ID>として置く。編み図の記録(ChartDocument)には入れない。入れると.knitに書き出され、復元した編み図が「書き出し済み」の日時を持ち込むため。DBの版・編み図の記録・.knitの形式は変えていないので、既存データは「まだバックアップしていない」として読める(移行テストあり)。編み図を削除すると記録も消す。
  • 書き出した日時の定義: 利用者が保存・共有を終えた時点。取りやめたら記録しない。ただし結果が分からないhostでは、ファイルを渡した時点とする。PCなどのダウンロード(ブラウザの保存先選択を取り消したかは分からない)、iPhone・iPadのSafariで共有できずダウンロードへ切り替えたとき、iOS版のネイティブの保存画面・共有シート(#122で結果を返す予定)がこれに当たる。
  • 処理中の表示: ファイルを渡し終えた時点で消す。iPhone・iPadのSafariで確認ダイアログを閉じるまで「バックアップを処理中」を出し続けないため。PNG・PDFの保存の流れは変えていない。
  • 勧める条件(すべて満たすとき): 最後の書き出し(まだなら作成)の後に変更がある/「あとで」の期間外/書き出し(まだなら作成)から7日以上、またはこの画面で50回以上編集(なぞり描き・貼り付け・盤面設定・元に戻す/やり直すを各1回)。7日はSafariがWebサイトのデータを7日使わないと消しうることに合わせた。変更の無い編み図には、どれだけ古くても出さない。
  • 「あとで」: 3日間、全編み図で出さない。期間と編集回数はリセットする。端末に記録するので再読み込み・再起動後も続く。「あとで」の記録を読めなかったときは、その画面では勧めない。
  • 表示の仕方: 盤面に重ねず、道具列と盤面の間に1段だけ出す。ドロワーを開いている間と処理中は出さない。帯が出ると盤面が1段下がるので、指・ポインタをすべて離し、編集の手を2.5秒止めてから出す。開いた時点で条件を満たすときはすぐ出す。
  • Web版: 同じ表示を出す。Web版もブラウザのデータ消去やSafariの保存期限で端末内データが消えうるため。差分は設けない。
  • PR feat: 編み図で使っている色の一覧から色を選べるようにする #115・#121との衝突を小さくする工夫: EditorView.tsxは帯の追加とExportControlsへの1属性だけにとどめ、PR #115が変える道具列・記号ピッカーの周辺には触れていない。WEB_SYNC.mdは#115と離れた行に足した。E2Eは別ファイル(tests/e2e/backup-reminder.spec.ts)にした。Swift(#121が変えるWebViewContainer.swift)は変えていない。

判断していただきたい点

レビューで挙がった次の点は、利用者の判断事項としてそのままにしています。

  • 勧める頻度: 書き出した後も、同じ画面で50回編集するとまた帯が出ます(新しい編み図でも、50回編集すればその日のうちに出ます)。閾値(50回)と「あとで」の期間(3日)でよいか。
  • アップデート直後の既存利用者への文言: これまで.knitを書き出していた人も記録が無いので「この編み図はまだバックアップしていません。」と出ます(作成から7日以上で変更のある編み図を開くとすぐ帯が出ます)。「このアプリで書き出した記録がありません」のような文言に寄せるか。
  • 名前の変更: 名前を変えただけでもupdatedAtが進むので「変更あり」になります(.knitに名前が入るので妥当とも言えます)。このままでよいか。
  • (参考)App Storeスクリーンショットの準備(testPrepareScreenshotBoard)は今は38回の編集で帯が出ませんが、盤面を大きくして50回を超えると撮影画面に帯が写ります。

実行した確認

レビュー後の修正(a0d189c)で実行したもの:

  • npm run typecheck:成功
  • npm test:29ファイル179件成功
  • npm run build:成功
  • npm run check:dist:成功(dist/CNAMEがknittingeditor.com、dist/guide/index.htmlに改めた文言が入ることも確認)
  • npm run test:e2e:chromium-mobile・webkit-mobile・chromium-desktopで107件成功(iPhone Safari専用の4件は設定どおりスキップ)。tests/e2e/backup-reminder.spec.tsは--repeat-each 5で50件成功
  • iOS Web(ios/Web):tsc -p tsconfig.app.json --noEmit、vitest run --config vite.config.ts(3ファイル8件)成功
  • 追加したテストのうち、指を置いている間の確認(Vitest・E2E)と取りやめたときの確認(Vitest)は、修正を一時的に外すと失敗することを確かめた

最初のコミット(e8d9b4f)で実行したもの:上の5つと iOS Web の確認に加え、xcodegen generateとSimulator向けDebugビルド、閾値を一時的に3回へ下げたビルドでiPhone 16・iPad(第10世代)のSimulatorに帯が出ることの目視、PlaywrightでiPhone SE・iPhone 14・Pixel 7・iPad(gen 7、縦横)・デスクトップ(1440×900)のスクリーンショットによる見た目の確認。

追加・更新したテスト:

  • packages/editor-core/state/backupReminder.test.ts:表示条件(変更なし、閾値、7日の境目、作成日から数える、「あとで」の期間)と文言・日時の書式
  • packages/editor-core/storage/lastBackup.test.ts:既存データ(記録前のアプリと同じDB名・版)を未書き出しとして読む移行、1件・全件の記録、.knitと復元した編み図に日時が入らない、壊れた記録を無視、削除で記録も消える
  • packages/editor-core/ui/BackupReminderBar.test.tsx:新しい編み図には出ない、帯から書き出して日時が出る、保存を取りやめると記録せず帯が残る、「あとで」が再表示後も続く、50回目の編集から手を止めるまで出ない、指を置いている間(2本指で1本だけ離した間を含む)は出さず、すべて離してから待ち時間の後に出る、ドロワー中は出ない
  • src/ShareFileDialog.test.tsx・src/platform.test.ts:共有でtrue、取り消し・「閉じる」でfalse、ダウンロードへの切り替えとダウンロードでundefinedを返す、閉じる前に次のファイルが来たら前はfalse
  • tests/e2e/backup-reminder.spec.ts:書き出し後に日時が出て再読み込み後も残る、帯が盤面に重ならない・横スクロールなし、「あとで」が再読み込み後も続く、期間後にまた出る、「書き出す」で消えて再読み込み後も出ない、レビューの再現(8日前に作ったまま変更していない編み図で1回タップし、すぐ次のなぞり描きを始めて3.5秒押したままにする)で押している間は盤面が動かず、帯は指を離した後に初めて出る、iPhone Safari相当で確認ダイアログを閉じても共有シートを取り消しても記録せず、共有を終えたら記録する

できなかった確認

  • スクリーンショットは手元で確認済みですが、PRへの添付は未了です(添付する手段がないため)。帯の見た目はレビュー後の修正で変えていません。
  • iOSのSimulatorテスト一式、アプリ更新テスト、Release Archive、オフラインbundle検査は手元では実行せず、このPRのCIに任せます(Swiftとビルド設定は変えていません)。
  • 実機(iPhone・iPad)では確かめていません。
  • 手元のNode.jsは指定の24ではなく26で実行しました。

Closes #83

🤖 Generated with Claude Code

Record when each chart was last exported as .knit and show it in the
backup section. Suggest an export with a slim bar between the tools and
the board when a changed chart has not been exported for a week or after
50 edits, only once editing pauses. "Later" hides it for three days.
Dates live in the settings store, so existing data and .knit files keep
their format.

Closes #83

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 6, 2026 12:06
@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.

レビュー(マージはしていません)

CI

コミットe8d9b4fのCI(CI and deploy Pages)はすべて成功しました。web、ios_web、release_archive、app_store_docs、ios (iPhone 17)、ios (iPad (A16))、app_update (iPhone 17)、app_update (iPad (A16))、ci-gateが成功、CodeQLも成功です(deployとsmokeはPRなのでスキップ)。帯で盤面が1段下がることによる既存のiOS UIテスト・E2Eへの影響は出ていません。既存のテストは50回以上編集したり古い作成日の編み図を使ったりしないので、帯が出ません。

良い点

  • 日時を編み図の記録(ChartDocument)ではなく設定ストアのlastBackupAt:<編み図ID>に置いており、DBの版・編み図の記録・.knitの形式は変わっていません。既存データは「まだバックアップしていない」として読め、移行テスト(storage/lastBackup.test.ts)もあります。
  • 削除時は同じトランザクションで日時も消し、複製と復元は新しいIDとcreatedAt/updatedAtを持つので、記録を持ち込みません。
  • 表示条件(state/backupReminder.ts)はReact/DOMから独立した純粋関数で、境目のテストがあります。
  • docs/dev-log/2026-10-06-backup-reminder.mdは書式どおりで、PR本文の検証内容と合っています。差分・本文・コミットメッセージに機密情報は見当たりません。Closes #83とenhancementラベルがあります。

直したほうがよい点

1.[中]なぞり描きの途中で帯が出て、盤面が1段下がる
countEdit()は指を離したとき(commitStroke)だけ呼ばれ、2.5秒の待ち時間は次に指を置いたときには延びません。そのため、1回目の編集から2.5秒以内に次のなぞり描きを始め、指を置いたまま2.5秒を過ぎると、描いている途中で帯が出ます。BoardCanvasは毎回getBoundingClientRect()からマスを求めるので、以降の描画は別の段に入ります。
手元で一時的なPlaywrightスクリプト(コミットしていません)で、8日前に作った未変更の編み図を開き、1回タップしてすぐ次のなぞり描きを始め、指を置いたまま3秒待つと、帯が出て盤面が47px(Desktop Chrome)下がることを確かめました。
直し方の案: 盤面で指が置かれている間(pointerdown〜pointerup/pointercancel)は待ち時間を止め、指を離してから数え直す。たとえばBoardCanvasのpointerdownでuseBackupReminderの待ちを取り消す口(pauseIdleなど)を呼ぶ。2本指の移動・拡大や範囲選択の途中も同じです。この場合を確かめるテスト(Vitestの偽タイマーかE2E)も足してください。

2.[中]書き出しを取りやめても「最後のバックアップ」として記録される
backup()はplatform.saveFile()が戻った時点で記録しますが、次の経路ではsaveFile()は利用者が保存する前に戻ります。

  • Web版のiPhone/iPad Safari: createWebPlatformのsaveFileはofferShare(file)で確認ダイアログを出すだけで、すぐ戻ります。ダイアログで「閉じる」を押しても、共有シートを取り消しても(AbortError)、「最後のバックアップ」が更新され帯も消えます。この経路はアプリ側のダイアログなので、結果を返すことができます。
  • iOS版: saveBlobWithNativeBridgeはメッセージを送った時点でtrueを返します。Swift側はdocumentPickerWasCancelledやUIActivityViewControllerのcompletionWithItemsHandlerで結果を知っていますが、Webへ返していません。
    Safariの7日の期限で消えやすいのはまさにiPhone/iPadのSafariなので、取り消しで「バックアップ済み」になると、勧めの目的と逆になります。PR本文の「ホストは保存を終えたかを返さない」はWeb版Safariについては当たりません。
    直し方の案: EditorPlatform.saveFileが保存したかどうか(例: Promise<boolean | undefined>、undefinedは不明)を返し、falseのときは記録しない。Web版はShareFileDialogの結果をPromiseで返す。iOSのネイティブ側の結果通知は別Issueに分けてもよいと思います。分ける場合は、PR本文・dev-log・使い方に「書き出しを始めた日時」であることが分かる書き方にしてください。

気になる点(判断をお願いしたい点)

  • [低〜中]勧める頻度: 書き出した後も、同じ画面で50回編集するとまた帯が出ます(新しい編み図でも、50回タップすればその日のうちに出ます)。「控えめに」というIssueの趣旨に対して多くないか、閾値(50回)と「あとで」の期間(3日)を確認してください。
  • [低]アップデート直後の既存利用者: これまで.knitを書き出していた人も記録が無いので「まだバックアップしていません」と出ます(作成から7日以上で変更のある編み図を開くと、すぐ帯が出ます)。事実と違って見える可能性があるので、文言を「このアプリで書き出した記録がありません」に寄せるかを検討してください。
  • [低]名前の変更: renameDocumentはupdatedAtを進めるので、名前を変えただけでも「変更あり」になります。.knitに名前が入るので妥当とも言えます。
  • [低]App Storeスクリーンショット準備: testPrepareScreenshotBoardは40×32で32回の盤面設定と6回のタップ(計38回)なので今は帯が出ませんが、もっと大きな盤面を用意すると50回を超え、タップの途中で盤面がずれたり撮影画面に帯が写ったりします。
  • [低]ファイル名: ui/BackupReminder.tsxの中身はBackupReminderBarです。
  • [低]E2Eの「出ない」確認: page.waitForTimeout(300)の後にtoBeHidden()で確かめているため、遅い環境では見逃しうるテストになっています(失敗はしない側なので、壊れても気づきにくい)。

Issue #83の完了条件で残っているもの

  • 「作業の邪魔にならないことをiPhoneとiPadで確かめる」: Simulatorでは確かめていますが、上の1のとおり、なぞり描きの途中でずれる場合が残っています。実機での確認もまだです。
  • UIの変更なので、AGENTS.mdにあるとおりPRへのスクリーンショット添付(狭い画面とデスクトップ)がまだです。

Stop the idle countdown for the backup reminder while any finger or
pointer is on the screen, and restart it after all are released, so the
bar no longer appears mid-stroke and shifts the board.

saveFile now returns whether the user saved or shared the file. The Web
share dialog on iPhone and iPad Safari reports closing and share-sheet
cancellation, and a cancelled .knit export is no longer recorded as the
last backup. The iOS native bridge does not report the result yet
(#122), so the app still records when the export started.

Rename ui/BackupReminder.tsx to BackupReminderBar.tsx to match the
component, and check that the bar stays hidden without fixed waits.

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

K0mork commented Oct 6, 2026

Copy link
Copy Markdown
Owner Author

レビュー指摘の修正(a0d189c)

直したこと

  • [中]なぞり描きの途中で帯が出る: 指・ポインタを画面に置いている間(1本指の描画、2本指の移動・拡大、範囲選択の途中を含む)は待ち時間を止め、すべて離してから2.5秒を数え直すようにしました。レビューの再現手順(8日前に作ったまま変更していない編み図で1回タップし、すぐ次のなぞり描きを始めて押したままにする)をE2Eに足し、押している間は盤面が動かず、帯は離した後に初めて出ることを確かめています(修正を外すと失敗することも確認)。
  • [中]取りやめても記録される: EditorPlatform.saveFileが{ saved }(true/false/不明はundefined)を返すようにし、falseなら記録しません。Web版のiPhone・iPad Safariでは、確認ダイアログを閉じたときと共有シートを取り消したときにfalseになります(Vitest・E2Eあり)。
  • iOS版のネイティブ側は、変える箇所がPR #121と同じ行に重なるため iOS版: .knitの書き出しを取りやめたことをWebへ返し、最後のバックアップとして記録しない #122(priority:P1、platform:ios)に分けました。iOS版は当面「書き出しを始めた日時」を記録し、iOSの使い方ページもそう書いています。
  • [低] E2Eの「出ない」確認は固定の待ち時間をやめ、記録を読み終えた(保存パネルに日時が出た)後にパネルを閉じた同じ描画で確かめる形にしました。ui/BackupReminder.tsxはui/BackupReminderBar.tsxへ改名しました。

頻度(50回・3日)、既存利用者への文言、名前変更の扱いは、PR本文の「判断していただきたい点」に挙げています。

確認

  • 手元: npm run typecheck、npm test(179件)、npm run build、npm run check:dist、npm run test:e2e(107件、iPhone Safari専用の4件はスキップ)、iOS Webの型検査とテストがすべて成功しました。追加したE2Eは5回繰り返して成功しました。
  • CI(CI and deploy Pages): web、ios_web、release_archive、app_store_docs、ios (iPhone 17)、ios (iPad (A16))、app_update (iPhone 17)、app_update (iPad (A16))、ci-gateが成功しました。CodeQLも成功です(PRのためdeployとsmokeはスキップ)。

まだ: 実機での確認と、PRへのスクリーンショット添付。マージはしていません。

K0mork and others added 2 commits October 7, 2026 08:38
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@K0mork
K0mork merged commit e1a2001 into main Oct 7, 2026
15 checks passed
@K0mork
K0mork deleted the feat/backup-reminder branch October 7, 2026 02:02
@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] 最後のバックアップ日時を表示し、.knitの書き出しを促す

2 participants