Skip to content

fix: self-contained ui:false mode without @nuxt/ui (#5)#6

Open
NicoKaempf wants to merge 2 commits into
mainfrom
fix/ui-false-standalone-issue-5
Open

fix: self-contained ui:false mode without @nuxt/ui (#5)#6
NicoKaempf wants to merge 2 commits into
mainfrom
fix/ui-false-standalone-issue-5

Conversation

@NicoKaempf

Copy link
Copy Markdown
Contributor

Closes #5

Problem

With bug: { ui: false } the module still imported #ui unconditionally (useOverlay/useToast in useBugReport.ts). When @nuxt/ui is not installed the #ui alias does not exist, so the consumer build failed:

[vite]: Rollup failed to resolve import "#ui/composables/useOverlay"

ui: false was therefore unusable in Tailwind v3 / non-@nuxt/ui projects, even though the option suggests an @nuxt/ui-free path exists.

Solution

Ship a fully self-contained runtime variant for ui: false with no #ui / @nuxt/ui import at all. The module registers either variant under the same public names depending on options.ui:

  • ui: true (default): unchanged — @nuxt/ui components, 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/ui is no longer installed.

New files:

  • src/runtime/composables/useBugReport.plain.ts — own modal mount + toast
  • src/runtime/utils/plainToast.ts — dependency-free DOM toast
  • src/runtime/plugins/bug-lt.plain.ts
  • src/runtime/components/plain/* — Button, Modal, Form, AttachmentsList, UserJourneyTimeline

Bonus: SSR crash fix

Surfaced while testing: networkRequests.ts read window.fetch/XMLHttpRequest at module top level, crashing during SSR of any page using useBugReport() / <BugReportButton> (Cannot read properties of undefined (reading 'fetch')). This affected both modes (verified on the ui: true playground too). Access is now guarded; monitoring still wires up client-side only.

Verification (local, SSR + browser, no @nuxt/ui/Tailwind)

  • ✅ No-UI consumer build succeeds — no #ui resolution error
  • ✅ SSR returns 200; floating button renders (perfect circle, bug icon)
  • ✅ Modal opens, screenshot auto-captured, validation works
  • ✅ Submit surfaces the server response as a plain in-modal error alert
  • ✅ POST payload (~211 KB) includes everything: screenshot, attachments (screenshot + extra attachment), browserInfo, consoleLogs, networkRequests, userInteractions — identical to the ui: true form logic
  • ✅ 57/57 tests pass, lint clean, module build clean

Docs

README gets a new "UI-Modi: @nuxt/ui vs. eigenständig" section and updated ui option docs.

🤖 Generated with Claude Code

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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: false runtime 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 thread src/runtime/utils/networkRequests.ts Outdated
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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Comment thread test/module.test.ts
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)
Comment thread README.md
|-------------------------|-----------|---------------------|-------------------------------------------------------|
| `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"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ui: false still requires @nuxt/ui (#ui/useOverlay) → forces Tailwind v4, unusable in Tailwind v3 projects

2 participants