wip bsky client for the web & android bbell.vt3e.cat

feat: optimise webp bluebell picture & add avif option; dynamically load hls.js

vt3e.cat 10e6ea43 7654dd38

verified
+76 -45
+17
bun.lock
··· 38 38 "npm-run-all2": "^8.0.4", 39 39 "oxlint": "~1.29.0", 40 40 "prettier": "3.6.2", 41 + "rollup-plugin-visualizer": "^6.0.5", 41 42 "sass-embedded": "^1.97.0", 42 43 "typescript": "^5.9.3", 43 44 "vite": "npm:rolldown-vite@latest", ··· 471 472 472 473 "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], 473 474 475 + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], 476 + 474 477 "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 475 478 476 479 "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], ··· 580 583 "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 581 584 582 585 "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], 586 + 587 + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], 583 588 584 589 "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], 585 590 ··· 793 798 794 799 "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 795 800 801 + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], 802 + 796 803 "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 797 804 798 805 "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], ··· 802 809 "rimraf": ["rimraf@6.1.2", "", { "dependencies": { "glob": "^13.0.0", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g=="], 803 810 804 811 "rolldown": ["rolldown@1.0.0-beta.53", "", { "dependencies": { "@oxc-project/types": "=0.101.0", "@rolldown/pluginutils": "1.0.0-beta.53" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.53", "@rolldown/binding-darwin-arm64": "1.0.0-beta.53", "@rolldown/binding-darwin-x64": "1.0.0-beta.53", "@rolldown/binding-freebsd-x64": "1.0.0-beta.53", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.53", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.53", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.53", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.53", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.53", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.53", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.53", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.53", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.53" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw=="], 812 + 813 + "rollup-plugin-visualizer": ["rollup-plugin-visualizer@6.0.5", "", { "dependencies": { "open": "^8.0.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rolldown": "1.x || ^1.0.0-beta", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg=="], 805 814 806 815 "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], 807 816 ··· 869 878 870 879 "slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="], 871 880 881 + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], 882 + 872 883 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 873 884 874 885 "speakingurl": ["speakingurl@14.0.1", "", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="], ··· 965 976 966 977 "xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], 967 978 979 + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], 980 + 968 981 "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], 982 + 983 + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], 984 + 985 + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], 969 986 970 987 "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], 971 988
+1
package.json
··· 52 52 "npm-run-all2": "^8.0.4", 53 53 "oxlint": "~1.29.0", 54 54 "prettier": "3.6.2", 55 + "rollup-plugin-visualizer": "^6.0.5", 55 56 "sass-embedded": "^1.97.0", 56 57 "typescript": "^5.9.3", 57 58 "vite": "npm:rolldown-vite@latest",
public/images/bluebell.avif

This is a binary file and will not be displayed.

public/images/bluebell.webp

This is a binary file and will not be displayed.

src/assets/bluebell.webp

This is a binary file and will not be displayed.

+11 -7
src/components/Feed/Embeds/VideoEmbed.vue
··· 1 1 <script setup lang="ts"> 2 2 import { ref, onUnmounted, computed, nextTick } from 'vue' 3 - import Hls from 'hls.js' 3 + import type Hls from 'hls.js' 4 4 import { IconPlayArrowRounded } from '@iconify-prerendered/vue-material-symbols' 5 5 6 6 import { AppBskyEmbedVideo } from '@atcute/bluesky' ··· 25 25 const video = videoRef.value 26 26 if (!video) return 27 27 28 - if (Hls.isSupported()) { 29 - hls = new Hls({ 28 + if (video.canPlayType('application/vnd.apple.mpegurl')) { 29 + video.src = props.embed.playlist 30 + video.play() 31 + return 32 + } 33 + 34 + const { default: HlsClass } = await import('hls.js') 35 + if (HlsClass.isSupported()) { 36 + hls = new HlsClass({ 30 37 maxBufferLength: 30, 31 38 }) 32 39 hls.loadSource(props.embed.playlist) 33 40 hls.attachMedia(video) 34 - hls.on(Hls.Events.MANIFEST_PARSED, () => { 41 + hls.on(HlsClass.Events.MANIFEST_PARSED, () => { 35 42 video.play() 36 43 }) 37 - } else if (video.canPlayType('application/vnd.apple.mpegurl')) { 38 - video.src = props.embed.playlist 39 - video.play() 40 44 } 41 45 } 42 46
+17 -10
src/views/Onboarding/OnboardingFlow.vue
··· 1 1 <script setup lang="ts"> 2 2 import { ref, computed } from 'vue' 3 - import BluebellImg from '@/assets/bluebell.webp' 4 3 5 4 import IntroStep from './steps/IntroStep.vue' 6 5 import ThemeStep from './steps/ThemeStep.vue' ··· 47 46 <div 48 47 class="bg-layer" 49 48 :style="{ 50 - backgroundImage: `url(${BluebellImg})`, 51 49 filter: `blur(${bgBlur})`, 52 50 transform: `scale(${bgScale})`, 53 51 }" 54 - ></div> 52 + > 53 + <picture> 54 + <source srcset="/images/bluebell.avif" type="image/avif" /> 55 + <source srcset="/images/bluebell.webp" type="image/webp" /> 56 + <img src="/images/bluebell.webp" alt="" /> 57 + </picture> 58 + </div> 55 59 56 60 <div class="bg-overlay"></div> 57 61 ··· 81 85 82 86 .bg-layer { 83 87 position: absolute; 84 - inset: -20px; 85 - background-size: cover; 86 - background-position: center; 87 - background-repeat: no-repeat; 88 + top: 0; 89 + left: 0; 90 + width: 100%; 91 + height: 100%; 92 + z-index: -1; 93 + will-change: transform, filter; 88 94 89 95 opacity: 1; 90 96 transition: ··· 93 99 filter 0.8s ease; 94 100 will-change: transform, opacity, filter; 95 101 96 - &::after { 97 - content: ''; 98 - position: absolute; 102 + img { 103 + width: 100%; 104 + height: 100%; 105 + object-fit: cover; 99 106 inset: 0; 100 107 } 101 108 }
+30 -28
vite.config.ts
··· 3 3 import { defineConfig } from 'vite' 4 4 import vue from '@vitejs/plugin-vue' 5 5 import vueDevTools from 'vite-plugin-vue-devtools' 6 + import { visualizer } from 'rollup-plugin-visualizer' 6 7 7 8 import packageJson from './package.json' 8 9 import metadata from './public/oauth-client-metadata.json' with { type: 'json' } ··· 11 12 const SERVER_PORT = 5173 12 13 13 14 export default defineConfig({ 14 - plugins: [ 15 - vue(), 16 - vueDevTools(), 17 - { 18 - name: 'oauth-config', 19 - config(_conf, { command }) { 20 - if (command === 'build') { 21 - process.env.VITE_OAUTH_CLIENT_ID = metadata.client_id 22 - process.env.VITE_OAUTH_REDIRECT_URI = metadata.redirect_uris[0] 23 - } else { 24 - const redirectUri = `http://${SERVER_HOST}:${SERVER_PORT}${new URL(metadata.redirect_uris[0]).pathname}` 25 - process.env.VITE_OAUTH_CLIENT_ID = 26 - `http://localhost?redirect_uri=${encodeURIComponent(redirectUri)}` + 27 - `&scope=${encodeURIComponent(metadata.scope)}` 28 - process.env.VITE_OAUTH_REDIRECT_URI = redirectUri 29 - } 30 - process.env.VITE_OAUTH_SCOPE = metadata.scope 31 - }, 32 - }, 33 - ], 34 - define: { 35 - __APP_VERSION__: JSON.stringify(packageJson.version), 36 - }, 37 - resolve: { 38 - alias: { 39 - '@': fileURLToPath(new URL('./src', import.meta.url)), 40 - }, 41 - }, 15 + plugins: [ 16 + vue(), 17 + visualizer(), 18 + vueDevTools(), 19 + { 20 + name: 'oauth-config', 21 + config(_conf, { command }) { 22 + if (command === 'build') { 23 + process.env.VITE_OAUTH_CLIENT_ID = metadata.client_id 24 + process.env.VITE_OAUTH_REDIRECT_URI = metadata.redirect_uris[0] 25 + } else { 26 + const redirectUri = `http://${SERVER_HOST}:${SERVER_PORT}${new URL(metadata.redirect_uris[0]).pathname}` 27 + process.env.VITE_OAUTH_CLIENT_ID = 28 + `http://localhost?redirect_uri=${encodeURIComponent(redirectUri)}` + 29 + `&scope=${encodeURIComponent(metadata.scope)}` 30 + process.env.VITE_OAUTH_REDIRECT_URI = redirectUri 31 + } 32 + process.env.VITE_OAUTH_SCOPE = metadata.scope 33 + }, 34 + }, 35 + ], 36 + define: { 37 + __APP_VERSION__: JSON.stringify(packageJson.version), 38 + }, 39 + resolve: { 40 + alias: { 41 + '@': fileURLToPath(new URL('./src', import.meta.url)), 42 + }, 43 + }, 42 44 })