Skip to content

PT-4326: Read freely-licensed texts when no project is open - #2774

Open
katherinejensen00 wants to merge 5 commits into
mainfrom
pt-4326-free-resources-no-project
Open

katherinejensen00 wants to merge 5 commits into
mainfrom
pt-4326-free-resources-no-project

Conversation

@katherinejensen00

@katherinejensen00 katherinejensen00 commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Lets a user with no project open pick and read freely-licensed Bible texts in the two reading
panels flanking the editor, instead of staring at "No project selected." Sub-task of PT-4323 (the
interim fix for users who finish setup with no projects).

The panels already render resources project-independently — only the chosen-resource list was
project-scoped. So the core of this PR is one seam that swaps where that list comes from, and
everything downstream is unchanged.

Ships with exactly one text (WEB, Public Domain). The curated allowlist is deliberately tiny;
the licence test for this first pass is Public Domain only. Expanding it is a data change, not a
code change.

Why review this

Not part of the current epic. It unblocks PT-4323's "user finishes setup with nothing to read"
case, and it is worth reviewing now because it is what studio testing needs in order to exercise
the no-project reading flow at all.

Where to start

Read these five in order — they are the whole idea. The other 32 files are tests, strings, stories
and call-site updates.

File What it does
free-resources.const.ts The curated allowlist + why it must be hand-curated (nothing in the stack reports a licence) and how to add to it.
use-resource-reference-source.hook.ts The seam. Returns the same state shape whether the list comes from the project's PDP or an app-scoped setting, so the panels branch on which source and nothing else.
resource-panel-readiness.utils.ts Front-of-state-machine, now with a registrationRequired state. Both panels share it, so they cannot drift.
no-project-reference-list.validator.ts The write chokepoint. Refuses newly-added non-free references while letting already-stored ones survive.
navigation-target.util.ts Without this the toolbar's book/chapter control stays disabled with no project, so a chosen text is readable at exactly one reference.

Two ADRs record the decisions and the rejected alternatives:
adr-no-project-reading-choice-in-app-settings and adr-bcv-falls-back-to-projectless-editor.

Details

How the exclusion guarantee is enforced (three layers)

A non-free resource must be unreachable, not merely refused:

  1. The picker is restricted — allowedResourceIds narrows the catalog before the dialog sees
    it, so the language filter and total count stay consistent with what is selectable. It carries a
    notice, because the dialog builds its own explanation of a short list from the fetch results,
    which this narrowing does not touch.
  2. The read path filters — a UID stored while the allowlist was wider stops rendering.
  3. The validator refuses new writes — but only newly added references. Stored ones survive an
    allowlist narrowing, so a later, wider list restores them rather than having destroyed them.

With no project, locally-downloaded resources are excluded from the panel's row list entirely —
nothing filters those, so including them would bypass all three layers.

Why an app-scoped hidden setting, not web-view state

The project path persists picks in the platformScripture.textConnectionSettings PDP, which does
not exist without a project. useWebViewState is not a substitute: Simple mode never persists its
layout, so a pick would be lost on every restart — for exactly the user this exists to help. Full
reasoning and the rejected UserStateContribution alternative are in the ADR.

Registration handling, and a trap worth knowing about

A missing Paratext registration makes the catalog unreachable in a way a retry cannot fix, so that
state offers a Register button instead of Try again.

Detecting it is not obvious: getCachedResources resolves undefined rather than throwing, so the
thrown-sentinel check never sees it. The catalog hook now probes
paratextRegistration.doesUserHaveValidRegistration, but only after the fetch has already failed
— so the cold-start retry storm behind adr-registration-validity-once-per-session does not apply.

The tempting shortcut is wrong. isGetDblResourcesAvailable looks free, but
DblResourcePasswordProvider.IsPasswordAvailable returns false both for an invalid registration
and for a build with no DBL user-secrets — so acting on it tells a developer with a perfectly good
registration to go register. Noted in the code so nobody reaches for it later.

Known gaps and a CI issue this surfaced
  • extensions/package.json has no typecheck script, so npm run typecheck --workspaces --if-present silently skips every extension. That hid a real type error in this branch (caught by
    review, now fixed) and is a pre-existing CI gap worth addressing separately.
  • Unverified against a live DBL catalog. Every test mocks the allowlist, and the dev machine
    this was written on has no DBL credentials, so whether 9879dbb7cfe39e4d resolves to WEB and
    installs is exactly what studio testing should check first.
  • The 12 failing extension tests on that machine are getMarkerMenuItems is not a function from a
    stale local platform-editor, in a file this branch does not touch. Expected green in CI.

Testing

  • npm test — 1,453 passing in platform-scripture-editor; new coverage for the allowlist,
    the source seam, the readiness states, the picker restriction, and the navigation fallback
  • npm run lint — clean
  • Typecheck clean (verified with tsc -p extensions/tsconfig.json directly, given the gap above)
  • Manual verification in paratext-10-studio — not yet done; this PR exists to enable it

AI Involvement

AI-assisted throughout: implementation, tests, and docs were generated with Claude Code and reviewed
by me. Two /code-review max passes were run against the branch and their findings addressed —
including one bug that would have shipped (locally-downloaded resources bypassing the allowlist) and
a permanent spinner in the Bible Texts panel.

Risk Level

Low–Medium. Every new branch is gated behind HAS_FREE_RESOURCES, so with an empty allowlist the
panels behave exactly as before. The project-open paths are unchanged apart from shared-component
signatures. The medium half is that this is the first code to run in the no-project state, and it has
not yet been exercised against a real catalog.


This change is Reviewable

@Sebastian-ubs

Sebastian-ubs commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

This does not look quite as expected, does it?
Maybe if a user had a project earlier, that is no longer available, this work flow breaks?

Screenshot 2026-09-08 185233

@katherinejensen00

Copy link
Copy Markdown
Contributor Author

@Sebastian-ubs That doesn't look right. Can you attach a log please?

@Sebastian-ubs

Copy link
Copy Markdown
Contributor

sent to you...

@jolierabideau

Copy link
Copy Markdown
Collaborator

Review summary

Sound design, no blocking findings, and the central seam is the right shape. The gaps are at the edges: one untested enforcement layer, one broken Storybook story, and a few user-visible copy inconsistencies.

Against PT-4326's acceptance criteria

AC Status Where
1. Both panels offer free resources with no project ✅ model-text-panel.web-view.tsx, resource-text-panel.web-view.tsx (gated isNoProject && HAS_FREE_RESOURCES)
2. Selection renders and survives restart ✅ by design, unverified live Two hidden app settings + use-resource-reference-source.hook.ts
3. BCV navigation works with no project ✅ navigation-target.util.ts — confirmed pinToMainEditor is exactly interfaceMode === 'simple' (window.service-shard.ts:231), so the "Simple mode only" claim holds
4. Non-free excluded at picker / read / write ✅ all three exist; the write layer is untested resource-picker.dialog.tsx, filterFreeReferences, no-project-reference-list.validator.ts
5. Unregistered user gets an actionable message ✅ registrationRequired readiness state + Register button
6. New localization keys; %webView_resourcePanel_noProject% not repurposed ✅ That key is untouched

Scope discipline is good: Commentaries is excluded via resourceType === 'ScriptureResource', and the Text Collection grid and editor empty state are untouched.

Pattern adherence

Strong. Both ADR entries land in correct LC_ALL=C byte-order slug position (verified across all 61 slugs). papi.d.ts was regenerated, not hand-edited — the hunk is byte-identical to the source change. Validators are registered and disposed through the established main.ts promise-into-dispose-list pattern. The process-boundary split of openParatextRegistration out of free-resources.utils.ts (because the latter is imported by the extension-host validator) is exactly right, and it says so. No C# writes, no shadcn edits, no keyboard handlers, no template-shared regions, no build config touched.

Worth addressing before merge

  1. no-project-reference-list.validator.ts has no test. 83 lines of branching, and it is the write layer of this PR's own three-layer exclusion guarantee — the only one of the three untested. The subtle part is the grandfathering rule (an already-stored, no-longer-allowlisted id passes; a newly added one doesn't, matched case-insensitively). The sibling it is modeled on, resourceReferenceListValidator, is tested in extensions/src/platform-scripture/src/resource-reference-list.utils.test.ts.

  2. The new RegistrationRequired Storybook story renders empty. panel-readiness-view.component.stories.tsx reads registrationRequiredMessage and registerLabel from localizedStrings, but the hand-maintained RESOURCE_PANEL_KEYS array (lines 15-22) wasn't extended with those two keys. getLocalizedStrings (.storybook/localization.utils.ts:78-86) builds its result only from the keys array it is passed, so both args are undefined and the story shows a blank description above a blank button — exactly the drift the file's own header comment warns about. Two-line fix.

  3. The Model Text tab title has no no-project branch. model-text-panel.web-view.tsx:103-113 unconditionally sets "Model text" / "Model text: {name}", while the whole NO_PROJECT_WORDING map exists because — in its own words — "there is no model relationship to name". A no-project user sees a tab reading Model text: WEB above a body that never uses the term.

  4. Pick buttons are missing their ellipsis. "Choose a text" renders into the same pickLabel slot as "Pick model text…" / "Pick Bible texts…" and opens the same cancellable modal. Per guidelines/ellipses.mdx rule 2 it needs ….

  5. Two backward-facing comments in use-resource-reference-source.hook.ts narrate earlier in-PR implementations ("an earlier version read both unconditionally…", "Filtering here destroyed any stored entry…"). Per .claude/rules/code-quality/forward-facing-comments.md the invariant stays, the history moves to the commit message.

  6. Four strings duplicated verbatim across two key namespaces (_noProject_pick, _register, _registrationRequired, _emptyState_prompt) in both en and es. None say anything panel-specific — and this same PR establishes the alternative for %webView_resourcePanel_freeResourcesOnly_notice%, declared once and shared.

  7. navigation-target.util.ts changes behavior for an existing input beyond the toolbar. In Simple mode with no project, getNavigationTargetWebView() now emits a defined target where every consumer previously saw undefined. The added tests exercise resolveTargetWebView directly only — did you walk the other consumers of the resolved target, not just isBookChapterControlDisabled?

Minor

  • Unexplained useEffect deps in model-text-panel.component.tsx (~line 480): [usj, isBookMissing, isBlankChapter] where the body reads only usj, and the comment above still describes the old behavior. Also the one change unrelated to the stated purpose — what is it for, and why in this PR?
  • FREE_RESOURCE_IDS is a bare alias for FREE_RESOURCE_DBL_ENTRY_UIDS whose TSDoc justifies it on stability grounds the original frozen const already satisfies.
  • es hidden-setting labels ship as English with a developer note baked into the string value: "Model text chosen with no project open (oculto - sin traducir intencionalmente)". No other string in extensions/src does this, and existing hidden settings do get real translations (e.g. platformScripture.modelTexts is isHidden: true yet localized as "Textos modelo"). Prefer a real translation, with the rationale in settings.json.
  • Mixed es formality in the new strings — the new prompts use usted ("Elija…", "Registre…") while %webView_modelTextPanel_noProject_installFailedOffline% uses tú. The tú ones mirror their existing siblings exactly, so the inconsistency comes from the new usted prompts.
  • settings.json property order — the two new keys break the file's alphabetical ordering.
  • .validator.ts is a new file-type suffix; the sibling settings validator lives in *.utils.ts.
  • allowedResourceIds TSDoc (which ships to extension authors via papi.d.ts) doesn't mention that matching is case-insensitive — that's part of the contract.
  • Validator hand-rolls the DBL shape check in its first findIndex while importing isDblResourceReference a few lines below.
  • Comment density is very high across the new files — free-resources.const.ts is 70 comment lines in 73, free-resources.utils.ts 65 in 95, the seam hook 93 in 230. Much of it is genuinely load-bearing rationale, so raising it once here rather than per site.

Worth crediting

The three-layer exclusion argument is written down and each layer actually exists. The WITH_PROJECT_PICKER_OPTIONS / NO_PROJECT_PICKER_OPTIONS split closes a real bypass — locally-downloaded rows are filtered by nothing and would have been auto-selected by rows[0] — and the reasoning sits at the constant. The seam keeps one state shape for both sources, so the panels grew a branch rather than a second state machine. no-project-settings-contribution.test.ts pins the JSON defaults to CURRENT_DATA_VERSION, a gap TypeScript genuinely cannot see. And registrationRequired correctly refuses to offer a retry that cannot succeed.


AI-assisted review (Claude Code) — findings verified against the code before posting.

@katherinejensen00
katherinejensen00 force-pushed the pt-4326-free-resources-no-project branch from 5e09689 to 4d54fc2 Compare September 12, 2026 01:50
@katherinejensen00

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and addressed the review. Summary by item:

@Sebastian-ubs — panels stuck on "Loading…" after projects were removed — fixed in "Treat a project id that cannot be found as no project". A restored layout (or the last-opened-project cache, or a recents entry) could name a project that is no longer on the machine, and every project-scoped hook waited forever on a data provider that never arrives. The editor and both reading panels now resolve the container project id through useResolvedContainerProjectId, so a dead id takes the same no-project path as an absent one; the toolbar picker reports no current project instead of an error card; and the Simple-mode switch skips project ids it cannot find. Rationale and rejected alternatives (notably pruning dead ids from the layout) are in adr-dangling-project-id-resolves-to-no-project.

@jolierabideau — worth addressing before merge

  1. Validator untested — added no-project-reference-list.utils.test.ts: shape checks, the allowlist, the grandfathering rule (a stored, no-longer-allowlisted id survives; a newly added one doesn't), case-insensitive matching on both sides, and a corrupt current value.
  2. RegistrationRequired story renders empty — resolved by the rebase: main replaced the hand-maintained key array with RESOURCE_PANEL_STRING_KEYS, and the no-project keys are now in that list.
  3. Model Text tab title — with no project the tab now reads "Text" / "Text: {textName}" (new %webView_modelTextPanel_noProject_title% / _title_withResource%). Happy to take different copy if UX prefers.
  4. Missing ellipsis — "Choose a text…" / "Elegir un texto…".
  5. Backward-facing comments — rewritten to state the invariant only.
  6. Duplicated strings — collapsed into one shared set, %webView_resourcePanel_noProject_{emptyState_prompt,pick,register,registrationRequired}%, following the freeResourcesOnly_notice precedent. Both panels read them.
  7. navigation-target.util.ts consumers — walked both. The resolved target has exactly two consumers, and neither assumed a projectId:
    • Toolbar (useNavigationTargetWebView): enables the book/chapter control, reads and writes that editor's scroll group, and passes definition.projectId (here undefined) to useScrollGroupScrRef (no versification conversion), useProjectSetting(…booksPresent) (default → whole canon) and useOpenProjectBookIds.
    • Main-process platform.goTo* commands (getNavigationContext): getCurrentRef reads the scroll group unconverted, getAvailableBooks(undefined) returns the whole canon, no versification PDP is acquired so the step uses the existing bounds-unaware fallback, and writeNewRef writes the scroll group. The reference-history commands already defaulted to scroll group 0 with no target, so they are unchanged.
      Power mode is untouched (allowProjectlessEditor is only passed when pinned).

Minor

  • useEffect deps in model-text-panel.component.tsx — reverted to main's [usj]. main now hides Editorial rather than unmounting it for the content-area states, which is what those extra deps were compensating for.
  • FREE_RESOURCE_IDS alias — removed; the frozen FREE_RESOURCE_DBL_ENTRY_UIDS is used directly.
  • es hidden-setting labels — real translations now.
  • es formality — main has since moved most of the neighbouring strings to usted; the two remaining tú no-project strings now match.
  • settings.json order — alphabetical.
  • .validator.ts suffix — renamed to no-project-reference-list.utils.ts, matching resource-reference-list.utils.ts.
  • allowedResourceIds TSDoc — now says matching is case-insensitive; papi.d.ts regenerated.
  • Hand-rolled DBL shape check — now uses isDblResourceReference, plus the string checks it doesn't do.
  • Comment density — trimmed the history-narrating comments above; I've kept the rationale that explains non-obvious constraints (the allowlist's licence test and how to populate it, the three exclusion layers), since that is what the next person widening the list will need.

Rebase notes — main's PT-4424 split the Bible texts panel into a web view plus resource-text-panel.component.tsx, so the no-project render branch moved into the component (new isFreeResourceEntryPoint / onOpenRegistration props) while the data-source wiring stayed in the web view. main's DBL catalog now resolves { status: 'unavailable', reason } instead of undefined; an unregistered install arrives as notConfigured, so that is where the registration probe now runs. main's bounded retry for the picker's current-project lookup is kept, and still arms when the lookup reports the project absent. main's new resource-text-panel.web-view.test.tsx needed a projectLookup mock and a partial mock of use-effective-resource-reference-list.hook to run against this branch's hooks.

Verification — npm run typecheck and npm run lint clean; the touched Vitest suites pass (renderer: 5 files / 169 tests; extensions: 15 files / 422 tests). No C# changes on this branch.

@katherinejensen00
katherinejensen00 force-pushed the pt-4326-free-resources-no-project branch 2 times, most recently from f67ea08 to 2d3bfce Compare September 17, 2026 15:23
@jolierabideau

Copy link
Copy Markdown
Collaborator

Re-reviewed — thanks @katherinejensen00. All seven "worth addressing" items and the minor items from my earlier review are fixed or answered, and I checked each against the branch. Approved from my side.

Before merging:

  • @Sebastian-ubs — could you confirm the "stuck on Loading…" issue from your screenshot is fixed with "Treat a project id that cannot be found as no project"? I'd like to hear from you before this merges.
  • Rebase note: the branch now conflicts with main. In web-view.service-shard.ts, getMostRecentUsableProjectId on main (PT-4549: Migrate the titlebar picker onto ProjectSelector #2801) checks all recent projects at once instead of one at a time. Please keep that and run isUsableSwitchTarget on each candidate. A lookup for a missing project is slow to fail, so checking one at a time could use up the 3 s limit on a single dead entry. The Architecture-Decisions.md conflict is a union; adr-dangling-… goes before adr-dbl-… in slug order.

AI-assisted review (Claude Code)

@Sebastian-ubs

Sebastian-ubs commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

@katherinejensen00 🙂 Stuck on loading is gone.

Problems

  • A) (Select project was replaced with Home, you need to update your code)
  • B) When user has no local projects they cannot select any
  • C) The info message "Only freely available texts can be added here" is not very speaking

Likely out of scope

  • D) Resource installation still fails
  • E) For the reference panel, the user is locked into the single selected resource, even with no project selected

Details...

B) When the user has no projects locally, (e.g. because they decided not to sync, or deleted them, or they did not have any access to them on the s/r server earlier but now they have, ..)
they cannot select any - the dropdown is disabled and shows a disallowed cursor.

image

C) The info message "Only freely available texts can be added here" is not very speaking

image

D+E) On clicking to download WEB produced "Resource could not be installed - Try again" for me. Reopening Paratext loaded it. Deleting the resource, still got me stuck with the only resource that I have selected
image

Requested changes

  1. The "No projects" state should never exist. Instead it should always be "Select project", always be enabled, never show a disallowed cursor.

    • For this state keeping the text muted is good, but hovering should make it text-foreground to indicate interactivity.
    • Wen there are no entries in the dropdown, clicking "Select project" should bring up Home (that recently replaced the "Select project dialog") immediately (not the empty dropdown).
  2. The project trigger should always (when a project is selected or not) have a hover:bg-muted background (like the BCV control) to indicate interactivity.

  3. For the info message guide the user to what they can do. E.g.

    "Freely available resources are available to you. Register with an organization to access more resources. [Register]"

    Where [Register] is a button that opens the "Account" dialog.

  4. Check for follow up for D + E. Is this the lock-in related to the change here? Should "No project" maybe not remember the resource selection?

@Sebastian-ubs

Copy link
Copy Markdown
Contributor

D and E even happen when on production servers.

  • F) No other resources than WEB are listed. Should WEB really be the only one available?
image

@katherinejensen00
katherinejensen00 force-pushed the pt-4326-free-resources-no-project branch from 2d3bfce to 400d27f Compare September 24, 2026 16:01
@katherinejensen00

Copy link
Copy Markdown
Contributor Author

@jolierabideau thanks for the re-review. The branch is now rebased onto current main (732db429d72) and has no conflicts.

Conflict resolutions

  • web-view.service-shard.ts getMostRecentUsableProjectId: kept PT-4549: Migrate the titlebar picker onto ProjectSelector #2801's concurrent Promise.all check and picked the first usable result in recents order, as you asked. It now runs isUsableSwitchTarget on each candidate, where main ran resolveProjectIsPublished. isUsableSwitchTarget rejects both a published resource and an id that cannot be found. That means a dead entry at the head of the list costs about the same as one lookup and no longer uses up the 3 s limit by itself. I updated the comment on that line to say so. main's resolveProjectIsPublished is gone, because isUsableSwitchTarget replaces it.
  • web-view.service-shard.test.ts: kept main's new test (all candidates in flight at once, a gated check) next to this branch's renamed "usable target" timeout test. Both pass against the combined implementation.
  • Architecture-Decisions.md: took the union by slug, in the order adr-dangling-project-id-resolves-to-no-project → adr-dbl-cache-recompute-on-read → adr-dbl-install-status-from-backend → adr-decision-log-sorted-insertion. I checked the full list with LC_ALL=C sort -c and found no duplicate slugs.
  • resource-picker.dialog.test.tsx: main's RESOURCE_PICKER_DIALOG registration block and this branch's allowedResourceIds restriction block are both kept.

Verification after the rebase: npm run typecheck and npm run lint are clean (0 errors). Regenerating papi.d.ts produced no changes. Renderer tests: 4 touched files, 157 tests passing. platform-scripture-editor extension suite: 102 files, 1779 tests passing.

@Sebastian-ubs: when you have a moment, could you confirm the "stuck on Loading…" case from your screenshot is fixed on the current branch?

@katherinejensen00

katherinejensen00 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor Author

@Sebastian-ubs thanks for testing again, and glad the stuck-on-Loading problem is gone.

First, why A and B happened: the build you tried was from before my local rebase was pushed. It still had the old Select-based project trigger, which disabled itself whenever the list was empty, and the old "Select project" dialog. The branch is now rebased onto current main (so it includes #2801's ProjectSelector trigger and #2831's Home routing) and pushed.

Requested changes

  1. Never show "No projects"; always "Select project"; never disabled. Done. The trigger always says "Select project" and stays enabled once the first load finishes. The placeholder is muted and turns text-foreground on hover or when open. With no projects on this computer, clicking it now opens Home (projects view) right away instead of an empty dropdown. This is a new opt-in ProjectSelector prop, shouldRunFooterActionWhenEmpty, which runs the existing "More projects…" footer action from the trigger. It works from the keyboard too, and a screen reader still hears "Select project, no projects on this computer".
  2. Always hover:bg-muted. The trigger is a ghost button, like the book/chapter control, and the ghost variant already includes hover:bg-muted. I also dropped the leftover bg-transparent from the old SelectTrigger, so nothing competes with it, whether or not a project is selected.
  3. A notice that says what the user can do. The picker now reads "You can use freely available texts without a project. Register with an organization to access more resources." and has a Register Paratext button that opens the Account (registration) dialog. The picker closes first, so the Account dialog doesn't end up behind the modal. The plumbing is generic: ResourcePickerDialog has a new noticeAction, and the dialog options have noticeCommandLabel / noticeCommand, copying the notification service's clickCommand pair.
    One copy question for you or UX: in this entry point the list stays limited to free texts even after registering with an organization, because what widens it is opening a project. So "Register … to access more resources" is true in general, but it won't change this picker right away. I kept your wording. Should it also mention opening a project?

D, E, F (probably out of scope)

  1. D: install fails with "Try again". This is PT-4588 (in review right now): a stale installed flag, and "already installed" reported as a failure. It happens the same way with a project open, as you found on production, so nothing in this PR causes it.
    E: stuck with one resource. After a failed install the panel shows only the error and Try again, with no way to pick another resource. That's PT-4718, and again it's the same with a project open. With no project, there's also nothing else to pick yet (see F), so even a working dropdown would only offer WEB. PT-4780 is the related "let the user change the reference text" story.
    Should "No project" forget the selection? I'd keep it: the ticket asks for the choice to survive a restart. The lock-in comes from the failure state (PT-4718), not from remembering, and forgetting would make the user pick again after every restart without fixing the failed-install case.
    I don't think we need a new ticket on top of these three. If you'd rather track "no-project panel after a failed install" on its own, I'm happy to file one.
  2. F: only WEB. That's deliberate for now. The no-project picker offers only a hand-curated allowlist (FREE_RESOURCE_DBL_ENTRY_UIDS) because nothing in the stack reports a licence. The first pass is Public Domain only, and WEB is the only entry harvested so far: matching open.bible's list to DBL entry ids needs a registered catalog dump. The steps are documented at the constant. Adding more entries doesn't depend on this PR and could be a small follow-up. Should I file it?

@Sebastian-ubs

Copy link
Copy Markdown
Contributor

One copy question for you or UX: in this entry point the list stays limited to free texts even after registering with an organization, because what widens it is opening a project. So "Register … to access more resources" is true in general, but it won't change this picker right away. I kept your wording. Should it also mention opening a project?

At best we should quickly meet tomorrow to talk about what would be a better / best action for this. I think I got a bit offtrack with the register suggestion, but also not sure, why a project not open would be the gatekeeper to only list freely available resources.

@katherinejensen00
katherinejensen00 enabled auto-merge (squash) September 24, 2026 21:17
@katherinejensen00
katherinejensen00 force-pushed the pt-4326-free-resources-no-project branch from 7bc6883 to 8c02615 Compare September 24, 2026 22:00
@Sebastian-ubs

Sebastian-ubs commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Will this also fix the following problems? (latest available build 22 Sept from main, not this branch)

  • Even with 1 project the dropdown is disabled.

  • Tooltip flickering when hovering over it

image

@katherinejensen00

Copy link
Copy Markdown
Contributor Author

@Sebastian-ubs Since this is dealing with viewing open license resources, I don't think it will affect the project dropdown.

@Sebastian-ubs

Copy link
Copy Markdown
Contributor
  • Even with 1 project the dropdown is disabled.

This either works on main and is newly fixed there or is wrongly introduced on this branch.
Main has
image

katherinejensen00 and others added 3 commits September 29, 2026 12:56
Lets a user with no project open pick and read freely-licensed Bible texts in
the two reading panels flanking the editor, instead of "No project selected."

The panels already render resources project-independently; only the chosen-
resource list was project-scoped. `useResourceReferenceSource` is the seam that
swaps where that list comes from - the project's text-connection PDP when a
project is open, an app-scoped hidden setting when none is - returning the same
state shape either way, so everything downstream is unchanged.

Ships with one curated text (WEB, Public Domain). The licence test for this
first pass is Public Domain only; expanding it is a data change, not a code
change. With an empty allowlist `HAS_FREE_RESOURCES` switches the whole entry
point off, so the panels behave exactly as before.

A non-free resource is unreachable rather than merely refused, in three layers:
the picker is restricted before the dialog sees the catalog, the read path
filters what is shown, and the setting validator refuses newly added non-free
references while letting stored ones survive an allowlist narrowing. Locally
downloaded resources are excluded entirely when there is no project, since
nothing filters those.

Navigation also had to change: without a project the toolbar's book/chapter
control resolved no target and disabled, leaving a chosen text readable at
exactly one reference. BCV navigation now falls back to a project-less editor,
in Simple mode only.

Decisions and rejected alternatives are recorded in
adr-no-project-reading-choice-in-app-settings and
adr-bcv-falls-back-to-projectless-editor.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
A restored layout, a recents entry, or the last-opened-project cache can
name a project that is not on this machine. The reading panels then sat on
their loading state forever, the editor showed a blank pane, and the
toolbar picker an error card the user could not act on. Reported on the PR
by a tester who emptied their projects folder.

- useResolvedContainerProjectId resolves the container id to undefined
  once getMetadataForProject rejects; the editor and both reading panels
  read the resolved id, never the raw prop.
- The project picker reports no current project for an id it cannot
  resolve, and keeps its error card for a metadata fetch that itself failed.
- The Simple-mode switch confirms the cached project before building a
  layout around it (clearing the cache on a definite miss), and the recents
  walk skips candidates it cannot find.
- Record the decision in the architecture-decision log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fd86WHD1RQo5tE5xkANftn
- Test the no-project settings validator (shape, allowlist, grandfathering
  of stored ids, case-insensitive matching) and rename it to
  no-project-reference-list.utils.ts to match its sibling; use
  isDblResourceReference for the shape check.
- Share one set of no-project strings between both panels
  (%webView_resourcePanel_noProject_*%) and give the pick button its
  ellipsis.
- Title the Model Text tab "Text" with no project open, matching the
  panel body.
- Restore main's [usj] deps on the model text panel's editor feed.
- Drop the FREE_RESOURCE_IDS alias for the frozen allowlist.
- Translate the hidden es setting labels, bring the remaining no-project
  es strings to usted, and order settings.json alphabetically.
- Note case-insensitive matching in allowedResourceIds' TSDoc and
  regenerate papi.d.ts.
- Rewrite comments that narrated earlier implementations.
- Mock projectLookup and keep the real mergeResourceReferenceLists in the
  resource panel web view test, which the rebase onto main exposed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fd86WHD1RQo5tE5xkANftn
katherinejensen00 and others added 2 commits September 29, 2026 13:05
…n the free-text picker

Responds to review of the no-project reading panels.

Toolbar project picker:
- Always reads "Select project", never "No projects".
- With no projects on this computer, activating the trigger opens Home
  directly instead of a popover holding only "More projects…". This is a
  new opt-in ProjectSelector prop, shouldRunFooterActionWhenEmpty.
- The placeholder is muted until hover or open. The trigger no longer sets
  bg-transparent, so the ghost variant's hover:bg-muted shows, the same as
  the book/chapter control.

Free-text resource picker:
- The notice now says what the user can do: "You can use freely available
  texts without a project. Register with an organization to access more
  resources."
- It carries a Register button that opens the registration ("Account")
  dialog. ResourcePickerDialog gains noticeAction; the dialog options gain
  noticeCommandLabel / noticeCommand, mirroring the notification service's
  clickCommand pair. The picker is cancelled before the command runs, so
  the registration dialog is not left behind the modal.

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

Rebasing onto main brought in #2859's third install-failure message,
"The model text is installed but couldn't be opened." The no-project
panel swaps every "model text" message for a neutral one, so the rebase
gave this message a no-project variant too ("The text is installed but
couldn't be opened.", en and es). This pins that swap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@katherinejensen00
katherinejensen00 force-pushed the pt-4326-free-resources-no-project branch from 8c02615 to 9372d7b Compare September 29, 2026 20:32
@katherinejensen00

Copy link
Copy Markdown
Contributor Author

@Sebastian-ubs You're right that those two problems were real, but this branch didn't introduce them, and they're already fixed on main.

What happened

On 22 Sept, the project dropdown in the title bar was swapped out for a new one (#2801). Your "22 Sept main" build must have been made just before that change, so it still had the old dropdown. You can tell from the label: the old dropdown showed names as "World English Bible USA … (WEB)", and the new one shows "WEB - World English Bible USA …".

The old dropdown had both bugs you saw:

  • Disabled with one project: it greyed itself out whenever it didn't think it had anything to list, even while it was showing WEB.
  • Tooltip flicker: the name inside it kept its own hover tooltip even when the dropdown was greyed out, which is what flickered.

Where things stand now

  • main: has the new dropdown. It's clickable with one project, and it doesn't flicker. That's what your second screenshot shows.
  • This branch: is up to date with main, so it has the same new dropdown. I didn't change when it's greyed out; that only happens for a moment while the app first loads its project list. The one thing this branch changes: if there are no projects on the computer at all, clicking "Select project" takes you straight to Home instead of opening an empty list.

If you try a build from after 22 Sept (latest main or this branch), both problems should be gone. If you still see either one, please send me a screenshot and I'll look again.

This branch has not been deployed

No deployments
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.

3 participants