@stephenov/feedbackwidget 0.3.5 → 0.5.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/dist/auto.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  "use strict";
2
3
  "use client";
3
4
  var __create = Object.create;
package/dist/auto.mjs CHANGED
@@ -1,7 +1,8 @@
1
+ 'use client';
1
2
  "use client";
2
3
  import {
3
4
  FeedbackWidget
4
- } from "./chunk-QQLLK6MC.mjs";
5
+ } from "./chunk-WCFS4MGE.mjs";
5
6
 
6
7
  // src/auto.tsx
7
8
  import { useEffect, useState } from "react";
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  // src/utils.ts
2
4
  function cn(...classes) {
3
5
  return classes.filter(Boolean).join(" ");
package/dist/cli.js CHANGED
@@ -88,38 +88,48 @@ async function initProject() {
88
88
  reject(new Error("Authentication timed out"));
89
89
  }, 5 * 60 * 1e3);
90
90
  });
91
- const snippet = `<FeedbackWidget apiKey="${apiKey}" />`;
92
- let copied = false;
93
- try {
94
- await copyToClipboard(snippet);
95
- copied = true;
96
- } catch {
97
- }
98
91
  console.log("\u2705 Authenticated!\n");
99
- console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
100
- console.log(" Your API Key:\n");
101
- console.log(` ${apiKey}
92
+ const injected = injectWidget(apiKey);
93
+ if (injected) {
94
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
95
+ console.log(" \u2705 Widget automatically added to your app!\n");
96
+ console.log(` Modified: ${injected.file}
102
97
  `);
103
- console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
104
- if (copied) {
105
- console.log(" \u{1F4CB} Copied to clipboard!\n");
106
- }
107
- const framework = detectFramework();
108
- console.log(" Add to your app:\n");
109
- console.log(' import { FeedbackWidget } from "@stephenov/feedbackwidget"\n');
110
- console.log(` ${snippet}
98
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
99
+ console.log(" Just run your dev server and the widget will appear.\n");
100
+ } else {
101
+ const snippet = `<FeedbackWidget apiKey="${apiKey}" />`;
102
+ let copied = false;
103
+ try {
104
+ await copyToClipboard(snippet);
105
+ copied = true;
106
+ } catch {
107
+ }
108
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
109
+ console.log(" Your API Key:\n");
110
+ console.log(` ${apiKey}
111
+ `);
112
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
113
+ if (copied) {
114
+ console.log(" \u{1F4CB} Copied to clipboard!\n");
115
+ }
116
+ const framework = detectFramework();
117
+ console.log(" Add to your app:\n");
118
+ console.log(' import { FeedbackWidget } from "@stephenov/feedbackwidget"\n');
119
+ console.log(` ${snippet}
111
120
  `);
112
- if (framework.name) {
113
- console.log(` Detected: ${framework.name}
121
+ if (framework.name) {
122
+ console.log(` Detected: ${framework.name}
114
123
  `);
115
- console.log(` Add to: ${framework.file}
124
+ console.log(` Add to: ${framework.file}
116
125
  `);
117
- if (framework.note) {
118
- console.log(` Note: ${framework.note}
126
+ if (framework.note) {
127
+ console.log(` Note: ${framework.note}
119
128
  `);
129
+ }
130
+ } else {
131
+ console.log(" Add to your root component (renders on every page)\n");
120
132
  }
121
- } else {
122
- console.log(" Add to your root component (renders on every page)\n");
123
133
  }
124
134
  console.log(" Dashboard: https://feedbackwidget-api.vercel.app/dashboard\n");
125
135
  }
@@ -186,6 +196,103 @@ function errorPage(error) {
186
196
  </body>
187
197
  </html>`;
188
198
  }
199
+ function injectWidget(apiKey) {
200
+ const cwd = process.cwd();
201
+ const importLine = 'import { FeedbackWidget } from "@stephenov/feedbackwidget";';
202
+ const component = `<FeedbackWidget apiKey="${apiKey}" />`;
203
+ try {
204
+ const pkgPath = import_path.default.join(cwd, "package.json");
205
+ const pkg = JSON.parse(import_fs.default.readFileSync(pkgPath, "utf-8"));
206
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
207
+ if (deps.next) {
208
+ const layoutPaths = [
209
+ import_path.default.join(cwd, "app", "layout.tsx"),
210
+ import_path.default.join(cwd, "app", "layout.js"),
211
+ import_path.default.join(cwd, "src", "app", "layout.tsx"),
212
+ import_path.default.join(cwd, "src", "app", "layout.js")
213
+ ];
214
+ for (const layoutPath of layoutPaths) {
215
+ if (import_fs.default.existsSync(layoutPath)) {
216
+ let content = import_fs.default.readFileSync(layoutPath, "utf-8");
217
+ if (content.includes("FeedbackWidget")) {
218
+ return null;
219
+ }
220
+ const importMatch = content.match(/^(import .+\n)+/m);
221
+ if (importMatch) {
222
+ const lastImportEnd = importMatch.index + importMatch[0].length;
223
+ content = content.slice(0, lastImportEnd) + importLine + "\n" + content.slice(lastImportEnd);
224
+ } else {
225
+ content = importLine + "\n" + content;
226
+ }
227
+ const bodyMatch = content.match(/(<body[^>]*>)(\s*)/);
228
+ if (bodyMatch) {
229
+ const insertPos = bodyMatch.index + bodyMatch[0].length;
230
+ content = content.slice(0, insertPos) + "\n " + component + content.slice(insertPos);
231
+ }
232
+ import_fs.default.writeFileSync(layoutPath, content);
233
+ return { file: layoutPath.replace(cwd + "/", "") };
234
+ }
235
+ }
236
+ const appPaths = [
237
+ import_path.default.join(cwd, "pages", "_app.tsx"),
238
+ import_path.default.join(cwd, "pages", "_app.js"),
239
+ import_path.default.join(cwd, "src", "pages", "_app.tsx"),
240
+ import_path.default.join(cwd, "src", "pages", "_app.js")
241
+ ];
242
+ for (const appPath of appPaths) {
243
+ if (import_fs.default.existsSync(appPath)) {
244
+ let content = import_fs.default.readFileSync(appPath, "utf-8");
245
+ if (content.includes("FeedbackWidget")) {
246
+ return null;
247
+ }
248
+ const importMatch = content.match(/^(import .+\n)+/m);
249
+ if (importMatch) {
250
+ const lastImportEnd = importMatch.index + importMatch[0].length;
251
+ content = content.slice(0, lastImportEnd) + importLine + "\n" + content.slice(lastImportEnd);
252
+ }
253
+ const componentMatch = content.match(/<Component\s+\{\.\.\.pageProps\}\s*\/>/);
254
+ if (componentMatch) {
255
+ const insertPos = componentMatch.index + componentMatch[0].length;
256
+ content = content.slice(0, insertPos) + "\n " + component + content.slice(insertPos);
257
+ }
258
+ import_fs.default.writeFileSync(appPath, content);
259
+ return { file: appPath.replace(cwd + "/", "") };
260
+ }
261
+ }
262
+ }
263
+ if (deps.vite || deps["react-scripts"] || deps.react) {
264
+ const appPaths = [
265
+ import_path.default.join(cwd, "src", "App.tsx"),
266
+ import_path.default.join(cwd, "src", "App.jsx")
267
+ ];
268
+ for (const appPath of appPaths) {
269
+ if (import_fs.default.existsSync(appPath)) {
270
+ let content = import_fs.default.readFileSync(appPath, "utf-8");
271
+ if (content.includes("FeedbackWidget")) {
272
+ return null;
273
+ }
274
+ const importMatch = content.match(/^(import .+\n)+/m);
275
+ if (importMatch) {
276
+ const lastImportEnd = importMatch.index + importMatch[0].length;
277
+ content = content.slice(0, lastImportEnd) + importLine + "\n" + content.slice(lastImportEnd);
278
+ }
279
+ const returnMatch = content.match(/return\s*\(\s*\n?\s*(<[A-Za-z>])/);
280
+ if (returnMatch) {
281
+ const match = content.match(/return\s*\(\s*\n?\s*<[A-Za-z]+[^>]*>/);
282
+ if (match) {
283
+ const insertPos = match.index + match[0].length;
284
+ content = content.slice(0, insertPos) + "\n " + component + content.slice(insertPos);
285
+ }
286
+ }
287
+ import_fs.default.writeFileSync(appPath, content);
288
+ return { file: appPath.replace(cwd + "/", "") };
289
+ }
290
+ }
291
+ }
292
+ } catch {
293
+ }
294
+ return null;
295
+ }
189
296
  function detectFramework() {
190
297
  const cwd = process.cwd();
191
298
  try {
@@ -194,49 +301,49 @@ function detectFramework() {
194
301
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
195
302
  if (deps.next && import_fs.default.existsSync(import_path.default.join(cwd, "app"))) {
196
303
  if (import_fs.default.existsSync(import_path.default.join(cwd, "app", "layout.tsx"))) {
197
- return { name: "Next.js (App Router)", file: "app/layout.tsx", note: "Add inside <body> before {children}" };
304
+ return { name: "Next.js (App Router)", file: "app/layout.tsx", note: "Inside <body>, before {children}" };
198
305
  }
199
306
  if (import_fs.default.existsSync(import_path.default.join(cwd, "app", "layout.js"))) {
200
- return { name: "Next.js (App Router)", file: "app/layout.js", note: "Add inside <body> before {children}" };
307
+ return { name: "Next.js (App Router)", file: "app/layout.js", note: "Inside <body>, before {children}" };
201
308
  }
202
309
  if (import_fs.default.existsSync(import_path.default.join(cwd, "src", "app", "layout.tsx"))) {
203
- return { name: "Next.js (App Router)", file: "src/app/layout.tsx", note: "Add inside <body> before {children}" };
310
+ return { name: "Next.js (App Router)", file: "src/app/layout.tsx", note: "Inside <body>, before {children}" };
204
311
  }
205
312
  }
206
313
  if (deps.next && import_fs.default.existsSync(import_path.default.join(cwd, "pages"))) {
207
314
  if (import_fs.default.existsSync(import_path.default.join(cwd, "pages", "_app.tsx"))) {
208
- return { name: "Next.js (Pages Router)", file: "pages/_app.tsx", note: "Add after <Component {...pageProps} />" };
315
+ return { name: "Next.js (Pages Router)", file: "pages/_app.tsx", note: "After <Component {...pageProps} />" };
209
316
  }
210
317
  if (import_fs.default.existsSync(import_path.default.join(cwd, "pages", "_app.js"))) {
211
- return { name: "Next.js (Pages Router)", file: "pages/_app.js", note: "Add after <Component {...pageProps} />" };
318
+ return { name: "Next.js (Pages Router)", file: "pages/_app.js", note: "After <Component {...pageProps} />" };
212
319
  }
213
320
  }
214
321
  if (deps.vite && (deps.react || deps["@vitejs/plugin-react"])) {
215
322
  if (import_fs.default.existsSync(import_path.default.join(cwd, "src", "App.tsx"))) {
216
- return { name: "Vite + React", file: "src/App.tsx", note: "Add at the end of your App component" };
323
+ return { name: "Vite + React", file: "src/App.tsx", note: "At end of App component" };
217
324
  }
218
325
  if (import_fs.default.existsSync(import_path.default.join(cwd, "src", "App.jsx"))) {
219
- return { name: "Vite + React", file: "src/App.jsx", note: "Add at the end of your App component" };
326
+ return { name: "Vite + React", file: "src/App.jsx", note: "At end of App component" };
220
327
  }
221
328
  }
222
329
  if (deps["react-scripts"]) {
223
330
  if (import_fs.default.existsSync(import_path.default.join(cwd, "src", "App.tsx"))) {
224
- return { name: "Create React App", file: "src/App.tsx", note: "Add at the end of your App component" };
331
+ return { name: "Create React App", file: "src/App.tsx", note: "At end of App component" };
225
332
  }
226
333
  if (import_fs.default.existsSync(import_path.default.join(cwd, "src", "App.js"))) {
227
- return { name: "Create React App", file: "src/App.js", note: "Add at the end of your App component" };
334
+ return { name: "Create React App", file: "src/App.js", note: "At end of App component" };
228
335
  }
229
336
  }
230
337
  if (deps.astro) {
231
- return { name: "Astro", file: "Create a React component", note: "Use client:load directive" };
338
+ return { name: "Astro", file: "A React component", note: "Use client:load directive" };
232
339
  }
233
340
  if (deps["@remix-run/react"]) {
234
341
  if (import_fs.default.existsSync(import_path.default.join(cwd, "app", "root.tsx"))) {
235
- return { name: "Remix", file: "app/root.tsx", note: "Add inside <body>" };
342
+ return { name: "Remix", file: "app/root.tsx", note: "Inside <body>" };
236
343
  }
237
344
  }
238
345
  if (deps.react) {
239
- return { name: "React", file: "Your main App component", note: "Add anywhere it renders on every page" };
346
+ return { name: "React", file: "Your App component", note: "Where it renders on every page" };
240
347
  }
241
348
  } catch {
242
349
  }
package/dist/cli.mjs CHANGED
@@ -65,38 +65,48 @@ async function initProject() {
65
65
  reject(new Error("Authentication timed out"));
66
66
  }, 5 * 60 * 1e3);
67
67
  });
68
- const snippet = `<FeedbackWidget apiKey="${apiKey}" />`;
69
- let copied = false;
70
- try {
71
- await copyToClipboard(snippet);
72
- copied = true;
73
- } catch {
74
- }
75
68
  console.log("\u2705 Authenticated!\n");
76
- console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
77
- console.log(" Your API Key:\n");
78
- console.log(` ${apiKey}
69
+ const injected = injectWidget(apiKey);
70
+ if (injected) {
71
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
72
+ console.log(" \u2705 Widget automatically added to your app!\n");
73
+ console.log(` Modified: ${injected.file}
79
74
  `);
80
- console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
81
- if (copied) {
82
- console.log(" \u{1F4CB} Copied to clipboard!\n");
83
- }
84
- const framework = detectFramework();
85
- console.log(" Add to your app:\n");
86
- console.log(' import { FeedbackWidget } from "@stephenov/feedbackwidget"\n');
87
- console.log(` ${snippet}
75
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
76
+ console.log(" Just run your dev server and the widget will appear.\n");
77
+ } else {
78
+ const snippet = `<FeedbackWidget apiKey="${apiKey}" />`;
79
+ let copied = false;
80
+ try {
81
+ await copyToClipboard(snippet);
82
+ copied = true;
83
+ } catch {
84
+ }
85
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
86
+ console.log(" Your API Key:\n");
87
+ console.log(` ${apiKey}
88
+ `);
89
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
90
+ if (copied) {
91
+ console.log(" \u{1F4CB} Copied to clipboard!\n");
92
+ }
93
+ const framework = detectFramework();
94
+ console.log(" Add to your app:\n");
95
+ console.log(' import { FeedbackWidget } from "@stephenov/feedbackwidget"\n');
96
+ console.log(` ${snippet}
88
97
  `);
89
- if (framework.name) {
90
- console.log(` Detected: ${framework.name}
98
+ if (framework.name) {
99
+ console.log(` Detected: ${framework.name}
91
100
  `);
92
- console.log(` Add to: ${framework.file}
101
+ console.log(` Add to: ${framework.file}
93
102
  `);
94
- if (framework.note) {
95
- console.log(` Note: ${framework.note}
103
+ if (framework.note) {
104
+ console.log(` Note: ${framework.note}
96
105
  `);
106
+ }
107
+ } else {
108
+ console.log(" Add to your root component (renders on every page)\n");
97
109
  }
98
- } else {
99
- console.log(" Add to your root component (renders on every page)\n");
100
110
  }
101
111
  console.log(" Dashboard: https://feedbackwidget-api.vercel.app/dashboard\n");
102
112
  }
@@ -163,6 +173,103 @@ function errorPage(error) {
163
173
  </body>
164
174
  </html>`;
165
175
  }
176
+ function injectWidget(apiKey) {
177
+ const cwd = process.cwd();
178
+ const importLine = 'import { FeedbackWidget } from "@stephenov/feedbackwidget";';
179
+ const component = `<FeedbackWidget apiKey="${apiKey}" />`;
180
+ try {
181
+ const pkgPath = path.join(cwd, "package.json");
182
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
183
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
184
+ if (deps.next) {
185
+ const layoutPaths = [
186
+ path.join(cwd, "app", "layout.tsx"),
187
+ path.join(cwd, "app", "layout.js"),
188
+ path.join(cwd, "src", "app", "layout.tsx"),
189
+ path.join(cwd, "src", "app", "layout.js")
190
+ ];
191
+ for (const layoutPath of layoutPaths) {
192
+ if (fs.existsSync(layoutPath)) {
193
+ let content = fs.readFileSync(layoutPath, "utf-8");
194
+ if (content.includes("FeedbackWidget")) {
195
+ return null;
196
+ }
197
+ const importMatch = content.match(/^(import .+\n)+/m);
198
+ if (importMatch) {
199
+ const lastImportEnd = importMatch.index + importMatch[0].length;
200
+ content = content.slice(0, lastImportEnd) + importLine + "\n" + content.slice(lastImportEnd);
201
+ } else {
202
+ content = importLine + "\n" + content;
203
+ }
204
+ const bodyMatch = content.match(/(<body[^>]*>)(\s*)/);
205
+ if (bodyMatch) {
206
+ const insertPos = bodyMatch.index + bodyMatch[0].length;
207
+ content = content.slice(0, insertPos) + "\n " + component + content.slice(insertPos);
208
+ }
209
+ fs.writeFileSync(layoutPath, content);
210
+ return { file: layoutPath.replace(cwd + "/", "") };
211
+ }
212
+ }
213
+ const appPaths = [
214
+ path.join(cwd, "pages", "_app.tsx"),
215
+ path.join(cwd, "pages", "_app.js"),
216
+ path.join(cwd, "src", "pages", "_app.tsx"),
217
+ path.join(cwd, "src", "pages", "_app.js")
218
+ ];
219
+ for (const appPath of appPaths) {
220
+ if (fs.existsSync(appPath)) {
221
+ let content = fs.readFileSync(appPath, "utf-8");
222
+ if (content.includes("FeedbackWidget")) {
223
+ return null;
224
+ }
225
+ const importMatch = content.match(/^(import .+\n)+/m);
226
+ if (importMatch) {
227
+ const lastImportEnd = importMatch.index + importMatch[0].length;
228
+ content = content.slice(0, lastImportEnd) + importLine + "\n" + content.slice(lastImportEnd);
229
+ }
230
+ const componentMatch = content.match(/<Component\s+\{\.\.\.pageProps\}\s*\/>/);
231
+ if (componentMatch) {
232
+ const insertPos = componentMatch.index + componentMatch[0].length;
233
+ content = content.slice(0, insertPos) + "\n " + component + content.slice(insertPos);
234
+ }
235
+ fs.writeFileSync(appPath, content);
236
+ return { file: appPath.replace(cwd + "/", "") };
237
+ }
238
+ }
239
+ }
240
+ if (deps.vite || deps["react-scripts"] || deps.react) {
241
+ const appPaths = [
242
+ path.join(cwd, "src", "App.tsx"),
243
+ path.join(cwd, "src", "App.jsx")
244
+ ];
245
+ for (const appPath of appPaths) {
246
+ if (fs.existsSync(appPath)) {
247
+ let content = fs.readFileSync(appPath, "utf-8");
248
+ if (content.includes("FeedbackWidget")) {
249
+ return null;
250
+ }
251
+ const importMatch = content.match(/^(import .+\n)+/m);
252
+ if (importMatch) {
253
+ const lastImportEnd = importMatch.index + importMatch[0].length;
254
+ content = content.slice(0, lastImportEnd) + importLine + "\n" + content.slice(lastImportEnd);
255
+ }
256
+ const returnMatch = content.match(/return\s*\(\s*\n?\s*(<[A-Za-z>])/);
257
+ if (returnMatch) {
258
+ const match = content.match(/return\s*\(\s*\n?\s*<[A-Za-z]+[^>]*>/);
259
+ if (match) {
260
+ const insertPos = match.index + match[0].length;
261
+ content = content.slice(0, insertPos) + "\n " + component + content.slice(insertPos);
262
+ }
263
+ }
264
+ fs.writeFileSync(appPath, content);
265
+ return { file: appPath.replace(cwd + "/", "") };
266
+ }
267
+ }
268
+ }
269
+ } catch {
270
+ }
271
+ return null;
272
+ }
166
273
  function detectFramework() {
167
274
  const cwd = process.cwd();
168
275
  try {
@@ -171,49 +278,49 @@ function detectFramework() {
171
278
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
172
279
  if (deps.next && fs.existsSync(path.join(cwd, "app"))) {
173
280
  if (fs.existsSync(path.join(cwd, "app", "layout.tsx"))) {
174
- return { name: "Next.js (App Router)", file: "app/layout.tsx", note: "Add inside <body> before {children}" };
281
+ return { name: "Next.js (App Router)", file: "app/layout.tsx", note: "Inside <body>, before {children}" };
175
282
  }
176
283
  if (fs.existsSync(path.join(cwd, "app", "layout.js"))) {
177
- return { name: "Next.js (App Router)", file: "app/layout.js", note: "Add inside <body> before {children}" };
284
+ return { name: "Next.js (App Router)", file: "app/layout.js", note: "Inside <body>, before {children}" };
178
285
  }
179
286
  if (fs.existsSync(path.join(cwd, "src", "app", "layout.tsx"))) {
180
- return { name: "Next.js (App Router)", file: "src/app/layout.tsx", note: "Add inside <body> before {children}" };
287
+ return { name: "Next.js (App Router)", file: "src/app/layout.tsx", note: "Inside <body>, before {children}" };
181
288
  }
182
289
  }
183
290
  if (deps.next && fs.existsSync(path.join(cwd, "pages"))) {
184
291
  if (fs.existsSync(path.join(cwd, "pages", "_app.tsx"))) {
185
- return { name: "Next.js (Pages Router)", file: "pages/_app.tsx", note: "Add after <Component {...pageProps} />" };
292
+ return { name: "Next.js (Pages Router)", file: "pages/_app.tsx", note: "After <Component {...pageProps} />" };
186
293
  }
187
294
  if (fs.existsSync(path.join(cwd, "pages", "_app.js"))) {
188
- return { name: "Next.js (Pages Router)", file: "pages/_app.js", note: "Add after <Component {...pageProps} />" };
295
+ return { name: "Next.js (Pages Router)", file: "pages/_app.js", note: "After <Component {...pageProps} />" };
189
296
  }
190
297
  }
191
298
  if (deps.vite && (deps.react || deps["@vitejs/plugin-react"])) {
192
299
  if (fs.existsSync(path.join(cwd, "src", "App.tsx"))) {
193
- return { name: "Vite + React", file: "src/App.tsx", note: "Add at the end of your App component" };
300
+ return { name: "Vite + React", file: "src/App.tsx", note: "At end of App component" };
194
301
  }
195
302
  if (fs.existsSync(path.join(cwd, "src", "App.jsx"))) {
196
- return { name: "Vite + React", file: "src/App.jsx", note: "Add at the end of your App component" };
303
+ return { name: "Vite + React", file: "src/App.jsx", note: "At end of App component" };
197
304
  }
198
305
  }
199
306
  if (deps["react-scripts"]) {
200
307
  if (fs.existsSync(path.join(cwd, "src", "App.tsx"))) {
201
- return { name: "Create React App", file: "src/App.tsx", note: "Add at the end of your App component" };
308
+ return { name: "Create React App", file: "src/App.tsx", note: "At end of App component" };
202
309
  }
203
310
  if (fs.existsSync(path.join(cwd, "src", "App.js"))) {
204
- return { name: "Create React App", file: "src/App.js", note: "Add at the end of your App component" };
311
+ return { name: "Create React App", file: "src/App.js", note: "At end of App component" };
205
312
  }
206
313
  }
207
314
  if (deps.astro) {
208
- return { name: "Astro", file: "Create a React component", note: "Use client:load directive" };
315
+ return { name: "Astro", file: "A React component", note: "Use client:load directive" };
209
316
  }
210
317
  if (deps["@remix-run/react"]) {
211
318
  if (fs.existsSync(path.join(cwd, "app", "root.tsx"))) {
212
- return { name: "Remix", file: "app/root.tsx", note: "Add inside <body>" };
319
+ return { name: "Remix", file: "app/root.tsx", note: "Inside <body>" };
213
320
  }
214
321
  }
215
322
  if (deps.react) {
216
- return { name: "React", file: "Your main App component", note: "Add anywhere it renders on every page" };
323
+ return { name: "React", file: "Your App component", note: "Where it renders on every page" };
217
324
  }
218
325
  } catch {
219
326
  }
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import {
2
3
  FeedbackApiClient,
3
4
  FeedbackPanel,
@@ -8,7 +9,7 @@ import {
8
9
  createApiClient,
9
10
  formatDuration,
10
11
  isVoiceSupported
11
- } from "./chunk-QQLLK6MC.mjs";
12
+ } from "./chunk-WCFS4MGE.mjs";
12
13
 
13
14
  // src/hooks/useFeedback.ts
14
15
  import { useState, useCallback, useMemo } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stephenov/feedbackwidget",
3
- "version": "0.3.5",
3
+ "version": "0.5.0",
4
4
  "description": "Voice-first feedback widget with AI analysis, GitHub, and Slack integration",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -25,8 +25,8 @@
25
25
  "dist"
26
26
  ],
27
27
  "scripts": {
28
- "build": "tsup src/index.ts src/auto.tsx src/cli.ts --format cjs,esm --dts --clean",
29
- "dev": "tsup src/index.ts src/auto.tsx src/cli.ts --format cjs,esm --dts --watch",
28
+ "build": "tsup",
29
+ "dev": "tsup --watch",
30
30
  "lint": "eslint src/",
31
31
  "clean": "rm -rf dist"
32
32
  },
package/dist/cli.d.mts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/cli.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node