Skip to content

Add live duplicate check to the new-entry dialog - #2433

Open
myieye wants to merge 28 commits into
developfrom
claude/duplicate-detection-review-ny053c
Open

Add live duplicate check to the new-entry dialog#2433
myieye wants to merge 28 commits into
developfrom
claude/duplicate-detection-review-ny053c

Conversation

@myieye

@myieye myieye commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Resolves #1752.

Searches existing entries as you type in the new-entry dialog and surfaces likely duplicates before you save.

Screenshots

A sticky pill is used to immediately communicate that the currently entered word or sense is a potential duplicate.

  • Clicking the pill scrolls the user to the potential duplicates at the bottom of the form and automatically expands the list
  • Clicking the pill's X permanently dismisses the pill for the lifetime of current dialog

Similar words "pill"

image

Same form/headword "pill"

image

Similar words list

Collapsed:
image

  • First potential duplicate is always auto-expanded (if/when the list is expanded)
  • "Go to word" is a primary button, because it closes the dialog
  • Various different buttons were considered:
    • "Add meaning" if sense data was entered => an edge case. Why would a user enter a sense if we clearly communicated that the word is likely a duplicate?
    • "Edit word" for opening the potential duplicate in a dialog => Nice to be able to see the entry in full without closing the "Create word" dialog, but seems very edge casey and then if you complete your task in the Edit word dialog you still need to close/dismiss the create dialog after
    • "Open in new window" => Only supported on desktop. Fairly slow. Not really worth finding a "UI home" for.
image

Three kinds of matches: Same word, similar word and similar meaning: (Should there be "Same meaning"? Meh)

image

Note "Same word" ("Same lexeme form" in classic view), because it's a lexeme form exact match even though it's not a headword exact match:
image

First 3 are shown. More can be added to the DOM on demand:

image

Diacritic mismatch remotes an exact match to a similar match:
image

With the added diacritic it's an exact match:
image

Morph-tokens are NOT stripped before querying for potential duplicates. That might be an improvement (feels very similar to stripping diacritics, which seems like an obviously good idea).

myieye and others added 10 commits July 4, 2026 22:05
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Spec-cited review against EntrySearchService/SqlHelpers found the client
fold diverged: host-locale lowercasing, unconditional accent-stripping
(backend keeps diacritics significant when the query has them), and no
morph-token handling (typed "-aji" vs suffix entry "aji" missed exact).
Also ranks each search by the writing system it was typed in, fixes the
mount-time "Checking" flash, singularizes the one-match banner, and
closes a pre-existing double-Enter double-create in the dialog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- match rows expand to reveal Add sense / Go to entry; DictionaryEntry gains an inline mode for the compact collapsed rows
- classification: attribute same-word matches to the field that hit (Same headword vs Same lexeme form), rank similar words closest-in-length first, drop cross-field coincidences instead of a vague 'related' kind
- classify in a $derived so the lazy morph-types resource warms at mount and re-classifies without re-searching
- dedupe queries per field kind: the same text typed as lexeme and gloss keeps its gloss query (same-meaning matches were lost)
- share the banner message/tint/Enter-trap between the strip and the extracted DuplicateSummaryPill
- scope backend-parity doc claims to the CRDT FTS path; drop unverified FLEx attribution
- adapt the Playwright suite to the new interaction model and cover the jump pill (7 tests); pin length-delta boundary, gloss containment direction, and closest-form ranking in unit tests (33; 8/8 targeted mutants killed)

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

# 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
…fill test gaps

- Move new-entry-duplicates.test.ts to tests/ui/ and port it to the
  DemoProjectPage page object introduced by the E2E restructure (#1866)
- Cap the jump pill at 32rem so long headword previews don't stretch it
  across the whole dialog
- New tests: trapEnter unit test, Enter-inside-strip must not create the
  entry, pill dismiss persists, add-sense must not also create an entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R49zrCE7hPgm9QJ5bfugaV
A failed searchEntries call previously vanished into the unread
resource.error — no toast, no status line, the widget just went blank.
Show an inline status line instead of a toast: the search re-fires per
typing pause, and a toast per failure would bury the dialog.

Show-more now uses $plural so translators get per-language plural forms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R49zrCE7hPgm9QJ5bfugaV
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

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 Plus

Run ID: 2f388721-629a-4b7d-9d44-e0794a8ea01f

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

Changes

Duplicate entry detection

Layer / File(s) Summary
Duplicate matching contracts and classification
frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.ts, frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.test.ts
Adds writing-system-aware query generation, normalization, candidate merging, match classification, and comprehensive unit tests.
Duplicate widget and summary pill
frontend/viewer/src/lib/entry-editor/duplicate-check/*.svelte
Adds expandable duplicate results, navigation, loading/error states, visibility tracking, and a dismissible sticky summary pill.
New entry dialog integration and validation
frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte, frontend/viewer/tests/ui/new-entry-duplicates.test.ts
Embeds duplicate checking in the dialog, prevents concurrent submissions, stabilizes layout, and adds end-to-end coverage.
Duplicate-check localization
frontend/viewer/src/locales/{en,es,fr,id,ko,ms,sw,vi}.po
Adds duplicate status, warning, badge, navigation, and expansion strings.
Dictionary rendering and navigation icons
frontend/viewer/src/lib/components/dictionary/DictionaryEntry.svelte, frontend/viewer/src/lib/activity/ActivityItemChangePreview.svelte, frontend/viewer/src/lib/entry-editor/EntryOrSenseItemList.svelte
Adds inline multi-sense rendering and updates entry-navigation icons.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: hahn-kev

Poem

I’m a rabbit with matches to find,
Sorting word clues neat in a line.
A pill hops in view,
“Go to entry!” too—
And duplicates now are easier to mind.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested duplicate list, sticky expandable summary, search matching, and navigation behavior from #1752.
Out of Scope Changes check ✅ Passed The extra UI, locale, and test changes all support the duplicate-check feature and do not appear unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed Clearly describes the main change: adding live duplicate checking in the new-entry dialog.
Description check ✅ Passed It directly matches the implemented duplicate-check UI, search flow, and sticky pill behavior.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/duplicate-detection-review-ny053c

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.

@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Jul 12, 2026
@argos-ci

argos-ci Bot commented Jul 12, 2026

Copy link
Copy Markdown

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

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Jul 23, 2026, 6:54 PM
e2e (Inspect) ✅ No changes detected - Jul 23, 2026, 7:02 PM

claude and others added 4 commits July 15, 2026 06:39
…ntry-detection-sync-1bcb1f

# 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
Always send the backend search with diacritics stripped (it only matches
accent-insensitively for a diacritic-free query), then classify an exact
diacritic match as same-word and an accent-only difference as similar-word.
Classification now runs against the displayed headword from the writing-system
service, dropping the client-side morph-token stripping. Extract the duplicate
pill/widget wiring into DuplicateCheckSection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The entries for "Meaning added to {0}" and "This entry may already exist" were extracted without a msgstr line in all seven non-English locales, leaving the catalogs malformed. Add the empty msgstr so they parse as normal untranslated entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
myieye and others added 7 commits July 20, 2026 12:31
expand() sets userToggled=true, which recomputed the `expanded` $derived
back to (hasExactWordMatch && !userToggled) = false — so clicking the
jump pill closed the strip it was meant to open. Restore `expanded` to
$state with an effect for the exact-match auto-open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@myieye
myieye force-pushed the claude/duplicate-detection-review-ny053c branch from 0940483 to e11f04a Compare July 20, 2026 13:10
The empty-input test in the normalizeForExactCompare block called
normalizeForLooseCompare, so exact-compare's handling of a missing
value was never covered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@myieye
myieye marked this pull request as ready for review July 20, 2026 14:12
@myieye
myieye requested a review from hahn-kev July 20, 2026 14:12

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.ts (1)

82-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant trim/coalesce.

normalizeForExactCompare already trims and always returns a string, so .trim() ?? '' on line 83 is dead. Minor cleanup.

♻️ Proposed tidy
-  const decomposed = normalizeForExactCompare(value).trim() ?? '';
-  return decomposed.toLowerCase()
+  return normalizeForExactCompare(value).toLowerCase()
     // remove diacritics
     .replace(/\p{Mn}/gu, '');
🤖 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/entry-editor/duplicate-check/duplicate-check.ts`
around lines 82 - 91, Remove the redundant `.trim() ?? ''` from
`normalizeForLooseCompare`; use the string returned by
`normalizeForExactCompare(value)` directly before lowercasing and removing
diacritics.
🤖 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 `@frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.ts`:
- Around line 127-133: Update the docstring for classifyDuplicateCheckResults to
remove the unsupported claim that similar words are ordered closest-in-length
first. Document only the ordering guarantees implemented by the kindRank sort
and preserved candidate search-relevance order.

---

Nitpick comments:
In `@frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.ts`:
- Around line 82-91: Remove the redundant `.trim() ?? ''` from
`normalizeForLooseCompare`; use the string returned by
`normalizeForExactCompare(value)` directly before lowercasing and removing
diacritics.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7b7390db-8a03-4492-a355-b942ad36a7c3

📥 Commits

Reviewing files that changed from the base of the PR and between 1fb71c9 and e3c2b56.

📒 Files selected for processing (18)
  • frontend/viewer/src/lib/activity/ActivityItemChangePreview.svelte
  • frontend/viewer/src/lib/components/dictionary/DictionaryEntry.svelte
  • frontend/viewer/src/lib/entry-editor/EntryOrSenseItemList.svelte
  • frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte
  • frontend/viewer/src/lib/entry-editor/duplicate-check/DuplicateCheck.svelte
  • frontend/viewer/src/lib/entry-editor/duplicate-check/DuplicateCheckSection.svelte
  • frontend/viewer/src/lib/entry-editor/duplicate-check/DuplicateSummaryPill.svelte
  • frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.test.ts
  • frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.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/tests/ui/new-entry-duplicates.test.ts

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • frontend/viewer/src/lib/entry-editor/duplicate-check/duplicate-check.ts

Commit: ba81d24ddf41bd0ed0e7e5eb779cccf47ff5b435

The changes have been pushed to the claude/duplicate-detection-review-ny053c branch.

Time taken: 8m 44s

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@hahn-kev

Copy link
Copy Markdown
Collaborator

Probably an edge case so feel free to ignore it, but I did find this a little janky

Video.mp4

@hahn-kev

Copy link
Copy Markdown
Collaborator

I couldn't help myself

Video.mp4

@hahn-kev hahn-kev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I left some feedback, I'm happy to merge it, but there's a few things that could be improved.

I also made some minor tweaks myself. Mostly around the pill, I added a threshold for triggering the intersection check, this means that when only 50% of the check area is showing, then the pill will show or hide, previously it would only trigger on 100%, however it is a bit weird now if the duplicate check is open, then the pill shows up too early possibly. I'll leave it and you can check it out and revert it if you don't like it.

analysis: string[];
}

export function duplicateResultContainerClass(hasExactWordMatch: boolean): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this seems odd to be here instead of in the svelte file

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

Moved into DuplicateCheck.svelte's module <script>, since it's only used by this component.

// the cap is min(shorter length, MAX_SIMILAR_LENGTH_DELTA=3): a short query grows by at most its
// own length, a long one by at most 3.
// (Typing "uz" must not surface every word containing it.)
const shortAtCap = makeEntry({lexemeForm: {seh: 'uzem'}}); // 'uz' + 2 = delta 2, kept

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I understand this enough, the comment says 'uz' + 2 = delta 2... what does that mean? this is saying it won't match words more than 2 chars longer than the search term, if it's 3 longer than it doesn't count as a match. In that case though we should probably change the name or something, since the max similar is actually 2, not 3. From what I can tell the code is working correctly but it is confusing. It might help to pull out a utility and test that with very simple inputs (eg isSimilar('abc', 'abcdef')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

Pulled the logic into an exported isSimilarWord(a, b) and deleted the confusing length-delta test. It's now tested directly with trivial inputs — isSimilarWord('abc', 'abcdef'), the short-word case ('a'/'an'/'and'), etc.

On the name: MAX_SIMILAR_LENGTH_DELTA (3) is the upper bound for long words; short words get a smaller allowance equal to their own length, so 'uz' effectively caps at a delta of 2. That's now documented on the function and pinned by tests rather than a comment.


it('matches the bare lexeme of a suffix as a lexeme hit, not a headword hit', () => {
// typing 'aji' (no token) against suffix entry '-aji': the lexeme matched, the headword didn't
const suffixEntry = makeEntry({lexemeForm: {seh: 'aji'}, morphType: MorphTypeKind.Suffix});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

would it not match citation form '-aji' like in the previous test?

@myieye myieye Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It would, but that'd just be a "similar" match. This test is documenting the fact that we report/find an exact match on lexeme-form even though the text is not an exact match on headword.

// citation form — the citation hit must win the field attribution
const entry = makeEntry({lexemeForm: {seh: 'fuz'}, citationForm: {seh: 'fuza'}});
const result = classifyDuplicateCheckResults([entry], vernQueries('fuz', 'fuza'), writingSystems);
expect(result[0]).toEqual({entry, kind: 'same-word', field: 'headword'});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

from this test it sounds like field should just be citation form then.

@myieye myieye Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What about calculated headwords that use the lexeme-form?

  • headword and lexeme-form cover everything.
  • citation-form and lexeme-form do not.

export type SameWordField = 'headword' | 'lexeme';

/** Minimal slice of WritingSystemsService, so unit-testable. */
export interface DuplicateCheckWritingSystems {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we change this so that it's based on the real class? Eg Pick<WritingSystemService, 'vernacularNoAudio' | 'analysisNoAudio' | 'headword'>? One Goto will work in the editor, and 2 if there's a change to a signature then it will break inside this file, and not where the functions exported from this class are called.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

Done, it's now Pick<WritingSystemService, 'vernacularNoAudio' | 'analysisNoAudio' | 'headword'>, so goto-definition lands on the real service and a signature change breaks here.

it('drops a candidate whose gloss equals a typed vernacular value (cross-field coincidence)', () => {
// typing lexeme 'nyumba' must not surface an entry merely because its gloss is 'nyumba'
const entry = withGloss('cabana', 'nyumba');
expect(classifyDuplicateCheckResults([entry], vernQueries('nyumba'), writingSystems)).toEqual([]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this test would probably be more clear if you defined 2 entries, used one for the search and one to generate the query via getDuplicateCheckQueries. In fact I put this comment on the wrong line at first because this test is so similar to the previous one

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

Reworked exactly that way: the test now builds a typed entry, runs it through getDuplicateCheckQueries, then checks a separate candidate entry, so it no longer mirrors the previous test.

}
</script>

<div class="min-h-9 flex flex-col justify-center w-full" aria-live="polite">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I assume we're always rendering this to avoid layout thrashing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

Yes. The outer min-h-9 shell is always rendered so the strip toggling in/out doesn't shift the form. Added a comment saying so.

{pt($t`Could not check for similar entries`, $t`Could not check for similar words`, viewService.currentView)}
{:else}
<Icon icon="i-mdi-check-circle-outline" class="size-4 text-green-600 dark:text-green-500" />
{pt($t`No similar entries found`, $t`Looks like a new word`, viewService.currentView)}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

these texts are very different between lite and FLEx was that intentional?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

Mostly drift, not intentional. Aligned the lite "success" copy to No similar words found to match the FLEx No similar entries found. The error/loading copy stays view-specific (words vs entries) on purpose via pt(...).

<div class="grow min-w-0 text-sm {isExpanded ? '' : 'line-clamp-1'}">
<DictionaryEntry entry={match.entry} inline={!isExpanded} hideExamples={!isExpanded} />
</div>
{#if badge}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this is always true

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Claude, autonomous]

You're right, kindLabel returns a string for every kind, so it was always truthy. Removed the {#if badge} and the badge now renders unconditionally.

claude added 2 commits July 23, 2026 12:01
- Type DuplicateCheckWritingSystems as a Pick of the real WritingSystemService
- Move duplicateResultContainerClass into DuplicateCheck.svelte's module script
- Drop auto-expand on exact match (form jumped while typing) and slide the box in/out
- Remove always-true {#if badge}; align lite copy to "No similar words found"
- Test isSimilarWord directly with trivial inputs; clarify suffix/lexeme-attribution
  test names; realistic gloss-overlap data; cross-field test via getDuplicateCheckQueries

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWYjmnKqQz7EURdNYU2YvA
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWYjmnKqQz7EURdNYU2YvA
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.

Show Existing Entries in Create Entry Dialog to Prevent Duplicate

3 participants