@narumitw/pi-image-drop 0.25.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 +11 -4
- package/package.json +16 -5
- package/src/index.ts +1 -0
- package/src/runtime.ts +1 -4
- package/src/server.ts +8 -2
- package/src/web/app.js +97 -537
- package/src/web/index.html +2 -102
- package/src/web/state.js +5 -114
- package/src/web/styles.css +2 -538
- package/src/web/ui/app.tsx +178 -0
- package/src/web/ui/client.ts +377 -0
- package/src/web/ui/components.tsx +615 -0
- package/src/web/ui/state.ts +149 -0
- package/src/web/ui/styles.css +572 -0
- package/src/web/ui/types.ts +56 -0
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
|
|
@@ -139,14 +140,19 @@ Then open the unchanged `http://127.0.0.1:45678/...` link locally. Image Drop do
|
|
|
139
140
|
## ๐๏ธ Package layout
|
|
140
141
|
|
|
141
142
|
```text
|
|
142
|
-
src/
|
|
143
|
+
src/index.ts Pi package entrypoint
|
|
144
|
+
src/image-drop.ts extension registration and command orchestration
|
|
143
145
|
src/runtime.ts Pi lifecycle and message orchestration
|
|
144
146
|
src/batch.ts in-memory draft and sent-history state machine
|
|
145
147
|
src/images.ts bounded image processing
|
|
146
148
|
src/server.ts authenticated loopback HTTP/SSE server
|
|
147
149
|
src/settings.ts extension settings
|
|
148
150
|
src/pi-settings.ts effective Pi image settings adapter
|
|
149
|
-
src/web/
|
|
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
|
|
150
156
|
```
|
|
151
157
|
|
|
152
158
|
## ๐งช Development
|
|
@@ -154,13 +160,14 @@ src/web/ framework-free browser page
|
|
|
154
160
|
From the repository root:
|
|
155
161
|
|
|
156
162
|
```bash
|
|
163
|
+
npm --workspace @narumitw/pi-image-drop run build:web
|
|
157
164
|
npm --workspace @narumitw/pi-image-drop run check
|
|
158
165
|
npm test
|
|
159
166
|
just try image-drop
|
|
160
167
|
just pack image-drop
|
|
161
168
|
```
|
|
162
169
|
|
|
163
|
-
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`.
|
|
164
171
|
|
|
165
172
|
## ๐ Publishing
|
|
166
173
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-image-drop",
|
|
3
|
-
"version": "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",
|
|
@@ -20,17 +20,25 @@
|
|
|
20
20
|
],
|
|
21
21
|
"pi": {
|
|
22
22
|
"extensions": [
|
|
23
|
-
"./src/
|
|
23
|
+
"./src/index.ts"
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
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/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./image-drop.js";
|
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
|
|
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
|
-
|
|
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");
|