Skip to content

Rework the activity view: readable summaries and a before/after diff#2388

Open
myieye wants to merge 52 commits into
developfrom
claude/brave-allen-91bf15
Open

Rework the activity view: readable summaries and a before/after diff#2388
myieye wants to merge 52 commits into
developfrom
claude/brave-allen-91bf15

Conversation

@myieye

@myieye myieye commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

The activity list used to name the change type; it now says what actually happened — "Apfel › apple · Set Definition (en) to …", "Changed part of speech to Noun", "Removed semantic domain 5.2 Food" — in Simple and Detailed modes. The detail panel shows a real before/after diff of the entry (a read-only reuse of the editor layout), and a commit that only builds one entry collapses to a read-only entry view instead of a stack of per-change diff cards.

Summaries name their subject — entry headword (with homograph subscript), "headword › gloss" for a sense, a vocab object's name — and any item a change references only by id (the part of speech set, the semantic domain / component removed or linked). Those names are resolved in one batched pass per page, so naming adds no per-row queries. Large commits (bulk creates, big sync batches) collapse to one or a few lines ("Created 100 semantic domains") rather than one line per change.

The list of change types is generated from the backend via Reinforced.Typings, and coverage tests fail until every change type has a summary, every model field is configured (or explicitly excluded), and every create type has a bulk-collapse noun — so this can't silently rot as the model grows.

Screenshots

Sadly such low-res that they're not very useful 😆

Activity summaries:
image

Activity previews:
image

Test plan

  • Frontend unit + coverage tests (summaries/recognizers; model↔config and config↔diff exhaustiveness guards — mutation-checked to fail when something's forgotten)
  • Backend resolver tests (subject + referenced-target naming, homograph-only-when-assigned, reorder)
  • svelte-check clean; FwLiteShared builds; generated types regenerated
  • Storybook galleries verified: activity/summaries, activity/previews, collapsed entry
  • Manual smoke in a real project — activity list (both modes) + detail panel

Notes for reviewers

  • New dependency @sanity/diff-match-patch powers the inline text diff.
  • The summary strings compose a few sentence frames around translated noun fragments (e.g. Added {noun}); correct in English and documented with translator context, but they won't inflect perfectly in gendered/noun-class languages. Deliberate tradeoff for terse log lines vs. a combinatorial msgid explosion — happy to expand if reviewers prefer.
  • HistoryService.ProjectActivity now materializes a page (the label resolver batch-loads across it); its IAsyncEnumerable signature could be tightened to Task<IReadOnlyList<…>> in a follow-up — all current consumers already materialize.
  • One incidental pnpm-lock.yaml line converged during install onto a transitive peer set already present on develop; not a deliberate dependency change.

🤖 Generated with Claude Code

myieye and others added 5 commits June 29, 2026 16:13
…r diff

The activity list now says what actually happened in each change — e.g.
"Apfel › apple · Set Definition (en) to …", "Changed part of speech to Noun",
"Removed semantic domain 5.2 Food" — instead of just naming the change type,
with Simple and Detailed modes mirroring the browse view. The detail panel
shows a real before/after diff of the entry (a read-only reuse of the editor
layout) in place of the old current-version toggle.

Each summary names its subject (entry headword, "headword › gloss" for a
sense, a vocab object's name) and any item a change references only by id
(the part of speech set, the domain/component removed or linked). Entry
headwords carry their homograph number as a subscript when assigned. Those
names are resolved in one batched pass per page, so naming adds no per-row
queries.

The list of change types is generated from the backend via Reinforced.Typings
and a coverage test fails until every type has a summary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two activity-summary wording tweaks, both prompted by review:
- A reorder now reads "<entry> · Reordered sense <gloss>" — the parent entry
  is the subject and the moved item is named — instead of "<entry> › <gloss> ·
  Reordered senses", which put one sense in the subject slot and read as if
  the whole list moved. Generalizes to examples and components: the parent is
  the subject, and the moved item is named when it has one.
- A field edit on a vocabulary object now names its type, e.g. "part of speech
  Noun · Set Abbreviation (en) to …", matching how the created/edited/deleted
  vocab lines already name the type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A commit that only builds one entry (its creation plus that entry's own
senses and fields) now renders as the finished entry in a read-only
EntryEditor, instead of a stack of per-change diff cards. It reads the
backend-resolved affectedEntries, so there's no change-by-change assembly
and no dependence on recognising every change type — an unfamiliar change
type can't break it. Other commits keep their per-change cards, and the
"(N changes)" header stays.

Adds a Storybook "activity/previews" gallery covering every preview pane
(entry/sense/example diffs, complex-form component, generic JSON, the
no-preview fallback) and the collapsed entry view, so these can be checked
without a live project.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Large commits no longer render a line per change. A commit is classified from
cheap signals it already carries — its change-type keys, per-change root ids,
and count — without parsing every change: building one entry collapses to
"Created entry X", a batch of one create type collapses to "Created 100
semantic domains", and anything else lists the first changes (capped in
Detailed mode) with the rest counted. So a 100-change sync commit is one or a
few lines, and Simple mode never parses past the first change.

Tests to keep this honest as the model grows — covering both ends and the
"don't forget to handle it" cases:
- every create change type has a bulk-collapse noun (new guard);
- the view config covers every model field — a new field must be configured
  or explicitly excluded (new guard);
- every backend change type still maps to a summary, and diff primitives
  render exactly the configured fields (existing guards);
- backend: the resolver names a vocab subject and the referenced part of
  speech, alongside the existing homograph and reorder coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-PR review follow-ups:
- export the two new generated types from the LcmCrdt barrel (index.ts)
- give the multi-key change-type filter test a positive count assertion so it
  can't pass vacuously on an empty result
- order multi-writing-system vocab labels by writing-system code, matching
  Entry.Headword(), so the shown alternative is deterministic
- SenseLabel is never null — drop the misleading nullable return
- remove the unused `activity` prop from ActivityItemChangePreview
- trim two design-rationale comments to their load-bearing part
- add translator context for the activity strings (Detailed, the Set-field
  lines, the "+N more" overflow) and broaden the shared "List mode" comment

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Jun 29, 2026
@coderabbitai

coderabbitai Bot commented Jun 29, 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: c1cae2c4-f706-493d-ae32-5ace15132502

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 an end-to-end enriched activity log: backend batch-resolves human-readable subject labels and root entry IDs per CRDT change via a new ActivityChangeInfoResolver, extends HistoryService to carry PreviousSnapshot, generates a ChangeType TypeScript union from all registered CRDT types, adds a full set of inline diff-view Svelte components, implements a ChangeFact-based change summary pipeline with ChangeSummary.svelte, and wires a simple/detailed view mode toggle into the activity list UI.

Changes

Activity log enrichment and change summary UI

Layer / File(s) Summary
Backend data model: ActivityChangeInfo, ChangeContext, ChangeTypes marker
backend/FwLite/LcmCrdt/HistoryService.cs, backend/FwLite/FwLiteShared/TypeGen/ChangeTypesMarker.cs
Introduces the ActivityChangeInfo record (Subject, RootEntryId, Target), adds ChangeInfo list to ProjectActivity, extends ChangeContext with PreviousSnapshot, and defines the ChangeTypes marker class for codegen.
ActivityChangeInfoResolver: batched label resolution
backend/FwLite/LcmCrdt/ActivityChangeInfoResolver.cs
Implements ResolveAsync that collects entity IDs from a full page of activities, batch-loads entries/senses/POS/domains/publications/morph types from the DB, and resolves per-change ActivityChangeInfo labels including homograph subscripts and sense/semantic domain formatting.
HistoryService orchestration and backend tests
backend/FwLite/LcmCrdt/HistoryService.cs, backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs
Materializes the page before calling ActivityChangeInfoResolver.ResolveAsync, extends LoadChangeContext to load PreviousSnapshot via LoadPreviousSnapshot, and tests ChangeInfo naming for homographs, morph types, reorder, empty entries, and POS scenarios.
TypeScript codegen: ChangeTypesCodeGenerator and generated types
backend/FwLite/FwLiteShared/TypeGen/ChangeTypesCodeGenerator.cs, backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs, frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/*
Adds ChangeTypesCodeGenerator that reflects all CRDT change types into a sorted ChangeType string-literal union and knownChangeTypes array. Wires ActivityChangeInfo and ChangeTypes into the generator config. Emits ChangeTypes.ts, IActivityChangeInfo.ts, updates IChangeContext.previousSnapshot to optional, adds changeInfo to IProjectActivity, and re-exports from the index.
Inline diff primitives and diff-view components
frontend/viewer/package.json, frontend/viewer/src/lib/entry-editor/diff-view/*, frontend/viewer/src/stories/editor/diff/*
Adds @sanity/diff-match-patch, implements computeDiff/hasChanges, and introduces DiffShell, DiffText, DiffValue, DiffSelect, DiffMultiString, DiffRichText, DiffMultiSelect, DiffEntryPrimitive, DiffSensePrimitive, DiffExamplePrimitive. Includes inline-diff tests, diff-field-coverage test, and a Storybook diff overview story.
Change summary logic: ChangeFact types, describeChange, ChangeSummary component
frontend/viewer/src/lib/activity/change-summary.ts, frontend/viewer/src/lib/activity/ChangeSummary.svelte, frontend/viewer/src/lib/activity/change-summary.test.ts, frontend/viewer/src/stories/activity/summaries.stories.svelte
Defines the ChangeFact discriminated union and supporting types. Implements describeChange, describeActivity, recognizeCommit, and summarizeActivity. Adds ChangeSummary.svelte rendering all fact kinds with i18n. Includes full test coverage and Storybook stories.
Activity UI integration: view mode, collapse-to-entry, diff preview
frontend/viewer/src/lib/activity/ActivityListViewOptions.svelte, frontend/viewer/src/lib/activity/ActivityView.svelte, frontend/viewer/src/lib/activity/ActivityItem.svelte, frontend/viewer/src/lib/activity/ActivityItemChangePreview.svelte, frontend/viewer/src/lib/components/dictionary/Headwords.svelte, frontend/viewer/src/lib/storage/project-storage.svelte.ts, frontend/viewer/src/lib/history/HistoryView.svelte, frontend/viewer/src/lib/components/ListItem.svelte, frontend/viewer/src/lib/views/entity-config-coverage.test.ts, frontend/viewer/src/stories/activity/previews.stories.svelte
Adds ActivityListViewOptions simple/detailed toggle persisted via activityListViewMode. ActivityView renders ChangeSummary rows via summarizeActivity. ActivityItem collapses single-entry commits to a read-only EntryEditor. ActivityItemChangePreview is rewritten to use Diff*Primitive components with previousSnapshot/snapshot. Headwords gains showHomograph prop.
Locale updates: activity-log strings across all languages
frontend/viewer/src/locales/*.po
Adds activity-log message keys for Added/Changed/Cleared/Created/Deleted/Edited/Linked/Moved/Removed/Reordered/Set/Unlinked/Updated and standalone noun/plural fragments across en, es, fr, id, ko, ms, sw, and vi. Replaces "Preview not available" with "Preview not available for this type of change". Adds "(+{0} more)" and "Detailed" strings.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • sillsdev/languageforge-lexbox#1844: Modifies ActivityView.svelte change-item rendering logic, the same file this PR refactors to add summarizeActivity/ChangeSummary row rendering.
  • sillsdev/languageforge-lexbox#2093: Extends HistoryService.ChangeContext and LoadChangeContext, the same plumbing this PR further enriches with PreviousSnapshot and ActivityChangeInfoResolver.
  • sillsdev/languageforge-lexbox#2357: Modifies HistoryService.cs ProjectActivity query behavior and ActivityView.svelte rendering to incorporate change-type information, directly connected integration points.

Suggested labels

📦 Lexbox

Poem

🐇 Hop, hop! The history log now glows,
Each change labeled where it goes—
Diff views bloom in green and red,
Subject, target, headword said.
From backend batch to frontend gleam,
The rabbit built the whole fine dream! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.40% 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 concise and accurately summarizes the main change: readable activity summaries plus a before/after diff view.
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.
Description check ✅ Passed The description directly matches the change set, covering activity summaries, diff previews, collapsed entry views, and generated change-type coverage.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/brave-allen-91bf15

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.

@argos-ci

argos-ci Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 4 changed Jul 6, 2026, 1:10 PM

myieye and others added 2 commits June 29, 2026 23:02
change-summary.ts keys are backend change-type discriminators and wire-format property names, all intentionally PascalCase, so disable naming-convention for the file (and the test's PascalCase payloads) rather than quoting ~50 keys. Reuse displayValue for span text instead of String()-ing an unknown, and drop an unused handler arg and an unused derived.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ReplaceComplexFormTypeChange had a summary handler but the backend never emits it — the change class isn't registered, so it's absent from the generated knownChangeTypes. Remove the dead handler and add a reverse-coverage test: every handler must map to a real generated change type. Paired with the existing forward check (every known type is handled), the two force the handler back the moment the backend registers the type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread backend/FwLite/LcmCrdt/ActivityChangeInfoResolver.cs Outdated
Comment thread backend/FwLite/LcmCrdt/ChangeTypes.cs Outdated
@hahn-kev

Copy link
Copy Markdown
Collaborator

This is probably not what we want here when there's not enough room
image

@hahn-kev

Copy link
Copy Markdown
Collaborator

When I open this row the whole activity page breaks.
image

@hahn-kev

Copy link
Copy Markdown
Collaborator

Json patch for homograph number summary could be improved
image

@hahn-kev

Copy link
Copy Markdown
Collaborator

I just checked this out on a couple projects. It looks great! I haven't done a detailed code review, even to get the flow of how it works. But so far I like it.

- Headwords use the best non-audio writing system + morph-type markers, so created entries show a real headword instead of "(Unknown)"; truly headword-less entries show a clear "(no headword)" placeholder.
- Standardized wording (dropped inconsistent articles); data values render as subtle pills so they stand apart from the template text; long rows truncate instead of breaking the layout.
- Fixed the activity page blowing out when a row is expanded (detail column min-width:0, content scrolls/wraps).
- Homograph: a dedicated summary for the homograph-number patch, and the subscript now shows in the preview header.
- Collapse a sense-creation commit (sense + its examples) to one line.
- Moved the ChangeTypes generation marker out of a confusing public file into TypeGen (internal).

Tests (resolver best-WS/morph/empty; frontend sense-collapse + homograph patch), storybook galleries, and i18n context updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IProjectActivity.ts (1)

17-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wire changeTypes to the generated ChangeType union.

This contract still exposes changeTypes as string[], so consumers keep losing typo/exhaustiveness checking even though this PR now generates the canonical backend-derived union in ChangeTypes.ts. I’d thread that alias into the generator here rather than leaving the main activity surface untyped.

🤖 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
`@frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IProjectActivity.ts`
around lines 17 - 18, The IProjectActivity contract still types changeTypes as
string[], so it should be updated to use the generated ChangeType union from
ChangeTypes.ts instead. Thread that alias into the IProjectActivity definition
so the changeTypes field references the canonical backend-derived type rather
than an untyped string array, preserving exhaustiveness and typo checking for
consumers.
🤖 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.

Inline comments:
In `@backend/FwLite/FwLiteShared/TypeGen/ChangeTypesCodeGenerator.cs`:
- Around line 35-39: The fallback in GetChangeTypeName is masking missing
serialized discriminators and can generate an incorrect frontend contract.
Update GetChangeTypeName to require a public static TypeName on each change type
and fail fast with an exception if it is absent or empty, so
ChangeTypesCodeGenerator only emits knownChangeTypes values from the explicit
discriminator and never silently substitutes changeType.Name.

In `@backend/FwLite/LcmCrdt/HistoryService.cs`:
- Around line 325-326: Update HistoryService’s Sense POS hydration so historical
snapshots use the commit they belong to instead of the latest state. In
ResolveSensePartOfSpeech and its callers in the diff flow, pass the
current/previous commit id through and resolve the PartOfSpeech via GetAtCommit
for each snapshot rather than GetLatest<PartOfSpeech>(). Make sure both the
current snapshot and previousSnapshot paths in the activity diff use the
commit-aware lookup so old diffs show the POS label that existed at that commit.

In `@frontend/viewer/src/lib/activity/change-summary.ts`:
- Around line 52-57: `ChangeFact` is still carrying English UI text in
`editObjectField.field` and `generic.text`, so move those values to
machine-readable identifiers and stop baking display strings into
`patchOpToObjectField()` and `humanizeType()`. Update `ChangeSummary.svelte` to
translate/render the localized labels itself using the fact’s symbols from
`change-summary.ts`, and make sure the `editObjectField` and `generic` cases no
longer depend on English text for vocab-field edits or fallback activity
messages.
- Around line 228-229: The create fallback for semantic domains in
change-summary.ts only uses the code, so unresolved summaries lose the
human-readable name. Update the CreateSemanticDomainChange handler to use the
existing semanticDomainLabel() helper, matching how semantic domains are labeled
elsewhere in this file, while leaving CreatePartOfSpeechChange unchanged.

In `@frontend/viewer/src/lib/components/dictionary/Headwords.svelte`:
- Around line 43-45: The homograph suffix condition in Headwords.svelte is too
strict because it hides the <sub> whenever headwords.length is zero, which also
suppresses the new placeholder rendering path. Update the conditional around the
entry.homographNumber display in the Headwords component so it shows whenever
showHomograph is enabled and a homograph number exists, even if the placeholder
branch is used; verify callers like ActivityItemChangePreview still render the
suffix for untitled entries.

In `@frontend/viewer/src/lib/history/HistoryView.svelte`:
- Line 106: The ActivityItem call in HistoryView.svelte is discarding the new
summary metadata by hardcoding changeTypes and changeInfo to empty arrays.
Update the record-to-activity mapping so ActivityItem receives the actual values
from record, preserving the enriched subject/type data added by this PR and
allowing the history drawer to render readable summaries.

---

Nitpick comments:
In
`@frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IProjectActivity.ts`:
- Around line 17-18: The IProjectActivity contract still types changeTypes as
string[], so it should be updated to use the generated ChangeType union from
ChangeTypes.ts instead. Thread that alias into the IProjectActivity definition
so the changeTypes field references the canonical backend-derived type rather
than an untyped string array, preserving exhaustiveness and typo checking for
consumers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a51d3835-ac75-4cef-b52e-c714cf574d9b

📥 Commits

Reviewing files that changed from the base of the PR and between 6900d0c and ff63fe6.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (48)
  • backend/FwLite/FwLiteShared/TypeGen/ChangeTypesCodeGenerator.cs
  • backend/FwLite/FwLiteShared/TypeGen/ChangeTypesMarker.cs
  • backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
  • backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs
  • backend/FwLite/LcmCrdt/ActivityChangeInfoResolver.cs
  • backend/FwLite/LcmCrdt/HistoryService.cs
  • frontend/viewer/package.json
  • frontend/viewer/src/lib/activity/ActivityItem.svelte
  • frontend/viewer/src/lib/activity/ActivityItemChangePreview.svelte
  • frontend/viewer/src/lib/activity/ActivityListViewOptions.svelte
  • frontend/viewer/src/lib/activity/ActivityView.svelte
  • frontend/viewer/src/lib/activity/ChangeSummary.svelte
  • frontend/viewer/src/lib/activity/change-summary.test.ts
  • frontend/viewer/src/lib/activity/change-summary.ts
  • frontend/viewer/src/lib/components/ListItem.svelte
  • frontend/viewer/src/lib/components/dictionary/Headwords.svelte
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/ChangeTypes.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivityChangeInfo.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IChangeContext.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IProjectActivity.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/index.ts
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffEntryPrimitive.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffExamplePrimitive.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffMultiSelect.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffMultiString.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffRichText.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffSelect.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffSensePrimitive.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffShell.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffText.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/DiffValue.svelte
  • frontend/viewer/src/lib/entry-editor/diff-view/diff-field-coverage.test.ts
  • frontend/viewer/src/lib/entry-editor/diff-view/inline-diff.test.ts
  • frontend/viewer/src/lib/entry-editor/diff-view/inline-diff.ts
  • frontend/viewer/src/lib/history/HistoryView.svelte
  • frontend/viewer/src/lib/storage/project-storage.svelte.ts
  • frontend/viewer/src/lib/views/entity-config-coverage.test.ts
  • frontend/viewer/src/locales/en.po
  • frontend/viewer/src/locales/es.po
  • frontend/viewer/src/locales/fr.po
  • frontend/viewer/src/locales/id.po
  • frontend/viewer/src/locales/ko.po
  • frontend/viewer/src/locales/ms.po
  • frontend/viewer/src/locales/sw.po
  • frontend/viewer/src/locales/vi.po
  • frontend/viewer/src/stories/activity/previews.stories.svelte
  • frontend/viewer/src/stories/activity/summaries.stories.svelte
  • frontend/viewer/src/stories/editor/diff/diff-overview.stories.svelte

Comment thread backend/FwLite/FwLiteShared/TypeGen/ChangeTypesCodeGenerator.cs
Comment thread backend/FwLite/LcmCrdt/HistoryService.cs
Comment thread frontend/viewer/src/lib/activity/change-summary.ts
Comment thread frontend/viewer/src/lib/activity/change-summary.ts Outdated
Comment thread frontend/viewer/src/lib/components/dictionary/Headwords.svelte Outdated
Comment thread frontend/viewer/src/lib/history/HistoryView.svelte
myieye and others added 11 commits June 30, 2026 10:41
…eviews, sense numbers

Builds on the merge of develop (new Sense.pictures field + 5 new change types) and addresses the round-2 review feedback + CodeRabbit:

- Handle the 5 develop-added change types (sense pictures ×4 + set-main-publication) with proper summaries; configure the new Sense.pictures field.
- Create-entry commit no longer blows out the page when expanded (the collapse-to-editor wrapper gets min-w-0/min-h-0 — the spot the earlier grid fix missed).
- VLists lazy-load properly (itemSize set) so a 19-change commit no longer loads all 19 at once.
- Resolver: project only the columns it reads (no heavy JSONB hydration), load morph types lazily/projected; sense subjects disambiguate by number ("… › sense 2" / "gloss (2)") for empty/duplicate glosses.
- Styled before/after previews for all 7 vocab object types (was raw JSON), guarded by a preview-entityType coverage test.
- View-aware activity nouns (Lite "word/meaning" vs Classic), matching the field labels.
- Sense-creation collapse only fires on a pure sense tree (won't hide a mixed edit).
- CodeRabbit: created-semantic-domain summary keeps its name (not just code); homograph subscript shows with the Untitled placeholder too; clarifying comment on the ChangeTypes discriminator fallback (it intentionally mirrors Harmony — failing fast there breaks SetPartOfSpeechChange).

Tests: resolver targets/reorders/vocab subjects/sense numbers; frontend new facts, collapse purity, semantic-domain label; preview-entityType guard. i18n extracted + context added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-91bf15

# Conflicts:
#	frontend/viewer/src/locales/en.po
#	frontend/viewer/src/locales/es.po
#	frontend/viewer/src/locales/fr.po
#	frontend/viewer/src/locales/id.po
#	frontend/viewer/src/locales/ko.po
#	frontend/viewer/src/locales/ms.po
#	frontend/viewer/src/locales/sw.po
#	frontend/viewer/src/locales/vi.po
A delete change has a null snapshot, so the History button's
context.snapshot?.id was undefined and clicking it did nothing. Fall back to
context.previousSnapshot?.id (the pre-delete entity) so History works there too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e preview, layout

Baseline checkpoint of the activity-view iteration before the design-review pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n dark mode

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…boxed data

Subject renders as a bold borderless token (the scannable element); the verb phrase inherits a muted container colour; data values stay as bordered chips. Drops the middot separators — the bold subject plus its trailing margin is enough separation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detailed now groups a commit's facts by subject — the headword shows once as a bold header, its changes listed beneath along an indent rail — instead of repeating the subject on every line. Gives the two list modes distinct jobs (skim vs inspect) rather than just different lengths. Adds groupBySubject + a hideSubject prop on ChangeSummary; keyed by subject string so distinct senses of one entry don't wrongly fold together.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Multi-select diffs merge before/after into one sorted list, so a removed item keeps its sorted position instead of trailing the kept ones (e.g. a removed semantic domain by code).
- Audio-only example creates read as "with audio" instead of leaking the sil-media:// URI; firstAlternative now skips audio writing systems everywhere.
- The example Reference field is framed like the text input it is (DiffShell) rather than rendered as bare text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Any media value (audio-WS field or a sil-media:// value on any ws) now shows an audio marker — an i-mdi-volume-high icon plus "audio" — instead of leaking the media URI. Covers Set/Cleared field edits and audio-only example creates, and drops the unreadable audio ws code. First use of iconography in the summaries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The collapsed create-entry preview rendered the entry's CURRENT (possibly since-edited) state. Now it assembles the entry as-of the commit from that commit's own change snapshots: entry + its senses + their examples, in commit order. Part of speech is resolved from the id when the snapshot carries only that; semantic-domain names come denormalized from the sense snapshot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
myieye and others added 30 commits July 2, 2026 10:47
The resource-create/upload/delete commit now reads 'Added audio recording' with the audio marker, and its preview plays the recording. Two constraints drove this: the resource change carries no kind, and the field-set that would identify it as audio lives in a SEPARATE commit — so kind can't be derived cheaply (would need a file fetch). Audio is the only resource kind today, so we assume it (documented). The preview couldn't find the resource because RemoteResource isn't an IObjectWithId (null snapshot, entityType 'Unknown'); we now read the resource id off the raw change instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…/remove

Empty audio delegates to AudioInput's own 'No audio' placeholder (matches the editor) instead of a blank. A replaced/removed recording now sits in a destructive-bordered surface above the added one in an emerald-bordered surface — a diff gutter reads better on a player than a line-through would. New surface-only palette tokens (bg tint, no text styling) for non-text diff content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A deleted component link is gone from the projection, so 'Removed component' had no headwords. Recover its endpoints from the create change (AddEntryComponentChange carries both entry ids) — two steps: load the CFC's changes, read the ids off the create — enough to name the complex form (subject) and component (target). Frontend renders the component target on the remove branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detailed rows now carry a coloured gutter glyph per change (added +, removed −, changed ✎, reordered ↕) for fast visual grepping down a commit — Simple mode stays a plain-sentence skim. The per-row change cap goes 10→50 since Detailed is the inspect view; still bounded because a row isn't internally virtualised. New pure factCategory() (tested) keeps the mapping out of the template.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Custom-view previews now diff their field lists (entry/sense/example) and writing-system lists (vernacular/analysis) plus base — not just the name. A new vocab-diff-coverage test parses every vocab model's generated interface and asserts each property is either handled by DiffVocabPrimitive or explicitly ignored (vocab-diff-fields.ts) — so a new backend field can't silently vanish from the diff. The guard immediately caught WritingSystem.isAudio, now shown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DiffSelect drew the transition with a literal Unicode → ; use i-mdi-arrow-right like the rest of the icon system for consistent sizing and alignment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CustomView.base and WritingSystem.type rendered raw enum values ('FwLite', '0'). Map them to the app's usual names — 'FieldWorks Lite'/'FieldWorks Classic' and 'Vernacular'/'Analysis' — keyed by both the string name and numeric value so it works whichever the wire sends; unknown values still fall through to the raw text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sense numbers are the sense's 1-based position, shown when an entry has more than one sense — independent of the gloss (FieldWorks' rule; homograph numbers, which key off identical headwords, stay separate). Replaces the old empty/duplicate-gloss subscript logic: >1 sense → "N gloss" (or "N" when gloss-less); a lone sense → its gloss or nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The humanized change-type stripe restated in technical vocabulary what the plain-English summary and the diffs below already show — low value for end users. Removed it and the now-unused ProjectActivity.ChangeTypeLabels (ChangeTypes stays for filtering); regenerated types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A commit that creates one sense plus its child examples now collapses to a single CollapsedSenseDiff (the sense and its examples, all-added) instead of a flat change list — the sense-level counterpart of the create-entry collapse. New assembleSenseAtCommit pieces the sense together from the commit's at-commit snapshots; entry-creation still takes precedence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An at-a-glance 'who' signal: a person icon coloured from a stable hash of the author name, using a small curated palette that reads in both themes. Colours the icon only — the name stays default text, so readability is untouched (the concern with colouring the name itself).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-91bf15

# Conflicts:
#	frontend/viewer/src/lib/activity/ActivityView.svelte
Pulls the per-change preview's headword dropdown (open-in-new-window,
go-to-browse) into its own component so the collapsed create views can
reuse it. Behaviour-neutral.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collapsed create-entry / create-sense cards now show the entry-headword
menu button and Preview / Details tabs mirroring the per-change cards;
Details renders the whole commit's change JSON.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DiffVocabPrimitive base-view / field-group / writing-system labels
were never extracted (a prior extract ran against the wrong working
tree), failing the i18n CI check. Also refreshes source references after
the EntryHeadwordButton / AuthorLabel extractions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nine errors in existing branch files (unused import, redundant assertion,
enum-mirroring object keys vs naming-convention, base-to-string, forbidden
inline import() type, mutable Map). Root-cause fixes, no rule suppression:
enum label maps → guarded functions; merge helper → array ops; import type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FieldWorks/System previously sat to the right of the name; put the new
coloured person icon there too so all authors keep one side.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The change-kind glyph's fixed gutter is bullet-list styling that helps
scan a group of siblings; on a lone change it just eats width. Reserve
the gutter only for grouped rows and inline the glyph otherwise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the gutter border and inline padding; let the surface background
carry the diff signal so the audio player aligns with sibling fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Row-flex is items-center so the glyph sits with the text (was baseline
with a nudge). Inline glyph uses align-middle. Meta line gets mt-2 to
separate what happened from who/when.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Inline vertical-align:middle sat the glyph on the x-height baseline —
visually low against lowercase text. Flex + items-center like the
grouped case aligns it with the summary line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The inline change-kind glyph on ungrouped rows becomes a small emerald
(added) / destructive (removed) triangle in the top-right of the list
item. Edits and reorders leave the corner bare — absence is the signal
that this row is a modification. Multi-fact commits keep per-fact glyphs
inline (a single corner can't represent a mixed set).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…view

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eted entities

Small commits now list their facts under the entry header exactly like the
same tree inside a bigger commit (no more one-line collapse), entity names
render bold in every summary context (chips stay reserved for data values),
and the Simple/Detailed list mode is replaced by the view picker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-91bf15

# Conflicts:
#	frontend/viewer/src/locales/en.po
#	frontend/viewer/src/locales/es.po
#	frontend/viewer/src/locales/fr.po
#	frontend/viewer/src/locales/id.po
#	frontend/viewer/src/locales/ko.po
#	frontend/viewer/src/locales/ms.po
#	frontend/viewer/src/locales/sw.po
#	frontend/viewer/src/locales/vi.po
The Comments feature merged from develop adds six change types the
activity coverage guards flagged: comments render as added/edited/deleted
comment lines (thread creation is fact-less — its first comment carries
the line), thread status reads as closed/reopened, and the two comment
entity types opt out of a styled preview.

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

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants