Skip to content

Feature/folder search#88

Merged
mind-murtaza merged 54 commits into
devfrom
feature/folder-search
Jun 1, 2026
Merged

Feature/folder search#88
mind-murtaza merged 54 commits into
devfrom
feature/folder-search

Conversation

@Ashminita28

@Ashminita28 Ashminita28 commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR adds folder wide search feature to the application and it also renames the test folder name.

Type of Change

  • Bug fix
  • New feature
  • Refactor (no functional change)
  • Documentation update
  • Style / UI change
  • Build / CI change

Related Issue

Closes #41

Checklist

  • My code follows the project's coding guidelines
  • I have tested my changes locally
  • I have used conventional commit messages
  • I have updated documentation if needed
  • My changes do not introduce new warnings or errors

Electron main / preload

  • Added folder-wide search implementation: apps/main-processor/src/folder-search.ts — exported async searchFolder(folderPath, query) that normalizes query, collects markdown files, scans lines for case-insensitive matches, returns FolderSearchResult entries (filePath, fileName, line (1-based), column (1-based), preview), tolerates common read errors, and stops at MAX_RESULTS.
  • Added file-collection helper: apps/main-processor/src/utils/helper/folder-search-helper.ts — collectMarkdownFiles(...) recursively gathers .md files (skips dotfiles & symlinks), sorts entries, respects MAX_FOLDER_DEPTH and MAX_RESULTS.
  • Introduced constants: apps/main-processor/src/utils/constants/folder-constants.ts — MAX_FOLDER_DEPTH = 10, MAX_RESULTS = 500.
  • IPC handler: apps/main-processor/src/ipc.ts registers IPC_CONSTANTS.SEARCH_FOLDER — validates sender, resolves/sanitizes and authorizes folder path, invokes searchFolder.
  • Preload API: apps/preload/src/index.ts exposes searchFolder(path, query) on window.api via ipcRenderer.invoke.

Renderer UI

  • Integrated folder search into App: wired useFolderSearch, menu events, and keyboard shortcuts to open folder search and handle opening results.
  • New hook: apps/renderer/src/hooks/useFolderSearch.ts — manages open/close state, folderQuery, folderResults, isSearchingFolder; performs API calls with request-id logic to avoid race conditions; returns openFolderSearch/closeFolderSearch/searchFolder handlers.
  • SearchBar updated: apps/renderer/src/components/SearchBar.tsx — supports mode='document'|'folder', folder-specific props (folderQuery, folderResults, isSearchingFolder, hasFolder, onOpenFolderResult); shows results panel in folder mode and disables document match navigation.
  • File actions: apps/renderer/src/hooks/useFileActions.ts now stores/exposes folderPath and persists selected folderPath when opening a folder; opening a folder result loads file into a tab, opens document search with folder query, then closes folder search (errors show toast).
  • Menu & shortcuts:
    • useMenuEvents accepts onSearchFolder and registers MENU_EVENTS.SEARCH_FOLDER.
    • useShortcuts accepts onOpenFolderSearch and remaps mod+Shift+F to open folder search.

Markdown rendering

  • TOC source changes: components/hooks now prefer activeTab.toc when available; useFile extracts TOC from raw markdown (toc supplied to tabs where available). extractTOC refactored to use marked lexer and heading-token helpers.

Shared packages & types

  • New FolderSearchResult type: packages/shared-types/src/search-type.ts exported interface { filePath, fileName, line, column, preview }.
  • MarkdownReaderAPI extended: added searchFolder(path: string, query: string): Promise<FolderSearchResult[]> and updated several settings/onMenuEvent typings (see type changes).
  • IPC constant added: packages/shared-constants/src/ipc-constants.ts → SEARCH_FOLDER = 'searchFolder'.
  • Renderer/shared type updates: SearchBarProps, Tab (optional toc), TabAction payloads, UseMenuEventsProps, UseShortcutsProps updated to support folder-search fields and new menu/theme handlers; shared theme/constants changes (THEMES array and Theme type).

Tests

  • Added tests covering folder search and related features:
    • apps/main-processor/tests/folder-search.test.ts — verifies .md-only matching, non-.md exclusion, empty results, case-insensitivity, and non-existent folder behavior.
    • Additional test coverage added/updated across main-processor and renderer (folder listing depth, inlineImages, file watcher debounce diagnostics, settings validation, TOC extraction tests, etc.).
  • Vitest setup: apps/main-processor/vitest.config.ts now uses setupFiles ['./setup.ts']; setup mocks electron.app for tests.

Tooling / CI

  • CI workflows (.github/workflows/development.yml and production.yml) updated:
    • Use pnpm/action-setup@v4.
    • Added security job running pnpm audit --audit-level=high and Trivy filesystem scan (fail on HIGH/CRITICAL).
  • package.json: bump @commitlint/cli dev dep and added pnpm.overrides for several transitive deps.
  • Husky: .husky/pre-push runs pnpm test.

Docs

  • PR checklist notes documentation update not confirmed; no explicit user-facing docs files modified in this diff. An assets/unsigned-install-notice.txt was added (install notice content).

Packaging

  • Added assets/unsigned-install-notice.txt and configured electron-builder.ts to include it as extraResources and NSIS license.
  • electron.vite.config.ts: removed explicit Rollup externals for main build.

Breaking changes

  • IPC/API: window.api and IPC surface extended with SEARCH_FOLDER / searchFolder — consumers of the API should verify compatibility.
  • Renderer hook/signature changes:
    • useFileActions return shape changed (now includes folderPath and returns setFolderTree).
    • useMenuEvents and useShortcuts accept new callbacks (onSearchFolder, onOpenSettings, onSetTheme, onOpenFolderSearch) — callers must supply updated handlers.
  • Keyboard shortcut remapped: mod+Shift+F now opens folder search (previous behavior changed).
  • Shared types: MarkdownReaderAPI, SearchBarProps, Tab/TabAction payloads, and other exported types changed — upstream consumers must update to new types.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Ashminita28, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 31 minutes and 34 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e531c6c3-c19c-4169-a865-97cd4b3c82a1

📥 Commits

Reviewing files that changed from the base of the PR and between 41c4480 and 4d3fb41.

📒 Files selected for processing (4)
  • .github/workflows/production.yml
  • apps/renderer/src/components/SettingsPanel.tsx
  • apps/renderer/src/hooks/useSettings.ts
  • packages/shared-constants/src/menu-constants.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/folder-search

@coderabbitai coderabbitai 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.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/renderer/src/hooks/useShortcuts.ts (1)

28-38: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Duplicate mod+shift+f condition makes focus-mode shortcut unreachable.

Line 34 reuses the exact same predicate as Line 28, so that branch never executes after the early return at Line 31. This silently breaks onToggleFocusMode() keyboard access. Use a distinct shortcut for one action (or remove the stale branch).

Suggested fix
-      if (mod && e.shiftKey && e.key.toLowerCase() === 'f') {
-        e.preventDefault();
-        onToggleFocusMode();
-        return;
-      }
+      if (mod && e.shiftKey && e.key.toLowerCase() === 'm') {
+        e.preventDefault();
+        onToggleFocusMode();
+        return;
+      }

As per coding guidelines, "apps/renderer/src/**/*.{ts,tsx}: ... keyboard-friendly ...".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/renderer/src/hooks/useShortcuts.ts` around lines 28 - 38, The duplicate
keyboard predicate (mod && e.shiftKey && e.key.toLowerCase() === 'f') makes the
onToggleFocusMode() branch unreachable; update the shortcut logic in
useShortcuts.ts by either changing the second predicate to a distinct key
combination (e.g., use a different key or remove the shift/mod as appropriate)
or removing the stale branch so onOpenFolderSearch() and onToggleFocusMode()
have unique conditions; update the checks referencing mod, e.shiftKey,
e.key.toLowerCase(), and call sites onOpenFolderSearch and onToggleFocusMode
accordingly so both shortcuts are reachable and non-conflicting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/main-processor/__tests__/folder-search.test.ts`:
- Around line 7-17: Replace the fixed DIR constant and setup/teardown in the
test to use a unique temporary directory per run: create the temp dir with
mkdtempSync(path.join(tmpdir(), 'md-search-test-')) inside the beforeAll
(instead of mkdirSync on DIR) and store it in the same variable used by the
test, write files into that directory using writeFileSync(join(tempDir, ...)),
and remove it in afterAll using rmSync(tempDir, { recursive: true, force: true
}); update references to DIR in the test to use the new tempDir variable
(adjusting the beforeAll/afterAll hooks accordingly).
- Around line 19-41: Add a failure-path test for searchFolder that simulates a
filesystem error: mock fs.promises.readdir (or fs.readdirSync) to throw an
ENOENT or EACCES error before calling searchFolder(DIR, 'anything') and assert
the function propagates the error (use await
expect(searchFolder(...)).rejects.toThrow()). Name the test clearly (e.g.,
"throws when directory is unreadable") and ensure you restore/unmock fs after
the test so other tests are unaffected; reference the searchFolder function and
the test file's existing describe block to place the new it() case.

In `@apps/main-processor/src/folder-search.ts`:
- Around line 19-20: The current await readFile(filePath, 'utf-8') in
folder-search.ts can throw and abort the whole search; wrap the file read and
subsequent content.split(...) in a try/catch around readFile (referencing
readFile, filePath, content, lines) and on recoverable FS errors (e.g., ENOENT,
EACCES, EPERM, EISDIR, EMFILE) log a warning with the filePath and error and
continue to the next file, only rethrow or fail for truly unexpected errors;
ensure you do not let a single-file read failure propagate and stop processing
the rest of the folder.

In `@apps/main-processor/src/ipc.ts`:
- Around line 179-180: The code currently auto-authorizes renderer-supplied
folder paths by resolving folderPath and unconditionally adding safeFolderPath
into allowedFolderRoots in the SEARCH_FOLDER handling; instead, change the
SEARCH_FOLDER flow (the handler using resolveDirectoryPath and
allowedFolderRoots) to NOT add safeFolderPath automatically—validate that the
resolved safeFolderPath is already present in allowedFolderRoots (i.e., was
added only via explicit folder-open flow) and reject the request if it is not;
return/emit an error response when the root is unauthorized and ensure only the
explicit folder-open code path is allowed to mutate allowedFolderRoots.

In `@apps/main-processor/src/utils/helper/folder-search-helper.ts`:
- Around line 14-26: The traversal currently fails if readdir or a recursive
collectMarkdownFiles call throws; wrap the await readdir(folderPath, ...) call
in a try/catch and likewise wrap the recursive call to
collectMarkdownFiles(fullPath, ...) so recoverable FS errors (ENOENT, EACCES,
EPERM) are caught and cause the function to skip that folder/entry and continue,
while re-throwing unknown errors; keep using MARKDOWN_FILE_PATTERN, MAX_RESULTS
and entry checks (isSymbolicLink/isDirectory) unchanged so behavior stays the
same except for skipping unreadable/missing/permission-denied paths.

In `@apps/renderer/src/App.tsx`:
- Around line 122-143: The folder SearchBar is being seeded with the document
search `query` instead of the folder-specific `folderQuery`, causing wrong input
and empty-state logic; update the JSX where `SearchBar` is rendered (inside the
`isFolderSearchOpen` block) to pass `query={folderQuery}` instead of
`query={query}` and keep `onQueryChange={searchFolder}` (which updates the
folder query) so the component uses the folder-specific state; ensure references
to `SearchBar`, `isFolderSearchOpen`, `searchFolder`, and `folderQuery` are
adjusted accordingly.
- Around line 134-140: The onOpenFolderResult handler currently calls
loadFileInTab(result.filePath).then(...) without handling rejections; update the
onOpenFolderResult implementation so that loadFileInTab is awaited or its
promise uses .catch/.finally: ensure closeFolderSearch() is always called (use
finally) and surface a user-facing error/feedback when loadFileInTab rejects
(e.g., via existing toast/error UI or set an error state) instead of leaving the
folder search open and producing an unhandled rejection; reference the
loadFileInTab, openSearch, setQuery, and closeFolderSearch symbols when making
the change.

In `@apps/renderer/src/components/SearchBar.tsx`:
- Around line 114-153: The folder-mode UI in SearchBar.tsx is using the generic
prop query (and seeding localQuery from it) but the parent provides a separate
folderQuery; update SearchBar to use the folder-specific prop when isFolderMode
is true: replace usages of query (and the localQuery initial value) with
folderQuery for the folder-mode branches that render the empty-state message and
prefilled input, ensuring folderResults, isSearchingFolder, hasFolder and the
onOpenFolderResult handler remain wired up to the same UI elements so behavior
stays consistent.

In `@apps/renderer/src/hooks/useFolderSearch.ts`:
- Around line 17-34: searchFolder should guard against missing bridge, catch IPC
rejections, and ignore out-of-order responses: first check window.api and return
early if missing; increment a local request token (useRef like currentRequestId)
at the start of searchFolder, capture it in the async call, and only call
setFolderResults or setIsSearchingFolder(false) if the captured token matches
the latest; wrap the await window.api.searchFolder(folderPath, query) in
try/catch to prevent unhandled rejections and setFolderResults([]) or leave
results unchanged on error, and ensure setIsSearchingFolder is cleared in a
finally block that also checks the token to avoid clearing state for newer
requests (references: searchFolder, window.api.searchFolder, setFolderResults,
setIsSearchingFolder, folderPath, folderQuery).

In `@apps/renderer/src/hooks/useMenuEvents.ts`:
- Around line 30-31: Replace the comma-operator expression that registers both
listeners with two separate statements: call
window.api.onMenuEvent(MENU_EVENTS.SEARCH_FOLDER, onSearchFolder) on its own
line and window.api.onMenuEvent(MENU_EVENTS.TOGGLE_TOC, onToggleToc) on the next
line so the registration of MENU_EVENTS.SEARCH_FOLDER/onSearchFolder and
MENU_EVENTS.TOGGLE_TOC/onToggleToc is explicit and not wrapped in a
parenthesized comma expression.

---

Outside diff comments:
In `@apps/renderer/src/hooks/useShortcuts.ts`:
- Around line 28-38: The duplicate keyboard predicate (mod && e.shiftKey &&
e.key.toLowerCase() === 'f') makes the onToggleFocusMode() branch unreachable;
update the shortcut logic in useShortcuts.ts by either changing the second
predicate to a distinct key combination (e.g., use a different key or remove the
shift/mod as appropriate) or removing the stale branch so onOpenFolderSearch()
and onToggleFocusMode() have unique conditions; update the checks referencing
mod, e.shiftKey, e.key.toLowerCase(), and call sites onOpenFolderSearch and
onToggleFocusMode accordingly so both shortcuts are reachable and
non-conflicting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 98c93dde-6ca5-4d82-83c7-3a7f4a16e8e9

📥 Commits

Reviewing files that changed from the base of the PR and between d962082 and ee8ad9b.

📒 Files selected for processing (41)
  • apps/main-processor/__tests__/cli.test.ts
  • apps/main-processor/__tests__/docx.test.ts
  • apps/main-processor/__tests__/export.test.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/main-processor/__tests__/ipc.test.ts
  • apps/main-processor/__tests__/menu.test.ts
  • apps/main-processor/__tests__/recent.test.ts
  • apps/main-processor/src/folder-search.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/utils/constants/folder-constants.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/preload/src/index.ts
  • apps/renderer/__tests__/components/Sidebar.test.tsx
  • apps/renderer/__tests__/components/StatusBar.test.tsx
  • apps/renderer/__tests__/components/TabBar.test.tsx
  • apps/renderer/__tests__/components/Welcome.test.tsx
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/renderer/__tests__/hooks/useSearch.test.ts
  • apps/renderer/__tests__/hooks/useSettings.test.ts
  • apps/renderer/__tests__/renderer/callout.test.ts
  • apps/renderer/__tests__/renderer/drag-drop.test.ts
  • apps/renderer/__tests__/renderer/katex.test.ts
  • apps/renderer/__tests__/renderer/markdown.test.ts
  • apps/renderer/__tests__/renderer/mermaid.test.ts
  • apps/renderer/__tests__/renderer/sanitize.test.ts
  • apps/renderer/__tests__/renderer/sikhi.test.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/renderer/__tests__/store/tabStore.test.ts
  • apps/renderer/src/App.tsx
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/hooks/useShortcuts.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/types/hook-types.ts
  • packages/shared-constants/src/ipc-constants.ts
  • packages/shared-types/src/index.ts
  • packages/shared-types/src/markdown-type.ts
  • packages/shared-types/src/search-type.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use TypeScript as the primary language for the application

Files:

  • packages/shared-constants/src/ipc-constants.ts
  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/main-processor/src/utils/constants/folder-constants.ts
  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/main-processor/src/folder-search.ts
  • apps/main-processor/src/ipc.ts
  • packages/shared-types/src/search-type.ts
  • apps/renderer/src/types/hook-types.ts
  • packages/shared-types/src/markdown-type.ts
  • packages/shared-types/src/index.ts
  • apps/renderer/src/types/component-types.ts
  • apps/preload/src/index.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useShortcuts.ts
apps/main-processor/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Marked for parsing Markdown content

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/main-processor/src/utils/constants/folder-constants.ts
  • apps/main-processor/src/folder-search.ts
  • apps/main-processor/src/ipc.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Vitest for testing

Files:

  • apps/main-processor/__tests__/folder-search.test.ts

⚙️ CodeRabbit configuration file

**/*.{test,spec}.{ts,tsx}: Review tests.

  • Cover success and failure paths, especially IPC, filesystem, markdown rendering, search, settings, tabs, and exports.
  • Use isolated temp directories for disk tests and clean them up.
  • Mock Electron/preload APIs explicitly.
  • Prefer Testing Library user-event and getByRole for UI tests.

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Write unit tests for all new features to maintain code quality, adding test cases alongside component source code and ensuring all tests pass via pnpm vitest

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
apps/main-processor/src/**/*.ts

⚙️ CodeRabbit configuration file

apps/main-processor/src/**/*.ts: Review as Electron main-process code.

  • IPC handlers must use shared constants and validate renderer input.
  • File/folder access must guard path traversal, missing files, permissions, symlinks, and deleted watched files.
  • Watchers, menus, dialogs, and IPC listeners must be cleaned up.
  • Do not expose Node/Electron internals or unrestricted filesystem access.
  • Export/update/download flows must sanitize content, close resources, and avoid executing embedded scripts.

Files:

  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/main-processor/src/utils/constants/folder-constants.ts
  • apps/main-processor/src/folder-search.ts
  • apps/main-processor/src/ipc.ts
apps/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

apps/renderer/**/*.{ts,tsx}: Use React for frontend UI components
Use Shiki for syntax highlighting in code blocks
Use KaTeX for mathematical equation rendering
Use Mermaid for diagram rendering

Files:

  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useShortcuts.ts
apps/renderer/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (README.md)

Use Tailwind CSS for styling

Files:

  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useShortcuts.ts
apps/renderer/src/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{ts,tsx}: Review as React renderer code.

  • Keep components typed, accessible, keyboard-friendly, and resilient to missing preload APIs.
  • Effects must have correct dependencies and cleanup.
  • Handle loading, empty, error, stale-response, and rejected-promise states.
  • Do not import Node-only modules into renderer code.
  • Avoid unnecessary derived state, unsafe globals, and broad any types.

Files:

  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useShortcuts.ts
apps/preload/src/**/*.ts

⚙️ CodeRabbit configuration file

apps/preload/src/**/*.ts: Review as a strict preload boundary.

  • Expose only typed contextBridge APIs, never raw ipcRenderer.
  • Use shared IPC constants and shared payload/result types.
  • Listener methods must return unsubscribe functions.
  • Reject broad channel names, arbitrary invoke/send wrappers, and any-typed payloads.

Files:

  • apps/preload/src/index.ts
apps/renderer/src/**/*.{css,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{css,tsx}: Review UI, theme, and accessibility.

  • Interactive controls need semantic elements, visible focus, and keyboard access.
  • Theme changes must preserve readable contrast in light and dark modes.
  • Markdown prose must remain readable for tables, code, blockquotes, links, lists, and images.
  • Prefer existing tokens/classes over ad hoc inline styling.

Files:

  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
🔇 Additional comments (9)
packages/shared-types/src/search-type.ts (1)

1-7: LGTM!

packages/shared-types/src/index.ts (1)

6-6: LGTM!

packages/shared-types/src/markdown-type.ts (1)

4-4: LGTM!

Also applies to: 20-20

packages/shared-constants/src/ipc-constants.ts (1)

12-12: LGTM!

apps/renderer/src/types/component-types.ts (1)

4-4: LGTM!

Also applies to: 67-73

apps/main-processor/src/utils/constants/folder-constants.ts (1)

1-2: LGTM!

apps/preload/src/index.ts (1)

18-18: LGTM!

apps/renderer/src/types/hook-types.ts (1)

31-31: LGTM!

Also applies to: 53-53

apps/renderer/src/hooks/useFileActions.ts (1)

7-7: LGTM!

Also applies to: 10-16, 41-41

Comment thread apps/main-processor/__tests__/folder-search.test.ts
Comment thread apps/main-processor/__tests__/folder-search.test.ts
Comment thread apps/main-processor/src/folder-search.ts Outdated
Comment thread apps/main-processor/src/ipc.ts
Comment thread apps/main-processor/src/utils/helper/folder-search-helper.ts Outdated
Comment thread apps/renderer/src/App.tsx
Comment thread apps/renderer/src/App.tsx
Comment thread apps/renderer/src/components/SearchBar.tsx
Comment thread apps/renderer/src/hooks/useFolderSearch.ts
Comment thread apps/renderer/src/hooks/useMenuEvents.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/main-processor/src/ipc.ts (1)

174-187: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate the IPC payload before resolving paths.

validateSender authenticates the caller, but it does not guarantee that folderPath and query are usable strings. A malformed payload reaches resolveDirectoryPath/searchFolder and can fail on the main-process boundary.

Suggested fix
   ipcMain.handle(IPC_CONSTANTS.SEARCH_FOLDER, async (event, folderPath: string, query: string) => {
     if (!validateSender(event)) {
       throw new Error('Untrusted sender');
     }
+    if (typeof folderPath !== 'string' || typeof query !== 'string') {
+      throw new Error('Invalid search payload');
+    }

     const safeFolderPath = await resolveDirectoryPath(folderPath);
     allowedFolderRoots.add(safeFolderPath);

As per coding guidelines, “IPC handlers must use shared constants and validate renderer input.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/main-processor/src/ipc.ts` around lines 174 - 187, The IPC handler for
IPC_CONSTANTS.SEARCH_FOLDER currently trusts folderPath and query after
validateSender; add explicit validation and sanitization of the incoming payload
before calling resolveDirectoryPath or searchFolder: verify folderPath and query
are strings, non-empty, within max length, and match allowed pattern(s) (use the
shared renderer-to-main validation utilities/constants), reject or throw a clear
error for invalid types/values, and only then call
resolveDirectoryPath(safeFolderPath) and proceed to allowedFolderRoots and
searchFolder; reference the handler, validateSender, resolveDirectoryPath,
searchFolder, and allowedFolderRoots when making the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/main-processor/__tests__/folder-search.test.ts`:
- Around line 42-46: The test title says the call should reject but the body
asserts an empty array; update the test for consistency by changing the
assertion to expect rejection: call searchFolder(nonExistentDir, 'React') via
Jest's promise matchers (await expect(searchFolder(...)).rejects.toThrow() or a
more specific error matcher) so the test truly verifies failure, and keep the
test name (or if you prefer to keep the current assertion, rename the it(...)
text to reflect that it returns an empty array); reference the searchFolder call
and the test description to make the change.

In `@apps/renderer/src/App.tsx`:
- Around line 140-141: The catch for loadFileInTab currently calls
setShowToast(true) which reuses the same toast used for the "File updated"
success message; change this to a dedicated error toast state (for example add
setShowErrorToast / showErrorToast or convert showToast to a typed toast object
like setToast({type:'error', message: '...'}) ) and update the component's
rendering logic where message="File updated" is used so success and error toasts
are rendered separately; ensure the catch block sets the error state and
supplies a clear error message while leaving the success toast untouched.

In `@apps/renderer/src/hooks/useFolderSearch.ts`:
- Around line 21-25: In useFolderSearch, when bailing out for empty/invalid
input (the branch that checks !folderPath || !query.trim() ||
!window.api?.searchFolder), invalidate any pending in-flight request by
advancing requestId.current (e.g., ++requestId.current) and set
isSearchingFolder to false before calling setFolderResults([]) and returning;
this ensures an older response cannot win later and repopulate folderResults for
an empty search.

---

Outside diff comments:
In `@apps/main-processor/src/ipc.ts`:
- Around line 174-187: The IPC handler for IPC_CONSTANTS.SEARCH_FOLDER currently
trusts folderPath and query after validateSender; add explicit validation and
sanitization of the incoming payload before calling resolveDirectoryPath or
searchFolder: verify folderPath and query are strings, non-empty, within max
length, and match allowed pattern(s) (use the shared renderer-to-main validation
utilities/constants), reject or throw a clear error for invalid types/values,
and only then call resolveDirectoryPath(safeFolderPath) and proceed to
allowedFolderRoots and searchFolder; reference the handler, validateSender,
resolveDirectoryPath, searchFolder, and allowedFolderRoots when making the
changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9aaddd77-14ed-433e-bb14-b4fbc64fd2d7

📥 Commits

Reviewing files that changed from the base of the PR and between ee8ad9b and 5276c12.

📒 Files selected for processing (9)
  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/main-processor/src/folder-search.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/renderer/src/App.tsx
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/types/component-types.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use TypeScript as the primary language for the application

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/main-processor/src/folder-search.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/main-processor/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Marked for parsing Markdown content

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/main-processor/src/folder-search.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Vitest for testing

Files:

  • apps/main-processor/__tests__/folder-search.test.ts

⚙️ CodeRabbit configuration file

**/*.{test,spec}.{ts,tsx}: Review tests.

  • Cover success and failure paths, especially IPC, filesystem, markdown rendering, search, settings, tabs, and exports.
  • Use isolated temp directories for disk tests and clean them up.
  • Mock Electron/preload APIs explicitly.
  • Prefer Testing Library user-event and getByRole for UI tests.

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Write unit tests for all new features to maintain code quality, adding test cases alongside component source code and ensuring all tests pass via pnpm vitest

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
apps/main-processor/src/**/*.ts

⚙️ CodeRabbit configuration file

apps/main-processor/src/**/*.ts: Review as Electron main-process code.

  • IPC handlers must use shared constants and validate renderer input.
  • File/folder access must guard path traversal, missing files, permissions, symlinks, and deleted watched files.
  • Watchers, menus, dialogs, and IPC listeners must be cleaned up.
  • Do not expose Node/Electron internals or unrestricted filesystem access.
  • Export/update/download flows must sanitize content, close resources, and avoid executing embedded scripts.

Files:

  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/utils/helper/folder-search-helper.ts
  • apps/main-processor/src/folder-search.ts
apps/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

apps/renderer/**/*.{ts,tsx}: Use React for frontend UI components
Use Shiki for syntax highlighting in code blocks
Use KaTeX for mathematical equation rendering
Use Mermaid for diagram rendering

Files:

  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/renderer/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (README.md)

Use Tailwind CSS for styling

Files:

  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/renderer/src/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{ts,tsx}: Review as React renderer code.

  • Keep components typed, accessible, keyboard-friendly, and resilient to missing preload APIs.
  • Effects must have correct dependencies and cleanup.
  • Handle loading, empty, error, stale-response, and rejected-promise states.
  • Do not import Node-only modules into renderer code.
  • Avoid unnecessary derived state, unsafe globals, and broad any types.

Files:

  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/renderer/src/**/*.{css,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{css,tsx}: Review UI, theme, and accessibility.

  • Interactive controls need semantic elements, visible focus, and keyboard access.
  • Theme changes must preserve readable contrast in light and dark modes.
  • Markdown prose must remain readable for tables, code, blockquotes, links, lists, and images.
  • Prefer existing tokens/classes over ad hoc inline styling.

Files:

  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
🔇 Additional comments (9)
apps/main-processor/__tests__/folder-search.test.ts (2)

7-17: Still using a shared temp directory.

tmpdir()/md-search-test can collide across parallel Vitest runs. Use mkdtempSync and clean up that unique directory in teardown. As per coding guidelines, “Use isolated temp directories for disk tests and clean them up.”


19-48: Still missing the per-file filesystem failure path.

This suite covers a missing root directory, but it still does not exercise the recoverable readFile errors in apps/main-processor/src/folder-search.ts. Add a case where a discovered .md file becomes unreadable or disappears and assert the search skips it instead of aborting. As per coding guidelines, “Cover success and failure paths, especially IPC, filesystem, markdown rendering, search, settings, tabs, and exports.”

apps/main-processor/src/ipc.ts (1)

179-186: This authorization check is still bypassed by Line 180.

Adding safeFolderPath to allowedFolderRoots before the some(...) check lets every resolved path authorize itself. Reject first, and only mutate the allow-list in the explicit folder-open flow. As per coding guidelines, “Do not expose Node/Electron internals or unrestricted filesystem access.”

apps/renderer/src/types/component-types.ts (1)

109-109: LGTM!

Also applies to: 120-128

apps/main-processor/src/utils/helper/folder-search-helper.ts (1)

14-22: LGTM!

apps/main-processor/src/folder-search.ts (1)

19-26: LGTM!

apps/renderer/src/hooks/useMenuEvents.ts (1)

30-31: LGTM!

Also applies to: 64-65

apps/renderer/src/App.tsx (1)

124-127: LGTM!

Also applies to: 187-187

apps/renderer/src/components/SearchBar.tsx (1)

27-30: LGTM!

Also applies to: 62-62

Comment thread apps/main-processor/__tests__/folder-search.test.ts Outdated
Comment thread apps/renderer/src/App.tsx
Comment thread apps/renderer/src/hooks/useFolderSearch.ts Outdated

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/renderer/src/hooks/useFolderSearch.ts (1)

12-17: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

closeFolderSearch leaves isSearchingFolder stuck. It bumps requestId (good, so the in-flight response is ignored), but the in-flight finally then won't run its setIsSearchingFolder(false) because the token no longer matches. Result: close during an active search and the loading flag stays true, surfacing a phantom spinner on reopen.

🛠️ Clear the flag on close
   const closeFolderSearch = useCallback(() => {
     requestId.current += 1;
     setIsFolderSearchOpen(false);
     setFolderQuery('');
     setFolderResults([]);
+    setIsSearchingFolder(false);
   }, []);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/renderer/src/hooks/useFolderSearch.ts` around lines 12 - 17, The
closeFolderSearch callback currently increments requestId and clears
query/results but doesn't clear the loading flag, leaving isSearchingFolder
stuck true; update closeFolderSearch to also call setIsSearchingFolder(false)
(alongside requestId.current += 1, setIsFolderSearchOpen(false),
setFolderQuery(''), setFolderResults([])) so any visible spinner is cleared
immediately when closing, ensuring the in-flight response being ignored won't
leave the loading state set.
♻️ Duplicate comments (1)
apps/main-processor/__tests__/folder-search.test.ts (1)

7-17: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Still using a fixed temp dir — collision risk across parallel runs.

DIR is a static md-search-test path, so concurrent runs can clobber each other's fixtures. Switch to a per-run mkdtempSync directory and clean it up in teardown.

🧹 Proposed fix
-import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
+import { writeFileSync, mkdtempSync, rmSync } from 'node:fs';
 import { join } from 'node:path';
 import { tmpdir } from 'node:os';
 import { searchFolder } from '../src/folder-search';

-const DIR = join(tmpdir(), 'md-search-test');
+let DIR: string;

 beforeAll(() => {
-  mkdirSync(DIR, { recursive: true });
+  DIR = mkdtempSync(join(tmpdir(), 'md-search-test-'));
   writeFileSync(join(DIR, 'README.md'), '# Project\nThis project uses React.\nReact is great.');
   writeFileSync(join(DIR, 'CHANGELOG.md'), '# Changes\nVersion 1.0 released.');
   writeFileSync(join(DIR, 'notes.txt'), 'not a markdown file');
 });
 afterAll(() => {
   rmSync(DIR, { recursive: true, force: true });
 });

As per coding guidelines, "Use isolated temp directories for disk tests and clean them up."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/main-processor/__tests__/folder-search.test.ts` around lines 7 - 17, The
test uses a fixed DIR constant which risks collisions; change setup to create a
unique temp dir per run using mkdtempSync (replace the DIR constant with a
variable created inside beforeAll), update beforeAll to call
mkdtempSync(tmpdir() + pathSep or use join with a prefix) and write the fixture
files into that new dir, and update afterAll to rmSync that variable; ensure
references to DIR in the test are replaced by the new variable so tests use an
isolated mkdtempSync directory and still clean it up in afterAll.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/renderer/src/hooks/useFolderSearch.ts`:
- Around line 12-17: The closeFolderSearch callback currently increments
requestId and clears query/results but doesn't clear the loading flag, leaving
isSearchingFolder stuck true; update closeFolderSearch to also call
setIsSearchingFolder(false) (alongside requestId.current += 1,
setIsFolderSearchOpen(false), setFolderQuery(''), setFolderResults([])) so any
visible spinner is cleared immediately when closing, ensuring the in-flight
response being ignored won't leave the loading state set.

---

Duplicate comments:
In `@apps/main-processor/__tests__/folder-search.test.ts`:
- Around line 7-17: The test uses a fixed DIR constant which risks collisions;
change setup to create a unique temp dir per run using mkdtempSync (replace the
DIR constant with a variable created inside beforeAll), update beforeAll to call
mkdtempSync(tmpdir() + pathSep or use join with a prefix) and write the fixture
files into that new dir, and update afterAll to rmSync that variable; ensure
references to DIR in the test are replaced by the new variable so tests use an
isolated mkdtempSync directory and still clean it up in afterAll.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b9d22c93-8328-45ce-b218-d7fdeaf466ef

📥 Commits

Reviewing files that changed from the base of the PR and between 5276c12 and 06f2341.

📒 Files selected for processing (2)
  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use TypeScript as the primary language for the application

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
apps/main-processor/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Marked for parsing Markdown content

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Vitest for testing

Files:

  • apps/main-processor/__tests__/folder-search.test.ts

⚙️ CodeRabbit configuration file

**/*.{test,spec}.{ts,tsx}: Review tests.

  • Cover success and failure paths, especially IPC, filesystem, markdown rendering, search, settings, tabs, and exports.
  • Use isolated temp directories for disk tests and clean them up.
  • Mock Electron/preload APIs explicitly.
  • Prefer Testing Library user-event and getByRole for UI tests.

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Write unit tests for all new features to maintain code quality, adding test cases alongside component source code and ensuring all tests pass via pnpm vitest

Files:

  • apps/main-processor/__tests__/folder-search.test.ts
apps/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

apps/renderer/**/*.{ts,tsx}: Use React for frontend UI components
Use Shiki for syntax highlighting in code blocks
Use KaTeX for mathematical equation rendering
Use Mermaid for diagram rendering

Files:

  • apps/renderer/src/hooks/useFolderSearch.ts
apps/renderer/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (README.md)

Use Tailwind CSS for styling

Files:

  • apps/renderer/src/hooks/useFolderSearch.ts
apps/renderer/src/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{ts,tsx}: Review as React renderer code.

  • Keep components typed, accessible, keyboard-friendly, and resilient to missing preload APIs.
  • Effects must have correct dependencies and cleanup.
  • Handle loading, empty, error, stale-response, and rejected-promise states.
  • Do not import Node-only modules into renderer code.
  • Avoid unnecessary derived state, unsafe globals, and broad any types.

Files:

  • apps/renderer/src/hooks/useFolderSearch.ts
🔇 Additional comments (3)
apps/main-processor/__tests__/folder-search.test.ts (1)

42-47: LGTM!

apps/renderer/src/hooks/useFolderSearch.ts (2)

22-25: Early-return still lets a stale response win. This branch clears results but never advances requestId or clears isSearchingFolder. If a search is already in flight when the query is emptied (or window.api is missing), the older response at Line 30 still satisfies current === requestId.current and repopulates folderResults for what should be an empty search.

🐛 Minimal fix
       setFolderQuery(query);
       if (!folderPath || !query.trim() || !window.api?.searchFolder) {
+        requestId.current += 1;
+        setIsSearchingFolder(false);
         setFolderResults([]);
         return;
       }

1-11: LGTM!

Also applies to: 26-49

@coderabbitai coderabbitai 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.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
.github/workflows/production.yml (1)

14-15: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden checkout: disable credential persistence.

actions/checkout persists the GITHUB_TOKEN in the runner's git config by default; the security/audit job in particular has no need for it. Setting persist-credentials: false reduces token-exfiltration surface (flagged by zizmor artipacked).

🔧 Suggested fix (apply to both checkout steps)
       - name: Checkout
         uses: actions/checkout@v4
+        with:
+          persist-credentials: false

Also applies to: 49-50

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/production.yml around lines 14 - 15, Update both checkout
steps that use actions/checkout@v4 to disable credential persistence by adding
persist-credentials: false to their step configuration; locate the steps
invoking actions/checkout@v4 and set persist-credentials: false so the
GITHUB_TOKEN is not written into the runner git config.
apps/main-processor/src/index.ts (1)

64-66: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle rejection on these async calls too.

sendFilePathToRenderer is now async and can reject (e.g. resolveMarkdownFilePath throws). The open-file handler at Line 76 guards with .catch, but the did-finish-load call here (and the second-instance call at Line 92) are fire-and-forget → unhandled promise rejection. Apply the same .catch logging for consistency.

🛡️ Proposed fix
-    if (filePathToOpen) {
-      sendFilePathToRenderer(filePathToOpen);
-    }
+    if (filePathToOpen) {
+      void sendFilePathToRenderer(filePathToOpen).catch((error) => {
+        console.error('Failed to open file:', error);
+      });
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/main-processor/src/index.ts` around lines 64 - 66, The calls to
sendFilePathToRenderer in the did-finish-load branch and the second-instance
handler are fire-and-forget but sendFilePathToRenderer is now async and can
reject; update both invocations to attach a .catch that logs the error (e.g.
processLogger.error("sendFilePathToRenderer failed", err)) so rejections are
handled consistently like the open-file handler does; locate
sendFilePathToRenderer usages in the did-finish-load handler and the
second-instance handler and append .catch(...) with clear context in the log
message.
electron-builder.ts (1)

20-29: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Redundant copy of the notice.

{ from: 'assets', to: 'assets' } already ships unsigned-install-notice.txt inside resources/assets/. The second entry additionally places it at resources/UNSIGNED_INSTALL_NOTICE.txt. Keep it if root-level visibility is intended; otherwise it's a duplicate artifact.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron-builder.ts` around lines 20 - 29, The extraResources array currently
contains two entries that copy the same file: the directory entry { from:
'assets', to: 'assets' } already includes assets/unsigned-install-notice.txt, so
remove the duplicate file-level entry ({ from:
'assets/unsigned-install-notice.txt', to: 'UNSIGNED_INSTALL_NOTICE.txt' })
unless you intentionally need the notice at the root
(UNSIGNED_INSTALL_NOTICE.txt); if root-level visibility is required keep the
file-level mapping, otherwise delete that specific mapping to avoid creating
duplicate artifacts.
apps/renderer/src/hooks/useFileActions.ts (1)

34-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard window.api in openFileDialog.

openFolder bails on a missing bridge, but openFileDialog calls window.api.openFileDialog() unguarded — it throws when preload is absent. Mirror the guard for consistency and resilience.

🛡️ Proposed fix
   const openFileDialog = useCallback(() => {
+    if (!window.api) return;
     void window.api.openFileDialog().then((chosenPath) => {
       if (chosenPath) {
         void loadFileInTab(chosenPath);
       }
     });
   }, [loadFileInTab]);
As per coding guidelines: renderer code should be "resilient to missing preload APIs."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/renderer/src/hooks/useFileActions.ts` around lines 34 - 40,
openFileDialog currently calls window.api.openFileDialog() without guarding
against a missing preload API, causing a crash if window.api is undefined;
update the openFileDialog function to mirror the openFolder guard by checking
that window.api and window.api.openFileDialog exist before calling, and return
early if they don't (keeping the existing loadFileInTab flow unchanged when a
path is returned).
apps/main-processor/__tests__/file.test.ts (1)

42-84: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

TEST_FILE writes into the working dir and is never cleaned up.

'./test-file.md' is created relative to cwd (likely the package root) and the new tests at 67-84 keep writing to it without an unlink. Prefer a path under test_dir (already a temp dir) and remove it in cleanup to avoid polluting the repo and risking cross-test state.

🧹 Suggested adjustment
-  const TEST_FILE = './test-file.md';
+  const TEST_FILE = join(test_dir, 'watcher-test.md');
+
+  afterAll(() => {
+    try { unlinkSync(TEST_FILE); } catch { /* ignore */ }
+  });
As per coding guidelines: "Use isolated temp directories for disk tests and clean them up."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/main-processor/__tests__/file.test.ts` around lines 42 - 84, Tests write
./test-file.md into repo root and never remove it; change TEST_FILE to point
into the existing temporary test_dir (e.g. construct TEST_FILE with
path.join(test_dir, 'test-file.md')) so each test uses an isolated temp path,
and add cleanup to remove the file after tests (use unlink/unlinkSync in
afterEach or afterAll). Update any references in these tests that use TEST_FILE
and ensure unWatchFile/watchFile behavior is unchanged.
♻️ Duplicate comments (1)
apps/renderer/src/App.tsx (1)

147-155: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Error path still shows the "File updated" success toast.

When loadFileInTab rejects, the .catch flips showToast, which renders the message="File updated" toast at Line 219 — so a failed open looks like success. Use a dedicated error toast/message.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/renderer/src/App.tsx` around lines 147 - 155, The catch block in the
onOpenFolderResult handler flips the generic showToast flag
(setShowToast(true)), which causes the existing success toast (message="File
updated") to appear on failure; instead, replace that behavior so
loadFileInTab's rejection triggers a dedicated error toast/state (e.g.,
setErrorToast(true) or setToast({type: 'error', message: 'Failed to open file:
<error>'})) rather than toggling the success showToast flag; update the
onOpenFolderResult catch to capture the error and call the new error-toast
setter (and/or set a toast payload) and update the toast render logic to show an
error message when that error state/payload is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/development.yml:
- Line 23: CI currently pins pnpm via packageManager and uses
pnpm/action-setup@v4 which is fine; update the workflow to run the security job
after the build by adding needs: build to the jobs.security definition (look for
the jobs.security block in the .github/workflows/development.yml and add the
needs dependency), ensuring scans (pnpm audit and Trivy steps) execute only
after the build/package steps complete.

In @.github/workflows/production.yml:
- Around line 52-53: The security job in production.yml uses
pnpm/action-setup@v4 without a pinned version while the build job sets with:
version: 10 and package.json declares packageManager: "pnpm@10.33.0", causing
inconsistent pnpm versions; update the security job to pass the exact same
version value (e.g., with: version: 10.33.0 or align both to "pnpm@10.33.0") to
match package.json and the build job, ensuring the pnpm/action-setup invocation
in both jobs uses an identical resolved version for deterministic installs.

In `@apps/main-processor/__tests__/folder.test.ts`:
- Around line 9-21: The test creates a real temp directory via mkdtemp (variable
root) but never removes it; wrap the test body that uses mkdtemp, mkdir,
writeFile and getFolder in a try/finally where you await cleanup in the finally
block (use fs.promises.rm or rm with recursive/force options) to remove root
after the test completes; locate the mkdtemp usage and the getFolder call in the
test (file variable root and function getFolder) and ensure the removal runs
regardless of assertions or errors.

In `@apps/renderer/src/components/SettingsPanel.tsx`:
- Around line 14-31: The modal currently sets focus but does not trap
Tab/Shift+Tab or restore the trigger's focus on close; update the effect that
runs when isOpen (the useEffect referencing dialogRef, isOpen, onClose) to (1)
save document.activeElement into a local variable (previouslyFocused) when
opening and restore it on cleanup/close, and (2) install a keydown handler (or
use focus-trap-react) that intercepts Tab/Shift+Tab and cycles focus inside
dialogRef by finding first/last focusable elements so focus cannot escape while
aria-modal="true"; ensure cleanup removes the handler and restores
previouslyFocused and still calls onClose on Escape/backdrop.

In `@apps/renderer/src/config/marked.ts`:
- Line 31: Replace the fragile index-based THEMES[1] usage in marked.ts with an
explicit stable reference or safe lookup: stop relying on array order and
instead set the theme variable to a concrete Theme (e.g., const theme: Theme =
'github-dark') or derive it via a safe lookup like THEMES.find(t => t ===
'github-dark') ?? 'github-dark'; update the variable named theme and any
downstream uses to use this explicit value so reordering THEMES won't change the
selected highlighting theme.

In `@apps/renderer/src/context/ThemeProvider.tsx`:
- Around line 18-27: The effect in ThemeProvider.tsx should seed the theme from
getSystemTheme() on mount (not only on media 'change') when there's no saved
override; update the useEffect to first check if
!localStorage.getItem('app-theme') then call setThemeState(getSystemTheme())
immediately, then create the MediaQueryList, attach onChange (which can remain
as setThemeState(getSystemTheme()) guard by the same localStorage check), and
keep the existing cleanup that calls media.removeEventListener('change',
onChange).

In `@apps/renderer/src/hooks/useMenuEvents.ts`:
- Around line 47-51: The SET_THEME IPC handler currently accepts any string and
force-casts to Theme; update the listener registered via window.api.onMenuEvent
for MENU_EVENTS.SET_THEME to first verify the incoming theme string is one of
the allowed values in APPTHEMES (e.g., APPTHEMES.includes(theme)) before calling
onSetTheme; only call onSetTheme(theme as Theme) when the membership check
passes, otherwise ignore or log an unexpected value.

In `@apps/renderer/src/hooks/useSettings.ts`:
- Around line 61-69: updateSettings in useSettings calls await
window.api.saveSettings(partial) without catching rejections, so a disk/IPC
failure will bubble up and leave the UI with stale state; update updateSettings
to perform an optimistic local setSettings((c)=>({...c,...partial}))
before/around the async call, wrap the await window.api.saveSettings(partial) in
try/catch, on success replace state with the returned next value
(setSettings(next)), and on failure log or surface the error (e.g., via
console.error or a provided error handler) while keeping the optimistic state so
the UI reflects the user’s intent; refer to the updateSettings function and
window.api.saveSettings symbols when making the change.
- Around line 10-19: The current hook swallows IPC/read errors and may apply an
invalid value from the main process; replace the empty catch on
window.api.getSettings() with a handler that logs the error (e.g., console.error
or processLogger) and does not call setSettings when savedSettings is falsy or
not an object; continue to call setSettings only when savedSettings is a valid
object (since getSettings already merges defaults). Also ensure the main-process
validateSettings function always returns an object (never undefined for missing
keys) so default merging in getSettings works correctly.

In `@apps/renderer/src/utils/helpers/heading-helper.ts`:
- Around line 16-21: The heading() renderer currently derives id from
stripHtml(text) which diverges from extractTOC()'s headingText() (which calls
parseInline(token.text)), and it also omits the duplicate-id suffix used by
extractTOC(), causing TOC mismatches; update heading() to derive plainText by
calling parseInline(text) (cast to string to maintain synchronous behavior like
headingText()), then compute id via getHeadingId(plainText), and implement the
same de-duplication/counter suffix logic used in extractTOC() so duplicate
headings produce matching, collision-safe ids; keep escapeHtml(stripHtml(...))
for inner text rendering if needed but ensure id generation follows parseInline
-> getHeadingId -> dedupe sequence.

In `@packages/shared-constants/src/menu-constants.ts`:
- Line 45: The OPEN_SETTINGS constant value breaks the channel naming
convention—update the value for OPEN_SETTINGS in menu-constants.ts (the
OPEN_SETTINGS export) from 'menu:open:settings' to the hyphenated convention
'menu:open-settings' so it matches other event names like SET_THEME and keeps
channel names consistent and greppable.

In `@packages/shared-constants/src/theme-constants.ts`:
- Around line 1-10: The Theme union in shared-types is hardcoded and must
instead be derived from the single source of truth in shared-constants to keep
runtime validation (THEMES) and compile-time types consistent; update
packages/shared-types/src/settings-type.ts to import and re-export the Theme
type (and/or import THEMES if needed) from the shared-constants package and add
shared-constants as a workspace dependency so the settings theme type uses the
exported Theme from shared-constants rather than its own hardcoded union,
ensuring apps validate against THEMES and type-check against the same symbol.

---

Outside diff comments:
In @.github/workflows/production.yml:
- Around line 14-15: Update both checkout steps that use actions/checkout@v4 to
disable credential persistence by adding persist-credentials: false to their
step configuration; locate the steps invoking actions/checkout@v4 and set
persist-credentials: false so the GITHUB_TOKEN is not written into the runner
git config.

In `@apps/main-processor/__tests__/file.test.ts`:
- Around line 42-84: Tests write ./test-file.md into repo root and never remove
it; change TEST_FILE to point into the existing temporary test_dir (e.g.
construct TEST_FILE with path.join(test_dir, 'test-file.md')) so each test uses
an isolated temp path, and add cleanup to remove the file after tests (use
unlink/unlinkSync in afterEach or afterAll). Update any references in these
tests that use TEST_FILE and ensure unWatchFile/watchFile behavior is unchanged.

In `@apps/main-processor/src/index.ts`:
- Around line 64-66: The calls to sendFilePathToRenderer in the did-finish-load
branch and the second-instance handler are fire-and-forget but
sendFilePathToRenderer is now async and can reject; update both invocations to
attach a .catch that logs the error (e.g.
processLogger.error("sendFilePathToRenderer failed", err)) so rejections are
handled consistently like the open-file handler does; locate
sendFilePathToRenderer usages in the did-finish-load handler and the
second-instance handler and append .catch(...) with clear context in the log
message.

In `@apps/renderer/src/hooks/useFileActions.ts`:
- Around line 34-40: openFileDialog currently calls window.api.openFileDialog()
without guarding against a missing preload API, causing a crash if window.api is
undefined; update the openFileDialog function to mirror the openFolder guard by
checking that window.api and window.api.openFileDialog exist before calling, and
return early if they don't (keeping the existing loadFileInTab flow unchanged
when a path is returned).

In `@electron-builder.ts`:
- Around line 20-29: The extraResources array currently contains two entries
that copy the same file: the directory entry { from: 'assets', to: 'assets' }
already includes assets/unsigned-install-notice.txt, so remove the duplicate
file-level entry ({ from: 'assets/unsigned-install-notice.txt', to:
'UNSIGNED_INSTALL_NOTICE.txt' }) unless you intentionally need the notice at the
root (UNSIGNED_INSTALL_NOTICE.txt); if root-level visibility is required keep
the file-level mapping, otherwise delete that specific mapping to avoid creating
duplicate artifacts.

---

Duplicate comments:
In `@apps/renderer/src/App.tsx`:
- Around line 147-155: The catch block in the onOpenFolderResult handler flips
the generic showToast flag (setShowToast(true)), which causes the existing
success toast (message="File updated") to appear on failure; instead, replace
that behavior so loadFileInTab's rejection triggers a dedicated error
toast/state (e.g., setErrorToast(true) or setToast({type: 'error', message:
'Failed to open file: <error>'})) rather than toggling the success showToast
flag; update the onOpenFolderResult catch to capture the error and call the new
error-toast setter (and/or set a toast payload) and update the toast render
logic to show an error message when that error state/payload is present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3bb2b33b-20f7-495c-b1bd-6a25187c8e96

📥 Commits

Reviewing files that changed from the base of the PR and between 06f2341 and 41c4480.

⛔ Files ignored due to path filters (2)
  • apps/renderer/public/icons/app-icon.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml
📒 Files selected for processing (63)
  • .github/workflows/development.yml
  • .github/workflows/production.yml
  • .gitignore
  • .husky/pre-push
  • .prettierignore
  • apps/main-processor/__tests__/export.test.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/__tests__/folder.test.ts
  • apps/main-processor/__tests__/recent.test.ts
  • apps/main-processor/__tests__/settings.test.ts
  • apps/main-processor/setup.ts
  • apps/main-processor/src/export/inlineImage.ts
  • apps/main-processor/src/file.ts
  • apps/main-processor/src/folder.ts
  • apps/main-processor/src/index.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/menu.ts
  • apps/main-processor/src/settings/get-settings.ts
  • apps/main-processor/src/settings/save-settings.ts
  • apps/main-processor/src/types/watch-file-types.ts
  • apps/main-processor/src/utils/constants/path-constants.ts
  • apps/main-processor/src/utils/constants/setting-constants.ts
  • apps/main-processor/src/utils/helper/menu-helper.ts
  • apps/main-processor/src/utils/helper/setting-helper.ts
  • apps/main-processor/vitest.config.ts
  • apps/preload/src/index.ts
  • apps/preload/src/utils/menu-event-helper.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/renderer/index.html
  • apps/renderer/src/App.tsx
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/components/SettingsPanel.tsx
  • apps/renderer/src/components/Sidebar.tsx
  • apps/renderer/src/config/marked.ts
  • apps/renderer/src/context/ThemeProvider.tsx
  • apps/renderer/src/hooks/useCollapsibleToc.ts
  • apps/renderer/src/hooks/useFile.ts
  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/hooks/useFilePersistence.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
  • apps/renderer/src/hooks/useSettings.ts
  • apps/renderer/src/renderer/toc.ts
  • apps/renderer/src/store/tabStore.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/utils/helpers/heading-helper.ts
  • apps/renderer/src/utils/helpers/sidebar-helper.ts
  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/renderer/src/utils/helpers/theme-helper.ts
  • assets/unsigned-install-notice.txt
  • electron-builder.ts
  • electron.vite.config.ts
  • package.json
  • packages/shared-constants/src/index.ts
  • packages/shared-constants/src/menu-constants.ts
  • packages/shared-constants/src/theme-constants.ts
  • packages/shared-types/package.json
  • packages/shared-types/src/index.ts
  • packages/shared-types/src/markdown-type.ts
  • packages/shared-types/src/settings-default.ts
  • packages/shared-types/src/settings-type.ts
💤 Files with no reviewable changes (1)
  • electron.vite.config.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use TypeScript as the primary language for the application

Files:

  • apps/main-processor/src/utils/constants/path-constants.ts
  • apps/main-processor/src/export/inlineImage.ts
  • apps/main-processor/src/settings/get-settings.ts
  • apps/renderer/src/utils/helpers/theme-helper.ts
  • apps/main-processor/src/utils/helper/menu-helper.ts
  • apps/main-processor/src/types/watch-file-types.ts
  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/main-processor/src/settings/save-settings.ts
  • apps/renderer/src/hooks/useFile.ts
  • apps/renderer/src/hooks/useFilePersistence.ts
  • packages/shared-types/src/settings-default.ts
  • apps/main-processor/vitest.config.ts
  • apps/preload/src/utils/menu-event-helper.ts
  • apps/renderer/src/hooks/useCollapsibleToc.ts
  • packages/shared-constants/src/theme-constants.ts
  • apps/main-processor/__tests__/export.test.ts
  • packages/shared-constants/src/menu-constants.ts
  • apps/main-processor/__tests__/folder.test.ts
  • apps/main-processor/src/file.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • electron-builder.ts
  • apps/renderer/src/utils/helpers/sidebar-helper.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/src/utils/constants/setting-constants.ts
  • apps/renderer/src/utils/helpers/heading-helper.ts
  • apps/main-processor/src/index.ts
  • apps/main-processor/src/menu.ts
  • apps/main-processor/__tests__/settings.test.ts
  • apps/renderer/src/renderer/toc.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/renderer/src/store/tabStore.ts
  • apps/renderer/src/config/marked.ts
  • apps/renderer/src/components/SettingsPanel.tsx
  • packages/shared-constants/src/index.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/setup.ts
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/components/Sidebar.tsx
  • packages/shared-types/src/settings-type.ts
  • apps/renderer/src/hooks/useFileActions.ts
  • packages/shared-types/src/index.ts
  • apps/main-processor/src/utils/helper/setting-helper.ts
  • apps/main-processor/__tests__/recent.test.ts
  • apps/preload/src/index.ts
  • apps/renderer/src/context/ThemeProvider.tsx
  • apps/renderer/src/hooks/useSettings.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/main-processor/src/folder.ts
  • packages/shared-types/src/markdown-type.ts
  • apps/renderer/src/App.tsx
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/main-processor/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Marked for parsing Markdown content

Files:

  • apps/main-processor/src/utils/constants/path-constants.ts
  • apps/main-processor/src/export/inlineImage.ts
  • apps/main-processor/src/settings/get-settings.ts
  • apps/main-processor/src/utils/helper/menu-helper.ts
  • apps/main-processor/src/types/watch-file-types.ts
  • apps/main-processor/src/settings/save-settings.ts
  • apps/main-processor/vitest.config.ts
  • apps/main-processor/__tests__/export.test.ts
  • apps/main-processor/__tests__/folder.test.ts
  • apps/main-processor/src/file.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/src/utils/constants/setting-constants.ts
  • apps/main-processor/src/index.ts
  • apps/main-processor/src/menu.ts
  • apps/main-processor/__tests__/settings.test.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/setup.ts
  • apps/main-processor/src/utils/helper/setting-helper.ts
  • apps/main-processor/__tests__/recent.test.ts
  • apps/main-processor/src/folder.ts
apps/main-processor/src/**/*.ts

⚙️ CodeRabbit configuration file

apps/main-processor/src/**/*.ts: Review as Electron main-process code.

  • IPC handlers must use shared constants and validate renderer input.
  • File/folder access must guard path traversal, missing files, permissions, symlinks, and deleted watched files.
  • Watchers, menus, dialogs, and IPC listeners must be cleaned up.
  • Do not expose Node/Electron internals or unrestricted filesystem access.
  • Export/update/download flows must sanitize content, close resources, and avoid executing embedded scripts.

Files:

  • apps/main-processor/src/utils/constants/path-constants.ts
  • apps/main-processor/src/export/inlineImage.ts
  • apps/main-processor/src/settings/get-settings.ts
  • apps/main-processor/src/utils/helper/menu-helper.ts
  • apps/main-processor/src/types/watch-file-types.ts
  • apps/main-processor/src/settings/save-settings.ts
  • apps/main-processor/src/file.ts
  • apps/main-processor/src/utils/constants/setting-constants.ts
  • apps/main-processor/src/index.ts
  • apps/main-processor/src/menu.ts
  • apps/main-processor/src/ipc.ts
  • apps/main-processor/src/utils/helper/setting-helper.ts
  • apps/main-processor/src/folder.ts
apps/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

apps/renderer/**/*.{ts,tsx}: Use React for frontend UI components
Use Shiki for syntax highlighting in code blocks
Use KaTeX for mathematical equation rendering
Use Mermaid for diagram rendering

Files:

  • apps/renderer/src/utils/helpers/theme-helper.ts
  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/renderer/src/hooks/useFile.ts
  • apps/renderer/src/hooks/useFilePersistence.ts
  • apps/renderer/src/hooks/useCollapsibleToc.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/renderer/src/utils/helpers/sidebar-helper.ts
  • apps/renderer/src/utils/helpers/heading-helper.ts
  • apps/renderer/src/renderer/toc.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/renderer/src/store/tabStore.ts
  • apps/renderer/src/config/marked.ts
  • apps/renderer/src/components/SettingsPanel.tsx
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/components/Sidebar.tsx
  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/context/ThemeProvider.tsx
  • apps/renderer/src/hooks/useSettings.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/renderer/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (README.md)

Use Tailwind CSS for styling

Files:

  • apps/renderer/src/utils/helpers/theme-helper.ts
  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/renderer/src/hooks/useFile.ts
  • apps/renderer/src/hooks/useFilePersistence.ts
  • apps/renderer/src/hooks/useCollapsibleToc.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/renderer/src/utils/helpers/sidebar-helper.ts
  • apps/renderer/src/utils/helpers/heading-helper.ts
  • apps/renderer/src/renderer/toc.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/renderer/src/store/tabStore.ts
  • apps/renderer/src/config/marked.ts
  • apps/renderer/src/components/SettingsPanel.tsx
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/components/Sidebar.tsx
  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/context/ThemeProvider.tsx
  • apps/renderer/src/hooks/useSettings.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/renderer/src/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{ts,tsx}: Review as React renderer code.

  • Keep components typed, accessible, keyboard-friendly, and resilient to missing preload APIs.
  • Effects must have correct dependencies and cleanup.
  • Handle loading, empty, error, stale-response, and rejected-promise states.
  • Do not import Node-only modules into renderer code.
  • Avoid unnecessary derived state, unsafe globals, and broad any types.

Files:

  • apps/renderer/src/utils/helpers/theme-helper.ts
  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/renderer/src/hooks/useFile.ts
  • apps/renderer/src/hooks/useFilePersistence.ts
  • apps/renderer/src/hooks/useCollapsibleToc.ts
  • apps/renderer/src/utils/helpers/sidebar-helper.ts
  • apps/renderer/src/utils/helpers/heading-helper.ts
  • apps/renderer/src/renderer/toc.ts
  • apps/renderer/src/store/tabStore.ts
  • apps/renderer/src/config/marked.ts
  • apps/renderer/src/components/SettingsPanel.tsx
  • apps/renderer/src/hooks/useFolderSearch.ts
  • apps/renderer/src/components/Sidebar.tsx
  • apps/renderer/src/hooks/useFileActions.ts
  • apps/renderer/src/context/ThemeProvider.tsx
  • apps/renderer/src/hooks/useSettings.ts
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
  • apps/renderer/src/types/hook-types.ts
  • apps/renderer/src/types/component-types.ts
  • apps/renderer/src/hooks/useMenuEvents.ts
apps/renderer/src/**/{renderer,markdown,utils}/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/{renderer,markdown,utils}/**/*.{ts,tsx}: Review markdown rendering carefully.

  • Sanitize raw HTML, links, images, Mermaid, KaTeX, anchors, and exported content.
  • Block script execution, javascript: URLs, unsafe inline handlers, and unsafe local file references.
  • Heading IDs and TOC entries must be stable and collision-safe.
  • Mermaid/KaTeX/code highlighting failures should not break the whole document.
  • Add tests for unsafe HTML, malformed markdown, links, images, code blocks, Mermaid, and KaTeX when changed.

Files:

  • apps/renderer/src/utils/helpers/theme-helper.ts
  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/renderer/src/utils/helpers/sidebar-helper.ts
  • apps/renderer/src/utils/helpers/heading-helper.ts
  • apps/renderer/src/renderer/toc.ts
apps/renderer/src/**/*.{css,tsx}

⚙️ CodeRabbit configuration file

apps/renderer/src/**/*.{css,tsx}: Review UI, theme, and accessibility.

  • Interactive controls need semantic elements, visible focus, and keyboard access.
  • Theme changes must preserve readable contrast in light and dark modes.
  • Markdown prose must remain readable for tables, code, blockquotes, links, lists, and images.
  • Prefer existing tokens/classes over ad hoc inline styling.

Files:

  • apps/renderer/src/utils/helpers/tab-helper.tsx
  • apps/renderer/src/components/SettingsPanel.tsx
  • apps/renderer/src/components/Sidebar.tsx
  • apps/renderer/src/context/ThemeProvider.tsx
  • apps/renderer/src/components/SearchBar.tsx
  • apps/renderer/src/App.tsx
**/package.json

⚙️ CodeRabbit configuration file

**/package.json: Review scripts and dependencies.

  • Scripts for lint, typecheck, test, coverage, build, and dist must fail on errors.
  • Dependencies should live in the package that imports them.
  • Runtime imports must not be placed only in devDependencies.
  • Electron, Vite, React, TypeScript, Tailwind, and testing upgrades need compatibility attention.

Files:

  • packages/shared-types/package.json
  • package.json
apps/preload/src/**/*.ts

⚙️ CodeRabbit configuration file

apps/preload/src/**/*.ts: Review as a strict preload boundary.

  • Expose only typed contextBridge APIs, never raw ipcRenderer.
  • Use shared IPC constants and shared payload/result types.
  • Listener methods must return unsubscribe functions.
  • Reject broad channel names, arbitrary invoke/send wrappers, and any-typed payloads.

Files:

  • apps/preload/src/utils/menu-event-helper.ts
  • apps/preload/src/index.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (README.md)

Use Vitest for testing

Files:

  • apps/main-processor/__tests__/export.test.ts
  • apps/main-processor/__tests__/folder.test.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/__tests__/settings.test.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/main-processor/__tests__/recent.test.ts

⚙️ CodeRabbit configuration file

**/*.{test,spec}.{ts,tsx}: Review tests.

  • Cover success and failure paths, especially IPC, filesystem, markdown rendering, search, settings, tabs, and exports.
  • Use isolated temp directories for disk tests and clean them up.
  • Mock Electron/preload APIs explicitly.
  • Prefer Testing Library user-event and getByRole for UI tests.

Files:

  • apps/main-processor/__tests__/export.test.ts
  • apps/main-processor/__tests__/folder.test.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/__tests__/settings.test.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/main-processor/__tests__/recent.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Write unit tests for all new features to maintain code quality, adding test cases alongside component source code and ensuring all tests pass via pnpm vitest

Files:

  • apps/main-processor/__tests__/export.test.ts
  • apps/main-processor/__tests__/folder.test.ts
  • apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts
  • apps/main-processor/__tests__/file.test.ts
  • apps/main-processor/__tests__/settings.test.ts
  • apps/renderer/__tests__/renderer/toc.test.ts
  • apps/main-processor/__tests__/recent.test.ts
electron-builder.ts

⚙️ CodeRabbit configuration file

electron-builder.ts: Review packaging.

  • Check appId, productName, files, asar, icons, targets, file associations, artifact names, and publish settings.
  • Exclude source-only, test, coverage, cache, and map files from releases.
  • Signing/notarisation/update config must not hardcode secrets.
  • Platform targets should match expected Windows, macOS, and Linux release formats.

Files:

  • electron-builder.ts
{package.json,package-lock.json,yarn.lock,pnpm-lock.yaml,.github/dependabot.yml}

📄 CodeRabbit inference engine (SECURITY.md)

Keep dependencies updated and monitor GitHub Dependabot alerts for security vulnerabilities

Files:

  • package.json
.github/workflows/**/*.yml

⚙️ CodeRabbit configuration file

.github/workflows/**/*.yml: Review CI/CD.

  • Actions should use version tags, not @main.
  • Secrets must use ${{ secrets.* }} and never be hardcoded.
  • CI should install with pinned pnpm, then run lint, typecheck, tests, build, and package checks.
  • Security scans should fail for high/critical findings unless justified.

Files:

  • .github/workflows/development.yml
  • .github/workflows/production.yml
🪛 zizmor (1.25.2)
.github/workflows/development.yml

[error] 23-23: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[warning] 47-48: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 48-48: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 51-51: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 54-54: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 66-66: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

.github/workflows/production.yml

[error] 23-23: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[warning] 49-50: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 50-50: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 53-53: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 56-56: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 68-68: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🔇 Additional comments (55)
.husky/pre-push (1)

1-1: LGTM!

.prettierignore (1)

4-9: LGTM!

apps/preload/src/utils/menu-event-helper.ts (1)

1-7: LGTM!

apps/renderer/src/types/hook-types.ts (1)

28-64: LGTM!

apps/renderer/src/hooks/useMenuEvents.ts (1)

29-46: LGTM!

apps/main-processor/src/ipc.ts (2)

198-212: ⚡ Quick win

Authorization check is defeated by the preceding add.

Line 204 adds safeFolderPath to allowedFolderRoots before the isAllowed check, so the check on Lines 205–210 always passes — any renderer-supplied folder is authorized. This is the same broad-filesystem-access risk raised previously; the guard only works if you stop auto-inserting the requested path. Let only explicit folder-open flows (OPEN_FOLDER_DIALOG/READ_FOLDER) mutate allowedFolderRoots.

Note: the prefix check startsWith(\${root}/`)assumes/separators; confirmresolveDirectoryPath` normalizes Windows paths so subfolder matching holds there too.

🔒 Proposed fix
     const safeFolderPath = await resolveDirectoryPath(folderPath);
-    allowedFolderRoots.add(safeFolderPath);
     const isAllowed = Array.from(allowedFolderRoots).some(
       (root) => safeFolderPath === root || safeFolderPath.startsWith(`${root}/`)
     );
     if (!isAllowed) {
       throw new Error('Folder path is not authorized');
     }
     return await searchFolder(safeFolderPath, query);
As per coding guidelines: "Do not expose Node/Electron internals or unrestricted filesystem access."

120-139: LGTM!

apps/main-processor/src/utils/constants/path-constants.ts (1)

14-14: LGTM!

apps/main-processor/src/settings/get-settings.ts (1)

7-19: LGTM!

apps/main-processor/src/types/watch-file-types.ts (1)

1-5: LGTM!

apps/main-processor/src/file.ts (1)

18-83: LGTM!

apps/main-processor/src/folder.ts (1)

7-58: LGTM!

apps/main-processor/src/export/inlineImage.ts (1)

12-14: ⚡ Quick win

Confirm match[2] is the correct capture group for image src
EXPORT_CONST.EXPORT_IMAGE_SRC_REGEX is /src=(["'])(?!https?:\/\/|data:|file:\/\/)(.*?)\1/gi, where match[1] is the quote and match[2] is the (.*?) image src. So apps/main-processor/src/export/inlineImage.ts using const src = match[2]; is correct and won’t make src become undefined.

assets/unsigned-install-notice.txt (1)

1-23: LGTM!

apps/renderer/src/utils/helpers/tab-helper.tsx (1)

4-13: LGTM!

electron-builder.ts (1)

60-67: ⚡ Quick win

license is set on nsis, not linux.

The summary states linux.license was set, but the change applies the notice as the NSIS installer license page (Windows). If the intent was a Linux license/notice, this is in the wrong block; if Windows was intended, the summary is misleading. Confirm placement.

apps/renderer/src/utils/helpers/sidebar-helper.ts (1)

4-20: LGTM!

apps/renderer/src/store/tabStore.ts (1)

11-11: LGTM!

apps/renderer/src/components/SettingsPanel.tsx (1)

32-101: LGTM!

apps/renderer/src/types/component-types.ts (2)

113-184: LGTM!


70-86: ⚡ Quick win

query being optional is already handled in SearchBar.

SearchBar derives activeQuery as (isFolderMode ? folderQuery : query) ?? '', so the input is always driven by a string (localQuery). The match-count/status UI is also gated behind query && !isFolderMode, with no direct query.length-style dereferences.

apps/renderer/src/utils/helpers/theme-helper.ts (1)

4-6: ⚡ Quick win

Confirm getSystemTheme() returns valid Theme values.

Theme is defined as (typeof APPTHEMES)[number], and APPTHEMES includes both 'github-light' and 'github-dark', so the returned literals match the Theme union.

apps/main-processor/src/utils/helper/menu-helper.ts (1)

3-9: LGTM!

apps/main-processor/src/utils/constants/setting-constants.ts (1)

3-14: LGTM!

apps/main-processor/src/menu.ts (1)

63-98: LGTM!

apps/main-processor/__tests__/settings.test.ts (1)

5-60: LGTM!

apps/main-processor/src/utils/helper/setting-helper.ts (1)

14-112: LGTM!

apps/preload/src/index.ts (1)

19-32: ⚡ Quick win

Preload contract matches the shared MarkdownReaderAPI.

apps/preload/src/index.ts types apiContract as MarkdownReaderAPI, and the implementation aligns with packages/shared-types/src/markdown-type.ts: searchFolder(path, query) matches the shared (string, string) => Promise<FolderSearchResult[]>, and onMenuEvent(...) matches (event: string, callback: (payload?: unknown) => void) => () => void (with unsubscribe removing the same handler). The channel is still constrained via isMenuEvent.

apps/main-processor/src/settings/save-settings.ts (1)

12-22: 🏗️ Heavy lift

Deadlock concern is invalid: getSettings() does not re-enter the mutex

  • getSettings() (apps/main-processor/src/settings/get-settings.ts) reads settings.json directly via node:fs/promises.readFile and never calls runExclusive.

  • runExclusive is only defined in apps/main-processor/src/utils/helper/setting-helper.ts and only used in apps/main-processor/src/settings/save-settings.ts, so the proposed reentrant deadlock path cannot occur (yet).

      		> Likely an incorrect or invalid review comment.
    
apps/main-processor/__tests__/recent.test.ts (1)

1-5: ⚡ Quick win

Keep the inline electron mock removed—Electron is already mocked globally, and recent helpers still import it indirectly.

  • apps/main-processor/setup.ts already does vi.mock('electron', { app: { isPackaged: false, getPath: vi.fn() }}).

  • getUniqueRecentFile imports MAX_RECENT from apps/main-processor/src/utils/constants/path-constants.ts, and that module imports app from electron (even though the test only uses MAX_RECENT).

  • So the tests rely on the global mock (not on recent code avoiding Electron). Expand the mock if future recent code starts using additional Electron exports beyond app.

      		> Likely an incorrect or invalid review comment.
    
apps/renderer/index.html (1)

6-7: LGTM!

apps/renderer/src/hooks/useFile.ts (1)

32-39: Good call sourcing the TOC from raw markdown.

Parsing the original markdown instead of the sanitized HTML is the right move — DOMPurify can drop/rewrite nodes and break heading text. Just make sure the anchor IDs extractTOC emits stay byte-for-byte identical to the ones produced by the heading() renderer, otherwise TOC clicks will scroll to nowhere. See the related note in heading-helper.ts.

.gitignore (1)

1-40: LGTM!

package.json (1)

69-75: LGTM!

packages/shared-types/package.json (1)

11-11: ⚡ Quick win

Ensure @package/shared-types build output matches "type": "module"
packages/shared-types/package.json sets "type": "module", but packages/shared-types/tsconfig.json doesn’t override compilerOptions.module (it only extends: ../../tsconfig.base.json), and the actual base module/moduleResolution settings weren’t checked—so we can’t rule out CJS emission and downstream breakage under Node. Repo usage is ESM import-based (no require('@package/shared-types') found), but external consumers could still hit ERR_REQUIRE_ESM/export mismatches. Confirm tsconfig.base.json module settings and that dist/src/index.js is emitted as ESM (otherwise align tsconfig/package type).

apps/renderer/src/hooks/useFilePersistence.ts (1)

25-32: LGTM!

apps/renderer/src/hooks/useCollapsibleToc.ts (1)

18-30: LGTM!

Also applies to: 47-48, 62-68

apps/renderer/src/hooks/useFolderSearch.ts (1)

19-40: Stale/early-return handling now correct. Token advanced before bailout, bridge guarded, rejections caught and gated.

apps/renderer/src/components/Sidebar.tsx (1)

10-13: LGTM!

Also applies to: 39-65

apps/renderer/src/components/SearchBar.tsx (1)

22-32: LGTM!

Also applies to: 121-140

apps/renderer/src/App.tsx (1)

134-146: LGTM!

packages/shared-types/src/settings-type.ts (2)

3-3: This Theme union duplicates the one derived from THEMES in shared-constants; tracked at the root in theme-constants.ts.


4-13: LGTM!

packages/shared-types/src/settings-default.ts (1)

3-12: LGTM!

apps/main-processor/vitest.config.ts (1)

9-9: LGTM!

packages/shared-constants/src/index.ts (1)

6-15: LGTM!

packages/shared-types/src/index.ts (1)

1-8: LGTM!

apps/renderer/src/hooks/useSettings.ts (2)

32-41: LGTM!


43-59: LGTM!

packages/shared-types/src/markdown-type.ts (1)

17-23: LGTM!

apps/main-processor/__tests__/export.test.ts (1)

58-70: LGTM!

apps/renderer/__tests__/hooks/useCollapsibleToc.test.ts (1)

23-35: LGTM!

apps/renderer/__tests__/renderer/toc.test.ts (1)

51-65: LGTM!

apps/main-processor/setup.ts (1)

1-8: LGTM!

.github/workflows/development.yml (1)

43-72: 💤 Low value

Solid security job; one note on ordering.

pnpm audit --audit-level=high and Trivy with exit-code: 1 on CRITICAL,HIGH correctly fail the pipeline — good. Note the job has no needs: build, so it runs in parallel with build rather than after it (the PR summary says "after the build"). That's fine if intentional; add needs: build only if you want gating.

Comment thread .github/workflows/development.yml
Comment thread .github/workflows/production.yml
Comment thread apps/main-processor/__tests__/folder.test.ts
Comment thread apps/renderer/src/components/SettingsPanel.tsx
Comment thread apps/renderer/src/config/marked.ts
Comment thread apps/renderer/src/hooks/useSettings.ts
Comment thread apps/renderer/src/hooks/useSettings.ts
Comment thread apps/renderer/src/utils/helpers/heading-helper.ts
Comment thread packages/shared-constants/src/menu-constants.ts Outdated
Comment thread packages/shared-constants/src/theme-constants.ts
@mind-murtaza mind-murtaza merged commit bafd9ab into dev Jun 1, 2026
3 checks passed
This was referenced Jun 1, 2026
Closed
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants