Follow the WOPI-in-new-tab change: the double-click scenario now
asserts that a new page opens with the office iframe and that the
preview modal stays closed, a new test covers the "Open in editor"
placeholder reached by keyboard navigating onto a WOPI file, and the
copy/paste scenario drives the detached wopiPage. Refresh canvas
snapshots accordingly and drop the obsolete per-spec snapshot copies.
WOPI editors (OnlyOffice / Collabora) work better as a full-page
experience than embedded inside the preview modal. Double-clicking a
WOPI file in the grid or activating one from the search modal now
opens /wopi/:id in a new tab. The preview modal still shows an
"Open in editor" placeholder when a user navigates onto a WOPI file
via keyboard, so the preview flow stays consistent.
The former WopiEditor component is renamed WopiEditorFrame and
hosted on the new page. The inline rename-sync path is removed
because the editor no longer lives inside the items list.
The effect that toggles the pdf-sidebar class scheduled a setTimeout
without returning a cleanup, so the timer could fire after the file
preview unmounted. Capture the timeout id and clear it on cleanup.
The component lives in DurationBar.tsx but was exported as ProgressBar,
which was confusing. Rename the export and update the two call sites
in AudioPlayer and VideoPlayer.
Cover the fallback viewer that kicks in for file types the app
cannot render inline. A minimal .bin fixture exercises the redesign
so the filename, the "Unsupported format." disclaimer and the
secondary download button are all checked end to end.
Cover the video viewer with a dedicated spec that uploads a small
mp4 fixture, opens the preview and exercises the player controls.
Keeps the new viewer types consistently covered alongside the audio
and image specs.
Cover the image viewer with a dedicated spec that uploads a small
png fixture, opens the preview and asserts the image renders and
reacts to the viewer controls. Mirrors the audio/video specs so
each viewer has its own file.
Cover the audio viewer with a dedicated spec that uploads a small
mp3 fixture, opens the preview and checks the playback controls.
Ensures the audio branch of the preview rewrite stays covered as we
iterate on the other viewers.
The preview-related specs are now numerous enough to warrant their
own subfolder. Move pdf, heic and preview-actions specs into
__tests__/app-drive/file-preview/ so future preview tests land next
to them rather than at the drive-level root. Import and asset paths
are adjusted to the new depth, the heic row lookup is simplified to
getByRole("cell", ...), and the preview-actions spec gains a small
File Preview Header describe block to exercise the header controls.
The predicate in the preview close branch was (className) =>
className !== className, which compared the lambda parameter to
itself and therefore never matched, leaving stale class names on the
preview when the PDF thumbnail sidebar was closed. Rename the inner
parameter so the filter compares against the outer className as
intended.
The preview for files that cannot be rendered inline now shows the
filename as the title and folds the "unsupported format" notice into
the description paragraph as a bold disclaimer, so the panel reads as
"<filename> — Unsupported format. Download it to view." The download
CTA is promoted to a secondary neutral button to stand out on the
empty-state panel.
The i18n key file_preview.unsupported.title is renamed to disclaimer
in en/fr/nl and the copy shortened accordingly. search.spec.ts
assertions are updated to match the filename via getByRole("heading")
now that the preview wraps the title in a heading element.
The preview refactor renamed CSS classes from
pdf-preview__controls / pdf-preview__zoom-controls
to file-preview__controls / file-preview__controls__zoom.
Update the e2e tests to match.
Reorganize all viewer components under a viewers/ directory
and rewrite FilesPreview as the single orchestrator at the
preview root. Add ZoomControls shared component for image
and PDF viewers. The new layout centralizes the preview
chrome (header, controls bar, navigation) in FilesPreview
while each viewer only handles its own rendering.
Rename PreviewControls to PlayerPreviewControls and swap
material icons for ui-kit SVG icons. Move arrow-key seeking
from global keydown to the DurationBar input so it only
activates when the slider is focused, and add a focus-visible
outline for accessibility. Simplify VolumeBar styling by
removing unused btn-mute overrides.
Track each uploading file's drop target folder via a Map.
When a folder is deleted, selectively cancel only the
uploads that were dropped into that folder, leaving other
uploads unaffected.
Add overflow-x: auto on the search modal inputs container to prevent
layout breakage when filters exceed available width, and upgrade
@gouvfr-lasuite/ui-kit from 0.20.0 to 0.20.1.
The trash hard delete confirmation modal always displayed "2 items"
regardless of how many items were selected, because it received a
boolean `multiple` prop and hardcoded `count: 2` for the i18n
interpolation. Replace the boolean with the real selection length so
the plural message reflects the actual count.
Test that My Files shows the more menu with create
folder and import options on mobile viewport.
Test that the more menu button is visible inside
a folder on mobile viewport.
The more_vert button was only shown inside folders
on mobile. This adds a dropdown with create folder
and import options on the My Files default route.
Also adds includeCreate option to useCreateMenuItems
to allow excluding document creation items.
Use String.localeCompare in three test sort() calls to satisfy
the reliability rule S2871, and drop the redundant Event branch
of the Event | unknown parameter type in customGetFilesFromEvent
(S6571).
Cover the custom drop traversal helpers: empty-folder marker
creation, batched readEntries() loop, recursive entry walking
for files, nested non-empty folders, and empty directories, plus
the customGetFilesFromEvent entry point for drop events and its
delegation to file-selector for non-drag inputs.
React-dropzone's default traversal silently drops empty directories
because webkitGetAsEntry()'s readEntries() yields no entry for them.
Add a custom getFilesFromEvent that walks the drop tree ourselves
and emits a zero-byte marker file for every empty folder. The marker
carries an isEmptyFolder flag and a path so useUpload can materialize
the folder chain via getFolderByPath, then skip the marker before any
size validation or backend upload.
When a drop contains only empty-folder markers, the file upload toast
is suppressed and a dedicated success toast is shown once the folders
have been created. Input change events (folder/file buttons) are
delegated to file-selector, matching the Google Drive / Proton Drive
limitation around <input webkitdirectory>.
Pillow 12.1.1 is vulnerable to CVE-2026-40192 (FITS GZIP decompression
bomb). easy_thumbnails pulls pillow transitively with no upper bound,
so enforce the floor via a uv constraint rather than a direct
dependency bump.
An item is considered hard deleted in two cases:
- hard deletion was forced by the user while sitting in the trashbin
- the item was only soft deleted but the trashbin retention period
has expired
Thus, a hard deleted item still exists in database with an associated
file in S3. What the asynchronous task is doing is another action:
permanently removing the item from database and its associated file
from S3. I propose to rename this action "purge".
We refactor the task to improve its resilience:
- don't recursively generate asynchronous tasks. We are already
outside of a request cycle so we don't need to spawn new
asynchronous tasks.
- purge the subtree from the bottom to the top: leaves first,
root last.
- fail fast if something goes wrong and make sure the task will
continue when we retry it.
- Add per-file status tracking (uploading/done/error/cancelled)
- Show error details inline with tooltips for failed uploads
- Display overall progress percentage during upload
- Add cancel overlay on hover for individual files
- Add confirmation modal when cancelling all uploads
- Handle too-large files as inline errors instead of separate toasts
- Add translations for error messages and cancel modal (en/fr/nl)
Added
- ✨(frontend) add PDF viewer with thumbnail sidebar, zoom and page navigation
- ✨(frontend) integrate PDF viewer into file preview modal
- 📝(doc) add local network setup documentation
- ✨(global) add custom columns feature with configurable grid columns
- 🔒️(frontend) prevent search engine indexing
- ✨(backend) allow ordering items by creator full name
- ✨(frontend) add item duplication with polling and visual feedback
- ⚡(ci) shard e2e tests and cache playwright browsers
- ⬆️(frontend) upgrade cunningham-react and ui-kit to 0.20.0
- ✨(frontend) improve custom columns with sortable config and i18n
Changed
- 🏷️(sdk) update Item interface by adding url_permalink
- 🔧(backend) allow extra CSRF origins via env variable
- 🔧(nginx) serve .mjs files with correct MIME type
Fixed
- 🐛(backend) fix hard delete of files created by other users
- 🐛(backend) handle race condition on concurrent LinkTrace creation
- 🐛(frontend) fix React SVG attributes in AddFolderButton
- 🔧(scalingo) compile translation files at deploy time
- 🐛(frontend) fix trash items not refreshing after hard delete
- 🐛(frontend) show modal when clicking files in trash
- 🐛(frontend) fix toasts appearing above modals
Introduce the 0.16.0 release note highlighting customizable columns,
column sorting, document duplication and the new built-in PDF viewer,
with translations for en, fr and nl.
CodeQL flagged the workflow for running with default GITHUB_TOKEN
permissions. None of the jobs need write scopes, so pin to the
minimum to contain blast radius if a third-party action is
compromised.
Replace the per-shard \`yarn dev\` with a single static build
job whose output is shared as an artifact and served by stock
nginx using the production vhost. Cuts e2e startup cost and
exercises the same bundle we ship.
The static export router only matches valid uuid patterns, so
"not_a_uuid" never reaches the preview page. Use a well-formed
but non-existent uuid to actually exercise the 404 path.
NODE_ENV no longer reliably signals a test/dev runtime once the
e2e build is produced as a production static export. Switch to
NEXT_PUBLIC_POSTHOG_TEST_MODE so we can opt in explicitly when
running under Playwright.
Key the Playwright browser cache by the installed @playwright/test
version instead of yarn.lock so cache hits actually match the binaries
Playwright expects, avoiding redundant browser downloads on every CI
run. On cache hit we still install system deps to keep headless runs
working.
Added interception for list endpoints in the duplicate item e2e tests to
override the state of duplicated items, ensuring accurate testing of
upload states during the duplication process.