The global childList observer scheduled replaceIconsInElement() with no
scope on ANY node insertion — including bare text nodes — so every
notification-bell progress tick during an upload and every
infinite-scroll batch re-scanned the whole document with an attribute
substring selector. Cost grew with total DOM size (5-10k nodes), not
with what was inserted.
Queue the added element roots per animation frame and scan just those
subtrees (an inserted <i> itself is caught via its parent). Text-node
churn no longer triggers any scan at all. The icon replacement is
idempotent, so overlapping roots are harmless.
https://claude.ai/code/session_01Dp3oWon5GBMVn4j3QXZdgx
- add coalesced protection to avoid mail bombing if an invited goes many grant in a short period
- add resentd method in share menu item (work for both internal and external users)
- user can disable email notification via his properties
- add env variable from admin to disable notifications
- OIDC JIT define the locale only at user creation, user can so change his preference later
- invited users will inherit inviter's locale
- email will use prefered_locale
- login to a new browser will use prefered_locale
this implements first version (manageable only by admin right now)
routes:
GET /api/groups
List subject groups (paginated). Admin-only.
POST /api/groups
Create a new ReBAC subject group. Admin-only. The name must match the RFC 5321 local-part shape and be globally unique (case-insensitive).
GET /api/groups/search
Search non-virtual groups by name substring. Authenticated only (no admin role required) — backs the share-dialog recipient autocomplete.
GET /api/groups/{id}
Fetch a single group's details. Admin-only.
DELETE /api/groups/{id}
Delete a group. Cascades to `subject_group_members` (FK) and to `access_grants` rows referencing this group as a subject. Admin-only.
PATCH /api/groups/{id}
Update a group's metadata. Admin-only. v1 only persists name renames.
GET /api/groups/{id}/effective-members
List every user transitively reached through this group (members of members of members, etc.). Used by admin / audit tooling. Admin-only.
GET /api/groups/{id}/members
List the *direct* members of a group (one level only). Admin-only.
POST /api/groups/{id}/members
Add a member to a group. Exactly one of `user_id` / `group_id` must be provided. Adding a group-member runs a write-time cycle check and a nesting-depth check (max 8). Admin-only.
DELETE /api/groups/{id}/members/group/{gid}
Remove a nested group-member from a group. Admin-only.
DELETE /api/groups/{id}/members/user/{uid}
Remove a user-member from a group. Admin-only.
fix hurl
groups
round
groups
- Add src/application/dtos/cursor.rs with three shared types:
· PageCursor trait — default base64url+JSON encode/decode; one bare
impl line per cursor struct
· CursorQuery struct — standard limit/cursor/sort_by query params with
limit_clamped() and decode_cursor<C>() helpers; compose via flatten
· CursorListResponse<T> — standard {items, next_cursor?} envelope with
from_oversized() and with_cursor() builders
- Migrate GrantCursor to impl PageCursor (remove duplicate encode/decode)
- Update GET /api/grants/incoming/resources:
· SharedWithMeQuery now embeds CursorQuery via #[serde(flatten)]
· Replace file/folder nullable pair with ResourceContentDto (untagged
enum) under a single always-present 'resource' field
· SharedWithMeDto is now a type alias for CursorListResponse<SharedWithMeItemDto>
· Handler uses q.paging.limit_clamped() and decode_cursor<GrantCursor>()
- Add docs/architecture/resource-listing.md — authoritative contract for
all listing endpoints (cursor design, SQL keyset WHERE, sort_by naming,
Rust + JS skeletons, compliance table, migration guide)
- Register doc in VitePress sidebar and architecture index
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Follow-up to PR #373 review feedback (thanks @EdouardVanbelle): with the
local t() removed in that PR, the safeT defensive name no longer earns
its purpose. There is no global t() left to shadow it, so the wrapper-
style name just creates confusion against callers writing i18n.t().
- function safeT -> function t
- export { t: safeT, ... } -> { t, ... }
- Remove stale comments in translateElement and above the definition
that explained the safeT/admin.js shadowing history
No call-site changes - every external caller already uses i18n.t(...),
which now points directly at the function of the same name.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ensure not loading twice the same variables, namespace to ensure it in the future
- end to end tests where successfull, need to check it is not reusing a previous release (in cache from build CI)
- fix: #377#378
Adds full Traditional Chinese translation (628 keys, 100% parity with
en.json) and the registration plumbing to make it pickable in the UI.
Registration spans three layers that all needed updating for the locale
to actually be selectable end-to-end:
- static/locales/zh-TW.json (new) — TW vocabulary (儲存/雲端/檔案/偵測),
uses 「」 corner brackets for in-string quoting
- core/i18n.js: add 'zh-TW' to supportedLocales — without this,
setLocale('zh-TW') was silently rejected by the whitelist and the
previous locale stayed active (visible as the "picked 繁中 but the
setup step still shows 簡中" bug)
- core/languageSelector.js: add 'zh-TW' to AVAILABLE_LOCALES + fallback
- features/auth/auth.js: add 'zh-TW' to ALL_LANGUAGES (🇹🇼 繁體中文)
and LANGUAGE_TEXTS bootstrap table (used before i18n loads)
Browser detection rewrite (i18n.js + auth.js detectBrowserLanguage):
The previous navigator.language?.substring(0, 2) truncated zh-TW → zh
and routed Traditional Chinese browsers to Simplified. Replaced with
three-tier matching: exact full-tag > Chinese script/region heuristic
(zh-Hant*, zh-{TW,HK,MO}) > primary subtag fallback.
Disambiguates the existing zh entry: "Chinese / 中文" became
"Simplified Chinese / 简体中文".
Drive-by cleanups discovered while wiring up the above:
- Remove dead t() in i18n.js (export uses safeT, no callers of bare t)
- Remove dead fetchUserData() and logout() in auth.js (userMenu.js has
its own local logout())
- Extract errMessage(unknown→string) and inputVal(id) helpers for the
catch sites and getElementById('x').value sites that needed TS
narrowing under checkJs
- Type-annotate module-scope let forms/errors/panels with
HTMLFormElement and HTMLElement so .addEventListener and .reset()
resolve under strict
- Drop navigator.userLanguage IE legacy fallback (DOM lib has no field)
- jsconfig.json: drop exactOptionalPropertyTypes (only valid with
strictNullChecks, which the project deliberately disables)
- .gitignore: ignore docker-compose.override.yml for local bind-mount
dev workflow
Verified clean before commit: biome ci, tsc --noEmit, i18n key parity
(628/628), HTTP smoke test against running container.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- protect file_management_service::rename_file with validate_storage_name
- remove specific rename modal and use the generic modal class (less duplicate)
- handle errors on modal action: do not close the modal on error and display this error
- hide "Go to parent folder" contextMenu if section is files and folder is the same as current one
this change replace original window.fetch by a wrapper that check any 401 response, is so it will request a refresh token
this solve current issue with Favorites & Recent sections that give blank page when token is expired
- exclusion of requests to other domain (401 will not be handled here)
- security with shares /api/s is not handled
- check with CSRF, no risk
- change worker: do not cache html pages (not necessary)
- remove use of window.XXX and maximize import/export,
this will provide more clarety,
show circular dependencies + you will benefit IDE help