Skip to content

fix: code quality enhancements and UI improvements (#23) - #24

Merged
ClaudiaFang merged 7 commits into
masterfrom
issue-23-code-quality
May 20, 2026
Merged

ClaudiaFang merged 7 commits into
masterfrom
issue-23-code-quality

Conversation

@ClaudiaFang

Copy link
Copy Markdown
Member

Summary

  • Real-time file status during refresh: checked files appear incrementally below the progress bar with live X/Y count and percentage
  • Fix unsynced file actions: removed erroneous fileStatus.file guard so Push/Remove buttons correctly appear for hidden (string-path) files
  • Fix canPush/canDelete in action bar to include hidden files
  • Add @types/jsdom to devDependencies for local/CI consistency (jsdom v29 exports-field types incompatible with moduleResolution: "node")
  • Fix Element.type type cast in setup-dom.ts
  • Fix lint errors in sync-manager-mapping.test.ts: correct import path, proper typed mockSettings, pre-declared vi.fn() to avoid unbound-method

Closes #23

Test plan

  • Refresh sync status view — files should appear one by one as they are checked, not all at once after completion
  • Local-only files should show Push and Remove action buttons
  • npm run lint passes
  • npm run build passes
  • CI passes on all Node versions (20, 22, 24)

🤖 Generated with Claude Code

ClaudiaFang and others added 6 commits May 20, 2026 04:55
- 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>
…Panel

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>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces support for binary files in the sync process, updates the gitignore management to scan local files, and refactors UI components. Several issues were identified regarding unsafe type casting when handling potential binary content as strings, which could lead to runtime failures in the conflict modal and gitignore processing. Additionally, there is significant code duplication across the codebase, particularly regarding path normalization and binary file detection, which should be consolidated into shared utility functions.

Comment on lines 142 to 144
if (remoteFile?.content) {
content = remoteFile.content;
content = remoteFile.content as string;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This is an unsafe type cast on line 143. If remoteFile.content happens to be an ArrayBuffer, this will not convert it to a string at runtime and will likely cause issues in the ignore library. It's safer to perform a type check.

Consider something like this:

if (remoteFile?.content) {
    if (typeof remoteFile.content === 'string') {
        content = remoteFile.content;
    } else {
        logger.warn(`Received unexpected binary content for gitignore file: ${fullGitignorePath}`);
    }
}

Comment thread src/logic/sync-manager.ts

if (remote.sha && lastSynced && remote.sha !== lastSynced.lastSyncedSha) {
new SyncConflictModal(this.app, name, content, remote.content, (choice) => {
new SyncConflictModal(this.app, name, content as string, remote.content as string, (choice) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This is an unsafe type cast. If content or remote.content is an ArrayBuffer (which is now possible for binary files), casting it to string will not work as expected and could cause SyncConflictModal to fail. The modal should be made aware of binary files and perhaps show a message that a text diff is not available, rather than attempting to render one from an ArrayBuffer.

Comment thread src/logic/sync-manager.ts
// Conflict detection for pull (only if local exists)
if (exists && remote.sha && lastSynced && remote.sha !== lastSynced.lastSyncedSha) {
new SyncConflictModal(this.app, name, localContent || '', remote.content, (choice) => {
new SyncConflictModal(this.app, name, (localContent as string) || '', remote.content as string, (choice) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similar to a previous comment, this is an unsafe cast for binary files. localContent and remote.content can be ArrayBuffers, which cannot be cast to string this way. The conflict modal needs to handle binary files gracefully, for example by showing a message that a diff is not available, instead of attempting to display one.

for (const subFolder of listing.folders) {
await this.scanDir(subFolder, out);
}
} catch { /* adapter.list may be unavailable in some environments */ }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Silently catching and ignoring errors can hide underlying problems. It's better to at least log a warning here so that if adapter.list fails for an unexpected reason, it can be debugged.

Suggested change
} catch { /* adapter.list may be unavailable in some environments */ }
} catch (e) { logger.warn(`Failed to scan directory ${vaultDir}:`, e); /* adapter.list may be unavailable in some environments */ }

Comment thread src/logic/sync-manager.ts
Comment on lines +33 to +41
private getNormalizedPath(path: string): string {
if (!this.settings.vaultFolder) return path;
const folderPath = this.settings.vaultFolder + '/';
if (path.startsWith(folderPath)) {
return path.substring(folderPath.length);
}
if (path === this.settings.vaultFolder) return '';
return path;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This getNormalizedPath method is duplicated in src/logic/gitignore-manager.ts and src/main.ts. To avoid code duplication and ensure consistent path handling, consider moving this logic to a shared utility file.

Comment thread src/logic/sync-manager.ts
Comment on lines +240 to +249
private isBinary(path: string): boolean {
const ext = path.split('.').pop()?.toLowerCase();
if (!ext) return false;
const BINARY_EXTENSIONS = new Set([
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'pdf', 'zip', 'gz', '7z', 'rar',
'mp3', 'mp4', 'wav', 'ogg', 'webm', 'mov', 'avi', 'wmv',
'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'epub', 'exe', 'dll', 'so'
]);
return BINARY_EXTENSIONS.has(ext);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This isBinary method is duplicated in several places, including SyncStatusView.ts and git-service-base.ts, and each has a slightly different list of extensions. A new utility file src/utils/path.ts was also added with similar logic. To ensure consistency and avoid maintaining multiple lists of binary extensions, this should be consolidated into a single, shared utility function that is used everywhere.

Comment thread src/ui/SyncStatusView.ts Outdated
Comment on lines +490 to +499
private isBinary(path: string): boolean {
const ext = path.split('.').pop()?.toLowerCase();
if (!ext) return false;
const BINARY_EXTENSIONS = new Set([
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'pdf', 'zip', 'gz', '7z', 'rar',
'mp3', 'mp4', 'wav', 'ogg', 'webm', 'mov', 'avi', 'wmv',
'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'epub', 'exe', 'dll', 'so'
]);
return BINARY_EXTENSIONS.has(ext);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This isBinary method is duplicated in src/logic/sync-manager.ts and src/services/git-service-base.ts. To avoid code duplication and ensure consistency, this logic should be centralized in a single utility function. The new src/utils/path.ts file would be a great place for it.

- 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>
@sonarqubecloud

Copy link
Copy Markdown

@ClaudiaFang
ClaudiaFang merged commit f9499a4 into master May 20, 2026
19 of 21 checks passed
@ClaudiaFang
ClaudiaFang deleted the issue-23-code-quality branch May 20, 2026 09:11
@ClaudiaFang

Copy link
Copy Markdown
Member Author

🎉 This PR is included in version 1.0.6 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor]: git-files-sync - Improve code quality and structure

1 participant