Skip to content

feat(viewer): migrate to @nextcloud/viewer package API - #9235

Open
skjnldsv wants to merge 14 commits into
mainfrom
feature/migrate-new-viewer-api
Open

skjnldsv wants to merge 14 commits into
mainfrom
feature/migrate-new-viewer-api

Conversation

@skjnldsv

@skjnldsv skjnldsv commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

📝 Summary

The viewer moved into the server as @nextcloud/viewer, and OCA.Viewer.registerHandler() is going away. Text now registers with registerHandler() from the package: its element is text-viewer, defined in onInit, so the script that runs on every page only registers the handler and the editor loads when a text file opens. The listener moves from the old LoadViewer event to BeforeTemplateRenderedEvent, skipping error pages like the server's own viewer listener.

I didn't skip blank templates as suggested in review: the server loads the viewer on them too, so a text file opened from one would have no handler.

The handler's element fills the viewer: the viewer centres what a handler renders and leaves it to size itself, which suits a picture, but the editor takes the width of its parent, and the two kept resizing each other. Older versions open read-only from their source, as with the old viewer, rather than as the current document under the file's id.

The reference providers are loaded with the rest of Text in Files and on public shares, for the smart picker and link previews; they came with the old LoadViewer event.

Dropped, as the new API has no equivalent:

  • downloadCallback, which saved unsaved changes before a download;
  • outside Files and public shares, the text initial state and RenderReferenceEvent are not provided when the viewer loads. The editor falls back to its defaults there (no assistant or translation entries, link previews not rendered). Providing them on every page would cost a task-processing query each time.

This needs @nextcloud/viewer 2.0.0-beta.16, which no longer remounts the file shown on each of Text's own saves (nextcloud-libraries/nextcloud-viewer#119). The end-to-end tests also need, on server master:

With those, the whole Playwright suite passes locally but for the versions Compare test, which needs the server change built.

🖼️ Screenshots

No UI change.

🏁 Checklist

  • Code is properly formatted (npm run lint / npm run stylelint / composer run cs:check)
  • Sign-off message is added to all commits
  • Tests (unit, integration and/or end-to-end) passing and the changes are covered with tests
  • Documentation (README or documentation) has been updated or is not required

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI tools
  • The AI-generated content was reviewed, comprehended and tested by a human

👾 This pull request was assisted by Claude Code, commits carry an Assisted-by trailer.

@skjnldsv skjnldsv added the enhancement New feature or request label Sep 22, 2026
@skjnldsv
skjnldsv force-pushed the feature/migrate-new-viewer-api branch 2 times, most recently from 8f4b8b4 to a6ddb27 Compare September 23, 2026 07:50
@skjnldsv skjnldsv self-assigned this Sep 23, 2026
@skjnldsv
skjnldsv force-pushed the feature/migrate-new-viewer-api branch from a6ddb27 to 06f59d2 Compare September 23, 2026 07:57

@max-nextcloud max-nextcloud left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for looking into this! Appreciate it a lot! ❤️

Just a few initial comments. Did not test nor look into test failures yet.

Comment thread src/views/TextViewerWrapper.vue Outdated
Comment thread lib/Listeners/LoadViewerListener.php
Comment thread lib/Listeners/LoadViewerListener.php
skjnldsv added a commit to nextcloud-libraries/nextcloud-viewer that referenced this pull request Oct 1, 2026
A handler had to define its custom element before the viewer opened, so
the view and everything it imports went into the registration script
that runs on every page, unless the app wrapped it in a lazy component
of its own (nextcloud/text#9235 does not, and pays for the editor on
every page).

A handler can now pass load(), returning the element's constructor.
The viewer calls it the first time it needs the element, once per tag,
defines the tag and only then renders it: rendered before its tag
exists, an element gets its bindings as attributes and loses them on
upgrade. A failed load shows the viewer's error and is retried on the
next open. Handlers without load() are unchanged.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
juliusknorr added a commit to nextcloud/office that referenced this pull request Oct 5, 2026
Build nextcloud/text#9235 so Text registers with @nextcloud/viewer on
server master. The Text spec stays fixme until opening a file works there.
Revert once Text main supports @nextcloud/viewer.

Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Julius Knorr <jus@bitgrid.net>
juliusknorr added a commit to nextcloud/office that referenced this pull request Oct 5, 2026
Build nextcloud/text#9235, pinned to a commit, so Text registers with
@nextcloud/viewer on server master. The Text spec stays fixme until opening a
file works there. Revert once Text main supports @nextcloud/viewer.

Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Julius Knorr <jus@bitgrid.net>
juliusknorr added a commit to nextcloud/office that referenced this pull request Oct 5, 2026
Build nextcloud/text#9235, pinned to a commit, so Text registers with
@nextcloud/viewer on server master. The Text spec stays fixme until opening a
file works there. Revert once Text main supports @nextcloud/viewer.

Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Julius Knorr <jus@bitgrid.net>
@skjnldsv
skjnldsv force-pushed the feature/migrate-new-viewer-api branch from 06f59d2 to 9c59abe Compare October 6, 2026 08:21
@skjnldsv
skjnldsv requested a review from max-nextcloud October 6, 2026 08:21
@skjnldsv
skjnldsv force-pushed the feature/migrate-new-viewer-api branch from 9c59abe to 32a65f7 Compare October 6, 2026 08:23

@max-nextcloud max-nextcloud left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good. Did not test it yet.

@max-nextcloud

Copy link
Copy Markdown
Collaborator

Playwright is failing when attempting to install viewer. My understanding is that we will no longer need to install the viewer app. I think we can drop it from the apps list in playwright/start-nextcloud-server.mjs.

@skjnldsv

skjnldsv commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

Playwright is failing when attempting to install viewer. My understanding is that we will no longer need to install the viewer app. I think we can drop it from the apps list in playwright/start-nextcloud-server.mjs.

yes, let me adjust !

@skjnldsv

skjnldsv commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

Done! Cypress was also still installing the old viewer app, so I removed that too.

@max-nextcloud

Copy link
Copy Markdown
Collaborator

#9313 has been merged in main which fixes cypress selectors for creating a folder description. Some viewer related selectors still seem to have changed.

@skjnldsv

skjnldsv commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

#9313 has been merged in main which fixes cypress selectors for creating a folder description. Some viewer related selectors still seem to have changed.

On it™

@skjnldsv

skjnldsv commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

Slight fix needed nextcloud/server#65174

Register with registerHandler() from @nextcloud/viewer instead of the
OCA.Viewer.registerHandler() global, which goes away with the viewer
app (nextcloud/server#63954).

The handler's element is text-viewer, defined in onInit, so the script
loaded on every page only registers the handler and the editor loads
when a text file opens. The wrapper maps the viewer's file to the
existing ViewerComponent props and turns swiping off, so selecting text
does not step to the next file.

The listener moves from OCA\Viewer\Event\LoadViewer to
BeforeTemplateRenderedEvent, skipping error pages like the server's own
viewer listener. The empty text-viewer stylesheet is no longer loaded,
and the Vue 2 ViewerView wrapper and the LoadViewer stub are gone.

downloadCallback has no equivalent in the new API and is dropped.

Assisted-by: ClaudeCode:claude-sonnet-4-6
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
The viewer is part of the server from 36: installing the app failed the Playwright setup ("viewer already installed"), and Cypress put the old viewer app on top of the server's. Cypress also stopped ignoring the ResizeObserver warning, whose wording Chrome changed.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
The viewer's modal is no longer #viewer, which is only where it mounts now: the dialog itself is teleported to the body, with the viewer__modal class and still its data-handler.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
The viewer's modal is gone once closed rather than hidden, and is viewer__modal rather than .viewer.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
The viewer centres the element a handler renders and leaves it to size itself, which suits a picture. The editor takes the width of its parent, so with the element left inline the two sized each other and never settled: the editor kept resizing, elements under the cursor detached, and Chrome warned about a ResizeObserver loop.

A version of a file carries the file's own id, and with it the editor opened the current document. It is read from its source instead, as the old viewer did.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
They came with the old viewer's LoadViewer event. Without them the smart picker in a file opened from Files lacked its providers, Link a file among them, and links rendered no preview.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
It is a button there now rather than an entry of the viewer's menu.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
…typing in it

The picker focuses its field once its dialog is ready. Until then the viewer around the editor kept the focus, so the link typed straight away went into the document instead.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
It no longer remounts the file shown when Text announces one of its own saves, which rebuilt the editor under the user.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
The print styles freed the old viewer's #viewer from its fixed position. The new viewer's modal is .viewer__modal, and its container and content both scroll within the window, so only the first page printed.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
A path starting with a slash leaves out index.php, which a server without pretty URLs needs: the Files app never loaded for these tests.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
The viewer pins its container under its header with rules as specific as Text's print ones, which load later and won: the document printed a header's height down the first page.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com>
@mejo-
mejo- force-pushed the feature/migrate-new-viewer-api branch from ace1047 to 0d153da Compare October 7, 2026 09:00
Also remove obsolete rule from Vue 2 and nextcloud-vue v8 times.

Signed-off-by: Jonas <jonas@freesources.org>
Assisted-by: ClaudeCode:claude-opus-5.5

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate off the OCA.Viewer global before Nextcloud 36

3 participants