@narumitw/pi-image-drop 0.28.0 โ†’ 0.30.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  `@narumitw/pi-image-drop` adds one `/image-drop` command to the latest [Pi Coding Agent](https://pi.dev). It serves a private loopback page where you can paste, drop, choose, preview, reorder, retry, and remove local images. The ordered batch is attached to your next non-empty interactive Pi message.
6
6
 
7
- The page never contains a prompt or Attach button: Pi remains the only place where messages are written and sent.
7
+ The page never contains a prompt or Attach button: Pi remains the only place where messages are written and sent. Its React and TypeScript frontend uses Radix Themes, Primitives, Colors, and Icons, and ships as local bundled assets with no CDN or runtime build step.
8
8
 
9
9
  ## โœจ Features
10
10
 
@@ -14,6 +14,7 @@ The page never contains a prompt or Attach button: Pi remains the only place whe
14
14
  - Supports PNG, JPEG, WebP, GIF, BMP, TIFF, HEIC/HEIF, and AVIF input.
15
15
  - Applies orientation, strips private metadata, and enforces Pi-compatible image limits.
16
16
  - Keeps bounded sent-image history for explicit re-attachment during the live session.
17
+ - Uses Radix UI for an adaptive light/dark design system, accessible dialogs and disclosures, semantic colors, and consistent action icons.
17
18
  - Reports batch state above Pi's editor and can start automatically with each session.
18
19
 
19
20
  ## ๐Ÿ“ฆ Install
@@ -147,7 +148,11 @@ src/images.ts bounded image processing
147
148
  src/server.ts authenticated loopback HTTP/SSE server
148
149
  src/settings.ts extension settings
149
150
  src/pi-settings.ts effective Pi image settings adapter
150
- src/web/ framework-free browser page
151
+ src/web/ui/ authored React and TypeScript browser source
152
+ src/web/app.js generated bundled React application
153
+ src/web/state.js generated compatibility helper module
154
+ src/web/styles.css generated Radix Themes, Colors, and local styles
155
+ src/web/index.html minimal authenticated React shell
151
156
  ```
152
157
 
153
158
  ## ๐Ÿงช Development
@@ -155,13 +160,14 @@ src/web/ framework-free browser page
155
160
  From the repository root:
156
161
 
157
162
  ```bash
163
+ npm --workspace @narumitw/pi-image-drop run build:web
158
164
  npm --workspace @narumitw/pi-image-drop run check
159
165
  npm test
160
166
  just try image-drop
161
167
  just pack image-drop
162
168
  ```
163
169
 
164
- The dry-run package must contain the manifest, license, README, TypeScript sources, and static web assets, but no tests, fixtures, image bytes, or `node_modules`.
170
+ Edit browser code under `src/web/ui/`, then run `build:web`. The package typecheck runs `check:web`, which rebuilds in a temporary directory and rejects stale generated assets. The dry-run package must contain the manifest, license, README, TypeScript/TSX sources, and bundled static web assets, but no tests, fixtures, image bytes, build scripts, or `node_modules`.
165
171
 
166
172
  ## ๐Ÿš€ Publishing
167
173
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-image-drop",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "description": "Pi extension for staging browser images and attaching them to the next message.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,13 +24,21 @@
24
24
  ]
25
25
  },
26
26
  "scripts": {
27
- "check": "biome check . && npm run typecheck",
28
- "format": "biome check --write .",
29
- "typecheck": "tsc --noEmit"
27
+ "build:web": "node scripts/build-web.mjs",
28
+ "check": "biome check --vcs-use-ignore-file=false src/*.ts src/web/index.html src/web/ui test scripts package.json tsconfig.json && npm run typecheck",
29
+ "check:web": "node scripts/build-web.mjs --check",
30
+ "format": "biome check --write --vcs-use-ignore-file=false src/*.ts src/web/index.html src/web/ui test scripts package.json tsconfig.json && npm run build:web",
31
+ "typecheck": "npm run check:web && tsc --noEmit"
30
32
  },
31
33
  "dependencies": {
34
+ "@radix-ui/colors": "3.0.0",
35
+ "@radix-ui/react-icons": "1.3.2",
36
+ "@radix-ui/themes": "3.3.0",
32
37
  "bmp-js": "^0.1.0",
33
38
  "heic-decode": "^2.1.0",
39
+ "radix-ui": "1.6.6",
40
+ "react": "19.2.8",
41
+ "react-dom": "19.2.8",
34
42
  "sharp": "^0.35.3"
35
43
  },
36
44
  "peerDependencies": {
@@ -41,6 +49,9 @@
41
49
  "@biomejs/biome": "2.5.3",
42
50
  "@earendil-works/pi-ai": "0.80.3",
43
51
  "@earendil-works/pi-coding-agent": "0.80.3",
52
+ "@types/react": "19.2.17",
53
+ "@types/react-dom": "19.2.3",
54
+ "esbuild": "0.25.12",
44
55
  "typescript": "6.0.3"
45
56
  },
46
57
  "repository": {
package/src/runtime.ts CHANGED
@@ -248,10 +248,7 @@ export class ImageDropRuntime {
248
248
  if (generation !== this.generation) return false;
249
249
  if (batch.publicState().phase !== "ready") {
250
250
  this.restoreEditor(ctx, text);
251
- ctx.ui.notify(
252
- "Images could not be updated for the current auto-resize setting.",
253
- "warning",
254
- );
251
+ ctx.ui.notify("Images could not be updated for the current auto-resize setting.", "warning");
255
252
  this.updateWidget(ctx);
256
253
  return false;
257
254
  }
package/src/server.ts CHANGED
@@ -11,6 +11,7 @@ import type { ImageDropSettings } from "./settings.js";
11
11
 
12
12
  const JSON_LIMIT = 64 * 1024;
13
13
  const CLIENT_ID = /^[A-Za-z0-9_-]{1,80}$/;
14
+ const CSP_NONCE_PLACEHOLDER = "__PI_CSP_NONCE__";
14
15
 
15
16
  export interface ImageDropServerOptions {
16
17
  batch: BatchStore;
@@ -47,6 +48,7 @@ export class ImageDropServer {
47
48
  readonly origin: string;
48
49
  private bootstrapToken?: string;
49
50
  private readonly sessionSecret = token();
51
+ private readonly cspNonce = token();
50
52
  private readonly cookieName = `pi_image_drop_${randomBytes(8).toString("hex")}`;
51
53
  private readonly abortController = new AbortController();
52
54
  private readonly sockets = new Set<Socket>();
@@ -463,7 +465,11 @@ export class ImageDropServer {
463
465
  }
464
466
 
465
467
  private async asset(response: ServerResponse, name: string, contentType: string): Promise<void> {
466
- const data = await readAsset(name);
468
+ const asset = await readAsset(name);
469
+ const data =
470
+ name === "index.html"
471
+ ? Buffer.from(asset.toString("utf8").replaceAll(CSP_NONCE_PLACEHOLDER, this.cspNonce))
472
+ : asset;
467
473
  response.setHeader("Content-Type", contentType);
468
474
  response.setHeader("Content-Length", data.byteLength);
469
475
  response.writeHead(200).end(data);
@@ -473,7 +479,7 @@ export class ImageDropServer {
473
479
  response.setHeader("Cache-Control", "no-store");
474
480
  response.setHeader(
475
481
  "Content-Security-Policy",
476
- "default-src 'self'; img-src 'self' blob:; script-src 'self'; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'",
482
+ `default-src 'self'; img-src 'self' blob:; script-src 'self'; style-src 'self' 'nonce-${this.cspNonce}'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'`,
477
483
  );
478
484
  response.setHeader("Referrer-Policy", "no-referrer");
479
485
  response.setHeader("X-Content-Type-Options", "nosniff");