Copy games between tabs, and notice when a file changes on disk - #3
Merged
Conversation
The second half of the multi-file work. **Copy to.** Right-click ▸ Copy to ▸ another open tab appends the selected games to that file and writes it back once. "Already there" is decided by the moves, not the headers: a `signature` column hashes the start position and the main line (check and mate suffixes stripped), because two files of one tournament disagree about the event's name, how a player is spelled and sometimes who had White, and the moves are the one thing both copies got from the same scoresheet. Copying the same games again lands nothing and says so. The column means a schema change. `PRAGMA user_version` now carries the schema version; an older cache is upgraded in place so it opens and reports `needs_rebuild`, which the app treats as stale — a two-second re-import against the alternative of query paths that tolerate every past layout. The index on the new column has to be created after the upgrade step, not in the CREATE batch: on an old cache that batch ran first and failed on "no such column". **The file is watched.** A `DispatchSource` on the path; the app's own writes are told apart by the modification date recorded after each one, and an editor's save-by-rename — which swaps the inode under the descriptor — re-arms the watch. The banner offers Reload (take the file as it is now; this file's sessions are detached first, since a re-import renumbers the games and a session keeping an old id could save into a different one) or Keep Mine (carry on; the next save replaces the file, with the .pgn.bak kept). Checked with the hooks: copying two games lands the one that is not a duplicate; copying again lands none; an external append raises the banner and leaves the count alone; Reload takes it to the new count; the app's own save raises nothing. Rust: 37 tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F9o9DSzZas4GsekCpG5M8T
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2 of the multi-file work, on top of #2.
Copy to. Right-click ▸ Copy to ▸ another open tab appends the selected games to that file and writes it back once. Duplicates are decided by the moves (a new
signaturecolumn: start position + main line, check/mate suffixes stripped), not by headers — two files of one tournament disagree about names and events, never about the moves. Copying again lands nothing and says so.The new column bumps the cache schema (
PRAGMA user_version= 2). Older caches are upgraded in place and reportneeds_rebuild; the app re-imports them (≈2 s per 100k games). One thing to know from the test: the index on the new column must be created after the upgrade step, not in the CREATE batch — on an old cache that batch ran first and failed on "no such column".File watcher. A
DispatchSourceper open file. The app's own writes are told apart by the modification date recorded after each write; an editor's save-by-rename re-arms the watch. Banner: Reload (take the file as it is now — this file's sessions are detached first, since a re-import renumbers games) or Keep Mine (next save replaces the file,.pgn.bakkept).What to review first:
DatabaseStore.copyGames/writeBack/reloadFromDisk,SessionRegistry.detach(store:), andDatabase::openindb.rs(the upgrade path).Checked with the hooks (
DCS_AUTO_COPY,DCS_AUTO_WATCH_OUT,DCS_AUTO_WATCH_RELOAD,DCS_AUTO_TAB_SAVE):Not done: drag and drop between tabs; a menubar Copy item (SwiftUI Commands cannot observe the open-store list).
🤖 Generated with Claude Code
https://claude.ai/code/session_01F9o9DSzZas4GsekCpG5M8T