@alliance-droid/status-feedback-system 1.3.15 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Breaking Changes
6
+
7
+ - `userEmail` prop on `DevFeedback` is now **required** (was optional). Pass the authenticated user's email; pass an empty string when no user is signed in.
8
+ - Widget renders nothing (no bubble, no context menu) when `userEmail` is empty. The previous lock-screen UI for unauthenticated users is removed.
9
+ - Anonymous feedback submission is no longer possible through the widget.
10
+
11
+ ### Added
12
+
13
+ - Dev-mode console warning when `userEmail` is empty, to surface misconfigurations in consumer apps.
14
+
3
15
  ## 1.3.0
4
16
 
5
17
  - Add composable FeedbackBoard components (FeedbackSidebar, FeedbackList, FeedbackItem, FeedbackDetail, FeedbackSubmitForm)
@@ -11,7 +11,7 @@
11
11
  } from '@alliance-droid/svelte-component-library';
12
12
  import DevContextMenu from './dev/DevContextMenu.svelte';
13
13
  import DevOnboardingToast from './dev/DevOnboardingToast.svelte';
14
- import { captureScreenshot } from './dev/screenshot.js';
14
+ import { captureScreenshot, type ScreenshotProvider } from './dev/screenshot.js';
15
15
  import {
16
16
  detectEnvironment,
17
17
  getEnvironmentName,
@@ -31,8 +31,22 @@
31
31
  showOnboarding?: boolean;
32
32
  /** URL to the feedback board page. If omitted, "View Open Issues" is hidden from the context menu. */
33
33
  boardUrl?: string;
34
- /** Authenticated user email. Required for submitting feedback. */
35
- userEmail?: string;
34
+ /**
35
+ * Authenticated user email. Required.
36
+ *
37
+ * Pass an empty string when the user is not signed in — the widget will
38
+ * render nothing (no floating bubble, no menu, no form). Anonymous
39
+ * feedback is not supported.
40
+ */
41
+ userEmail: string;
42
+ /**
43
+ * Custom screenshot provider. When supplied, this is called instead of
44
+ * the default html2canvas client-side capture. Use this to delegate
45
+ * screenshot capture to a server endpoint (e.g. Puppeteer / screenshot-service).
46
+ *
47
+ * See `createServerScreenshotProvider()` for a ready-made helper.
48
+ */
49
+ screenshotProvider?: ScreenshotProvider;
36
50
  }
37
51
 
38
52
  let {
@@ -43,8 +57,24 @@
43
57
  showOnboarding = true,
44
58
  boardUrl,
45
59
  userEmail,
60
+ screenshotProvider,
46
61
  }: Props = $props();
47
62
 
63
+ // Dev-mode warning when userEmail is empty
64
+ $effect(() => {
65
+ if (!userEmail && typeof console !== 'undefined') {
66
+ // eslint-disable-next-line no-restricted-globals -- safe typeof guard for universal code
67
+ const proc = typeof globalThis !== 'undefined' ? (globalThis as Record<string, unknown>).process as { env?: Record<string, string> } | undefined : undefined;
68
+ const env = proc?.env?.NODE_ENV || 'development';
69
+ if (env !== 'production') {
70
+ console.warn(
71
+ '[status-feedback-system] <DevFeedback> was rendered with an empty userEmail. ' +
72
+ "The widget will not appear. Pass the authenticated user's email, or do not render <DevFeedback> when no user is signed in.",
73
+ );
74
+ }
75
+ }
76
+ });
77
+
48
78
  // --- Client (lazy, stable) ---
49
79
  let client: StatusClient | null = $state(null);
50
80
  let lastApiUrl = '';
@@ -61,6 +91,7 @@
61
91
 
62
92
  // --- Environment gate ---
63
93
  let isEnabled = $state(false);
94
+ let hasUserEmail = $derived(!!userEmail);
64
95
 
65
96
  // --- State ---
66
97
  let menuOpen = $state(false);
@@ -116,7 +147,7 @@
116
147
  let formTitle = $state('');
117
148
  let formMessage = $state('');
118
149
 
119
- let isAuthenticated = $derived(!!userEmail);
150
+
120
151
  let submitting = $state(false);
121
152
  let submitResult = $state<'success' | 'error' | null>(null);
122
153
 
@@ -162,7 +193,7 @@
162
193
  const ONBOARDING_KEY = 'sf-devfeedback-onboarded';
163
194
 
164
195
  onMount(() => {
165
- isEnabled = detectEnvironment(enabled);
196
+ isEnabled = detectEnvironment(enabled) && !!userEmail;
166
197
  if (!isEnabled) return;
167
198
 
168
199
  // Hook console.error
@@ -225,7 +256,7 @@
225
256
  async function doScreenshot() {
226
257
  capturingScreenshot_ = true;
227
258
  screenshotFailed = false;
228
- screenshotData = await captureScreenshot();
259
+ screenshotData = await captureScreenshot(screenshotProvider);
229
260
  if (!screenshotData) screenshotFailed = true;
230
261
  capturingScreenshot_ = false;
231
262
  }
@@ -249,7 +280,7 @@
249
280
  }
250
281
 
251
282
  async function handleSubmit() {
252
- if (!formMessage.trim() || !isAuthenticated) return;
283
+ if (!formMessage.trim() || !hasUserEmail) return;
253
284
  submitting = true;
254
285
  submitResult = null;
255
286
 
@@ -372,11 +403,6 @@
372
403
  {formType === 'bug' ? "We'll look into this." : "Thanks for the suggestion!"}
373
404
  </p>
374
405
  </div>
375
- {:else if !isAuthenticated}
376
- <div class="text-center py-6 text-foreground-tertiary">
377
- <i class="fa-solid fa-lock text-2xl" aria-hidden="true"></i>
378
- <p class="mt-2 text-sm">Sign in to submit feedback</p>
379
- </div>
380
406
  {:else}
381
407
  <div class="flex items-center gap-1.5 text-xs text-foreground-tertiary">
382
408
  <i class="fa-solid fa-user" aria-hidden="true"></i>
@@ -473,7 +499,7 @@
473
499
  </div>
474
500
 
475
501
  <!-- Footer -->
476
- {#if submitResult !== 'success' && isAuthenticated}
502
+ {#if submitResult !== 'success' && hasUserEmail}
477
503
  <div class="flex justify-end gap-2 px-5 py-4 border-t border-border shrink-0">
478
504
  <Button variant="neutral" style="outline" onclick={() => (formOpen = false)}>
479
505
  Cancel
@@ -1,3 +1,4 @@
1
+ import { type ScreenshotProvider } from './dev/screenshot.js';
1
2
  interface Props {
2
3
  apiUrl: string;
3
4
  apiKey: string;
@@ -9,8 +10,22 @@ interface Props {
9
10
  showOnboarding?: boolean;
10
11
  /** URL to the feedback board page. If omitted, "View Open Issues" is hidden from the context menu. */
11
12
  boardUrl?: string;
12
- /** Authenticated user email. Required for submitting feedback. */
13
- userEmail?: string;
13
+ /**
14
+ * Authenticated user email. Required.
15
+ *
16
+ * Pass an empty string when the user is not signed in — the widget will
17
+ * render nothing (no floating bubble, no menu, no form). Anonymous
18
+ * feedback is not supported.
19
+ */
20
+ userEmail: string;
21
+ /**
22
+ * Custom screenshot provider. When supplied, this is called instead of
23
+ * the default html2canvas client-side capture. Use this to delegate
24
+ * screenshot capture to a server endpoint (e.g. Puppeteer / screenshot-service).
25
+ *
26
+ * See `createServerScreenshotProvider()` for a ready-made helper.
27
+ */
28
+ screenshotProvider?: ScreenshotProvider;
14
29
  }
15
30
  declare const DevFeedback: import("svelte").Component<Props, {}, "">;
16
31
  type DevFeedback = ReturnType<typeof DevFeedback>;
@@ -1,6 +1,36 @@
1
1
  /**
2
2
  * Screenshot capture utility for DevFeedback.
3
- * Dynamically imports html2canvas — gracefully degrades if not installed
4
- * (html2canvas is an optional peer dependency).
3
+ *
4
+ * Supports two modes:
5
+ * 1. Custom provider — consumer supplies a `ScreenshotProvider` callback
6
+ * (e.g. server-side capture via Puppeteer / screenshot-service).
7
+ * 2. html2canvas fallback — dynamically imports html2canvas if no provider
8
+ * is given (html2canvas is an optional peer dependency).
5
9
  */
6
- export declare function captureScreenshot(): Promise<string | null>;
10
+ /**
11
+ * A function that captures a screenshot and returns a data URL (or null on failure).
12
+ * Consumers can implement this however they like — server endpoint, different
13
+ * client library, canvas API, etc.
14
+ */
15
+ export type ScreenshotProvider = () => Promise<string | null>;
16
+ /**
17
+ * Capture a screenshot using the given provider, or fall back to html2canvas.
18
+ */
19
+ export declare function captureScreenshot(provider?: ScreenshotProvider): Promise<string | null>;
20
+ /**
21
+ * Create a screenshot provider that POSTs to a server endpoint.
22
+ *
23
+ * The endpoint receives `{ url, viewport }` and should return
24
+ * `{ screenshot: "data:image/..." }` (or any JSON with a `screenshot` string field).
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * <DevFeedback
29
+ * screenshotProvider={createServerScreenshotProvider('/api/screenshot')}
30
+ * ...
31
+ * />
32
+ * ```
33
+ */
34
+ export declare function createServerScreenshotProvider(endpoint: string, options?: {
35
+ headers?: Record<string, string>;
36
+ }): ScreenshotProvider;
@@ -1,9 +1,65 @@
1
1
  /**
2
2
  * Screenshot capture utility for DevFeedback.
3
- * Dynamically imports html2canvas — gracefully degrades if not installed
4
- * (html2canvas is an optional peer dependency).
3
+ *
4
+ * Supports two modes:
5
+ * 1. Custom provider — consumer supplies a `ScreenshotProvider` callback
6
+ * (e.g. server-side capture via Puppeteer / screenshot-service).
7
+ * 2. html2canvas fallback — dynamically imports html2canvas if no provider
8
+ * is given (html2canvas is an optional peer dependency).
5
9
  */
6
- export async function captureScreenshot() {
10
+ /**
11
+ * Capture a screenshot using the given provider, or fall back to html2canvas.
12
+ */
13
+ export async function captureScreenshot(provider) {
14
+ if (provider) {
15
+ try {
16
+ return await provider();
17
+ }
18
+ catch (err) {
19
+ console.error('[DevFeedback] Screenshot provider failed:', err);
20
+ return null;
21
+ }
22
+ }
23
+ return captureWithHtml2Canvas();
24
+ }
25
+ /**
26
+ * Create a screenshot provider that POSTs to a server endpoint.
27
+ *
28
+ * The endpoint receives `{ url, viewport }` and should return
29
+ * `{ screenshot: "data:image/..." }` (or any JSON with a `screenshot` string field).
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * <DevFeedback
34
+ * screenshotProvider={createServerScreenshotProvider('/api/screenshot')}
35
+ * ...
36
+ * />
37
+ * ```
38
+ */
39
+ export function createServerScreenshotProvider(endpoint, options) {
40
+ return async () => {
41
+ const res = await fetch(endpoint, {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Content-Type': 'application/json',
45
+ ...options?.headers,
46
+ },
47
+ body: JSON.stringify({
48
+ url: window.location.href,
49
+ viewport: {
50
+ width: window.innerWidth,
51
+ height: window.innerHeight,
52
+ },
53
+ }),
54
+ });
55
+ if (!res.ok)
56
+ return null;
57
+ const data = await res.json();
58
+ return data.screenshot ?? null;
59
+ };
60
+ }
61
+ /** html2canvas fallback — gracefully degrades if not installed. */
62
+ async function captureWithHtml2Canvas() {
7
63
  try {
8
64
  const html2canvas = (await import('html2canvas')).default;
9
65
  const canvas = await html2canvas(document.body, {
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@
9
9
  */
10
10
  export type { Project, System, Incident, IncidentUpdate, Feedback, FeedbackResponse, FeedbackVisibility, StatusHistoryEntry, SystemStatus, IncidentStatus, IncidentSeverity, FeedbackStatus, ApiResponse, } from './types.js';
11
11
  export { deriveOverallStatus } from './utils.js';
12
+ export { createServerScreenshotProvider } from './components/dev/screenshot.js';
13
+ export type { ScreenshotProvider } from './components/dev/screenshot.js';
12
14
  export { createStatusClient } from './api.js';
13
15
  export type { StatusClient, StatusClientConfig, StatusData, IncidentData, UptimeData, BoardData, BoardItem, BoardCategory } from './api.js';
14
16
  export { default as StatusPage } from './components/StatusPage.svelte';
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
  // ── Utilities ──
11
11
  export { deriveOverallStatus } from './utils.js';
12
+ export { createServerScreenshotProvider } from './components/dev/screenshot.js';
12
13
  // ── API Client ──
13
14
  export { createStatusClient } from './api.js';
14
15
  // ── Client Components ──
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alliance-droid/status-feedback-system",
3
- "version": "1.3.15",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Drop-in system status pages and user feedback forms for SvelteKit apps",
6
6
  "svelte": "./dist/index.js",