diff --git a/archive/2026-07.md b/archive/2026-07.md index 0461cc4..72a6a2a 100644 --- a/archive/2026-07.md +++ b/archive/2026-07.md @@ -7,3 +7,9 @@ over — don't let a single archive file grow without bound either. - feat-001: Project Setup verified clean (npm install/lint/build/test) (commit 28f4f8e) - feat-002: Settings UX bundle implemented — conflict modal resize + tabs (#42), connection status badge (#41), local ignore patterns (#40); 254 tests pass, lint/build clean (commit 28f4f8e, branch claude/settings-ux-improvements-260713) +- feat-005: Folder picker for root path / vault folder settings (#48) (commit c107979) +- feat-003: Symlinked directories no longer break pull discovery (#33) — root cause: local scan recursed into a directory symlink as if it were a real tree, producing bogus remote .gitignore 404s and a permanently-stuck "remote only" status (commit 4c8896b) +- feat-006: Tree-SHA-based refresh (#36) — status check now compares a locally-computed git blob SHA against each tree entry's SHA instead of a per-file getFile network request; diff content fetched on demand via new getBlob(sha,path) (commit 2ed5a43) +- feat-007: Clear error when requestUrl() itself rejects with an HTML body (#31) — some Obsidian versions eagerly JSON-parse inside requestUrl(), so a login/proxy HTML page rejected the whole call with a raw SyntaxError bypassing the existing parseJson() HTML-detection wrapper (commit a867217) +- feat-008: "What's new" modal after a version bump (#39) — hand-curated src/changelog.ts (separate from auto-generated CHANGELOG.md) with a `notable` flag per entry, numeric compareVersions(), WhatsNewModal shown once per upgrade (commit 4eebebc) +- All six of the above (feat-002/003/005/006/007/008) consolidated onto branch claude/fix-directory-symlink-pull-260713 → PR #51 (open, not yet merged) to keep the PR count down per user request; PRs #49/#50/#52/#53 closed/auto-resolved as superseded diff --git a/feature_list.json b/feature_list.json index 665c129..1de1749 100644 --- a/feature_list.json +++ b/feature_list.json @@ -1,5 +1,6 @@ { "_note": "GitHub Issues (firstsun-dev/git-files-sync, Project #6) is the source of truth for the full backlog and priority/estimate fields. This file mirrors only the active feature and the next few candidates so an agent session has a local, offline checkpoint — sync it against `gh issue list --repo firstsun-dev/git-files-sync --state open` at the start of a session rather than treating it as authoritative.", + "_prPolicy": "User explicitly requested fewer PRs (2026-07-13): all issue work is consolidated onto branch claude/fix-directory-symlink-pull-260713 -> PR #51, not one PR per issue. Commit new work directly onto that branch unless told otherwise.", "features": [ { "id": "feat-001", @@ -14,16 +15,16 @@ "name": "Settings UX bundle (issues #40, #41, #42)", "description": "Local ignore-pattern sync setting (#40), persistent connection status badge in settings (#41), and resized/tabbed conflict resolution modal (#42)", "dependencies": ["feat-001"], - "status": "in-review", - "evidence": "Commit 28f4f8e on branch claude/settings-ux-improvements-260713 - lint/build/test pass (254 tests); pushed to origin, PR not yet opened" + "status": "done", + "evidence": "Consolidated onto PR #51 (branch claude/fix-directory-symlink-pull-260713); 302 tests pass" }, { "id": "feat-003", "name": "fix: symbolic link pull fails (issue #33)", - "description": "Bug report with screenshot; needs repro/investigation before a fix can be scoped", + "description": "Directory symlinks were walked as real trees by local scanning code, causing bogus remote .gitignore 404s and a permanently-stuck 'remote only' status; also fixed a missing Windows symlinkSync type hint", "dependencies": [], - "status": "not-started", - "evidence": "" + "status": "done", + "evidence": "Commit 4c8896b, consolidated onto PR #51" }, { "id": "feat-004", @@ -38,16 +39,48 @@ "name": "feat(settings): folder picker for root path / vault folder (issue #48)", "description": "Searchable folder suggester for the Root path and Vault folder settings fields, replacing free-text entry", "dependencies": [], - "status": "not-started", - "evidence": "" + "status": "done", + "evidence": "Commit c107979, consolidated onto PR #51" }, { "id": "feat-006", - "name": "feat: add i18n (multi-language) support (issue #38)", - "description": "Extract hardcoded UI text (settings tab, Notice messages) into i18n keys; detect language via window.moment.locale() with English fallback; ship English (default) and Traditional Chinese (zh-tw)", + "name": "perf(refresh): use tree blob SHAs to avoid per-file content fetches (issue #36)", + "description": "Status refresh compares a locally-computed git blob SHA against each remote tree entry's SHA instead of one getFile per file; diff content fetched on demand via new getBlob(sha,path); symlink-aware hashing per real/follow/skip modes", "dependencies": [], - "status": "in-review", - "evidence": "Branch claude/i18n-support-260713 (based on origin/claude/fix-directory-symlink-pull-260713) - added src/i18n/{index,locales/en,locales/zh-tw}.ts and tests/i18n/index.test.ts; ~130 strings replaced across settings.ts, main.ts, and 7 ui/ files; lint/build clean, 308 tests pass; not yet pushed/PR'd, to be merged back into claude/fix-directory-symlink-pull-260713" + "status": "done", + "evidence": "Commit 2ed5a43, consolidated onto PR #51; 302 tests pass" + }, + { + "id": "feat-007", + "name": "fix: clear error when requestUrl() rejects with HTML (issue #31)", + "description": "Some Obsidian versions eagerly JSON-parse inside requestUrl() itself, so a login/proxy HTML page rejected the whole call with a raw SyntaxError bypassing the existing parseJson() HTML-detection wrapper; added the same detection to safeRequest's outer catch", + "dependencies": [], + "status": "done", + "evidence": "Commit a867217, consolidated onto PR #51" + }, + { + "id": "feat-008", + "name": "feat: show new feature tips after update (issue #39)", + "description": "Hand-curated src/changelog.ts (separate from auto-generated CHANGELOG.md) with a notable flag per entry, numeric compareVersions(), WhatsNewModal shown once per version bump via a new lastSeenVersion setting", + "dependencies": [], + "status": "done", + "evidence": "Commit 4eebebc, consolidated onto PR #51" + }, + { + "id": "feat-009", + "name": "feat: add i18n (multi-language) support (issue #38)", + "description": "Extract hardcoded UI strings (settings tab, ribbon/command labels, Notice messages, sync status view/modals) into an i18n key system with locale detection and English fallback", + "dependencies": [], + "status": "done", + "evidence": "Commit 144eb28 on branch claude/i18n-support-260713, merged into claude/fix-directory-symlink-pull-260713 - added src/i18n/{index,locales/en,locales/zh-tw}.ts (159 keys, en + zh-tw) and tests/i18n/index.test.ts; ~130 strings replaced across settings.ts, main.ts, and 7 ui/ files; lint/build clean, 308 tests pass; consolidated onto PR #51" + }, + { + "id": "feat-010", + "name": "feat: add bitbucket support (issue #37)", + "description": "Add Bitbucket as a fourth GitServiceInterface provider; Bitbucket's API has no native blob SHA concept and is PR-based rather than direct-commit, so GitFile.sha semantics may need adjusting", + "dependencies": ["feat-009"], + "status": "not-started", + "evidence": "" } ], "_evidenceStyle": "Keep evidence to one line: commit hash + short pointer (e.g. 'Commit abc1234 - added X, tests pass'). Debugging narrative and design discussion belong in the commit message, not this file." diff --git a/progress.md b/progress.md index cb56fcf..8ae5ef4 100644 --- a/progress.md +++ b/progress.md @@ -12,57 +12,55 @@ Completed work is archived in [archive/](./archive/), one file per calendar mont ## Current State -**Last Updated:** 2026-07-13 14:20 -**Session ID:** (this session) -**Active Feature:** feat-006 - i18n (multi-language) support (issue #38) +**Last Updated:** 2026-07-13 14:35 +**Session ID:** (this session, merged with session_01YYCTyZw7gUmJ7oh1VTmAqh's parallel work) +**Active Feature:** none — feat-001 through feat-009 are all done; feat-010 (Bitbucket, #37) is next up but not started ## Status ### What's Done -- [x] feat-001 - Project Setup verified (see archive/2026-07.md) -- [x] feat-002 - Settings UX bundle (see archive/2026-07.md); this branch's history already contains it (merged upstream of `claude/fix-directory-symlink-pull-260713`) -- [x] feat-006 implementation - i18n core (`src/i18n/index.ts`, `locales/en.ts`, `locales/zh-tw.ts`), ~130 strings extracted across settings.ts, main.ts, and 7 `ui/` files, tests added, lint/build/test all clean +- [x] feat-001..008 (project setup, settings UX bundle, folder picker, symlink pull fix, tree-SHA refresh, HTML-response error clarity, what's-new modal) — see archive/2026-07.md +- [x] feat-009 - i18n / multi-language support (issue #38): `src/i18n/{index,locales/en,locales/zh-tw}.ts` (159 keys, en + zh-tw, full parity), ~130 hardcoded strings replaced across `settings.ts`, `main.ts`, and 7 `ui/` files, `tests/i18n/index.test.ts` added (6 cases) +- All consolidated onto branch `claude/fix-directory-symlink-pull-260713` → **PR #51**, per the user's explicit request to keep the PR count down rather than one PR per issue. ### What's In Progress -- [ ] feat-006 - push branch `claude/i18n-support-260713` and open a PR (or fold it back into `claude/fix-directory-symlink-pull-260713` per the user's stated intent) - - Details: worked in a separate branch/worktree because `claude/fix-directory-symlink-pull-260713` was already checked out elsewhere in this repo's other worktree (main checkout at `/home/tianyao/git-files-sync`), so git wouldn't allow checking out the same branch twice - - Blockers: none technically; needs the user's go-ahead on push/merge direction +- Nothing actively in progress. feat-009 landed as a separate commit (`144eb28`, from a temporary branch `claude/i18n-support-260713`) merged into this branch and pushed to origin as part of PR #51. ### What's Next -1. Confirm with user whether to push `claude/i18n-support-260713` and open a PR, or merge it locally into `claude/fix-directory-symlink-pull-260713` -2. Re-sync this file's backlog (feat-003..005) against `gh issue list --repo firstsun-dev/git-files-sync --state open` +1. Issue #37 (Bitbucket provider support, feat-010) — large, was deferred until #38 landed (agreed order: 39→38→37). Now unblocked. +2. Re-sync against `gh issue list --repo firstsun-dev/git-files-sync --state open`. Remaining genuinely-unstarted issues as of this session: #47 (regex ignore lists), #45 (SonarQube findings), #37 (Bitbucket), #28 (non-engineering: community visibility). +3. PR #51 is large (7 issues' worth of changes now). If the user wants to review/merge it before more work piles on, flag this rather than continuing to add commits indefinitely. ## Blockers / Risks -- None currently. +- None currently. (Earlier in this session, feat-009 was recorded as "blocked, awaiting scope decision" by a parallel session working in the main checkout — that scope question was resolved directly with the user in this session instead: flat key-value dict, `window.moment.locale()` detection with English fallback, cover settings.ts + all Notices now. No longer blocked.) ## Decisions Made -- **Worked on a new branch instead of the pre-existing `claude/fix-directory-symlink-pull-260713`**: that branch was already checked out in another worktree (the main repo checkout), and git disallows the same branch in two worktrees. Created `claude/i18n-support-260713` off `origin/claude/fix-directory-symlink-pull-260713` instead, per the user's choice. +- **All work goes into one PR (#51)**: user explicitly said "不要那麼多pr merge" (don't want so many PRs). Any further issue work should commit directly onto `claude/fix-directory-symlink-pull-260713`, not a new branch — the i18n work below was an exception only because that branch was already checked out in another worktree when the session started; it was merged back in immediately rather than left as a standing separate branch/PR. +- **feat-009 scope**: settings.ts + all Notice() messages, done in one pass rather than split into infra-first/settings-only phases — a half-migrated i18n system (some strings extracted, most not) would be worse than finishing it. - **Flat key-value i18n dict, not nested namespaces**: matches this plugin's small scale; simpler `t(key, vars)` lookup, no external i18n library dependency. -- **Locale detection via `window.moment.locale()`**: per issue #38's suggested approach; unknown/unmapped locales fall back to English. A bare `zh` locale code is mapped to `zh-tw` since that's the only Chinese variant shipped. +- **Locale detection via `window.moment.locale()`**: per issue #38's suggested approach; unresolvable/unsupported locales fall back to English. A bare `zh` locale code maps to `zh-tw`, the only Chinese variant shipped. - **Diff-format markers, changelog release-note text, and proper nouns (GitHub/GitLab/Gitea) were left untranslated** — out of scope for UI-chrome i18n. +- **`src/changelog.ts` is hand-curated, separate from the auto-generated `CHANGELOG.md`**: semantic-release already maintains `CHANGELOG.md` from Conventional Commit messages, but it's commit-log-level detail (too granular for an end-user popup) and isn't shipped in the release assets. The what's-new modal needs a small, hand-written, user-facing "highlights" list instead. +- **Fixed two duplication regressions mid-session** (SonarCloud gate is 3% on new code, learned the hard way on PR #49 earlier): deduped `TextComponent`/`TextAreaComponent` test mocks, and deduped the GitHub/Gitea `getBlob()` bodies into a shared `fetchGitHubStyleBlob()` base helper. ## Files Modified This Session -- `src/i18n/index.ts` (new) - `t(key, vars?)` helper, locale detection/resolution -- `src/i18n/locales/en.ts`, `src/i18n/locales/zh-tw.ts` (new) - 159 keys each, full parity -- `src/settings.ts`, `src/main.ts` - all hardcoded UI strings replaced with `t()` calls -- `src/ui/SyncStatusView.ts`, `src/ui/SyncConflictModal.ts`, `src/ui/WhatsNewModal.ts`, `src/ui/ConfirmModal.ts` - same -- `src/ui/components/ActionBar.ts`, `FileListItem.ts`, `DiffPanel.ts` - same -- `tests/i18n/index.test.ts` (new) - 6 test cases covering fallback, locale resolution, interpolation +- feat-009 (i18n): `src/i18n/index.ts`, `src/i18n/locales/en.ts`, `src/i18n/locales/zh-tw.ts`, `tests/i18n/index.test.ts` (all new); `src/settings.ts`, `src/main.ts`, `src/ui/SyncStatusView.ts`, `src/ui/SyncConflictModal.ts`, `src/ui/WhatsNewModal.ts`, `src/ui/ConfirmModal.ts`, `src/ui/components/ActionBar.ts`, `FileListItem.ts`, `DiffPanel.ts` (modified) +- See archive/2026-07.md entries feat-003/005/006/007/008 for the earlier features' file lists. ## Evidence of Completion -- [x] Tests pass: `npx vitest run` → 308/308 passed (302 pre-existing + 6 new i18n tests) +- [x] Tests pass: `npx vitest run` → 308/308 passed - [x] Type check clean: `npm run build` (tsc + Obsidian 1.11.0 compat typecheck + esbuild) → clean - [x] Lint clean: `npx eslint .` → 0 errors -- [ ] Manual verification in Obsidian: not done (no Obsidian instance available in this environment) +- [ ] Manual verification in Obsidian: not done (no Obsidian instance available in this environment) for any feature this session ## Notes for Next Session -- Branch `claude/i18n-support-260713` has uncommitted working-tree changes as of this session's end — not yet committed or pushed. -- `feature_list.json`'s backlog (feat-003..005) is a snapshot from 2026-07-13; re-check `gh issue list` before trusting it. +- Working branch for all further commits: `claude/fix-directory-symlink-pull-260713` (PR #51). Do not open a new branch/PR for the next issue unless the user says otherwise. +- `feature_list.json`'s backlog is a snapshot from this session — re-check `gh issue list` before trusting it. diff --git a/session-handoff.md b/session-handoff.md index ed18676..e75cd89 100644 --- a/session-handoff.md +++ b/session-handoff.md @@ -9,19 +9,20 @@ ## Current Objective -- Goal: Implement issue #38 (i18n / multi-language support for settings + Notice messages) -- Current status: Implemented, tested, linted, built clean. Not yet committed or pushed. -- Branch / commit: `claude/i18n-support-260713` (working tree, uncommitted) — branched off `origin/claude/fix-directory-symlink-pull-260713` +- Goal: Work through open issues one at a time, all consolidated into a single PR (user explicitly asked for fewer PRs, not one per issue). This session's issue: #38 (i18n / multi-language support). +- Current status: #38 implemented, tested, linted, built clean, and merged onto the shared branch. 7 issues done total (#33, #36, #31, #39, #38, plus #40/#41/#42/#48 from an earlier session), all on one branch/PR. +- Branch / commit: `claude/fix-directory-symlink-pull-260713` → **PR #51** (open). i18n work landed via a temporary branch `claude/i18n-support-260713` (commit `144eb28`), merged in and pushed. ## Completed This Session -- [x] Created `src/i18n/index.ts` — `t(key, vars?)` flat-dict lookup with `{placeholder}` interpolation, English fallback for missing keys +- [x] #38 - i18n / multi-language support: `src/i18n/index.ts` (`t(key, vars?)` flat-dict lookup, `{placeholder}` interpolation, English fallback), `src/i18n/locales/en.ts` (159 keys, source of truth) and `zh-tw.ts` (full parity, verified no missing/extra keys) - [x] Locale detection via `window.moment.locale()`; unresolvable/unsupported locales fall back to `en`; bare `zh` maps to `zh-tw` -- [x] `src/i18n/locales/en.ts` (source of truth, 159 keys) and `zh-tw.ts` (full parity, verified no missing/extra keys) - [x] Replaced ~130 hardcoded strings across `src/settings.ts`, `src/main.ts`, `src/ui/SyncStatusView.ts`, `SyncConflictModal.ts`, `WhatsNewModal.ts`, `ConfirmModal.ts`, `components/ActionBar.ts`, `FileListItem.ts`, `DiffPanel.ts` - [x] Left untranslated on purpose: GitHub/GitLab/Gitea proper nouns, diff-format markers (`--- Remote`/`+++ Local`), URL placeholders, changelog release-note content - [x] Fixed a lint regression (cognitive-complexity + nested-ternary) introduced by the change in `SyncStatusView.runBatchOperation` by extracting a lookup table and a helper method - [x] Added `tests/i18n/index.test.ts` (6 cases: fallback with no `window.moment`, fallback for unsupported locale, zh-tw resolution, bare `zh` → `zh-tw` mapping, interpolation, fallback-per-key when zh-tw is missing a translation) +- [x] Worked on a temporary branch (`claude/i18n-support-260713`, off `origin/claude/fix-directory-symlink-pull-260713`) because that branch was already checked out in this repo's other worktree; merged it back into `claude/fix-directory-symlink-pull-260713` immediately afterward per the one-PR policy, rather than leaving it as a standing separate branch/PR +- [x] Resolved a merge conflict in `feature_list.json`/`progress.md`/`session-handoff.md` against a parallel session's harness-state commit that had (incorrectly, since it predated this session's actual implementation) recorded #38 as "blocked, awaiting scope decision" ## Verification Evidence @@ -29,32 +30,31 @@ |---|---|---|---| | Lint | `npx eslint .` | 0 errors | Repo-wide, no exceptions | | Type check + compat | `npm run build` | Pass | Includes `typecheck-compat.mjs` against Obsidian 1.11.0 | -| Tests | `npx vitest run` | 308/308 passed | 23 test files (302 pre-existing + 6 new) | +| Tests | `npx vitest run` | 308/308 passed | 23 test files (302 pre-existing + 6 new i18n tests) | | Manual (in Obsidian) | — | Not done | No Obsidian instance available in this environment | -## Files Changed +## Files Changed (this session) - New: `src/i18n/index.ts`, `src/i18n/locales/en.ts`, `src/i18n/locales/zh-tw.ts`, `tests/i18n/index.test.ts` - Modified: `src/settings.ts`, `src/main.ts`, `src/ui/SyncStatusView.ts`, `src/ui/SyncConflictModal.ts`, `src/ui/WhatsNewModal.ts`, `src/ui/ConfirmModal.ts`, `src/ui/components/ActionBar.ts`, `src/ui/components/FileListItem.ts`, `src/ui/components/DiffPanel.ts` ## Decisions Made -- Worked on a fresh branch (`claude/i18n-support-260713`) instead of `claude/fix-directory-symlink-pull-260713` directly, because that branch was already checked out in this repo's other worktree (main checkout) and git disallows the same branch in two worktrees. User confirmed this approach and indicated intent to eventually merge back into `claude/fix-directory-symlink-pull-260713`. -- Flat key-value dict per locale (not nested namespaces) — simplest fit for this plugin's scale, user-confirmed. -- `window.moment.locale()` detection with English fallback — matches issue #38's suggested approach, user-confirmed. +- **One PR, not one-per-issue**: user said "不要那麼多pr merge" (don't want so many PRs). All further issue work goes directly onto `claude/fix-directory-symlink-pull-260713` — do not create a new branch/PR for the next issue unless a branch conflict (as above) forces a temporary one, and merge it back in immediately if so. +- **#38 scope**: settings.ts + all Notice() messages, done in one pass — user confirmed flat key-value dict (not nested namespaces) and `window.moment.locale()` detection with English fallback. +- **`src/changelog.ts` is hand-curated, separate from `CHANGELOG.md`**: the auto-generated changelog (via semantic-release) isn't shipped in release assets and is too commit-log-granular for an end-user popup anyway. +- Deduped two accidental code-duplication regressions in an earlier part of this session (test mocks, `getBlob` bodies) before they could trip SonarCloud's new-code gate again. ## Blockers / Risks -- None blocking. Nothing has been committed yet — working tree only. +- None currently. PR #51 is now fairly large (7 issues). Consider flagging to the user that it may be worth reviewing/merging before more commits pile on. ## Next Session Startup -1. Read `CLAUDE.md`. -2. Read `feature_list.json` and `progress.md`. -3. Review this handoff. -4. Run `./init.sh` before editing. -5. Ask the user whether to commit + push `claude/i18n-support-260713` (and open a PR), or merge these changes locally into `claude/fix-directory-symlink-pull-260713` instead. +1. Read `CLAUDE.md`, `feature_list.json`, `progress.md`, then this file. +2. Run `./init.sh` before editing. +3. All new commits go on `claude/fix-directory-symlink-pull-260713` (PR #51) unless told otherwise. ## Recommended Next Step -- Get the user's decision on push/PR vs. local merge, then commit with a `feat: add i18n (multi-language) support` message referencing issue #38 so it auto-closes on merge. +- Move to issue #37 (Bitbucket provider support) per the previously agreed order (39→38→37, all now done ahead of it).