@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 +1 -0
- package/dist/auto.mjs +2 -1
- package/dist/{chunk-QQLLK6MC.mjs → chunk-WCFS4MGE.mjs} +2 -0
- package/dist/cli.js +144 -37
- package/dist/cli.mjs +144 -37
- package/dist/index.js +1 -0
- package/dist/index.mjs +2 -1
- package/package.json +3 -3
- package/dist/cli.d.mts +0 -1
- package/dist/cli.d.ts +0 -1
package/dist/auto.js
CHANGED
package/dist/auto.mjs
CHANGED
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
|
|
121
|
+
if (framework.name) {
|
|
122
|
+
console.log(` Detected: ${framework.name}
|
|
114
123
|
`);
|
|
115
|
-
|
|
124
|
+
console.log(` Add to: ${framework.file}
|
|
116
125
|
`);
|
|
117
|
-
|
|
118
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
90
|
-
|
|
98
|
+
if (framework.name) {
|
|
99
|
+
console.log(` Detected: ${framework.name}
|
|
91
100
|
`);
|
|
92
|
-
|
|
101
|
+
console.log(` Add to: ${framework.file}
|
|
93
102
|
`);
|
|
94
|
-
|
|
95
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
|
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
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-
|
|
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
|
+
"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
|
|
29
|
-
"dev": "tsup
|
|
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
|