@antigenic-oss/paint 0.2.8 → 0.3.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.
@@ -1,23 +1,37 @@
1
1
  import type { Metadata } from 'next'
2
2
  import Link from 'next/link'
3
3
  import {
4
- CodeBlock,
5
4
  FaqAccordion,
6
5
  FaqSection,
7
- FrameworkAccordion,
8
- FrameworkSection,
9
6
  Sidebar,
10
7
  } from './DocsClient'
11
8
 
12
9
  export const metadata: Metadata = {
13
- title: 'pAInt — Setup Guide',
10
+ title: 'Setup Guide',
14
11
  description:
15
- 'Framework-specific setup instructions for connecting pAInt to your localhost project.',
12
+ 'Framework-specific setup instructions for connecting pAInt to localhost projects, including proxy, bridge, and terminal-assisted workflows.',
13
+ keywords: [
14
+ 'pAInt setup',
15
+ 'localhost visual editor setup',
16
+ 'bridge server setup',
17
+ 'terminal server setup',
18
+ 'Next.js inspector script',
19
+ 'Claude Code changelog workflow',
20
+ ],
21
+ openGraph: {
22
+ title: 'pAInt Setup Guide',
23
+ description:
24
+ 'Connect pAInt to your localhost app with framework-specific instructions and efficient AI handoff workflows.',
25
+ type: 'article',
26
+ },
27
+ twitter: {
28
+ card: 'summary',
29
+ title: 'pAInt Setup Guide',
30
+ description:
31
+ 'Configure pAInt quickly for localhost projects and ship cleaner AI-assisted edits.',
32
+ },
16
33
  }
17
34
 
18
- const SCRIPT_TAG =
19
- '<script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>'
20
-
21
35
  export default function DocsPage() {
22
36
  return (
23
37
  <div
@@ -72,11 +86,17 @@ export default function DocsPage() {
72
86
  </strong>
73
87
  , the editor loads your page through a{' '}
74
88
  <strong style={{ color: 'var(--text-primary)' }}>
75
- built-in reverse proxy
89
+ Service Worker proxy
76
90
  </strong>
77
- . The proxy fetches your HTML, strips client-side scripts (to
78
- prevent routing conflicts), and injects a lightweight inspector
79
- script. This inspector communicates with the editor via{' '}
91
+ . The proxy intercepts requests, fetches your page from
92
+ localhost, injects a lightweight inspector script, and strips
93
+ security headers that would block the iframe all happening
94
+ in the browser with no server-side proxying needed. Your
95
+ page&apos;s scripts and client-side rendering work normally,
96
+ so you see the fully interactive version of your site.
97
+ </p>
98
+ <p style={{ ...bodyText, marginTop: '0.75rem' }}>
99
+ The inspector communicates with the editor via{' '}
80
100
  <code style={inlineCodeStyle}>postMessage</code> — reporting
81
101
  element metadata when you hover or click, and applying style
82
102
  previews in real time.
@@ -89,10 +109,11 @@ export default function DocsPage() {
89
109
  actual source files.
90
110
  </p>
91
111
  <p style={{ ...bodyText, marginTop: '0.75rem' }}>
92
- If automatic injection fails (e.g., non-standard HTML
93
- responses), a banner will prompt you to add the script tag
94
- manually. The framework guides below show exactly where to place
95
- it.
112
+ <strong style={{ color: 'var(--success)' }}>
113
+ No script tags needed
114
+ </strong>{' '}
115
+ — the proxy handles everything automatically. Just click
116
+ Connect and start editing.
96
117
  </p>
97
118
  </Section>
98
119
 
@@ -187,402 +208,78 @@ export default function DocsPage() {
187
208
  </Section>
188
209
 
189
210
  {/* Framework Guides */}
190
- <Section id="framework-guides" title="Framework Guides">
191
- <p style={{ ...bodyText, marginBottom: '1rem' }}>
192
- If the automatic connection doesn&apos;t detect the inspector
193
- within 5 seconds, add the script tag manually to your project.
194
- Select your framework below for the exact file and placement.
211
+ <Section id="framework-guides" title="Framework Compatibility">
212
+ <p style={bodyText}>
213
+ pAInt works with any framework out of the box — no script
214
+ tags or configuration needed. The Service Worker proxy
215
+ automatically handles inspector injection for all projects.
195
216
  </p>
196
- <div
197
- className="flex items-center gap-2 px-4 py-3 rounded-md mb-4 text-sm"
198
- style={{
199
- background: 'var(--accent-bg)',
200
- border: '1px solid var(--accent)',
201
- color: 'var(--text-secondary)',
202
- }}
203
- >
204
- <span style={{ color: 'var(--accent)' }}>Note:</span>
205
- <span>
206
- The snippets below use the hosted URL. If running pAInt
207
- locally, replace{' '}
208
- <code style={{ ...inlineCodeStyle, fontSize: '0.8em' }}>
209
- https://dev-editor-flow.vercel.app
210
- </code>{' '}
211
- with{' '}
212
- <code style={{ ...inlineCodeStyle, fontSize: '0.8em' }}>
213
- http://localhost:4000
214
- </code>{' '}
215
- (or your custom port).
216
- </span>
217
+ <p style={{ ...bodyText, marginTop: '0.75rem' }}>
218
+ Tested and confirmed working with:
219
+ </p>
220
+ <div className="flex flex-col gap-2 mt-4">
221
+ <CompatItem icon="&#9650;" name="Next.js" detail="App Router & Pages Router" />
222
+ <CompatItem icon="&#9889;" name="Vite + React" detail="Including React Router, TanStack, etc." />
223
+ <CompatItem icon="&#9883;" name="Create React App" detail="Standard and ejected setups" />
224
+ <CompatItem icon="&#128241;" name="React Native / Expo Web" detail="Expo Router and custom web entry" />
225
+ <CompatItem icon="&#9899;" name="Vue / Nuxt" detail="Vue 3 (Vite) and Nuxt 3" />
226
+ <CompatItem icon="&#127793;" name="Svelte / SvelteKit" detail="SvelteKit and plain Svelte + Vite" />
227
+ <CompatItem icon="&#128196;" name="Plain HTML" detail="Static sites and vanilla JS" />
217
228
  </div>
218
- <FrameworkAccordion>
219
- {/* Next.js */}
220
- <FrameworkSection id="nextjs" title="Next.js" icon="&#9650;">
221
- <div>
222
- <h4
223
- className="text-base font-medium mb-1"
224
- style={{ color: 'var(--text-primary)' }}
225
- >
226
- App Router
227
- </h4>
228
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
229
- Add to <code style={inlineCodeStyle}>app/layout.tsx</code>
230
- </p>
231
- <CodeBlock
232
- language="tsx"
233
- copyText={SCRIPT_TAG}
234
- code={`// app/layout.tsx
235
- export default function RootLayout({ children }: { children: React.ReactNode }) {
236
- return (
237
- <html lang="en">
238
- <body>
239
- {children}
240
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
241
- </body>
242
- </html>
243
- );
244
- }`}
245
- />
246
- </div>
247
- <div>
248
- <h4
249
- className="text-base font-medium mb-1"
250
- style={{ color: 'var(--text-primary)' }}
251
- >
252
- Pages Router
253
- </h4>
254
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
255
- Add to{' '}
256
- <code style={inlineCodeStyle}>pages/_document.tsx</code>
257
- </p>
258
- <CodeBlock
259
- language="tsx"
260
- copyText={SCRIPT_TAG}
261
- code={`// pages/_document.tsx
262
- import { Html, Head, Main, NextScript } from 'next/document';
263
-
264
- export default function Document() {
265
- return (
266
- <Html>
267
- <Head />
268
- <body>
269
- <Main />
270
- <NextScript />
271
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
272
- </body>
273
- </Html>
274
- );
275
- }`}
276
- />
277
- </div>
278
- </FrameworkSection>
279
-
280
- {/* Vite + React */}
281
- <FrameworkSection
282
- id="vite-react"
283
- title="Vite + React"
284
- icon="&#9889;"
285
- >
286
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
287
- Add to <code style={inlineCodeStyle}>index.html</code>{' '}
288
- (project root)
289
- </p>
290
- <CodeBlock
291
- language="html"
292
- copyText={SCRIPT_TAG}
293
- code={`<!-- index.html -->
294
- <!DOCTYPE html>
295
- <html lang="en">
296
- <head>
297
- <meta charset="UTF-8" />
298
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
299
- <title>My App</title>
300
- </head>
301
- <body>
302
- <div id="root"></div>
303
- <script type="module" src="/src/main.tsx"></script>
304
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
305
- </body>
306
- </html>`}
307
- />
308
- </FrameworkSection>
309
-
310
- {/* Create React App */}
311
- <FrameworkSection
312
- id="create-react-app"
313
- title="Create React App"
314
- icon="&#9883;"
315
- >
316
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
317
- Add to{' '}
318
- <code style={inlineCodeStyle}>public/index.html</code>
319
- </p>
320
- <CodeBlock
321
- language="html"
322
- copyText={SCRIPT_TAG}
323
- code={`<!-- public/index.html -->
324
- <!DOCTYPE html>
325
- <html lang="en">
326
- <head>
327
- <meta charset="utf-8" />
328
- <meta name="viewport" content="width=device-width, initial-scale=1" />
329
- <title>My App</title>
330
- </head>
331
- <body>
332
- <div id="root"></div>
333
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
334
- </body>
335
- </html>`}
336
- />
337
- </FrameworkSection>
338
-
339
- {/* Plain HTML */}
340
- <FrameworkSection
341
- id="plain-html"
342
- title="Plain HTML"
343
- icon="&#128196;"
344
- >
345
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
346
- Add before{' '}
347
- <code style={inlineCodeStyle}>&lt;/body&gt;</code> in any{' '}
348
- <code style={inlineCodeStyle}>.html</code> file
349
- </p>
350
- <CodeBlock
351
- language="html"
352
- copyText={SCRIPT_TAG}
353
- code={`<!DOCTYPE html>
354
- <html>
355
- <head>
356
- <title>My Page</title>
357
- </head>
358
- <body>
359
- <h1>Hello</h1>
360
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
361
- </body>
362
- </html>`}
363
- />
364
- </FrameworkSection>
365
-
366
- {/* React Native / Expo Web */}
367
- <FrameworkSection
368
- id="react-native-expo"
369
- title="React Native / Expo Web"
370
- icon="&#128241;"
371
- >
372
- <div>
373
- <h4
374
- className="text-base font-medium mb-1"
375
- style={{ color: 'var(--text-primary)' }}
376
- >
377
- Expo Router
378
- </h4>
379
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
380
- Add to{' '}
381
- <code style={inlineCodeStyle}>app/_layout.tsx</code> using{' '}
382
- a <code style={inlineCodeStyle}>&lt;Script&gt;</code>{' '}
383
- component or the web{' '}
384
- <code style={inlineCodeStyle}>index.html</code>
385
- </p>
386
- <CodeBlock
387
- language="tsx"
388
- copyText={SCRIPT_TAG}
389
- code={`// app/_layout.tsx
390
- import { Slot } from 'expo-router';
391
- import { Platform } from 'react-native';
392
- import { useEffect } from 'react';
393
-
394
- export default function RootLayout() {
395
- useEffect(() => {
396
- if (Platform.OS === 'web') {
397
- const script = document.createElement('script');
398
- script.src = 'https://dev-editor-flow.vercel.app/dev-editor-inspector.js';
399
- document.body.appendChild(script);
400
- }
401
- }, []);
402
-
403
- return <Slot />;
404
- }`}
405
- />
406
- </div>
407
- <div>
408
- <h4
409
- className="text-base font-medium mb-1"
410
- style={{ color: 'var(--text-primary)' }}
411
- >
412
- Custom <code style={inlineCodeStyle}>web/index.html</code>
413
- </h4>
414
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
415
- If your Expo project has a custom web entry point
416
- </p>
417
- <CodeBlock
418
- language="html"
419
- copyText={SCRIPT_TAG}
420
- code={`<!-- web/index.html -->
421
- <!DOCTYPE html>
422
- <html>
423
- <head>
424
- <meta charset="utf-8" />
425
- <meta name="viewport" content="width=device-width, initial-scale=1" />
426
- </head>
427
- <body>
428
- <div id="root"></div>
429
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
430
- </body>
431
- </html>`}
432
- />
433
- </div>
434
- </FrameworkSection>
435
-
436
- {/* Vue / Nuxt */}
437
- <FrameworkSection
438
- id="vue-nuxt"
439
- title="Vue / Nuxt"
440
- icon="&#9899;"
441
- >
442
- <div>
443
- <h4
444
- className="text-base font-medium mb-1"
445
- style={{ color: 'var(--text-primary)' }}
446
- >
447
- Vue (Vite)
448
- </h4>
449
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
450
- Add to <code style={inlineCodeStyle}>index.html</code>{' '}
451
- (project root)
452
- </p>
453
- <CodeBlock
454
- language="html"
455
- copyText={SCRIPT_TAG}
456
- code={`<!-- index.html -->
457
- <!DOCTYPE html>
458
- <html lang="en">
459
- <head>
460
- <meta charset="UTF-8" />
461
- <title>My Vue App</title>
462
- </head>
463
- <body>
464
- <div id="app"></div>
465
- <script type="module" src="/src/main.ts"></script>
466
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
467
- </body>
468
- </html>`}
469
- />
470
- </div>
471
- <div>
472
- <h4
473
- className="text-base font-medium mb-1"
474
- style={{ color: 'var(--text-primary)' }}
475
- >
476
- Nuxt 3
477
- </h4>
478
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
479
- Add via{' '}
480
- <code style={inlineCodeStyle}>nuxt.config.ts</code> or{' '}
481
- <code style={inlineCodeStyle}>app.html</code>
482
- </p>
483
- <CodeBlock
484
- language="ts"
485
- copyText={`app: {\n head: {\n script: [{ src: 'https://dev-editor-flow.vercel.app/dev-editor-inspector.js' }]\n }\n}`}
486
- code={`// nuxt.config.ts
487
- export default defineNuxtConfig({
488
- app: {
489
- head: {
490
- script: [
491
- { src: 'https://dev-editor-flow.vercel.app/dev-editor-inspector.js' }
492
- ]
493
- }
494
- }
495
- });`}
496
- />
497
- </div>
498
- </FrameworkSection>
499
-
500
- {/* Svelte / SvelteKit */}
501
- <FrameworkSection
502
- id="svelte-sveltekit"
503
- title="Svelte / SvelteKit"
504
- icon="&#127793;"
505
- >
506
- <p style={{ ...mutedText, marginBottom: '0.5rem' }}>
507
- Add to <code style={inlineCodeStyle}>src/app.html</code>
508
- </p>
509
- <CodeBlock
510
- language="html"
511
- copyText={SCRIPT_TAG}
512
- code={`<!-- src/app.html -->
513
- <!DOCTYPE html>
514
- <html lang="en">
515
- <head>
516
- <meta charset="utf-8" />
517
- <meta name="viewport" content="width=device-width, initial-scale=1" />
518
- %sveltekit.head%
519
- </head>
520
- <body data-sveltekit-preload-data="hover">
521
- <div style="display: contents">%sveltekit.body%</div>
522
- <script src="https://dev-editor-flow.vercel.app/dev-editor-inspector.js"></script>
523
- </body>
524
- </html>`}
525
- />
526
- </FrameworkSection>
527
- </FrameworkAccordion>
229
+ <p style={{ ...bodyText, marginTop: '1rem' }}>
230
+ Just start your dev server, open pAInt, select the port,
231
+ and click{' '}
232
+ <strong style={{ color: 'var(--text-primary)' }}>
233
+ Connect
234
+ </strong>
235
+ . The proxy preserves your page&apos;s scripts and
236
+ client-side rendering, so interactive features like 3D scenes
237
+ (Spline, Three.js), animations (GSAP, Framer Motion), and
238
+ client-side routing all work normally.
239
+ </p>
528
240
  </Section>
529
241
 
530
242
  {/* Troubleshooting */}
531
243
  <Section id="troubleshooting" title="Troubleshooting">
532
244
  <div className="flex flex-col gap-4">
533
- <TroubleshootItem title="Inspector script not detected">
245
+ <TroubleshootItem title="Stuck on &quot;Connecting&quot;">
534
246
  <p>
535
- If the banner appears after 5 seconds, the automatic proxy
536
- injection didn&apos;t work for your setup. Add the script
537
- tag manually using the framework guides above. Make sure
538
- pAInt is running on the port shown in the script URL.
247
+ If the editor stays in &quot;Connecting&quot; state, your
248
+ browser may be caching an old Service Worker. Open DevTools
249
+ &rarr; Application &rarr; Service Workers, unregister any
250
+ workers for <code style={inlineCodeStyle}>localhost:4000</code>,
251
+ clear Cache Storage, then hard refresh (Cmd+Shift+R).
539
252
  </p>
540
253
  </TroubleshootItem>
541
254
 
542
255
  <TroubleshootItem title="CORS or Cross-Origin errors">
543
256
  <p>
544
- pAInt proxy runs on a different port than your project. If
545
- your project sets strict CORS headers, the proxy may be
546
- blocked. The automatic method handles this by serving
547
- everything from the same origin. For the manual method,
548
- ensure your dev server allows requests from{' '}
549
- <code style={inlineCodeStyle}>localhost:4000</code>.
550
- </p>
551
- </TroubleshootItem>
552
-
553
- <TroubleshootItem title="COEP / COOP headers blocking the iframe">
554
- <p>
555
- Some frameworks set{' '}
556
- <code style={inlineCodeStyle}>
557
- Cross-Origin-Embedder-Policy
558
- </code>{' '}
559
- or{' '}
560
- <code style={inlineCodeStyle}>
561
- Cross-Origin-Opener-Policy
562
- </code>{' '}
563
- headers that prevent loading in an iframe. Check your server
564
- config or middleware and relax these headers in development.
257
+ The Service Worker proxy serves everything from the same
258
+ origin, which handles most CORS issues automatically. If
259
+ your project makes API calls to external services during
260
+ render, those requests are not proxied. This typically
261
+ doesn&apos;t affect visual editing.
565
262
  </p>
566
263
  </TroubleshootItem>
567
264
 
568
- <TroubleshootItem title="Infinite iframe reload">
265
+ <TroubleshootItem title="Page looks different or broken">
569
266
  <p>
570
- This happens when the target page&apos;s client-side router
571
- detects the proxy URL and redirects. pAInt&apos;s proxy
572
- strips <code style={inlineCodeStyle}>&lt;script&gt;</code>{' '}
573
- tags to prevent this. If you still see reloads, check that
574
- no inline scripts or meta-refresh tags are causing
575
- navigation.
267
+ The proxy preserves all scripts and client-side rendering.
268
+ If something looks off, try a hard refresh to ensure the
269
+ latest Service Worker is active. If the issue persists,
270
+ check the browser console for errors some pages with
271
+ very strict CSP headers may need those headers relaxed in
272
+ development.
576
273
  </p>
577
274
  </TroubleshootItem>
578
275
 
579
- <TroubleshootItem title="Styles look different in the editor">
276
+ <TroubleshootItem title="Target dev server not reachable">
580
277
  <p>
581
- The proxy serves SSR HTML with CSS intact but strips
582
- JavaScript. If your styles depend on client-side JS (e.g.,
583
- CSS-in-JS runtime injection), some styles may be missing.
584
- Use the manual script method with your full dev server if
585
- CSS-in-JS is critical.
278
+ Make sure your project&apos;s dev server is running on the
279
+ port you selected. The proxy connects to{' '}
280
+ <code style={inlineCodeStyle}>localhost:&lt;port&gt;</code>{' '}
281
+ from the browser, so the server must be accessible from
282
+ your machine.
586
283
  </p>
587
284
  </TroubleshootItem>
588
285
  </div>
@@ -592,17 +289,13 @@ export default defineNuxtConfig({
592
289
  <Section id="faq" title="FAQ">
593
290
  <FaqAccordion>
594
291
  <FaqSection
595
- id="faq-safe"
596
- question="Is it safe to add the inspector script to my project?"
292
+ id="faq-no-setup"
293
+ question="Do I need to add any script tags to my project?"
597
294
  >
598
295
  <p>
599
- Yes. The inspector script is a lightweight, read-only
600
- observer that listens for hover/click events and reports
601
- element metadata (tag name, styles, bounding box) back to
602
- pAInt via <code style={inlineCodeStyle}>postMessage</code>.
603
- It does not modify your source code, send data to external
604
- servers, or execute arbitrary code. It only communicates
605
- with pAInt origin.
296
+ No. pAInt uses a Service Worker proxy that automatically
297
+ injects the inspector script into your page. Just click
298
+ Connect no modifications to your project needed.
606
299
  </p>
607
300
  </FaqSection>
608
301
 
@@ -642,15 +335,13 @@ export default defineNuxtConfig({
642
335
 
643
336
  <FaqSection
644
337
  id="faq-production"
645
- question="Should I remove the script tag before deploying to production?"
338
+ question="Does pAInt affect my production build?"
646
339
  >
647
340
  <p>
648
- Yes. The inspector script is intended for local development
649
- only. Remove it (or wrap it in an environment check) before
650
- deploying. If you forget, the script will try to connect to
651
- pAInt origin and silently fail — it won&apos;t affect your
652
- users — but it&apos;s best practice to keep it out of
653
- production builds.
341
+ No. pAInt runs entirely in the browser through a Service
342
+ Worker nothing is added to your project&apos;s source
343
+ code or build output. There&apos;s nothing to remove before
344
+ deploying.
654
345
  </p>
655
346
  </FaqSection>
656
347
 
@@ -798,6 +489,42 @@ function TroubleshootItem({
798
489
  )
799
490
  }
800
491
 
492
+ function CompatItem({
493
+ icon,
494
+ name,
495
+ detail,
496
+ }: {
497
+ icon: string
498
+ name: string
499
+ detail: string
500
+ }) {
501
+ return (
502
+ <div
503
+ className="flex items-center gap-3 rounded-md px-4 py-3"
504
+ style={{
505
+ background: 'var(--bg-secondary)',
506
+ border: '1px solid var(--border)',
507
+ }}
508
+ >
509
+ <span className="text-base">{icon}</span>
510
+ <div>
511
+ <span
512
+ className="text-sm font-medium"
513
+ style={{ color: 'var(--text-primary)' }}
514
+ >
515
+ {name}
516
+ </span>
517
+ <span
518
+ className="text-sm ml-2"
519
+ style={{ color: 'var(--text-muted)' }}
520
+ >
521
+ — {detail}
522
+ </span>
523
+ </div>
524
+ </div>
525
+ )
526
+ }
527
+
801
528
  function UseCaseItem({
802
529
  title,
803
530
  description,