Skip to content

Commit 3d57a97

Browse files
Trimmed down AGENTS.md to where it was before post-model-change update
1 parent a1648f5 commit 3d57a97

1 file changed

Lines changed: 23 additions & 60 deletions

File tree

AGENTS.md

Lines changed: 23 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,18 @@ This is a **Platform.Bible extension** for interlinear Bible text alignment. Pla
3131

3232
[src/main.ts](src/main.ts) — called by Platform.Bible on activation. Exports two lifecycle functions:
3333

34-
- `activate(context)` — stores the `ExecutionToken`, registers the `interlinearizer.mainWebView` WebView provider, the command handlers below, and the `onDidOpenWebView` / `onDidCloseWebView` subscriptions. All registrations are added to `context.registrations` so the platform disposes them on deactivation.
34+
- `activate(context)` — stores the `ExecutionToken`, registers the `interlinearizer.mainWebView` WebView provider, command handlers, and `onDidOpenWebView` / `onDidCloseWebView` subscriptions. All registrations are added to `context.registrations` so the platform disposes them on deactivation.
3535
- `deactivate()` — clears `openWebViewsByProject` and returns `true`.
3636

3737
`openWebViewsByProject` (`Map<string, string>`) tracks one open WebView ID per project to prevent duplicates; reopening an already-open project brings that tab to front via the `existingId` option.
3838

39-
Registered commands:
40-
41-
- `interlinearizer.openForWebView` — opens the Interlinearizer for the WebView's project (or a picker if no ID is given).
42-
- `interlinearizer.createProject` — backend handler that delegates to [projectStorage.createProject](src/services/projectStorage.ts); returns a JSON-serialized `InterlinearProject` or `undefined` on failure.
43-
- `interlinearizer.getProjectsForSource` — returns a JSON-stringified `InterlinearProject[]` filtered by source project.
44-
- `interlinearizer.updateProjectMetadata` — updates name/description/analysisLanguages/targetProjectId for a project.
45-
- `interlinearizer.deleteProject` — deletes a project; no-ops silently when the ID is unknown.
46-
- `interlinearizer.openSelectProjectModal`, `interlinearizer.openNewProjectModal`, `interlinearizer.openProjectInfoModal` — registered server-side as no-op handlers so the platform menu system knows about them; the actual behavior lives in the WebView, which listens for the matching menu-item activation.
47-
4839
### WebView UI
4940

50-
[src/interlinearizer.web-view.tsx](src/interlinearizer.web-view.tsx) is the entry point that the PAPI host renders inside its WebView iframe. It just delegates to [InterlinearizerLoader](src/components/InterlinearizerLoader.tsx) when a `projectId` is present. `useWebViewScrollGroupScrRef` and `useWebViewState` are **props injected by the PAPI host** (not hook imports).
41+
[src/interlinearizer.web-view.tsx](src/interlinearizer.web-view.tsx) entry point rendered inside Platform.Bible's WebView iframe; delegates to [InterlinearizerLoader](src/components/InterlinearizerLoader.tsx) when a `projectId` is present. `useWebViewScrollGroupScrRef` and `useWebViewState` are **props injected by the PAPI host** (not hook imports).
5142

52-
[InterlinearizerLoader](src/components/InterlinearizerLoader.tsx) is the real top of the React tree: it owns modal state, persists the active interlinear project via `useWebViewState`, fetches and tokenizes book data, renders the `TabToolbar` + `ScriptureNavControls` + `ContinuousScrollToggle`, and routes top-menu commands (`openSelectProjectModal` / `openNewProjectModal` / `openProjectInfoModal`) to the appropriate modal. The "View Project Info" item is filtered out of the menu when no project is active.
43+
[InterlinearizerLoader](src/components/InterlinearizerLoader.tsx) real top of the React tree: owns modal state, persists the active interlinear project, fetches and tokenizes book data, and routes top-menu commands to the appropriate modal.
5344

54-
[Interlinearizer](src/components/Interlinearizer.tsx) renders the actual interlinear view: an optional `ContinuousView` strip above a list of `SegmentView`s for the current chapter.
45+
[Interlinearizer](src/components/Interlinearizer.tsx) renders the interlinear view from the loaded book data.
5546

5647
The WebView is injected into the main bundle via Webpack's `?inline` query:
5748

@@ -68,66 +59,38 @@ The WebView root component is assigned to `globalThis.webViewComponent` (not exp
6859

6960
### Project modals
7061

71-
[src/components/ProjectModals.tsx](src/components/ProjectModals.tsx) is the single mount point for all project-related dialogs; it switches between `'select' | 'create' | 'metadata' | 'none'` based on a `modal` prop owned by `InterlinearizerLoader`:
72-
73-
- [SelectInterlinearProjectModal](src/components/SelectInterlinearProjectModal.tsx) — lists existing projects for the source via `interlinearizer.getProjectsForSource`, with an info icon that opens the metadata modal.
74-
- [CreateProjectModal](src/components/CreateProjectModal.tsx) — collects name, description, and analysis-language tags, then calls `interlinearizer.createProject`.
75-
- [ProjectMetadataModal](src/components/ProjectMetadataModal.tsx) — edits / deletes an existing project via `interlinearizer.updateProjectMetadata` and `interlinearizer.deleteProject`.
76-
77-
The active project is persisted in WebView state under the `activeProject` key so it survives tab restores. The `isInterlinearProjectSummary` type guard in `SelectInterlinearProjectModal.tsx` validates JSON parsed from backend commands without `as` casts.
62+
[src/components/ProjectModals.tsx](src/components/ProjectModals.tsx) — single mount point for all project-related dialogs, switching between `'select' | 'create' | 'metadata' | 'none'` states. The three modal components ([SelectInterlinearProjectModal](src/components/SelectInterlinearProjectModal.tsx), [CreateProjectModal](src/components/CreateProjectModal.tsx), [ProjectMetadataModal](src/components/ProjectMetadataModal.tsx)) call backend commands to list, create, update, and delete projects.
7863

7964
### Project storage
8065

81-
[src/services/projectStorage.ts](src/services/projectStorage.ts) owns all `papi.storage` reads and writes for interlinearizer projects:
82-
83-
- Projects are persisted under the `project:{uuid}` key; the ordered list of all UUIDs lives at `projectIds`.
84-
- Two serialization queues prevent interleaved read-modify-write races: `indexQueue` guards the `projectIds` index and a per-project `projectQueues` map guards each project's record. `createProject` rolls the project write back when the index update fails.
85-
- ENOENT (`isNotFound`) is treated as "key has never been written" rather than an error — used for both project reads and the initial-empty-index case.
86-
- Tests must call `resetQueuesForTesting()` between tests because module state is not cleared by `resetMocks`.
66+
[src/services/projectStorage.ts](src/services/projectStorage.ts) — owns all `papi.storage` reads and writes for interlinearizer projects. Two serialization queues prevent interleaved read-modify-write races. Tests must call `resetQueuesForTesting()` between tests because module state is not cleared by `resetMocks`.
8767

8868
### Styling
8969

9070
All UI uses Tailwind CSS (via `src/tailwind.css`). Every Tailwind class is prefixed `tw:` to avoid collisions with Platform.Bible's own styles (configured in `tailwind.config.ts`). For modifier variants the prefix comes first: `tw:hover:px-3`, not `hover:tw-px-3`.
9171

92-
### Hooks
93-
94-
[src/hooks/useInterlinearizerBookData.ts](src/hooks/useInterlinearizerBookData.ts) — orchestrates the per-project book pipeline. Reads USJ via `useProjectData('platformScripture.USJ_Book', projectId)` and the writing system via `useProjectSetting('platform.languageTag', ...)`, runs them through `extractBookFromUsj` and `tokenizeBook`, and returns `{ book, chapterSegments, isLoading, bookError, tokenizeError }`. The hook only depends on `scrRef.book` (not chapter/verse) for loading; chapter scoping happens during filtering.
95-
96-
[src/hooks/useOptimisticBooleanSetting.ts](src/hooks/useOptimisticBooleanSetting.ts) — wraps `useProjectSetting` with optimistic UI: a toggle update is shown immediately, the platform's confirmation is ignored for `TIMEOUT_MS` (15s) so the toggle does not visibly bounce, and if the platform never confirms the optimistic value persists rather than reverting.
97-
9872
### Parser pipeline
9973

10074
Data flows from Platform.Bible's USJ (Unified Scripture JSON) format through two stages:
10175

102-
1. [src/parsers/papi/usjBookExtractor.ts](src/parsers/papi/usjBookExtractor.ts)walks USJ nodes (book / chapter / verse / para / note) into a `RawBook` (`bookCode`, `writingSystem`, `contentHash`, `verses`). Heading-class `para` markers are dropped so their text never bleeds into the verse baseline; `note` content is also skipped. The `contentHash` is an FNV-1a 32-bit hash of a stably-stringified `usj.content`, used as `Book.textVersion` to detect baseline drift.
103-
2. [src/parsers/papi/bookTokenizer.ts](src/parsers/papi/bookTokenizer.ts) — segments and tokenizes the book into `Segment`/`Token` structures. The tokenizer regex uses Unicode property classes (`\p{L}\p{N}\p{M}\p{Join_Control}`), absorbs apostrophes/right-single-quotes at word edges (for languages where they mark phonemic glottal stops), and treats `'`, `-`, Unicode dashes, and `` as word-internal joiners only when surrounded by word characters. Whitespace is not tokenized; the invariant `Segment.baselineText.slice(charStart, charEnd) === Token.surfaceText` is preserved.
76+
1. [src/parsers/papi/usjBookExtractor.ts](src/parsers/papi/usjBookExtractor.ts)converts USJ to the internal `RawBook` type
77+
2. [src/parsers/papi/bookTokenizer.ts](src/parsers/papi/bookTokenizer.ts) — segments and tokenizes the book into `Segment`/`Token` structures with character offsets
10478

105-
[src/parsers/pt9/interlinearXmlParser.ts](src/parsers/pt9/interlinearXmlParser.ts) — separately parses Paratext 9 interlinear XML via `fast-xml-parser`. Strict mode for clusters (required `Range` with non-negative integer `Index`/`Length`; lexemes require `Id`), lenient for punctuation (entries with missing/invalid `Range` are silently filtered). The XML schema is documented in [pt9-xml.md](src/parsers/pt9/pt9-xml.md). The exported `InterlinearXmlParser` class holds one configured `XMLParser` — reuse a single instance across `parse()` calls.
79+
[src/parsers/pt9/interlinearXmlParser.ts](src/parsers/pt9/interlinearXmlParser.ts) — separately parses Paratext 9 interlinear XML into the alignment model. The XML schema is documented in [pt9-xml.md](src/parsers/pt9/pt9-xml.md).
10680

10781
### Data model ([src/types/interlinearizer.d.ts](src/types/interlinearizer.d.ts))
10882

109-
The file declares two ambient modules:
110-
111-
- `papi-shared-types` — augments `ProjectSettingTypes` (`interlinearizer.continuousScroll: boolean`) and `CommandHandlers` (the seven interlinearizer commands described above) so Platform.Bible's typed APIs know about them.
112-
- `interlinearizer` — the project's domain types.
113-
114-
The core domain types are:
115-
116-
- `InterlinearProject` — persisted envelope: id, createdAt, optional name/description, `sourceProjectId`, optional `targetProjectId`, `analysisLanguages`, `analysis: TextAnalysis`, and `links?: AlignmentLink[]`. Only this is serialized to storage; the `Book` hierarchy is rebuilt from USJ on each load.
117-
- `ActiveProject` — runtime pairing of `project: InterlinearProject` with reconstructed `source: Book[]` and optional `target: Book[]`.
118-
- `Book → Segment → Token` — the text hierarchy.
119-
- `TextAnalysis` — flat analysis layer keyed by id (does **not** mirror the text hierarchy). Holds `segmentAnalyses`, `tokenAnalyses`, `phraseAnalyses`, plus a `*Links` array for each that attaches an analysis record to one or more text-layer targets.
120-
- `TokenAnalysis / MorphemeAnalysis` — parse and 1:1 glosses; multiple analyses per token are allowed, distinguished by `status`.
121-
- `AlignmentLink` — directional links between source and target endpoints.
122-
- `AlignmentEndpoint` — has either token-level or morpheme-level specificity via the optional `morphemeLink` field (when present, both `tokenAnalysisId` and `morphemeId` are required).
123-
- `EntryRef` / `SenseRef` / `AllomorphRef` / `GrammarRef` — references to the Lexicon extension. The file documents current gaps in `IEntryService` that the Lexicon extension is expected to close.
83+
The core types are:
12484

125-
Key invariants:
85+
- `InterlinearProject` — persisted envelope: id, createdAt, optional name/description, `sourceProjectId`, optional `targetProjectId`, `analysisLanguages`, `analysis: TextAnalysis`, and optional `links`. Only this is serialized to storage; the `Book` hierarchy is rebuilt from USJ on each load.
86+
- `ActiveProject` — runtime pairing of `project: InterlinearProject` with reconstructed `source` and optional `target` books.
87+
- `Book → Segment → Token` — the text hierarchy
88+
- `TextAnalysis` — flat analysis layer keyed by id (does **not** mirror text hierarchy)
89+
- `TokenAnalysis / MorphemeAnalysis` — parse and 1:1 glosses; multiple analyses per token are allowed, distinguished by `status`
90+
- `AlignmentLink` — directional links between source and target endpoints
91+
- `AlignmentEndpoint` — has either token-level or morpheme-level specificity
12692

127-
- `Segment.baselineText.slice(charStart, charEnd) === Token.surfaceText`.
128-
- At most one linked `TokenAnalysisLink` per token may have `status: 'approved'`; same for `SegmentAnalysisLink` per segment and for `PhraseAnalysisLink` per token covered.
129-
- `MultiString` values are keyed by BCP 47 tags.
130-
- `TokenSnapshot.surfaceText` is the drift-detection mechanism: when it no longer matches the current `Token.surfaceText`, consumers flip the link's `status` to `'stale'`.
93+
Key invariants: `Segment.baselineText.slice(charStart, charEnd) === Token.surfaceText`; at most one linked analysis per token/segment may have `status: 'approved'`. `MultiString` values are keyed by BCP 47 tags. `TokenSnapshot.surfaceText` detects drift when baseline text changes.
13194

13295
### TypeScript path aliases
13396

@@ -153,13 +116,13 @@ Key semantic properties of the mock setup:
153116
Mock files:
154117

155118
- **`__mocks__/fileMock.ts`** — Stub static asset imports.
156-
- **`__mocks__/lucide-react.tsx`** — Stubs the icon components used in modals (`Info`, `Trash2`).
157-
- **`__mocks__/papi-backend.ts`** — Mocks with jest fns including `commands`, `dialogs`, `notifications`, `storage`, `webViewProviders`, and `webViews`. Re-exports internal jest fns on the default export as `__mock*` properties (e.g., `papi.__mockRegisterCommand`, `papi.__mockReadUserData`) so tests can assert on them without re-importing. See file for the full list.
119+
- **`__mocks__/lucide-react.tsx`** — Stubs icon components used in modals.
120+
- **`__mocks__/papi-backend.ts`** — Mocks with jest fns. Re-exports internal jest fns on the default export as `__mock*` properties (e.g., `papi.__mockRegisterCommand`) so tests can assert on them without re-importing. See file for full list.
158121
- **`__mocks__/papi-core.ts`** — Empty module; exists only for module resolution since `@papi/core` is types-only at runtime.
159122
- **`__mocks__/papi-frontend.ts`** — Stubs `logger` (debug/error/info/warn as jest fns) and `papi.commands.sendCommand` / `papi.notifications.send`.
160-
- **`__mocks__/papi-frontend-react.ts`** — Stubs PAPI React hooks (`useData`, `useProjectData`, `useProjectSetting`, `useLocalizedStrings`, `useRecentScriptureRefs`).
161-
- **`__mocks__/platform-bible-react.tsx`** — Stubs components (`TabToolbar`, `BookChapterControl`, `ScrollGroupSelector`, `Button`, `Switch`, `Label`) with appropriate `data-testid` attributes. See file for test IDs.
162-
- **`__mocks__/platform-bible-utils.ts`** — Stubs util functions including `isPlatformError`.
123+
- **`__mocks__/papi-frontend-react.ts`** — Stubs PAPI React hooks.
124+
- **`__mocks__/platform-bible-react.tsx`** — Stubs components with appropriate `data-testid` attributes. See file for test IDs.
125+
- **`__mocks__/platform-bible-utils.ts`** — Stubs util functions.
163126
- **`__mocks__/styleInlineMock.ts`** and **`__mocks__/styleMock.ts`** — Stub `.scss?inline` and `.scss`.
164127
- **`__mocks__/web-view-inline.ts`** — Stubs `*.web-view?inline` imports as a null-returning React component.
165128
- **`src/__tests__/test-helpers.ts`** — Exports `createTestActivationContext()` for testing `activate()` without type assertions.

0 commit comments

Comments
 (0)