feat(ui): premium app features — installability, WCO, share, badging#2982
feat(ui): premium app features — installability, WCO, share, badging#2982tomayac wants to merge 19 commits into
Conversation
Enable the service worker (previously disabled), configure Workbox with network-only navigation fallback to preserve ISR freshness, and add standalone display mode to the manifest so browsers offer installation. Add a PwaPrompt component that surfaces a toast when a new SW version is waiting, and an install button in settings that appears only when the browser fires beforeinstallprompt.
Adds a Share button to the package header button group (alongside Compare and Likes) that invokes navigator.share with the package name, description, and current URL. The button only renders when the browser supports the Web Share API and is also registered as a command palette action.
Enable display_override: window-controls-overlay so the app header fills the installed-PWA title bar. The header background becomes the drag handle; the nav is explicitly non-draggable. env(titlebar-area-y/x) push content below the OS controls and clear the macOS traffic lights. Add three manifest shortcuts (Search, Compare, Settings) so users can jump straight to key destinations from the home-screen/taskbar icon.
Use input-switch-polyfill so Safari renders a native OS switch control and other browsers get a consistent polyfilled fallback. The elaborate hand-painted CSS is replaced by a single accent-color custom property that integrates with the user's chosen accent. Polyfill JS is loaded lazily only when 'switch' is absent from HTMLInputElement.prototype.
useAppBadge wraps navigator.setAppBadge/clearAppBadge with feature detection. useLikesBadge activates when the npm connector is connected: it loads the user's packages once, then polls /api/social/likes/:pkg every 10 minutes and sets the badge to the count of new likes received since the previous check. The baseline is persisted in localStorage per npm username so the badge never fires on the very first visit.
Adds a Playwright-based script (scripts/generate-pwa-screenshots.ts) that captures the homepage and a package page in light + dark mode at desktop (1280×800) and mobile (390×844) viewports. The script auto-starts nuxt preview, takes all 8 screenshots, then stops the server. Pass --url <url> to skip the local server and screenshot any running instance (useful in CI: --url https://npmx.dev). The PWA manifest now includes a screenshots[] array (nuxt.config.ts) with form_factor 'wide'/'narrow' entries, enabling Chrome's richer install dialog on desktop (Chrome 108+) and Android (Chrome 94+). Usage: pnpm build && pnpm generate:screenshots # commit public/screenshots/*.png
Add id: '/' to the web app manifest for stable PWA identity across manifest URL changes (spec-recommended practice). Generate all 8 PWA screenshots (desktop/mobile × dark/light × home/package) and commit them so Vercel CI picks them up. Chrome 108+ on desktop will show these in the richer install dialog carousel.
Window Controls Overlay:
- Fix header and scroll container being 15 px short of right edge by
resetting scrollbar-gutter to auto in WCO mode (html { scrollbar-gutter:
stable } reserved a gutter even when overflow: hidden removed the bar)
- Make header position:fixed and span full viewport width (inset-inline: 0)
in WCO mode so its border-bottom reaches the window edge
- Add #app-scroll fixed scroll container starting at the header's bottom
border so the scrollbar track never appears in the title bar
- Solid opaque header background (--bg) and no backdrop-filter in WCO mode
- Full-width nav with two-value padding-inline to handle both macOS traffic
lights (left) and Windows min/max/close (right)
- All interactive descendants declare no-drag; empty header space is drag
- Keep theme-color meta in sync with --bg (oklch) by writing directly to the
DOM node and guarding against @unhead re-asserting the PWA module's static
value after onMounted
Share button:
- Register 'v' keyboard shortcut; programmatic click anchors the share sheet
at the button position instead of the mouse cursor
- Include og:image as a shareable file when the browser supports file sharing
- Expose click() on ButtonBase via defineExpose for programmatic triggering
input-switch-polyfill:
- Bump to 1.12.0
- Add MutationObserver that re-syncs --switch-accent on every color-mode or
accent-color change so switches react live without a page reload
Adds an ambient module declaration for the untyped input-switch-polyfill package and fixes a noUncheckedIndexedAccess violation when reading the first matched switch element.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds PWA install/update support and manifest updates, package sharing, badge handling, window-controls-overlay shell behaviour, and switch polyfill support, with matching locale, schema, dependency, and screenshot tooling changes. ChangesPWA, sharing, badges, and shell support
Sequence Diagram(s)sequenceDiagram
participant ServiceWorker
participant PwaPrompt
participant SettingsPage
participant User
ServiceWorker->>PwaPrompt: needRefresh
PwaPrompt->>User: show update toast
User->>PwaPrompt: updateServiceWorker()
User->>SettingsPage: open install section
SettingsPage->>User: show install button
User->>SettingsPage: $pwa.install()
sequenceDiagram
participant User
participant PackageHeader
participant ShareButton
participant Navigator
User->>PackageHeader: choose share command or button
PackageHeader->>ShareButton: trigger sharePackage()
ShareButton->>ShareButton: read og:image and build ShareData
ShareButton->>Navigator: navigator.share(data)
Navigator-->>User: native share sheet
sequenceDiagram
participant LikesBadgePlugin
participant useLikesBadge
participant API
participant LocalStorage
participant useAppBadge
LikesBadgePlugin->>useLikesBadge: initialise
useLikesBadge->>API: list likes for cached packages
API-->>useLikesBadge: counts
useLikesBadge->>LocalStorage: loadStored / saveStored
useLikesBadge->>useAppBadge: setBadge() or clearBadge()
sequenceDiagram
participant BrowserChrome
participant AppShell
participant AppHeader
participant AppScroll
BrowserChrome->>AppShell: display-mode: window-controls-overlay
AppShell->>AppHeader: fixed top header styles
AppShell->>AppScroll: fixed internal scroll region
AppShell->>BrowserChrome: theme-color meta updated from --bg
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hello! Thank you for opening your first PR to npmx, @tomayac! 🚀 Here’s what will happen next:
|
Lunaria Status Overview🌕 This pull request will trigger status changes. Learn moreBy default, every PR changing files present in the Lunaria configuration's You can change this by adding one of the keywords present in the Tracked Files
Warnings reference
|
…-features # Conflicts: # pnpm-lock.yaml
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
❌ 10 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
app/composables/useLikesBadge.ts (1)
54-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNon-null assertion on array index bypasses the strict index-safety guideline.
userPackages.value[i]!at Line 58 uses a non-null assertion instead of checking the value before use. As per coding guidelines, "Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index." Prefer destructuring the package name alongside the result to avoid re-indexing.♻️ Proposed fix
const current: Record<string, number> = {} - for (let i = 0; i < userPackages.value.length; i++) { - const r = results[i] - if (r?.status === 'fulfilled') { - current[userPackages.value[i]!] = r.value.totalLikes - } - } + userPackages.value.forEach((pkg, i) => { + const r = results[i] + if (r?.status === 'fulfilled') { + current[pkg] = r.value.totalLikes + } + })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/composables/useLikesBadge.ts` around lines 54 - 60, The loop in useLikesBadge is re-indexing userPackages.value and using a non-null assertion on the package name, which bypasses strict index safety. Update the logic in the current/results processing to avoid userPackages.value[i]! by destructuring or otherwise carrying the package name together with the corresponding promise result before awaiting, then use that safe value when populating current.Source: Coding guidelines
app/app.vue (1)
248-280: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHeader-height magic number duplicated across files.
calc(env(titlebar-area-y, 0px) + 3.5rem + 1px)(Line 275) must stay in sync withAppHeader.vue'snavheight (min-h-14= 3.5rem) plus its border-bottom. Any future change to the header's height inAppHeader.vuewill silently desync this offset, causing the scrollbar/content to start at the wrong position in WCO mode.Consider exposing the header height as a shared CSS custom property (e.g. set on
:rootfromAppHeader.vue, consumed here) to keep the two files in sync.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/app.vue` around lines 248 - 280, The WCO scroll offset in App.vue is duplicating AppHeader.vue’s header height, so update the fixed `#app-scroll` positioning to derive its top offset from a shared CSS custom property instead of hardcoding 3.5rem + 1px. Set that custom property from AppHeader.vue based on the nav’s actual height and border, then consume it in the `@media` (display-mode: window-controls-overlay) block so both files stay in sync if the header changes.app/components/AppHeader.vue (1)
260-263: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueInteractive-element selector may miss some focusable/clickable elements.
The
:is(a, button, input, select, [role='button'], [role='combobox'])list doesn't covertextarea,[role='menuitem']/[role='tab'], or arbitrary elements with atabindexand click handler that some components (e.g.LogoContextMenu) might render. Any such element left with inheriteddragwould swallow clicks under WCO.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/components/AppHeader.vue` around lines 260 - 263, The interactive-element selector in AppHeader’s header drag override is too narrow and can leave focusable/clickable elements inheriting drag behavior. Update the selector in the `header :deep(:is(...))` rule to also cover missing interactive cases such as `textarea`, `[role='menuitem']`, `[role='tab']`, and generic tabbable/clickable elements (for example those with `tabindex`), so components like `LogoContextMenu` don’t have clicks swallowed under WCO.test/unit/a11y-component-coverage.spec.ts (1)
64-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPlease test
PwaPrompt.client.vueinstead of skipping it.This component does not need a real service worker to render; a vitest mount can stub
useNuxtApp().$pwa.needRefreshand exercise the new alert and action buttons. Keeping it on the skip list leaves the update prompt without any accessibility coverage. As per coding guidelines,**/*.{test,spec}.{ts,tsx}: "Write unit tests for core functionality usingvitest".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/unit/a11y-component-coverage.spec.ts` at line 64, The a11y coverage list is incorrectly skipping PwaPrompt.client.vue, so it never gets tested. Remove the skip entry from the coverage spec and add a vitest mount-based test for PwaPrompt.client.vue that stubs useNuxtApp().$pwa.needRefresh to render the prompt and verify the alert plus action buttons. Locate the change in the a11y-component-coverage.spec.ts entry for PwaPrompt.client.vue and update the related component test coverage accordingly.Source: Coding guidelines
app/components/Package/Header.vue (1)
81-87: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShare through one code path.
This duplicates the payload building in
app/components/Package/ShareButton.client.vuebut omits the optionalog:imagefile, so the command-palette action already behaves differently from the visible share button. Please extract a shared helper/composable and reuse it from both entry points.Also applies to: 114-123
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/components/Package/Header.vue` around lines 81 - 87, The package sharing logic is duplicated between Header.vue’s sharePackage and ShareButton.client.vue, and the Header path currently omits the optional og:image file, causing behavior drift. Extract the payload-building and navigator.share invocation into a shared helper/composable, then reuse it from both share entry points so both paths include the same fields and fallbacks, including the og:image file when available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/components/Package/ShareButton.client.vue`:
- Around line 45-50: The share flow in ShareButton.client.vue is setting
shareData.files after getOgImageFile() returns, but the real payload is not
being validated before navigator.share is called. Update the ShareButton logic
around the shareData object so the final ShareData shape is checked with files
included before mutating shareData, using getOgImageFile() and the
navigator.share call as the key points to adjust. Keep the URL/text fallback
available by only attaching the file once the full payload is confirmed valid.
In `@app/composables/useLikesBadge.ts`:
- Around line 25-43: The `useLikesBadge` logic has a race between the `npmUser`
refresh watcher and the immediate `checkLikes()` watcher, which can persist a
stale package baseline for a new user. Update `useLikesBadge` so `checkLikes()`
only runs after the package list refresh for the current `npmUser` has
completed, either by sharing the refresh path in the `npmUser` watcher or by
introducing a readiness guard that blocks the immediate watcher until
`userPackages` is current. Make the fix around the `watch(npmUser, ...)` block
and the `checkLikes()` watcher so the first poll always uses the new user’s
package list.
In `@scripts/generate-pwa-screenshots.ts`:
- Around line 42-44: The `generate-pwa-screenshots.ts` argument parsing for
`--url` does not validate that a value follows the flag before reading
`args[urlFlagIdx + 1]`. Update the `explicitUrl` handling to first confirm
`--url` is present and that the next array entry exists and is a valid non-empty
string; if it is missing, fail fast with a clear error instead of falling back
to the preview-server path. Keep the fix localized to the
`args`/`urlFlagIdx`/`explicitUrl` logic so the script’s existing flow remains
unchanged.
- Around line 96-109: The startup flow in startPreviewServer currently leaves
the spawned nuxt preview running if waitForServer(url) fails, and
server.on('error') throws outside main().catch(). Change the preview startup to
reject/propagate errors through the Promise returned by startPreviewServer, and
make sure the child process is terminated/cleaned up before rejecting or
rethrowing. Use the existing startPreviewServer, waitForServer, and server event
handlers as the main places to fix this.
---
Nitpick comments:
In `@app/app.vue`:
- Around line 248-280: The WCO scroll offset in App.vue is duplicating
AppHeader.vue’s header height, so update the fixed `#app-scroll` positioning to
derive its top offset from a shared CSS custom property instead of hardcoding
3.5rem + 1px. Set that custom property from AppHeader.vue based on the nav’s
actual height and border, then consume it in the `@media` (display-mode:
window-controls-overlay) block so both files stay in sync if the header changes.
In `@app/components/AppHeader.vue`:
- Around line 260-263: The interactive-element selector in AppHeader’s header
drag override is too narrow and can leave focusable/clickable elements
inheriting drag behavior. Update the selector in the `header :deep(:is(...))`
rule to also cover missing interactive cases such as `textarea`,
`[role='menuitem']`, `[role='tab']`, and generic tabbable/clickable elements
(for example those with `tabindex`), so components like `LogoContextMenu` don’t
have clicks swallowed under WCO.
In `@app/components/Package/Header.vue`:
- Around line 81-87: The package sharing logic is duplicated between
Header.vue’s sharePackage and ShareButton.client.vue, and the Header path
currently omits the optional og:image file, causing behavior drift. Extract the
payload-building and navigator.share invocation into a shared helper/composable,
then reuse it from both share entry points so both paths include the same fields
and fallbacks, including the og:image file when available.
In `@app/composables/useLikesBadge.ts`:
- Around line 54-60: The loop in useLikesBadge is re-indexing userPackages.value
and using a non-null assertion on the package name, which bypasses strict index
safety. Update the logic in the current/results processing to avoid
userPackages.value[i]! by destructuring or otherwise carrying the package name
together with the corresponding promise result before awaiting, then use that
safe value when populating current.
In `@test/unit/a11y-component-coverage.spec.ts`:
- Line 64: The a11y coverage list is incorrectly skipping PwaPrompt.client.vue,
so it never gets tested. Remove the skip entry from the coverage spec and add a
vitest mount-based test for PwaPrompt.client.vue that stubs
useNuxtApp().$pwa.needRefresh to render the prompt and verify the alert plus
action buttons. Locate the change in the a11y-component-coverage.spec.ts entry
for PwaPrompt.client.vue and update the related component test coverage
accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: cc5c172b-099f-4e84-8a6c-497d259c7474
⛔ Files ignored due to path filters (9)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/screenshots/desktop-dark-home.pngis excluded by!**/*.pngpublic/screenshots/desktop-dark-package.pngis excluded by!**/*.pngpublic/screenshots/desktop-light-home.pngis excluded by!**/*.pngpublic/screenshots/desktop-light-package.pngis excluded by!**/*.pngpublic/screenshots/mobile-dark-home.pngis excluded by!**/*.pngpublic/screenshots/mobile-dark-package.pngis excluded by!**/*.pngpublic/screenshots/mobile-light-home.pngis excluded by!**/*.pngpublic/screenshots/mobile-light-package.pngis excluded by!**/*.png
📒 Files selected for processing (18)
app/app.vueapp/components/AppHeader.vueapp/components/Button/Base.vueapp/components/Package/Header.vueapp/components/Package/ShareButton.client.vueapp/components/PwaPrompt.client.vueapp/components/Settings/Toggle.client.vueapp/composables/useAppBadge.tsapp/composables/useLikesBadge.tsapp/pages/settings.vueapp/plugins/input-switch-polyfill.client.tsapp/plugins/likes-badge.client.tsapp/types/input-switch-polyfill.d.tsi18n/locales/en.jsonnuxt.config.tspackage.jsonscripts/generate-pwa-screenshots.tstest/unit/a11y-component-coverage.spec.ts
|
| 📦 Package | 📏 Size |
|---|---|
| @napi-rs/wasm-runtime@1.1.5 | 6.3 MB |
| @babel/types@7.29.0 | 2.8 MB |
| @vue/compiler-sfc@3.5.34 | 2.6 MB |
| @nuxt/vite-builder@4.4.5 | 2.4 MB |
| @emnapi/core@1.11.1 | 2 MB |
| @babel/parser@7.29.3 | 2 MB |
| lodash@4.18.1 | 1.4 MB |
| seroval@1.5.3 | 1.1 MB |
| ajv@8.20.0 | 1 MB |
| ajv@6.15.0 | 938.2 kB |
| ajv@6.14.0 | -938.2 kB |
| js-yaml@4.2.0 | 874 kB |
| @tybys/wasm-util@0.10.2 | 813 kB |
| @babel/traverse@7.29.7 | 708.1 kB |
| @vue/compiler-dom@3.5.34 | 647.3 kB |
| @vue/compiler-core@3.5.34 | 630.1 kB |
| vite-plugin-checker@0.13.0 | 590.3 kB |
| acorn@8.17.0 | 563 kB |
| @babel/generator@7.29.7 | 538.5 kB |
| minimatch@10.2.5 | 536.1 kB |
| @babel/helper-create-class-features-plugin@7.29.7 | 441.2 kB |
| @emnapi/runtime@1.11.1 | 433.5 kB |
| enhanced-resolve@5.24.0 | 409.5 kB |
| magicast@0.5.2 | 379.2 kB |
| unhead@2.1.13 | 333.7 kB |
| fuse.js@7.3.0 | 311.6 kB |
| @oxc-parser/binding-linux-x64-gnu@0.135.0 → @oxc-parser/binding-linux-x64-gnu@0.137.0 | -245.8 kB |
| @emnapi/wasi-threads@1.2.2 | 225.1 kB |
| postcss@8.5.14 | 205.3 kB |
| postcss-selector-parser@7.1.1 | 187.5 kB |
| giget@3.2.0 | 183.1 kB |
| @nuxt/schema@4.4.5 | 170 kB |
| resolve@1.22.12 | 165.4 kB |
| webpack-sources@3.5.0 | 164.1 kB |
| @babel/helper-module-transforms@7.29.7 | 161.7 kB |
| fast-uri@3.1.2 | 156.9 kB |
| chokidar@4.0.3 | 148.8 kB |
| css-declaration-sorter@7.3.1 | 125.2 kB |
| srvx@0.11.15 | 117.1 kB |
| srvx@0.11.18 | 114.3 kB |
| srvx@0.11.17 | -114 kB |
| unimport@6.2.0 | 104.9 kB |
| @nuxt/kit@4.4.6 | 101.6 kB |
| @nuxt/kit@4.4.5 | 101.5 kB |
| es-module-lexer@2.1.0 | 100.7 kB |
| semver@7.8.1 | 100.2 kB |
| @vue/shared@3.5.34 | 87.8 kB |
| terser-webpack-plugin@5.3.16 → terser-webpack-plugin@5.6.1 | 86.7 kB |
| @vitejs/plugin-vue@6.0.6 | 75.5 kB |
| nuxtseo-shared@5.3.1 | 72.1 kB |
| nuxtseo-shared@5.3.0 | -71.8 kB |
| @babel/template@7.29.7 | 70.7 kB |
| @babel/compat-data@7.29.7 | 68.2 kB |
| @rollup/pluginutils@5.4.0 | 66.3 kB |
| @unhead/vue@2.1.13 | 66.3 kB |
| @babel/helper-module-imports@7.29.7 | 63.7 kB |
| postcss-merge-longhand@7.0.7 | 55.4 kB |
| exsolve@1.1.0 | 55.3 kB |
| @babel/helper-member-expression-to-functions@7.29.7 | 54.9 kB |
| @babel/helper-compilation-targets@7.29.7 | 53.8 kB |
| nypm@0.6.8 | 51 kB |
| @babel/helper-define-polyfill-provider@0.6.6 → @babel/helper-define-polyfill-provider@0.6.8 | 48.9 kB |
| @babel/helper-validator-identifier@7.28.5 | 48.8 kB |
| @vue/compiler-ssr@3.5.34 | 48.5 kB |
| nuxtseo-shared@5.1.3 | 48 kB |
| nypm@0.6.7 | -48 kB |
| nuxt-site-config@4.1.1 | 47.9 kB |
| brace-expansion@5.0.6 | 47.6 kB |
| nypm@0.6.6 | 47.5 kB |
| which-typed-array@1.1.22 | 45.4 kB |
| @babel/helper-replace-supers@7.29.7 | 43.3 kB |
| stylehacks@7.0.11 | 38.9 kB |
| tinyglobby@0.2.16 | 38.7 kB |
| is-core-module@2.16.2 | 38.6 kB |
| readdirp@4.1.2 | 36.1 kB |
| tinyexec@1.1.1 | 35.4 kB |
| @babel/code-frame@7.29.7 | 34.6 kB |
| nanoid@3.3.14 | -32.8 kB |
| nanoid@3.3.13 | 32.7 kB |
| nanoid@3.3.11 | 32.6 kB |
| postcss-ordered-values@7.0.4 | 32.4 kB |
| @babel/helper-string-parser@7.27.1 | 31.8 kB |
| postcss-merge-rules@7.0.11 | 31.2 kB |
| terser@5.46.0 → terser@5.48.0 | 31.2 kB |
| site-config-stack@4.1.1 | 27.4 kB |
| postcss-minify-selectors@7.1.2 | 27.1 kB |
| @types/estree@1.0.9 | 26.2 kB |
| cssnano-preset-default@7.0.17 | 25.8 kB |
| nuxt-site-config-kit@4.1.1 | 25.3 kB |
| input-switch-polyfill@1.12.0 | 25.2 kB |
| nuxt-og-image@6.6.0 → nuxt-og-image@6.7.0 | 25.1 kB |
| call-bind@1.0.9 | 24.1 kB |
| @rolldown/pluginutils@1.0.0-rc.13 | 23.6 kB |
| @babel/helper-globals@7.29.7 | 23.1 kB |
| side-channel@1.1.1 | 22.9 kB |
| empathic@2.0.0 | 21.2 kB |
| postcss-reduce-initial@7.0.9 | 18.7 kB |
| postcss-minify-font-values@7.0.3 | 18.6 kB |
| side-channel-list@1.0.1 | 17 kB |
| rollup@2.79.2 → rollup@2.80.0 | 16.5 kB |
| postcss-discard-comments@7.0.8 | 16 kB |
| es-abstract-get@1.0.0 | 15.9 kB |
| smob@1.5.0 → smob@1.6.2 | -15.3 kB |
| @humanfs/types@0.15.0 | 15.2 kB |
| postcss-convert-values@7.0.12 | 14.5 kB |
| watchpack@2.5.1 → watchpack@2.5.2 | 12.8 kB |
| caniuse-api@3.0.0 | 12.2 kB |
| es-object-atoms@1.1.2 | 12 kB |
| @babel/helper-validator-option@7.29.7 | 11.8 kB |
| @babel/helper-plugin-utils@7.29.7 | 11.8 kB |
| postcss-normalize-string@7.0.3 | 11.7 kB |
| postcss-colormin@7.0.10 | 11.4 kB |
| postcss-normalize-url@7.0.3 | 11.1 kB |
| postcss-svgo@7.1.3 | 10.6 kB |
| postcss-minify-gradients@7.0.5 | 10.5 kB |
| hasown@2.0.4 | 10.3 kB |
| @babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7 | 9.9 kB |
| postcss-reduce-transforms@7.0.3 | 9.2 kB |
| is-document.all@1.0.0 | 9 kB |
| postcss-normalize-positions@7.0.4 | 8.5 kB |
| postcss-minify-params@7.0.9 | 8.2 kB |
| tinyclip@0.1.15 | 8.1 kB |
| cssnano@7.1.9 | 7.4 kB |
| postcss-normalize-unicode@7.0.9 | 7.3 kB |
| postcss-normalize-repeat-style@7.0.4 | 7.3 kB |
| postcss-discard-duplicates@7.0.4 | 7.1 kB |
| tinyclip@0.1.14 | -6.8 kB |
| cssnano-utils@5.0.3 | 6.7 kB |
| @babel/helper-optimise-call-expression@7.29.7 | 6.7 kB |
| postcss-discard-overridden@7.0.3 | 6.6 kB |
| postcss-normalize-timing-functions@7.0.3 | 6.5 kB |
| tinyclip@0.1.12 | 6.5 kB |
| postcss-normalize-display-values@7.0.3 | 6 kB |
| postcss-normalize-whitespace@7.0.3 | 6 kB |
| @babel/helper-skip-transparent-expression-wrappers@7.29.7 | 6 kB |
| vue-component-type-helpers@3.3.3 | 5.6 kB |
| vue-component-type-helpers@3.3.6 | 5.6 kB |
| vue-component-type-helpers@3.3.5 | -5.6 kB |
| postcss-unique-selectors@7.0.7 | 5 kB |
| postcss-normalize-charset@7.0.3 | 4.6 kB |
| postcss-discard-empty@7.0.3 | 4.5 kB |
| @babel/helper-annotate-as-pure@7.29.7 | 4 kB |
| function.prototype.name@1.1.8 → function.prototype.name@1.2.0 | 2.7 kB |
| string.prototype.trim@1.2.10 → string.prototype.trim@1.2.11 | 2.2 kB |
| @unocss/core@66.7.2 → @unocss/core@66.7.4 | 1.8 kB |
| string.prototype.trimend@1.0.9 → string.prototype.trimend@1.0.10 | 1.7 kB |
| core-js-compat@3.48.0 → core-js-compat@3.49.0 | 1.7 kB |
| typed-array-length@1.0.7 → typed-array-length@1.0.8 | 1.6 kB |
| es-abstract@1.24.1 → es-abstract@1.24.2 | 1.5 kB |
| @apideck/better-ajv-errors@0.3.6 → @apideck/better-ajv-errors@0.3.7 | 1.3 kB |
| es-to-primitive@1.3.0 → es-to-primitive@1.3.1 | 1.2 kB |
| safe-array-concat@1.1.3 → safe-array-concat@1.1.4 | 1.2 kB |
| @babel/preset-env@7.29.0 → @babel/preset-env@7.29.7 | 828 B |
| picomatch@2.3.1 → picomatch@2.3.2 | 653 B |
| @babel/plugin-transform-modules-systemjs@7.29.0 → @babel/plugin-transform-modules-systemjs@7.29.7 | 584 B |
| loader-runner@4.3.1 → loader-runner@4.3.2 | 538 B |
| @babel/runtime@7.28.6 → @babel/runtime@7.29.7 | -448 B |
| @nuxt/cli@3.36.0 → @nuxt/cli@3.36.1 | 372 B |
| @humanfs/node@0.16.7 → @humanfs/node@0.16.8 | 332 B |
| flatted@3.3.4 → flatted@3.4.2 | -245 B |
| oxc-parser@0.135.0 → oxc-parser@0.137.0 | 229 B |
| @eslint/config-array@0.21.1 → @eslint/config-array@0.21.2 | 162 B |
| @eslint/eslintrc@3.3.4 → @eslint/eslintrc@3.3.5 | -126 B |
| jsonfile@6.2.0 → jsonfile@6.2.1 | 115 B |
| @bomb.sh/tab@0.0.16 → @bomb.sh/tab@0.0.17 | 82 B |
| @babel/plugin-transform-function-name@7.27.1 → @babel/plugin-transform-function-name@7.29.7 | -81 B |
| @babel/plugin-transform-for-of@7.27.1 → @babel/plugin-transform-for-of@7.29.7 | -79 B |
| @babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5 → @babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7 | -48 B |
| @babel/plugin-transform-destructuring@7.28.5 → @babel/plugin-transform-destructuring@7.29.7 | -46 B |
| @babel/plugin-transform-typeof-symbol@7.27.1 → @babel/plugin-transform-typeof-symbol@7.29.7 | -42 B |
| @babel/plugin-transform-arrow-functions@7.27.1 → @babel/plugin-transform-arrow-functions@7.29.7 | -40 B |
| @babel/helper-create-regexp-features-plugin@7.28.5 → @babel/helper-create-regexp-features-plugin@7.29.7 | -36 B |
| @babel/plugin-transform-unicode-escapes@7.27.1 → @babel/plugin-transform-unicode-escapes@7.29.7 | -28 B |
| @babel/plugin-transform-block-scoped-functions@7.27.1 → @babel/plugin-transform-block-scoped-functions@7.29.7 | -24 B |
| @humanfs/core@0.19.1 → @humanfs/core@0.19.2 | 24 B |
| regjsparser@0.13.0 → regjsparser@0.13.2 | 13 B |
| @babel/helper-remap-async-to-generator@7.27.1 → @babel/helper-remap-async-to-generator@7.29.7 | 0 B |
| @babel/helper-wrap-function@7.28.6 → @babel/helper-wrap-function@7.29.7 | 0 B |
| @babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1 → @babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7 | 0 B |
| @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1 → @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7 | 0 B |
| @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1 → @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7 | 0 B |
| @babel/plugin-transform-optional-chaining@7.28.6 → @babel/plugin-transform-optional-chaining@7.29.7 | 0 B |
| @babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6 → @babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7 | 0 B |
| @babel/plugin-syntax-import-assertions@7.28.6 → @babel/plugin-syntax-import-assertions@7.29.7 | 0 B |
| @babel/plugin-syntax-import-attributes@7.28.6 → @babel/plugin-syntax-import-attributes@7.29.7 | 0 B |
| @babel/plugin-transform-async-generator-functions@7.29.0 → @babel/plugin-transform-async-generator-functions@7.29.7 | 0 B |
| @babel/plugin-transform-async-to-generator@7.28.6 → @babel/plugin-transform-async-to-generator@7.29.7 | 0 B |
| @babel/plugin-transform-block-scoping@7.28.6 → @babel/plugin-transform-block-scoping@7.29.7 | 0 B |
| @babel/plugin-transform-class-properties@7.28.6 → @babel/plugin-transform-class-properties@7.29.7 | 0 B |
| @babel/plugin-transform-class-static-block@7.28.6 → @babel/plugin-transform-class-static-block@7.29.7 | 0 B |
| @babel/plugin-transform-classes@7.28.6 → @babel/plugin-transform-classes@7.29.7 | 0 B |
| @babel/plugin-transform-computed-properties@7.28.6 → @babel/plugin-transform-computed-properties@7.29.7 | 0 B |
| @babel/plugin-transform-dotall-regex@7.28.6 → @babel/plugin-transform-dotall-regex@7.29.7 | 0 B |
| @babel/plugin-transform-duplicate-keys@7.27.1 → @babel/plugin-transform-duplicate-keys@7.29.7 | 0 B |
| @babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0 → @babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7 | 0 B |
| @babel/plugin-transform-dynamic-import@7.27.1 → @babel/plugin-transform-dynamic-import@7.29.7 | 0 B |
| @babel/plugin-transform-explicit-resource-management@7.28.6 → @babel/plugin-transform-explicit-resource-management@7.29.7 | 0 B |
| @babel/plugin-transform-exponentiation-operator@7.28.6 → @babel/plugin-transform-exponentiation-operator@7.29.7 | 0 B |
| @babel/plugin-transform-export-namespace-from@7.27.1 → @babel/plugin-transform-export-namespace-from@7.29.7 | 0 B |
| @babel/plugin-transform-json-strings@7.28.6 → @babel/plugin-transform-json-strings@7.29.7 | 0 B |
| @babel/plugin-transform-literals@7.27.1 → @babel/plugin-transform-literals@7.29.7 | 0 B |
| @babel/plugin-transform-logical-assignment-operators@7.28.6 → @babel/plugin-transform-logical-assignment-operators@7.29.7 | 0 B |
| @babel/plugin-transform-member-expression-literals@7.27.1 → @babel/plugin-transform-member-expression-literals@7.29.7 | 0 B |
| @babel/plugin-transform-modules-amd@7.27.1 → @babel/plugin-transform-modules-amd@7.29.7 | 0 B |
| @babel/plugin-transform-modules-commonjs@7.28.6 → @babel/plugin-transform-modules-commonjs@7.29.7 | 0 B |
| @babel/plugin-transform-modules-umd@7.27.1 → @babel/plugin-transform-modules-umd@7.29.7 | 0 B |
| @babel/plugin-transform-named-capturing-groups-regex@7.29.0 → @babel/plugin-transform-named-capturing-groups-regex@7.29.7 | 0 B |
| @babel/plugin-transform-new-target@7.27.1 → @babel/plugin-transform-new-target@7.29.7 | 0 B |
| @babel/plugin-transform-nullish-coalescing-operator@7.28.6 → @babel/plugin-transform-nullish-coalescing-operator@7.29.7 | 0 B |
| @babel/plugin-transform-numeric-separator@7.28.6 → @babel/plugin-transform-numeric-separator@7.29.7 | 0 B |
| @babel/plugin-transform-object-rest-spread@7.28.6 → @babel/plugin-transform-object-rest-spread@7.29.7 | 0 B |
| @babel/plugin-transform-parameters@7.27.7 → @babel/plugin-transform-parameters@7.29.7 | 0 B |
| @babel/plugin-transform-object-super@7.27.1 → @babel/plugin-transform-object-super@7.29.7 | 0 B |
| @babel/plugin-transform-optional-catch-binding@7.28.6 → @babel/plugin-transform-optional-catch-binding@7.29.7 | 0 B |
| @babel/plugin-transform-private-methods@7.28.6 → @babel/plugin-transform-private-methods@7.29.7 | 0 B |
| @babel/plugin-transform-private-property-in-object@7.28.6 → @babel/plugin-transform-private-property-in-object@7.29.7 | 0 B |
| @babel/plugin-transform-property-literals@7.27.1 → @babel/plugin-transform-property-literals@7.29.7 | 0 B |
| @babel/plugin-transform-regenerator@7.29.0 → @babel/plugin-transform-regenerator@7.29.7 | 0 B |
| @babel/plugin-transform-regexp-modifiers@7.28.6 → @babel/plugin-transform-regexp-modifiers@7.29.7 | 0 B |
| @babel/plugin-transform-reserved-words@7.27.1 → @babel/plugin-transform-reserved-words@7.29.7 | 0 B |
| @babel/plugin-transform-shorthand-properties@7.27.1 → @babel/plugin-transform-shorthand-properties@7.29.7 | 0 B |
| @babel/plugin-transform-spread@7.28.6 → @babel/plugin-transform-spread@7.29.7 | 0 B |
| @babel/plugin-transform-sticky-regex@7.27.1 → @babel/plugin-transform-sticky-regex@7.29.7 | 0 B |
| @babel/plugin-transform-template-literals@7.27.1 → @babel/plugin-transform-template-literals@7.29.7 | 0 B |
| @babel/plugin-transform-unicode-property-regex@7.28.6 → @babel/plugin-transform-unicode-property-regex@7.29.7 | 0 B |
| @babel/plugin-transform-unicode-regex@7.27.1 → @babel/plugin-transform-unicode-regex@7.29.7 | 0 B |
| @babel/plugin-transform-unicode-sets-regex@7.28.6 → @babel/plugin-transform-unicode-sets-regex@7.29.7 | 0 B |
| babel-plugin-polyfill-corejs2@0.4.15 → babel-plugin-polyfill-corejs2@0.4.17 | 0 B |
| babel-plugin-polyfill-corejs3@0.14.0 → babel-plugin-polyfill-corejs3@0.14.2 | 0 B |
| babel-plugin-polyfill-regenerator@0.6.6 → babel-plugin-polyfill-regenerator@0.6.8 | 0 B |
| @unocss/config@66.7.2 → @unocss/config@66.7.4 | 0 B |
| @oxc-project/types@0.135.0 → @oxc-project/types@0.137.0 | 0 B |
| lodash.memoize@4.1.2 | Unknown |
| lodash.uniq@4.5.0 | Unknown |
Total size change: 34.4 MB
⚠️ Package Trust Level Decreased
Caution
Decreased trust levels may indicate a higher risk of supply chain attacks. Please review these changes carefully.
| 📦 Package | 🔒 Before | 🔓 After |
|---|---|---|
| @babel/helper-string-parser | trusted-with-provenance | none |
| chokidar | trusted-with-provenance | none |
| empathic | trusted-with-provenance | provenance |
| readdirp | trusted-with-provenance | provenance |
navigator.canShare() was only checked against a dummy file to test generic files-sharing support, not against the actual title/text/url/files combination. Some implementations support sharing files or url/text but not both together, so attaching the file unconditionally could make navigator.share() reject silently. Now the file is only attached once canShare() confirms the full payload is shareable.
Previously a missing value after --url silently fell through to the preview-server path instead of erroring, which is confusing when the flag was clearly intended to be used.
gameroman
left a comment
There was a problem hiding this comment.
This probably needs a discussion first
(I was chatting about this with @danielroe.) |
…to pwa-and-premium-app-features
…lure The child's 'error' event handler threw directly, which crashes the process instead of surfacing through the promise chain. It's now raced against waitForServer() so a spawn failure or readiness timeout both reject startPreviewServer() and kill the still-running child process before propagating, instead of leaking it.
- Drop the standalone input-switch-polyfill.d.ts ambient module file (knip flagged it as an unused file) in favor of a targeted @ts-expect-error at the one dynamic-import call site. - Regenerate i18n/schema.json to include the new pwa.*, settings.app, package.links.share and package.share_aria_label keys. - Pin vue to 3.5.39 in pnpm-workspace.yaml overrides; the regenerated lockfile had resolved two separate vue versions (3.5.34 and 3.5.39).
Package sub-headers use sticky top-14 to clear the fixed <header> when the viewport is the scroll container. In WCO mode #app-scroll already starts below the header, so that offset left a redundant 3.5rem gap between the title bar and the sticky sub-header instead of gluing it to the header.
@vite-pwa/nuxt's client plugin only registers a beforeinstallprompt listener when pwa.client.installPrompt is truthy; it defaults to false. Without it, showInstallPrompt never flips to true and the Settings "Install app" button never appears, even though the browser fires the event normally.






Summary
@vite-pwa/nuxt) with a manifest id and generated install-UI screenshots.<input type="checkbox" switch>polyfill, kept live-synced with the accent color/theme.theme-colorin sync with--bgso the WCO title-bar strip matches the header.Test plan
pnpm test:unit— 1652/1652 passingpnpm run test:types— passing (fixed 2 pre-existing type errors in the switch polyfill plugin)vp lint— 0 errors (3 pre-existing style warnings, unrelated to correctness)pnpm run build— succeeds