apostil 0.1.7 → 0.2.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/cli/index.js CHANGED
@@ -24,10 +24,10 @@ function parseMode(flags) {
24
24
  }
25
25
  function printHelp() {
26
26
  console.log(`
27
- apostil \u2014 Pin-and-comment feedback for React & Next.js
27
+ apostil \u2014 Lightweight, Figma-like commenting for React
28
28
 
29
29
  Usage:
30
- npx apostil init [mode] Set up apostil in your Next.js project
30
+ npx apostil init [mode] Set up apostil in your project
31
31
  npx apostil remove Remove apostil from your project
32
32
  npx apostil help Show this help
33
33
 
@@ -35,20 +35,54 @@ function printHelp() {
35
35
  (default) Personal \u2014 local dev only, comments gitignored
36
36
  --dev Dev + staging \u2014 comments gitignored, env-controlled
37
37
  --public All environments \u2014 comments committed to git
38
+
39
+ Supported frameworks: Next.js (App Router), Vite + React
40
+ Framework is auto-detected from your project.
38
41
  `);
39
42
  }
43
+ async function detectFramework(cwd) {
44
+ for (const name of ["next.config.js", "next.config.ts", "next.config.mjs"]) {
45
+ if (await fileExists(path.join(cwd, name))) return "nextjs";
46
+ }
47
+ for (const name of ["vite.config.js", "vite.config.ts", "vite.config.mjs"]) {
48
+ if (await fileExists(path.join(cwd, name))) return "vite";
49
+ }
50
+ try {
51
+ const pkg = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
52
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
53
+ if (deps["next"]) return "nextjs";
54
+ if (deps["vite"]) return "vite";
55
+ } catch {
56
+ }
57
+ return null;
58
+ }
40
59
  async function init(mode) {
41
60
  const cwd = process.cwd();
61
+ const framework = await detectFramework(cwd);
62
+ if (!framework) {
63
+ console.log(" Could not detect your framework.");
64
+ console.log(" Apostil supports Next.js (App Router) and Vite + React.");
65
+ console.log(" Make sure you're running this from your project root.\n");
66
+ process.exit(1);
67
+ }
68
+ console.log(`
69
+ Detected: ${framework === "nextjs" ? "Next.js" : "Vite + React"}`);
70
+ if (framework === "nextjs") {
71
+ await initNextjs(cwd, mode);
72
+ } else {
73
+ await initVite(cwd, mode);
74
+ }
75
+ }
76
+ async function initNextjs(cwd, mode) {
42
77
  const appDir = await findAppDir(cwd);
43
78
  if (!appDir) {
44
79
  console.log(" Could not find a Next.js app/ directory.");
45
- console.log(" Make sure you're running this from your project root.\n");
80
+ console.log(" Make sure you're using the App Router.\n");
46
81
  process.exit(1);
47
82
  }
48
83
  const useSrc = appDir.includes("src/app");
49
84
  const modeLabel = mode === "personal" ? "personal" : mode === "dev" ? "dev" : "public";
50
- console.log(`
51
- Setting up apostil (${modeLabel} mode)...
85
+ console.log(` Setting up apostil (${modeLabel} mode)...
52
86
  `);
53
87
  const apiDir = path.join(appDir, "api", "apostil");
54
88
  const apiFile = path.join(apiDir, "route.ts");
@@ -67,80 +101,37 @@ async function init(mode) {
67
101
  const componentsDir = path.join(cwd, useSrc ? "src/components" : "components");
68
102
  const wrapperFile = path.join(componentsDir, "apostil-wrapper.tsx");
69
103
  await fs.mkdir(componentsDir, { recursive: true });
70
- await fs.writeFile(wrapperFile, getWrapperComponent(mode), "utf-8");
104
+ await fs.writeFile(wrapperFile, getNextjsWrapper(mode), "utf-8");
71
105
  console.log(` \u2713 Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);
72
106
  const commentsDir = path.join(cwd, ".apostil");
73
107
  await fs.mkdir(commentsDir, { recursive: true });
74
108
  console.log(" \u2713 Created .apostil/ directory");
75
- const gitignorePath = path.join(cwd, ".gitignore");
76
- let gitignore = "";
77
- try {
78
- gitignore = await fs.readFile(gitignorePath, "utf-8");
79
- } catch {
80
- }
81
- if (mode === "public") {
82
- if (gitignore.includes(".apostil")) {
83
- console.log(" \u2713 .gitignore unchanged (public mode \u2014 comments will be committed)");
84
- } else {
85
- console.log(" \u2713 Comments will be committed to git (public mode)");
86
- }
87
- } else {
88
- if (!gitignore.includes(".apostil")) {
89
- const entry = "\n# Apostil comments\n.apostil/\n";
90
- await fs.appendFile(gitignorePath, entry, "utf-8");
91
- console.log(" \u2713 Added .apostil/ to .gitignore");
92
- } else {
93
- console.log(" \u2713 .gitignore already configured");
94
- }
95
- }
96
- const layoutInjected = await injectIntoLayout(appDir, useSrc);
109
+ await handleGitignore(cwd, mode);
110
+ const layoutInjected = await injectIntoNextjsLayout(appDir, useSrc);
97
111
  if (layoutInjected) {
98
112
  console.log(" \u2713 Added <ApostilWrapper> to root layout");
99
113
  }
100
114
  console.log("\n Done! Run your dev server and press C to start commenting.\n");
101
115
  }
102
- async function remove() {
103
- const cwd = process.cwd();
104
- const appDir = await findAppDir(cwd);
105
- const useSrc = appDir?.includes("src/app") ?? false;
106
- console.log("\n Removing apostil...\n");
107
- if (appDir) {
108
- const apiDir = path.join(appDir, "api", "apostil");
109
- if (await fileExists(path.join(apiDir, "route.ts"))) {
110
- await fs.rm(apiDir, { recursive: true });
111
- console.log(" \u2713 Removed API route");
112
- }
113
- }
116
+ async function initVite(cwd, mode) {
117
+ const useSrc = await fileExists(path.join(cwd, "src"));
118
+ const modeLabel = mode === "personal" ? "personal" : mode === "dev" ? "dev" : "public";
119
+ console.log(` Setting up apostil (${modeLabel} mode)...
120
+ `);
114
121
  const componentsDir = path.join(cwd, useSrc ? "src/components" : "components");
115
122
  const wrapperFile = path.join(componentsDir, "apostil-wrapper.tsx");
116
- if (await fileExists(wrapperFile)) {
117
- await fs.rm(wrapperFile);
118
- console.log(" \u2713 Removed wrapper component");
119
- }
120
- if (appDir) {
121
- const unwrapped = await removeFromLayout(appDir);
122
- if (unwrapped) {
123
- console.log(" \u2713 Removed <ApostilWrapper> from root layout");
124
- }
125
- }
126
- const commentsDir = path.join(cwd, ".apostil");
127
- if (await fileExists(commentsDir)) {
128
- await fs.rm(commentsDir, { recursive: true });
129
- console.log(" \u2713 Removed .apostil/ directory");
130
- }
131
- const gitignorePath = path.join(cwd, ".gitignore");
132
- try {
133
- let gitignore = await fs.readFile(gitignorePath, "utf-8");
134
- gitignore = gitignore.replace(/\n?# Apostil comments\n\.apostil\/\n?/g, "");
135
- await fs.writeFile(gitignorePath, gitignore, "utf-8");
136
- console.log(" \u2713 Cleaned .gitignore");
137
- } catch {
123
+ await fs.mkdir(componentsDir, { recursive: true });
124
+ const hasRouter = await hasReactRouter(cwd);
125
+ await fs.writeFile(wrapperFile, getViteWrapper(mode, hasRouter), "utf-8");
126
+ console.log(` \u2713 Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);
127
+ console.log(" \u2713 Using localStorage adapter (comments stored in browser)");
128
+ const entryInjected = await injectIntoViteEntry(cwd, useSrc);
129
+ if (entryInjected) {
130
+ console.log(" \u2713 Added <ApostilWrapper> to app entry");
138
131
  }
139
- console.log(`
140
- Done! Now run: npm uninstall apostil
141
- `);
132
+ console.log("\n Done! Run your dev server and press C to start commenting.\n");
142
133
  }
143
- function getWrapperComponent(mode) {
134
+ function getNextjsWrapper(mode) {
144
135
  const envGuard = mode === "personal" ? `
145
136
  // Personal mode \u2014 only active in local development
146
137
  if (process.env.NODE_ENV !== "development") {
@@ -186,7 +177,86 @@ export function ApostilWrapper({ children }: { children: React.ReactNode }) {${e
186
177
  }
187
178
  `;
188
179
  }
189
- async function injectIntoLayout(appDir, useSrc) {
180
+ async function hasReactRouter(cwd) {
181
+ try {
182
+ const pkg = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8"));
183
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
184
+ return !!deps["react-router-dom"] || !!deps["react-router"];
185
+ } catch {
186
+ return false;
187
+ }
188
+ }
189
+ function getViteWrapper(mode, hasRouter) {
190
+ const envGuard = mode === "personal" ? `
191
+ // Personal mode \u2014 only active in local development
192
+ if (import.meta.env.PROD) {
193
+ return <>{children}</>;
194
+ }
195
+ ` : mode === "dev" ? `
196
+ // Dev mode \u2014 active in dev + staging, disabled in production
197
+ // Set VITE_APOSTIL=true to force on in any environment
198
+ const forceOn = import.meta.env.VITE_APOSTIL === "true";
199
+ if (import.meta.env.PROD && !forceOn) {
200
+ return <>{children}</>;
201
+ }
202
+ ` : `
203
+ // Public mode \u2014 active in all environments
204
+ // Set VITE_APOSTIL=false to disable
205
+ if (import.meta.env.VITE_APOSTIL === "false") {
206
+ return <>{children}</>;
207
+ }
208
+ `;
209
+ const routerImport = hasRouter ? `import { useLocation } from "react-router-dom";
210
+ ` : "";
211
+ const pageIdLogic = hasRouter ? ` const location = useLocation();
212
+ const pageId = location.pathname.replace(/\\//g, "--").replace(/^--/, "") || "home";` : ` const pageId = window.location.pathname.replace(/\\//g, "--").replace(/^--/, "") || "home";`;
213
+ return `${routerImport}import {
214
+ ApostilProvider,
215
+ CommentOverlay,
216
+ CommentToggle,
217
+ CommentSidebar,
218
+ } from "apostil";
219
+ import { localStorageAdapter } from "apostil/adapters/localStorage";
220
+ import "apostil/styles.css";
221
+
222
+ export function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}
223
+ ${pageIdLogic}
224
+
225
+ return (
226
+ <ApostilProvider pageId={pageId} storage={localStorageAdapter}>
227
+ {children}
228
+ <CommentOverlay />
229
+ <CommentSidebar />
230
+ <CommentToggle />
231
+ </ApostilProvider>
232
+ );
233
+ }
234
+ `;
235
+ }
236
+ async function handleGitignore(cwd, mode) {
237
+ const gitignorePath = path.join(cwd, ".gitignore");
238
+ let gitignore = "";
239
+ try {
240
+ gitignore = await fs.readFile(gitignorePath, "utf-8");
241
+ } catch {
242
+ }
243
+ if (mode === "public") {
244
+ if (gitignore.includes(".apostil")) {
245
+ console.log(" \u2713 .gitignore unchanged (public mode \u2014 comments will be committed)");
246
+ } else {
247
+ console.log(" \u2713 Comments will be committed to git (public mode)");
248
+ }
249
+ } else {
250
+ if (!gitignore.includes(".apostil")) {
251
+ const entry = "\n# Apostil comments\n.apostil/\n";
252
+ await fs.appendFile(gitignorePath, entry, "utf-8");
253
+ console.log(" \u2713 Added .apostil/ to .gitignore");
254
+ } else {
255
+ console.log(" \u2713 .gitignore already configured");
256
+ }
257
+ }
258
+ }
259
+ async function injectIntoNextjsLayout(appDir, useSrc) {
190
260
  const layoutPath = await findLayout(appDir);
191
261
  if (!layoutPath) return false;
192
262
  let content = await fs.readFile(layoutPath, "utf-8");
@@ -229,14 +299,124 @@ async function injectIntoLayout(appDir, useSrc) {
229
299
  await fs.writeFile(layoutPath, content, "utf-8");
230
300
  return true;
231
301
  }
232
- async function removeFromLayout(appDir) {
233
- const layoutPath = await findLayout(appDir);
234
- if (!layoutPath) return false;
235
- let content = await fs.readFile(layoutPath, "utf-8");
302
+ async function injectIntoViteEntry(cwd, useSrc) {
303
+ const baseDir = useSrc ? path.join(cwd, "src") : cwd;
304
+ for (const ext of ["tsx", "jsx"]) {
305
+ const mainFile = path.join(baseDir, `main.${ext}`);
306
+ if (await fileExists(mainFile)) {
307
+ return await injectIntoViteMain(mainFile);
308
+ }
309
+ }
310
+ console.log(" \u26A0 Could not find main.tsx or main.jsx \u2014 add <ApostilWrapper> manually");
311
+ return false;
312
+ }
313
+ async function injectIntoViteMain(mainFile, wrapperFile) {
314
+ let content = await fs.readFile(mainFile, "utf-8");
315
+ if (content.includes("ApostilWrapper")) {
316
+ console.log(" \u2713 Entry already has <ApostilWrapper>");
317
+ return false;
318
+ }
319
+ const mainDir = path.dirname(mainFile);
320
+ const wrapperPath = wrapperFile ?? path.join(mainDir, "components", "apostil-wrapper.tsx");
321
+ let relativePath = path.relative(mainDir, wrapperPath).replace(/\.tsx$/, "");
322
+ if (!relativePath.startsWith(".")) relativePath = "./" + relativePath;
323
+ const importLine = `import { ApostilWrapper } from "${relativePath}";
324
+ `;
325
+ const importRegex = /^import\s.+$/gm;
326
+ let lastImportIndex = 0;
327
+ let match;
328
+ while ((match = importRegex.exec(content)) !== null) {
329
+ lastImportIndex = match.index + match[0].length;
330
+ }
331
+ if (lastImportIndex > 0) {
332
+ content = content.slice(0, lastImportIndex) + "\n" + importLine + content.slice(lastImportIndex);
333
+ } else {
334
+ content = importLine + content;
335
+ }
336
+ content = content.replace(/<App\s*\/>/, `<ApostilWrapper><App /></ApostilWrapper>`);
337
+ await fs.writeFile(mainFile, content, "utf-8");
338
+ return true;
339
+ }
340
+ async function remove() {
341
+ const cwd = process.cwd();
342
+ const framework = await detectFramework(cwd);
343
+ console.log("\n Removing apostil...\n");
344
+ if (framework === "nextjs") {
345
+ await removeNextjs(cwd);
346
+ } else if (framework === "vite") {
347
+ await removeVite(cwd);
348
+ } else {
349
+ await removeNextjs(cwd);
350
+ await removeVite(cwd);
351
+ }
352
+ const commentsDir = path.join(cwd, ".apostil");
353
+ if (await fileExists(commentsDir)) {
354
+ await fs.rm(commentsDir, { recursive: true });
355
+ console.log(" \u2713 Removed .apostil/ directory");
356
+ }
357
+ const gitignorePath = path.join(cwd, ".gitignore");
358
+ try {
359
+ let gitignore = await fs.readFile(gitignorePath, "utf-8");
360
+ gitignore = gitignore.replace(/\n?# Apostil comments\n\.apostil\/\n?/g, "");
361
+ await fs.writeFile(gitignorePath, gitignore, "utf-8");
362
+ console.log(" \u2713 Cleaned .gitignore");
363
+ } catch {
364
+ }
365
+ console.log(`
366
+ Done! Now run: npm uninstall apostil
367
+ `);
368
+ }
369
+ async function removeNextjs(cwd) {
370
+ const appDir = await findAppDir(cwd);
371
+ const useSrc = appDir?.includes("src/app") ?? false;
372
+ if (appDir) {
373
+ const apiDir = path.join(appDir, "api", "apostil");
374
+ if (await fileExists(path.join(apiDir, "route.ts"))) {
375
+ await fs.rm(apiDir, { recursive: true });
376
+ console.log(" \u2713 Removed API route");
377
+ }
378
+ }
379
+ const componentsDir = path.join(cwd, useSrc ? "src/components" : "components");
380
+ const wrapperFile = path.join(componentsDir, "apostil-wrapper.tsx");
381
+ if (await fileExists(wrapperFile)) {
382
+ await fs.rm(wrapperFile);
383
+ console.log(" \u2713 Removed wrapper component");
384
+ }
385
+ if (appDir) {
386
+ const layoutPath = await findLayout(appDir);
387
+ if (layoutPath) {
388
+ const unwrapped = await removeWrapperFromFile(layoutPath);
389
+ if (unwrapped) {
390
+ console.log(" \u2713 Removed <ApostilWrapper> from root layout");
391
+ }
392
+ }
393
+ }
394
+ }
395
+ async function removeVite(cwd) {
396
+ const useSrc = await fileExists(path.join(cwd, "src"));
397
+ const baseDir = useSrc ? path.join(cwd, "src") : cwd;
398
+ const componentsDir = path.join(cwd, useSrc ? "src/components" : "components");
399
+ const wrapperFile = path.join(componentsDir, "apostil-wrapper.tsx");
400
+ if (await fileExists(wrapperFile)) {
401
+ await fs.rm(wrapperFile);
402
+ console.log(" \u2713 Removed wrapper component");
403
+ }
404
+ for (const name of ["App.tsx", "App.jsx", "main.tsx", "main.jsx"]) {
405
+ const file = path.join(baseDir, name);
406
+ if (await fileExists(file)) {
407
+ const unwrapped = await removeWrapperFromFile(file);
408
+ if (unwrapped) {
409
+ console.log(` \u2713 Removed <ApostilWrapper> from ${name}`);
410
+ }
411
+ }
412
+ }
413
+ }
414
+ async function removeWrapperFromFile(filePath) {
415
+ let content = await fs.readFile(filePath, "utf-8");
236
416
  if (!content.includes("ApostilWrapper")) return false;
237
417
  content = content.replace(/import\s*\{[^}]*ApostilWrapper[^}]*\}\s*from\s*["'][^"']+["'];?\n?/g, "");
238
418
  content = content.replace(/<ApostilWrapper>([\s\S]*?)<\/ApostilWrapper>/g, "$1");
239
- await fs.writeFile(layoutPath, content, "utf-8");
419
+ await fs.writeFile(filePath, content, "utf-8");
240
420
  return true;
241
421
  }
242
422
  async function findAppDir(cwd) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\ntype Mode = \"personal\" | \"dev\" | \"public\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (command === \"init\") {\n const mode = parseMode(args.slice(1));\n init(mode);\n} else if (command === \"remove\") {\n remove();\n} else if (command === \"help\" || command === \"--help\" || command === \"-h\" || !command) {\n printHelp();\n} else {\n console.log(` Unknown command: ${command}\\n`);\n printHelp();\n}\n\nfunction parseMode(flags: string[]): Mode {\n if (flags.includes(\"--dev\")) return \"dev\";\n if (flags.includes(\"--public\")) return \"public\";\n return \"personal\";\n}\n\nfunction printHelp() {\n console.log(`\n apostil — Pin-and-comment feedback for React & Next.js\n\n Usage:\n npx apostil init [mode] Set up apostil in your Next.js project\n npx apostil remove Remove apostil from your project\n npx apostil help Show this help\n\n Modes:\n (default) Personal — local dev only, comments gitignored\n --dev Dev + staging — comments gitignored, env-controlled\n --public All environments — comments committed to git\n`);\n}\n\nasync function init(mode: Mode) {\n const cwd = process.cwd();\n\n // Detect Next.js app directory\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.log(\" Could not find a Next.js app/ directory.\");\n console.log(\" Make sure you're running this from your project root.\\n\");\n process.exit(1);\n }\n\n // Detect src/ prefix\n const useSrc = appDir.includes(\"src/app\");\n\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(`\\n Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create API route\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n const apiFile = path.join(apiDir, \"route.ts\");\n if (await fileExists(apiFile)) {\n console.log(\" ✓ API route already exists\");\n } else {\n await fs.mkdir(apiDir, { recursive: true });\n await fs.writeFile(\n apiFile,\n `export { GET, POST } from \"apostil/adapters/nextjs\";\\n`,\n \"utf-8\"\n );\n console.log(` ✓ Created ${rel(cwd, apiFile)}`);\n }\n\n // 2. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n await fs.writeFile(wrapperFile, getWrapperComponent(mode), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n // 3. Create .apostil/ directory for comment storage\n const commentsDir = path.join(cwd, \".apostil\");\n await fs.mkdir(commentsDir, { recursive: true });\n console.log(\" ✓ Created .apostil/ directory\");\n\n // 4. Handle .gitignore based on mode\n const gitignorePath = path.join(cwd, \".gitignore\");\n let gitignore = \"\";\n try {\n gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {}\n\n if (mode === \"public\") {\n // Public mode: don't gitignore comments — they get committed\n if (gitignore.includes(\".apostil\")) {\n console.log(\" ✓ .gitignore unchanged (public mode — comments will be committed)\");\n } else {\n console.log(\" ✓ Comments will be committed to git (public mode)\");\n }\n } else {\n // Personal & dev: gitignore comments\n if (!gitignore.includes(\".apostil\")) {\n const entry = \"\\n# Apostil comments\\n.apostil/\\n\";\n await fs.appendFile(gitignorePath, entry, \"utf-8\");\n console.log(\" ✓ Added .apostil/ to .gitignore\");\n } else {\n console.log(\" ✓ .gitignore already configured\");\n }\n }\n\n // 5. Inject wrapper into root layout\n const layoutInjected = await injectIntoLayout(appDir, useSrc);\n if (layoutInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to root layout\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\nasync function remove() {\n const cwd = process.cwd();\n const appDir = await findAppDir(cwd);\n const useSrc = appDir?.includes(\"src/app\") ?? false;\n\n console.log(\"\\n Removing apostil...\\n\");\n\n // 1. Remove API route\n if (appDir) {\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n if (await fileExists(path.join(apiDir, \"route.ts\"))) {\n await fs.rm(apiDir, { recursive: true });\n console.log(\" ✓ Removed API route\");\n }\n }\n\n // 2. Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // 3. Remove wrapper from layout\n if (appDir) {\n const unwrapped = await removeFromLayout(appDir);\n if (unwrapped) {\n console.log(\" ✓ Removed <ApostilWrapper> from root layout\");\n }\n }\n\n // 4. Remove .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n if (await fileExists(commentsDir)) {\n await fs.rm(commentsDir, { recursive: true });\n console.log(\" ✓ Removed .apostil/ directory\");\n }\n\n // 5. Remove from .gitignore\n const gitignorePath = path.join(cwd, \".gitignore\");\n try {\n let gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n gitignore = gitignore.replace(/\\n?# Apostil comments\\n\\.apostil\\/\\n?/g, \"\");\n await fs.writeFile(gitignorePath, gitignore, \"utf-8\");\n console.log(\" ✓ Cleaned .gitignore\");\n } catch {}\n\n console.log(`\n Done! Now run: npm uninstall apostil\n`);\n}\n\n// --- Wrapper component template ---\n\nfunction getWrapperComponent(mode: Mode): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (process.env.NODE_ENV !== \"development\") {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set NEXT_PUBLIC_APOSTIL=true to force on in any environment\n const forceOn = process.env.NEXT_PUBLIC_APOSTIL === \"true\";\n if (process.env.NODE_ENV === \"production\" && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set NEXT_PUBLIC_APOSTIL=false to disable\n if (process.env.NEXT_PUBLIC_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n return `\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\nimport \"apostil/styles.css\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n const pathname = usePathname();\n const pageId = pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";\n\n return (\n <ApostilProvider pageId={pageId}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\n// --- Layout injection ---\n\nasync function injectIntoLayout(appDir: string, useSrc: boolean): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n // Skip if already injected\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Layout already has <ApostilWrapper>\");\n return false;\n }\n\n // Add import at the top (after existing imports or \"use\" directives)\n const importPath = useSrc ? \"@/components/apostil-wrapper\" : \"../components/apostil-wrapper\";\n const importLine = `import { ApostilWrapper } from \"${importPath}\";\\n`;\n\n // Find the last import statement and insert after it\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n // No imports found, add at the top (after \"use client\" or \"use server\" if present)\n const useDirective = content.match(/^[\"']use (client|server)[\"'];?\\n/);\n const insertAt = useDirective ? useDirective[0].length : 0;\n content = content.slice(0, insertAt) + importLine + content.slice(insertAt);\n }\n\n // Wrap {children} with <ApostilWrapper>\n // Handle both {children} and { children } patterns inside <body>\n const bodyChildrenRegex = /(<body[^>]*>)([\\s\\S]*?)(\\{[\\s]*children[\\s]*\\})([\\s\\S]*?)(<\\/body>)/;\n const bodyMatch = content.match(bodyChildrenRegex);\n\n if (bodyMatch) {\n content = content.replace(\n bodyChildrenRegex,\n `$1$2<ApostilWrapper>$3</ApostilWrapper>$4$5`\n );\n } else {\n // Fallback: try to find {children} anywhere and wrap it\n const childrenRegex = /(\\{[\\s]*children[\\s]*\\})/;\n if (childrenRegex.test(content)) {\n content = content.replace(childrenRegex, `<ApostilWrapper>$1</ApostilWrapper>`);\n } else {\n console.log(\" ⚠ Could not find {children} in layout — add <ApostilWrapper> manually\");\n return false;\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\nasync function removeFromLayout(appDir: string): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n if (!content.includes(\"ApostilWrapper\")) return false;\n\n // Remove import line\n content = content.replace(/import\\s*\\{[^}]*ApostilWrapper[^}]*\\}\\s*from\\s*[\"'][^\"']+[\"'];?\\n?/g, \"\");\n\n // Unwrap <ApostilWrapper>{children}</ApostilWrapper>\n content = content.replace(/<ApostilWrapper>([\\s\\S]*?)<\\/ApostilWrapper>/g, \"$1\");\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\n// --- Helpers ---\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n for (const candidate of [\"src/app\", \"app\"]) {\n const dir = path.join(cwd, candidate);\n try {\n const stat = await fs.stat(dir);\n if (stat.isDirectory()) return dir;\n } catch {}\n }\n return null;\n}\n\nasync function findLayout(appDir: string): Promise<string | null> {\n for (const ext of [\"tsx\", \"jsx\", \"ts\", \"js\"]) {\n const file = path.join(appDir, `layout.${ext}`);\n if (await fileExists(file)) return file;\n }\n return null;\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction rel(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\ntype Mode = \"personal\" | \"dev\" | \"public\";\ntype Framework = \"nextjs\" | \"vite\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (command === \"init\") {\n const mode = parseMode(args.slice(1));\n init(mode);\n} else if (command === \"remove\") {\n remove();\n} else if (command === \"help\" || command === \"--help\" || command === \"-h\" || !command) {\n printHelp();\n} else {\n console.log(` Unknown command: ${command}\\n`);\n printHelp();\n}\n\nfunction parseMode(flags: string[]): Mode {\n if (flags.includes(\"--dev\")) return \"dev\";\n if (flags.includes(\"--public\")) return \"public\";\n return \"personal\";\n}\n\nfunction printHelp() {\n console.log(`\n apostil — Lightweight, Figma-like commenting for React\n\n Usage:\n npx apostil init [mode] Set up apostil in your project\n npx apostil remove Remove apostil from your project\n npx apostil help Show this help\n\n Modes:\n (default) Personal — local dev only, comments gitignored\n --dev Dev + staging — comments gitignored, env-controlled\n --public All environments — comments committed to git\n\n Supported frameworks: Next.js (App Router), Vite + React\n Framework is auto-detected from your project.\n`);\n}\n\n// --- Framework detection ---\n\nasync function detectFramework(cwd: string): Promise<Framework | null> {\n // Check config files first\n for (const name of [\"next.config.js\", \"next.config.ts\", \"next.config.mjs\"]) {\n if (await fileExists(path.join(cwd, name))) return \"nextjs\";\n }\n for (const name of [\"vite.config.js\", \"vite.config.ts\", \"vite.config.mjs\"]) {\n if (await fileExists(path.join(cwd, name))) return \"vite\";\n }\n\n // Fallback: check package.json dependencies\n try {\n const pkg = JSON.parse(await fs.readFile(path.join(cwd, \"package.json\"), \"utf-8\"));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps[\"next\"]) return \"nextjs\";\n if (deps[\"vite\"]) return \"vite\";\n } catch {}\n\n return null;\n}\n\n// --- Init ---\n\nasync function init(mode: Mode) {\n const cwd = process.cwd();\n const framework = await detectFramework(cwd);\n\n if (!framework) {\n console.log(\" Could not detect your framework.\");\n console.log(\" Apostil supports Next.js (App Router) and Vite + React.\");\n console.log(\" Make sure you're running this from your project root.\\n\");\n process.exit(1);\n }\n\n console.log(`\\n Detected: ${framework === \"nextjs\" ? \"Next.js\" : \"Vite + React\"}`);\n\n if (framework === \"nextjs\") {\n await initNextjs(cwd, mode);\n } else {\n await initVite(cwd, mode);\n }\n}\n\n// --- Next.js init ---\n\nasync function initNextjs(cwd: string, mode: Mode) {\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.log(\" Could not find a Next.js app/ directory.\");\n console.log(\" Make sure you're using the App Router.\\n\");\n process.exit(1);\n }\n\n const useSrc = appDir.includes(\"src/app\");\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(` Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create API route\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n const apiFile = path.join(apiDir, \"route.ts\");\n if (await fileExists(apiFile)) {\n console.log(\" ✓ API route already exists\");\n } else {\n await fs.mkdir(apiDir, { recursive: true });\n await fs.writeFile(\n apiFile,\n `export { GET, POST } from \"apostil/adapters/nextjs\";\\n`,\n \"utf-8\"\n );\n console.log(` ✓ Created ${rel(cwd, apiFile)}`);\n }\n\n // 2. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n await fs.writeFile(wrapperFile, getNextjsWrapper(mode), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n // 3. Create .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n await fs.mkdir(commentsDir, { recursive: true });\n console.log(\" ✓ Created .apostil/ directory\");\n\n // 4. Handle .gitignore\n await handleGitignore(cwd, mode);\n\n // 5. Inject wrapper into root layout\n const layoutInjected = await injectIntoNextjsLayout(appDir, useSrc);\n if (layoutInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to root layout\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\n// --- Vite init ---\n\nasync function initVite(cwd: string, mode: Mode) {\n const useSrc = await fileExists(path.join(cwd, \"src\"));\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(` Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n const hasRouter = await hasReactRouter(cwd);\n await fs.writeFile(wrapperFile, getViteWrapper(mode, hasRouter), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n console.log(\" ✓ Using localStorage adapter (comments stored in browser)\");\n\n // 3. Inject wrapper into entry point\n const entryInjected = await injectIntoViteEntry(cwd, useSrc);\n if (entryInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to app entry\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\n// --- Wrapper templates ---\n\nfunction getNextjsWrapper(mode: Mode): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (process.env.NODE_ENV !== \"development\") {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set NEXT_PUBLIC_APOSTIL=true to force on in any environment\n const forceOn = process.env.NEXT_PUBLIC_APOSTIL === \"true\";\n if (process.env.NODE_ENV === \"production\" && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set NEXT_PUBLIC_APOSTIL=false to disable\n if (process.env.NEXT_PUBLIC_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n return `\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\nimport \"apostil/styles.css\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n const pathname = usePathname();\n const pageId = pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";\n\n return (\n <ApostilProvider pageId={pageId}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\nasync function hasReactRouter(cwd: string): Promise<boolean> {\n try {\n const pkg = JSON.parse(await fs.readFile(path.join(cwd, \"package.json\"), \"utf-8\"));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n return !!deps[\"react-router-dom\"] || !!deps[\"react-router\"];\n } catch {\n return false;\n }\n}\n\n// No \"use client\" directive — Vite doesn't use React Server Components\nfunction getViteWrapper(mode: Mode, hasRouter: boolean): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (import.meta.env.PROD) {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set VITE_APOSTIL=true to force on in any environment\n const forceOn = import.meta.env.VITE_APOSTIL === \"true\";\n if (import.meta.env.PROD && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set VITE_APOSTIL=false to disable\n if (import.meta.env.VITE_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n const routerImport = hasRouter\n ? `import { useLocation } from \"react-router-dom\";\\n`\n : \"\";\n\n const pageIdLogic = hasRouter\n ? ` const location = useLocation();\n const pageId = location.pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";`\n : ` const pageId = window.location.pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";`;\n\n return `${routerImport}import {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\nimport { localStorageAdapter } from \"apostil/adapters/localStorage\";\nimport \"apostil/styles.css\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n${pageIdLogic}\n\n return (\n <ApostilProvider pageId={pageId} storage={localStorageAdapter}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\n// --- Gitignore ---\n\nasync function handleGitignore(cwd: string, mode: Mode) {\n const gitignorePath = path.join(cwd, \".gitignore\");\n let gitignore = \"\";\n try {\n gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {}\n\n if (mode === \"public\") {\n if (gitignore.includes(\".apostil\")) {\n console.log(\" ✓ .gitignore unchanged (public mode — comments will be committed)\");\n } else {\n console.log(\" ✓ Comments will be committed to git (public mode)\");\n }\n } else {\n if (!gitignore.includes(\".apostil\")) {\n const entry = \"\\n# Apostil comments\\n.apostil/\\n\";\n await fs.appendFile(gitignorePath, entry, \"utf-8\");\n console.log(\" ✓ Added .apostil/ to .gitignore\");\n } else {\n console.log(\" ✓ .gitignore already configured\");\n }\n }\n}\n\n// --- Next.js layout injection ---\n\nasync function injectIntoNextjsLayout(appDir: string, useSrc: boolean): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Layout already has <ApostilWrapper>\");\n return false;\n }\n\n const importPath = useSrc ? \"@/components/apostil-wrapper\" : \"../components/apostil-wrapper\";\n const importLine = `import { ApostilWrapper } from \"${importPath}\";\\n`;\n\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n const useDirective = content.match(/^[\"']use (client|server)[\"'];?\\n/);\n const insertAt = useDirective ? useDirective[0].length : 0;\n content = content.slice(0, insertAt) + importLine + content.slice(insertAt);\n }\n\n const bodyChildrenRegex = /(<body[^>]*>)([\\s\\S]*?)(\\{[\\s]*children[\\s]*\\})([\\s\\S]*?)(<\\/body>)/;\n const bodyMatch = content.match(bodyChildrenRegex);\n\n if (bodyMatch) {\n content = content.replace(\n bodyChildrenRegex,\n `$1$2<ApostilWrapper>$3</ApostilWrapper>$4$5`\n );\n } else {\n const childrenRegex = /(\\{[\\s]*children[\\s]*\\})/;\n if (childrenRegex.test(content)) {\n content = content.replace(childrenRegex, `<ApostilWrapper>$1</ApostilWrapper>`);\n } else {\n console.log(\" ⚠ Could not find {children} in layout — add <ApostilWrapper> manually\");\n return false;\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\n// --- Vite entry injection ---\n\nasync function injectIntoViteEntry(cwd: string, useSrc: boolean): Promise<boolean> {\n const baseDir = useSrc ? path.join(cwd, \"src\") : cwd;\n\n // Inject into main.tsx/jsx — wrap <App /> which is a reliable, unique target\n for (const ext of [\"tsx\", \"jsx\"]) {\n const mainFile = path.join(baseDir, `main.${ext}`);\n if (await fileExists(mainFile)) {\n return await injectIntoViteMain(mainFile);\n }\n }\n\n console.log(\" ⚠ Could not find main.tsx or main.jsx — add <ApostilWrapper> manually\");\n return false;\n}\n\nasync function injectIntoViteMain(mainFile: string, wrapperFile?: string): Promise<boolean> {\n let content = await fs.readFile(mainFile, \"utf-8\");\n\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Entry already has <ApostilWrapper>\");\n return false;\n }\n\n // Compute relative import path from main file to wrapper\n const mainDir = path.dirname(mainFile);\n const wrapperPath = wrapperFile ?? path.join(mainDir, \"components\", \"apostil-wrapper.tsx\");\n let relativePath = path.relative(mainDir, wrapperPath).replace(/\\.tsx$/, \"\");\n if (!relativePath.startsWith(\".\")) relativePath = \"./\" + relativePath;\n const importLine = `import { ApostilWrapper } from \"${relativePath}\";\\n`;\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n content = importLine + content;\n }\n\n // Wrap <App /> with <ApostilWrapper>\n content = content.replace(/<App\\s*\\/>/, `<ApostilWrapper><App /></ApostilWrapper>`);\n\n await fs.writeFile(mainFile, content, \"utf-8\");\n return true;\n}\n\n// --- Remove ---\n\nasync function remove() {\n const cwd = process.cwd();\n const framework = await detectFramework(cwd);\n\n console.log(\"\\n Removing apostil...\\n\");\n\n if (framework === \"nextjs\") {\n await removeNextjs(cwd);\n } else if (framework === \"vite\") {\n await removeVite(cwd);\n } else {\n // Try both\n await removeNextjs(cwd);\n await removeVite(cwd);\n }\n\n // Remove .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n if (await fileExists(commentsDir)) {\n await fs.rm(commentsDir, { recursive: true });\n console.log(\" ✓ Removed .apostil/ directory\");\n }\n\n // Remove from .gitignore\n const gitignorePath = path.join(cwd, \".gitignore\");\n try {\n let gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n gitignore = gitignore.replace(/\\n?# Apostil comments\\n\\.apostil\\/\\n?/g, \"\");\n await fs.writeFile(gitignorePath, gitignore, \"utf-8\");\n console.log(\" ✓ Cleaned .gitignore\");\n } catch {}\n\n console.log(`\n Done! Now run: npm uninstall apostil\n`);\n}\n\nasync function removeNextjs(cwd: string) {\n const appDir = await findAppDir(cwd);\n const useSrc = appDir?.includes(\"src/app\") ?? false;\n\n // Remove API route\n if (appDir) {\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n if (await fileExists(path.join(apiDir, \"route.ts\"))) {\n await fs.rm(apiDir, { recursive: true });\n console.log(\" ✓ Removed API route\");\n }\n }\n\n // Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // Remove wrapper from layout\n if (appDir) {\n const layoutPath = await findLayout(appDir);\n if (layoutPath) {\n const unwrapped = await removeWrapperFromFile(layoutPath);\n if (unwrapped) {\n console.log(\" ✓ Removed <ApostilWrapper> from root layout\");\n }\n }\n }\n}\n\nasync function removeVite(cwd: string) {\n const useSrc = await fileExists(path.join(cwd, \"src\"));\n const baseDir = useSrc ? path.join(cwd, \"src\") : cwd;\n\n // Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // Remove wrapper from App.tsx or main.tsx\n for (const name of [\"App.tsx\", \"App.jsx\", \"main.tsx\", \"main.jsx\"]) {\n const file = path.join(baseDir, name);\n if (await fileExists(file)) {\n const unwrapped = await removeWrapperFromFile(file);\n if (unwrapped) {\n console.log(` ✓ Removed <ApostilWrapper> from ${name}`);\n }\n }\n }\n}\n\nasync function removeWrapperFromFile(filePath: string): Promise<boolean> {\n let content = await fs.readFile(filePath, \"utf-8\");\n if (!content.includes(\"ApostilWrapper\")) return false;\n\n // Remove import line\n content = content.replace(/import\\s*\\{[^}]*ApostilWrapper[^}]*\\}\\s*from\\s*[\"'][^\"']+[\"'];?\\n?/g, \"\");\n\n // Unwrap <ApostilWrapper>...</ApostilWrapper>\n content = content.replace(/<ApostilWrapper>([\\s\\S]*?)<\\/ApostilWrapper>/g, \"$1\");\n\n await fs.writeFile(filePath, content, \"utf-8\");\n return true;\n}\n\n// --- Helpers ---\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n for (const candidate of [\"src/app\", \"app\"]) {\n const dir = path.join(cwd, candidate);\n try {\n const stat = await fs.stat(dir);\n if (stat.isDirectory()) return dir;\n } catch {}\n }\n return null;\n}\n\nasync function findLayout(appDir: string): Promise<string | null> {\n for (const ext of [\"tsx\", \"jsx\", \"ts\", \"js\"]) {\n const file = path.join(appDir, `layout.${ext}`);\n if (await fileExists(file)) return file;\n }\n return null;\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction rel(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath);\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -1052,11 +1052,6 @@ function CommentOverlay() {
1052
1052
  },
1053
1053
  [pendingPin, addThread]
1054
1054
  );
1055
- const cancelPending = (0, import_react6.useCallback)(() => {
1056
- setPendingPin(null);
1057
- setPendingPixel(null);
1058
- setCommentMode(false);
1059
- }, [setCommentMode]);
1060
1055
  (0, import_react6.useEffect)(() => {
1061
1056
  if (!pendingPixel || !overlayRef.current) return;
1062
1057
  const overlayRect = overlayRef.current.getBoundingClientRect();
@@ -1069,16 +1064,16 @@ function CommentOverlay() {
1069
1064
  }, [pendingPixel, overlayRef]);
1070
1065
  (0, import_react6.useEffect)(() => {
1071
1066
  const hash = window.location.hash;
1072
- console.log("[apostil] hash check:", hash, "threads:", threads.length, threads.map((t) => t.id));
1067
+ debug.log("hash check:", hash, "threads:", threads.length);
1073
1068
  if (!hash.startsWith("#apostil-")) return;
1074
1069
  const threadId = hash.replace("#apostil-", "");
1075
- console.log("[apostil] looking for thread:", threadId);
1070
+ debug.log("looking for thread:", threadId);
1076
1071
  if (threads.length === 0) {
1077
- console.log("[apostil] no threads loaded yet, waiting...");
1072
+ debug.log("no threads loaded yet, waiting...");
1078
1073
  return;
1079
1074
  }
1080
1075
  const found = threads.find((t) => t.id === threadId);
1081
- console.log("[apostil] found thread:", found ? "yes" : "no");
1076
+ debug.log("found thread:", found ? "yes" : "no");
1082
1077
  if (found) {
1083
1078
  setActiveThreadId(threadId);
1084
1079
  window.history.replaceState(null, "", window.location.pathname);