Skip to content

Add activity filters to the activity view#2357

Open
hahn-kev wants to merge 9 commits into
developfrom
activity-filters
Open

Add activity filters to the activity view#2357
hahn-kev wants to merge 9 commits into
developfrom
activity-filters

Conversation

@hahn-kev

@hahn-kev hahn-kev commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Users of the activity page can now filter by Author, Change Type, and order by sync date.

Author filter:
image

History view:
image
opens to the selected commit on the activity page

- Updated ProjectActivity method to accept additional parameters for filtering by author and sorting.
- Introduced ListActivityAuthors and ListActivityChangeTypes methods to retrieve distinct authors and change types.
- Added ActivityQuery record to encapsulate query parameters.
- Implemented frontend components for filtering activities by author and change type.
- Updated TypeGen configuration to include new types for activity authors and change types.
- Added tests for new functionality in HistoryService.

This commit improves the activity tracking capabilities and enhances the user experience by allowing more granular filtering of activities.
@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Jun 16, 2026
@coderabbitai

coderabbitai Bot commented Jun 16, 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: 88006a22-3e03-4bb3-8459-11d86e8e0129

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:

  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Extends the project activity feed with author filtering, change-type filtering, FieldWorks exclusion, and sort ordering. New ActivityQuery, ActivitySort, ActivityAuthor, and ActivityChangeType types are added to HistoryService, which gains ListActivityAuthors and ListActivityChangeTypes methods. HTTP and JS-invokable routes, generated TypeScript contracts, the frontend HistoryService, and a new ActivityFilter Svelte component are all updated accordingly. Integration tests and locale strings for nine languages are added.

Changes

Activity Filter, Sort, and Author/Change-Type Listing

Layer / File(s) Summary
Backend activity models and HistoryService core logic
backend/FwLite/LcmCrdt/HistoryService.cs
Adds ActivitySort enum, ActivityAuthor, ActivityChangeType, ActivityQuery records, and ChangeTypes property on ProjectActivity. Implements ListActivityAuthors, ListActivityChangeTypes, and updated ProjectActivity with ApplyActivityFilters (author/FieldWorks filtering) and ApplyActivitySort (including JSON-extracted SyncDate ordering).
HTTP route and JS-invokable wiring
backend/FwLite/FwLiteWeb/Routes/ActivityRoutes.cs, backend/FwLite/FwLiteShared/Services/HistoryServiceJsInvokable.cs, backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
Updates the GET / activity route and HistoryServiceJsInvokable.ProjectActivity to accept authorId, authorName, excludeFieldWorks, and sort parameters forwarded via ActivityQuery. Adds TypeGen exports for the new types and ActivitySort string enum.
Generated TypeScript contracts
frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/ActivitySort.ts, frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivity*.ts, frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/IHistoryServiceJsInvokable.ts, frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/index.ts
Adds generated ActivitySort, IActivityAuthor, IActivityChangeType, IActivityQuery files; adds changeTypes: string[] to IProjectActivity; extends IHistoryServiceJsInvokable with new method signatures and re-exports all new types.
Frontend HistoryService refactor
frontend/viewer/src/lib/services/history-service.ts, frontend/viewer/src/lib/services/service-provider-dotnet.ts
Requires historyApi via ensureLoaded() (throws when absent), removes HTTP fetch fallbacks, re-exports activity types, adds listActivityAuthors/listActivityChangeTypes, changes activity() to accept query?, and makes loadChangeContext async. Adds error-logging try/catch to the dotnet proxy.
ActivityFilter Svelte component
frontend/viewer/src/lib/activity/ActivityFilter.svelte
New component fetching author and change-type lists via resource wrappers, rendering author/change-type dropdowns with commit counts, a Hide FieldWorks switch, and a sort menu with translated ActivitySort labels.
ActivityView rework and HistoryView compatibility
frontend/viewer/src/lib/activity/ActivityView.svelte, frontend/viewer/src/lib/history/HistoryView.svelte, frontend/viewer/src/lib/activity/utils.ts
Replaces project-code pagination with filter/query-driven resource loading, adds queryKey stale-response discarding, awaitingFreshData state, visibleActivity derivation, and integrates ActivityFilter. Updates HistoryView to pass changeTypes: [] to ActivityItem. Condenses formatJsonForUi pipeline.
Integration tests
backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs
EF-backed tests covering ListActivityAuthors distinct counts, ListActivityChangeTypes label, ProjectActivity author/FieldWorks filtering, OldestFirst/SyncedNewestFirst sort ordering, pagination with filters, and ChangeTypes inclusion; includes AddEntryCommit and SetSyncDate helpers.
Locale strings
frontend/viewer/src/locales/*.po
Adds activity filter/sort UI strings ("All authors", "All change types", "Hide FieldWorks", sort options, loading/error/empty-state messages) across en, es, fr, id, ko, ms, sw, vi locales; non-English entries have empty msgstr values.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • sillsdev/languageforge-lexbox#2094: Introduced the (skip, take) pagination to ProjectActivity and the JS-invokable/route wiring that this PR directly replaces with the richer ActivityQuery-based signature.
  • sillsdev/languageforge-lexbox#1844: Modified ActivityView.svelte's #each visibleItems loop, the same component this PR substantially rewrites for filter/query-driven pagination.
  • sillsdev/languageforge-lexbox#2126: Updated the same Malay (ms.po) and Swahili (sw.po) locale files that this PR adds new activity filter strings to.

Suggested labels

💻 FW Lite

Suggested reviewers

  • myieye

Poem

🐇 Hop, hop! The feed is sorted now,
With authors, types, and FieldWorks bowed.
Newest first or synced with care,
Empty states handled with flair.
Filters bloom like clover in spring! 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 PR title 'Add activity filters to the activity view' accurately and concisely summarizes the main change—adding filtering capabilities to the activity view with author, change type, and sort options.
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 PR description clearly describes the feature additions: author filtering, change type filtering, sync date ordering, and includes screenshots demonstrating the new UI components.

✏️ 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 activity-filters

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.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

UI unit Tests

  1 files  ±0   62 suites  ±0   30s ⏱️ +3s
186 tests ±0  186 ✅ ±0  0 💤 ±0  0 ❌ ±0 
258 runs  ±0  258 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 6216487. ± Comparison against base commit fa14cfa.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

C# FwHeadless Unit Tests

48 tests   48 ✅  16s ⏱️
 5 suites   0 💤
 1 files     0 ❌

Results for commit 6216487.

♻️ This comment has been updated with latest results.

@argos-ci

argos-ci Bot commented Jun 16, 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 - Jun 18, 2026, 6:16 AM

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

C# Unit Tests

165 tests   165 ✅  19s ⏱️
 23 suites    0 💤
  1 files      0 ❌

Results for commit 6216487.

♻️ This comment has been updated with latest results.

@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: 1

🧹 Nitpick comments (1)
backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs (1)

91-104: ⚡ Quick win

Add a synced-order regression test with two synced commits using different offsets.

Current synced coverage checks synced-vs-unsynced placement, but not relative ordering between synced commits whose SyncDate offsets differ. Add that case to prevent regressions in synced sort semantics.

🤖 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 `@backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs` around lines 91
- 104, The test method ProjectActivity_SyncedSort_PlacesUnsyncedLast currently
only verifies relative ordering between a synced and an unsynced commit, but
does not test the relative ordering between multiple synced commits with
different SyncDate offsets. Add a second synced commit with a different
DateTimeOffset value (either earlier or later than the first synced commit's
date), then use Array.FindIndex to locate both synced commits in the results and
add an assertion to verify they are ordered correctly relative to each other
according to the ActivitySort.SyncedNewestFirst sort semantics.
🤖 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/activity/ActivityView.svelte`:
- Around line 130-131: The style attribute in the div element contains an
invalid CSS declaration with orphaned minmax fragments on line 131. The fragment
"minmax(min-content, 1fr) minmax(min-content, 2fr);" appears between
grid-template-rows and grid-template-columns without a property name, causing
the browser to ignore it. Remove this orphaned fragment from the style attribute
to ensure the grid layout declaration is valid and the layout behavior is
consistent.

---

Nitpick comments:
In `@backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs`:
- Around line 91-104: The test method
ProjectActivity_SyncedSort_PlacesUnsyncedLast currently only verifies relative
ordering between a synced and an unsynced commit, but does not test the relative
ordering between multiple synced commits with different SyncDate offsets. Add a
second synced commit with a different DateTimeOffset value (either earlier or
later than the first synced commit's date), then use Array.FindIndex to locate
both synced commits in the results and add an assertion to verify they are
ordered correctly relative to each other according to the
ActivitySort.SyncedNewestFirst sort semantics.
🪄 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: 5bd4e79f-5dfb-45f0-9803-e9062c28582b

📥 Commits

Reviewing files that changed from the base of the PR and between fa14cfa and fbda954.

📒 Files selected for processing (26)
  • backend/FwLite/FwLiteShared/Services/HistoryServiceJsInvokable.cs
  • backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
  • backend/FwLite/FwLiteWeb/Routes/ActivityRoutes.cs
  • backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs
  • backend/FwLite/LcmCrdt/HistoryService.cs
  • frontend/viewer/src/lib/activity/ActivityFilter.svelte
  • frontend/viewer/src/lib/activity/ActivityView.svelte
  • frontend/viewer/src/lib/activity/utils.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/IHistoryServiceJsInvokable.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/ActivitySort.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivityAuthor.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivityChangeType.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivityQuery.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/history/HistoryView.svelte
  • frontend/viewer/src/lib/services/history-service.ts
  • frontend/viewer/src/lib/services/service-provider-dotnet.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

Comment thread frontend/viewer/src/lib/activity/ActivityView.svelte Outdated
@myieye

myieye commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

This is very cool.

I find several things about the UI a little awkward:

1) I think it would be great if we combined the author options (dropdown and FieldWorks switch)

E.g. something like this:

  • FieldWorks
  • FieldWorks Lite
    • Unknown
    • Tim
    • Robin

I assume grouping them is more intuitive.
Also, selecting multiple checkboxes would support scenarios such as:

  • I want to see all FieldWorks Lite changes except my own
  • I want to see changes from the non-core team-members X and Y
  • I want to see the changes from the two people I assigned to work on entry set X

2) The sort options are cool, but challenging. They have non-trivial meanings.

  • Perhaps we should break it into 2 things:
    Sort by: Authored vs Synced
    Direction: Asc or Desc
    (Although...who would ever want to sort Asc? Maybe we just drop that? Or make it dev only for fun?)

  • Those would still probably confuse users. If we truly want them to understand what's going on we'd need to find a place in the UI to explain the 2 different orders and potentially what they actually mean for the data.

  • We have some special badge-button for the sort options in browse view. We should likely reuse that concept for this.

3) The whole view width?

I'm a bit surprised you opted for a design that uses the whole width of the view, rather than just the changes panel e.g. how the filters in the browse view are only above the entry list.

@hahn-kev

Copy link
Copy Markdown
Collaborator Author

In the meeting we decided

  • use multiselects for both filters (remove the "Hide FW" option)
  • style sorting like we do on the entry listt
  • make the filter bar only span the width of the list, not the whole page
  • consider adopting the browse ui with a resize panel and overlay on mobile.
  • explain sort options (Tim's phone number)
  • add indicator for unsynced commits

@hahn-kev

hahn-kev commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator Author

I've implemented most of the changes we talked about (updated screenshots at the top):

  • Multi select filters
  • history button on each change
  • styled the sorting
  • filter bar only spans the list
  • indicator for unsynced changes

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/viewer/src/lib/history/HistoryView.svelte (1)

34-65: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard load() against stale async completions.

Concurrent load() calls can resolve out of order and overwrite history after id changes or after the dialog is closed.

💡 Suggested change
+  let loadToken = 0;
+
   async function load() {
+    const token = ++loadToken;
     loading = true;
     try {
       history = [];
-      history = await historyService.load(id);
+      const nextHistory = await historyService.load(id);
+      if (token !== loadToken || !open) return;
+      history = nextHistory;
       if (!selectedCommitId)
         selectedCommitId = history[0]?.commitId;
     } finally {
-      loading = false;
+      if (token === loadToken) loading = false;
     }
   }

   function reset() {
+    loadToken++;
     selectedCommitId = undefined;
     history = [];
   }
🤖 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/history/HistoryView.svelte` around lines 34 - 65, The
load() function can have multiple concurrent calls that resolve out of order,
causing stale results to overwrite the current history. When id changes or the
dialog closes (open becomes false), a previous load() call might still be
pending and could restore outdated data after reset() clears it. Implement a
mechanism to track and ignore stale async completions in the load() function by
either using an AbortController to cancel previous in-flight requests when a new
load starts, or by tracking a request identifier that gets updated each time
load() is called and checking that the identifier still matches before updating
the history state at the end of the async operation.
🤖 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/activity/ActivityItem.svelte`:
- Around line 119-125: Move the HistoryView component rendering outside of the
per-row conditional block that checks showHistoryButton. The HistoryView
component and its wrapping {`#if` openHistoryId} conditional should be placed
after the main content loop at the component level, so that only a single
HistoryView instance is rendered regardless of how many rows exist. Keep the
HistoryView binding and props the same, but ensure it renders once for the
entire component rather than once per row where showHistoryButton is true.

---

Outside diff comments:
In `@frontend/viewer/src/lib/history/HistoryView.svelte`:
- Around line 34-65: The load() function can have multiple concurrent calls that
resolve out of order, causing stale results to overwrite the current history.
When id changes or the dialog closes (open becomes false), a previous load()
call might still be pending and could restore outdated data after reset() clears
it. Implement a mechanism to track and ignore stale async completions in the
load() function by either using an AbortController to cancel previous in-flight
requests when a new load starts, or by tracking a request identifier that gets
updated each time load() is called and checking that the identifier still
matches before updating the history state at the end of the async operation.
🪄 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: e8f010f7-3908-4a1e-9752-b71a904798db

📥 Commits

Reviewing files that changed from the base of the PR and between fbda954 and 6216487.

📒 Files selected for processing (22)
  • backend/FwLite/FwLiteShared/Services/HistoryServiceJsInvokable.cs
  • backend/FwLite/FwLiteWeb/Routes/ActivityRoutes.cs
  • backend/FwLite/LcmCrdt.Tests/HistoryServiceActivityTests.cs
  • backend/FwLite/LcmCrdt/HistoryService.cs
  • frontend/viewer/src/lib/activity/ActivityFilter.svelte
  • frontend/viewer/src/lib/activity/ActivityItem.svelte
  • frontend/viewer/src/lib/activity/ActivityView.svelte
  • frontend/viewer/src/lib/activity/utils.ts
  • frontend/viewer/src/lib/components/ui/select/select-item.svelte
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/IHistoryServiceJsInvokable.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivityQuery.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IProjectActivity.ts
  • frontend/viewer/src/lib/history/HistoryView.svelte
  • frontend/viewer/src/lib/services/history-service.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
✅ Files skipped from review due to trivial changes (7)
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IProjectActivity.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/LcmCrdt/IActivityQuery.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/IHistoryServiceJsInvokable.ts
  • frontend/viewer/src/lib/activity/utils.ts
  • frontend/viewer/src/locales/ms.po
  • frontend/viewer/src/locales/en.po
  • frontend/viewer/src/locales/sw.po
🚧 Files skipped from review as they are similar to previous changes (4)
  • backend/FwLite/LcmCrdt/HistoryService.cs
  • frontend/viewer/src/locales/es.po
  • frontend/viewer/src/lib/activity/ActivityView.svelte
  • frontend/viewer/src/lib/services/history-service.ts

Comment on lines +119 to +125
{#if showHistoryButton}
<Button icon="i-mdi-history" onclick={() => openHistoryId = context.snapshot?.id}>
{$t`History`}
</Button>
{#if openHistoryId}
<HistoryView bind:open={() => !!openHistoryId, (open) => (open ? undefined : openHistoryId = undefined)} id={openHistoryId} selectedCommitId={activity.commitId}/>
{/if}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Render HistoryView once, outside the per-change row.

openHistoryId is shared for the whole component, but the modal is rendered inside each row. When it is set, multiple HistoryView instances can be created at once.

💡 Suggested change
-                      {`#if` openHistoryId}
-                        <HistoryView bind:open={() => !!openHistoryId, (open) => (open ? undefined : openHistoryId = undefined)} id={openHistoryId} selectedCommitId={activity.commitId}/>
-                      {/if}
+                      <!-- Keep the button here, but move HistoryView outside the row loop -->
{`#if` openHistoryId}
  <HistoryView
    bind:open={() => !!openHistoryId, (open) => (open ? undefined : openHistoryId = undefined)}
    id={openHistoryId}
    selectedCommitId={activity.commitId}
  />
{/if}
🤖 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/activity/ActivityItem.svelte` around lines 119 - 125,
Move the HistoryView component rendering outside of the per-row conditional
block that checks showHistoryButton. The HistoryView component and its wrapping
{`#if` openHistoryId} conditional should be placed after the main content loop at
the component level, so that only a single HistoryView instance is rendered
regardless of how many rows exist. Keep the HistoryView binding and props the
same, but ensure it renders once for the entire component rather than once per
row where showHistoryButton is true.

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