@firstpick/pi-package-webui 0.1.0 → 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/README.md +142 -44
- package/bin/pi-webui.mjs +878 -43
- package/index.ts +454 -22
- package/package.json +7 -2
- package/public/app.js +1185 -44
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon.svg +8 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/index.html +74 -20
- package/public/manifest.webmanifest +40 -0
- package/public/service-worker.js +46 -0
- package/public/styles.css +1014 -19
- package/tests/mobile-static.test.mjs +170 -0
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Pi Web UI">
|
|
2
|
+
<rect width="64" height="64" rx="14" fill="#11111b"/>
|
|
3
|
+
<rect x="5" y="5" width="54" height="54" rx="12" fill="#1e1e2e" stroke="#89b4fa" stroke-width="3"/>
|
|
4
|
+
<path d="M17 20h31" fill="none" stroke="#f5c2e7" stroke-width="8" stroke-linecap="round"/>
|
|
5
|
+
<path d="M24 21v27" fill="none" stroke="#94e2d5" stroke-width="8" stroke-linecap="round"/>
|
|
6
|
+
<path d="M43 21v27" fill="none" stroke="#94e2d5" stroke-width="8" stroke-linecap="round"/>
|
|
7
|
+
<path d="M17 48h10" fill="none" stroke="#cba6f7" stroke-width="6" stroke-linecap="round" opacity="0.95"/>
|
|
8
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
package/public/index.html
CHANGED
|
@@ -2,8 +2,16 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content" />
|
|
6
6
|
<title>Pi Web UI</title>
|
|
7
|
+
<meta name="theme-color" content="#11111b" />
|
|
8
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
9
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
10
|
+
<meta name="apple-mobile-web-app-title" content="Pi Web" />
|
|
11
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
12
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
13
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
14
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
7
15
|
<link rel="stylesheet" href="/styles.css" />
|
|
8
16
|
</head>
|
|
9
17
|
<body>
|
|
@@ -16,8 +24,15 @@
|
|
|
16
24
|
|
|
17
25
|
<main class="layout">
|
|
18
26
|
<section class="chat-panel">
|
|
27
|
+
<header class="terminal-tabs-shell">
|
|
28
|
+
<button id="terminalTabsToggleButton" class="terminal-tabs-toggle-button" type="button" aria-controls="tabBar" aria-expanded="false">Tabs</button>
|
|
29
|
+
<div id="tabBar" class="terminal-tabs" role="tablist" aria-label="Pi terminal tabs">
|
|
30
|
+
<button id="newTabButton" class="terminal-new-tab-button" type="button" title="Start a separate isolated Pi terminal">+ Tab</button>
|
|
31
|
+
</div>
|
|
32
|
+
</header>
|
|
19
33
|
<div id="widgetArea" class="widget-area"></div>
|
|
20
34
|
<div id="chat" class="chat" aria-live="polite"></div>
|
|
35
|
+
<button id="jumpToLatestButton" class="jump-to-latest-button" type="button" hidden>Latest ↓</button>
|
|
21
36
|
<div id="statusBar" class="statusbar" aria-live="polite"></div>
|
|
22
37
|
<section id="gitWorkflowPanel" class="git-workflow-panel" aria-live="polite" hidden>
|
|
23
38
|
<div class="git-workflow-header">
|
|
@@ -33,34 +48,50 @@
|
|
|
33
48
|
<div id="gitWorkflowActions" class="git-workflow-actions"></div>
|
|
34
49
|
</section>
|
|
35
50
|
<form id="composer" class="composer">
|
|
36
|
-
<textarea id="promptInput" rows="
|
|
51
|
+
<textarea id="promptInput" rows="1" enterkeyhint="enter" placeholder="Ask Pi…"></textarea>
|
|
37
52
|
<div id="commandSuggest" class="command-suggest" role="listbox" aria-label="Command suggestions" hidden></div>
|
|
38
53
|
<div class="composer-row">
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
<button id="composerActionsButton" class="composer-actions-button" type="button" aria-controls="composerActionsPanel" aria-expanded="false">Actions</button>
|
|
55
|
+
<div id="composerActionsPanel" class="composer-actions-panel" aria-label="Composer actions">
|
|
56
|
+
<label>
|
|
57
|
+
Busy prompt behavior
|
|
58
|
+
<select id="busyBehavior">
|
|
59
|
+
<option value="followUp">follow-up</option>
|
|
60
|
+
<option value="steer">steer</option>
|
|
61
|
+
</select>
|
|
62
|
+
</label>
|
|
63
|
+
<button id="newSessionButton" class="composer-new-button" type="button">New</button>
|
|
64
|
+
<button id="compactButton" class="composer-compact-button" type="button" title="Compact the current Pi session context">Compact</button>
|
|
65
|
+
<button
|
|
66
|
+
id="gitWorkflowButton"
|
|
67
|
+
class="composer-git-button"
|
|
68
|
+
type="button"
|
|
69
|
+
title="Guided Git workflow: git add ., /git-staged-msg, preview messages, commit short/long, git push. Cancel is available at each step."
|
|
70
|
+
aria-label="Start guided Git workflow: git add dot, run git-staged-msg, preview messages, commit short or long, then git push. Cancel is available at each step."
|
|
71
|
+
data-tooltip="Guided Git workflow: 1. Run git add . 2. Run /git-staged-msg 3. Preview short + long messages 4. Commit with short or long message 5. Run git push Cancel is available at each step."
|
|
72
|
+
>Git workflow</button>
|
|
73
|
+
</div>
|
|
46
74
|
<div class="spacer"></div>
|
|
47
|
-
<button id="newSessionButton" class="composer-new-button" type="button">New</button>
|
|
48
|
-
<button id="compactButton" class="composer-compact-button" type="button" title="Compact the current Pi session context">Compact</button>
|
|
49
75
|
<button
|
|
50
|
-
id="
|
|
51
|
-
|
|
76
|
+
id="steerButton"
|
|
77
|
+
type="button"
|
|
78
|
+
title="Type your message first, then tap Steer to guide the active Pi run without sending a normal prompt. Use it for corrections or direction changes while Pi is working."
|
|
79
|
+
aria-label="Steer: type your message first, then tap this button to guide the active Pi run."
|
|
80
|
+
data-tooltip="Steer usage: 1. Type your steering message in the textarea. 2. Tap Steer to guide the active Pi run. Use for corrections or direction changes while Pi is working."
|
|
81
|
+
>Steer</button>
|
|
82
|
+
<button
|
|
83
|
+
id="followUpButton"
|
|
52
84
|
type="button"
|
|
53
|
-
title="
|
|
54
|
-
aria-label="
|
|
55
|
-
data-tooltip="
|
|
56
|
-
>
|
|
57
|
-
<button id="
|
|
58
|
-
<button id="followUpButton" type="button">Follow-up</button>
|
|
59
|
-
<button type="submit" class="primary">Send</button>
|
|
85
|
+
title="Type your message first, then tap Follow-up to send it as a follow-up instead of a normal prompt. Use it to add extra context or the next request."
|
|
86
|
+
aria-label="Follow-up: type your message first, then tap this button to send it as a follow-up."
|
|
87
|
+
data-tooltip="Follow-up usage: 1. Type your message in the textarea. 2. Tap Follow-up to send it as a follow-up. Use for extra context or the next request."
|
|
88
|
+
>Follow-up</button>
|
|
89
|
+
<button id="sendButton" type="submit" class="primary">Send</button>
|
|
60
90
|
</div>
|
|
61
91
|
</form>
|
|
62
92
|
</section>
|
|
63
93
|
|
|
94
|
+
<button id="sidePanelBackdrop" class="side-panel-backdrop" type="button" aria-label="Close side panel" hidden></button>
|
|
64
95
|
<aside id="sidePanel" class="side-panel">
|
|
65
96
|
<div class="side-panel-header">
|
|
66
97
|
<div>
|
|
@@ -95,6 +126,11 @@
|
|
|
95
126
|
</select>
|
|
96
127
|
<button id="setThinkingButton" type="button">Set thinking</button>
|
|
97
128
|
</div>
|
|
129
|
+
<div class="control-field network-control-field">
|
|
130
|
+
<label>Network</label>
|
|
131
|
+
<div id="networkStatus" class="network-status closed">Local only</div>
|
|
132
|
+
<button id="openNetworkButton" type="button">Open to network</button>
|
|
133
|
+
</div>
|
|
98
134
|
<button id="abortButton" type="button" class="danger">Abort</button>
|
|
99
135
|
</div>
|
|
100
136
|
<h2>Session</h2>
|
|
@@ -118,6 +154,24 @@
|
|
|
118
154
|
</form>
|
|
119
155
|
</dialog>
|
|
120
156
|
|
|
157
|
+
<dialog id="pathPickerDialog" class="extension-dialog path-picker-dialog">
|
|
158
|
+
<form method="dialog">
|
|
159
|
+
<h2 id="pathPickerTitle">Choose working directory</h2>
|
|
160
|
+
<div class="path-picker-current-row">
|
|
161
|
+
<p id="pathPickerCurrent" class="path-picker-current"></p>
|
|
162
|
+
<button id="pathPickerAddFastPickButton" type="button" class="path-picker-add-fast-pick">Add fast pick</button>
|
|
163
|
+
</div>
|
|
164
|
+
<div id="pathPickerFastPicks" class="path-picker-fast-picks" aria-label="Fast picks"></div>
|
|
165
|
+
<div id="pathPickerRoots" class="path-picker-roots"></div>
|
|
166
|
+
<div id="pathPickerList" class="path-picker-list" role="listbox" aria-label="Directories"></div>
|
|
167
|
+
<p id="pathPickerError" class="path-picker-error" hidden></p>
|
|
168
|
+
<menu>
|
|
169
|
+
<button id="pathPickerCancelButton" type="button">Cancel</button>
|
|
170
|
+
<button id="pathPickerChooseButton" type="button" class="primary">Use this directory</button>
|
|
171
|
+
</menu>
|
|
172
|
+
</form>
|
|
173
|
+
</dialog>
|
|
174
|
+
|
|
121
175
|
<script type="module" src="/app.js"></script>
|
|
122
176
|
</body>
|
|
123
177
|
</html>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "/",
|
|
3
|
+
"name": "Pi Web UI",
|
|
4
|
+
"short_name": "Pi Web",
|
|
5
|
+
"description": "Mobile-friendly web interface for Pi coding agent sessions.",
|
|
6
|
+
"start_url": "/",
|
|
7
|
+
"scope": "/",
|
|
8
|
+
"display": "standalone",
|
|
9
|
+
"display_override": ["window-controls-overlay", "standalone", "minimal-ui", "browser"],
|
|
10
|
+
"background_color": "#11111b",
|
|
11
|
+
"theme_color": "#11111b",
|
|
12
|
+
"orientation": "any",
|
|
13
|
+
"categories": ["developer", "productivity", "utilities"],
|
|
14
|
+
"icons": [
|
|
15
|
+
{
|
|
16
|
+
"src": "/favicon.svg",
|
|
17
|
+
"sizes": "any",
|
|
18
|
+
"type": "image/svg+xml",
|
|
19
|
+
"purpose": "any"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"src": "/apple-touch-icon.png",
|
|
23
|
+
"sizes": "180x180",
|
|
24
|
+
"type": "image/png",
|
|
25
|
+
"purpose": "any"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"src": "/icon-192.png",
|
|
29
|
+
"sizes": "192x192",
|
|
30
|
+
"type": "image/png",
|
|
31
|
+
"purpose": "any maskable"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"src": "/icon-512.png",
|
|
35
|
+
"sizes": "512x512",
|
|
36
|
+
"type": "image/png",
|
|
37
|
+
"purpose": "any maskable"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const CACHE_NAME = "pi-webui-pwa-v1";
|
|
2
|
+
const APP_SHELL = [
|
|
3
|
+
"/",
|
|
4
|
+
"/index.html",
|
|
5
|
+
"/styles.css",
|
|
6
|
+
"/app.js",
|
|
7
|
+
"/favicon.svg",
|
|
8
|
+
"/apple-touch-icon.png",
|
|
9
|
+
"/icon-192.png",
|
|
10
|
+
"/icon-512.png",
|
|
11
|
+
"/manifest.webmanifest",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
self.addEventListener("install", (event) => {
|
|
15
|
+
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)).then(() => self.skipWaiting()));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
self.addEventListener("activate", (event) => {
|
|
19
|
+
event.waitUntil(
|
|
20
|
+
caches.keys()
|
|
21
|
+
.then((keys) => Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))))
|
|
22
|
+
.then(() => self.clients.claim()),
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
self.addEventListener("fetch", (event) => {
|
|
27
|
+
const { request } = event;
|
|
28
|
+
if (request.method !== "GET") return;
|
|
29
|
+
|
|
30
|
+
const url = new URL(request.url);
|
|
31
|
+
if (url.origin !== self.location.origin || url.pathname.startsWith("/api/")) return;
|
|
32
|
+
|
|
33
|
+
if (request.mode === "navigate") {
|
|
34
|
+
event.respondWith(fetch(request).catch(() => caches.match("/index.html")));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!APP_SHELL.includes(url.pathname)) return;
|
|
39
|
+
event.respondWith(
|
|
40
|
+
caches.match(request).then((cached) => cached || fetch(request).then((response) => {
|
|
41
|
+
const copy = response.clone();
|
|
42
|
+
caches.open(CACHE_NAME).then((cache) => cache.put(request, copy));
|
|
43
|
+
return response;
|
|
44
|
+
})),
|
|
45
|
+
);
|
|
46
|
+
});
|