Skip to content

Add interlinearizer project storage and createProject command#33

Open
alex-rawlings-yyc wants to merge 11 commits intomainfrom
create-interlinearizer-project
Open

Add interlinearizer project storage and createProject command#33
alex-rawlings-yyc wants to merge 11 commits intomainfrom
create-interlinearizer-project

Conversation

@alex-rawlings-yyc
Copy link
Copy Markdown
Contributor

@alex-rawlings-yyc alex-rawlings-yyc commented Apr 29, 2026


This change is Reviewable

Summary by CodeRabbit

  • New Features

    • Interlinear project UI: create, select, view/edit metadata, and delete projects via modal dialogs and WebView menu integration.
  • User Notifications

    • Error/success notifications for create/save/delete and storage failures; improved error logging.
  • Localization

    • Added English strings for creation, selection, info, and error messages.
  • Tests

    • Expanded unit/integration tests covering project CRUD, command handlers, WebView flows, parsers, and error cases.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds persistent Interlinearizer project CRUD, a new interlinearizer.createProject command, WebView menu/modal orchestration and three modal components, expanded Jest mocks (backend/frontend/react), parser tweaks, localization/menu entries, and comprehensive unit tests covering storage and UI flows.

Changes

Project Creation & Storage

Layer / File(s) Summary
Type Definitions
src/types/interlinearizer.d.ts
Adds InterlinearProject and `interlinearizer.createProject(sourceProjectId, analysisWritingSystem, name?, description?) => Promise<string
Storage Data / Helpers
src/projectStorage.ts
Centralizes ENOENT handling, per-project keys project:{id}, projectIds index, and empty-analysis initialization.
Core Storage Implementation
src/projectStorage.ts
Implements createProject, getProject, listProjects, getProjectsForSource, updateProjectMetadata, and deleteProject with index maintenance and ENOENT-aware semantics.
Command Implementation & Activation
src/main.ts
Captures executionToken during activate; registers interlinearizer.createProject, getProjectsForSource, updateProjectMetadata, deleteProject, newProject; delegates to projectStorage, logs and sends notifications on errors.
WebView Orchestration
src/interlinearizer.web-view.tsx
Loads menu data, computes projectMenuData, persists activeProject snapshot in WebView state, and routes top-menu project commands to select/create/metadata modal flows.
Modal Components
src/components/SelectInterlinearProjectModal.tsx, src/components/CreateProjectModal.tsx, src/components/ProjectMetadataModal.tsx
Adds modal components that gate on localized strings, call backend commands, handle errors via logger + papi.notifications.send, and expose callbacks for create/select/save/delete flows.

Mocks, Test Config & Miscellaneous

Layer / File(s) Summary
Mock Surface (backend/frontend/react)
__mocks__/papi-backend.ts, __mocks__/papi-frontend.ts, __mocks__/papi-frontend-react.ts
Adds backend storage and notifications mocks and exposes handles; adds frontend commands/notifications mock and Proxy-based useData mock.
Platform UI & Utils Mocks
__mocks__/platform-bible-react.tsx, __mocks__/platform-bible-utils.ts
Expands mocked component props and handler shapes; refactors UnsubscriberAsyncList to accept sync/async disposables; widens PlatformError and adjusts isPlatformError signature.
Icon Mock
__mocks__/lucide-react.tsx
Adds simple SVG stubs for Trash2 and Info.
Parser & Tests Tweaks
src/parsers/pt9/interlinearXmlParser.ts, src/__tests__/parsers/papi/usjBookExtractor.test.ts
Replaces parseStrictNumber with digits-only validation and JSDoc; adds USJ extractor tests for undefined/null equivalence and duplicate verse SID error.
Localization & Menus
contributions/localizedStrings.json, contributions/menus.json
Adds English strings for Interlinearizer modals and a webViewMenus["interlinearizer.mainWebView"] entry with project commands.
Jest Config & CSpell
jest.config.ts, cspell.json
Enables restoreMocks: true and adds interlinearize/interlinearizing to cspell word list.

Sequence Diagram

sequenceDiagram
    participant User
    participant Handler as createInterlinearProject
    participant Storage as projectStorage
    participant Backend as papi.storage
    participant Notify as papi.notifications

    User->>Handler: invoke createProject(sourceProjectId, analysisWritingSystem, name?, description?)
    Handler->>Storage: createProject(token, sourceProjectId, analysisWritingSystem, name?, description?)
    Storage->>Backend: readUserData(projectIds)
    Backend-->>Storage: projectIds or ENOENT
    Storage->>Backend: writeUserData(project:{id}, JSON)
    Storage->>Backend: writeUserData(projectIds, JSON)
    alt storage succeeds
        Storage-->>Handler: created project id
        Handler-->>User: project id
    else storage fails
        Handler->>Notify: send(error notification)
        Handler-->>User: undefined
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

🟪Idea

Suggested reviewers

  • imnasnainaec
  • jasonleenaylor

"🐰
I hopped through code and stored a key—
Projects born with UUID glee.
Mocks in place and tests that play,
Modals guide the chosen way.
Logger listens, rabbit hops away!"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main objective of adding interlinearizer project storage and the createProject command, which are central to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch create-interlinearizer-project

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.

@alex-rawlings-yyc alex-rawlings-yyc linked an issue Apr 29, 2026 that may be closed by this pull request
@alex-rawlings-yyc alex-rawlings-yyc self-assigned this Apr 29, 2026
- Add `src/projectStorage.ts` with `createProject`, `getProject`, `listProjects`, and `deleteProject` backed by `papi.storage`
- Register `interlinearizer.createProject` command in `main.ts` that prompts for source/target projects, writes the record, and surfaces storage errors as notifications
- Add `InterlinearProject` type and `interlinearizer.createProject` signature to shared type declarations
- Extend PAPI backend mock with `papi.storage` and `papi.notifications`
- Broaden Jest coverage to all `src/**` files
- Add full test suites for the storage module and the new command
@alex-rawlings-yyc alex-rawlings-yyc force-pushed the create-interlinearizer-project branch from e2e2f8a to 3da254f Compare April 30, 2026 18:03
@alex-rawlings-yyc alex-rawlings-yyc marked this pull request as ready for review May 5, 2026 18:25
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/main.ts`:
- Around line 209-213: The result schema for the createProject command deviates
from the established WebView convention by using { type: ['string', 'null'] }
instead of the conventionally used { type: 'string' } for commands that return
undefined on cancellation; update the result schema in the createProject
command's result object to { type: 'string' } to match openForWebView and other
WebView-opening commands (or alternatively add a concise comment next to the
createProject result explaining why null is intentionally used if you truly need
null serialization), ensuring you edit the result object associated with
createProject and keep the summary text describing that undefined is returned on
cancel consistent.

In `@src/projectStorage.ts`:
- Around line 131-136: deleteProject currently calls papi.storage.deleteUserData
unconditionally and will throw if the project file is missing; wrap the
deleteUserData call in a try/catch inside deleteProject (referencing
deleteProject, papi.storage.deleteUserData, and projectKey) and swallow only
ENOENT errors (i.e., if err.code === 'ENOENT' treat as no-op) while rethrowing
other errors, then continue to readIds and write the updated PROJECT_IDS_KEY
index; also add the provided test to src/__tests__/projectStorage.test.ts to
assert index cleanup when deleteUserData rejects with ENOENT.

In `@src/types/interlinearizer.d.ts`:
- Around line 15-23: Update the JSDoc for the 'interlinearizer.createProject'
declaration to state that the Promise may resolve to undefined not only when the
user cancels either picker but also if creating the project in storage fails
(e.g., when projectStorage.createProject throws and the implementation logs an
error/sends a notification in main.ts), so callers know undefined can indicate a
storage failure as well as a user cancellation.
🪄 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

Run ID: 602669d3-ae6c-442a-9329-6a0a9f86bf08

📥 Commits

Reviewing files that changed from the base of the PR and between 5b35404 and fc6426f.

📒 Files selected for processing (6)
  • __mocks__/papi-backend.ts
  • src/__tests__/main.test.ts
  • src/__tests__/projectStorage.test.ts
  • src/main.ts
  • src/projectStorage.ts
  • src/types/interlinearizer.d.ts

Comment thread src/projectStorage.ts
Comment thread src/types/interlinearizer.d.ts Outdated
…ect and add associated test, fix schema inconsistency
Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc resolved 3 discussions.
Reviewable status: 0 of 9 files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 9 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@src/__tests__/projectStorage.test.ts`:
- Around line 36-40: The test setup fails to reset __mockReadUserData and leaves
crypto.randomUUID permanently spied-on; update the tests to call
jest.resetAllMocks() at the start of beforeEach (or explicitly reset
__mockReadUserData) so any once-queues are cleared before re-establishing
default mockResolvedValue, and add an afterEach that calls
jest.restoreAllMocks() to unwrap the spy on crypto.randomUUID; locate the setup
in the test file where __mockWriteUserData, __mockDeleteUserData and
jest.spyOn(crypto, 'randomUUID') are referenced and apply these 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb6b9254-c4f1-4b22-a279-06c6ef431f4f

📥 Commits

Reviewing files that changed from the base of the PR and between fc6426f and 0fdeeb7.

📒 Files selected for processing (5)
  • src/__tests__/projectStorage.test.ts
  • src/main.ts
  • src/parsers/pt9/interlinearXmlParser.ts
  • src/projectStorage.ts
  • src/types/interlinearizer.d.ts
✅ Files skipped from review due to trivial changes (1)
  • src/parsers/pt9/interlinearXmlParser.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/types/interlinearizer.d.ts
  • src/projectStorage.ts
  • src/main.ts

Comment thread src/__tests__/projectStorage.test.ts
Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 1 file and all commit messages, and resolved 1 discussion.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

…er index/data write to `papi` storage to ensure data doesn't become inaccessible
@alex-rawlings-yyc
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@src/main.ts`:
- Around line 211-215: Update the result schema summary for
createInterlinearProject to reflect both cancellation and storage failures:
change the text in the result object (the "result" entry that currently reads
"The UUID of the new project, or undefined if the user cancelled") to something
like "The UUID of the new project, or undefined if the user cancelled or if
projectStorage.createProject failed" so JSON-RPC consumers know undefined can
mean a storage failure as well as user cancellation.
- Around line 120-130: Ensure the source and target projects chosen via
papi.dialogs.selectProject are distinct: after obtaining sourceProjectId and
before/after prompting for targetProjectId, validate that the newly selected
targetProjectId !== sourceProjectId and if equal re-prompt the user (or show an
error and re-open the selectProject dialog) until a different project is chosen;
update any user-facing error text by adding a localization key (e.g., for
interlinearizer_error_same_project) and use it when rejecting identical
selections so the InterlinearProject never gets created with sourceProjectId ===
targetProjectId (this also avoids confusing entries in listProjects).
🪄 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

Run ID: 6b872de8-61ff-46d2-b985-f3d814186f84

📥 Commits

Reviewing files that changed from the base of the PR and between 0fdeeb7 and f17c64e.

📒 Files selected for processing (3)
  • jest.config.ts
  • src/main.ts
  • src/projectStorage.ts

Comment thread src/main.ts Outdated
Comment thread src/main.ts
…update mocks to match real API signatures

- If the user picks the same project for both source and target roles, a warning notification is shown and the target picker re-opens until distinct projects are chosen or cancelled.
- Includes new tests for this flow, updated platform-bible-react/utils mocks to match current API shapes, a new localized error string, and minor test/parser cleanups.
Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 9 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/main.ts (1)

115-127: 💤 Low value

Optional: convert recursion to a loop.

promptForDistinctTargetProject recurses on every same-project mistake. JavaScript doesn't optimize tail calls, so the call stack grows with each retry. In practice this is fine, but a while loop expresses the "re-prompt until valid" intent more directly and avoids any stack growth concern.

♻️ Suggested loop form
 async function promptForDistinctTargetProject(
   sourceProjectId: string,
 ): Promise<string | undefined> {
-  const targetProjectId = await papi.dialogs.selectProject({
-    title: '%interlinearizer_dialog_create_target_title%',
-    prompt: '%interlinearizer_dialog_create_target_prompt%',
-  });
-  if (!targetProjectId || targetProjectId !== sourceProjectId) return targetProjectId;
-  await papi.notifications
-    .send({ message: '%interlinearizer_error_same_project%', severity: 'warning' })
-    .catch(() => {});
-  return promptForDistinctTargetProject(sourceProjectId);
+  while (true) {
+    const targetProjectId = await papi.dialogs.selectProject({
+      title: '%interlinearizer_dialog_create_target_title%',
+      prompt: '%interlinearizer_dialog_create_target_prompt%',
+    });
+    if (!targetProjectId || targetProjectId !== sourceProjectId) return targetProjectId;
+    await papi.notifications
+      .send({ message: '%interlinearizer_error_same_project%', severity: 'warning' })
+      .catch(() => {});
+  }
 }
🤖 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 `@src/main.ts` around lines 115 - 127, The function
promptForDistinctTargetProject currently uses recursion to re-prompt on
same-project selection; replace the recursive pattern with an explicit loop:
repeatedly call papi.dialogs.selectProject inside a while loop, return the
selected targetProjectId immediately if it's falsy or different from
sourceProjectId, otherwise await papi.notifications.send(...) and continue the
loop; remove the recursive call to promptForDistinctTargetProject to avoid stack
growth.
__mocks__/platform-bible-react.tsx (1)

37-65: 💤 Low value

Mock prop typing is inconsistent with usage.

onSelectProjectMenuItem and onSelectViewInfoMenuItem are typed as required (no ?), but the JSX renders them conditionally with onSelectProjectMenuItem && (…) / onSelectViewInfoMenuItem && (…). Real consumers like src/interlinearizer.web-view.tsx (per the provided snippet) don't pass these callbacks at all, so the conditional rendering is the correct behavior — the type should reflect that they're optional.

♻️ Make the callbacks optional
-  onSelectProjectMenuItem: (selectedMenuItem: unknown) => void;
-  onSelectViewInfoMenuItem: (selectedMenuItem: unknown) => void;
+  onSelectProjectMenuItem?: (selectedMenuItem: unknown) => void;
+  onSelectViewInfoMenuItem?: (selectedMenuItem: unknown) => void;
🤖 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 `@__mocks__/platform-bible-react.tsx` around lines 37 - 65, The prop types for
onSelectProjectMenuItem and onSelectViewInfoMenuItem are declared as required
but the component conditionally renders based on their existence; update the
prop definitions to make these two props optional (add ? to
onSelectProjectMenuItem and onSelectViewInfoMenuItem in the component's props
type) and ensure their callback signature allows being called with undefined
(e.g., accept unknown | undefined) so the onClick handlers that call
onSelectProjectMenuItem(undefined) / onSelectViewInfoMenuItem(undefined) remain
valid.
🤖 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 `@contributions/localizedStrings.json`:
- Around line 4-9: Add the missing localization keys referenced by src/main.ts
so UI dialogs don't show raw placeholders: add entries for
"%interlinearizer_dialog_create_source_title%",
"%interlinearizer_dialog_create_source_prompt%",
"%interlinearizer_dialog_create_target_title%",
"%interlinearizer_dialog_create_target_prompt%", and
"%interlinearizer_error_create_project_failed%" to the existing "en" locale
block (use wording consistent with existing keys like
"%interlinearizer_dialog_open_title%" and
"%interlinearizer_error_same_project%"); ensure each new key has an appropriate
human-friendly string for the createProject flow and follow the same JSON
structure and comma placement as surrounding entries.

In `@src/__tests__/interlinearizer.web-view.test.tsx`:
- Around line 358-378: The two tests named "logs with 'und' writing system when
tokenization fails …" claim to verify that the fallback writing system 'und' is
used but only assert the error heading; update each test to either (A) tighten
the assertion by spying on logger.error from `@papi/frontend` and assert it was
called with a message or metadata containing the writing system 'und' when
tokenizeBook throws (locate the tests using InterlinearizerWebView and the
mocked tokenizeBook), or (B) if you prefer not to inspect logs, rename the tests
to reflect that they only assert the error UI is shown; for option A import
logger, create a jest.spyOn(logger, 'error'), trigger the failing tokenizeBook
in the existing tests, and assert the spy was called with a payload including
'und'.

---

Nitpick comments:
In `@__mocks__/platform-bible-react.tsx`:
- Around line 37-65: The prop types for onSelectProjectMenuItem and
onSelectViewInfoMenuItem are declared as required but the component
conditionally renders based on their existence; update the prop definitions to
make these two props optional (add ? to onSelectProjectMenuItem and
onSelectViewInfoMenuItem in the component's props type) and ensure their
callback signature allows being called with undefined (e.g., accept unknown |
undefined) so the onClick handlers that call onSelectProjectMenuItem(undefined)
/ onSelectViewInfoMenuItem(undefined) remain valid.

In `@src/main.ts`:
- Around line 115-127: The function promptForDistinctTargetProject currently
uses recursion to re-prompt on same-project selection; replace the recursive
pattern with an explicit loop: repeatedly call papi.dialogs.selectProject inside
a while loop, return the selected targetProjectId immediately if it's falsy or
different from sourceProjectId, otherwise await papi.notifications.send(...) and
continue the loop; remove the recursive call to promptForDistinctTargetProject
to avoid stack growth.
🪄 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

Run ID: 9bd5c686-a23c-48ca-b23d-88481c6ae8c8

📥 Commits

Reviewing files that changed from the base of the PR and between f17c64e and 42b5c16.

📒 Files selected for processing (8)
  • __mocks__/platform-bible-react.tsx
  • __mocks__/platform-bible-utils.ts
  • contributions/localizedStrings.json
  • src/__tests__/interlinearizer.web-view.test.tsx
  • src/__tests__/main.test.ts
  • src/__tests__/parsers/papi/usjBookExtractor.test.ts
  • src/main.ts
  • src/parsers/pt9/interlinearXmlParser.ts

Comment thread contributions/localizedStrings.json
Comment thread src/__tests__/interlinearizer.web-view.test.tsx
Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 3 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 1 file and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/main.ts (1)

56-60: 💤 Low value

Consider a definite-assignment assertion on executionToken.

let executionToken: ExecutionToken; types reads as ExecutionToken, but TypeScript does not actually verify the variable is assigned before use. If a future refactor lets a command handler run before activate() completes (or after deactivation if you decide to clear it), reads would silently see undefined. Either narrow the type or signal definite assignment:

-let executionToken: ExecutionToken;
+let executionToken!: ExecutionToken;

Or let executionToken: ExecutionToken | undefined; with an explicit guard inside createInterlinearProject for stronger safety.

🤖 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 `@src/main.ts` around lines 56 - 60, The module-level variable executionToken
is declared without a definite-assignment guarantee, which can allow undefined
reads if command handlers run before activate() or after deactivation; change
the declaration to either use a definite-assignment assertion (let
executionToken!: ExecutionToken) or make it nullable (let executionToken:
ExecutionToken | undefined) and then add an explicit runtime guard at the call
site—specifically inside createInterlinearProject and any command handlers that
reference executionToken—to throw or return early when executionToken is
undefined, and ensure activate() still assigns it and deactivate() clears it
consistently.
src/__tests__/interlinearizer.web-view.test.tsx (1)

359-391: 💤 Low value

Restore the logger.error spy to prevent leaking into later tests.

jest.spyOn(logger, 'error').mockImplementation(() => {}) is created inside each test but never restored. The mock implementation persists for the rest of the worker, silencing any subsequent test that exercises logger.error (or that you add later). Add mockRestore() (or a global afterEach(() => jest.restoreAllMocks())) so the spy is scoped to the test that creates it.

♻️ Proposed scoped restore
   it('logs with "und" writing system when tokenization fails and writing system is a PlatformError', () => {
     const loggerErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {});
     mockBookData({});
     mockWritingSystem({ platformErrorVersion: 1, message: 'Setting unavailable' });
     jest.mocked(tokenizeBook).mockImplementation(() => {
       throw new Error('parse failure');
     });
     render(<InterlinearizerWebView {...makeProps(testProjectId)} />);

     expect(screen.getByRole('heading', { name: /error processing book/i })).toBeInTheDocument();
     expect(loggerErrorSpy).toHaveBeenCalledWith(
       expect.any(String),
       expect.anything(),
       expect.objectContaining({ writingSystem: 'und' }),
     );
+    loggerErrorSpy.mockRestore();
   });

Apply the same change to the empty-string variant, or add a single afterEach(() => jest.restoreAllMocks()) to the describe block.

🤖 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 `@src/__tests__/interlinearizer.web-view.test.tsx` around lines 359 - 391, The
tests create a spy via jest.spyOn(logger, 'error').mockImplementation(() => {})
in the two it blocks ('logs with "und" writing system when tokenization
fails...') but never restore it, leaking the mock to later tests; fix by
restoring the spy after each test that creates it (call mockRestore() on the
loggerErrorSpy at the end of each test) or add a scoped cleanup like
afterEach(() => jest.restoreAllMocks()) in the describe containing these tests
so the logger.error mock does not persist beyond the test.
🤖 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.

Nitpick comments:
In `@src/__tests__/interlinearizer.web-view.test.tsx`:
- Around line 359-391: The tests create a spy via jest.spyOn(logger,
'error').mockImplementation(() => {}) in the two it blocks ('logs with "und"
writing system when tokenization fails...') but never restore it, leaking the
mock to later tests; fix by restoring the spy after each test that creates it
(call mockRestore() on the loggerErrorSpy at the end of each test) or add a
scoped cleanup like afterEach(() => jest.restoreAllMocks()) in the describe
containing these tests so the logger.error mock does not persist beyond the
test.

In `@src/main.ts`:
- Around line 56-60: The module-level variable executionToken is declared
without a definite-assignment guarantee, which can allow undefined reads if
command handlers run before activate() or after deactivation; change the
declaration to either use a definite-assignment assertion (let executionToken!:
ExecutionToken) or make it nullable (let executionToken: ExecutionToken |
undefined) and then add an explicit runtime guard at the call site—specifically
inside createInterlinearProject and any command handlers that reference
executionToken—to throw or return early when executionToken is undefined, and
ensure activate() still assigns it and deactivate() clears it consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d134961c-f2b1-42cc-a7d3-086903328e5d

📥 Commits

Reviewing files that changed from the base of the PR and between 42b5c16 and 3b6e241.

📒 Files selected for processing (4)
  • contributions/localizedStrings.json
  • src/__tests__/interlinearizer.web-view.test.tsx
  • src/main.ts
  • src/types/interlinearizer.d.ts

Copy link
Copy Markdown
Contributor

@imnasnainaec imnasnainaec left a comment

Choose a reason for hiding this comment

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

@imnasnainaec partially reviewed 7 files and made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on alex-rawlings-yyc).


src/main.ts line 151 at r7 (raw file):

  const targetProjectId = await promptForDistinctTargetProject(sourceProjectId);
  if (!targetProjectId) return undefined;

This seems restrictive. Shouldn't it be optional to have a target project? We definitely want to support that, but I thought we'd want users to be able to start using the interlinearizer without having to first create an "output" project for it.

Introduces three modal components (CreateProjectModal, ProjectMetadataModal, SelectInterlinearProjectModal) with supporting menu contributions, localized strings, command registrations in main.ts, storage helpers in projectStorage.ts, and updated type definitions. Adds full test coverage for all new components and expands existing test suites to cover the new flows.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/components/SelectInterlinearProjectModal.tsx (1)

108-158: ⚡ Quick win

Add ARIA dialog role, aria-modal, aria-labelledby, and an Escape-key handler.

The modal overlay is an unsemantic <div>. Screen readers won't announce it as a dialog, and the virtual cursor isn't constrained to it. The standard ARIA dialog pattern requires:

  • role="dialog" and aria-modal="true" on the container so AT restricts navigation.
  • A stable id on the <h2> and a matching aria-labelledby so the dialog has an accessible name.
  • Closing on Escape (standard keyboard expectation for dialogs).
♿ Proposed ARIA additions
+const TITLE_ID = 'select-interlinear-project-title';
 ...
-<div className="tw-fixed tw-inset-0 tw-z-50 tw-flex tw-items-center tw-justify-center tw-bg-black/40">
-  <div className="tw-bg-background tw-rounded-lg tw-border tw-border-border tw-p-6 tw-w-[32rem] tw-shadow-lg">
-    <h2 className="tw-text-base tw-font-semibold tw-mb-4">
+<div
+  className="tw-fixed tw-inset-0 tw-z-50 tw-flex tw-items-center tw-justify-center tw-bg-black/40"
+  role="dialog"
+  aria-modal="true"
+  aria-labelledby={TITLE_ID}
+  onKeyDown={(e) => { if (e.key === 'Escape') onClose(); }}
+>
+  <div className="tw-bg-background tw-rounded-lg tw-border tw-border-border tw-p-6 tw-w-[32rem] tw-shadow-lg">
+    <h2 id={TITLE_ID} className="tw-text-base tw-font-semibold tw-mb-4">
🤖 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 `@src/components/SelectInterlinearProjectModal.tsx` around lines 108 - 158, The
modal in SelectInterlinearProjectModal lacks ARIA dialog attributes and
Escape-key handling; add role="dialog" and aria-modal="true" to the outer
container div, give the <h2> a stable id (e.g., modalTitleId) and add
aria-labelledby referencing that id so the dialog has an accessible name, and
implement an Escape-key handler using useEffect inside the
SelectInterlinearProjectModal component that listens for keydown (Escape) and
calls the existing onClose prop, with proper cleanup on unmount; keep existing
handlers (handleSelect, handleViewInfo, onCreateNew) unchanged.
src/interlinearizer.web-view.tsx (1)

450-456: 💤 Low value

makeHandleProjectCreated(projectId) returns a new function on every render.

Calling the factory inline in JSX produces a fresh onProjectCreated reference each render, which invalidates CreateProjectModal's handleSubmit useCallback dependency. Memoizing once for the current projectId avoids that churn:

♻️ Proposed fix
-      {modal === 'create' && projectId && (
-        <CreateProjectModal
-          projectId={projectId}
-          onClose={() => setModal('none')}
-          onProjectCreated={makeHandleProjectCreated(projectId)}
-        />
-      )}
+      {modal === 'create' && projectId && (
+        <CreateProjectModalForSource
+          projectId={projectId}
+          onClose={() => setModal('none')}
+          makeHandleProjectCreated={makeHandleProjectCreated}
+        />
+      )}

…or simply hoist the memoized handler:

+  const handleProjectCreated = useMemo(
+    () => (projectId ? makeHandleProjectCreated(projectId) : undefined),
+    [makeHandleProjectCreated, projectId],
+  );
   ...
-          onProjectCreated={makeHandleProjectCreated(projectId)}
+          onProjectCreated={handleProjectCreated}
🤖 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 `@src/interlinearizer.web-view.tsx` around lines 450 - 456, The inline call to
makeHandleProjectCreated(projectId) inside the JSX creates a new function each
render, causing CreateProjectModal's onProjectCreated prop to change and
invalidating its internal useCallback (e.g., handleSubmit); fix by memoizing the
handler for the current projectId (e.g., useMemo or useCallback at component
scope) and pass that stable reference to onProjectCreated, ensuring you compute
the memoizedHandler when projectId changes and still call setModal('none') in
the handler as before.
🤖 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 `@src/components/CreateProjectModal.tsx`:
- Around line 53-70: The handleSubmit in CreateProjectModal calls onClose() even
when papi.commands.sendCommand('interlinearizer.createProject', ...) returns
undefined (storage errors are swallowed in main.ts), which causes the modal to
close and discard user input; change the logic in handleSubmit so that onClose()
is only invoked when a valid newId is returned (i.e., after the
onProjectCreated?.(newId, analysisLanguage) branch), and instead handle the
undefined case by keeping the modal open and surfacing an appropriate
notification or error state to the user; refer to handleSubmit,
onProjectCreated, onClose, and the interlinearizer.createProject call to locate
where to gate the dismiss.

In `@src/components/ProjectMetadataModal.tsx`:
- Around line 88-103: handleSave currently sends and propagates the raw
editLanguage (which may contain surrounding whitespace) even though the Save
button uses editLanguage.trim() to gate submission; trim editLanguage once
before persisting and passing to callbacks to avoid leaking whitespace. Update
handleSave to compute a trimmedLanguage (e.g. from editLanguage?.trim()) and use
that value when calling
papi.commands.sendCommand('interlinearizer.updateProjectMetadata', ...) and
onProjectSaved(...), ensuring you still coerce empty strings to undefined as
done for newName/newDescription.

---

Nitpick comments:
In `@src/components/SelectInterlinearProjectModal.tsx`:
- Around line 108-158: The modal in SelectInterlinearProjectModal lacks ARIA
dialog attributes and Escape-key handling; add role="dialog" and
aria-modal="true" to the outer container div, give the <h2> a stable id (e.g.,
modalTitleId) and add aria-labelledby referencing that id so the dialog has an
accessible name, and implement an Escape-key handler using useEffect inside the
SelectInterlinearProjectModal component that listens for keydown (Escape) and
calls the existing onClose prop, with proper cleanup on unmount; keep existing
handlers (handleSelect, handleViewInfo, onCreateNew) unchanged.

In `@src/interlinearizer.web-view.tsx`:
- Around line 450-456: The inline call to makeHandleProjectCreated(projectId)
inside the JSX creates a new function each render, causing CreateProjectModal's
onProjectCreated prop to change and invalidating its internal useCallback (e.g.,
handleSubmit); fix by memoizing the handler for the current projectId (e.g.,
useMemo or useCallback at component scope) and pass that stable reference to
onProjectCreated, ensuring you compute the memoizedHandler when projectId
changes and still call setModal('none') in the handler as before.
🪄 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

Run ID: 6b260117-e9da-4fc9-8fa6-d76d6f3c6c07

📥 Commits

Reviewing files that changed from the base of the PR and between 42b5c16 and efc4994.

📒 Files selected for processing (21)
  • __mocks__/lucide-react.tsx
  • __mocks__/papi-frontend-react.ts
  • __mocks__/papi-frontend.ts
  • __mocks__/platform-bible-react.tsx
  • contributions/localizedStrings.json
  • contributions/menus.json
  • cspell.json
  • jest.config.ts
  • src/__tests__/components/CreateProjectModal.test.tsx
  • src/__tests__/components/ProjectMetadataModal.test.tsx
  • src/__tests__/components/SelectInterlinearProjectModal.test.tsx
  • src/__tests__/interlinearizer.web-view.test.tsx
  • src/__tests__/main.test.ts
  • src/__tests__/projectStorage.test.ts
  • src/components/CreateProjectModal.tsx
  • src/components/ProjectMetadataModal.tsx
  • src/components/SelectInterlinearProjectModal.tsx
  • src/interlinearizer.web-view.tsx
  • src/main.ts
  • src/projectStorage.ts
  • src/types/interlinearizer.d.ts
✅ Files skipped from review due to trivial changes (3)
  • cspell.json
  • contributions/localizedStrings.json
  • src/tests/components/CreateProjectModal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/tests/interlinearizer.web-view.test.tsx
  • src/main.ts
  • src/types/interlinearizer.d.ts
  • mocks/platform-bible-react.tsx

Comment thread src/components/CreateProjectModal.tsx
Comment thread src/components/ProjectMetadataModal.tsx
…nput

- `CreateProjectModal`: guard `onClose()` behind a `!newId` early-return so the modal doesn't close when project creation returns no ID
- `ProjectMetadataModal`: trim whitespace from the language field before saving and passing to callbacks
- Replace `makeHandleProjectCreated` factory with a plain `handleProjectCreated` callback; removes the closure-over-srcId pattern and reads `projectId` from the enclosing scope directly
Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 21 files and all commit messages, made 1 comment, and resolved 2 discussions.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).


src/main.ts line 151 at r7 (raw file):

Previously, imnasnainaec (D. Ror.) wrote…

This seems restrictive. Shouldn't it be optional to have a target project? We definitely want to support that, but I thought we'd want users to be able to start using the interlinearizer without having to first create an "output" project for it.

Correct. Addressed in latest commit

Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 5 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).

@alex-rawlings-yyc
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/components/SelectInterlinearProjectModal.tsx (1)

64-74: ⚡ Quick win

Validate required fields when filtering parsed projects.

The current type predicate accepts any non-null object. If interlinearizer.getProjectsForSource ever returns entries lacking required fields (e.g. due to a future schema change or storage corruption), they’ll pass the filter and cause subtler failures downstream — key={project.id} with undefined, onSelect/onViewInfo receiving objects whose id is missing, etc. Tightening the predicate fails fast and keeps malformed entries out of state.

🛡️ Proposed fix
-      if (Array.isArray(parsed))
-        setProjects(
-          parsed.filter((p): p is InterlinearProjectSummary => !!p && typeof p === 'object'),
-        );
+      if (Array.isArray(parsed))
+        setProjects(
+          parsed.filter((p): p is InterlinearProjectSummary => {
+            if (!p || typeof p !== 'object') return false;
+            const o = p as Record<string, unknown>;
+            return (
+              typeof o.id === 'string' &&
+              typeof o.createdAt === 'string' &&
+              typeof o.sourceProjectId === 'string' &&
+              typeof o.analysisWritingSystem === 'string'
+            );
+          }),
+        );
🤖 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 `@src/components/SelectInterlinearProjectModal.tsx` around lines 64 - 74, The
JSON parsing/filter step currently treats any non-null object as an
InterlinearProjectSummary; tighten the type guard used when calling setProjects
so you explicitly check for required fields (e.g. typeof p === 'object' && p !==
null && typeof (p as any).id === 'string' && typeof (p as any).name === 'string'
and any other required props per InterlinearProjectSummary) before narrowing;
update the predicate around parsed.filter(...) (the block that sets projects
after interlinearizer.getProjectsForSource) to return only objects that satisfy
those checks so downstream uses like key={project.id}, onSelect and onViewInfo
always receive well-formed projects.
🤖 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 `@src/__tests__/components/CreateProjectModal.test.tsx`:
- Around line 131-134: The test asserts onProjectCreated immediately after
awaiting userEvent.click; mirror the onClose test by wrapping the
expect(onProjectCreated).toHaveBeenCalledWith('new-project-id', 'en') inside a
waitFor to ensure promise continuations from sendCommand are flushed. Locate the
assertion using the onProjectCreated mock and userEvent.click call in
CreateProjectModal.test.tsx and replace the direct expect with a waitFor-based
expect to make the test consistent and stable.

In `@src/interlinearizer.web-view.tsx`:
- Around line 311-321: handleProjectCreated currently only receives
(interlinearProjectId, analysisWritingSystem) and fabricates createdAt while
dropping name/description; fix by making createProject/its command handler
return the full persisted Project object instead of just the id, update
CreateProjectModal to pass that returned Project into onProjectCreated, and
change handleProjectCreated to accept the Project (use its id, name,
description, createdAt and analysisWritingSystem) and call setActiveProject with
the exact persisted object (no local new Date()). Update related call sites of
interlinearizer.createProject and the command handler to return the full project
shape so UI and metadata modal see the authoritative createdAt, name and
description.

In `@src/main.ts`:
- Around line 154-184: Wrap the bodies of deleteInterlinearProject and
updateProjectMetadata in try/catch blocks mirroring
createInterlinearProject/getProjectsForSource: on error catch the thrown storage
error from projectStorage.deleteProject or projectStorage.updateProjectMetadata,
log it with the same logger used elsewhere (e.g. processLogger.error) including
contextual text and the error, send a user-facing notification using the same
WebView/user-notification mechanism used by the other functions, and return a
safe fallback (swallow the error for deleteInterlinearProject so it doesn't
reject the WebView caller; for updateProjectMetadata return undefined on
failure).
- Around line 314-325: Add a no-op registration for the
interlinearizer.viewProjectInfo command analogous to the existing
interlinearizer.newProject registration: call
papi.commands.registerCommand('interlinearizer.viewProjectInfo', async () => {},
{...}) in the same initialization area (same pattern as
newProjectCommandRegistration) and provide method metadata (summary, params: [],
result schema void/null) so the platform recognizes the command referenced by
contributions/menus.json; ensure the variable name and registration are
consistent with the existing newProjectCommandRegistration usage.

---

Nitpick comments:
In `@src/components/SelectInterlinearProjectModal.tsx`:
- Around line 64-74: The JSON parsing/filter step currently treats any non-null
object as an InterlinearProjectSummary; tighten the type guard used when calling
setProjects so you explicitly check for required fields (e.g. typeof p ===
'object' && p !== null && typeof (p as any).id === 'string' && typeof (p as
any).name === 'string' and any other required props per
InterlinearProjectSummary) before narrowing; update the predicate around
parsed.filter(...) (the block that sets projects after
interlinearizer.getProjectsForSource) to return only objects that satisfy those
checks so downstream uses like key={project.id}, onSelect and onViewInfo always
receive well-formed projects.
🪄 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

Run ID: cddc4664-6910-4300-aa98-d747b1dc5377

📥 Commits

Reviewing files that changed from the base of the PR and between 42b5c16 and 7e2c4c0.

📒 Files selected for processing (21)
  • __mocks__/lucide-react.tsx
  • __mocks__/papi-frontend-react.ts
  • __mocks__/papi-frontend.ts
  • __mocks__/platform-bible-react.tsx
  • contributions/localizedStrings.json
  • contributions/menus.json
  • cspell.json
  • jest.config.ts
  • src/__tests__/components/CreateProjectModal.test.tsx
  • src/__tests__/components/ProjectMetadataModal.test.tsx
  • src/__tests__/components/SelectInterlinearProjectModal.test.tsx
  • src/__tests__/interlinearizer.web-view.test.tsx
  • src/__tests__/main.test.ts
  • src/__tests__/projectStorage.test.ts
  • src/components/CreateProjectModal.tsx
  • src/components/ProjectMetadataModal.tsx
  • src/components/SelectInterlinearProjectModal.tsx
  • src/interlinearizer.web-view.tsx
  • src/main.ts
  • src/projectStorage.ts
  • src/types/interlinearizer.d.ts
✅ Files skipped from review due to trivial changes (3)
  • cspell.json
  • contributions/localizedStrings.json
  • src/components/CreateProjectModal.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/tests/interlinearizer.web-view.test.tsx
  • mocks/platform-bible-react.tsx
  • src/tests/projectStorage.test.ts

Comment thread src/__tests__/components/CreateProjectModal.test.tsx
Comment thread src/interlinearizer.web-view.tsx
Comment thread src/main.ts
Comment thread src/main.ts
…r delete/update

- `interlinearizer.createProject` now returns the full persisted project as a JSON string instead of just the UUID, so the WebView can populate `activeProject` with authoritative server data rather than reconstructing it locally
- `CreateProjectModal.onProjectCreated` callback now receives the parsed `InterlinearProjectSummary` object instead of `(id, writingSystem)` pair
- Add `isInterlinearProjectSummary` type guard to `SelectInterlinearProjectModal` and reuse it in the project list filter and the new create flow
- Wrap `deleteProject` and `updateProjectMetadata` backend handlers in try/catch with logging and error notifications (previously unhandled rejections)
- Register a no-op `interlinearizer.viewProjectInfo` backend command so the platform menu system can surface it; all logic runs in the WebView
- Update tests and type declarations to match
Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc resolved 4 discussions.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).

Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Choose a reason for hiding this comment

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

@alex-rawlings-yyc reviewed 9 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).

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.

Create new interlinearizer project

2 participants