@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 +12 -0
- package/dist/components/DevFeedback.svelte +39 -13
- package/dist/components/DevFeedback.svelte.d.ts +17 -2
- package/dist/components/dev/screenshot.d.ts +33 -3
- package/dist/components/dev/screenshot.js +59 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
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
|
-
/**
|
|
35
|
-
|
|
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
|
-
|
|
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() || !
|
|
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' &&
|
|
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
|
-
/**
|
|
13
|
-
|
|
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
|
-
*
|
|
4
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
4
|
-
*
|
|
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
|
-
|
|
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