Skip to content

[PB Extension] Create FW project#2394

Open
imnasnainaec wants to merge 14 commits into
developfrom
pb-extension/create-project-api
Open

[PB Extension] Create FW project#2394
imnasnainaec wants to merge 14 commits into
developfrom
pb-extension/create-project-api

Conversation

@imnasnainaec

@imnasnainaec imnasnainaec commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Lets users create a brand-new FW Lite lexicon directly from the lexicon selector in Platform.Bible instead of only picking an existing one. This wires up the backend create endpoint and the project-type-aware URL routing the flow needs, and fixes a few adjacent bugs (empty-response parsing, language-tag filtering, lexicon-code validation) surfaced along the way.

Summary

  • Adds FwLiteApi.createProject(name, code, vernacularWs, analysisWs?) to the Platform.Bible extension's HTTP client, wrapping the POST /api/project/create endpoint added in Wire up template-based project creation via JSON snapshot import #2281.
  • Fixes a latent bug in fetchUrl: it previously called response.json() unconditionally, which throws a SyntaxError on empty-body responses (HTTP 200 with no content). The fix reads the body as text first and only parses JSON when the body is non-empty. On error responses it now throws with the response body, so backend validation messages survive. This also silently affected deleteEntry.
  • Adds a Create new lexicon flow to the SelectLexicon WebView: a form with name, auto-derived code, and vernacular language tag (pre-filled from the Paratext project's language but editable), plus an optional analysis language tag. The code is validated client-side against the backend's format rule and against the existing lexicon codes, so a duplicate is caught before submitting. On success the new lexicon is auto-selected and the dialog shows the standard "Lexicon selection saved" confirmation; on failure the form shows the backend's reason (e.g. "not a valid IETF language tag").
  • Tracks each local project's type (FwData vs Harmony/CRDT) so lexicon and browse URLs route to the correct backend endpoints. The lookup is self-healing: because the cache is in-memory only, on a miss (e.g. after an extension restart) it reloads the project list from the backend before falling back to FwData, so a previously created CRDT lexicon stays reachable without the user reopening the selector.
  • Hardens the pre-existing lexicon.lexiconCode project-settings validator, which accepted any resolvable code because it tested an always-truthy array (.analysis); it now requires at least one vernacular writing system (analysis writing systems remain optional and are handled by selectLexicon).

Changes

  • platform.bible-extension/src/utils/fw-lite-api.ts
    • fetchUrl: results.json()results.text() + conditional JSON.parse; throws the response body on error responses
    • FwLiteApi.createProject: new public method using URLSearchParams for the create endpoint
    • projectTypeByCode cache + resolveProjectType: checkLexiconCode and getBrowseUrl (now async) resolve a project's type, repopulating from the backend on a cache miss
    • doesProjectMatchLangTag: reads each vernacular writing system's wsId instead of Object.keys on the array (which returned index strings), so getProjectsMatchingLanguage actually filters — a path the select-lexicon flow newly exercises by passing projectId to lexicon.lexicons
  • platform.bible-extension/src/main.ts — registers the lexicon.createLexicon command (returns the backend error message on failure); validateLexiconCode now checks for a vernacular writing system instead of the always-truthy .analysis array
  • platform.bible-extension/src/types/lexicon.d.ts'lexicon.createLexicon' command handler type; optional error on SuccessHolder
  • platform.bible-extension/src/types/localized-string-keys.ts + contributions/localizedStrings.json — 9 new %lexicon_createLexicon_*% strings
  • platform.bible-extension/src/components/create-lexicon.tsx — new form component; pre-checks the entered code against existing lexicons and surfaces backend error messages
  • platform.bible-extension/src/components/lexicon-combo-box.tsx — optional onCreateNew prop adds the "Create new lexicon" button
  • platform.bible-extension/src/utils/project-manager.ts — passes the project's vernacular language tag when opening the SelectLexicon WebView
  • platform.bible-extension/src/web-views/select-lexicon.web-view.tsx — wires up the create flow with auto-selection on success, passing existing codes for the duplicate pre-check
  • platform.bible-extension/src/web-views/index.tsx — types the SelectLexicon provider as LexiconWebViewOptions so vernacularLanguage is part of the declared contract

Implemented by Claude Sonnet 4.6 & Opus 4.8.

Devin review: https://app.devin.ai/review/sillsdev/languageforge-lexbox/pull/2394

Closes #2061

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7722fb91-8411-46d4-9820-1b4708c7850b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a lexicon creation form, localized UI strings, a creation command, MiniLCM project creation integration, project-type URL routing, and selection web-view states for creating and selecting lexicons.

Changes

Lexicon creation

Layer / File(s) Summary
Creation contracts and API routing
platform.bible-extension/src/types/lexicon.d.ts, platform.bible-extension/src/utils/fw-lite-api.ts, platform.bible-extension/src/main.ts
Adds the lexicon.createLexicon command, project-type caching, asynchronous lexicon-code resolution, project creation, and project-specific browse URLs.
Creation form and localized controls
platform.bible-extension/src/components/create-lexicon.tsx, platform.bible-extension/src/components/lexicon-combo-box.tsx, platform.bible-extension/src/types/localized-string-keys.ts, platform.bible-extension/contributions/localizedStrings.json
Adds validated name, code, and language-tag inputs, localized labels and errors, creation state handling, and a button to enter the creation flow.
Selection web-view integration
platform.bible-extension/src/web-views/select-lexicon.web-view.tsx, platform.bible-extension/src/web-views/index.tsx, platform.bible-extension/src/utils/project-manager.ts
Passes vernacular language options into the web view and coordinates lexicon loading, creation, selection, refresh, cancellation, and saved-state rendering.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: 🟨Medium

Suggested reviewers: jasonleenaylor, hahn-kev, rmunn

Poem

I’m a rabbit with a lexicon bright,
Creating new words by moonlight.
Codes hop into place,
Tags spring with grace,
And saved little projects take flight!

🚥 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 is related to the main change, since the PR adds FW Lite project/lexicon creation in the extension.
Description check ✅ Passed The description matches the changeset and summarizes the new create-lexicon flow and backend integration.
Linked Issues check ✅ Passed The PR fulfills #2061 by adding MiniLCM project creation support and wiring it into the Platform.Bible lexicon flow.
Out of Scope Changes check ✅ Passed The extra API and validation fixes are supporting changes for the creation flow, not unrelated scope creep.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pb-extension/create-project-api

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.

@imnasnainaec imnasnainaec self-assigned this Jun 30, 2026
@imnasnainaec imnasnainaec changed the title Add FwLiteApi.createProject (closes #2061) [PB Extension] Create FW project Jun 30, 2026

@myieye myieye left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Generally looks fine to me. 👍


function isValidLangTag(tag: string): boolean {
try {
return Intl.getCanonicalLocales(tag).length === 1;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What's the === 1 about? I'd expect >= 1.

Comment thread platform.bible-extension/src/components/create-lexicon.tsx
async getProjects(): Promise<IProjectModel[]> {
return (await this.fetchPath('localProjects')) as IProjectModel[];
const projects = (await this.fetchPath('localProjects')) as IProjectModel[];
projects.forEach((p) => FwLiteApi.projectTypeByCode.set(p.code, p.crdt ? 'Harmony' : 'FwData'));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If I'm not mistaken, these projects are sometimes flagged as both crdt and fwdata.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Correct, and if the project has both flags, don't we want to default to Harmony interaction?

@imnasnainaec imnasnainaec force-pushed the pb-extension/create-project-api branch from 26cae9e to 77b6250 Compare July 2, 2026 20:52
imnasnainaec added a commit that referenced this pull request Jul 10, 2026
Address review feedback on #2394:
- validateLexiconCode checked `.analysis` truthiness, but writing-system
  fields are arrays, so an empty array always passed. Require a vernacular
  writing system instead (analysis is optional; selectLexicon handles its
  absence).
- The create-lexicon form now blocks codes that already exist before the
  create round-trips, showing a localized message instead of a raw 400.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
imnasnainaec and others added 12 commits July 10, 2026 09:20
`fetchUrl` now reads the response as text before conditionally parsing
JSON, so endpoints that return an empty 200 (like DELETE entry and the
new project/create route) no longer throw a SyntaxError.

`FwLiteApi.createProject` wraps `POST /api/project/create`, which was
added in #2281 to resolve #1920. Closes #2061.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds an end-to-end flow for creating a new FW Lite CRDT project from
within the SelectLexicon dialog:
- New `lexicon.createLexicon` PAPI command wrapping FwLiteApi.createProject
- New CreateLexicon form component (name, auto-derived code, vernacular
  WS pre-filled from the Paratext project language, optional analysis WS)
- "Create new lexicon" button in LexiconComboBox opens the form
- On successful creation the new lexicon is auto-selected and the dialog
  shows the standard "Lexicon selection saved" confirmation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…key order

- fw-lite-api: add static projectTypeByCode cache (shared across FwLiteApi
  instances including EntryService) so CRDT projects created via createProject
  route through mini-lcm/Harmony/... instead of mini-lcm/FwData/...
  getProjects() populates the cache; createProject() seeds 'Harmony' immediately
  so the writingSystems call in selectLexicon succeeds without a re-fetch
- fw-lite-api: include response body text in error messages so backend
  validation failures (duplicate code, invalid lang tag, etc.) reach the logger
- create-lexicon: validate vernacular and analysis language tags with
  Intl.getCanonicalLocales() before enabling the submit button
- localized-string-keys / localizedStrings.json: restore alphabetical ordering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- deriveCode now replaces invalid characters with '-' instead of removing
  them, matching the backend SanitizeProjectCode behavior
- WebView onCreated catches selectLexicon failure: logs the error, refreshes
  the lexicon list, and falls back to the combo box so the user can manually
  select the project that was created on disk

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Svelte frontend routes FwData projects via /paratext/fwdata/{code}
and CRDT/Harmony projects via /paratext/project/{code}. The standalone
getBrowseUrl always used 'fwdata', producing a broken URL for any
Harmony lexicon.

Move getBrowseUrl into FwLiteApi as a method so it can consult the
projectTypeByCode cache and emit 'project' vs 'fwdata' accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hints the default analysis writing system on the Create new lexicon form,
per review feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Display the backend error message (e.g. duplicate code, invalid language
tag) instead of only logging it, per review feedback. Also drop the now-
pointless useCallback around handleSubmit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ping

- createLexicon now returns the backend validation message (e.g. "Project
  already exists", "not a valid IETF language tag") instead of a generic
  failure, so the create-lexicon form shows the actual reason. Also logs the
  real message rather than JSON.stringify(e), which rendered Errors as {}.
- Type the SelectLexicon WebView provider and its caller as
  LexiconWebViewOptions so vernacularLanguage is part of the declared contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cons

The in-memory projectTypeByCode map is empty after an extension restart, so a
previously created Harmony/CRDT lexicon was misrouted to the FwData endpoints
(browse, search, and entry operations all failed) until the user reopened the
lexicon selector. checkLexiconCode and getBrowseUrl now resolve the type via a
new resolveProjectType helper, which reloads the project list from the backend
on a cache miss before falling back to FwData.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
selectLexicon awaited the command but ignored its result, so onCreated and
LexiconComboBox.saveSetting showed a "saved" confirmation even if the command
returned { success: false }. Throw on a non-success result so the create flow
falls back to the combo box and the combo box surfaces its save error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback on #2394:
- validateLexiconCode checked `.analysis` truthiness, but writing-system
  fields are arrays, so an empty array always passed. Require a vernacular
  writing system instead (analysis is optional; selectLexicon handles its
  absence).
- The create-lexicon form now blocks codes that already exist before the
  create round-trips, showing a localized message instead of a raw 400.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
doesProjectMatchLangTag used Object.keys on the vernacular IWritingSystem[],
which yields index strings ('0', '1', ...) rather than language tags, so
getProjectsMatchingLanguage never matched and always fell back to all
projects. Map over the array and read each writing system's wsId instead.

Also drops a redundant comment on the lexicon-code validator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@imnasnainaec imnasnainaec force-pushed the pb-extension/create-project-api branch from ee4770c to 07ce2d1 Compare July 10, 2026 13:20
@imnasnainaec imnasnainaec marked this pull request as ready for review July 10, 2026 19:01

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

🧹 Nitpick comments (1)
platform.bible-extension/src/components/create-lexicon.tsx (1)

107-119: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add validation feedback for code pattern mismatches.

When a user manually edits the code field and types uppercase letters or invalid characters, CODE_PATTERN.test(code) fails and the submit button is disabled, but no error message is shown — only codeExists has a visible error. Consider either auto-lowercasing the input or displaying a validation message when the code fails the pattern.

♻️ Suggested fix: auto-lowercase code input
         <Input
           id="createLexiconCode"
           onChange={(e) => {
-            setCode(e.target.value);
+            setCode(e.target.value.toLowerCase());
             setCodeEdited(true);
           }}
           value={code}
         />
🤖 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 `@platform.bible-extension/src/components/create-lexicon.tsx` around lines 107
- 119, Show validation feedback when the manually edited value in the code input
fails CODE_PATTERN, alongside the existing codeExists message, and ensure the
submit-disabled condition uses the same validation state. Alternatively,
normalize input to lowercase in the createLexiconCode onChange handler before
calling setCode, while preserving setCodeEdited.
🤖 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 `@platform.bible-extension/src/components/create-lexicon.tsx`:
- Around line 107-119: Show validation feedback when the manually edited value
in the code input fails CODE_PATTERN, alongside the existing codeExists message,
and ensure the submit-disabled condition uses the same validation state.
Alternatively, normalize input to lowercase in the createLexiconCode onChange
handler before calling setCode, while preserving setCodeEdited.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 46132e95-f38d-433c-a579-dc5d00cad5b6

📥 Commits

Reviewing files that changed from the base of the PR and between b5e8ecb and 07ce2d1.

📒 Files selected for processing (10)
  • platform.bible-extension/contributions/localizedStrings.json
  • platform.bible-extension/src/components/create-lexicon.tsx
  • platform.bible-extension/src/components/lexicon-combo-box.tsx
  • platform.bible-extension/src/main.ts
  • platform.bible-extension/src/types/lexicon.d.ts
  • platform.bible-extension/src/types/localized-string-keys.ts
  • platform.bible-extension/src/utils/fw-lite-api.ts
  • platform.bible-extension/src/utils/project-manager.ts
  • platform.bible-extension/src/web-views/index.tsx
  • platform.bible-extension/src/web-views/select-lexicon.web-view.tsx

imnasnainaec and others added 2 commits July 13, 2026 08:28
Previously an invalid code (uppercase, spaces, symbols) silently
disabled Submit with no feedback, unlike the codeExists case which
already had a visible error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Auto-lowercase the code as typed (matching Lexbox's create-project
zod schema) instead of erroring on uppercase, and add Lexbox's
4-character minimum length with its own message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a new project via the MiniLCM API

2 participants