@firstpick/pi-package-webui 0.1.7 → 0.1.9
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 +14 -24
- package/bin/pi-webui.mjs +311 -1
- package/images/Guardrails_v0.1.7.png +0 -0
- package/images/Guided_GitWorkflow_v0.1.7.png +0 -0
- package/images/Main_Window_v0.1.7.png +0 -0
- package/images/Matrix_Theme_v0.1.7.png +0 -0
- package/package.json +3 -1
- package/public/app.js +452 -97
- package/public/index.html +14 -0
- package/public/styles.css +77 -0
- package/tests/mobile-static.test.mjs +12 -0
package/public/index.html
CHANGED
|
@@ -240,6 +240,20 @@
|
|
|
240
240
|
<div id="optionalFeaturesBox" class="optional-features-box muted">Checking optional features…</div>
|
|
241
241
|
</div>
|
|
242
242
|
</section>
|
|
243
|
+
<section class="side-panel-section" data-side-panel-section="codex-usage">
|
|
244
|
+
<h2>
|
|
245
|
+
<button id="sidePanelSectionToggleCodexUsage" class="side-panel-section-toggle" type="button" aria-expanded="true" aria-controls="sidePanelSectionCodexUsage" aria-label="Collapse Codex Usage section" title="Collapse Codex Usage section" data-side-panel-section-toggle="codex-usage">
|
|
246
|
+
<span class="side-panel-section-label">Codex Usage</span>
|
|
247
|
+
<span class="side-panel-section-chevron" aria-hidden="true">›</span>
|
|
248
|
+
</button>
|
|
249
|
+
</h2>
|
|
250
|
+
<div id="sidePanelSectionCodexUsage" class="side-panel-section-content">
|
|
251
|
+
<div class="codex-usage-actions">
|
|
252
|
+
<button id="refreshCodexUsageButton" type="button">Refresh usage</button>
|
|
253
|
+
</div>
|
|
254
|
+
<div id="codexUsageBox" class="codex-usage-box muted">Checking Codex usage…</div>
|
|
255
|
+
</div>
|
|
256
|
+
</section>
|
|
243
257
|
<section class="side-panel-section" data-side-panel-section="session">
|
|
244
258
|
<h2>
|
|
245
259
|
<button id="sidePanelSectionToggleSession" class="side-panel-section-toggle" type="button" aria-expanded="true" aria-controls="sidePanelSectionSession" aria-label="Collapse Session section" title="Collapse Session section" data-side-panel-section-toggle="session">
|
package/public/styles.css
CHANGED
|
@@ -808,6 +808,83 @@ body.side-panel-collapsed .terminal-tabs-shell {
|
|
|
808
808
|
background: rgba(var(--ctp-crust-rgb), 0.46);
|
|
809
809
|
box-shadow: inset 0 1px 0 rgba(255,255,255,0.035), 0 0 1rem rgba(148, 226, 213, 0.05);
|
|
810
810
|
}
|
|
811
|
+
.codex-usage-actions {
|
|
812
|
+
display: flex;
|
|
813
|
+
justify-content: flex-end;
|
|
814
|
+
margin-bottom: 0.48rem;
|
|
815
|
+
}
|
|
816
|
+
.codex-usage-actions button {
|
|
817
|
+
min-height: 34px;
|
|
818
|
+
padding: 0.32rem 0.58rem;
|
|
819
|
+
font-size: 0.72rem;
|
|
820
|
+
}
|
|
821
|
+
.codex-usage-box {
|
|
822
|
+
display: grid;
|
|
823
|
+
gap: 0.58rem;
|
|
824
|
+
padding: 0.72rem;
|
|
825
|
+
border: 1px solid rgba(180, 190, 254, 0.16);
|
|
826
|
+
border-radius: 0.85rem;
|
|
827
|
+
background: rgba(var(--ctp-crust-rgb), 0.50);
|
|
828
|
+
box-shadow: inset 0 1px 0 rgba(255,255,255,0.035), 0 0 1rem rgba(203, 166, 247, 0.05);
|
|
829
|
+
}
|
|
830
|
+
.codex-usage-summary {
|
|
831
|
+
display: flex;
|
|
832
|
+
flex-wrap: wrap;
|
|
833
|
+
justify-content: space-between;
|
|
834
|
+
gap: 0.35rem 0.55rem;
|
|
835
|
+
align-items: baseline;
|
|
836
|
+
}
|
|
837
|
+
.codex-usage-plan {
|
|
838
|
+
color: rgba(var(--ctp-text-rgb), 0.94);
|
|
839
|
+
font-weight: 900;
|
|
840
|
+
}
|
|
841
|
+
.codex-usage-fetched,
|
|
842
|
+
.codex-usage-detail,
|
|
843
|
+
.codex-usage-reset {
|
|
844
|
+
color: rgba(var(--ctp-subtext-rgb), 0.70);
|
|
845
|
+
font-size: 0.72rem;
|
|
846
|
+
line-height: 1.35;
|
|
847
|
+
}
|
|
848
|
+
.codex-usage-unavailable,
|
|
849
|
+
.codex-usage-warning {
|
|
850
|
+
color: var(--ctp-yellow);
|
|
851
|
+
font-weight: 800;
|
|
852
|
+
}
|
|
853
|
+
.codex-usage-bucket {
|
|
854
|
+
display: grid;
|
|
855
|
+
gap: 0.32rem;
|
|
856
|
+
}
|
|
857
|
+
.codex-usage-row {
|
|
858
|
+
display: flex;
|
|
859
|
+
justify-content: space-between;
|
|
860
|
+
gap: 0.6rem;
|
|
861
|
+
align-items: center;
|
|
862
|
+
}
|
|
863
|
+
.codex-usage-label {
|
|
864
|
+
min-width: 0;
|
|
865
|
+
color: rgba(var(--ctp-text-rgb), 0.86);
|
|
866
|
+
font-size: 0.78rem;
|
|
867
|
+
font-weight: 800;
|
|
868
|
+
overflow-wrap: anywhere;
|
|
869
|
+
}
|
|
870
|
+
.codex-usage-percent {
|
|
871
|
+
color: var(--ctp-teal);
|
|
872
|
+
font-size: 0.82rem;
|
|
873
|
+
}
|
|
874
|
+
.codex-usage-meter {
|
|
875
|
+
overflow: hidden;
|
|
876
|
+
height: 0.5rem;
|
|
877
|
+
border: 1px solid rgba(180, 190, 254, 0.14);
|
|
878
|
+
border-radius: 999px;
|
|
879
|
+
background: rgba(var(--ctp-surface-rgb), 0.26);
|
|
880
|
+
}
|
|
881
|
+
.codex-usage-meter-fill {
|
|
882
|
+
display: block;
|
|
883
|
+
height: 100%;
|
|
884
|
+
border-radius: inherit;
|
|
885
|
+
background: linear-gradient(90deg, var(--ctp-green), var(--ctp-yellow), var(--ctp-peach));
|
|
886
|
+
box-shadow: 0 0 0.85rem rgba(148, 226, 213, 0.20);
|
|
887
|
+
}
|
|
811
888
|
.optional-feature-row {
|
|
812
889
|
display: grid;
|
|
813
890
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
@@ -51,6 +51,8 @@ assert.match(html, /id="thinkingVisibilityStatus"/, "thinking-output visibility
|
|
|
51
51
|
assert.match(html, /id="nativeCommandDialog"/, "native slash selector UI should have a dedicated dialog");
|
|
52
52
|
assert.match(html, /id="nativeCommandSearch"[^>]*type="search"/, "native slash selector dialog should expose a filter box");
|
|
53
53
|
assert.match(html, /id="optionalFeaturesBox"/, "side panel should expose optional feature status and controls");
|
|
54
|
+
assert.match(html, /id="codexUsageBox"/, "side panel should expose Codex subscription usage status");
|
|
55
|
+
assert.match(html, /data-side-panel-section="codex-usage"/, "Codex usage should live in a collapsible side-panel section");
|
|
54
56
|
assert.match(html, /id="serverOfflinePanel"/, "PWA/offline shell should expose a backend-offline recovery panel");
|
|
55
57
|
assert.match(html, /id="copyServerCommandButton"/, "backend-offline recovery panel should expose a start-command copy button");
|
|
56
58
|
assert.match(html, /id="retryServerConnectionButton"/, "backend-offline recovery panel should expose a retry button");
|
|
@@ -258,6 +260,8 @@ assert.match(app, /Optional feature detection intentionally checks loaded Pi cap
|
|
|
258
260
|
assert.match(app, /function resetOptionalFeatureAvailability\(\)/, "optional feature state should reset across active-tab and reload boundaries");
|
|
259
261
|
assert.match(app, /function renderOptionalFeaturePanel\(\)/, "side panel should render optional feature installed/enabled state");
|
|
260
262
|
assert.match(app, /function setSidePanelSectionCollapsed\(record, collapsed/, "side panel sections should have explicit collapse/expand behavior");
|
|
263
|
+
assert.match(app, /function renderCodexUsage\(\)/, "frontend should render Codex usage buckets in the side panel");
|
|
264
|
+
assert.match(app, /api\(`\/api\/codex-usage\$\{suffix\}`, \{ scoped: false \}\)/, "Codex usage should load through a server endpoint without browser credentials");
|
|
261
265
|
assert.match(app, /restoreSidePanelSectionState\(\);\nbindSidePanelSectionToggles\(\);/, "side panel section state should restore before toggles are bound");
|
|
262
266
|
assert.match(app, /OPTIONAL_FEATURES_STORAGE_KEY/, "optional feature disable toggles should persist in browser storage");
|
|
263
267
|
assert.match(app, /function renderOptionalFeatureDependentDisplays\(\)[\s\S]*renderOptionalFeatureControls\(\);[\s\S]*renderThemeSelect\(\);[\s\S]*renderWidgets\(\);[\s\S]*renderStatus\(\);[\s\S]*renderCommands\(\);[\s\S]*renderAllMessages\(\{ preserveScroll: true \}\);[\s\S]*if \(streamRawText\) renderStreamingAssistantText\(\);/, "optional feature toggles should immediately refresh visible controls, commands, transcript, and live stream displays");
|
|
@@ -291,6 +295,10 @@ assert.match(app, /const TOOL_LIVE_UPDATE_THROTTLE_MS = 80/, "live tool cards sh
|
|
|
291
295
|
assert.match(app, /function updateLiveToolCard\(bubble, message\)[\s\S]*?body\.replaceChildren\(\);[\s\S]*?renderToolExecution\(body, message\);/, "live tool card updates should re-render the existing card body in place");
|
|
292
296
|
assert.match(app, /function scheduleLiveToolRunRender\(run[\s\S]*?liveToolRenderQueue\.set[\s\S]*?TOOL_LIVE_UPDATE_THROTTLE_MS/, "live tool update events should be queued and throttled for smoother browser output");
|
|
293
297
|
assert.match(app, /function handleToolExecutionUpdate\(event\)[\s\S]*?event\.partialResult[\s\S]*?scheduleLiveToolRunRender\(run, \{ scroll: false \}\)/, "live tool_execution_update events should update transcript-visible tool cards without replacing them per event");
|
|
298
|
+
assert.match(app, /function captureReusableToolCards\(\)[\s\S]*?\.message\.toolExecution\[data-tool-call-id\]/, "full transcript re-renders should capture existing tool cards before clearing the chat");
|
|
299
|
+
assert.match(app, /function appendMessage\(message,[\s\S]*?reusableToolCards = null[\s\S]*?reuseToolExecutionBubble\(reusableToolCards, message/, "message rendering should reuse matching tool cards instead of replacing them during refreshes");
|
|
300
|
+
assert.match(app, /function renderAllMessages\(\{ preserveScroll = false \} = \{\}\)[\s\S]*?const reusableToolCards = captureReusableToolCards\(\);[\s\S]*?appendTranscriptMessage\(item\.message,[\s\S]*?reusableToolCards,/, "transcript refreshes should pass reusable tool cards through to item rendering");
|
|
301
|
+
assert.match(app, /const keyedToolExecution = message\.role === "toolExecution" && message\.toolCallId[\s\S]*?keyedToolExecution \? "toolExecution"[\s\S]*?keyedToolExecution \? "" : message\.title[\s\S]*?keyedToolExecution \? "" : message\.timestamp/, "tool action entry identity should stay stable when live transient cards become persisted transcript cards");
|
|
294
302
|
assert.match(app, /appendText\(preview, toolResultPreviewText\(message, 10\), "code-block tool-result-preview-text"\)/, "collapsed tool results should render the first ten preview lines by default");
|
|
295
303
|
assert.match(app, /function assistantDisplayMessages\(message\)/, "assistant history should split thinking and tool-call parts out of the final Assistant output card");
|
|
296
304
|
assert.match(app, /function assistantHasToolCallAfter\(content, index\)/, "assistant text that precedes a tool call should be detectable and suppressible");
|
|
@@ -486,6 +494,10 @@ assert.ok(icon512.length > icon192.length, "PWA 512px icon should be present and
|
|
|
486
494
|
assert.ok(matrixBackground.length > 100000, "Matrix background image should be present as an optimized WebP asset");
|
|
487
495
|
assert.ok(mochaBackground.length > 8000, "Catppuccin Mocha background image should be present as a compact PNG asset");
|
|
488
496
|
|
|
497
|
+
assert.match(server, /AuthStorage, SessionManager/, "server should import AuthStorage for safe OAuth token refresh");
|
|
498
|
+
assert.match(server, /const CODEX_TOKEN_REFRESH_SKEW_MS = 5 \* 60 \* 1000/, "server should refresh Codex OAuth tokens before they expire");
|
|
499
|
+
assert.match(server, /url\.pathname === "\/api\/codex-usage" && req\.method === "GET"/, "server should expose a sanitized Codex usage endpoint");
|
|
500
|
+
assert.match(server, /OPENAI_CODEX_USAGE_ENDPOINT/, "server should query Codex usage from the backend, not the browser");
|
|
489
501
|
assert.match(server, /const NATIVE_SLASH_COMMANDS = \[/, "server should define Pi native slash commands for autocomplete");
|
|
490
502
|
assert.match(server, /\{ name: "reload", description: "Reload keybindings, extensions, skills, prompts, and themes" \}/, "native /reload should be advertised for autocomplete");
|
|
491
503
|
assert.match(server, /function parseSlashCommand\(message\)/, "server should parse native slash commands before prompt forwarding");
|