@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.
package/LICENSE CHANGED
@@ -175,4 +175,4 @@ of your accepting any such warranty or additional liability.
175
175
 
176
176
  END OF TERMS AND CONDITIONS
177
177
 
178
- Copyright 2026 Antigenic Research Institute
178
+ Copyright 2026 Antigenic Technologies Inc.
package/NOTICE CHANGED
@@ -1,4 +1,4 @@
1
1
  pAInt
2
- Copyright 2026 Antigenic Research Institute
2
+ Copyright 2026 Antigenic Technologies Inc.
3
3
 
4
- This product includes software developed at Antigenic Research Institute.
4
+ This product includes software developed at Antigenic Technologies Inc.
package/README.md CHANGED
@@ -1,28 +1,35 @@
1
1
  # pAInt
2
2
 
3
- [![License](https://img.shields.io/github/license/Antigenic-OSS/pAInt)](https://github.com/Antigenic-OSS/pAInt/blob/main/LICENSE)
4
- [![Stars](https://img.shields.io/github/stars/Antigenic-OSS/pAInt?style=social)](https://github.com/Antigenic-OSS/pAInt/stargazers)
5
- [![Issues](https://img.shields.io/github/issues/Antigenic-OSS/pAInt)](https://github.com/Antigenic-OSS/pAInt/issues)
6
- [![npm version](https://img.shields.io/npm/v/@antigenic-oss/paint)](https://www.npmjs.com/package/@antigenic-oss/paint)
7
- [![npm downloads](https://img.shields.io/npm/dm/@antigenic-oss/paint)](https://www.npmjs.com/package/@antigenic-oss/paint)
8
- [![Bun](https://img.shields.io/badge/local%20dev-Bun-000000)](https://bun.sh)
9
- [![Node.js](https://img.shields.io/badge/runtime-Node.js-5FA04E)](https://nodejs.org)
10
- [![Next.js](https://img.shields.io/badge/framework-Next.js-000000)](https://nextjs.org)
11
- [![TypeScript](https://img.shields.io/badge/language-TypeScript-3178C6)](https://www.typescriptlang.org)
3
+ <p>
4
+ <a href="https://www.npmjs.com/package/@antigenic-oss/paint"><img alt="npm version" src="https://img.shields.io/npm/v/@antigenic-oss/paint" /></a>
5
+ <a href="https://www.npmjs.com/package/@antigenic-oss/paint"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@antigenic-oss/paint" /></a>
6
+ <a href="https://github.com/Antigenic-OSS/pAInt/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/Antigenic-OSS/pAInt" /></a>
7
+ <a href="https://github.com/Antigenic-OSS/pAInt/issues"><img alt="Issues" src="https://img.shields.io/github/issues/Antigenic-OSS/pAInt" /></a>
8
+ <br />
9
+ <a href="https://github.com/Antigenic-OSS/pAInt/stargazers"><img alt="Stars" src="https://img.shields.io/github/stars/Antigenic-OSS/pAInt?style=social" /></a>
10
+ <a href="https://bun.sh"><img alt="Bun" src="https://img.shields.io/badge/local%20dev-Bun-000000" /></a>
11
+ <a href="https://nodejs.org"><img alt="Node.js" src="https://img.shields.io/badge/runtime-Node.js-5FA04E" /></a>
12
+ <a href="https://nextjs.org"><img alt="Next.js" src="https://img.shields.io/badge/framework-Next.js-000000" /></a>
13
+ <a href="https://www.typescriptlang.org"><img alt="TypeScript" src="https://img.shields.io/badge/language-TypeScript-3178C6" /></a>
14
+ </p>
12
15
 
13
16
  pAInt is a visual editor for localhost web projects. It helps you inspect elements, edit styles, manage CSS variables, and export changelogs for [Claude Code](https://claude.ai/claude-code).
14
17
 
18
+ Built by [Antigenic](https://antigenic.org).
19
+
15
20
  ## Table of Contents
16
21
 
17
22
  - [Project Status](#project-status)
18
23
  - [Global CLI](#global-cli)
19
24
  - [What You Can Do](#what-you-can-do)
25
+ - [Why pAInt Saves AI Tokens](#why-paint-saves-ai-tokens)
20
26
  - [Prerequisites](#prerequisites)
21
27
  - [Quick Start](#quick-start)
22
28
  - [Connection Modes](#connection-modes)
23
29
  - [Core Workflow](#core-workflow)
24
30
  - [Interface Layout](#interface-layout)
25
31
  - [Commands](#commands)
32
+ - [Bridge vs Server vs Terminal](#bridge-vs-server-vs-terminal)
26
33
  - [Architecture Summary](#architecture-summary)
27
34
  - [Security Notes](#security-notes)
28
35
  - [Documentation](#documentation)
@@ -101,6 +108,15 @@ Terminal WS (when started): `ws://localhost:4001/ws`
101
108
  - Track every change and export a structured changelog
102
109
  - Send changelogs to Claude Code for source-file application
103
110
 
111
+ ## Why pAInt Saves AI Tokens
112
+
113
+ pAInt helps you use fewer AI tokens by turning broad prompts into precise, structured change data:
114
+
115
+ - You edit visually first, so you do not need long back-and-forth prompt iterations
116
+ - Exported changelogs include exact selectors, properties, and before/after values
117
+ - Claude Code receives focused instructions, reducing token-heavy ambiguity
118
+ - You review only real diffs instead of asking the model to rediscover page context each time
119
+
104
120
  ## Prerequisites
105
121
 
106
122
  - Local repository development: Bun `>=1.3`
@@ -172,6 +188,20 @@ bun run start # Production server (port 4000)
172
188
  bun run lint # Biome check
173
189
  ```
174
190
 
191
+ ## Bridge vs Server vs Terminal
192
+
193
+ These three pieces have different jobs:
194
+
195
+ - `pAInt server` (`paint start`): runs the main pAInt web app UI (default `http://127.0.0.1:4000`)
196
+ - `bridge server` (`paint bridge start`): local HTTP bridge used when pAInt UI is hosted remotely (for example Vercel) but still needs safe local machine access (`http://127.0.0.1:4002` by default)
197
+ - `terminal server` (`paint terminal start`): local WebSocket PTY service for the in-app Terminal tab (`ws://localhost:4001/ws` by default)
198
+
199
+ When to run each:
200
+
201
+ - Local-only workflow: run `paint start` (and optionally `paint terminal start` if you want in-app terminal streaming)
202
+ - Hosted UI + local project workflow: run `paint bridge start` so the hosted UI can reach local-only capabilities
203
+ - Claude/CLI visibility workflow: run `paint terminal start` to see command output and progress inside pAInt
204
+
175
205
  ## Architecture Summary
176
206
 
177
207
  - Next.js App Router frontend (develop locally with Bun, run globally via Node.js CLI)
@@ -201,15 +231,12 @@ See `CONTRIBUTING.md` for setup, workflow, and pull request expectations.
201
231
 
202
232
  ## Release Automation
203
233
 
204
- - Versioning and release PRs are managed with Changesets.
234
+ - Versioning is manual (update `package.json` before merging to `main`).
205
235
  - CI workflow: `.github/workflows/ci.yml`
206
236
  - Release workflow: `.github/workflows/release.yml`
207
237
  - Publishing uses npm Trusted Publishing (OIDC) from the `release` GitHub Environment.
208
- - To queue a release, add a changeset:
209
-
210
- ```bash
211
- bun run changeset
212
- ```
238
+ - On every push to `main`, the release workflow runs build/lint/smoke checks.
239
+ - Publish happens automatically only when `package.json` version differs from the currently published npm version.
213
240
 
214
241
  ## License
215
242
 
package/bin/paint.js CHANGED
@@ -491,6 +491,8 @@ async function startApp(options) {
491
491
  console.log(`pAInt started at http://${options.host}:${options.port}`)
492
492
  console.log(`Web pid: ${webChild.pid}`)
493
493
  console.log(`Web logs: ${WEB_LOG_FILE}`)
494
+
495
+ checkForUpdate()
494
496
  }
495
497
 
496
498
  function stopApp() {
@@ -529,6 +531,8 @@ function appStatus() {
529
531
  console.log(`URL: http://${existing.host}:${existing.port}`)
530
532
  console.log(`Started: ${existing.startedAt}`)
531
533
  if (existing.logs?.web) console.log(`Web logs: ${existing.logs.web}`)
534
+
535
+ checkForUpdate()
532
536
  }
533
537
 
534
538
  function appLogs() {
@@ -747,6 +751,34 @@ function bridgeLogs() {
747
751
  process.stdout.write(fs.readFileSync(BRIDGE_LOG_FILE, 'utf8'))
748
752
  }
749
753
 
754
+ function checkForUpdate() {
755
+ const pkg = JSON.parse(fs.readFileSync(APP_PACKAGE_JSON, 'utf8'))
756
+ const name = pkg.name
757
+ const registryUrl = `https://registry.npmjs.org/${name}/latest`
758
+
759
+ const req = https.get(registryUrl, { timeout: 3000 }, (res) => {
760
+ if (res.statusCode !== 200) return
761
+ const chunks = []
762
+ res.on('data', (chunk) => chunks.push(chunk))
763
+ res.on('end', () => {
764
+ try {
765
+ const data = JSON.parse(Buffer.concat(chunks).toString('utf8'))
766
+ const latest = data.version
767
+ if (latest && latest !== APP_VERSION) {
768
+ console.log(
769
+ `\nUpdate available: ${APP_VERSION} → ${latest}\nRun: npm install -g ${name}\n`,
770
+ )
771
+ }
772
+ } catch {
773
+ // ignore parse errors
774
+ }
775
+ })
776
+ })
777
+
778
+ req.on('error', () => {})
779
+ req.on('timeout', () => req.destroy())
780
+ }
781
+
750
782
  function showHelp() {
751
783
  console.log(`paint - pAInt server manager
752
784
 
package/next.config.mjs CHANGED
@@ -20,6 +20,14 @@ const nextConfig = {
20
20
  { key: 'Cross-Origin-Resource-Policy', value: 'cross-origin' },
21
21
  ],
22
22
  },
23
+ {
24
+ // Allow the SW to control /sw-proxy/ scope even though the script
25
+ // lives at /sw-proxy/sw.js (scope matches script directory)
26
+ source: '/sw-proxy/sw.js',
27
+ headers: [
28
+ { key: 'Service-Worker-Allowed', value: '/sw-proxy/' },
29
+ ],
30
+ },
23
31
  ]
24
32
  },
25
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antigenic-oss/paint",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "Visual editor for localhost web projects with a global paint CLI",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/Antigenic-OSS/pAInt",
@@ -13,10 +13,16 @@
13
13
  },
14
14
  "keywords": [
15
15
  "paint",
16
+ "pAInt",
16
17
  "visual-editor",
17
18
  "nextjs",
18
19
  "css",
19
- "localhost"
20
+ "localhost",
21
+ "bridge-server",
22
+ "terminal",
23
+ "claude-code",
24
+ "ai-coding",
25
+ "developer-tools"
20
26
  ],
21
27
  "bin": {
22
28
  "paint": "bin/paint.js"
@@ -53,10 +59,7 @@
53
59
  "lint:fix": "biome lint --write .",
54
60
  "format": "biome format --write .",
55
61
  "smoke:packed-global": "node ./bin/smoke-packed-global.js",
56
- "prepublishOnly": "npm run smoke:packed-global",
57
- "changeset": "changeset",
58
- "version-packages": "changeset version",
59
- "release": "changeset publish"
62
+ "prepublishOnly": "npm run smoke:packed-global"
60
63
  },
61
64
  "dependencies": {
62
65
  "@tailwindcss/postcss": "4.2.1",
@@ -77,7 +80,6 @@
77
80
  "zustand": "^5.0.11"
78
81
  },
79
82
  "devDependencies": {
80
- "@biomejs/biome": "2.4.5",
81
- "@changesets/cli": "^2.29.7"
83
+ "@biomejs/biome": "2.4.5"
82
84
  }
83
85
  }
@@ -429,6 +429,13 @@
429
429
  'position:absolute;top:-18px;left:-1px;padding:1px 6px;font-size:10px;font-family:-apple-system,BlinkMacSystemFont,sans-serif;line-height:14px;color:#fff;background:#1D3F23;border-radius:3px 3px 0 0;white-space:nowrap;pointer-events:none;'
430
430
  hoverOverlay.appendChild(hoverLabel)
431
431
 
432
+ // Watch for framework hydration removing our overlays from the DOM.
433
+ // When detected, re-append them to the current document.body.
434
+ new MutationObserver(function () {
435
+ if (!selectionOverlay.isConnected) document.body.appendChild(selectionOverlay)
436
+ if (!hoverOverlay.isConnected) document.body.appendChild(hoverOverlay)
437
+ }).observe(document.documentElement, { childList: true, subtree: true })
438
+
432
439
  var hoveredElement = null
433
440
 
434
441
  function getElementLabel(el) {
@@ -447,6 +454,7 @@
447
454
  }
448
455
 
449
456
  document.addEventListener('mousemove', (e) => {
457
+
450
458
  if (!selectionModeEnabled) {
451
459
  hoverOverlay.style.display = 'none'
452
460
  return
@@ -524,6 +532,7 @@
524
532
  function selectElement(el) {
525
533
  // Don't select elements when selection mode is disabled (preview mode)
526
534
  if (!selectionModeEnabled) return
535
+
527
536
  selectedElement = el
528
537
  var rect = el.getBoundingClientRect()
529
538
  selectionOverlay.style.display = 'block'
@@ -575,6 +584,7 @@
575
584
  }
576
585
 
577
586
  function updateSelectionOverlay() {
587
+
578
588
  if (!selectedElement || selectionOverlay.style.display === 'none') return
579
589
  var rect = selectedElement.getBoundingClientRect()
580
590
  selectionOverlay.style.top = `${rect.top}px`
@@ -1439,6 +1449,10 @@
1439
1449
  }
1440
1450
  if (resolved.origin !== window.location.origin) continue
1441
1451
  var linkPath = resolved.pathname
1452
+ // Strip /sw-proxy/ prefix added by SW proxy URL rewriting
1453
+ if (linkPath.indexOf('/sw-proxy/') === 0) {
1454
+ linkPath = linkPath.substring('/sw-proxy'.length) || '/'
1455
+ }
1442
1456
  if (linkPath.indexOf('/api/') === 0 || linkPath === '') continue
1443
1457
  if (!linkPath.startsWith('/')) continue
1444
1458
  if (seen[linkPath]) continue