pi-web 0.5.0 → 0.6.1
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 +13 -5
- package/build/server/server.js +22 -8
- package/build/server/sessions.js +41 -14
- package/dist/assets/index-Bj7L_AxY.css +1 -0
- package/dist/assets/index-CT7aAMWr.js +52 -0
- package/dist/index.html +2 -2
- package/package.json +4 -2
- package/dist/assets/index-BCDK0PJs.css +0 -1
- package/dist/assets/index-o4RbTX68.js +0 -13
package/README.md
CHANGED
|
@@ -10,14 +10,21 @@ A web UI for the [pi coding agent](https://github.com/badlogic/pi-mono).
|
|
|
10
10
|
npx -y pi-web@latest
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Then open [http://
|
|
13
|
+
Then open [http://127.0.0.1:8192](http://127.0.0.1:8192) in your browser.
|
|
14
14
|
|
|
15
15
|
## Options
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
--port <number>
|
|
19
|
-
--host <string>
|
|
20
|
-
--
|
|
18
|
+
--port <number> Port to listen on (default: 8192, env: PORT)
|
|
19
|
+
--host <string> Host to bind to (default: 127.0.0.1, env: HOST)
|
|
20
|
+
--agent <pi|omp> Agent backend profile (default: pi)
|
|
21
|
+
--help Show help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To run against Oh My Pi, start with:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx -y pi-web@latest --agent omp
|
|
21
28
|
```
|
|
22
29
|
|
|
23
30
|
## Features
|
|
@@ -35,7 +42,8 @@ Then open [http://localhost:3100](http://localhost:3100) in your browser.
|
|
|
35
42
|
git clone https://github.com/ravshansbox/pi-web
|
|
36
43
|
cd pi-web
|
|
37
44
|
npm install
|
|
38
|
-
npm run dev
|
|
45
|
+
npm run dev:pi # Pi backend
|
|
46
|
+
npm run dev:omp # Oh My Pi backend
|
|
39
47
|
```
|
|
40
48
|
|
|
41
49
|
Requires Node.js 22+.
|
package/build/server/server.js
CHANGED
|
@@ -14,9 +14,10 @@ pi-web - Web UI for the pi coding agent
|
|
|
14
14
|
Usage: pi-web [options]
|
|
15
15
|
|
|
16
16
|
Options:
|
|
17
|
-
--port <number>
|
|
18
|
-
--host <string>
|
|
19
|
-
|
|
17
|
+
--port <number> Port to listen on (default: 8192, env: PORT)
|
|
18
|
+
--host <string> Host to bind to (default: 127.0.0.1, env: HOST)
|
|
19
|
+
--agent <pi|omp> Agent backend profile (default: pi)
|
|
20
|
+
-h, --help Show this help message
|
|
20
21
|
`.trim());
|
|
21
22
|
process.exit(0);
|
|
22
23
|
}
|
|
@@ -30,9 +31,22 @@ function getArg(name) {
|
|
|
30
31
|
return process.argv[idx + 1];
|
|
31
32
|
return undefined;
|
|
32
33
|
}
|
|
34
|
+
function parseAgent(value) {
|
|
35
|
+
const agent = (value || 'pi').toLowerCase();
|
|
36
|
+
if (agent === 'pi' || agent === 'omp')
|
|
37
|
+
return agent;
|
|
38
|
+
console.error(`invalid --agent value "${value}". expected "pi" or "omp"`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
function getAgentCommand(agent) {
|
|
42
|
+
return agent === 'omp'
|
|
43
|
+
? 'npx -y @oh-my-pi/pi-coding-agent@latest'
|
|
44
|
+
: 'npx -y @mariozechner/pi-coding-agent@latest';
|
|
45
|
+
}
|
|
46
|
+
const AGENT = parseAgent(getArg('agent'));
|
|
33
47
|
const PORT = parseInt(getArg('port') || process.env.PORT || '8192', 10);
|
|
34
48
|
const HOST = getArg('host') || process.env.HOST || '127.0.0.1';
|
|
35
|
-
const
|
|
49
|
+
const AGENT_CMD = getAgentCommand(AGENT);
|
|
36
50
|
const DEFAULT_IDLE_SESSION_TTL_MS = 60_000;
|
|
37
51
|
const idleSessionTtlMsEnv = parseInt(process.env.PI_WEB_IDLE_SESSION_TTL_MS || '', 10);
|
|
38
52
|
const IDLE_SESSION_TTL_MS = Number.isFinite(idleSessionTtlMsEnv) && idleSessionTtlMsEnv >= 0
|
|
@@ -106,7 +120,7 @@ const server = createServer((req, res) => {
|
|
|
106
120
|
if (req.url?.startsWith('/api/sessions')) {
|
|
107
121
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
108
122
|
const cwd = url.searchParams.get('cwd') || undefined;
|
|
109
|
-
listSessions({ cwd, limit: 50 })
|
|
123
|
+
listSessions({ cwd, limit: 50, agent: AGENT })
|
|
110
124
|
.then((data) => {
|
|
111
125
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
112
126
|
res.end(JSON.stringify(data));
|
|
@@ -140,7 +154,7 @@ const server = createServer((req, res) => {
|
|
|
140
154
|
res.end(JSON.stringify({ error: 'cwd and filename parameters required' }));
|
|
141
155
|
return;
|
|
142
156
|
}
|
|
143
|
-
const file = getSessionFilePath(cwd, filename);
|
|
157
|
+
const file = getSessionFilePath(cwd, filename, AGENT);
|
|
144
158
|
if (req.method === 'DELETE') {
|
|
145
159
|
try {
|
|
146
160
|
unlinkSync(file);
|
|
@@ -272,10 +286,10 @@ function registerDiscoveredSessionKey(managed, event) {
|
|
|
272
286
|
registerManagedSessionKey(managed, key);
|
|
273
287
|
}
|
|
274
288
|
function createManagedSession(cwd, sessionFile) {
|
|
275
|
-
const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile) : undefined;
|
|
289
|
+
const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile, AGENT) : undefined;
|
|
276
290
|
let managed = null;
|
|
277
291
|
const rpc = new RpcSession({
|
|
278
|
-
piCmd:
|
|
292
|
+
piCmd: AGENT_CMD,
|
|
279
293
|
cwd,
|
|
280
294
|
sessionFile: sessionPath,
|
|
281
295
|
onEvent: (event) => {
|
package/build/server/sessions.js
CHANGED
|
@@ -1,25 +1,40 @@
|
|
|
1
1
|
import { readdir } from 'node:fs/promises';
|
|
2
|
-
import { basename, join } from 'node:path';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { createReadStream } from 'node:fs';
|
|
5
5
|
import { createInterface } from 'node:readline';
|
|
6
|
-
const
|
|
7
|
-
function
|
|
8
|
-
|
|
6
|
+
const HOME_DIR = resolve(homedir());
|
|
7
|
+
function getSessionDir(agent) {
|
|
8
|
+
const configDir = agent === 'omp' ? '.omp' : '.pi';
|
|
9
|
+
return join(HOME_DIR, configDir, 'agent', 'sessions');
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
function cwdToSessionDir(cwd, agent) {
|
|
12
|
+
const normalisedCwd = resolve(cwd);
|
|
13
|
+
if (agent === 'omp') {
|
|
14
|
+
if (normalisedCwd === HOME_DIR ||
|
|
15
|
+
normalisedCwd.startsWith(`${HOME_DIR}/`) ||
|
|
16
|
+
normalisedCwd.startsWith(`${HOME_DIR}\\`)) {
|
|
17
|
+
const relative = normalisedCwd.slice(HOME_DIR.length).replace(/^[/\\]/, '');
|
|
18
|
+
return `-${relative.replace(/[/\\:]/g, '-')}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const encoded = normalisedCwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-');
|
|
22
|
+
return `--${encoded}--`;
|
|
23
|
+
}
|
|
24
|
+
export function getSessionFilePath(cwd, filename, agent = 'pi') {
|
|
25
|
+
return join(getSessionDir(agent), cwdToSessionDir(cwd, agent), filename);
|
|
12
26
|
}
|
|
13
27
|
export async function listSessions(opts) {
|
|
14
|
-
const { cwd, limit = 30 } = opts;
|
|
28
|
+
const { cwd, limit = 30, agent = 'pi' } = opts;
|
|
15
29
|
const results = [];
|
|
30
|
+
const sessionDir = getSessionDir(agent);
|
|
16
31
|
try {
|
|
17
|
-
const cwdDirs = await readdir(
|
|
32
|
+
const cwdDirs = await readdir(sessionDir, { withFileTypes: true });
|
|
18
33
|
const targetDirs = cwd
|
|
19
|
-
? cwdDirs.filter((d) => d.isDirectory() && d.name === cwdToSessionDir(cwd))
|
|
34
|
+
? cwdDirs.filter((d) => d.isDirectory() && d.name === cwdToSessionDir(cwd, agent))
|
|
20
35
|
: cwdDirs.filter((d) => d.isDirectory());
|
|
21
36
|
for (const dir of targetDirs) {
|
|
22
|
-
const dirPath = join(
|
|
37
|
+
const dirPath = join(sessionDir, dir.name);
|
|
23
38
|
let files;
|
|
24
39
|
try {
|
|
25
40
|
files = (await readdir(dirPath)).filter((f) => f.endsWith('.jsonl'));
|
|
@@ -67,9 +82,13 @@ export async function readSessionMessages(filePath) {
|
|
|
67
82
|
continue;
|
|
68
83
|
if (role === 'toolResult' || role === 'tool_result') {
|
|
69
84
|
const id = msg.toolCallId;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
85
|
+
if (id) {
|
|
86
|
+
toolResults.set(id, {
|
|
87
|
+
content: msg.content,
|
|
88
|
+
details: msg.details,
|
|
89
|
+
isError: Boolean(msg.isError),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
73
92
|
continue;
|
|
74
93
|
}
|
|
75
94
|
if (role === 'tool')
|
|
@@ -96,7 +115,15 @@ export async function readSessionMessages(filePath) {
|
|
|
96
115
|
continue;
|
|
97
116
|
for (const block of msg.content) {
|
|
98
117
|
if (block.type === 'toolCall' && block.id && toolResults.has(block.id)) {
|
|
99
|
-
|
|
118
|
+
const result = toolResults.get(block.id);
|
|
119
|
+
if (!result)
|
|
120
|
+
continue;
|
|
121
|
+
block.result = {
|
|
122
|
+
content: result.content,
|
|
123
|
+
details: result.details,
|
|
124
|
+
isError: result.isError,
|
|
125
|
+
};
|
|
126
|
+
block.isError = result.isError;
|
|
100
127
|
}
|
|
101
128
|
}
|
|
102
129
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap";@layer components;@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-semibold:600;--tracking-wide:.025em;--leading-relaxed:1.625;--radius-lg:.5rem;--radius-xl:.75rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-pi-accent:#5a8080;--color-pi-border-muted:#b0b0b0;--color-pi-error:#a55;--color-pi-warning:#9a7326;--color-pi-muted:#6c6c6c;--color-pi-dim:#767676;--color-pi-user-bg:#e8e8e8;--color-pi-tool-pending:#e8e8f0;--color-pi-tool-success:#e8f0e8;--color-pi-tool-error:#f0e8e8;--color-pi-md-heading:#9a7326;--color-pi-md-link:#547da7;--color-pi-md-code:#5a8080;--color-pi-md-code-block:#588458;--color-pi-tool-output:#6c6c6c;--color-pi-page-bg:#f8f8f8;--color-pi-card-bg:#fff}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.relative{position:relative}.start{inset-inline-start:var(--spacing)}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-3{top:calc(var(--spacing) * 3)}.-right-1\.5{right:calc(var(--spacing) * -1.5)}.right-3{right:calc(var(--spacing) * 3)}.mx-auto{margin-inline:auto}.my-1\.5{margin-block:calc(var(--spacing) * 1.5)}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline-flex{display:inline-flex}.aspect-square{aspect-ratio:1}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-10{height:calc(var(--spacing) * 10)}.h-\[42px\]{height:42px}.h-full{height:100%}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[200px\]{max-height:200px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[42px\]{min-height:42px}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-10{width:calc(var(--spacing) * 10)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-pi-border-muted>:not(:last-child)){border-color:var(--color-pi-border-muted)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-pi-border-muted{border-color:var(--color-pi-border-muted)}.bg-pi-accent{background-color:var(--color-pi-accent)}.bg-pi-card-bg{background-color:var(--color-pi-card-bg)}.bg-pi-error{background-color:var(--color-pi-error)}.bg-pi-page-bg{background-color:var(--color-pi-page-bg)}.bg-pi-tool-error{background-color:var(--color-pi-tool-error)}.bg-pi-tool-pending{background-color:var(--color-pi-tool-pending)}.bg-pi-tool-success{background-color:var(--color-pi-tool-success)}.bg-pi-user-bg{background-color:var(--color-pi-user-bg)}.bg-pi-warning{background-color:var(--color-pi-warning)}.bg-white{background-color:var(--color-white)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-pi-accent{color:var(--color-pi-accent)}.text-pi-dim{color:var(--color-pi-dim)}.text-pi-error{color:var(--color-pi-error)}.text-pi-md-code{color:var(--color-pi-md-code)}.text-pi-md-code-block{color:var(--color-pi-md-code-block)}.text-pi-md-heading{color:var(--color-pi-md-heading)}.text-pi-md-link{color:var(--color-pi-md-link)}.text-pi-muted{color:var(--color-pi-muted)}.text-pi-tool-output{color:var(--color-pi-tool-output)}.text-pi-warning{color:var(--color-pi-warning)}.text-white{color:var(--color-white)}.underline{text-decoration-line:underline}.opacity-60{opacity:.6}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:inline-flex:is(:where(.group):hover *){display:inline-flex}.hover\:bg-pi-tool-error:hover{background-color:var(--color-pi-tool-error)}.hover\:bg-pi-user-bg:hover{background-color:var(--color-pi-user-bg)}.hover\:text-pi-accent:hover{color:var(--color-pi-accent)}.hover\:text-pi-error:hover{color:var(--color-pi-error)}.hover\:opacity-85:hover{opacity:.85}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-pi-accent:focus{border-color:var(--color-pi-accent)}.disabled\:cursor-default:disabled{cursor:default}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:px-6{padding-inline:calc(var(--spacing) * 6)}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}}html,body,#root{height:100dvh;overflow:hidden}*,:before,:after{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important;font-size:14px!important}@supports (-webkit-touch-callout:none){.prompt-input{font-size:16px!important}}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.4}}.animate-pulse-dot{animation:1s infinite pulse-dot}.select-fit-content{field-sizing:content}.tool-io-pre{white-space:pre;tab-size:4;margin:0;line-height:1.3;overflow-x:auto}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}
|