diff --git a/frontend/src/composables/use-playback.ts b/frontend/src/composables/use-playback.ts index 166222d78f7..16e9eba0ed4 100644 --- a/frontend/src/composables/use-playback.ts +++ b/frontend/src/composables/use-playback.ts @@ -18,7 +18,7 @@ export function usePlayback() { * - iOS's Safari fullscreen API is only available for the video element */ const fullscreen = useFullscreen().isSupported.value - ? useFullscreen(undefined, { autoExit: true }) + ? useFullscreen(document.body, { autoExit: true }) : useFullscreen(mediaElementRef, { autoExit: true }); const keys = useMagicKeys(); diff --git a/package-lock.json b/package-lock.json index 397f54acbe1..40114ade40b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4199,6 +4199,16 @@ "eslint": ">=8.40.0" } }, + "node_modules/@tauri-apps/api": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.2.0.tgz", + "integrity": "sha512-R8epOeZl1eJEl603aUMIGb4RXlhPjpgxbGVEaqY+0G5JG9vzV/clNlzTeqc+NLYXVqXcn8mb4c5b9pJIUDEyAg==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, "node_modules/@tauri-apps/cli": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.2.7.tgz", @@ -15374,7 +15384,8 @@ "packages/tauri-runtime": { "name": "@jellyfin-vue/tauri-runtime", "dependencies": { - "@jellyfin-vue/frontend": "*" + "@jellyfin-vue/frontend": "*", + "@tauri-apps/api": "2.2.0" }, "devDependencies": { "@jellyfin-vue/configs": "*", diff --git a/packages/configs/src/lint/rules/typescript-vue.ts b/packages/configs/src/lint/rules/typescript-vue.ts index 3d2ec34fc2d..da974dd81ba 100644 --- a/packages/configs/src/lint/rules/typescript-vue.ts +++ b/packages/configs/src/lint/rules/typescript-vue.ts @@ -133,7 +133,7 @@ const common = [ { name: '(@jellyfin-vue/configs/lint/typescript-vue - sonarcloud) Custom config', rules: { - 'sonarjs/function/return-type': 'off' + 'sonarjs/function-return-type': 'off' } } ] satisfies Linter.Config[]; diff --git a/packages/tauri-runtime/README.md b/packages/tauri-runtime/README.md new file mode 100644 index 00000000000..d019f37db33 --- /dev/null +++ b/packages/tauri-runtime/README.md @@ -0,0 +1,10 @@ +This is the package that provides a build from `@jellyfin-vue/frontend` with all the Tauri-specific +code, since `@jellyfin-vue/frontend` must not have any code that doesn't belong to the DOM/Browser environment. + +It works by injecting the Tauri logic into the standard `document`, `window` or `globalThis` objects +(or any other relevant global) and loading those modules before the frontend's one, so Tauri code is loaded +always first. + +See `entrypoint.ts` and `src/fullscreen.ts` for an example. + +Every "polyfilled" feature must be in their own module to keep the logic simple enough. diff --git a/packages/tauri-runtime/entrypoint.ts b/packages/tauri-runtime/entrypoint.ts index d5fb95427a4..35e31d48cf0 100644 --- a/packages/tauri-runtime/entrypoint.ts +++ b/packages/tauri-runtime/entrypoint.ts @@ -1,4 +1,7 @@ -import './src/main.ts'; +/** + * Every polyfilled feature must be in their own module. + */ +import '#/fullscreen.ts'; /** * The Tauri-specific code must be loaded before the frontend code to ensure that * all the polyfills for the runtime have been loaded diff --git a/packages/tauri-runtime/package.json b/packages/tauri-runtime/package.json index 8033695af19..c53f36707b3 100644 --- a/packages/tauri-runtime/package.json +++ b/packages/tauri-runtime/package.json @@ -2,6 +2,9 @@ "name": "@jellyfin-vue/tauri-runtime", "type": "module", "description": "The frontend including tauri-specific runtime code", + "imports": { + "#/*": "./src/*" + }, "scripts": { "analyze:bundle": "vite build --mode analyze:bundle", "analyze:cycles": "vite build --mode analyze:cycles", @@ -21,6 +24,7 @@ "vite": "6.0.11" }, "dependencies": { - "@jellyfin-vue/frontend": "*" + "@jellyfin-vue/frontend": "*", + "@tauri-apps/api": "2.2.0" } } diff --git a/packages/tauri-runtime/src/fullscreen.ts b/packages/tauri-runtime/src/fullscreen.ts new file mode 100644 index 00000000000..3c67c6e63e0 --- /dev/null +++ b/packages/tauri-runtime/src/fullscreen.ts @@ -0,0 +1,4 @@ +import { getCurrentWindow } from '@tauri-apps/api/window'; + +Element.prototype.requestFullscreen = async () => getCurrentWindow().setFullscreen(true); +Document.prototype.exitFullscreen = async () => getCurrentWindow().setFullscreen(false); diff --git a/packages/tauri-runtime/src/main.ts b/packages/tauri-runtime/src/main.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/tauri-runtime/tsconfig.json b/packages/tauri-runtime/tsconfig.json index 0692e8899d2..78bc226a655 100644 --- a/packages/tauri-runtime/tsconfig.json +++ b/packages/tauri-runtime/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "@jellyfin-vue/configs/typescript", "compilerOptions": { - "baseUrl": "." + "baseUrl": ".", + "lib": [ + "DOM" + ] }, "exclude": [ "dist", diff --git a/packages/tauri-runtime/vite.config.ts b/packages/tauri-runtime/vite.config.ts index 7f1b5b08307..97d03481cc7 100644 --- a/packages/tauri-runtime/vite.config.ts +++ b/packages/tauri-runtime/vite.config.ts @@ -17,7 +17,7 @@ export default defineConfig( } }, // don't minify for debug builds - minify: process.env.TAURI_ENV_DEBUG ? false : 'esbuild', + ...(process.env.TAURI_ENV_DEBUG ? { minify: false } : {}), // produce sourcemaps for debug builds sourcemap: !!process.env.TAURI_ENV_DEBUG }, diff --git a/packaging/tauri/tauri.conf.json b/packaging/tauri/tauri.conf.json index c90a72eaf39..231900dc66c 100644 --- a/packaging/tauri/tauri.conf.json +++ b/packaging/tauri/tauri.conf.json @@ -30,15 +30,21 @@ "plugins": {}, "app": { "security": { - "csp": null + "csp": null, + "capabilities": [ + { + "identifier": "all instances", + "description": "application capabilities for all instances", + "windows": ["*"], + "permissions": [ + "core:window:allow-set-fullscreen" + ] + } + ] }, "windows": [ { - "title": "Jellyfin Vue", - "width": 800, - "height": 600, - "resizable": true, - "fullscreen": false + "title": "Jellyfin Vue" } ] }