Skip to content

Analysis catalog: analysis-keyed mutations (edit, merge, delete) #194

Description

@alex-rawlings-yyc

Makes the Analysis Catalog (#186) editable. Depends on the read-only panel.

Analysis-keyed reducers — the key is the blast radius

The existing reducers (writeGloss, writeMorphemes, writeMorphemeGloss) take a tokenRef and fork any shared payload, so they are inherently per-token. The catalog has no token in context — a row is the shared analysis — so it needs a second family keyed by analysisId that never forks:

writeAnalysisGloss({ analysisId, value })
writeAnalysisMorphemes({ analysisId, forms })
writeAnalysisMorphemeGloss({ analysisId, morphemeId, value })
deleteAnalysis({ analysisId })
mergeAnalysisInto({ sourceAnalysisId, targetAnalysisId })

Because the key alone determines scope — tokenRef → per-token (forks), analysisId → global (never forks) — no scope flag and no intent-recovery confirmation modal is needed.

Do not collapse the two families into one reducer parameterized by a boolean. That would put the local/global distinction behind a flag, which is exactly the distinction worth keeping structural.

Each new reducer calls mergeIntoIdenticalPayload after its edit, as the per-token reducers do, and returns enough for the UI to report what happened.

Operations

  • Edit gloss and morpheme breakdown. Editing a row's breakdown fixes every usage at once — 100 occurrences of a mis-split word are one row, one edit.
  • Merge into another row, first-class and prominent when the row has pool peers. Most deletions are really re-assignments: merge moves the links instead of stranding them. mergeIntoIdenticalPayload (src/store/analysisSlice.ts) already performs this when an edit makes two payloads identical.
  • Delete, with a ModalShell confirmation stating the concrete outcome rather than a generic "are you sure". Deleting an analysis also removes it from the suggestion pool, so unless a competing homograph survives, all N tokens go genuinely blank rather than falling back to a suggestion. The confirmation must say which case applies.
  • Merge-on-edit must be visible. Editing row A into equality with row B makes A vanish and B's count jump; unexplained that reads as data loss. Surface it ("merged into beginning, now 59 usages") and scroll to the combined row.

Single-row only — no multi-select, no bulk edit.

Behaviors to test

Reducers:

  1. writeAnalysisGloss changes the gloss for every token linked to the payload — no fork.
  2. writeAnalysisMorphemes likewise.
  3. deleteAnalysis removes the payload and every link to it; the affected tokens read as blank.
  4. deleteAnalysis on a payload with a surviving homograph peer leaves the tokens falling back to that peer as a suggestion — the two delete outcomes differ, and the confirmation must be able to tell them apart.
  5. mergeAnalysisInto moves every link to the target and drops the source; the target's usage count becomes the sum.
  6. Editing row A into content-equality with row B collapses A into B via mergeIntoIdenticalPayload, and the surviving row is B.

Panel:

  1. Editing a row's gloss updates the interlinear view for all its tokens in one edit.
  2. Delete opens a ModalShell confirmation naming the concrete outcome — "N tokens will become blank" vs "N tokens will fall back to " — driven by behavior 4.
  3. Canceling the confirmation leaves the analysis untouched.
  4. Merge is offered prominently when the row has pool peers, and not at all when it has none.
  5. After a merge-on-edit the panel announces it and scrolls to the combined row — the row must not simply vanish.

The sharp edge

Delete ships before undo (#184). #186 accepts this explicitly. The confirmation copy is the only guard, so behaviors 4 and 8 are the highest-value tests here — an inaccurate confirmation is worse than none.

Lexicon

No lexicon calls. Lexicon references (glossSenseRef, entryRef, senseRef, allomorphRef, grammarRef) are outbound only: editing or deleting an analysis drops our reference and leaves the lexicon untouched. Nothing here may cascade into the lexicon.

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions