Skip to content

Sync with upstream - #73

Merged
ArtyomSavchenko merged 38 commits into
developfrom
sync-upstream-3-aug
Aug 3, 2026
Merged

Sync with upstream#73
ArtyomSavchenko merged 38 commits into
developfrom
sync-upstream-3-aug

Conversation

@ArtyomSavchenko

Copy link
Copy Markdown
Collaborator

No description provided.

ArtyomSavchenko and others added 30 commits August 1, 2026 07:51
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
(cherry picked from commit 7cf280694c7569cea0a2734923226163dff49aaa)

Co-authored-by: Artyom Savchenko <armisav@gmail.com>
Adapted from intabia-fusion f475337671 (Hide notification groups).
Ported by hand rather than cherry-picked: NotificationSettings.svelte
has diverged from intabia's base, so the fix is re-applied against our
current structure using the existing isDisabled() helper from
@hcengineering/presentation.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Kristina Fefelova <kristin.fefelova@gmail.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Ported from intabia-fusion (WebpushesPreferencesPresenter.svelte and
related plugin/model plumbing), rewritten by hand against our current
notification plugin rather than cherry-picked, since the source
component never existed in this fork.

- plugins/notification: new PushSubscriptionSetting class, name field
  on PushSubscription, WebpushesPreferencesPresenter component id,
  new IntlStrings.
- models/notification: TPushSubscriptionSetting model, wired as the
  presenter for PushNotificationProvider.
- notification-resources/utils.ts: parseUserAgent() helper, subscribePush()
  now records navigator.userAgent as PushSubscription.name, exported
  getPushPublicKey() for the settings screen's disabled-state checks.
- New WebpushesPreferencesPresenter.svelte: list of a user's webpush
  subscriptions (device/browser label, per-device enable toggle, remove
  with confirmation, current-device indicator) + Subscribe button
  wired to the existing subscribePush(). Simplified vs. intabia's
  version: our subscribePush() returns a plain boolean rather than a
  granular error union, so the failure path shows one generic
  PushSubscribeError message instead of per-cause messages (permission
  denied / network / not supported). That granularity can be added
  later by extending subscribePush()'s return type.
- en/ru locale strings for the new UI.

Verified: tsc --noEmit clean for plugins/notification, models/notification
and notification-resources/src (only pre-existing, unrelated
presentation-package errors remain). Full svelte-check of the .svelte
file was not completed in this session (workspace-wide svelte-check on
this monorepo exceeds the available command timeout) - recommended
follow-up before merge.

This unblocks (fully or partially, still needs individual review)
the 8 group-2.1 webpush bugfix commits that targeted this screen:
545279381f, 1f502582ea, 0096aaabed, 0f64f1cda9, 2b9ec98816, b90ecd98a1,
844f057b8d, 0d6169c9cc.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Elena Ostapenko <lunopad@gmail.com>
… is removed

Adapted from intabia-fusion 360f67af4a (Fix inbox selection on delete).
Not a direct port: intabia's InboxGroupedListView uses 'clear' semantics
and a different displayData/selection model than ours (which has
archive/unarchive). Ported only the underlying idea - track the last
known-valid selection index (stableIndex) instead of recomputing
highlightIndex via findIndex every render, which briefly returns -1
(and drops the highlight) for one render after the selected context
is filtered out of displayData, before the parent prop updates.

Also carries the one safely-portable piece of that commit as-is:
ListView.svelte now passes the actual array item as a 'value' slot
prop alongside the index (purely additive, no consumer is required to
use it) so consumers like InboxGroupedListView can avoid re-deriving
the item from a possibly-stale index.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Kristina Fefelova <kristin.fefelova@gmail.com>
…sion

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Adapted from intabia-fusion da03810af4 (Fix common notification
pushes). That commit was mostly a props->intlParams rename across
several services (export/github/calendar-mailer) working around a bug
where CommonInboxNotification push content only read intlParams. We
don't have that bug: commonInboxNotificationToText() already merges
doc.props into params, so push/inbox/email all already pick up props
correctly - confirmed by reading server-plugins/notification-resources/
src/index.ts. The rename is a no-op for us and was skipped.

What *was* a real, applicable bug: OnUserMeetingInvite in love-resources
built the notification with 'props: { name: "" }' - an empty string
that (since props is merged after content's own intlParams) blanked out
the {name} placeholder in love.string.InvitingYou ("{name} is asking
you to join"), so meeting-invite notifications always showed a blank
inviter name. Fixed by resolving the actual sender name via
formatName(senderInfo.person.name, ...), same helper used by
getSenderName() elsewhere in the notification pipeline.

The request-resources part of that commit (propsIntl on
sendRequestCreateNotification/removeRequestCreateNotification) doesn't
apply either - those functions don't exist in this form in our fork.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Kristina Fefelova <kristin.fefelova@gmail.com>
Adapted from intabia-fusion 8b7182d321 (Fix notification translate).
Real bug in translate()/translateCB(): the whole function body was one
try/catch funnelling every error - including IntlMessageFormat#format()
throwing because of missing/invalid params - into
handleIntlPipelineFailure(), which caches a permanent Status failure
for that message. So one call with bad params (e.g. a notification
whose intlParams were missing a placeholder) would mark the message as
untranslatable forever, breaking every subsequent (valid) call for the
rest of the session/until reload.

Fix: format() failures are now caught separately and just fall back to
returning the raw message for that one call, without touching the
compiled-message cache. Compile/lookup failures (bad id, no loader,
etc.) still go through handleIntlPipelineFailure and get cached as
before - that part of the behavior is intentional and unchanged.

Ported the two regression tests from the same commit (cache-poisoning
for translate and translateCB) as-is - they matched our test file's
existing structure. Full package test suite (31 tests) passes.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Kristina Fefelova <kristin.fefelova@gmail.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
… on thread replies

Adapted from intabia-fusion 2b9ec98816 (Fix push notifications links).
Ported the server-side (push.ts) part only - the desktop/Electron IPC
side (desktop/src/ui/notifications.ts) has the same underlying bug but
is left for separate follow-up since it needs the equivalent fix
mirrored into a different file/function and I want to keep this change
narrowly scoped and verifiable.

Bug: createPushFromInbox() built the push notification's link straight
from n.objectId/n.objectClass. For a reply inside a thread, those point
at the ThreadMessage itself, which isn't a directly navigable page -
clicking the push notification for a thread reply would fail to
resolve to a real page/would land on the wrong document.

Fix: added getObjectIdentity(), which detects the thread-reply case
(attachedToClass derived from chunter.class.ThreadMessage) and resolves
to the ThreadMessage's own objectId/objectClass (the document the
thread is actually attached to) instead. Non-thread notifications are
unaffected (identity passthrough).

Verified: tsc --noEmit and eslint clean for
server-plugins/notification-resources. No existing test suite in this
package to run (none exists).

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Kristina Fefelova <kristin.fefelova@gmail.com>
…ribe errors

Two related intabia-fusion fixes landed together in this commit (staged
files got combined by an earlier scripting mistake on my end - not
worth unpicking into two commits after the fact, both are verified and
intentional):

1. packages/ui/src/tooltips.ts - adapted from 0f64f1cda9 (Fix creating
   webpush subscription after deleting). The webpush settings screen
   uses use:tooltip={{ label: disabledReason }} where disabledReason
   starts undefined and later becomes a string reactively. The old
   tooltip() action had an early return with a
   "TODO: Fix reactive options update in this case" when initial
   options had no label/component, so it never attached the
   mousemove/mouseleave listeners - a tooltip starting empty and later
   getting content would silently never show. Fixed by always
   returning the action object and attaching/detaching listeners in
   update() based on content transitions. This is a general fix to a
   shared component, not webpush-specific.

2. plugins/notification(-resources)/*, notification-assets/lang/*.json -
   adapted from b90ecd98a1 (Push subscription errors for Edge).
   Edge doesn't implicitly prompt for notification permission from
   pushManager.subscribe() the way Chrome/Firefox do, so subscribing
   could silently fail there - fixed by calling
   Notification.requestPermission() explicitly first. Also upgrades
   subscribePush()'s return type from boolean to
   'success' | 'permission_denied' | 'network_error' | 'not_supported'
   so the settings screen can show the actual failure reason instead
   of one generic message (this was called out as a follow-up in the
   original webpush-screen commit). Updated all three call sites:
   WebpushesPreferencesPresenter.svelte (shows the specific error),
   BrowserNotificatator.svelte (was doing `if (isSubscribed)` on the
   old boolean - every string in the new union is truthy, so this had
   to become an explicit `=== 'success'` check), and
   Notification.svelte (unaffected - never inspected the return value).

Verified: tsc --noEmit and eslint clean for packages/ui,
plugins/notification, plugins/notification-resources (only
pre-existing, unrelated presentation-package errors remain).

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Elena Ostapenko <lunopad@gmail.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Storage (account-service):
- Postgres migration v28: workspace_members.has_unread BOOLEAN DEFAULT FALSE.
- AccountDB.setWorkspaceMemberUnread(account, workspace, hasUnread), implemented
  for both Postgres and Mongo backends.
- getAccountWorkspaces() now selects/returns the flag as
  WorkspaceInfoWithStatus.hasUnread (new optional field on the core interface).

RPC surface:
- New account-service method setWorkspaceMemberUnread, exposed via
  utils.ts/operations.ts following the updateWorkspaceRole pattern.
  Authorization: raising the flag (hasUnread=true) for any account, or clearing
  someone else's, requires a 'notification' service token; clearing your own
  flag is self-service (no special privileges needed).
- AccountClient.setWorkspaceMemberUnread added to @hcengineering/account-client.

Trigger:
- New OnInboxNotificationCreate trigger in server-plugins/notification-resources
  (registered in models/server-notification), fires on every InboxNotification
  creation independent of push-notification settings. Reports hasUnread=true to
  account-service for each receiver using a workspace-scoped service token
  (generateToken + getAccountClient, mirroring the calendar-resources pattern
  for calling out to external services from an in-process trigger). Best-effort:
  failures are logged, never block notification delivery.
- Added @hcengineering/server-client and @hcengineering/server-token as
  dependencies of server-plugins/notification-resources.

Not yet done (tracked in NOTIFICATIONS_PORT_PLAN.md): client-side self-clear
call when a user's local unread count in a workspace hits zero, and the
frontend badge in SelectWorkspaceMenu.svelte.

Verified with tsc --noEmit on: server/account, foundations/core/packages/core,
foundations/core/packages/account-client, server-plugins/notification-resources,
server-plugins/notification, models/server-notification. No live account-service/
Postgres available in this sandbox to integration-test the migration or RPC path.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
- SelectWorkspaceMenu.svelte: renders a small NotifyMarker dot next to a
  workspace's name when WorkspaceInfoWithStatus.hasUnread is true and it's
  not the currently open workspace.
- Workbench.svelte: when the local inbox transitions from "has unread" to
  "all read" (existing HasInboxNotifications reactive check), calls the new
  reportWorkspaceRead() helper to self-clear the flag for this account+workspace.
- workbench-resources/utils.ts: reportWorkspaceRead() — self-service call to
  AccountClient.setWorkspaceMemberUnread(myAccountUuid, false) using the
  existing login.metadata.AccountsUrl / presentation.metadata.Token, mirroring
  the existing logIn/logOut account-client usage in this file.

Raising the flag remains server-side only (OnInboxNotificationCreate trigger,
previous commit) — the client never sets it to true, only clears its own.

Verified with tsc --noEmit + eslint on the touched .ts sources (0 errors).
Svelte template changes reviewed by hand — svelte-check itself could not be
run to completion in this sandbox (exceeds the available command timeout),
so a full svelte-check pass before merge is still recommended.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
…mselves)

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
…/server-token deps

The port added @hcengineering/server-client and @hcengineering/server-token
to server-plugins/notification-resources/package.json but the shrinkwrap
was not regenerated, so 'rush install' failed the shrinkwrap validation and
the entire build job (and every job that needs it) errored out. Ran
'rush update' to add the two workspace links to the lockfile.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ntWorkspaces expectation

getAccountWorkspaces now returns a per-workspace hasUnread field, which broke
the existing mongo unit test that asserted the raw workspace objects. Update
the expectation and strengthen it to assert the true/false/default mapping.

Add unit coverage for setWorkspaceMemberUnread authorization in both the mongo
collection (flag write) and the RPC handler (utils): raising another member's
flag requires the notification service token, a member may self-clear their
own flag without it, no token can raise its own flag, and the write always
targets the token's workspace rather than any caller-supplied value.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…atures, buttonDisabled)

The formatting CI job flagged three pre-existing prettier violations in the
ported code that were never run through 'rush format': the long
setWorkspaceMemberUnread signatures in the mongo/postgres account collections
and the buttonDisabled reactive statement in WebpushesPreferencesPresenter.
Applied the repo format pipeline (prettier + eslint --fix) to just these files.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The port added 15 new notification strings (webpush subscribe/remove flow and
push error states) to en and ru only. Add translations for the other 12
locales (cs, de, es, fr, it, ja, ko, pl, pt, pt-br, tr, zh) so the web push
settings screen isn't left English-only there. Placeholders ({app}, {title},
{value}) and <b> markup preserved; Value is a passthrough.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ace unread works

The OnInboxNotificationCreate trigger runs inside the transactor (pods/server)
and reports unread notifications to the account service via getAccountClient()
from @hcengineering/server-client, which resolves the account URL from
serverClient.metadata.Endpoint. The transactor never set that metadata (it only
kept accountsUrl as a local for its own session login), so the RPC had no
endpoint, failed, was swallowed by the trigger's fire-and-forget catch, and
workspace_members.has_unread was never raised — the switcher dot never lit up.

Set serverClient.metadata.Endpoint from config.accountsUrl at transactor
startup (adding @hcengineering/server-client as a direct dependency).

Also add a unit test for OnInboxNotificationCreate covering the behaviour the
feature depends on: one account-service call per distinct receiver with the
notification-scoped service token, skipping already-viewed notifications and
the read-only guest, and swallowing account-service failures.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nsumer

Replaces the per-receiver account RPC fan-out from the notification trigger
with a queue-based path, so a channel-wide mention no longer becomes an RPC
storm against account-service (the shared chokepoint).

- server-core: new QueueTopic.WorkspaceMemberUnread + WorkspaceMemberUnreadMessage
  { accounts }; kafka registers the topic (2 partitions).
- trigger (OnInboxNotificationCreate): collapses a whole notification batch into
  a single fire-and-forget produce keyed by workspace, instead of one account
  RPC per receiver. Drops the server-client/server-token deps it no longer needs.
- account-service: consumes the topic on a stable group (work-queue semantics)
  and raises the flag via a new bulk AccountDB.setWorkspaceMembersUnread, writing
  the DB directly — no RPC or service token on the set path. Degrades quietly
  when no QUEUE_CONFIG is present.
- account DB: setWorkspaceMembersUnread updates all members in one statement,
  guarded by has_unread <> target so a repeat broadcast writes nothing.
- Revert the transactor serverClient.metadata.Endpoint wiring: the trigger no
  longer calls the account RPC, so it is no longer needed.

Client self-clear (single-account RPC) is unchanged. Unit tests cover the
trigger producer, the consumer handler, and the bulk DB method.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…:^0.7.18

rush check requires one consistent version specifier per dependency across the
monorepo. The whole repo pins @hcengineering/kafka at workspace:^0.7.18; I had
added it to account-service as ^0.7.20 (its package version), which tripped
'Found 1 mis-matching dependencies' in the rush check gate and failed build +
all uitest* jobs before the app stack even started. Align to ^0.7.18.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n, workbench and desktop

Makes the cross-workspace unread indicator a first-class, live signal instead
of a load-time snapshot only visible inside the open switcher.

Foundation (workbench utils):
- refreshWorkspaces() re-fetches workspacesStore (incl. per-workspace hasUnread);
  Workbench polls it (~45s) and on window focus/visibility, since the flag is
  raised server-side in workspaces the client isn't connected to.
- hasCrossWorkspaceUnread derived store: single source of truth (any workspace
  other than the current has unread).
- reportWorkspaceRead() now optimistically clears the current workspace's flag
  locally so switching away and back is instant.

Surfaces:
- Change workspace / already logged in: covered by the switcher's on-open refetch
  plus the new focus + interval refresh.
- Login SelectWorkspace: a dot on each workspace that has unread.
- Working in a workspace: a NotifyMarker on the workbench workspace logo whenever
  another workspace has unread (always visible, not just in the open switcher).
- Desktop: the dock/taskbar/tray badge folds in cross-workspace unread as one
  extra count (same additive pattern as in-workspace + communication unread), so
  the app icon lights up even when the current workspace is fully read.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o-op flag writes

Review follow-up:

- Self-clear (reportWorkspaceRead) previously fired only on the has-unread ->
  all-read transition, so a session that loaded already-read left a stale-true
  server flag (e.g. items read on another device) sticky until something else
  cleared it. Now it also clears once on load when the local inbox is
  empty-and-read, tracked by a flag so it fires at most once per read state.

- Guard the single-account setWorkspaceMemberUnread with 'has_unread <> target'
  (postgres) / hasUnread $ne (mongo) so the on-load clear is a no-op write when
  the flag is already in the desired state, matching the bulk variant.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Pause the 45s workspacesStore poll while document.hidden; the
  visibilitychange/focus handler already refreshes on return, so backgrounded
  tabs stop hitting GetWorkspaces.
- Drop the hardcoded English title on the login unread dot (i18n gap); the dot
  keeps NotifyMarker's color token, so no visual inconsistency.
- Document that the raise path now goes through the WorkspaceMemberUnread queue,
  so setWorkspaceMemberUnread's service-token branch is a guarded fallback rather
  than the normal raise path (kept intentionally to keep the endpoint safe).

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… for diagnosis

The trigger only logged on error, making the producer half a black box. Add an
info log when the trigger publishes to the WorkspaceMemberUnread queue (with
workspace + receiver count) and a warn when control.queue is unexpectedly
absent, plus an info in the account-service consumer when it applies a batch.
Lets an operator confirm end to end where the chain breaks (produce vs consume).

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…opic

The cross-workspace unread topic is produced by region-specific transactors
(REGION=<region>) but consumed by the single global account-service (no REGION).
getKafkaTopicId prefixes topics with the producer's region, so the transactor
published to 'cockroach.workspace-member-unread' while account-service consumed
the bare 'workspace-member-unread' — the message was never delivered and
has_unread was never raised (producer logged 'published', consumer never
'applied').

Mark WorkspaceMemberUnread as a global topic so both ends resolve to the same
name regardless of region. Postfix (staging/prod) is preserved.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the unread dot subtler in the workspace switcher list and on the collapsed
workspace logo (x-small -> xx-small), kept consistent across both.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
claude and others added 8 commits August 2, 2026 16:29
…xed orange

The fallback initial box in the workspace rail logo was hardcoded to one brand
orange for every workspace, while the Settings workspace avatar generates its
color from the name (AvatarType.COLOR) — so the same workspace looked orange in
the rail but, say, blue in settings. Generate the rail box color from the
workspace name via getPlatformColorForText so both match. Uploaded custom icons
are unaffected.

(Unrelated to the cross-workspace notifications work; small consistency fix.)

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nto the logo

Now that workspace logos are coloured by name, a red unread dot could sit on a
red logo and disappear. Add a background-coloured ring around the marker on the
workspace logo (standard avatar-badge cutout) so it stays visible on any logo
colour.

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ocks

The module-level `globalTopics` Set referenced QueueTopic at import time,
which failed under a circular import (server-core's enum not yet
initialized) with "Cannot read properties of undefined (reading
'WorkspaceMemberUnread')". Replace it with an isGlobalTopic() function
that reads the enum at call time.

Also add the info/warn stubs to the trigger and consumer ctx test mocks
so the new diagnostic logging in push.ts and unread.ts no longer throws
"ctx.info is not a function" / "control.ctx.warn is not a function".

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
The web-push settings screen writes PushSubscriptionSetting.enabled per
device, but the server push path gathered subscriptions filtered only by
receiver and never joined against the setting — so toggling a device off
was a no-op and pushes kept arriving.

Filter out subscriptions whose setting has enabled === false in
PushNotificationsHandler (absence of a setting means enabled). Extract
the join as a pure filterEnabledSubscriptions() helper and unit-test it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012NwjLAe9GVw1QeYjkhu72R
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Notification fixes + cross-workspace unread indicator
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
@ArtyomSavchenko
ArtyomSavchenko merged commit a6e7171 into develop Aug 3, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants