Skip to content

feat: run notes import in worker#292

Merged
vitonsky merged 29 commits into
masterfrom
125-run-notes-import-in-worker
May 15, 2026
Merged

feat: run notes import in worker#292
vitonsky merged 29 commits into
masterfrom
125-run-notes-import-in-worker

Conversation

@vitonsky

@vitonsky vitonsky commented May 14, 2026

Copy link
Copy Markdown
Member

Closes #125

Key changes

  • Introduced transactions for DB. The transaction allow to run queries to DB exclusively until complete. Other queries and transactions will be enqueued
  • Refactored dependencies of controllers, now they require only minimal database object, not a managed database
  • Notes import runs in a background worker for faster, more responsive imports
  • Implemented controller methods for batch note updates and snapshotting for more efficient bulk edits
  • Improved UI & added button to cancel import

TODO

  • Describe changes
  • Add button to cancel import
  • Check that export dos not use uncompleted notes
  • Update locales

Summary by CodeRabbit

  • New Features

    • Database transactions for safer concurrent ops and improved reliability
    • Notes import runs in a background worker with richer progress/cancel UI
    • Batch note updates and snapshotting for more efficient bulk edits
    • Better import handling for tags and attachments to preserve links
  • Chores

    • Updated build tooling (webpack) and added async synchronization dependency

Review Change Stack

@vitonsky vitonsky linked an issue May 14, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds mutex-backed SQLiteTransaction support and transaction-aware query blocking; refactors controllers to accept raw SQLiteDB and use wrapSQLite(this.db); introduces a Comlink-based Notes importer worker with deep-object transfer handling and batch import processing; updates tests and vault hooks to use managedDb.get().

Changes

Database Transaction System and Controller Wiring

Layer / File(s) Summary
SQLiteDatabase transaction implementation
packages/app/package.json, packages/app/src/core/database/sqlite/SQLiteDatabase.ts, packages/app/src/core/database/sqlite/index.ts
Adds async-mutex dependency; introduces transactionMutex, transaction() API (callback and returned-transaction forms), and exports SQLiteTransaction; query() waits for active transactions.
SQLiteDatabase worker transaction proxying
packages/app/src/core/database/sqlite/SQLiteDatabase.worker.ts, packages/app/src/core/database/sqlite/SQLiteDatabaseWorker.ts
Worker APIs proxy transaction through Comlink, supporting callback and returned-transaction forms with captured callback error semantics.
Transaction behavior tests
packages/app/src/core/database/sqlite/SQLiteDatabase.test.ts
Adds manual and auto transaction tests covering sequential queries, concurrency/waiting, callback return values, and error handling.
Database wrapper type widening
packages/app/src/core/database/sqlite/utils/wrapDB.ts
wrapSQLite now accepts `SQLiteDB
Controllers refactored to raw SQLiteDB
packages/app/src/core/features/{attachments,files,notes,tags,workspaces,history}/...
Constructors changed to accept SQLiteDB; methods now call wrapSQLite(this.db); transactional write paths use this.db.transaction(...) where applicable.
Batch operation APIs
packages/app/src/core/features/notes/controller/index.ts, packages/app/src/core/features/notes/controller/NotesController.ts, packages/app/src/core/features/notes/history/NoteVersions.ts
Adds NoteContentUpdateInfo and INotesController.updateBatch; NotesController.update delegates to updateBatch; NoteVersions.batchSnapshot added for multi-note snapshots.
Test updates for raw SQLiteDB usage
packages/app/src/__tests__/*, various feature tests
Tests and utilities updated to derive workspace IDs and controllers from managedDb.get() and to close managedDB where appropriate.
useVaultDB hook and vault refactoring
packages/app/src/features/App/Vault/index.tsx, various vault components/hooks
Adds useVaultDB(managed?: boolean) overloads and updates components/services to source DB via this hook instead of useVaultControls().

Notes Importer Worker System and Deep Serialization

Layer / File(s) Summary
Comlink deep-object serialization infrastructure
packages/app/src/core/storage/interop/import/deepSerialize.ts, words.txt
New deep serialization for Comlink: recursive serialize/deserialize, special AbortSignal transfer via MessagePort, DeepObject<T> marker, deepObject() helper, and registerDeepObjectTransferHandler(); adds transferables token.
NotesImporterWorker class and worker module
packages/app/src/core/storage/interop/import/NotesImporterWorker.ts, packages/app/src/core/storage/interop/import/NotesImporter.worker.ts
Introduces NotesImporterWorker that spawns a module worker, registers deep-object handlers, passes registries via deepObject(), transfers a ComlinkHostFS, invokes worker import, and guarantees termination. Worker module exposes import(deps, files, { config, options }?).
NotesImporter refactoring with batching and deferred tags
packages/app/src/core/storage/interop/import/index.ts
Refactors importer to accept NotesImporterDeps and NotesImporterConfig; separates execution NotesImportOptions (batchSize, abortSignal, onProcessed); parses YAML frontmatter from AST yaml nodes; defers tag attachment and resolves tag IDs via tagsRegistry.add(..., { returnIfExist: true }); replaces per-note updates with batch-driven notesRegistry.updateBatch and noteVersions.batchSnapshot.
useNotesImport hook and test updates
packages/app/src/hooks/notes/useNotesImport.tsx, packages/app/src/core/storage/interop/import/NotesImporter.test.ts
Hook switched to NotesImporterWorker with NotesImporterConfig; removed per-file throttling option; tests updated for batch behavior, tag resolution assertions, batchSize enforcement, and onProcessed lifecycle.

Sequence Diagram (Notes import flow)

sequenceDiagram
  participant UI
  participant NotesImporterWorker
  participant WorkerModule
  participant NotesImporter
  participant TagsRegistry
  participant NotesRegistry
  UI->>NotesImporterWorker: import(files, {config, options})
  NotesImporterWorker->>WorkerModule: spawn & wrap via Comlink
  NotesImporterWorker->>WorkerModule: api.import(deps, ComlinkHostFS(files), {config, options})
  WorkerModule->>NotesImporter: run import logic (parse, batch, upload)
  NotesImporter->>TagsRegistry: add tags (returnIfExist)
  NotesImporter->>NotesRegistry: updateBatch(notes)
  NotesImporter-->>WorkerModule: finish
  WorkerModule-->>NotesImporterWorker: resolve
  NotesImporterWorker-->>UI: resolve
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

"🐰 I tunneled through mutexed night,
I carried deep objects in Comlink flight,
I batched the notes and kept them tight,
The worker hums, the tests delight,
A carrot cheer for async might."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: run notes import in worker' accurately describes the main change—moving notes import logic to a Web Worker for improved performance.
Linked Issues check ✅ Passed The PR successfully implements all key coding requirements from issue #125: database transactions for query exclusivity, controller dependency refactor to use raw SQLiteDB instead of ManagedDatabase, notes import moved to Web Worker with Comlink proxying, batch update/snapshot methods added, and ArrayBuffer transfer patterns via deepSerialize handler.
Out of Scope Changes check ✅ Passed All changes are directly related to the PR objectives: database transactions, controller refactoring, worker-based import, batch operations, and supporting UI/configuration updates. No unrelated modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 125-run-notes-import-in-worker

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Caution

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

⚠️ Outside diff range comments (1)
packages/app/src/core/features/notes/history/NoteVersions.ts (1)

97-107: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Compare against the latest version per note, not one global latest row.

The last subquery uses ORDER BY ... LIMIT 1 without partitioning by note_id, so it returns only one row globally. For batch input, unchanged notes can be treated as missing and re-snapshotted repeatedly.

💡 Suggested fix
-      LEFT JOIN (
-        SELECT v.title, v.text, v.note_id
-        FROM note_versions v
-        -- Order by monotonic "rowid" in case of timestamp collisions
-        ORDER BY v.created_at DESC, rowid DESC
-        LIMIT 1
-      ) last ON last.note_id = n.id
+      LEFT JOIN (
+        SELECT note_id, title, text
+        FROM (
+          SELECT
+            v.note_id,
+            v.title,
+            v.text,
+            ROW_NUMBER() OVER (
+              PARTITION BY v.note_id
+              ORDER BY v.created_at DESC, v.rowid DESC
+            ) AS rn
+          FROM note_versions v
+          WHERE v.note_id IN (${qb.values(noteIds)})
+        ) ranked
+        WHERE rn = 1
+      ) last ON last.note_id = n.id
🤖 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 `@packages/app/src/core/features/notes/history/NoteVersions.ts` around lines 97
- 107, The subquery aliased as last in NoteVersions.ts selects a single global
latest row from note_versions (ORDER BY ... LIMIT 1) instead of the latest row
per note_id; change it to select the latest version for each note_id (e.g. use a
window function like ROW_NUMBER() OVER (PARTITION BY v.note_id ORDER BY
v.created_at DESC, rowid DESC) and then filter for row_number = 1, or use
DISTINCT ON (v.note_id) if your DB supports it) so that the JOIN compares each
note against its own latest version when using qb.values(noteIds) and workspace
filtering.
🧹 Nitpick comments (3)
packages/app/src/core/storage/interop/import/index.ts (3)

287-350: 💤 Low value

Consider abortion responsiveness within batches.

The abortion check (Line 288) is only performed at the start of each batch. If abortSignal is triggered while processing a batch (e.g., after 10 of 100 notes), the remaining notes in that batch will still be processed. This delays the abortion response and may result in unnecessary work, especially with large batch sizes.

For improved responsiveness, consider checking abortSignal within the per-note processing loop or reducing the default batchSize for more frequent checks.

🤖 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 `@packages/app/src/core/storage/interop/import/index.ts` around lines 287 -
350, The batch loop only calls checkForAbortion() once per batch; to make
abortion responsive, call checkForAbortion() inside each per-note handler in the
notesSlice.map (e.g., at the start of the Promise.resolve().then(...) body and
again before heavy ops like replaceUrls, before pushing into updates, or before
attachmentsRegistry.set) so the handler throws immediately when aborted; this
ensures Promise.all short-circuits with an exception and the batch stops
processing further notes. Use the existing checkForAbortion() helper (or
abortSignal.aborted) within the per-note async function in the notesSlice.map
and throw to propagate cancellation up to the outer loop.

226-245: ⚡ Quick win

Consider skipping tag attachment when no tags are present.

Currently, even when tagNamesToAttach is empty, the code still calls getTagIds([]) and pushes an entry with an empty tags array to tagsToAttach. According to the commit summary, one optimization was to "skip tag attachment when none provided."

♻️ Proposed optimization
 		const tagIds = await this.getTagIds(tagNamesToAttach);
-		tagsToAttach.push({
-			noteId,
-			tags: tagIds,
-		});
+		if (tagIds.length > 0) {
+			tagsToAttach.push({
+				noteId,
+				tags: tagIds,
+			});
+		}
🤖 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 `@packages/app/src/core/storage/interop/import/index.ts` around lines 226 -
245, The code currently calls getTagIds(tagNamesToAttach) and pushes a
tagsToAttach entry even when tagNamesToAttach is empty; update the block around
convertPathToTag/tagNamesToAttach so that you only call this.getTagIds(...) and
push into tagsToAttach when tagNamesToAttach.length > 0 (use the existing
variables tagNamesToAttach, noteDirPath, convertPathToTag and noteId to
determine and guard this), thereby skipping both the unnecessary getTagIds([])
call and the empty entry in tagsToAttach when no tags are present.

164-177: ⚡ Quick win

Guard against YAML node remaining in output if parent/index are missing.

If parent or index are undefined, the YAML node won't be removed from the AST but noteMeta will still be parsed. This would result in the YAML frontmatter appearing in the final note content. While unist-util-visit should always provide parent/index when traversing with remark-parse, a defensive check could prevent unexpected output.

🛡️ Proposed defensive handling
 			visit(mdTree, (node, index, parent) => {
 				if (node.type === 'yaml') {
 					const rawMeta = parseYaml(node.value);
 					noteMeta = RawNoteMetaScheme.parse(rawMeta);
 
 					// Remove header node with meta data
 					if (parent && index !== undefined) {
 						parent.children.splice(index, 1);
+					} else {
+						console.warn('YAML node missing parent or index - frontmatter not removed from content');
 					}
 					return EXIT;
 				}
🤖 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 `@packages/app/src/core/storage/interop/import/index.ts` around lines 164 -
177, The YAML frontmatter visit block can leave the YAML node in the AST if
parent or index are undefined; update the visit callback (the visit/mdTree
handler that calls parseYaml and RawNoteMetaScheme.parse) to defensively remove
or neutralize the YAML node when parent/index are missing: if parent && index
are present keep the existing parent.children.splice(index,1) path, otherwise
clear the node by replacing its value/type (e.g. set node.value = '' and
node.type = 'text' or similar neutralization) so the frontmatter won't appear in
output; optionally emit a debug/warn (processLogger or console) indicating a
malformed AST when parent/index are absent.
🤖 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 `@packages/app/src/core/database/sqlite/SQLiteDatabase.ts`:
- Around line 106-108: The transaction overload in SQLiteDatabase.ts wrongly
declares a return type of Promise<void>; change the signature of transaction<T
extends unknown>(cb: (tx: SQLiteTransaction) => Promise<T>): Promise<T> so it
returns the callback result type T (matching the interface in index.ts and tests
in SQLiteDatabase.test.ts); update the declaration for the overload in the
SQLiteDatabase class (the transaction method) to return Promise<T> so the
implementation that returns cb(...) .finally(transaction.close) correctly
preserves and types the callback's return value.

In `@packages/app/src/core/database/sqlite/SQLiteDatabaseWorker.ts`:
- Around line 35-37: The transaction overload signature currently declares
Promise<void> but should propagate the callback's generic return type; update
the declaration of transaction<T extends unknown>(cb: (tx: SQLiteTransaction) =>
Promise<T>): Promise<void> to return Promise<T> instead so the overload matches
the interface in index.ts and preserves the callback's return value (ensure you
change the return type for the transaction generic overload in
SQLiteDatabaseWorker to Promise<T>).

In
`@packages/app/src/core/features/notes/controller/__tests__/NotesController.bench.ts`:
- Around line 32-33: The benchmark opens a SQLite handle with openSQLite(...)
returning managedDB but never closes it; update the benchmark teardown (the
afterEach/afterAll or the teardown function used in these tests) to call
managedDB.close() (or the appropriate close/destroy method on the managedDB
object) after the run so resources are released; locate where managedDB is
created (the lines with openSQLite and managedDB.get()) and add a teardown hook
to invoke managedDB.close() and handle any errors.

In `@packages/app/src/core/features/notes/controller/index.ts`:
- Around line 111-114: The JSDoc above updateBatch is misleadingly describing a
single-note update; update the comment for the updateBatch(notes:
NoteContentUpdateInfo[]): Promise<void> method to state that it updates multiple
notes in a single operation (e.g., "Update multiple notes by their unique ids"
or similar), mention that it accepts an array of NoteContentUpdateInfo and
returns a Promise<void>, and remove any singular "note" wording copied from
update().

In `@packages/app/src/core/features/notes/history/NoteVersions.ts`:
- Around line 58-69: batchSnapshot currently builds SQL with qb.values(noteIds)
even when noteIds is an empty array which produces invalid IN (...) clauses; add
an early guard at the start of NoteVersions.batchSnapshot to check if
noteIds.length === 0 and return an appropriate empty result (or
Promise.resolve([]) / no-op) instead of proceeding, and apply the same guard
wherever qb.values(noteIds) is used in this file (the other query-building sites
around the same logic) to avoid constructing IN () queries; ensure callers
relying on the return shape still receive the expected empty structure.

In `@packages/app/src/core/features/tags/controller/TagsController.ts`:
- Around line 333-372: The method setAttachedTagsInTransaction must await the
transaction and stop manually issuing BEGIN/COMMIT/ROLLBACK inside the
transaction callback: change the call to await this.db.transaction(...) so the
function waits for completion and any errors propagate to the caller, and remove
the explicit db.query(qb.sql`BEGIN`), db.query(qb.sql`COMMIT`) and
db.query(qb.sql`ROLLBACK`) calls inside the transaction callback (leave only the
DELETE, INSERT logic using wrapSQLite(tx) and qb.* helpers). After the awaited
transaction completes, call this.onChanged('noteTags'); do not catch and rethrow
transaction errors inside the callback — let the transaction helper handle
rollback and error propagation.

In `@packages/app/src/core/storage/interop/import/index.ts`:
- Around line 300-346: The batch import must run atomically: wrap the parallel
URL-replace/attachments work (the Promise.all over notesSlice that calls
replaceUrls and attachmentsRegistry.set), the subsequent
notesRegistry.updateBatch(updates), and noteVersions.batchSnapshot(noteIds)
inside one database transaction so all three operations commit or all are rolled
back; replicate the approach used by setAttachedTagsInTransaction (or the
project’s transaction helper) to obtain a transactional context, perform the
Promise.all and then call attachmentsRegistry.set, notesRegistry.updateBatch,
and noteVersions.batchSnapshot within that same transaction, and ensure any
thrown error causes a rollback rather than leaving attachments persisted while
note content/versions fail.

---

Outside diff comments:
In `@packages/app/src/core/features/notes/history/NoteVersions.ts`:
- Around line 97-107: The subquery aliased as last in NoteVersions.ts selects a
single global latest row from note_versions (ORDER BY ... LIMIT 1) instead of
the latest row per note_id; change it to select the latest version for each
note_id (e.g. use a window function like ROW_NUMBER() OVER (PARTITION BY
v.note_id ORDER BY v.created_at DESC, rowid DESC) and then filter for row_number
= 1, or use DISTINCT ON (v.note_id) if your DB supports it) so that the JOIN
compares each note against its own latest version when using qb.values(noteIds)
and workspace filtering.

---

Nitpick comments:
In `@packages/app/src/core/storage/interop/import/index.ts`:
- Around line 287-350: The batch loop only calls checkForAbortion() once per
batch; to make abortion responsive, call checkForAbortion() inside each per-note
handler in the notesSlice.map (e.g., at the start of the
Promise.resolve().then(...) body and again before heavy ops like replaceUrls,
before pushing into updates, or before attachmentsRegistry.set) so the handler
throws immediately when aborted; this ensures Promise.all short-circuits with an
exception and the batch stops processing further notes. Use the existing
checkForAbortion() helper (or abortSignal.aborted) within the per-note async
function in the notesSlice.map and throw to propagate cancellation up to the
outer loop.
- Around line 226-245: The code currently calls getTagIds(tagNamesToAttach) and
pushes a tagsToAttach entry even when tagNamesToAttach is empty; update the
block around convertPathToTag/tagNamesToAttach so that you only call
this.getTagIds(...) and push into tagsToAttach when tagNamesToAttach.length > 0
(use the existing variables tagNamesToAttach, noteDirPath, convertPathToTag and
noteId to determine and guard this), thereby skipping both the unnecessary
getTagIds([]) call and the empty entry in tagsToAttach when no tags are present.
- Around line 164-177: The YAML frontmatter visit block can leave the YAML node
in the AST if parent or index are undefined; update the visit callback (the
visit/mdTree handler that calls parseYaml and RawNoteMetaScheme.parse) to
defensively remove or neutralize the YAML node when parent/index are missing: if
parent && index are present keep the existing parent.children.splice(index,1)
path, otherwise clear the node by replacing its value/type (e.g. set node.value
= '' and node.type = 'text' or similar neutralization) so the frontmatter won't
appear in output; optionally emit a debug/warn (processLogger or console)
indicating a malformed AST when parent/index are absent.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 596532c9-ce3f-480e-b3f0-4fcda359795f

📥 Commits

Reviewing files that changed from the base of the PR and between d267f34 and b4b8986.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/app/src/core/storage/interop/import/__snapshots__/NotesImporter.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (38)
  • packages/app/package.json
  • packages/app/scripts/webpack/app.ts
  • packages/app/src/__tests__/utils/vaultContext.ts
  • packages/app/src/core/database/sqlite/SQLiteDatabase.test.ts
  • packages/app/src/core/database/sqlite/SQLiteDatabase.ts
  • packages/app/src/core/database/sqlite/SQLiteDatabase.worker.ts
  • packages/app/src/core/database/sqlite/SQLiteDatabaseWorker.ts
  • packages/app/src/core/database/sqlite/index.ts
  • packages/app/src/core/database/sqlite/utils/wrapDB.ts
  • packages/app/src/core/features/attachments/AttachmentsController.ts
  • packages/app/src/core/features/files/FilesController.ts
  • packages/app/src/core/features/notes/bin/DeletedNotesController.test.ts
  • packages/app/src/core/features/notes/controller/NotesController.filters.test.ts
  • packages/app/src/core/features/notes/controller/NotesController.test.ts
  • packages/app/src/core/features/notes/controller/NotesController.ts
  • packages/app/src/core/features/notes/controller/NotesTextIndexer.test.ts
  • packages/app/src/core/features/notes/controller/__tests__/NotesController.bench.ts
  • packages/app/src/core/features/notes/controller/index.ts
  • packages/app/src/core/features/notes/history/NoteVersions.ts
  • packages/app/src/core/features/tags/controller/TagsController.ts
  • packages/app/src/core/features/workspaces/WorkspacesController.test.ts
  • packages/app/src/core/features/workspaces/WorkspacesController.ts
  • packages/app/src/core/storage/interop/import/NotesImporter.test.ts
  • packages/app/src/core/storage/interop/import/NotesImporter.worker.ts
  • packages/app/src/core/storage/interop/import/NotesImporterWorker.ts
  • packages/app/src/core/storage/interop/import/deepSerialize.ts
  • packages/app/src/core/storage/interop/import/index.ts
  • packages/app/src/features/App/Settings/sections/WorkspaceSettings.tsx
  • packages/app/src/features/App/Vault/index.tsx
  • packages/app/src/features/App/Vault/services/useBinService.tsx
  • packages/app/src/features/App/Vault/services/useFilesIntegrityService.tsx
  • packages/app/src/features/App/Vaults/hooks/useVaultContainers.ts
  • packages/app/src/features/App/Workspace/WorkspaceErrorScreen.tsx
  • packages/app/src/features/App/Workspace/useWorkspace.ts
  • packages/app/src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx
  • packages/app/src/features/MainScreen/WorkspacesPanel/useWorkspacesList.tsx
  • packages/app/src/hooks/notes/useNotesImport.tsx
  • words.txt

Comment thread packages/app/src/core/database/sqlite/SQLiteDatabase.ts Outdated
Comment thread packages/app/src/core/database/sqlite/SQLiteDatabaseWorker.ts Outdated
Comment thread packages/app/src/core/features/notes/controller/index.ts
Comment thread packages/app/src/core/features/notes/history/NoteVersions.ts
Comment thread packages/app/src/core/features/tags/controller/TagsController.ts
Comment thread packages/app/src/core/storage/interop/import/index.ts

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/app/src/features/App/Settings/sections/ImportAndExport.tsx (1)

89-99: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disable export while import is in progress.

Line 91 only disables export during export activity, not during import, so users can export mid-import and potentially capture incomplete state.

Suggested fix
 				<Button
 					size="sm"
-					isDisabled={notesExport.progress !== null}
+					isDisabled={notesExport.progress !== null || importProgress !== null}
 					onClick={async () => {
+						if (importProgress !== null) return;
 						await notesExport.exportNotes(
 							buildFileName(workspaceData?.name, 'backup'),
 						);
 					}}
 				>
🤖 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 `@packages/app/src/features/App/Settings/sections/ImportAndExport.tsx` around
lines 89 - 99, The export Button currently disables only when
notesExport.progress !== null, allowing exports during an import; update the
disabled condition on the Button in ImportAndExport.tsx to also check the import
progress (e.g., disable when notesImport?.progress !== null OR
notesExport.progress !== null) so that exports are blocked while an import is in
progress; keep the onClick calling
notesExport.exportNotes(buildFileName(workspaceData?.name, 'backup')) unchanged.
🤖 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 `@packages/app/src/locales/en/settings.json`:
- Line 244: Update the English locale string for the "directory" key in
settings.json to read "Import a directory with Markdown files" (replace the
current "Import directory with Markdown files") so the label is grammatically
correct; locate the "directory" property in the JSON (key: "directory") and
change its value accordingly.

---

Outside diff comments:
In `@packages/app/src/features/App/Settings/sections/ImportAndExport.tsx`:
- Around line 89-99: The export Button currently disables only when
notesExport.progress !== null, allowing exports during an import; update the
disabled condition on the Button in ImportAndExport.tsx to also check the import
progress (e.g., disable when notesImport?.progress !== null OR
notesExport.progress !== null) so that exports are blocked while an import is in
progress; keep the onClick calling
notesExport.exportNotes(buildFileName(workspaceData?.name, 'backup')) unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b288d28e-82c4-4f12-bbed-ee1860a68e35

📥 Commits

Reviewing files that changed from the base of the PR and between 08d0366 and 7b9de82.

📒 Files selected for processing (3)
  • packages/app/src/features/App/Settings/sections/ImportAndExport.tsx
  • packages/app/src/hooks/notes/useNotesImport.tsx
  • packages/app/src/locales/en/settings.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/app/src/hooks/notes/useNotesImport.tsx

Comment thread packages/app/src/locales/en/settings.json Outdated
@vitonsky vitonsky merged commit faf5b51 into master May 15, 2026
6 checks passed
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.

Run notes Import in worker

1 participant