ai-app-feedback 0.1.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.
@@ -0,0 +1,11 @@
1
+ import type { FeedbackCategory, FeedbackSeverity, JsonValue } from "../core/types";
2
+ export interface FeedbackWidgetProps {
3
+ triggerLabel?: string;
4
+ title?: string;
5
+ description?: string;
6
+ defaultCategory?: FeedbackCategory;
7
+ defaultSeverity?: FeedbackSeverity;
8
+ metadata?: Record<string, JsonValue>;
9
+ tags?: string[];
10
+ }
11
+ export declare function FeedbackWidget({ triggerLabel, title, description, defaultCategory, defaultSeverity, metadata, tags, }: FeedbackWidgetProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { type ReactNode } from "react";
2
+ import { type FeedbackSession } from "../core/session";
3
+ import type { FeedbackDraft, FeedbackReport, FeedbackSessionOptions, NavigationEvent } from "../core/types";
4
+ export interface FeedbackProviderProps extends FeedbackSessionOptions {
5
+ children: ReactNode;
6
+ defaultOpen?: boolean;
7
+ }
8
+ export interface FeedbackContextValue {
9
+ session: FeedbackSession;
10
+ isOpen: boolean;
11
+ open: () => void;
12
+ close: () => void;
13
+ submit: (draft: FeedbackDraft) => Promise<FeedbackReport>;
14
+ recordNavigation: (event: Omit<NavigationEvent, "type" | "timestamp">) => void;
15
+ }
16
+ export declare function FeedbackProvider({ children, defaultOpen, ...sessionOptions }: FeedbackProviderProps): import("react").JSX.Element;
17
+ export declare function useFeedback(): FeedbackContextValue;
package/dist/style.css ADDED
@@ -0,0 +1,276 @@
1
+ /* src/style.css */
2
+ :root {
3
+ --aaf-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
4
+ --aaf-bg: oklch(.99 .005 250);
5
+ --aaf-surface: oklch(1 0 0);
6
+ --aaf-surface-muted: oklch(.965 .012 250);
7
+ --aaf-border: oklch(.86 .018 250);
8
+ --aaf-text: oklch(.22 .025 250);
9
+ --aaf-muted: oklch(.43 .03 250);
10
+ --aaf-primary: oklch(.52 .16 255);
11
+ --aaf-primary-hover: oklch(.46 .17 255);
12
+ --aaf-danger: oklch(.48 .18 25);
13
+ --aaf-success: oklch(.43 .12 155);
14
+ --aaf-focus: oklch(.68 .18 255);
15
+ --aaf-z-feedback: 70;
16
+ }
17
+
18
+ .aaf-root {
19
+ color: var(--aaf-text);
20
+ font-family: var(--aaf-font);
21
+ position: fixed;
22
+ right: max(16px, env(safe-area-inset-right));
23
+ bottom: max(16px, env(safe-area-inset-bottom));
24
+ z-index: var(--aaf-z-feedback);
25
+ font-size: 14px;
26
+ line-height: 1.4;
27
+ }
28
+
29
+ .aaf-root *, .aaf-root *:before, .aaf-root *:after {
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ .aaf-trigger, .aaf-primary, .aaf-secondary, .aaf-iconButton {
34
+ cursor: pointer;
35
+ display: inline-flex;
36
+ font: inherit;
37
+ border-radius: 8px;
38
+ justify-content: center;
39
+ align-items: center;
40
+ min-height: 36px;
41
+ transition: background-color .18s, border-color .18s, color .18s;
42
+ font-weight: 650;
43
+ }
44
+
45
+ .aaf-trigger:focus-visible {
46
+ outline: 2px solid var(--aaf-focus);
47
+ outline-offset: 2px;
48
+ }
49
+
50
+ .aaf-primary:focus-visible {
51
+ outline: 2px solid var(--aaf-focus);
52
+ outline-offset: 2px;
53
+ }
54
+
55
+ .aaf-secondary:focus-visible {
56
+ outline: 2px solid var(--aaf-focus);
57
+ outline-offset: 2px;
58
+ }
59
+
60
+ .aaf-iconButton:focus-visible {
61
+ outline: 2px solid var(--aaf-focus);
62
+ outline-offset: 2px;
63
+ }
64
+
65
+ .aaf-field :where(input, select, textarea):focus-visible {
66
+ outline: 2px solid var(--aaf-focus);
67
+ outline-offset: 2px;
68
+ }
69
+
70
+ .aaf-check input:focus-visible {
71
+ outline: 2px solid var(--aaf-focus);
72
+ outline-offset: 2px;
73
+ }
74
+
75
+ .aaf-trigger {
76
+ background: var(--aaf-primary);
77
+ border: 1px solid var(--aaf-primary);
78
+ color: #fff;
79
+ padding: 0 14px;
80
+ }
81
+
82
+ .aaf-trigger:hover, .aaf-primary:hover:not(:disabled) {
83
+ background: var(--aaf-primary-hover);
84
+ border-color: var(--aaf-primary-hover);
85
+ }
86
+
87
+ .aaf-panel {
88
+ background: var(--aaf-surface);
89
+ border: 1px solid var(--aaf-border);
90
+ overflow: auto;
91
+ position: absolute;
92
+ border-radius: 8px;
93
+ width: min(392px, 100vw - 32px);
94
+ max-height: min(720px, 100vh - 80px);
95
+ padding: 16px;
96
+ bottom: 48px;
97
+ right: 0;
98
+ }
99
+
100
+ .aaf-panelHeader {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: flex-start;
104
+ gap: 12px;
105
+ }
106
+
107
+ .aaf-panelHeader h2 {
108
+ color: var(--aaf-text);
109
+ text-wrap: balance;
110
+ margin: 0;
111
+ font-size: 16px;
112
+ line-height: 1.25;
113
+ }
114
+
115
+ .aaf-panelHeader p {
116
+ color: var(--aaf-muted);
117
+ max-width: 52ch;
118
+ margin: 4px 0 0;
119
+ font-size: 13px;
120
+ }
121
+
122
+ .aaf-iconButton {
123
+ color: var(--aaf-muted);
124
+ background: none;
125
+ border: 1px solid #0000;
126
+ flex: none;
127
+ width: 32px;
128
+ height: 32px;
129
+ padding: 0;
130
+ }
131
+
132
+ .aaf-iconButton:hover {
133
+ background: var(--aaf-surface-muted);
134
+ color: var(--aaf-text);
135
+ }
136
+
137
+ .aaf-form {
138
+ display: flex;
139
+ flex-direction: column;
140
+ gap: 12px;
141
+ margin-top: 16px;
142
+ }
143
+
144
+ .aaf-grid {
145
+ display: grid;
146
+ grid-template-columns: repeat(2, minmax(0, 1fr));
147
+ gap: 10px;
148
+ }
149
+
150
+ .aaf-field {
151
+ color: var(--aaf-text);
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 6px;
155
+ font-size: 13px;
156
+ font-weight: 650;
157
+ }
158
+
159
+ .aaf-field :where(input, select, textarea) {
160
+ background: var(--aaf-bg);
161
+ border: 1px solid var(--aaf-border);
162
+ color: var(--aaf-text);
163
+ font: inherit;
164
+ border-radius: 8px;
165
+ width: 100%;
166
+ min-height: 38px;
167
+ padding: 9px 10px;
168
+ font-weight: 450;
169
+ }
170
+
171
+ .aaf-field textarea {
172
+ resize: vertical;
173
+ min-height: 86px;
174
+ }
175
+
176
+ .aaf-field ::placeholder {
177
+ color: oklch(.49 .025 250);
178
+ opacity: 1;
179
+ }
180
+
181
+ .aaf-check {
182
+ color: var(--aaf-text);
183
+ display: flex;
184
+ align-items: flex-start;
185
+ gap: 8px;
186
+ font-size: 13px;
187
+ }
188
+
189
+ .aaf-check input {
190
+ accent-color: var(--aaf-primary);
191
+ width: 16px;
192
+ height: 16px;
193
+ margin: 2px 0 0;
194
+ }
195
+
196
+ .aaf-context {
197
+ background: var(--aaf-surface-muted);
198
+ border: 1px solid var(--aaf-border);
199
+ color: var(--aaf-muted);
200
+ display: flex;
201
+ border-radius: 8px;
202
+ flex-direction: column;
203
+ gap: 2px;
204
+ padding: 9px 10px;
205
+ font-size: 12px;
206
+ }
207
+
208
+ .aaf-error, .aaf-success {
209
+ margin: 0;
210
+ font-size: 13px;
211
+ }
212
+
213
+ .aaf-error {
214
+ color: var(--aaf-danger);
215
+ }
216
+
217
+ .aaf-success {
218
+ color: var(--aaf-success);
219
+ }
220
+
221
+ .aaf-actions {
222
+ display: flex;
223
+ justify-content: flex-end;
224
+ gap: 8px;
225
+ }
226
+
227
+ .aaf-primary, .aaf-secondary {
228
+ padding: 0 12px;
229
+ }
230
+
231
+ .aaf-primary {
232
+ background: var(--aaf-primary);
233
+ border: 1px solid var(--aaf-primary);
234
+ color: #fff;
235
+ }
236
+
237
+ .aaf-primary:disabled {
238
+ cursor: not-allowed;
239
+ opacity: .55;
240
+ }
241
+
242
+ .aaf-secondary {
243
+ background: var(--aaf-surface);
244
+ border: 1px solid var(--aaf-border);
245
+ color: var(--aaf-text);
246
+ }
247
+
248
+ .aaf-secondary:hover {
249
+ background: var(--aaf-surface-muted);
250
+ }
251
+
252
+ @media (max-width: 460px) {
253
+ .aaf-root {
254
+ left: max(12px, env(safe-area-inset-left));
255
+ right: max(12px, env(safe-area-inset-right));
256
+ }
257
+
258
+ .aaf-trigger {
259
+ width: 100%;
260
+ }
261
+
262
+ .aaf-panel {
263
+ width: 100%;
264
+ bottom: 48px;
265
+ }
266
+
267
+ .aaf-grid {
268
+ grid-template-columns: 1fr;
269
+ }
270
+ }
271
+
272
+ @media (prefers-reduced-motion: reduce) {
273
+ .aaf-trigger, .aaf-primary, .aaf-secondary, .aaf-iconButton {
274
+ transition: none;
275
+ }
276
+ }
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "ai-app-feedback",
3
+ "version": "0.1.0",
4
+ "description": "A Next.js-ready feedback widget that captures user reports, navigation context, screenshots, and AI-agent triage prompts.",
5
+ "license": "MIT",
6
+ "author": "dazuaz",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./next": {
17
+ "types": "./dist/next.d.ts",
18
+ "import": "./dist/next.js"
19
+ },
20
+ "./core": {
21
+ "types": "./dist/core-entry.d.ts",
22
+ "import": "./dist/core-entry.js"
23
+ },
24
+ "./style.css": "./dist/style.css"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "PRODUCT.md"
30
+ ],
31
+ "sideEffects": [
32
+ "./dist/style.css"
33
+ ],
34
+ "scripts": {
35
+ "build": "bun build ./src/index.ts ./src/next.tsx ./src/core-entry.ts --outdir ./dist --target browser --format esm --external react --external react-dom --external next/navigation && bun build ./src/style.css --outdir ./dist && tsc -p tsconfig.build.json",
36
+ "test": "bun test",
37
+ "typecheck": "tsc --noEmit",
38
+ "prepublishOnly": "bun run typecheck && bun test && bun run build"
39
+ },
40
+ "devDependencies": {
41
+ "@happy-dom/global-registrator": "^20.10.2",
42
+ "@testing-library/react": "^16.3.2",
43
+ "@types/bun": "latest",
44
+ "@types/react": "^19.2.7",
45
+ "@types/react-dom": "^19.2.3",
46
+ "react": "^19.2.0",
47
+ "react-dom": "^19.2.0"
48
+ },
49
+ "peerDependencies": {
50
+ "next": ">=14.2.0 <17",
51
+ "react": ">=18.2.0 <20",
52
+ "react-dom": ">=18.2.0 <20",
53
+ "typescript": "^5"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "next": {
57
+ "optional": true
58
+ }
59
+ }
60
+ }