Conversation
skjnldsv
force-pushed
the
feat/merge-viewer
branch
from
September 2, 2026 12:26
92e753a to
c0f841d
Compare
skjnldsv
force-pushed
the
feat/merge-viewer
branch
2 times, most recently
from
September 2, 2026 12:50
8018718 to
7a2fb77
Compare
skjnldsv
marked this pull request as ready for review
September 8, 2026 06:58
skjnldsv
requested review from
CarlSchwan,
kristian-zendato,
leftybournes,
salmart-dev,
sorbaugh and
susnux
and removed request for
a team
September 8, 2026 06:58
skjnldsv
force-pushed
the
feat/merge-viewer
branch
3 times, most recently
from
September 9, 2026 00:31
88fcb71 to
59fecf7
Compare
skjnldsv
force-pushed
the
feat/merge-viewer
branch
from
September 9, 2026 08:16
93ec687 to
bc519a9
Compare
This was referenced Sep 9, 2026
skjnldsv
force-pushed
the
feat/merge-viewer
branch
2 times, most recently
from
September 10, 2026 12:49
4bda0f6 to
7d3288e
Compare
This was referenced Sep 11, 2026
fix(handlers): register the default handlers only when asked
nextcloud-libraries/nextcloud-viewer#36
Merged
skjnldsv
force-pushed
the
feat/merge-viewer
branch
2 times, most recently
from
September 17, 2026 18:18
3aa0273 to
82d2afc
Compare
Member
Author
|
Ready for reviews! 💪 🚀 |
The suite the viewer app carried in Cypress, ported to Playwright and to the fixtures the server harness already sets up: opening every media type, navigating between files, sorting, sharing, odd names, and the actions around them. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
2.0.0-beta.9, which carries the viewer and its handlers. The entry is built with the Vue 3 frontend rather than the legacy one. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
The viewer only offers a mime its handler can actually show, and for HEIC, HEIF, TIFF and friends that means the server has to render a preview. The app read the enabled providers from an initial state it provided itself; a package cannot, so core reports them under capabilities.previews.enabled_providers, the same array_keys() the app used. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
A file link is not only on the Files list: an app can put one on any page, and the old LoadViewer event existed so those pages could ask the app to load itself. Core puts the init script on every rendered page instead, the themed error page aside, and the script only registers the handlers: nothing of the viewer is fetched until a file is opened. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
The global was the app's own API and goes with it. The versions sidebar, the local-edit action and the settings PDF link ask the package instead. Comparison stays with the pictures it means something for: the old viewer asked each handler through a canCompare flag that none of the bundled ones set, and the package carries no such flag yet. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Nothing needs it any more: the handlers come from @nextcloud/viewer, core puts the init script on every page, and the enabled preview providers are a core capability. With it goes the LoadViewer event, which three places dispatched behind a class_exists() guard to ask the app to load itself. An app that wants a file view imports the package and registers a handler; it no longer matters whether the viewer app is enabled, or which app got there first. The app config rows left behind on an upgrade are cleaned up by the repair step that already does this for the other apps we dropped. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
skjnldsv
force-pushed
the
feat/merge-viewer
branch
from
September 17, 2026 19:02
4b826c1 to
bea8c51
Compare
This was referenced Sep 17, 2026
Merged
It carries the close fix: the viewer no longer leaves openfile=true in the URL while the history unwinds, which had the Files list opening a second viewer over the one that was closing. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Three of them asked a question whose answer changes under them: waitForClosed asked whether the first matching modal was hidden, which passes while a second one is still up. It counts them now, and close() waits for openfile to leave the URL before looking. runAction decided between a header button and a menu entry with one isVisible() call. The header re-renders whenever the shown file changes, so on the second delete it found neither and then waited for a menu it had not opened. The sequence is retried as a unit. The sidebar test asserted the loading spinner was on screen. The list fetches the preview for its own row and the viewer reuses it, so delaying the viewer's request holds nothing back: the spinner measured about 600ms, which passed on CI and failed here. It asserts the action it exists for instead. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
The old suite had two specs for it and the port dropped both: a viewer opened on a list of its own rather than a folder, which is the API Photos and the versions sidebar use, and loadMore continuing that list when navigation runs out. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
viewerapp into server repository #60480Summary
The viewer used to be a bundled app that every other app had to check for before
it could register a handler. It is a library now —
@nextcloud/viewer, innextcloud-libraries/nextcloud-viewer —
and this drops the app.
An app that wants to add a file view imports the package and registers its
handler. It no longer matters whether the viewer app is enabled, or which app
loaded it first: several copies on a page elect the newest between them, and only
that copy fetches the viewer, only when a file is opened.
What is left in the server
apps/filesputs a thirteen line init script on every page a file can be openedfrom, on the same event the viewer app's own listener used. The script does
nothing but import the package; the handlers for images, video and audio come
with it.
dist/files-viewer-init.jsis 24 kB, 8.3 kB gzipped.LoadViewergoes with the app. Three places dispatched it behind aclass_exists()guard to ask the app to load itself; none of them need to now.enabled_preview_providers becomes a core capability
Which mimes have a preview provider decides what the viewer offers to open — heic,
heif and tiff are viewable through a preview and not otherwise. That was an
initial state the viewer app provided, so anything wanting it had to be on a page
that app had touched. It is
core.previews.enabled_providersnow, next to thecapabilities clients already read, public share pages included.
PreviewManager::getProviders()registers closures and instantiates no provider,so this costs nothing per request, and it reports nothing when previews are off.
OCA.Viewer is gone, and three callers were still on it
Viewer 7.0.0 dropped the global. These do nothing on current master:
They take nodes now. The versions sidebar builds one for the version it shows,
pointing at the version's own dav endpoint rather than at the 250px thumbnail
meant for the list;
canView()from the package decides whether to offer thebutton, which is the same test the click-to-open action uses.
Third-party apps calling
OCA.Viewer.open()— Photos, Talk, Deck, Mail,Collectives — need to move to the package. There is no shim.
Testing
tests/playwright/e2e/viewer/, which comes over from the viewer's ownrepository in the first commit: 46 passed, 1 skipped
versionToNodeand the capabilitytests/lib/OCS/CoreCapabilitiesTest.php,tests/lib/AppTest.php,tests/lib/App/AppManagerTest.phpUpgrades:
viewerjoins the abandoned appsCleanUpAbandonedAppsalreadycleans the config rows for.
TODO
OCA.ViewerAPI and theLoadViewereventChecklist
3. to review, feature component)stable32)AI (if applicable)