Skip to content

chore: main to 1.47.0-beta.dev-1784059879-fix1 (admin-cms version) - #217

Merged
volarname merged 1108 commits into
mainfrom
chore/main-from-1.47.0-beta.dev-1784059879-fix1
Sep 28, 2026
Merged

volarname merged 1108 commits into
mainfrom
chore/main-from-1.47.0-beta.dev-1784059879-fix1

Conversation

@volarname

Copy link
Copy Markdown
Contributor

Brings main up to tag 1.47.0-beta.dev-1784059879-fix1 (9f26b68), the version admin-cms pins on both develop and master.

  • Branch = the tag + a merge of main. There were no conflicts.
  • main's only commits missing from the tag are scraper docs (Omny clip, Sportnet video; 3 commits, docs/ only). The merge keeps them.
  • Compared with the tag, the only changes are in docs/, all from main. The code is identical to what admin-cms runs.

Merge with a merge commit (not squash or rebase), so main's history includes the tag's commits and later branches from the tag merge into main cleanly.

🤖 Generated with Claude Code

volar and others added 30 commits November 19, 2025 11:13
Add approveRequestBlocked prop for ACollabManagement and emit approve…
volar and others added 29 commits July 27, 2026 11:41
The one-shot reconnect flag was burned by the first transient network error, so a
later JWT expiration never triggered a token refresh. Transient errors are also no
longer sent to Sentry, socket.io retries them on its own.
Promise.all discarded the successfully loaded image form when the audio or video
form returned 403, which broke image upload for users without those permissions.
Both locales shipped the English sentence, so consumers inheriting the library
error messages showed English to Slovak and Czech users.
Unresolved ids handed back through add() are already in the cache, so counting
them as incoming inflated the overflow and evicted more entries than needed.
Also exposes isUnresolved as the terminal counterpart of isLoaded.
useDebounceFn's maxWait timer is a second invocation path whose promise never
reaches the caller, so catching on the returned promise still let a rejection
escape as an unhandled rejection. Adds the missing tests for the state machine.
The server serialises join and leave per room only for the lifetime of
its Redis lease, not of the action holding it, so a leave whose work
outruns the lease lets a following join acquire, finish and acknowledge
first. The leave then reports the membership it read before that join and
marks a live room inactive, after which changeCollabAnyData and lock
acquisition return early and the client goes quiet with nothing visible
to show for it.

Every acknowledgement that writes collabRoomInfoState now claims a
sequence number first and writes only if it is still the newest. The
counter is global and never resets: a per-room one cleared on reconnect
hands the same number out twice, and an acknowledgement claimed before
the reconnect would match the one claimed by the re-join after it.

A claim that ends in failure writes nothing yet still suppresses older
acknowledgements, so a failed join records an explicitly inactive room
rather than deleting the entry. The mutation guards test for an entry
that is inactive; a missing one falls straight through and emits.

leaveCollabRoom() returns a promise so a caller that re-joins the same
room can serialise the two. It never rejects, because every existing
caller invokes it without handling the result, mostly from unmount hooks.
They had grown into essays: the reasoning behind a decision, the alternative
that was rejected and the bug that prompted it, where a line of why would do.
Comment-only, no behaviour change.
The server answers with the locks it holds only when it holds some, so a lock
released while this client was out of the room produces no event and the field
reads as locked until some later one arrives. Anything remembered from a
previous stay is a guess the join is about to correct.
…stale chip

- FiltersSelected: the chip styles were hardcoded light (#f5f5f5 container, white
  chip) with no dark variant, so in dark theme Vuetify's light chip foreground
  rendered white text on a white chip. Add a dark-theme block driven by the theme
  tokens; light theme is untouched.
- en/common/model.json: filterPlaceholder shipped Slovak strings in the English
  bundle, so every AFilterString placeholder was Slovak under the en locale.
- AFilterValueObjectOptionsSelect: emptying a multi-select through the dropdown
  returned early and left the entry in filterSelected, so a chip stayed behind
  for a filter that was no longer applied. Drop it, like clearField does.
Vuetify 4.1 moves DOM focus onto the clicked day before the picker emits the new
value. The resulting text field blur re-applied its stale content over the date
just picked, so the first click on a freshly opened calendar did nothing and the
previously selected day stayed.

Skip the re-assignment when the re-parsed text has not changed, comparing by
second so lastMinuteMoment's ms=999 is neither missed nor flattened.

Adds the first test coverage the component has had.
@sentry/vue, @shikijs/vitepress-twoslash, eslint and oxfmt; reindents the
FlagSk eslint-disable that the newer oxfmt flags.
Pasting an article editor URL straight into the address bar left the
moderator unset: no save button, no add-author button, for the only user
in the room, indefinitely. The join acknowledgement was correct on the
wire and its write was discarded on arrival.

On that path the router guard awaits joinCollabRoom while the socket is
still connecting, so socket.io buffers the packet. On connect it flushes
sendBuffer before emitting the public connect event, so the join goes out
and the handler here then reset every write claim. The acknowledgement
arrived a round trip later to find its claim gone, isNewestWrite() said
no, and collabRoomInfo fell back to a default with a null moderator that
nothing repairs while the client is alone in the room.

resetRoomInfoWrites() is dropped rather than confined to reconnects.
socket.io already deletes the acknowledgement handler of a packet it put
on the wire and keeps the one still in sendBuffer, so an acknowledgement
emitted before a reconnect cannot land after it and the reset guarded
against nothing. Confining it to reconnects would still discard the claim
of a join emitted during one, which is the same failure a connection
later.

The per-room latest-wins ordering that made a late leave lose to a
following join is untouched.
The declaration build ran over src/test as well, because tsconfig.libdts
carried an include with no exclude beside it, and it did so without
DOM.Iterable, which tsconfig.app has. Spreading a NodeList in the
datetime picker test therefore failed the library build under TS2488
while a typecheck against tsconfig.app stayed clean.

Excludes the same four test patterns tsconfig.app already lists, so test
sources cannot reach the published types, and adds DOM.Iterable to match.
… silent save failures

ASortableListEditor: SortableJS options are built once at setup, so a `#view-body`
consumer entering reorder mode got an instance re-created from a stale
`disabled: true` object — handles rendered, dragging did nothing. Watch the rows
container alongside `dragEnabled`.

ImageWidgetMultiple:
- gate the editor's mount on the initial fetch, so images arriving later are not
  baselined as "added" ("N unconfirmed changes" on an untouched form)
- re-baseline via `commit()` after a successful save, whose `setImages` replaces
  every row key
- emit the empty id list when the last image is removed, so the parent stops
  holding the old ids and the images do not come back on reload
- refuse to save while the initial fetch is pending or after it failed: the store
  is empty for reasons unrelated to the user, and saving that emptiness detached
  every image the gallery still had
- report the real save error instead of returning a bare `false`
- drop the dead drag handle and arrows from the tile; reordering lives in reorder
  mode, which is the only place SortableJS binds

Tests: dirty baseline at mount and after `commit(rows)`, and a `#view-body`
consumer having a live Sortable instance in reorder mode.
8d87197 stopped the null VAutocomplete raises when its search text is
emptied, which had poisoned models bound to a non-nullable backend enum:
core-cms then 500'd on every following save. That guard keyed on "null
arriving over a non-null value", so it also swallowed a legitimate pick of
a null-valued option, and a tri-state Yes / No / By system select could
never leave Yes or No.

Gate the guard on whether the list actually offers null instead. Vuetify
resolves a bare null item and {value: null} alike to a null value, so the
predicate covers both shapes and agrees with its own hasNullItem test.

A list that does offer null cannot tell Vuetify's clear-on-empty-search
apart from a pick, so backspacing over such a field lands on the null
option. That is accepted: null is a legal answer there and no model is
poisoned by it.

One Vuetify bug is left in place. After a swallowed null its selectionIndex
goes stale, so one Backspace past empty costs the typed search term. A
wrapper cannot reach it; VAutocomplete.js:206-207 should gate on
props.multiple, the way VSelect already gates its Backspace clear on
clearable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main gets the version admin-cms runs on develop and master; main's own
commits since the fork are the scraper docs (Omny clip, Sportnet video).
@volarname
volarname merged commit 2c12a3f into main Sep 28, 2026
4 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.

5 participants