feat: add PWA install icon and uppercase branding

This commit is contained in:
Federico Jaramillo Martinez
2026-05-23 20:50:45 +02:00
parent 428f7bb8c2
commit 1546143038
38 changed files with 248 additions and 211 deletions
+3
View File
@@ -4,7 +4,10 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>PI WEB</title>
<meta name="theme-color" content="#0d1117" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/manifest.webmanifest" />
<style>
:root {
color-scheme: dark;
Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

+1 -1
View File
@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="title desc">
<title id="title">piweb mark</title>
<title id="title">PI WEB mark</title>
<desc id="desc">A horizontal brand bar.</desc>
<style>
.bar { fill: #00f0d8; }

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 392 B

+24
View File
@@ -0,0 +1,24 @@
{
"name": "PI WEB",
"short_name": "PI WEB",
"description": "Remote web UI and browser control plane for persistent Pi Coding Agent sessions.",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#0d1117",
"theme_color": "#0d1117",
"icons": [
{
"src": "/pwa-icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/pwa-icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

+4 -4
View File
@@ -359,9 +359,9 @@ function optionalPiWebInstallationInfo(value: unknown): PiWebInstallationInfo |
if (value === undefined) return undefined;
const record = requireRecord(value);
const kind = requireString(record, "kind");
if (kind !== "pi-package" && kind !== "npm-global" && kind !== "local" && kind !== "unknown") throw new Error("Invalid Pi Web installation kind");
if (kind !== "pi-package" && kind !== "npm-global" && kind !== "local" && kind !== "unknown") throw new Error("Invalid PI WEB installation kind");
const scope = record["scope"];
if (scope !== undefined && scope !== "user" && scope !== "project") throw new Error("Invalid Pi Web installation scope");
if (scope !== undefined && scope !== "user" && scope !== "project") throw new Error("Invalid PI WEB installation scope");
return {
kind,
...optionalField("path", optionalString(record, "path")),
@@ -400,12 +400,12 @@ function parsePiWebStatusMessage(value: unknown): PiWebStatusMessage {
}
function parsePiWebServiceComponent(value: unknown): PiWebServiceComponent {
if (value !== "web" && value !== "sessiond") throw new Error("Invalid Pi Web service component");
if (value !== "web" && value !== "sessiond") throw new Error("Invalid PI WEB service component");
return value;
}
function parsePiWebStatusSeverity(value: unknown): PiWebStatusSeverity {
if (value !== "info" && value !== "warning" && value !== "error") throw new Error("Invalid Pi Web status severity");
if (value !== "info" && value !== "warning" && value !== "error") throw new Error("Invalid PI WEB status severity");
return value;
}
+5 -5
View File
@@ -218,7 +218,7 @@ export class PiWebApp extends LitElement {
try {
this.setState({ piWebStatus: await piWebApi.piWebStatus() });
} catch (error) {
console.warn("Failed to refresh Pi Web status", error);
console.warn("Failed to refresh PI WEB status", error);
}
}
@@ -431,7 +431,7 @@ export class PiWebApp extends LitElement {
});
return html`
<header>
<strong>Pi Web</strong>
<strong>PI WEB</strong>
<button title="Show Actions" aria-label="Show Actions" @click=${() => { this.setState({ actionPaletteOpen: true }); }}>Actions</button>
</header>
<project-list
@@ -517,7 +517,7 @@ export class PiWebApp extends LitElement {
if (this.state.isLoadingProjects) {
return {
title: "Loading projects…",
body: "Looking for projects you have added to Pi Web.",
body: "Looking for projects you have added to PI WEB.",
};
}
if (project === undefined) {
@@ -603,13 +603,13 @@ export class PiWebApp extends LitElement {
try {
this.plugins.register(registration);
} catch (error) {
console.warn(`Failed to register Pi Web plugin ${registration.id}`, error);
console.warn(`Failed to register PI WEB plugin ${registration.id}`, error);
}
}
this.applyPreferredTheme(false);
this.requestUpdate();
} catch (error) {
console.warn("Failed to load external Pi Web plugins", error);
console.warn("Failed to load external PI WEB plugins", error);
}
}
+1 -1
View File
@@ -92,7 +92,7 @@ export class ProjectList extends LitElement {
private close(project: Project) {
this.openMenuProjectId = undefined;
if (confirm(`Close ${project.name}?\n\nThis only removes it from Pi Web; it will not change the project folder.`)) this.onClose?.(project);
if (confirm(`Close ${project.name}?\n\nThis only removes it from PI WEB; it will not change the project folder.`)) this.onClose?.(project);
}
static override styles = listStyles;
+1 -1
View File
@@ -43,7 +43,7 @@ export function createCoreActions(): PluginAction[] {
{
id: "theme.select",
title: "Select Theme",
description: "Choose the Pi Web color theme",
description: "Choose the PI WEB color theme",
group: "Preferences",
run: (context) => { context.openThemePicker(); },
},
+1 -1
View File
@@ -4,7 +4,7 @@ import { createCoreWorkspacePanels } from "./panels";
export const corePlugin: PiWebPlugin = {
apiVersion: 1,
name: "Pi Web Core",
name: "PI WEB Core",
activate: () => ({
contributions: {
actions: createCoreActions(),
+1 -1
View File
@@ -21,7 +21,7 @@ export async function loadExternalPlugins(manifestUrl = "/pi-web-plugins/manifes
const plugin = parsePluginModule(module, moduleUrl);
registrations.push({ id: entry.id, plugin });
} catch (error) {
console.warn(`Failed to load Pi Web plugin ${entry.module}`, error);
console.warn(`Failed to load PI WEB plugin ${entry.module}`, error);
}
}
return registrations;
+1 -1
View File
@@ -120,7 +120,7 @@ describe("PluginRegistry", () => {
expect(new Set(shortcuts.map(([, shortcut]) => shortcut)).size).toBe(shortcuts.length);
});
it("collects built-in Pi Web themes from an in-app plugin", () => {
it("collects built-in PI WEB themes from an in-app plugin", () => {
const registry = new PluginRegistry();
registry.register({ id: "themes", plugin: themePackPlugin });
+9 -9
View File
@@ -116,30 +116,30 @@ const piWebLightTokens = {
export const themePackPlugin: PiWebPlugin = {
apiVersion: 1,
name: "Pi Web Themes",
name: "PI WEB Themes",
activate: () => ({
contributions: {
themes: [
{
id: "pi-web-dark",
name: "Pi Web Dark",
description: "Dark Pi Web palette.",
name: "PI WEB Dark",
description: "Dark PI WEB palette.",
order: 10,
colorScheme: "dark",
tokens: piWebDarkTokens,
},
{
id: "pi-web-light",
name: "Pi Web Light",
description: "Light Pi Web palette.",
name: "PI WEB Light",
description: "Light PI WEB palette.",
order: 20,
colorScheme: "light",
tokens: piWebLightTokens,
},
{
id: "classic",
name: "Pi Web Classic",
description: "The original Pi Web dark palette.",
name: "PI WEB Classic",
description: "The original PI WEB dark palette.",
order: 30,
colorScheme: "dark",
tokens: classicTokens,
@@ -148,8 +148,8 @@ export const themePackPlugin: PiWebPlugin = {
themePairs: [
{
id: "pi-web",
name: "Pi Web",
description: "Follow the system light/dark preference with Pi Web themes.",
name: "PI WEB",
description: "Follow the system light/dark preference with PI WEB themes.",
order: 10,
light: "pi-web-light",
dark: "pi-web-dark",
+4 -4
View File
@@ -41,9 +41,9 @@ const tokens = {
} satisfies ThemeTokens;
const themes = [
theme("pi-web-dark", "Pi Web Dark", "dark"),
theme("pi-web-light", "Pi Web Light", "light"),
theme("classic", "Pi Web Classic", "dark"),
theme("pi-web-dark", "PI WEB Dark", "dark"),
theme("pi-web-light", "PI WEB Light", "light"),
theme("classic", "PI WEB Classic", "dark"),
];
const themePairs: QualifiedThemePairContribution[] = [
@@ -51,7 +51,7 @@ const themePairs: QualifiedThemePairContribution[] = [
id: "themes:pi-web",
pluginId: "themes",
localId: "pi-web",
name: "Pi Web",
name: "PI WEB",
light: "themes:pi-web-light",
dark: "themes:pi-web-dark",
},