Skip to content

Token Morphosyntactic Analysis UI#104

Open
alex-rawlings-yyc wants to merge 14 commits into
mainfrom
token-analysis
Open

Token Morphosyntactic Analysis UI#104
alex-rawlings-yyc wants to merge 14 commits into
mainfrom
token-analysis

Conversation

@alex-rawlings-yyc

@alex-rawlings-yyc alex-rawlings-yyc commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This change is Reviewable

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a “Show morphology” toggle to the View Options dropdown (persisted as a project setting). When enabled, word tokens show morpheme breakdowns and per-morpheme glosses beneath the token gloss, with morpheme add/edit/delete via a popover.
    • Toggling morphology re-centers the focused view.
  • Documentation

    • Added reviewer guidance for an intentional markup/accessibility exception in the morphology UI.
  • Tests

    • Expanded coverage for morphology rendering, focus/rerender behavior, and morpheme/gloss editing.
  • Chores

    • Updated English localization strings for morphology, and extended the spelling dictionary.

@alex-rawlings-yyc alex-rawlings-yyc self-assigned this Jun 12, 2026
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

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

This PR introduces a morphology display toggle that allows users to view and edit morpheme breakdowns and per-morpheme glosses alongside token-level glosses. It extends Redux state with morpheme write/delete/gloss operations, creates reusable morpheme editing components, threads the toggle through view layers to control rendering, and updates focus routing to skip morpheme gloss inputs when the phrase box is clicked.

Changes

Morphology Feature Implementation

Layer / File(s) Summary
Type contracts, settings, localization, mocks, and documentation
src/types/interlinearizer.d.ts, src/types/view-options.ts, contributions/projectSettings.json, contributions/localizedStrings.json, cspell.json, __mocks__/platform-bible-react.tsx, src/components/__mocks__/AnalysisStore.tsx, REVIEW.md
ProjectSettingTypes and MorphemeAnalysis types extended with showMorphology setting and morpheme-level gloss field; new ViewOptions type bundles display toggles; project setting metadata and localized UI labels added; mock popover components (Popover, PopoverAnchor, PopoverContent) and AnalysisStore mock hooks support test isolation; spelling dictionary updated; reviewer guidance documented for button-in-label markup exception.
Redux morpheme write/delete/gloss reducers and selectors
src/store/analysisSlice.ts
writeMorphemes, deleteMorphemes, and writeMorphemeGloss reducers manage morpheme state with ID preservation for unchanged forms and gloss recovery by form matching; internal helpers resolve approved token-analysis links and detect empty analyses; orphaned approved-link repair and empty-record deletion logic; writeGloss refactored to use new helpers; selectAnalysisLanguage exported; selectApprovedMorphemes selector returns stable empty-array fallback when absent.
Redux morpheme reducer and selector test coverage
src/__tests__/store/analysisSlice.test.ts
Tests validate writeMorphemes creation, form-change suppression, reduplication with distinct glosses, orphaned-link replacement, surface-text refresh on both analysis and link, and ID preservation/generation; deleteMorphemes tests verify removal with gloss/metadata preservation logic; writeMorphemeGloss tests verify targeted updates and no-op behavior; selectApprovedMorphemes tests verify stability and empty-array handling; writeGloss tests extended with surface-text refresh and deletion semantics.
AnalysisStore morpheme read and dispatch hooks
src/components/AnalysisStore.tsx
useMorphemes and useAnalysisLanguage hooks read from Redux; useMorphemeBreakdownDispatch, useMorphemeDeleteDispatch, useMorphemeGlossDispatch dispatch Redux actions then call onSave with post-dispatch TextAnalysis snapshot, bypassing onGlossChange spy. Internal helpers useRequiredCallbacks and useAnalysisSave centralize provider validation and dispatch-then-save patterns; existing hooks refactored to use shared helpers.
AnalysisStore morpheme hook tests
src/__tests__/components/AnalysisStore.test.tsx
Tests verify read operations return appropriate values within provider, dispatch hooks trigger Redux actions and onSave callbacks with updated state, all hooks throw when used outside AnalysisStoreProvider, and dispatch includes expected morpheme state in saved TextAnalysis.
Morpheme editor UI components
src/components/MorphemeEditor.tsx
MorphemeBreakdownPopover renders autofocused modal with internal draft state seeded from initialValue, Enter commit/Escape dismiss, outside-click save-vs-dismiss based on trimmed equality, conditional Delete button, and event-propagation suppression. MorphemeGlossInput renders per-morpheme gloss input with blur-based dispatch and disabled-state behavior.
Morpheme editor component tests
src/__tests__/components/MorphemeEditor.test.tsx
Tests verify popover autofocus, initial value rendering, Done commit/dismiss rules for unchanged/empty/whitespace text, keyboard flows, outside-interaction behavior via sentinel, Delete rendering/action; gloss input tests verify initialization from analysis language, blur dispatch when draft differs, and disabled-state suppression of dispatch.
TokenChip morphology UI
src/components/TokenChip.tsx
TokenChip loads morphemes and analysis language when showMorphology enabled; renders morpheme trigger button showing existing forms or surface text, MorphemeBreakdownPopover modal, per-morpheme MorphemeGlossInput fields, and conditional delete; splits popover input on whitespace and dispatches morpheme write; closes popover when showMorphology toggles off; updates label focus routing to ignore click origins from input/button descendants.
TokenChip morphology UI tests
src/__tests__/components/TokenChip.test.tsx
Jest mocks for MorphemeEditor provide test stubs; tests verify conditional button rendering, popover open/close and disabled-state behavior, morpheme form/gloss input rendering, dispatching save/delete with whitespace suppression, mouse-down/focus routing with main gloss precedence, popover state non-persistence, and close via callback.
View hierarchy prop threading
src/components/Interlinearizer.tsx, src/components/ContinuousView.tsx, src/components/SegmentListView.tsx, src/components/SegmentView.tsx, src/components/PhraseStripContext.tsx, src/hooks/usePhraseStripSetup.ts
Interlinearizer accepts viewOptions bundle and forwards to ContinuousView and SegmentListView; each view passes prop down through hierarchy to PhraseStripContext; usePhraseStripSetup includes in memoized context value and dependency array; ContinuousView re-centers focused group when showMorphology changes alongside simplifyPhrases.
PhraseBox focus behavior
src/components/PhraseBox.tsx
PhraseBox destructures showMorphology from context; updates click and keyboard handlers to focus first non-morpheme gloss input (via input:not([data-morpheme-gloss])) instead of first input; passes showMorphology to all token chips in all rendering paths.
ViewOptionsDropdown setting UI
src/components/controls/ViewOptionsDropdown.tsx
Adds showMorphology and onShowMorphologyChange to props contract; includes new localization key; renders new ViewToggle in dropdown wired to prop and change handler.
InterlinearizerLoader setting loading and wiring
src/components/InterlinearizerLoader.tsx
Loads interlinearizer.showMorphology via useOptimisticBooleanSetting; includes loading state in aggregated settings-ready check; passes to both ViewOptionsDropdown and Interlinearizer.
Test infrastructure and view test updates
src/__tests__/test-helpers.ts, src/__tests__/components/SegmentView.test.tsx, src/__tests__/components/ContinuousView.test.tsx, src/__tests__/components/Interlinearizer.test.tsx, src/__tests__/components/InterlinearizerLoader.test.tsx, src/__tests__/components/controls/ViewOptionsDropdown.test.tsx, src/__tests__/components/PhraseBox.test.tsx
Test helpers default showMorphology to false; view tests include showMorphology in requiredProps and viewOptions; ContinuousView tests verify re-centering on toggle; Interlinearizer tests pass showMorphology: false across all renderings; InterlinearizerLoader tests read view settings from nested viewOptions; ViewOptionsDropdown tests cover new toggle and update checkbox indices; PhraseBox tests verify morphology-aware focus and propagation via data attributes.

Sequence Diagram

sequenceDiagram
  participant User
  participant ViewOptionsDropdown
  participant InterlinearizerLoader
  participant ContinuousView
  participant TokenChip
  participant MorphemeBreakdownPopover
  participant AnalysisStore
  participant Redux

  User->>ViewOptionsDropdown: Toggle "Show Morphology"
  ViewOptionsDropdown->>InterlinearizerLoader: onShowMorphologyChange(true)
  InterlinearizerLoader->>InterlinearizerLoader: useOptimisticBooleanSetting saves
  InterlinearizerLoader->>ContinuousView: viewOptions with showMorphology=true
  ContinuousView->>TokenChip: render with showMorphology=true
  
  Note over TokenChip: Morphology UI enabled
  User->>TokenChip: Click morpheme trigger button
  TokenChip->>MorphemeBreakdownPopover: Open popover with initialValue
  User->>MorphemeBreakdownPopover: Type morpheme forms (space-separated)
  User->>MorphemeBreakdownPopover: Click Done
  MorphemeBreakdownPopover->>TokenChip: onSave(forms)
  TokenChip->>TokenChip: handleMorphemeSave splits on whitespace
  TokenChip->>Redux: useMorphemeBreakdownDispatch writeMorphemes
  Redux->>AnalysisStore: Analysis state updated with morpheme array
  AnalysisStore->>TokenChip: useMorphemes returns new morpheme array
  TokenChip->>TokenChip: Render per-morpheme gloss inputs
  
  User->>TokenChip: Edit morpheme gloss (blur)
  TokenChip->>Redux: useMorphemeGlossDispatch writeMorphemeGloss
  Redux->>AnalysisStore: Morpheme.gloss updated for analysisLanguage
  AnalysisStore->>TokenChip: onSave called with updated TextAnalysis
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • sillsdev/interlinearizer-extension#91: Both PRs touch the same phrase-token UI surface—PhraseBox/token chip rendering and interaction wiring—main PR threads showMorphology through PhraseBox into TokenChip, while retrieved PR overhauls PhraseBox's phrase/link-mode behavior.
  • sillsdev/interlinearizer-extension#86: The main PR extends the Redux-backed analysis store introduced in #86 by adding new morpheme-specific selectors/actions and corresponding AnalysisStore hooks.
  • sillsdev/interlinearizer-extension#98: Both PRs build on the same "view options" segment-view wiring by updating display toggles; the main PR extends that viewOptions plumbing by adding a new showMorphology toggle.

Suggested reviewers

  • imnasnainaec
  • jasonleenaylor

Poem

A morpheme breaks down with glee, 🐰
Whiskers twitch, syllables free!
Per-token glosses arranged so neat,
Making linguistic data complete—
Now every token's story shines! ✨

🚥 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 title 'Token Morphosyntactic Analysis UI' directly and accurately summarizes the main feature added across all files: comprehensive morphology (morphosyntactic) analysis UI functionality for tokens, including morpheme breakdown editing, per-morpheme glosses, UI controls, and supporting infrastructure.
Docstring Coverage ✅ Passed Docstring coverage is 96.92% 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 token-analysis

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


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 Jun 12, 2026 that may be closed by this pull request
Base automatically changed from refine-segment-view to main June 12, 2026 19:08
@alex-rawlings-yyc alex-rawlings-yyc marked this pull request as ready for review June 12, 2026 19:20
coderabbitai[bot]

This comment was marked as outdated.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

coderabbitai[bot]

This comment was marked as outdated.

@alex-rawlings-yyc

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

@imnasnainaec imnasnainaec left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Clarifying intent...

This pr gives the user the ability to type whatever they want in the morpheme-splitter pop-up text field. Do we want to leave it that open-ended or just (e.g.) let them click between characters to split/unsplit the surface form? (Perhaps that not sufficient for situations with, e.g., composed characters or circumfixes, but handling for those may be better left as power/settings-unlocked.)

@imnasnainaec reviewed 4 files and all commit messages, and made 1 comment.
Reviewable status: 4 of 32 files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

@alex-rawlings-yyc

Copy link
Copy Markdown
Contributor Author

My intent for this PR was to get the bare minimum UI needed for morpheme-splitting. If we decide that we want to extend the scope of this PR to add that sort of control that's fine with me, but for now I'm fine with a keeping things simple.

imnasnainaec and others added 3 commits June 17, 2026 09:17
* Address token-analysis review comments

- MorphemeEditor: normalize internal whitespace in isUnedited to avoid
  no-op saves on spacing-only changes
- TokenChip: document the deliberate onOpenChange omission on the
  controlled Popover

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

* Add JSDoc to normalize helper in MorphemeEditor

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Do token analysis

2 participants