fix: self-contained ui:false mode without @nuxt/ui (#5)#6
Open
NicoKaempf wants to merge 2 commits into
Open
Conversation
With `bug: { ui: false }` the module still imported `#ui` unconditionally
(`useOverlay`/`useToast` in useBugReport.ts), so the consumer build failed
with "Rollup failed to resolve import #ui/composables/useOverlay" whenever
@nuxt/ui was not installed. This made `ui: false` unusable in Tailwind v3 /
non-@nuxt/ui projects.
Ship a fully self-contained runtime variant for `ui: false` that has no
`#ui` / `@nuxt/ui` import at all:
- src/module.ts branches on `options.ui` and registers either the
@nuxt/ui-based or the standalone components/composable/plugin under the
same public names; @nuxt/ui is only installed when `ui: true`.
- New standalone pieces: useBugReport.plain.ts (own modal mount + toast),
plainToast.ts (dependency-free DOM toast), bug-lt.plain.ts plugin, and
plain/* components (button, modal, form, attachments, timeline) with
scoped styles + inline SVG/PNG icons, so they work in any styling stack.
- ui: true path is unchanged (zero behaviour change).
Also fix a pre-existing SSR crash surfaced by the standalone button:
networkRequests.ts read `window.fetch`/`XMLHttpRequest` at module top
level, crashing during SSR of any page using `useBugReport()` /
`<BugReportButton>`. Access is now guarded; monitoring still only wires
up client-side.
Verified locally (SSR + browser): button renders/opens modal, screenshot
capture, and the POST payload includes screenshot, extra attachments,
browserInfo, consoleLogs, networkRequests and userInteractions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR makes the module truly usable with bug: { ui: false } by shipping a separate, fully self-contained runtime (no #ui / @nuxt/ui imports) and selecting the runtime variant at module registration time. It also fixes an SSR crash caused by top-level access to window.fetch / XMLHttpRequest in network monitoring.
Changes:
- Add a standalone
ui: falseruntime variant (plugin, composable, plain components, dependency-free toast). - Switch module registration to conditionally register either the Nuxt UI or standalone runtime under the same public component/composable names.
- Make network request monitoring SSR-safe; update tests and README accordingly.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/module.test.ts | Adds coverage for ui: false registration and makes ui: true explicit in tests that bypass default option merging. |
| src/runtime/utils/plainToast.ts | Introduces a dependency-free DOM toast for standalone mode. |
| src/runtime/utils/networkRequests.ts | Guards SSR evaluation and adjusts fetch/XHR interception to avoid SSR crashes. |
| src/runtime/plugins/bug-lt.plain.ts | New client-only plugin mounting the standalone floating button and wiring monitoring/error tracking without #ui. |
| src/runtime/composables/useBugReport.plain.ts | Standalone useBugReport that mounts its own modal + toast and avoids #ui. |
| src/runtime/components/plain/UserJourneyTimelinePlain.vue | Standalone timeline UI for standalone mode. |
| src/runtime/components/plain/BugReportModalPlain.vue | Standalone modal shell for standalone mode. |
| src/runtime/components/plain/BugReportFormPlain.vue | Standalone form implementation mirroring existing report payload logic. |
| src/runtime/components/plain/BugReportButtonPlain.vue | Standalone floating action button (no Nuxt UI / Teleport dependency). |
| src/runtime/components/plain/AttachmentsListPlain.vue | Standalone attachments upload/list UI. |
| src/module.ts | Conditional registration of UI vs standalone runtime files based on options.ui. |
| README.md | Documents the two UI modes and clarifies ui: false behavior/limitations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
105
to
109
| const options = first instanceof Request ? first : args[1] | ||
|
|
||
| if (!shouldCaptureUrl(url)) { | ||
| return originalFetch(...args) | ||
| return originalFetch!(...args) | ||
| } |
Comment on lines
286
to
290
| if (typeof window !== 'undefined') { | ||
| window.fetch = originalFetch | ||
| XMLHttpRequest.prototype.open = originalXHROpen | ||
| XMLHttpRequest.prototype.send = originalXHRSend | ||
| window.fetch = originalFetch! | ||
| XMLHttpRequest.prototype.open = originalXHROpen! | ||
| XMLHttpRequest.prototype.send = originalXHRSend! | ||
| } |
Comment on lines
+50
to
+54
| const dataUrl = await new Promise<string>((resolve) => { | ||
| const reader = new FileReader() | ||
| reader.onload = e => resolve(e.target?.result as string) | ||
| reader.readAsDataURL(file) | ||
| }) |
- networkRequests.ts: initializeNetworkMonitoring bails out (without marking initialized) when fetch/XHR primitives are unavailable, instead of throwing on interception; drop the now-redundant non-null assertions. - networkRequests.ts: resetNetworkMonitoring restores fetch/XHR only when the originals were actually captured, never assigning undefined or touching XMLHttpRequest when it's missing. - AttachmentsListPlain.vue: handle FileReader onerror/onabort so a failed read rejects (and the file is skipped) instead of hanging processFiles and blocking further uploads. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment on lines
+98
to
+136
| it('should register self-contained components when ui is false', async () => { | ||
| const mockNuxt = { | ||
| options: { | ||
| runtimeConfig: { | ||
| public: { bugLt: {} }, | ||
| bugLt: {}, | ||
| }, | ||
| modules: [], | ||
| }, | ||
| } | ||
|
|
||
| // ui: false must register the standalone (no @nuxt/ui) variants and the | ||
| // standalone plugin/composable, and must NOT install @nuxt/ui (issue #5). | ||
| const mockOptions: ModuleOptions = { ui: false } | ||
|
|
||
| await module.setup(mockOptions, mockNuxt) | ||
|
|
||
| expect(installModule).not.toHaveBeenCalled() | ||
|
|
||
| expect(addComponent).toHaveBeenCalledWith({ | ||
| name: 'BugReportButton', | ||
| filePath: './runtime/components/plain/BugReportButtonPlain.vue', | ||
| }) | ||
|
|
||
| expect(addComponent).toHaveBeenCalledWith({ | ||
| name: 'BugReportModal', | ||
| filePath: './runtime/components/plain/BugReportModalPlain.vue', | ||
| }) | ||
|
|
||
| expect(addImports).toHaveBeenCalledWith({ | ||
| name: 'useBugReport', | ||
| from: './runtime/composables/useBugReport.plain', | ||
| }) | ||
|
|
||
| expect(addPlugin).toHaveBeenCalledWith({ | ||
| src: './runtime/plugins/bug-lt.plain', | ||
| mode: 'client', | ||
| }) | ||
| }) |
Comment on lines
+6
to
+8
| // @ts-expect-error - PNG asset import is resolved by the consumer's Vite build, | ||
| // not by vue-tsc (no global asset type shim in this package). | ||
| import iconPng from '../../public/icon.png' |
| return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i] | ||
| } | ||
|
|
||
| const generateId = () => Math.random().toString(36).substr(2, 9) |
| |-------------------------|-----------|---------------------|-------------------------------------------------------| | ||
| | `enabled` | `boolean` | `true` | Komplettes Modul aktivieren | | ||
| | `ui` | `boolean` | `true` | @nuxt/ui Installation aktivieren | | ||
| | `ui` | `boolean` | `true` | `true`: nutzt @nuxt/ui · `false`: eigenständiger Modus ohne @nuxt/ui (siehe [UI-Modi](#ui-modi-nuxtui-vs-eigenständig)) | |
Comment on lines
+156
to
+160
| <label class="buglt-label"> | ||
| Titel <span class="buglt-required">*</span> | ||
| </label> | ||
| <input | ||
| v-model="state.title" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5
Problem
With
bug: { ui: false }the module still imported#uiunconditionally (useOverlay/useToastinuseBugReport.ts). When@nuxt/uiis not installed the#uialias does not exist, so the consumer build failed:ui: falsewas therefore unusable in Tailwind v3 / non-@nuxt/uiprojects, even though the option suggests an@nuxt/ui-free path exists.Solution
Ship a fully self-contained runtime variant for
ui: falsewith no#ui/@nuxt/uiimport at all. The module registers either variant under the same public names depending onoptions.ui:ui: true(default): unchanged —@nuxt/uicomponents, zero behaviour change.ui: false: standalone button, modal, form, attachments, timeline + own toast. Scoped CSS + inline SVG/PNG icons, so it works in any styling stack (Tailwind v3, no Tailwind, …).@nuxt/uiis no longer installed.New files:
src/runtime/composables/useBugReport.plain.ts— own modal mount + toastsrc/runtime/utils/plainToast.ts— dependency-free DOM toastsrc/runtime/plugins/bug-lt.plain.tssrc/runtime/components/plain/*— Button, Modal, Form, AttachmentsList, UserJourneyTimelineBonus: SSR crash fix
Surfaced while testing:
networkRequests.tsreadwindow.fetch/XMLHttpRequestat module top level, crashing during SSR of any page usinguseBugReport()/<BugReportButton>(Cannot read properties of undefined (reading 'fetch')). This affected both modes (verified on theui: trueplayground too). Access is now guarded; monitoring still wires up client-side only.Verification (local, SSR + browser, no
@nuxt/ui/Tailwind)#uiresolution errorscreenshot,attachments(screenshot + extra attachment),browserInfo,consoleLogs,networkRequests,userInteractions— identical to theui: trueform logicDocs
README gets a new "UI-Modi: @nuxt/ui vs. eigenständig" section and updated
uioption docs.🤖 Generated with Claude Code