Commit graph

42 commits

Author SHA1 Message Date
Claude
d896015765 fix(compat): support Obsidian down to 1.11.0
Release 1.2.0 raised minAppVersion to 1.13.0, pushing users still on
Obsidian 1.11/1.12 back to the older 1.1.2 build via versions.json. Only
three 1.13-era APIs were in use; two were already runtime-guarded, but
ButtonComponent.setDestructive() would throw on older Obsidian.

- Guard setDestructive() behind applyDestructiveStyle() (no-ops on < 1.13).
- Declare SettingDefinitionItem locally and read update() via a cast so the
  forward-compatible settings code type-checks against 1.11 typings.
- Ship 1.2.1 with minAppVersion 1.11.0 (manifest.json + versions.json).
- Add tsconfig.compat.json and `npm run typecheck:compat` to type-check src
  against the 1.11 API, plus regression tests for applyDestructiveStyle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSikT9ZAfQF8K4SE3DatD3
2026-07-07 01:53:28 +00:00
ClaudiaFang
06953e1b12 fix(sync): stop false-positive rename detection and 422 on rename push
detectRename only checked whether a tracked old path's file was missing
from the vault, with no content verification. Any orphaned syncMetadata
entry (e.g. left behind by a local delete, which never cleared it) would
cause the next unrelated push to be misclassified as a rename from that
stale path, using create-only semantics that 422'd if the target path
already existed remotely.

- detectRename now confirms identity by checking that the remote content
  at the candidate old path still matches what's being pushed.
- handleRename looks up the existing remote file at the new path and
  sends its sha, so pushing onto an existing remote path updates instead
  of failing with "file already exists".
- Local deletes (single and batch) now clear syncMetadata for the
  deleted path so it can't become an orphaned rename source later.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 05:44:54 +00:00
ClaudiaFang
1364b94f0a fix(sync): stop batch push/pull from silently overwriting conflicts
Single-file push/pull already detected when both local and remote
changed since the last sync and prompted via SyncConflictModal, but
"Push all"/"Pull all"/multi-select batch actions skipped that check
and force-overwrote. Batch operations now skip conflicting files and
report a conflicts count so nothing is silently clobbered.

Also correct delete-confirmation copy: it claimed local deletes are
recoverable ("moved to trash") unconditionally, but the actual
destination depends on the vault's "Deleted files" setting (which can
be permanent). Wording now defers to that setting instead of
asserting recoverability.
2026-07-05 05:05:54 +00:00
ClaudiaFang
2f6859a2f1 fix(sync): clearer branch-not-found errors and connection test
- listFilesDetailed rethrows 404 branch-resolution failures with a
  message naming the branch, instead of a bare "Git Service Error (404)"
- testConnection now also checks the configured branch exists and
  reports repoOk/branchOk separately so the settings UI can warn when
  the repo is reachable but the branch is missing
- fixes settings.ts silently reporting "connection successful" even
  when testConnection's result was never checked

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 04:57:47 +00:00
Claude
9bcaed65f4
feat(sync): real symbolic link support (GitHub) with configurable handling
Adds end-to-end symlink syncing driven by the "Symbolic links" setting
(real / follow / skip; default real), building on the earlier detection.

- Pull: on desktop with "real", a remote symlink is recreated as a real OS
  link via Node fs (utils/symlink.ts, guarded by Platform/FileSystemAdapter);
  otherwise the target path is written as content.
- Push: GitHubService.pushSymlink commits a real symlink blob (mode 120000)
  through the Git Data API (blob -> tree -> commit -> ref). getFile now
  reports isSymlink/symlinkTarget.
- Config 防呆: only GitHub offers "real"; on GitLab/Gitea (no API to create
  symlinks) "real" resolves to "skip" via getEffectiveSymlinkHandling.
- Safety: a "follow" push never overwrites a detected remote symlink with a
  regular file; it is skipped with a notice.
- Docs: docs/symlink-handling.md plus a README settings note.

Lint is satisfied without disables (Electron global require, minimal Node
type shims). Adds tests for getFile detection, the pushSymlink Git Data
sequence, and the remote-symlink push guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-28 05:14:35 +00:00
Claude
90981261a5
Merge remote-tracking branch 'origin/claude/git-files-sync-issue-31-54izdm' into claude/trusting-volta-qlg8bk
# Conflicts:
#	.github/workflows/ci.yml
#	package-lock.json
2026-06-28 04:50:34 +00:00
Claude
62b475d632
feat(sync): detect symbolic links and add a configurable handling setting
Symlinks are Git blobs with mode 120000 whose content is the target path.
They were treated as ordinary files, which caused 404s on fetch and could
corrupt the file on pull. Add detection and let the user choose behavior.

- Settings: new "Symbolic links" option (symlinkHandling) with real
  (default) / follow / skip.
- Services: listFilesDetailed() reports each entry's symlink flag from the
  tree mode (120000) for GitHub and GitLab; listFiles() now delegates to it.
- Refresh/discovery: with "skip", remote symlinks are excluded from sync;
  with "follow"/"real" they are included and synced as the target content.

Note: true OS-level symlink recreation on desktop and pushing files as
symlinks (Git Data API) are not yet implemented — on all platforms "real"
currently syncs the target content like "follow"; mobile has no symlink
API regardless. Tracked as a follow-up.

Add tests for symlink detection on both services and update settings fixtures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-28 04:45:41 +00:00
Claude
c474a7e6c4
fix(services): stop logging expected 404s as errors during refresh
Loading .gitignore files probes paths that often don't exist remotely.
getFile already treats 404 as "empty file" (handleFileNotFound) and the
gitignore lookup ignores failures, but safeRequest logged every 404 as an
error — twice, because its own catch re-caught the error it had just
thrown. This produced scary "Git Service Request Failed (404): Not Found"
console output during a normal pull/refresh.

Restructure safeRequest so the catch only wraps the network call (no
double-logging of HTTP-status errors), and log an expected 404 at debug
level instead of error. Non-404 statuses are still logged as errors and
all statuses still throw so callers can handle them.

Add a debug level to the logger and regression tests for 404 vs 500.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-28 04:27:25 +00:00
Claude
76405cf2f7
fix(sync): fall back to adapter read for symlinked files
Reading a symlinked file via Obsidian's cached vault.read(TFile) can fail
(notably on mobile), which broke both refresh and push: the status check
swallowed the error and mislabeled the file as 'unsynced', so the user
saw a Push button that then failed re-reading the symlink.

Fall back to vault.adapter.read/readBinary(path) when vault.read throws,
in both the status view and the sync manager. Also stop silently
swallowing the status-check error so the real cause is logged.

Add a regression test covering the adapter fallback on push.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-26 06:28:25 +00:00
Claude
d8d6f9dcb1
refactor(ui): unify Sync Status icons via Lucide setIcon
The view mixed hand-picked Unicode glyphs (✓ ⚠ ↑ ↓ ⟳ ↻ ✕ ≡ ⎇ 📁),
duplicated across files, which rendered at inconsistent sizes/weights
across platforms and could drift (e.g. the Refresh button used ↻ while
the "checking" status used ⟳).

Centralize every icon in src/ui/components/icons.ts as Lucide icon ids and
render them with Obsidian's setIcon, so status, action-bar, tab, and
info-strip icons all share one consistent icon set. Tabs now derive their
icon from statusMeta so they can no longer diverge from the file list.
Add CSS to size the SVGs uniformly.

Add setIcon to the obsidian test mock and update statusMeta expectations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-26 06:22:53 +00:00
Claude
339d5cbe77
fix(services): omit blank sha so creating a new file doesn't 422
Pushing a new file via the single-file push button failed with GitHub
HTTP 422. A 404 lookup returns sha === '' for new files, and the manual
push path (sync-manager pushFile/conflict resolution) forwarded that
empty string into the Contents API request body as "sha":"", which
GitHub rejects. Batch push avoided this via `remote.sha || undefined`.

Fix at the service layer so every caller is safe: only include sha in the
GitHub request body when it is non-empty. Apply the same guard to the
GitLab service's last_commit_id for symmetry.

Add regression tests for blank-sha pushes on both services.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-26 06:07:59 +00:00
Claude
bcc5cda69c
fix(services): clear error when Git API returns HTML instead of JSON
Refresh failed with the cryptic "Unexpected token '<', "<!DOCTYPE ..."
is not valid JSON" whenever the Git server returned an HTML page (login,
SSO redirect, or proxy/error page) on a 2xx/3xx response. safeRequest
only threw on status >= 400, so such bodies slipped through and crashed
at response.json.

Add BaseGitService.parseJson() which detects non-JSON/HTML bodies and
throws an actionable message, and use it for all response.json reads in
the GitHub and GitLab services. Also harden parseErrorResponse so HTML
error pages produce a clear message instead of dumping the raw document.

Add tests covering HTML 2xx responses, HTML detection by leading '<',
malformed JSON, and HTML error pages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwioG4CNKUBuKiZdowLFWe
2026-06-26 06:02:40 +00:00
Claude
7648eef279
fix: use two-step branch→SHA resolution in GiteaService.listFiles
Gitea's git/trees endpoint requires a tree or commit SHA, not a branch
name, on instances older than ~1.17. Resolve branch to commit SHA via
/branches/{branch} first, then fetch /git/trees/{commitSha}?recursive=1.

Also updates README with provider compatibility table and SVG icons for
GitHub, GitLab, and Gitea.

https://claude.ai/code/session_019Jbz6HpQvWU1wpm5M6MZpd
2026-06-16 04:09:29 +00:00
Claude
130bd93f84
feat: add Gitea support as third-party Git provider
- Add GiteaService implementing BaseGitService/GitServiceInterface
  - Uses Gitea API v1 endpoints (/api/v1/repos/{owner}/{repo}/...)
  - POST for file creation, PUT for updates (differs from GitHub)
  - Authorization: token {token} header
- Add giteaToken, giteaBaseUrl, giteaOwner, giteaRepo settings fields
- Add Gitea option to service type dropdown in settings UI
- Add displayGiteaSettings() panel in settings tab
- Update initializeGitService() to instantiate GiteaService
- Update getServiceName() to handle 'gitea' type
- Add comprehensive test suite (gitea-service.test.ts, 15 test cases)
- Update existing test fixtures to include new Gitea settings fields

Closes #26

https://claude.ai/code/session_019Jbz6HpQvWU1wpm5M6MZpd
2026-06-16 04:04:01 +00:00
ClaudiaFang
f49600eb66 refactor(test): eliminate duplicate code flagged by SonarCloud
- Extract adapter variable and loadWith() helper in gitignore hidden-file tests
- Hoist shared ArrayBuffer constants in path.test.ts
Reduces new_duplicated_lines_density from ~78%/46% to near 0%.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 03:52:32 +00:00
ClaudiaFang
1f71a43980 refactor(test): extract shared SyncManager mock setup to reduce duplication
Move repeated beforeEach mock initialization into createSyncManagerMocks()
helper to eliminate ~45 lines of copy-paste across binary and hidden test files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 03:40:45 +00:00
ClaudiaFang
fcb9447733 test: expand coverage for binary files, hidden paths, and edge cases
- Add sync-manager-binary.test.ts: push/pull ArrayBuffer via adapter.readBinary/writeBinary
- Add sync-manager-hidden.test.ts: hidden path mkdir, push, pull via string paths
- Add utils/path.test.ts: isBinaryPath and contentsEqual full coverage
- Add metadata-on-equal assertion to sync-manager tests
- Add GitHub truncated result and GitLab pagination boundary tests
- Add hidden file gitignore filter tests
- Add docs/test-coverage.md with all 189 test cases documented

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 03:35:36 +00:00
ClaudiaFang
556f9e930d refactor: eliminate code duplication flagged by SonarCloud
- Move contentsEqual to src/utils/path.ts (shared by sync-manager + SyncStatusView)
- Replace private isBinary in both files with isBinaryPath from path.ts
- Extract fileItemCallbacks() in SyncStatusView to remove duplicate callback objects
- Create tests/services/service-test-helpers.ts with shared testConnection,
  getRepoGitignores, getFile error handling, getLastRequestCall helpers
- Refactor github/gitlab service tests to use shared helpers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 09:09:02 +00:00
ClaudiaFang
8f7207a388 fix(test): add @types/jsdom and fix Element type cast in setup-dom
jsdom v29 uses exports field for types, incompatible with moduleResolution
"node" in CI — adding @types/jsdom provides standalone type declarations.
Cast el to HTMLInputElement directly instead of instanceof window.HTMLInputElement
to avoid unresolvable type narrowing across jsdom window context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 08:59:05 +00:00
ClaudiaFang
7f223e33b8 refactor: code quality enhancements for issue #23
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 08:56:28 +00:00
ClaudiaFang
c6152a61bb fix(ui): real-time file status during refresh and unsynced file actions
- Render checked files incrementally below progress bar during refresh
- Progress bar now shows X/Y count and percentage
- Remove fileStatus.file guard from push/remove for unsynced files
- Fix canPush/canDelete in action bar to include hidden (string-path) files
- Fix lint: use pre-declared vi.fn() to avoid unbound-method errors
- Fix test import path and mockSettings typing for sync-manager-mapping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 08:53:51 +00:00
ClaudiaFang
c945b9dfb0 ci: add attestations and id-token permissions for artifact provenance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 07:25:32 +00:00
ClaudiaFang
a77e0150a1 test(ui): add interaction tests for ActionBar, FileListItem, and DiffPanel
Adds comprehensive UI component test coverage with 54 new test cases across three components to close issue #23. Includes JSDOM setup polyfills and tooltip mock utilities for DOM-dependent component testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 05:25:13 +00:00
ClaudiaFang
31ac9189cf refactor: code quality enhancements for issue #23
- Fix plugin scanner warnings: remove !important from styles.css,
  fix CSS shorthand (0 0 8px), use activeWindow.setTimeout(),
  remove .zip from release assets
- Extract LCS diff algorithm to src/utils/diff.ts with full unit tests
- Extract UI render components: ActionBar, FileListItem, DiffPanel
  reducing SyncStatusView from 853 to 523 lines
- Add shared types in src/ui/types.ts
- Add unified logger in src/utils/logger.ts replacing 9 console.* calls

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 04:55:26 +00:00
ClaudiaFang
4574abc294
refactor: address SonarCloud issues and reduce code duplication (#20)
* ci: use shared obsidian plugin workflow and update obsidian version

* Add quality gate status badge to README

Added a quality gate status badge to the README.

* refactor: address SonarCloud issues and reduce code duplication

---------

Co-authored-by: ClaudiaFang <tianyao.firstsun@gmail.coim>
2026-04-27 01:20:40 +08:00
ClaudiaFang
591dd6e82a
refactor: consolidate duplicated methods into BaseGitService (#17) 2026-04-26 10:46:44 +08:00
ClaudiaFang
655cd69252
Fix lint (#15)
* ci: optimize workflow to reduce redundancy and refine permissions

* ci: fix syntax errors in workflow file

* docs: resolve SonarCloud issues and refactor for complexity

* refactor: extract BaseGitService to reduce duplication and fix lint/tests

* fix: resolve TS build errors due to unchecked indexed access

* refactor: address gemini-code-assist bot reviews

- Add last_commit_id to GitLabFileResponse
- Use last_commit_id as sha in GitLabService.getFile
- Add rename detection to SyncManager.processSingleBatchPush

---------

Co-authored-by: ClaudiaFang <tianyao.firstsun@gmail.coim>
2026-04-26 05:23:13 +08:00
ClaudiaFang
5c62533d93
fix: final test coverage and logic improvements (#11)
* chore: update GitHub Actions to latest versions and increase test coverage for batch operations

* fix: resolve linting errors in batch tests

* chore: consolidate GitHub workflows into unified ci.yml and allow sonar failure

* fix: include missing test coverage and logic improvements

---------

Co-authored-by: ClaudiaFang <tianyao.firstsun@gmail.coim>
2026-04-25 15:40:36 +00:00
ClaudiaFang
684551b8f8
fix: resolve SonarCloud Quality Gate failures and improve marketplace readiness (#10)
This commit consolidates several improvements to satisfy SonarCloud Quality Gate and enhance the overall plugin stability:

- Code Quality & Refactoring:
  - Reduced duplication to 0% by refactoring SyncManager and SyncStatusView.
  - Implemented centralized 'processBatch' logic for multi-file sync operations.
  - Fixed security hotspots by replacing legacy atob/btoa with Buffer.
  - Added memory safety limits to the diff algorithm to prevent DoS.

- Test Coverage & Reliability:
  - Improved SyncManager coverage to 81.8% and GitignoreManager to 92.8%.
  - Added comprehensive unit tests for batch operations and complex .gitignore patterns.
  - Resolved all ESLint 'unsafe-member-access' and 'unbound-method' warnings in tests.

- Infrastructure & Metadata:
  - Consolidated GitHub Actions into a single optimized 'ci.yml' using latest versions (v6-v8).
  - Synchronized manifest.json and versions.json to v1.1.0.
  - Fixed workflow permission issues identified by CodeQL.
2026-04-25 23:35:32 +08:00
ClaudiaFang
a6104d991c fix: ensure type-safe mocks in sync-manager.test.ts and resolve lint errors 2026-04-25 03:34:57 +00:00
ClaudiaFang
bad9dad097 chore: fix status check names and finalize sonar configuration 2026-04-25 03:33:40 +00:00
ClaudiaFang
662cb83cf9 test: fix tsc compilation error (TS2532) in tests 2026-04-25 03:29:46 +00:00
ClaudiaFang
60e3790c73 test: fix lint errors in tests by using type-safe mocks and assertions 2026-04-25 03:27:10 +00:00
ClaudiaFang
f354749d4c test: add github service tests and fix existing gitlab/sync-manager tests 2026-04-25 03:24:22 +00:00
ClaudiaFang
ecd2aabfca fix: resolve obsidian community guidelines and lint errors 2026-04-25 03:13:04 +00:00
ClaudiaFang
d5539434dc
feat(core,ui,ci,docs): add GitHub support, sync status view, batch operations, and CI/CD workflows (#1)
Major feature release including:
- GitHub service support alongside GitLab
- Sync status view with visual diff
- Batch push/pull operations
- Conflict resolution modal
- Vault folder filtering
- File rename detection
- Complete CI/CD workflows (check, auto-release, semantic-release)
- Comprehensive documentation (CHANGELOG, COMPLIANCE, RELEASE guides)

Co-authored-by: tianyao <tianyao@heavendev01.royal-powan.ts.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:03:05 +08:00
Tianyao
c2a6bb5fba perf(sync): optimize GitLab push by avoiding redundant getFile calls 2026-04-02 19:55:41 +08:00
Tianyao
6161bd75cd fix(build): resolve dependencies, fix Vitest 4 mocking, and handle file existence 2026-04-02 19:50:43 +08:00
tianyao
cccf096103 feat: implement rootPath setting and Test connection button
- Added rootPath to GitLabFilesPushSettings and DEFAULT_SETTINGS
- Implemented testConnection in GitLabService to verify credentials
- Added rootPath support to GitLabService API URL generation
- Added UI fields for rootPath and Test connection button in settings tab
- Updated tests to include rootPath in mock settings and services

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-01 17:13:12 +00:00
tianyao
b472594d31 task(gitlab-service): enhance getFile robustness and handle 404
- Update getFile to return empty content and sha on 404
- Use presence of sha to determine if push should be POST or PUT
- Fix test setup for Obsidian TFile mock
- Handle ESLint and TypeScript issues in tests and sync manager

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:45:55 +00:00
tianyao
362def7cd3 feat: integrate SyncConflictModal into SyncManager
- Implement conflict detection in pushFile and pullFile using SHA comparison.
- Use SyncConflictModal to prompt users for manual resolution ('Use local' or 'Use remote').
- Added unit tests in sync-manager.test.ts to verify conflict handling logic.
- Fixed linting issues related to unhandled promises and mock type safety.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:43:10 +00:00
tianyao
bb80936834 Implement conflict detection in SyncManager using syncMetadata
- Add SHA comparison in pushFile to detect remote changes before pushing
- Update syncMetadata (lastSyncedSha) after successful push and pull
- Add comprehensive test cases for conflict scenarios and metadata updates
- Use syncMetadata to track state and prevent overwriting remote work

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:40:29 +00:00