pi-openui 0.1.1 → 0.1.2
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/CHANGELOG.md +9 -0
- package/README.md +10 -0
- package/package.json +2 -1
- package/src/diagram/canvas.ts +20 -14
- package/src/diagram/infra.ts +2 -1
- package/src/diagram/wide.ts +36 -0
- package/src/lang/docs.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.2 (2026-10-31)
|
|
6
|
+
|
|
7
|
+
- Fixed borders drawn out of line on rows with wide characters. The terminal draws emoji and many symbols (✅ ❌ ⚡ 🚀 ⭐ ☕
|
|
8
|
+
and about 8,800 more) two cells wide, and the widgets counted them as one, so everything to their right on that row, and the
|
|
9
|
+
right border, slid by a cell. The widgets now measure with the same table pi uses. The `storage` icon of an InfraNode was one
|
|
10
|
+
of these characters (U+2630); it is now ⊟.
|
|
11
|
+
- The README now says what to do when the widget does not appear because pi-compact-tools collapses the tool call into a
|
|
12
|
+
"Called render_widget" row: exclude `render_widget` in its config. Nothing changes in the extension.
|
|
13
|
+
|
|
5
14
|
## 0.1.1 (2026-10-30)
|
|
6
15
|
|
|
7
16
|
- The README now renders on pi.dev and npm: screenshots are markdown images (pi.dev drops raw HTML tables and images) and
|
package/README.md
CHANGED
|
@@ -128,6 +128,16 @@ More in [docs/design.md](https://github.com/newtonapple/pi-openui/blob/main/docs
|
|
|
128
128
|
Mono. A different font works if it has them: `PI_OPENUI_FONT=/path/to/font.ttf npm test` checks yours.
|
|
129
129
|
- Pictures need a terminal that supports the Kitty graphics protocol. Other terminals get a framed placeholder.
|
|
130
130
|
|
|
131
|
+
## If the widget does not appear
|
|
132
|
+
|
|
133
|
+
You see a collapsed `Called render_widget ... + Show details` row and no widget, and the model says the widget was drawn. Another
|
|
134
|
+
extension is drawing the tool call in place of this one. [pi-compact-tools](https://www.npmjs.com/package/pi-compact-tools) does
|
|
135
|
+
this to every tool by default. Tell it to leave `render_widget` alone in `~/.pi/agent/compact-tools.json`, then run `/reload`:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{ "custom_tools": { "exclude": ["render_widget"] } }
|
|
139
|
+
```
|
|
140
|
+
|
|
131
141
|
## Development
|
|
132
142
|
|
|
133
143
|
Working on the code? Start pi in this folder instead of installing it: `.pi/extensions/openui.ts` loads the extension (pi asks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-openui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pi extension: interactive dashboards, charts, diagrams and maps in the terminal. The agent draws with OpenUI Lang; you click, hover and pin notes.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"scripts": {
|
|
50
50
|
"test": "node --import ./test/register-pi.mjs --test --test-reporter=spec test/*.test.ts",
|
|
51
51
|
"gallery": "node scripts/langgallery.ts",
|
|
52
|
+
"width": "node --import ./test/register-pi.mjs scripts/width-table.ts",
|
|
52
53
|
"screenshots": "node --import ./test/register-pi.mjs scripts/shots.ts && python3 scripts/render_shots.py && python3 scripts/hero.py",
|
|
53
54
|
"docs": "node scripts/gen_docs.ts",
|
|
54
55
|
"prepublishOnly": "npm test"
|
package/src/diagram/canvas.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Text measurement and a cell canvas. Pure, no dependencies.
|
|
2
|
+
import { WIDE } from "./wide.ts";
|
|
2
3
|
|
|
3
4
|
const seg: { segment(s: string): Iterable<{ segment: string }> } | null =
|
|
4
5
|
typeof Intl !== "undefined" && (Intl as any).Segmenter ? new (Intl as any).Segmenter(undefined, { granularity: "grapheme" }) : null;
|
|
@@ -7,23 +8,28 @@ export function graphemes(s: string): string[] {
|
|
|
7
8
|
return seg ? Array.from(seg.segment(s), (x) => x.segment) : Array.from(s);
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
/** Whether `cp` is in the generated table of code points pi draws two cells wide (binary search over [first, last] pairs). */
|
|
12
|
+
function isWide(cp: number): boolean {
|
|
13
|
+
let lo = 0;
|
|
14
|
+
let hi = WIDE.length / 2 - 1;
|
|
15
|
+
while (lo <= hi) {
|
|
16
|
+
const mid = (lo + hi) >> 1;
|
|
17
|
+
if (cp < WIDE[mid * 2]) hi = mid - 1;
|
|
18
|
+
else if (cp > WIDE[mid * 2 + 1]) lo = mid + 1;
|
|
19
|
+
else return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Columns one code point takes. It must agree with the terminal, or everything to its right on the row slides and the borders
|
|
26
|
+
* come out of line: wide characters (CJK, and the emoji and symbols models like: U+2705 U+274C U+26A1 U+1F680) are two, taken from pi's own width
|
|
27
|
+
* function through src/diagram/wide.ts; marks and controls are none.
|
|
28
|
+
*/
|
|
10
29
|
function cpWidth(cp: number): number {
|
|
11
30
|
if (cp === 0 || cp < 32 || (cp >= 0x7f && cp < 0xa0)) return 0;
|
|
12
31
|
if ((cp >= 0x300 && cp <= 0x36f) || (cp >= 0x200b && cp <= 0x200f) || (cp >= 0xfe00 && cp <= 0xfe0f) || (cp >= 0x20d0 && cp <= 0x20ff)) return 0;
|
|
13
|
-
|
|
14
|
-
(cp >= 0x1100 && cp <= 0x115f) ||
|
|
15
|
-
(cp >= 0x2e80 && cp <= 0xa4cf) ||
|
|
16
|
-
(cp >= 0xac00 && cp <= 0xd7a3) ||
|
|
17
|
-
(cp >= 0xf900 && cp <= 0xfaff) ||
|
|
18
|
-
(cp >= 0xfe30 && cp <= 0xfe6f) ||
|
|
19
|
-
(cp >= 0xff00 && cp <= 0xff60) ||
|
|
20
|
-
(cp >= 0xffe0 && cp <= 0xffe6) ||
|
|
21
|
-
(cp >= 0x1f300 && cp <= 0x1f64f) ||
|
|
22
|
-
(cp >= 0x1f900 && cp <= 0x1f9ff) ||
|
|
23
|
-
(cp >= 0x20000 && cp <= 0x3fffd)
|
|
24
|
-
)
|
|
25
|
-
return 2;
|
|
26
|
-
return 1;
|
|
32
|
+
return isWide(cp) ? 2 : 1;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/** Display width of one grapheme cluster (0, 1 or 2 columns). */
|
package/src/diagram/infra.ts
CHANGED
|
@@ -11,7 +11,8 @@ import { drawWires, type WireSpec } from "./wires.ts";
|
|
|
11
11
|
export const INFRA_KINDS = ["client", "gateway", "service", "gpu", "database", "storage", "queue", "cloud"] as const;
|
|
12
12
|
export const INFRA_STATUSES = ["up", "degraded", "down", "unknown"] as const;
|
|
13
13
|
export const INFRA_TONES = ["neutral", "info", "good", "warn", "bad"] as const;
|
|
14
|
-
|
|
14
|
+
// Every icon must be one cell wide in pi (test/width.test.ts): a wide one (the old storage icon, U+2630, was) slides the rest of its row.
|
|
15
|
+
export const KIND_ICON: Record<string, string> = { client: "□", gateway: "↔", service: "◇", gpu: "⌸", database: "◎", storage: "⊟", queue: "≡", cloud: "◌" };
|
|
15
16
|
const STATUS_GLYPH: Record<string, string> = { up: "●", degraded: "●", down: "●", unknown: "○" };
|
|
16
17
|
|
|
17
18
|
export interface InfraNodeIn {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Generated by scripts/width-table.ts from pi's own width function: do not edit. Run `npm run width` after pi updates.
|
|
2
|
+
// The code points pi draws two cells wide, as inclusive [first, last] pairs in ascending order.
|
|
3
|
+
export const WIDE: readonly number[] = [
|
|
4
|
+
0x1100, 0x115e, 0x231a, 0x231b, 0x2329, 0x232a, 0x23e9, 0x23ec,
|
|
5
|
+
0x23f0, 0x23f0, 0x23f3, 0x23f3, 0x25fd, 0x25fe, 0x2614, 0x2615,
|
|
6
|
+
0x2630, 0x2637, 0x2648, 0x2653, 0x267f, 0x267f, 0x268a, 0x268f,
|
|
7
|
+
0x2693, 0x2693, 0x26a1, 0x26a1, 0x26aa, 0x26ab, 0x26bd, 0x26be,
|
|
8
|
+
0x26c4, 0x26c5, 0x26ce, 0x26ce, 0x26d4, 0x26d4, 0x26ea, 0x26ea,
|
|
9
|
+
0x26f2, 0x26f3, 0x26f5, 0x26f5, 0x26fa, 0x26fa, 0x26fd, 0x26fd,
|
|
10
|
+
0x2705, 0x2705, 0x270a, 0x270b, 0x2728, 0x2728, 0x274c, 0x274c,
|
|
11
|
+
0x274e, 0x274e, 0x2753, 0x2755, 0x2757, 0x2757, 0x2795, 0x2797,
|
|
12
|
+
0x27b0, 0x27b0, 0x27bf, 0x27bf, 0x2b1b, 0x2b1c, 0x2b50, 0x2b50,
|
|
13
|
+
0x2b55, 0x2b55, 0x2e80, 0x2e99, 0x2e9b, 0x2ef3, 0x2f00, 0x2fd5,
|
|
14
|
+
0x2ff0, 0x3029, 0x3030, 0x303e, 0x3041, 0x3096, 0x309b, 0x30ff,
|
|
15
|
+
0x3105, 0x312f, 0x3131, 0x3163, 0x3165, 0x318e, 0x3190, 0x31e5,
|
|
16
|
+
0x31ef, 0x321e, 0x3220, 0x3247, 0x3250, 0xa48c, 0xa490, 0xa4c6,
|
|
17
|
+
0xa960, 0xa97c, 0xac00, 0xd7a3, 0xf900, 0xfaff, 0xfe10, 0xfe19,
|
|
18
|
+
0xfe30, 0xfe52, 0xfe54, 0xfe66, 0xfe68, 0xfe6b, 0xff01, 0xff60,
|
|
19
|
+
0xffe0, 0xffe6, 0x16fe0, 0x16fe3, 0x16ff2, 0x16ff6, 0x17000, 0x18cd5,
|
|
20
|
+
0x18cff, 0x18d1e, 0x18d80, 0x18df2, 0x1aff0, 0x1aff3, 0x1aff5, 0x1affb,
|
|
21
|
+
0x1affd, 0x1affe, 0x1b000, 0x1b122, 0x1b132, 0x1b132, 0x1b150, 0x1b152,
|
|
22
|
+
0x1b155, 0x1b155, 0x1b164, 0x1b167, 0x1b170, 0x1b2fb, 0x1d300, 0x1d356,
|
|
23
|
+
0x1d360, 0x1d376, 0x1f004, 0x1f004, 0x1f0cf, 0x1f0cf, 0x1f18e, 0x1f18e,
|
|
24
|
+
0x1f191, 0x1f19a, 0x1f1e6, 0x1f202, 0x1f210, 0x1f23b, 0x1f240, 0x1f248,
|
|
25
|
+
0x1f250, 0x1f251, 0x1f260, 0x1f265, 0x1f300, 0x1f320, 0x1f32d, 0x1f335,
|
|
26
|
+
0x1f337, 0x1f37c, 0x1f37e, 0x1f393, 0x1f3a0, 0x1f3ca, 0x1f3cf, 0x1f3d3,
|
|
27
|
+
0x1f3e0, 0x1f3f0, 0x1f3f4, 0x1f3f4, 0x1f3f8, 0x1f43e, 0x1f440, 0x1f440,
|
|
28
|
+
0x1f442, 0x1f4fc, 0x1f4ff, 0x1f53d, 0x1f54b, 0x1f54e, 0x1f550, 0x1f567,
|
|
29
|
+
0x1f57a, 0x1f57a, 0x1f595, 0x1f596, 0x1f5a4, 0x1f5a4, 0x1f5fb, 0x1f64f,
|
|
30
|
+
0x1f680, 0x1f6c5, 0x1f6cc, 0x1f6cc, 0x1f6d0, 0x1f6d2, 0x1f6d5, 0x1f6d8,
|
|
31
|
+
0x1f6dc, 0x1f6df, 0x1f6eb, 0x1f6ec, 0x1f6f4, 0x1f6fc, 0x1f7e0, 0x1f7eb,
|
|
32
|
+
0x1f7f0, 0x1f7f0, 0x1f90c, 0x1f93a, 0x1f93c, 0x1f945, 0x1f947, 0x1f9ff,
|
|
33
|
+
0x1fa70, 0x1fa7c, 0x1fa80, 0x1fa8a, 0x1fa8e, 0x1fac6, 0x1fac8, 0x1fac8,
|
|
34
|
+
0x1facd, 0x1fadc, 0x1fadf, 0x1faea, 0x1faef, 0x1faf8, 0x20000, 0x2fffd,
|
|
35
|
+
0x30000, 0x3fffd,
|
|
36
|
+
];
|
package/src/lang/docs.ts
CHANGED
|
@@ -502,7 +502,7 @@ r = Relation("users", "projects", "1:N", "owns")`,
|
|
|
502
502
|
"InfraZone.tone": "Tints the zone: `neutral` (default), `info`, `good`, `warn`, `bad`.",
|
|
503
503
|
"InfraNode.id": "Unique across the whole map. Links use it.",
|
|
504
504
|
"InfraNode.label": "Bold text in the box.",
|
|
505
|
-
"InfraNode.kind": "Picks the icon: client □, gateway ↔, service ◇, gpu ⌸, database ◎, storage
|
|
505
|
+
"InfraNode.kind": "Picks the icon: client □, gateway ↔, service ◇, gpu ⌸, database ◎, storage ⊟, queue ≡, cloud ◌.",
|
|
506
506
|
"InfraNode.status": "`up` (green dot), `degraded` (amber), `down` (red) or `unknown` (hollow, dashed box). Omit it for no dot.",
|
|
507
507
|
"InfraNode.detail": "A short second line, such as a port or a count.",
|
|
508
508
|
"InfraLink.from": "The id of the node where the arrow starts (marked with a dot).",
|