diff --git a/.github/workflows/cypress-e2e.yml b/.github/workflows/cypress-e2e.yml index a7aacc75159..484cf9beb82 100644 --- a/.github/workflows/cypress-e2e.yml +++ b/.github/workflows/cypress-e2e.yml @@ -37,14 +37,6 @@ jobs: ref: ${{ matrix.server-versions }} submodules: true - - name: Checkout viewer - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - with: - persist-credentials: false - repository: nextcloud/viewer - ref: ${{ matrix.server-versions }} - path: apps/viewer - - name: Checkout assistant if: matrix.server-versions == 'main' uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 @@ -162,7 +154,6 @@ jobs: export OC_PASS=1234561 php occ user:add --password-from-env user1 php occ user:add --password-from-env user2 - php occ app:enable viewer php occ app:enable text php occ app:enable assistant --force php occ app:enable testing diff --git a/cypress/e2e/nodes/CodeBlock.spec.js b/cypress/e2e/nodes/CodeBlock.spec.js index ca7e2de66bc..d947e76481e 100644 --- a/cypress/e2e/nodes/CodeBlock.spec.js +++ b/cypress/e2e/nodes/CodeBlock.spec.js @@ -148,7 +148,7 @@ describe('Front matter support', function() { .contains('function') // Mermaid diagram - cy.get('#viewer .modal-container__content').scrollTo('bottom') + cy.get('.viewer__modal .modal-container__content').scrollTo('bottom') cy.getContent() .find('.split-view__preview') .eq(2) diff --git a/cypress/e2e/share.spec.js b/cypress/e2e/share.spec.js index e72c32e223c..f0e16830050 100644 --- a/cypress/e2e/share.spec.js +++ b/cypress/e2e/share.spec.js @@ -103,7 +103,7 @@ describe('Open test.md in viewer', function() { .find('h2') .should('contain', 'Hello world') cy.getModal().find('.modal-header button.header-close').click() - cy.get('.modal-mask').should('not.be.visible') + cy.get('.viewer__modal').should('not.exist') // cy.get('#rich-workspace').getContent().should('contain', 'Hello world') }) }) diff --git a/cypress/e2e/shareWithAttachments.spec.js b/cypress/e2e/shareWithAttachments.spec.js index fc85807a7ee..c4564a7dc25 100644 --- a/cypress/e2e/shareWithAttachments.spec.js +++ b/cypress/e2e/shareWithAttachments.spec.js @@ -94,7 +94,7 @@ describe('Public Share with attachments', () => { cy.visit(`/s/${this.token}`) cy.get('.editor__content-wrapper').should('exist') cy.get('.editor__content-wrapper .name', { timeout: 10_000 }).click() - cy.get('.viewer').should('exist') + cy.get('.viewer__modal').should('exist') cy.get('.language-plaintext').should('contain', 'multiple lines') }) }) diff --git a/cypress/e2e/viewer.spec.js b/cypress/e2e/viewer.spec.js index 3d7fe575054..c1acec9e856 100644 --- a/cypress/e2e/viewer.spec.js +++ b/cypress/e2e/viewer.spec.js @@ -7,7 +7,7 @@ import { initUserAndFiles, randUser } from '../utils/index.js' const user = randUser() describe('Open test.md in viewer', function() { - const getViewer = () => cy.get('#viewer') + const getViewer = () => cy.get('.viewer__modal') before(function() { initUserAndFiles(user, 'test.md', 'empty.md') @@ -67,8 +67,8 @@ describe('Open test.md in viewer', function() { it('Closes the editor', function() { cy.openFile('test.md') - cy.get('#viewer .modal-header button.header-close').click() - cy.get('#viewer').should('not.exist') + cy.get('.viewer__modal .modal-header button.header-close').click() + cy.get('.viewer__modal').should('not.exist') }) it('Can use tab keys for list in the viewer', function() { diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 1840fb6d31d..55eb7137bb8 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -261,8 +261,8 @@ Cypress.Commands.add('openFile', (fileName, params = {}) => { Cypress.Commands.add('closeFile', (params = {}) => { cy.intercept({ method: 'POST', url: '**/apps/text/session/*/close' }).as('close') - cy.get('#viewer .modal-header button.header-close').click(params) - cy.get('#viewer .modal-header').should('not.exist') + cy.get('.viewer__modal .modal-header button.header-close').click(params) + cy.get('.viewer__modal .modal-header').should('not.exist') cy.wait('@close', { timeout: 7000 }) }) @@ -297,7 +297,7 @@ Cypress.Commands.add('getFile', (fileName) => { }) Cypress.Commands.add('getModal', () => { - return cy.get('#viewer[data-handler="text"]') + return cy.get('.viewer__modal[data-handler="text"]') }) Cypress.Commands.add('getEditor', { prevSubject: 'optional' }, (subject) => { @@ -464,5 +464,7 @@ Cypress.Commands.add('createDirectEditingLinkForNewFile', (path) => { Cypress.on( 'uncaught:exception', - (err) => !err.message.includes('ResizeObserver loop limit exceeded'), + // Chrome said "limit exceeded", it now says "completed with undelivered + // notifications": a warning either way, not an error of the page + (err) => !err.message.includes('ResizeObserver loop'), ) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index cd1c8f2c703..213bc543ccf 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -28,11 +28,11 @@ use OCA\Text\Middleware\SessionMiddleware; use OCA\Text\Notification\Notifier; use OCA\TpAssistant\Event\BeforeAssistantNotificationEvent; -use OCA\Viewer\Event\LoadViewer; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent as HttpBeforeTemplateRenderedEvent; use OCP\DB\Events\AddMissingIndicesEvent; use OCP\DirectEditing\RegisterDirectEditorEvent; use OCP\Files\Events\Node\BeforeNodeDeletedEvent; @@ -53,7 +53,7 @@ public function register(IRegistrationContext $context): void { $context->registerConfigLexicon(\OCA\Text\ConfigLexicon::class); $context->registerEventListener(RegisterDirectEditorEvent::class, RegisterDirectEditorEventListener::class); - $context->registerEventListener(LoadViewer::class, LoadViewerListener::class); + $context->registerEventListener(HttpBeforeTemplateRenderedEvent::class, LoadViewerListener::class); $context->registerEventListener(LoadAdditionalScriptsEvent::class, FilesLoadAdditionalScriptsListener::class); $context->registerEventListener(BeforeTemplateRenderedEvent::class, FilesSharingLoadAdditionalScriptsListener::class); $context->registerEventListener(LoadEditor::class, LoadEditorListener::class); diff --git a/lib/Listeners/FilesLoadAdditionalScriptsListener.php b/lib/Listeners/FilesLoadAdditionalScriptsListener.php index d8284159c6f..54f5344b7ae 100644 --- a/lib/Listeners/FilesLoadAdditionalScriptsListener.php +++ b/lib/Listeners/FilesLoadAdditionalScriptsListener.php @@ -10,7 +10,9 @@ use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCA\Text\Service\InitialStateProvider; +use OCP\Collaboration\Reference\RenderReferenceEvent; use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; use OCP\EventDispatcher\IEventListener; /** @@ -19,6 +21,7 @@ class FilesLoadAdditionalScriptsListener implements IEventListener { public function __construct( private readonly InitialStateProvider $initialStateProvider, + private readonly IEventDispatcher $eventDispatcher, ) { } @@ -34,5 +37,8 @@ public function handle(Event $event): void { \OCP\Util::addStyle('text', 'text-files'); $this->initialStateProvider->provideState(); + // The viewer no longer has an event of its own to hang this on: the + // smart picker and link previews of a file opened from here need it + $this->eventDispatcher->dispatchTyped(new RenderReferenceEvent()); } } diff --git a/lib/Listeners/FilesSharingLoadAdditionalScriptsListener.php b/lib/Listeners/FilesSharingLoadAdditionalScriptsListener.php index c99e84ab7c8..6ac423a90c9 100644 --- a/lib/Listeners/FilesSharingLoadAdditionalScriptsListener.php +++ b/lib/Listeners/FilesSharingLoadAdditionalScriptsListener.php @@ -10,7 +10,9 @@ use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; use OCA\Text\Service\InitialStateProvider; +use OCP\Collaboration\Reference\RenderReferenceEvent; use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; use OCP\EventDispatcher\IEventListener; use OCP\IConfig; use OCP\Util; @@ -20,6 +22,7 @@ class FilesSharingLoadAdditionalScriptsListener implements IEventListener { public function __construct( IConfig $config, protected InitialStateProvider $initialStateProvider, + private readonly IEventDispatcher $eventDispatcher, ) { } @@ -34,6 +37,8 @@ public function handle(Event $event): void { Util::addStyle('text', 'text-init'); $this->initialStateProvider->provideState(); + // For the smart picker and link previews, as in the Files app + $this->eventDispatcher->dispatchTyped(new RenderReferenceEvent()); $node = $event->getShare()->getNode(); if ($node instanceof \OCP\Files\File) { $this->initialStateProvider->provideFileId($node->getId()); diff --git a/lib/Listeners/LoadViewerListener.php b/lib/Listeners/LoadViewerListener.php index a5b4872c708..4a9fe962f3a 100644 --- a/lib/Listeners/LoadViewerListener.php +++ b/lib/Listeners/LoadViewerListener.php @@ -8,30 +8,24 @@ namespace OCA\Text\Listeners; -use OCA\Text\Service\InitialStateProvider; -use OCA\Viewer\Event\LoadViewer; -use OCP\Collaboration\Reference\RenderReferenceEvent; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; +use OCP\AppFramework\Http\TemplateResponse; use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventDispatcher; use OCP\EventDispatcher\IEventListener; use OCP\Util; -/** @implements IEventListener */ +/** @implements IEventListener */ class LoadViewerListener implements IEventListener { - public function __construct( - private readonly InitialStateProvider $initialStateProvider, - private readonly IEventDispatcher $eventDispatcher, - ) { - } - public function handle(Event $event): void { - if (!$event instanceof LoadViewer) { + if (!($event instanceof BeforeTemplateRenderedEvent)) { + return; + } + + if ($event->getResponse()->getRenderAs() === TemplateResponse::RENDER_AS_ERROR) { return; } - Util::addScript('text', 'text-viewer', 'viewer'); - Util::addStyle('text', 'text-viewer'); - $this->eventDispatcher->dispatchTyped(new RenderReferenceEvent()); - $this->initialStateProvider->provideState(); + // Only registers the handler: the editor loads when a text file opens + Util::addInitScript('text', 'text-viewer'); } } diff --git a/package-lock.json b/package-lock.json index 2eafc9eb042..168b2e181ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@nextcloud/notify_push": "^1.4.0", "@nextcloud/router": "^3.2.0", "@nextcloud/sharing": "^0.4.0", + "@nextcloud/viewer": "^2.0.0-beta.17", "@nextcloud/vue": "^9.13.1", "@quartzy/markdown-it-mentions": "^0.2.0", "@tiptap/core": "3.31.3", @@ -2754,7 +2755,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2771,7 +2771,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2788,7 +2787,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2805,7 +2803,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2822,7 +2819,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2839,7 +2835,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2856,7 +2851,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2873,7 +2867,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2890,7 +2883,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2907,7 +2899,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2924,7 +2915,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2941,7 +2931,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2958,7 +2947,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2975,7 +2963,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2992,7 +2979,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3009,7 +2995,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3026,7 +3011,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3043,7 +3027,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3060,7 +3043,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3077,7 +3059,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3094,7 +3075,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3111,7 +3091,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3128,7 +3107,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3145,7 +3123,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3162,7 +3139,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3179,7 +3155,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3662,6 +3637,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -4246,6 +4234,24 @@ "node": "^24.0.0 || >=26.0.0" } }, + "node_modules/@nextcloud/image-editor": { + "version": "1.0.0-beta.7", + "resolved": "https://registry.npmjs.org/@nextcloud/image-editor/-/image-editor-1.0.0-beta.7.tgz", + "integrity": "sha512-JZG4HCX3XMm2hc54xvEs8xPo60q4g+qvevJu7WWEAqeGTK3erl6q70224OhGC7gDijMZE+kC753WKpJLnYBMwA==", + "license": "AGPL-3.0-or-later", + "dependencies": { + "@nextcloud/l10n": "^3.4.0", + "konva": "~10.3.2" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + }, + "peerDependencies": { + "@nextcloud/dialogs": "^7.0.0", + "@nextcloud/vue": "^9.0.0", + "vue": "^3.5.0" + } + }, "node_modules/@nextcloud/initial-state": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@nextcloud/initial-state/-/initial-state-3.0.0.tgz", @@ -4357,6 +4363,40 @@ "node": "^20.0.0 || ^22.0.0 || ^24.0.0" } }, + "node_modules/@nextcloud/viewer": { + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@nextcloud/viewer/-/viewer-2.0.0-beta.17.tgz", + "integrity": "sha512-qO6CgUhCmoOBT179+61entCSvM99X1bpQFMkblhKD58VO6Yx2zOA6lGvHQfnwnVPUlbB5hfu/TB+eCTJ99x8fQ==", + "license": "AGPL-3.0-or-later", + "dependencies": { + "@mdi/svg": "^7.4.47", + "@nextcloud/image-editor": "^1.0.0-beta.6", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/vue": "^9.13.0", + "@skjnldsv/vue-plyr": "^7.5.0", + "debounce": "^3.0.0", + "dompurify": "^3.4.14", + "opensheetmusicdisplay": "^2.1.3", + "vue": "^3.5.0", + "vue-material-design-icons": "^5.3.1", + "webdav": "^5.10.0" + }, + "engines": { + "node": "^24.0.0 || >=26.0.0" + }, + "peerDependencies": { + "@nextcloud/axios": "^2.5.2", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/dialogs": "^7.5.0", + "@nextcloud/event-bus": "^3.3.3", + "@nextcloud/files": "^4.0.0", + "@nextcloud/initial-state": "^3.0.0", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/paths": "^3.1.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.4.0 || ^1.0.0-beta.2" + } + }, "node_modules/@nextcloud/vite-config": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/@nextcloud/vite-config/-/vite-config-2.5.4.tgz", @@ -4564,7 +4604,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4605,7 +4644,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4627,7 +4665,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4649,7 +4686,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4671,7 +4707,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4693,7 +4728,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4715,7 +4749,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4737,7 +4770,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4759,7 +4791,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4781,7 +4812,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4803,7 +4833,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4825,7 +4854,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4847,7 +4875,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4869,7 +4896,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5067,7 +5093,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5081,7 +5106,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5095,7 +5119,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5109,7 +5132,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5123,7 +5145,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5137,7 +5158,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5151,7 +5171,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5165,7 +5184,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5179,7 +5197,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5193,7 +5210,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5207,7 +5223,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5221,7 +5236,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5235,7 +5249,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5249,7 +5262,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5263,7 +5275,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5277,7 +5288,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5291,7 +5301,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5318,7 +5327,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5332,7 +5340,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5346,7 +5353,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5360,7 +5366,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5374,7 +5379,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5388,7 +5392,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5402,7 +5405,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5606,6 +5608,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@skjnldsv/vue-plyr": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@skjnldsv/vue-plyr/-/vue-plyr-7.5.0.tgz", + "integrity": "sha512-JmbKPe8Eh0yTP+7w4VYeqGhrUqDKM4qSsCn37CbADAvvwMvceBwYC0IBXil0RBtyzStxwXPtOj1oI//lQ5DeTw==", + "license": "MIT", + "dependencies": { + "plyr": "^3.7.8", + "vue": "^3.3.4" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0", + "npm": "^9.0.0 || ^10.0.0" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -6613,7 +6629,7 @@ "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -6665,6 +6681,12 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/vexflow": { + "version": "1.2.42", + "resolved": "https://registry.npmjs.org/@types/vexflow/-/vexflow-1.2.42.tgz", + "integrity": "sha512-bj3If1sm1FYJN0tJMV2BJNrwoeQ6xfrTt+rbmFvUytyxUhklLlW79MR4uwQ+upzrNJLVy012TnC7oZqSpEglSw==", + "license": "MIT" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.21", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", @@ -8136,7 +8158,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -8186,6 +8208,16 @@ "require-from-string": "^2.0.2" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/birpc": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", @@ -8195,11 +8227,18 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==", + "license": "MIT", + "optional": true + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -8254,7 +8293,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -8470,7 +8509,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -8760,7 +8799,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/chrome-remote-interface": { @@ -9128,7 +9167,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, "license": "MIT" }, "node_modules/cose-base": { @@ -9293,6 +9331,12 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/custom-event-polyfill": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz", + "integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==", + "license": "MIT" + }, "node_modules/cypress": { "version": "16.1.0", "resolved": "https://registry.npmjs.org/cypress/-/cypress-16.1.0.tgz", @@ -10004,6 +10048,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -10037,6 +10097,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -10203,7 +10273,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, "license": "Apache-2.0", "optional": true, "peer": true, @@ -10665,7 +10734,7 @@ "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -10697,6 +10766,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -11429,6 +11508,16 @@ "node": ">=4" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -11439,6 +11528,13 @@ "node": ">=12.0.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "optional": true + }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -11647,6 +11743,13 @@ "flat-cache": "^6.1.23" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT", + "optional": true + }, "node_modules/filing-cabinet": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.2.0.tgz", @@ -11716,7 +11819,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -12209,7 +12312,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/fs-extra": { @@ -12232,7 +12335,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -12384,7 +12486,7 @@ "version": "4.13.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -12413,6 +12515,32 @@ "url": "https://github.com/fisker/git-hooks-list?sponsor=1" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT", + "optional": true + }, + "node_modules/gl": { + "version": "9.0.0-rc.10", + "resolved": "https://registry.npmjs.org/gl/-/gl-9.0.0-rc.10.tgz", + "integrity": "sha512-G6lYaWoan0d2d8UO0UmaSS8zqyyZwYt6q2dFVJ2hD62sRWUwkMIH+Jp7gEoQesLlo28Nzelh+GIYz1qOUa5WmQ==", + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "bit-twiddle": "^1.0.2", + "glsl-tokenizer": "^2.1.5", + "nan": "^2.26.2", + "node-gyp": "^12.2.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -12533,6 +12661,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glsl-tokenizer": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz", + "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==", + "license": "MIT", + "optional": true, + "dependencies": { + "through2": "^0.6.3" + } + }, "node_modules/gonzales-pe": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", @@ -12565,7 +12703,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/hachure-fill": { @@ -12954,7 +13092,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -12981,11 +13119,17 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immutable": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true }, @@ -13025,7 +13169,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -13238,7 +13381,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13278,7 +13421,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -13348,7 +13491,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -13963,37 +14106,85 @@ "verror": "1.10.0" } }, - "node_modules/katex": { - "version": "0.18.9", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.9.tgz", - "integrity": "sha512-8ad9RyoKsb/g8/yLFE+KAlP+DhbCTRUNi/V9XGsxn0R+trJJltNwzcDNo0q/DEkOy5fQUQTAQyCXCYSE+OakTQ==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { - "commander": "^15.0.0" - }, - "bin": { - "katex": "cli.js" + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" } }, - "node_modules/katex/node_modules/commander": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", - "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", - "license": "MIT", - "engines": { - "node": ">=22.12.0" - } + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, - "node_modules/keyv": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", - "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", - "dev": true, - "license": "MIT", + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/katex": { + "version": "0.18.9", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.9.tgz", + "integrity": "sha512-8ad9RyoKsb/g8/yLFE+KAlP+DhbCTRUNi/V9XGsxn0R+trJJltNwzcDNo0q/DEkOy5fQUQTAQyCXCYSE+OakTQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^15.0.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -14009,6 +14200,38 @@ "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", "license": "MIT" }, + "node_modules/konva": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/konva/-/konva-10.3.3.tgz", + "integrity": "sha512-j3Rrn504bYFwJ0zV0ZbSOQRsM9KMBLOpEq6xZuyIjbaKeFbBXwgiM5SA4IDq4nwM1l49GVKs66Mo+sl7+4G7PQ==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/lavrton" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/konva" + }, + { + "type": "github", + "url": "https://github.com/sponsors/lavrton" + } + ], + "license": "MIT", + "peerDependencies": { + "canvas": "^3.0.0", + "skia-canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + }, + "skia-canvas": { + "optional": true + } + } + }, "node_modules/layerr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/layerr/-/layerr-3.0.0.tgz", @@ -14066,6 +14289,15 @@ "url": "https://github.com/sponsors/dmonad" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/linkify-it": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-6.1.0.tgz", @@ -14194,6 +14426,12 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/loadjs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loadjs/-/loadjs-4.3.0.tgz", + "integrity": "sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==", + "license": "MIT" + }, "node_modules/local-pkg": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", @@ -14417,6 +14655,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -15381,7 +15632,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -15395,7 +15646,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -15469,6 +15720,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -15502,7 +15766,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15512,12 +15776,25 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, + "devOptional": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -15528,7 +15805,7 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/mlly": { @@ -15630,7 +15907,6 @@ "version": "2.28.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -15652,6 +15928,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT", + "optional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -15675,11 +15958,36 @@ "integrity": "sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==", "license": "MIT" }, + "node_modules/node-abi": { + "version": "3.97.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.97.0.tgz", + "integrity": "sha512-6Pu8F0eXGZlyz0EepPJrWgWDcBI8rKt2Wj8exOJU1tjVbu/VyZy5ZaLYS5N+Jp/Y3IP3sIRKcIOElTu7uC6KKA==", + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true, "peer": true @@ -15721,6 +16029,96 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/node-releases": { "version": "2.0.54", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", @@ -15924,7 +16322,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -15946,6 +16344,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opensheetmusicdisplay": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/opensheetmusicdisplay/-/opensheetmusicdisplay-2.2.0.tgz", + "integrity": "sha512-KUJ1OXhjGZP3n1ZTkLoEotL7+g4pQts9jSICY1I1WvShPI7F4Lr6G9Hqmmaoo1jNAaQB89Zy2yOC+mEquv6OuQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/vexflow": "^1.2.38", + "jszip": "3.10.1", + "loglevel": "^1.8.0", + "typescript-collections": "^1.3.3", + "vexflow": "1.2.93" + }, + "optionalDependencies": { + "gl": "^9.0.0-rc.10" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -16067,7 +16481,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true, "license": "(MIT AND Zlib)" }, "node_modules/parse-asn1": { @@ -16402,6 +16815,23 @@ "node": ">=4" } }, + "node_modules/plyr": { + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/plyr/-/plyr-3.8.5.tgz", + "integrity": "sha512-SqGU1s76fq4lI8ABYhi1uxwrcLGYqmyiMazFAUIOQKKwt6S7FfflEMljLaM3SV8HUUb1KdZ04FY/SNFRpPZHUw==", + "deprecated": "Plyr is deprecated and receives security updates only. Use Video.js 10 for new projects. Migration guide: https://videojs.org/docs/framework/html/guides/migrate-from-plyr", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "custom-event-polyfill": "^1.0.7", + "loadjs": "^4.3.0", + "rangetouch": "^2.0.1", + "url-polyfill": "^1.1.14" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -16488,6 +16918,44 @@ "postcss": "^8.2.9" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/precinct": { "version": "12.2.0", "resolved": "https://registry.npmjs.org/precinct/-/precinct-12.2.0.tgz", @@ -16565,6 +17033,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -16579,7 +17057,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, "license": "MIT" }, "node_modules/property-information": { @@ -16906,7 +17383,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -17060,11 +17537,50 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rangetouch": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rangetouch/-/rangetouch-2.0.1.tgz", + "integrity": "sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==", + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC", + "optional": true + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -17431,7 +17947,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -17782,7 +18298,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -17851,7 +18366,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -17896,7 +18411,7 @@ "version": "1.94.2", "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -17945,7 +18460,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -17962,7 +18477,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "engines": { @@ -18047,7 +18562,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true, "license": "MIT" }, "node_modules/sha.js": { @@ -18255,6 +18769,53 @@ "node": ">=14.16" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -18646,7 +19207,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -18915,11 +19476,28 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar": { + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tar-fs": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chownr": "^1.1.1", @@ -18932,7 +19510,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "bl": "^4.0.3", @@ -18945,6 +19523,26 @@ "node": ">=6" } }, + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", @@ -18975,6 +19573,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "license": "MIT", + "optional": true, + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT", + "optional": true + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT", + "optional": true + }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -19091,7 +19727,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -19221,7 +19857,7 @@ "version": "4.20.6", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "esbuild": "~0.25.0", @@ -19258,7 +19894,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -19316,6 +19952,12 @@ "node": ">=14.17" } }, + "node_modules/typescript-collections": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/typescript-collections/-/typescript-collections-1.3.3.tgz", + "integrity": "sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ==", + "license": "MIT" + }, "node_modules/typescript-eslint": { "version": "8.62.1", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.1.tgz", @@ -19362,7 +20004,7 @@ "version": "6.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.0.tgz", "integrity": "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18.17" @@ -19372,7 +20014,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -19701,6 +20343,12 @@ "requires-port": "^1.0.0" } }, + "node_modules/url-polyfill": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.14.tgz", + "integrity": "sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ==", + "license": "MIT" + }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -19726,7 +20374,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/uuid": { @@ -19771,6 +20418,12 @@ "extsprintf": "^1.2.0" } }, + "node_modules/vexflow": { + "version": "1.2.93", + "resolved": "https://registry.npmjs.org/vexflow/-/vexflow-1.2.93.tgz", + "integrity": "sha512-LwHQDCc257Lwju35BhyZuPYcVWu0hIUqEdM7j9+B+bq91bSelssnAG5JR8odTUtgGuwwvGwLhXw37wtmHNCS6Q==", + "license": "MIT" + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -19960,7 +20613,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -19977,7 +20629,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -19994,7 +20645,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20011,7 +20661,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20028,7 +20677,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20045,7 +20693,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20062,7 +20709,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20079,7 +20725,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20096,7 +20741,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20113,7 +20757,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20130,7 +20773,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20147,7 +20789,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20164,7 +20805,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20181,7 +20821,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20198,7 +20837,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20215,7 +20853,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20232,7 +20869,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20249,7 +20885,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20266,7 +20901,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20283,7 +20917,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20300,7 +20933,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20317,7 +20949,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20334,7 +20965,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20351,7 +20981,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20368,7 +20997,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20385,7 +21013,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -20995,7 +21622,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/ws": { @@ -21056,7 +21683,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.4" diff --git a/package.json b/package.json index fae02f62cb1..9bc46ff7e7f 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@nextcloud/notify_push": "^1.4.0", "@nextcloud/router": "^3.2.0", "@nextcloud/sharing": "^0.4.0", + "@nextcloud/viewer": "^2.0.0-beta.17", "@nextcloud/vue": "^9.13.1", "@quartzy/markdown-it-mentions": "^0.2.0", "@tiptap/core": "3.31.3", diff --git a/playwright/e2e/editor-api-create-table.spec.ts b/playwright/e2e/editor-api-create-table.spec.ts index 4b063f4cb70..52163777080 100644 --- a/playwright/e2e/editor-api-create-table.spec.ts +++ b/playwright/e2e/editor-api-create-table.spec.ts @@ -9,7 +9,7 @@ import { test } from '../support/fixtures/editor-api.ts' test.describe('createTable API', () => { test.beforeEach(async ({ page }) => { // Open the files app so we're somewhere with `window.OCA.Text` available - await page.goto('/apps/files') + await page.goto('apps/files') // Load the editor API bundle const textRoot = await page.evaluate(() => (window as any).OC.appswebroots.text) diff --git a/playwright/e2e/editor-api.spec.ts b/playwright/e2e/editor-api.spec.ts index 703c2a97aff..279a6d914ee 100644 --- a/playwright/e2e/editor-api.spec.ts +++ b/playwright/e2e/editor-api.spec.ts @@ -12,7 +12,7 @@ const fileTest = mergeTests(uploadFileTest, test) test.describe('editor API at window.OCA.Text - MarkdownContentEditor.vue without collaboration session', () => { test.beforeEach(async ({ page }) => { // Open the files app so we're somewhere with `window.OCA.Text` available - await page.goto('/apps/files') + await page.goto('apps/files') // Load the editor API bundle const textRoot = await page.evaluate(() => (window as any).OC.appswebroots.text) @@ -65,7 +65,7 @@ fileTest.describe( fileTest.beforeEach(async ({ page }) => { // Open the files app so we're somewhere with `window.OCA.Text` available - await page.goto('/apps/files') + await page.goto('apps/files') // Load the editor API bundle const textRoot = await page.evaluate(() => (window as any).OC.appswebroots.text) diff --git a/playwright/e2e/folder-description/create.spec.ts b/playwright/e2e/folder-description/create.spec.ts index bb88e057a87..1ebaf19beb6 100644 --- a/playwright/e2e/folder-description/create.spec.ts +++ b/playwright/e2e/folder-description/create.spec.ts @@ -10,7 +10,7 @@ import { test as randomUserTest } from '../../support/fixtures/random-user.ts' const test = mergeTests(editorTest, randomUserTest) test('from new menu', async ({ page, editor }) => { - await page.goto('/apps/files') + await page.goto('apps/files') await page.getByRole('button', { name: 'New' }).click() await page.getByRole('menuitem', { name: 'Add folder description' }).click() await editor.content.click() diff --git a/playwright/e2e/propfind.spec.ts b/playwright/e2e/propfind.spec.ts index 72d4f09bf6f..17b44310578 100644 --- a/playwright/e2e/propfind.spec.ts +++ b/playwright/e2e/propfind.spec.ts @@ -26,7 +26,7 @@ test.describe('Text PROPFIND extension', () => { const properties = [PROPERTY_WORKSPACE_FLAT, PROPERTY_WORKSPACE_FILE_FLAT] const fiveSecondsAgo = Math.floor(Date.now() / 1000) - 5 - await page.goto('/apps/dashboard') + await page.goto('apps/dashboard') await user.uploadFile({ name: 'Readme.md', content: '', mtime: fiveSecondsAgo }) const [root1] = await propfindFolder(user, '/', 0, properties) @@ -44,7 +44,7 @@ test.describe('Text PROPFIND extension', () => { test('never adds rich workspace property to nested folders for flat properties', async ({ page, user }) => { const properties = [PROPERTY_WORKSPACE_FLAT, PROPERTY_WORKSPACE_FILE_FLAT] - await page.goto('/apps/dashboard') + await page.goto('apps/dashboard') await createFolder({ name: 'workspace-flat', owner: user }) const results1 = await propfindFolder(user, '/', 1, properties) @@ -61,7 +61,7 @@ test.describe('Text PROPFIND extension', () => { test('adds rich workspace property to nested folders for the default properties', async ({ page, user }) => { const properties = [PROPERTY_WORKSPACE, PROPERTY_WORKSPACE_FILE] - await page.goto('/apps/dashboard') + await page.goto('apps/dashboard') await createFolder({ name: 'workspace', owner: user }) const results1 = await propfindFolder(user, '/', 1, properties) @@ -81,7 +81,7 @@ test.describe('Text PROPFIND extension', () => { }) test('does not return a rich workspace property', async ({ page, user }) => { - await page.goto('/apps/dashboard') + await page.goto('apps/dashboard') const results1 = await propfindFolder(user, '/', 1, [PROPERTY_WORKSPACE_FLAT, PROPERTY_WORKSPACE_FILE_FLAT]) for (const result of results1) { diff --git a/playwright/e2e/smart-picker.spec.ts b/playwright/e2e/smart-picker.spec.ts index e5ce2d7c6c3..8973fd6d038 100644 --- a/playwright/e2e/smart-picker.spec.ts +++ b/playwright/e2e/smart-picker.spec.ts @@ -29,6 +29,10 @@ test('Create heading', async ({ editor }) => { test('Insert Link', async ({ editor }) => { await editor.type('/Any') await editor.getSuggestion('Any link').click() + // The picker focuses its field once its dialog is ready; until then, the + // viewer around the editor keeps the focus, and what is typed lands in + // the document + await expect(editor.referencePicker).toBeFocused() await editor.referencePicker.fill('https://github.com') await expect(editor.referenceWidget).toContainText('GitHub') await editor.referencePicker.press('Enter') diff --git a/playwright/e2e/versions.spec.ts b/playwright/e2e/versions.spec.ts index 8538b2e4324..34d10b4b838 100644 --- a/playwright/e2e/versions.spec.ts +++ b/playwright/e2e/versions.spec.ts @@ -67,7 +67,7 @@ test.describe('Versions with distant timestamps', () => { // Test that version contents are vertically aligned const oldBox = await oldVersion.getByRole('heading', { name: 'V1' }).boundingBox() - const currentBox = await oldVersion.getByRole('heading', { name: 'V3' }).boundingBox() + const currentBox = await current.getByRole('heading', { name: 'V3' }).boundingBox() expect(Math.abs(oldBox!.y - currentBox!.y)).toBeLessThan(5) }) }) diff --git a/playwright/start-nextcloud-server.mjs b/playwright/start-nextcloud-server.mjs index 83d0ee70a15..e01cbbcda79 100644 --- a/playwright/start-nextcloud-server.mjs +++ b/playwright/start-nextcloud-server.mjs @@ -65,7 +65,8 @@ if (await isServerRunning()) { } else { const ip = await start() await waitOnNextcloud(ip) - await configureNextcloud(['text', 'viewer']) + // The viewer ships with the server + await configureNextcloud(['text']) } // Idle to wait for shutdown diff --git a/playwright/support/fixtures/folder-description.ts b/playwright/support/fixtures/folder-description.ts index 11cd97a7be6..3d6ba5b4991 100644 --- a/playwright/support/fixtures/folder-description.ts +++ b/playwright/support/fixtures/folder-description.ts @@ -23,7 +23,7 @@ export const test = base.extend({ open: ({ file, page }, use) => use(async () => { // Make sure file is initialized. expect(file.name).toBe('Readme.md') - await page.goto('/apps/files') + await page.goto('apps/files') }), }) diff --git a/playwright/support/sections/ViewerSection.ts b/playwright/support/sections/ViewerSection.ts index 20484687d9e..37dd2d79ebf 100644 --- a/playwright/support/sections/ViewerSection.ts +++ b/playwright/support/sections/ViewerSection.ts @@ -18,7 +18,8 @@ export class ViewerSection { } public async openSidebarTab(name: 'Versions') { - await this.clickAction('Open sidebar') + // A button in the viewer's header, not an entry of its menu + await this.el.getByRole('button', { name: 'Open sidebar' }).click() await this.page.getByRole('tab', { name }).click() return this.page.getByRole('tabpanel', { name }) } diff --git a/src/components/ViewerComponent.vue b/src/components/ViewerComponent.vue index 5d53d81f68f..1aa314b343d 100644 --- a/src/components/ViewerComponent.vue +++ b/src/components/ViewerComponent.vue @@ -167,12 +167,8 @@ body .toastify.dialogs { margin-top: calc(45px + var(--default-clickable-area)); } -.viewer--split .source-viewer .editor__content-wrapper { +.viewer__comparison .source-viewer .editor__content-wrapper { // Account for missing menubar for old version in version comparison margin-top: calc(var(--default-clickable-area) + 2 * var(--default-grid-baseline)); } - -.viewer[data-handler="text"] .modal-wrapper .modal-container { - bottom: 0; -} diff --git a/src/css/print.scss b/src/css/print.scss index 79bbe3d2275..071e3294af9 100644 --- a/src/css/print.scss +++ b/src/css/print.scss @@ -32,7 +32,9 @@ display: none !important; } - #viewer[data-handler='text'] { + // .modal-mask as well, to outweigh the viewer's own rules for its container, + // which are as specific and load after these + .viewer__modal.modal-mask[data-handler='text'] { // Hide top border border: none; width: 100% !important; @@ -43,9 +45,20 @@ // Hide modal header (close button) display: none !important; } - .modal-container { - // Make sure top aligned as we hided the menubar */ - top: 0px; + .modal-wrapper { + height: fit-content; + } + .modal-container, .modal-container__content { + // Make sure top aligned as we hided the menubar, and let the + // content run over as many pages as it needs: the viewer pins + // the container between its header and its footer, and both + // scroll what does not fit + top: 0 !important; + bottom: auto !important; + height: fit-content !important; + overflow: visible !important; + } + text-viewer { height: fit-content; } } diff --git a/src/tests/components/TextViewerWrapper.spec.ts b/src/tests/components/TextViewerWrapper.spec.ts new file mode 100644 index 00000000000..ad444774203 --- /dev/null +++ b/src/tests/components/TextViewerWrapper.spec.ts @@ -0,0 +1,58 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { IFile } from '@nextcloud/files' + +import { mount } from '@vue/test-utils' +import { describe, expect, it, vi } from 'vitest' +import { defineComponent } from 'vue' + +// The editor itself is not what is tested here, only what it is handed +vi.mock('../../components/ViewerComponent.vue', () => ({ + default: defineComponent({ + name: 'ViewerComponent', + props: ['filename', 'fileid', 'mime', 'source', 'e2EeIsEncrypted', 'active', 'onLoadedHandler'], + template: '
', + }), +})) + +import TextViewerWrapper from '../../views/TextViewerWrapper.vue' + +/** + * A markdown file, or one of its older versions. + * + * @param root - Where the file lives + * @param source - Its URL + */ +function makeFile(root = '/files/alice', source = 'https://cloud.example.com/remote.php/dav/files/alice/notes.md'): IFile { + // Only what the wrapper reads: @nextcloud/files is mocked for all tests + return { fileid: 42, source, encodedSource: source, root, path: '/notes.md', mime: 'text/markdown', attributes: {} } as unknown as IFile +} + +describe('the text viewer wrapper', () => { + it('opens the file itself for editing', () => { + const file = makeFile() + const editor = mount(TextViewerWrapper, { props: { file } }).findComponent({ name: 'ViewerComponent' }) + + expect(editor.props('fileid')).toBe(42) + expect(editor.props('source')).toBe(file.encodedSource) + }) + + // A version carries the id of the file it belongs to: handed that, the + // editor opened the current document instead of the version + it('opens an older version from its source, to read', () => { + const version = makeFile('/versions/alice/versions/42', 'https://cloud.example.com/remote.php/dav/versions/alice/versions/42/1691420501') + const editor = mount(TextViewerWrapper, { props: { file: version } }).findComponent({ name: 'ViewerComponent' }) + + expect(editor.props('fileid')).toBeNull() + expect(editor.props('source')).toBe(version.encodedSource) + }) + + it('stops the viewer from swiping, so text can be selected', () => { + const wrapper = mount(TextViewerWrapper, { props: { file: makeFile() } }) + + expect(wrapper.emitted('update:canSwipe')).toEqual([[false]]) + }) +}) diff --git a/src/tests/viewer.spec.ts b/src/tests/viewer.spec.ts new file mode 100644 index 00000000000..da3f145fe47 --- /dev/null +++ b/src/tests/viewer.spec.ts @@ -0,0 +1,51 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { IHandler } from '@nextcloud/viewer' + +import { beforeAll, describe, expect, it, vi } from 'vitest' + +const registerHandler = vi.hoisted(() => vi.fn()) +vi.mock('@nextcloud/viewer', () => ({ registerHandler })) +// The editor itself is not what is tested here, only that it is loaded late +vi.mock('../views/TextViewerWrapper.vue', async () => { + const { defineComponent } = await import('vue') + return { default: defineComponent({ render: () => null }) } +}) + +describe('the text viewer handler', () => { + let handler: IHandler + + beforeAll(async () => { + await import('../viewer.js') + // Read now: the test setup clears mocks between tests + expect(registerHandler).toHaveBeenCalledTimes(1) + handler = registerHandler.mock.calls[0][0] + }) + + it('is registered under a tag name starting with the app id', () => { + expect(handler).toMatchObject({ id: 'text', tagName: 'text-viewer', canCompare: true }) + }) + + it.each(['text/markdown', 'text/plain', 'application/json'])('opens %s', (mime) => { + expect(handler.enabled([{ mime }] as never)).toBe(true) + }) + + it('does not open a picture, nor a mix with one', () => { + expect(handler.enabled([{ mime: 'image/png' }] as never)).toBe(false) + expect(handler.enabled([{ mime: 'text/markdown' }, { mime: 'image/png' }] as never)).toBe(false) + }) + + // The registration runs on every page, the editor only once a text file opens + it('defines its element only in onInit', async () => { + expect(window.customElements.get('text-viewer')).toBeUndefined() + + await handler.onInit!() + expect(window.customElements.get('text-viewer')).toBeDefined() + + // A second call, from another copy of the viewer, must not throw + await expect(handler.onInit!()).resolves.toBeUndefined() + }) +}) diff --git a/src/viewer.js b/src/viewer.js index 7a0117c3245..ce4bcc3e536 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1,46 +1,33 @@ -import { logger } from './helpers/logger.ts' -import { openMimetypesMarkdown, openMimetypesPlainText } from './helpers/mime.js' - /** * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ + +import { t } from '@nextcloud/l10n' +import { registerHandler } from '@nextcloud/viewer' +import { openMimetypesMarkdown, openMimetypesPlainText } from './helpers/mime.js' + import 'vite/modulepreload-polyfill' -/** - * Wrapper for async registration of ViewerComponent. - * Note: it should be named function - the name is used for component registration. - * - * @return {Promise} ViewerComponent - */ -function AsyncTextViewerComponent() { - return import('./views/ViewerView.js') -} +const tagName = 'text-viewer' +const mimes = new Set([...openMimetypesMarkdown, ...openMimetypesPlainText]) -if (typeof OCA.Viewer === 'undefined') { - logger.error('Viewer app is not installed') -} else { - OCA.Viewer.registerHandler({ - id: 'text', - mimes: [...openMimetypesMarkdown, ...openMimetypesPlainText], - component: AsyncTextViewerComponent, - group: null, - theme: 'default', - canCompare: true, - downloadCallback: async (fileInfo) => { - // Save any unsaved changes before download - const editors = window.OCA?.Text?.editorComponents - if (editors instanceof Set) { - for (const editor of editors) { - if (editor?.fileId === fileInfo.fileid && editor?.dirty) { - logger.debug('Saving file before download', { - fileId: fileInfo.fileid, - }) - await editor.save() - return - } - } - } - }, - }) -} +// This script runs on every page: the editor is only loaded, and its element +// defined, the first time the viewer opens a text file +registerHandler({ + id: 'text', + displayName: t('text', 'Text'), + tagName, + enabled: (nodes) => nodes.every((node) => mimes.has(node.mime ?? '')), + onInit: async () => { + const [{ defineCustomElement }, { default: TextViewerWrapper }] = await Promise.all([ + import('vue'), + import('./views/TextViewerWrapper.vue'), + ]) + if (window.customElements.get(tagName) === undefined) { + window.customElements.define(tagName, defineCustomElement(TextViewerWrapper, { shadowRoot: false })) + } + }, + theme: 'default', + canCompare: true, +}) diff --git a/src/views/TextViewerWrapper.vue b/src/views/TextViewerWrapper.vue new file mode 100644 index 00000000000..5daa4d3c742 --- /dev/null +++ b/src/views/TextViewerWrapper.vue @@ -0,0 +1,53 @@ + + + + + + + diff --git a/src/views/ViewerView.js b/src/views/ViewerView.js deleted file mode 100644 index 6e5e4529992..00000000000 --- a/src/views/ViewerView.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -import { createApp, defineComponent } from 'vue' -import ViewerComponent from '../components/ViewerComponent.vue' - -// The vue instance used inside text constructed with the import above. -let innerApp - -/** - * This thin Component wrapper can be rendered inside the viewer. - * - * The viewers vue instance is used for this component as it simply exports - * the options for the options api. - * - * When mounted this component constructs the vue instance - * used inside text based on texts vue import. - */ -export default defineComponent({ - name: 'ViewerView', - render: (h) => h('div', { style: { display: 'contents' } }), - props: ViewerComponent.props, - inheritAttrs: false, - mounted() { - innerApp = createApp(ViewerComponent, { - ...this.$props, - ...this.$attrs, - onLoadedHandler: () => this.$emit('update:loaded', true), - }) - innerApp.mount(this.$el) - }, - // Viewer still uses Vue 2 - beforeDestroy() { - innerApp.unmount() - innerApp = undefined - }, - // Once Viewer migrated to Vue 3 - beforeUnmount() { - innerApp.unmount() - innerApp = undefined - }, -}) diff --git a/tests/stub.php b/tests/stub.php index cd14233ef89..0ca6b52e163 100644 --- a/tests/stub.php +++ b/tests/stub.php @@ -35,11 +35,6 @@ class LoadAdditionalScriptsEvent extends \OCP\EventDispatcher\Event { } } -namespace OCA\Viewer\Event { - class LoadViewer extends \OCP\EventDispatcher\Event { - } -} - namespace OCA\Files_Sharing\Event { abstract class BeforeTemplateRenderedEvent extends \OCP\EventDispatcher\Event { abstract public function getShare(): \OCP\Share\IShare;