65044 Commits

Author SHA1 Message Date
David Langley
bd1de1998c moar 2026-03-27 17:01:51 +00:00
David Langley
15eeb22825 fix(cursors): use computeNodeAndOffset to avoid mutating window selection 2026-03-05 22:37:32 +00:00
David Langley
9b88c0b9fb fix(cursors): use selectContent for block-boundary-aware offset mapping 2026-03-05 22:26:05 +00:00
David Langley
0fd952708b fix: stable rtc object ref + per-device Matrix event filter 2026-03-05 22:19:38 +00:00
David Langley
ce1ea1c91d debug: add __docApplyDelta console helper + fix save_after type
- window.__docApplyDelta(base64) lets you manually inject a delta from
  the browser console on the receiver tab, confirming whether applyDeltaBytes
  itself works (delivery issue vs rendering issue)
- Log warning if onDeltaRef.current is null when injection is attempted
2026-03-05 21:09:16 +00:00
David Langley
a9f507520e debug: add diagnostic logging for live-update troubleshooting
- Add save_after to CollaborativeComposerModel interface (TypeScript fix)
- Log innerHTML before/after renderProjections to confirm DOM change
- Log applyDeltaBytes entry, receive_changes result, ref wiring, and
  Matrix timeline events
- Log DataReceived in useDocumentRTC for RTC message tracing
2026-03-05 21:04:07 +00:00
David Langley
0c95060797 feat: refactor DocumentView to use model as source of truth + remote cursors 2026-03-05 20:44:51 +00:00
David Langley
930cc4d51c fix(docs): stable left edge on save status indicator using min-width 2026-03-05 19:43:41 +00:00
David Langley
e39fc255a0 fix(docs): send RTC delta on every keystroke, Matrix only on 500ms debounce
Previously both channels shared the same 500ms debounce, which defeated the
point of the low-latency RTC data channel.

New behaviour:
- RTC (LiveKit): fires on every call to scheduleDeltaSend using
  save_after(lastRtcHeadsRef) to capture only the changes since the previous
  RTC publish. Updated on each send so each message is a minimal delta.
- Matrix timeline: unchanged 500ms debounce using save_incremental(), which
  has its own independent internal cursor unaffected by the RTC path.

Also advance lastRtcHeadsRef when remote deltas are applied (applyDeltaBytes)
and after the initial document load drain, so the RTC cursor never echoes
received or pre-existing content back to peers.
2026-03-05 19:40:49 +00:00
David Langley
e584c7d107 fix(docs): place cursor at start (position 0) on document load 2026-03-05 19:36:27 +00:00
David Langley
d23a768a5f fix(docs): place cursor after document content is loaded, not at WASM ready 2026-03-05 19:35:33 +00:00
David Langley
93f2a2af3c fix(docs): enforce 500ms minimum display time for Saving… indicator 2026-03-05 19:32:20 +00:00
David Langley
d21c92aa73 fix(docs): always persist deltas to Matrix timeline; add Editing/Saving/Saved status
Previously, when LiveKit/MatrixRTC was connected the debounced send would
only go to the LiveKit data channel and skip Matrix timeline events entirely.
This meant data could be lost if the user exited before the 50 ms RTC debounce
fired, since the fire-and-forget snapshot on unmount is unreliable once the
browser begins unloading.

Changes:
- Single 500 ms debounce timer (replaces the separate 50 ms RTC / 500 ms Matrix
  split).  On each debounce tick:
    1. save_incremental() captures pending local changes.
    2. If LiveKit is connected, the delta is published via the data channel for
       low-latency peer delivery.
    3. The delta is ALWAYS sent as a Matrix timeline event (or
Previously, when LiveKit/MatrixRTC was connected the debounced send would
only go to the LiveKit data channel and skip Matrix timeline events entirely.
This meant data could be lost if the user exited before the 50 ms RTC debounce
fired, since the fire-and-forget snapshot on unmount is unreliable once the
browser begins unloadi- Aonly go to the LiveKit data channel and skip Matrix timeline events entiDoThis meant data could be lost if the user exited before the 50 ms RTC debounngfired, since the fire-and-forget snapshot on unmount is unreliable once the
b mbrowser begins unloading.

Changes:
- Single 500 ms debounce timer (replacd;
Changes:
- Single 500 ms
2026-03-05 19:30:15 +00:00
David Langley
6c79900295 fix: room header height alignment and docs icon toggle colour
- Replace height:100% on .mx_DocumentView with flex:1 + min-height:0.
  In the flex-column mx_RoomView_body, height:100% resolves to the full
  parent height (not the remaining space), causing the total content to
  exceed the container and flex-shrinking the 64px RoomHeader — making
  the bottom border appear at the wrong vertical position. flex:1 takes
  only the leftover space after the header.

- Add mx_RoomHeader_toggled class to DocumentIcon when isViewingDocument
  is true, matching the same pattern used by ToggleableIcon for Threads,
  Notifications, and RoomInfo buttons. The existing CSS rule
  .mx_RoomHeader .mx_RoomHeader_toggled { fill: --cpd-color-icon-accent-primary }
  then turns the icon green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 22:35:14 +00:00
David Langley
b3118d4859 fix: add .well-known fallback for MatrixRTC discovery
MatrixRTC transport discovery for MSC4143 is unstable-only and some
deployments still advertise foci via /.well-known/matrix/client
(org.matrix.msc4143.rtc_foci).

Try /unstable/org.matrix.msc4143/rtc/transports first, then fall back to
well-known so LiveKit can be used where configured.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 22:22:11 +00:00
David Langley
63075503c2 fix: drain save_incremental cursor after load/receive to prevent M_TOO_LARGE
After load_document() + receive_changes() replay, the Automerge incremental
save cursor is not advanced — so the next save_incremental() returns the
entire document history, not just new local edits. For a document with 90+
deltas this easily exceeds the Matrix 65KB encrypted event size limit.

Fix: call save_incremental() and discard the result:
1. After replaying timeline deltas on load (prevents 413 on first keystroke)
2. After receive_changes() in applyDeltaBytes (prevents remote changes from
   being included in the next scheduled delta send)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 22:13:29 +00:00
David Langley
0e535724cb feat(docs): LiveKit data channel transport for real-time delta sync
Replace per-keystroke Matrix timeline events with a LiveKit data
channel for real-time Automerge delta delivery:

- Add livekit-client dependency
- New useDocumentRTC hook:
  - Calls client._unstable_getRTCTransports() to get SFU URL
  - Exchanges Matrix OpenID token with SFU (/sfu/get) for LiveKit JWT
  - Connects to LiveKit room in data-only mode (autoSubscribe: false)
  - Exposes publishDelta(bytes) and onDeltaRef callback
  - Falls back gracefully if no LiveKit transport configured
- DocumentView wiring:
  - useDocumentSync now accepts optional rtc transport
  - When LiveKit connected: send deltas via data channel (50ms debounce)
  - When LiveKit not available: fall back to Matrix timeline events (500ms)
  - Matrix event listener disabled when LiveKit is connected
  - Snapshots still go via Matrix state events (every 20 deltas + on close)
  - __docDebug() now shows rtcConnected status

Benefits: no homeserver rate limiting on deltas, ~50ms latency vs
~500ms+sync, no delta events cluttering the room timeline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 22:08:29 +00:00
David Langley
8c45fe3736 feat(docs): add window.__docDebug() console diagnostic
Exposes a lightweight function on window that returns CRDT heads, HTML,
doc hash, DOM HTML, timeline delta count/senders, and snapshot info.
Call it on both clients and compare to pinpoint divergence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 21:50:39 +00:00
David Langley
1e225789a0 fix(docs): robust snapshot strategy with delta replay on load
Previous design saved a full snapshot to room state after every delta
send. This caused rate-limiting under rapid typing, meaning the snapshot
could be stale by many deltas. On refresh, only the snapshot was loaded
— any deltas not included were lost.

New design:
- Snapshots saved to room state only every 20 deltas (SNAPSHOT_EVERY_N_DELTAS)
- On unmount (close/navigate away), flush any pending delta and always
  save a final snapshot so the state event stays reasonably fresh
- On load, after loading the snapshot from room state, replay all delta
  timeline events newer than the snapshot timestamp. receive_changes()
  is idempotent so re-applying already-merged deltas is a harmless no-op
- Include heads in snapshot content for future dedup optimization

This ensures no data is lost even if the snapshot is stale, and avoids
hammering the homeserver with state event updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 21:29:52 +00:00
David Langley
3f42a0bcd3 fix(docs): fix MutationObserver feedback loop and own-event echo
Two root causes of instability:

1. Receiver re-sends deltas it just received: MutationObserver callbacks
   are async microtasks, so setting suppressMutations=false synchronously
   after innerHTML= was too early — the observer callback hadn't fired
   yet. Fix: defer the reset via requestAnimationFrame() so the flag is
   still true when the observer's microtask runs.

2. Sender's own events echo back and rewrite DOM: in encrypted rooms
   device_id is not reliably in event.getUnsigned(), so the per-device
   skip check failed and the sender's own deltas were treated as remote.
   This caused innerHTML to be set from get_content_as_html() which may
   differ from the WASM useListeners DOM output (e.g. <p> vs <br> for
   newlines), collapsing structure. Fix: skip by userId alone — the
   local model already has our changes from save_incremental(), so
   applying our own events is both unnecessary and harmful.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 21:16:23 +00:00
David Langley
5c5b3d7215 fix(docs): restore missing toHex function declaration
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:29:59 +00:00
David Langley
6ea1ac6565 fix(docs): preserve caret on remote updates; send deltas for formatting changes
Two fixes:

1. Caret reset on remote delta: when receive_changes() was applied and
   innerHTML was set, the browser lost the caret position. Fix: save the
   caret as a character offset before the update and restore it via a
   TreeWalker walk after. A suppressMutations flag prevents the
   MutationObserver from incorrectly scheduling a local delta send while
   the remote HTML is being written.

2. Formatting/structural edits not sending deltas: onInput doesn't fire
   for toolbar actions (bold, italic, heading, etc.) because those are
   applied programmatically via the WASM model. Fix: attach a
   MutationObserver to the contentEditable div that calls
   scheduleDeltaSend() on any DOM change (childList, subtree,
   characterData, attributes). The observer is suppressed during remote
   innerHTML writes to avoid re-sending remote changes back.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:29:14 +00:00
David Langley
e7bb3bf146 fix(docs): handle encrypted rooms by listening to MatrixEventEvent.Decrypted
In encrypted rooms, Room.timeline fires with type m.room.encrypted so the
org.element.doc.delta type check always fails. The event only has its real
type after decryption.

Fix: also listen on client MatrixEventEvent.Decrypted, which fires once the
event is fully decrypted with the correct type. Keep Room.timeline for
unencrypted rooms. Both listeners share the same applyDeltaEvent handler.
Since applyDeltaEvent is idempotent (receive_changes is a CRDT merge), it
is safe for an unencrypted event to be processed by both listeners.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:23:48 +00:00
David Langley
a850212ea8 fix(docs): fix live delta reception using stable ref-based listener
The timeline listener was inside an effect gated on isCollaborative(composerModel).
When composerModel was null on first mount the effect returned early without
registering the listener. By the time the model became available the effect
re-ran — but onContentChanged had a new identity each render, causing the
listener to be torn down and re-added on every re-render.

Fix:
- Use a composerModelRef so the listener closure always reads the latest
  model without the effect needing composerModel as a dependency.
- Register the listener unconditionally (guard inside the handler instead),
  so it is set up on mount regardless of when the model initialises.
- Use a stable notifyContentChangedRef so the effect deps don't change,
  preventing unnecessary listener churn.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:16:31 +00:00
David Langley
a906d3826b fix(docs): remove duplicate useDocumentSync call
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:10:49 +00:00
David Langley
99f2c2e063 fix(docs): fix real-time sync, placeholder visibility, and same-user multi-device
Three fixes:

1. Real-time remote updates: the sender check compared only userId, so
   testing with the same account on two devices caused all events to be
   skipped. Now also compares device_id from event.getUnsigned() so only
   events from this exact device are skipped.

2. Placeholder persists: Editor always received the placeholder prop so
   the ::before pseudo-element never disappeared. Now track hasContent
   state (updated on input and on remote content load) and pass
   placeholder={undefined} when the editor has content — matching how the
   send composer conditionally hides its placeholder.

3. Text appearing below placeholder: same root cause as above — with the
   placeholder ::before always present and the content in a block <p>,
   text rendered on the line below the overlay. Removing the placeholder
   class when content exists fixes the layout.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:09:21 +00:00
David Langley
b8dc0e0796 fix(docs): fix actor ID encoding, remote delta DOM update, and snapshot persistence
Three bugs fixed:

1. Actor ID was passed as raw string (e.g. '@user:server:deviceId') but
   set_actor_id() requires a hex-encoded byte string. Fix: encode the
   userId:deviceId string as UTF-8 hex before passing to set_actor_id().

2. receive_changes() updated the Automerge CRDT model but the editor DOM
   was never updated, so remote edits were invisible. Fix: after calling
   receive_changes(), set editorRef.current.innerHTML to the result of
   get_content_as_html().

3. No room state snapshot was saved after editing, so the document was
   lost on refresh. Fix: after each successful delta send, also call
   sendStateEvent with a full save_document() snapshot.

Also adds get_content_as_html() to the CollaborativeComposerModel interface
and passes editorRef into useDocumentSync.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 14:01:52 +00:00
David Langley
867dc78791 fix(docs): always mount Editor so WASM model initialises; remove debug logging
Root cause of isWysiwygReady never becoming true: the early-return loading
state prevented the <Editor> from mounting, so ref.current was null when
useComposerModel's effect ran — which guards initModel() behind
'if (editorRef.current)'. With no element in the DOM, initAsync was never
awaited and composerModel stayed null forever.

Fix: always render the Editor regardless of isLoaded. The toolbar is still
hidden during loading; only the formatting buttons are deferred.

Also removes all temporary debug logging added during investigation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:53:39 +00:00
David Langley
1134c7cb56 fix(docs): link @vector-im/matrix-wysiwyg-wasm locally and add debug logging
The RTE dist bundle imports @vector-im/matrix-wysiwyg-wasm as an external
dependency. Without this package resolving, initAsync never fires and
isWysiwygReady stays false permanently — keeping the editor disabled
(contentEditable=false) so focus() is silently ignored.

Fix: add pnpm override and manual symlink for @vector-im/matrix-wysiwyg-wasm
pointing to the local bindings/wysiwyg-wasm build.

Also adds temporary console logging to DocumentView to confirm click
handling, focus state, and editor readiness.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:33:00 +00:00
David Langley
706a151e19 fix(docs): stop LoggedInView from hijacking keypresses in document view
LoggedInView has a global keydown handler that redirects any printable
character typed outside an input/contenteditable to the send-message
composer. When the document editor's outer wrapper div (tabIndex=-1) has
focus (i.e. before the first click reaches the contentEditable), this
handler fires and dispatches FocusSendMessageComposer, visibly stealing
all keystrokes.

Fix: skip the redirect when the active element is inside .mx_DocumentView.
Same guard added to the paste handler.

Also simplify handleContentClick to always call ref.current.focus() on
any click inside the content area, not just clicks that miss the editor
div — this ensures focus is reliably placed in the contentEditable even
when the click lands on a wrapper div.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:23:41 +00:00
David Langley
a3875fc854 fix(docs): fix document editor focus and cursor issues
Three problems prevented the document editor from being usable:

1. The contentEditable div inherited composer sizing (~22px tall) so
   clicks in the document content area landed outside it and bubbled up
   to the mx_RoomView div[tabIndex=-1], making the whole room view flash
   blue instead of focusing the editor. Fixed by making the Editor stack
   fill the full content area height in _DocumentView.pcss.

2. useSetCursorPosition was not called, so even when the editor received
   focus there was no selection range and no visible cursor. Added the
   hook call (same pattern as WysiwygComposer).

3. Any click outside the now-tall contentEditable div (e.g. in padding)
   still fell through. Added an onClick handler on the content wrapper
   that calls ref.current.focus() when the click target isn't the editor
   itself, ensuring any click in the document area focuses the editor.

Bonus: suppress the browser default outline on .mx_RoomView:focus so
pressing a key no longer shows a blue box around the entire room view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:18:46 +00:00
David Langley
2853fefc16 fix(docs): add ComposerContext.Provider and link local RTE build
- Wrap DocumentView's Editor in ComposerContext.Provider so useSelection
  (used internally by the Editor) has the required context, enabling
  focus and keyboard input to work correctly.
- Add pnpm override pointing @vector-im/matrix-wysiwyg at the local
  matrix-rich-text-editor/platforms/web build so the automerge
  collaboration API (composerModel, useCollaboration, etc.) is available
  when running element-web locally end-to-end.
- Remove the wysiwyg patchedDependency entry (superseded by the override).
- Update pnpm-lock.yaml to reflect the link resolution.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:11:31 +00:00
David Langley
6245a5a5a0 feat(docs): add collaborative document view to rooms
Add a new 'Document' content type alongside Timeline/Call/Widget that
shows a full-room Automerge-backed collaborative editor.

Changes:
- Add `MainSplitContentType.Document` to RoomContext enum
- Add `view_document?: boolean` to ViewRoomPayload
- Track `viewingDocument` state in RoomViewStore with `isViewingDocument()`
- Update RoomView.getMainSplitContentType to return Document when active,
  and render <DocumentView> in the main split switch statement
- Add document toggle button (📄) to RoomHeader using the Compound
  IconButton / Tooltip pattern
- New DocumentView component:
  - Uses `useWysiwyg` for the rich text editor surface
  - Loads initial document from room-state event (org.element.doc.automerge)
  - Sends incremental Automerge deltas (debounced 500 ms) as
    org.element.doc.delta timeline events
  - Receives and applies remote deltas from other room participants
  - Full-height document layout with formatting toolbar
- New _DocumentView.pcss stylesheet + import in _components.pcss
- i18n strings: room.document.open / room.document.close

The collaboration methods (save_incremental, receive_changes, etc.) are
guarded by the isCollaborative() runtime type-check so the component
degrades gracefully with the current 2.40.0 npm package until the
langleyd/automerge build is published.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 12:55:55 +00:00
renovate[bot]
ac37bebf22
Update dependency caniuse-lite to v1.0.30001774 (#32549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 19:42:28 +00:00
renovate[bot]
699a8759c5
Update ghcr.io/element-hq/synapse:develop Docker digest to b256d74 (#32695)
* Update ghcr.io/element-hq/synapse:develop Docker digest to b256d74

* Update screenshot due to new API availability on Synapse

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-03-03 19:42:09 +00:00
Florian Duros
93dc9fedc8
Room list: remove direct usage of the old rls in rls v3 (#32692)
* refactor: move `DefaultTagID` and `TagID` to rls v3

Move the enum and type in rls v3 and update imports

* refactor: move `getChangedOverrideRoomMutePushRules` from rls to rls v3

* refactor: replace `VisiblityProvider` by `isRoomVisible` and move it to rls v3
2026-03-03 19:25:20 +00:00
renovate[bot]
49dffe83cc
Update dependency @vector-im/compound-design-tokens to v6.10.1 (#32698)
* Update dependency @vector-im/compound-design-tokens to v6.10.1

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-03-03 19:17:40 +00:00
renovate[bot]
2c9f55cbea
Update npm non-major dependencies (#32702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 18:19:50 +00:00
renovate[bot]
b6dbe1c259
Update pnpm to v10.30.3 (#32703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 17:52:55 +00:00
renovate[bot]
e19e338aa9
Update dependency @sentry/webpack-plugin to v5 (#32704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 17:41:57 +00:00
renovate[bot]
3f69aec64a
Update dependency css-minimizer-webpack-plugin to v8 (#32706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 17:41:05 +00:00
David Langley
cea684c065
Implement new widget permissions module api (#32565)
* Add widget lifecycle API at top level

* Integrate while still falling back to the legacy api

* Remove WidgetKind

* Update module api

to the one that includes the new widget lifecycle api

* lint

* Make preload checks easier to understand

- Have single code path for preload checks.
- Remove duplicated logic for preapproveIdentity check
- Fix headers

* lint
2026-03-03 17:06:39 +00:00
renovate[bot]
611e924dc2
Update dependency wrap-ansi to v10 (#32707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 17:04:28 +00:00
renovate[bot]
e439d6adc1
Update eslint-plugins (#32701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 16:51:43 +00:00
renovate[bot]
255f9f03e9
Update dependency wrap-ansi-cjs to v10 (#32708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:52:23 +00:00
renovate[bot]
7a2f092c6d
Update GitHub Artifact Actions (#32709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:50:22 +00:00
renovate[bot]
580949038c
Update dependency copy-webpack-plugin to v14 (#32705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:41:05 +00:00
renovate[bot]
ba0365d04e
Update storybook to v10.2.13 (#32699)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:40:40 +00:00
renovate[bot]
b0e12b829f
Update typescript (#32700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:40:39 +00:00
renovate[bot]
13b070d03d
Update nginxinc/nginx-unprivileged:alpine-slim Docker digest to 800307a (#32696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:40:25 +00:00