@algochad/archcoder 2.0.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 +113 -0
- package/bin/cli-entry.js +55 -0
- package/bin/cli-output.js +145 -0
- package/bin/cli.js +5108 -0
- package/bin/cli.test.js +56 -0
- package/dist/apple-touch-icon-120x120.png +0 -0
- package/dist/apple-touch-icon-152x152.png +0 -0
- package/dist/apple-touch-icon-167x167.png +0 -0
- package/dist/apple-touch-icon-180x180.png +0 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/apple-touch-icon.svg +67 -0
- package/dist/assets/MultiRunWindow-BZp3MjJP.js +1 -0
- package/dist/assets/SettingsWindow-DoGYXpX7.js +1 -0
- package/dist/assets/TerminalView-BN7BR5Ff.js +3 -0
- package/dist/assets/TimelineDialog-ZQ33oVQR.js +1 -0
- package/dist/assets/ToolOutputDialog-Blv3pnug.js +16 -0
- package/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/assets/index-CtCEGYrr.css +1 -0
- package/dist/assets/index-o_d2wtWC.js +48 -0
- package/dist/assets/main-5QGBtzdq.css +1 -0
- package/dist/assets/main-B6oiMU86.js +8033 -0
- package/dist/assets/vendor--DbVqbJpV.css +1 -0
- package/dist/assets/vendor-.bun-HTKwyaEM.js +10086 -0
- package/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/assets/worker-bqd4RMrj.js +155 -0
- package/dist/favicon-16.png +0 -0
- package/dist/favicon-32.png +0 -0
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +67 -0
- package/dist/index.html +533 -0
- package/dist/logo-dark-192x192.png +0 -0
- package/dist/logo-dark-512x512.svg +16 -0
- package/dist/logo-light-192x192.png +0 -0
- package/dist/logo-light-512x512.svg +16 -0
- package/dist/pwa-192.png +0 -0
- package/dist/pwa-512.png +0 -0
- package/dist/pwa-maskable-192.png +0 -0
- package/dist/pwa-maskable-512.png +0 -0
- package/dist/site.webmanifest +22 -0
- package/dist/sw.js +1 -0
- package/package.json +107 -0
- package/public/apple-touch-icon-120x120.png +0 -0
- package/public/apple-touch-icon-152x152.png +0 -0
- package/public/apple-touch-icon-167x167.png +0 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/apple-touch-icon.svg +67 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon.png +0 -0
- package/public/favicon.svg +67 -0
- package/public/logo-dark-192x192.png +0 -0
- package/public/logo-dark-512x512.svg +16 -0
- package/public/logo-light-192x192.png +0 -0
- package/public/logo-light-512x512.svg +16 -0
- package/public/pwa-192.png +0 -0
- package/public/pwa-512.png +0 -0
- package/public/pwa-maskable-192.png +0 -0
- package/public/pwa-maskable-512.png +0 -0
- package/public/site.webmanifest +22 -0
- package/server/TERMINAL_INPUT_WS_PROTOCOL.md +44 -0
- package/server/index.d.ts +37 -0
- package/server/index.js +14694 -0
- package/server/lib/cloudflare-tunnel.js +650 -0
- package/server/lib/git/DOCUMENTATION.md +146 -0
- package/server/lib/git/credentials.js +74 -0
- package/server/lib/git/identity-storage.js +110 -0
- package/server/lib/git/index.js +6 -0
- package/server/lib/git/service.js +3117 -0
- package/server/lib/github/DOCUMENTATION.md +170 -0
- package/server/lib/github/auth.js +307 -0
- package/server/lib/github/device-flow.js +50 -0
- package/server/lib/github/index.js +24 -0
- package/server/lib/github/octokit.js +10 -0
- package/server/lib/github/pr-status.js +478 -0
- package/server/lib/github/repo/index.js +55 -0
- package/server/lib/installer/desktop.js +289 -0
- package/server/lib/installer/download.js +208 -0
- package/server/lib/installer/index.js +45 -0
- package/server/lib/installer/platform.js +100 -0
- package/server/lib/notifications/DOCUMENTATION.md +61 -0
- package/server/lib/notifications/index.js +1 -0
- package/server/lib/notifications/message.js +49 -0
- package/server/lib/notifications/message.test.js +59 -0
- package/server/lib/opencode/DOCUMENTATION.md +59 -0
- package/server/lib/opencode/agents.js +634 -0
- package/server/lib/opencode/auth.js +81 -0
- package/server/lib/opencode/commands.js +339 -0
- package/server/lib/opencode/index.js +66 -0
- package/server/lib/opencode/mcp.js +206 -0
- package/server/lib/opencode/providers.js +96 -0
- package/server/lib/opencode/shared.js +527 -0
- package/server/lib/opencode/skills.js +480 -0
- package/server/lib/opencode/tunnel-auth.js +591 -0
- package/server/lib/opencode/ui-auth.js +510 -0
- package/server/lib/package-manager.js +505 -0
- package/server/lib/quota/DOCUMENTATION.md +55 -0
- package/server/lib/quota/index.js +24 -0
- package/server/lib/quota/providers/claude.js +107 -0
- package/server/lib/quota/providers/codex.js +113 -0
- package/server/lib/quota/providers/copilot.js +165 -0
- package/server/lib/quota/providers/google/api.js +92 -0
- package/server/lib/quota/providers/google/auth.js +108 -0
- package/server/lib/quota/providers/google/index.js +124 -0
- package/server/lib/quota/providers/google/transforms.js +109 -0
- package/server/lib/quota/providers/index.js +152 -0
- package/server/lib/quota/providers/interface.js +55 -0
- package/server/lib/quota/providers/kimi.js +108 -0
- package/server/lib/quota/providers/minimax-cn-coding-plan.js +15 -0
- package/server/lib/quota/providers/minimax-coding-plan.js +15 -0
- package/server/lib/quota/providers/minimax-shared.js +136 -0
- package/server/lib/quota/providers/nanogpt.js +124 -0
- package/server/lib/quota/providers/ollama-cloud.js +112 -0
- package/server/lib/quota/providers/openai.js +91 -0
- package/server/lib/quota/providers/openrouter.js +92 -0
- package/server/lib/quota/providers/zai.js +91 -0
- package/server/lib/quota/utils/auth.js +46 -0
- package/server/lib/quota/utils/formatters.js +76 -0
- package/server/lib/quota/utils/index.js +10 -0
- package/server/lib/quota/utils/transformers.js +55 -0
- package/server/lib/skills-catalog/DOCUMENTATION.md +178 -0
- package/server/lib/skills-catalog/cache.js +32 -0
- package/server/lib/skills-catalog/clawdhub/api.js +158 -0
- package/server/lib/skills-catalog/clawdhub/index.js +30 -0
- package/server/lib/skills-catalog/clawdhub/install.js +238 -0
- package/server/lib/skills-catalog/clawdhub/scan.js +113 -0
- package/server/lib/skills-catalog/curated-sources.js +21 -0
- package/server/lib/skills-catalog/git.js +77 -0
- package/server/lib/skills-catalog/index.js +42 -0
- package/server/lib/skills-catalog/install.js +294 -0
- package/server/lib/skills-catalog/scan.js +221 -0
- package/server/lib/skills-catalog/source.js +85 -0
- package/server/lib/terminal/DOCUMENTATION.md +114 -0
- package/server/lib/terminal/index.js +12 -0
- package/server/lib/terminal/input-ws-protocol.js +66 -0
- package/server/lib/terminal/input-ws-protocol.test.js +138 -0
- package/server/lib/tts/DOCUMENTATION.md +134 -0
- package/server/lib/tts/index.js +16 -0
- package/server/lib/tts/service.js +162 -0
- package/server/lib/tts/summarization.js +171 -0
- package/server/lib/tunnels/index.js +166 -0
- package/server/lib/tunnels/providers/cloudflare.js +260 -0
- package/server/lib/tunnels/registry.js +51 -0
- package/server/lib/tunnels/types.js +219 -0
- package/server/lib/utils/lru.js +107 -0
- package/server/lib/utils/sse.js +121 -0
|
Binary file
|
|
Binary file
|
package/dist/favicon.png
ADDED
|
Binary file
|
package/dist/favicon.svg
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" stop-color="#1a1a1a"/>
|
|
5
|
+
<stop offset="100%" stop-color="#0d0d0d"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
8
|
+
<stop offset="0%" stop-color="#ff8533" class="shimmer-stop"/>
|
|
9
|
+
<stop offset="50%" stop-color="#ff6b00" class="shimmer-stop"/>
|
|
10
|
+
<stop offset="100%" stop-color="#cc5500" class="shimmer-stop"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
|
|
13
|
+
<feGaussianBlur stdDeviation="8" result="coloredBlur" class="pulse-blur"/>
|
|
14
|
+
<feMerge>
|
|
15
|
+
<feMergeNode in="coloredBlur"/>
|
|
16
|
+
<feMergeNode in="SourceGraphic"/>
|
|
17
|
+
</feMerge>
|
|
18
|
+
</filter>
|
|
19
|
+
<filter id="innerShadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
20
|
+
<feOffset dx="0" dy="4"/>
|
|
21
|
+
<feGaussianBlur stdDeviation="4" result="offset-blur"/>
|
|
22
|
+
<feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/>
|
|
23
|
+
<feFlood flood-color="#ffffff" flood-opacity="0.1" result="color"/>
|
|
24
|
+
<feComposite operator="in" in="color" in2="inverse" result="shadow"/>
|
|
25
|
+
<feComposite operator="over" in="shadow" in2="SourceGraphic"/>
|
|
26
|
+
</filter>
|
|
27
|
+
|
|
28
|
+
<!-- Animation for pulsing glow effect -->
|
|
29
|
+
<style>
|
|
30
|
+
@keyframes pulse {
|
|
31
|
+
0%, 100% { stdDeviation: 8; }
|
|
32
|
+
50% { stdDeviation: 12; }
|
|
33
|
+
}
|
|
34
|
+
@keyframes shimmer {
|
|
35
|
+
0%, 100% { stop-color: #ff8533; }
|
|
36
|
+
50% { stop-color: #ffaa66; }
|
|
37
|
+
}
|
|
38
|
+
.pulse-blur {
|
|
39
|
+
animation: pulse 2s ease-in-out infinite;
|
|
40
|
+
}
|
|
41
|
+
.shimmer-stop {
|
|
42
|
+
animation: shimmer 3s ease-in-out infinite;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
</defs>
|
|
46
|
+
|
|
47
|
+
<rect width="512" height="512" rx="112" fill="url(#bgGrad)"/>
|
|
48
|
+
<rect x="12" y="12" width="488" height="488" rx="100" fill="none" stroke="#2a2a2a" stroke-width="4"/>
|
|
49
|
+
|
|
50
|
+
<g filter="url(#glow)">
|
|
51
|
+
<path d="M256 64L400 400H336L296 304H216L176 400H112L256 64Z" fill="#ffffff" filter="url(#innerShadow)"/>
|
|
52
|
+
</g>
|
|
53
|
+
|
|
54
|
+
<path d="M256 128L288 208H224L256 128Z" fill="url(#orangeGrad)"/>
|
|
55
|
+
<rect x="232" y="248" width="48" height="16" rx="8" fill="url(#orangeGrad)"/>
|
|
56
|
+
|
|
57
|
+
<circle cx="256" cy="64" r="16" fill="url(#orangeGrad)"/>
|
|
58
|
+
|
|
59
|
+
<circle cx="136" cy="432" r="10" fill="#ff6b00"/>
|
|
60
|
+
<circle cx="184" cy="432" r="8" fill="#ffffff" opacity="0.4"/>
|
|
61
|
+
<circle cx="256" cy="432" r="8" fill="#ffffff" opacity="0.6"/>
|
|
62
|
+
<circle cx="328" cy="432" r="8" fill="#ffffff" opacity="0.4"/>
|
|
63
|
+
<circle cx="376" cy="432" r="10" fill="#ff6b00"/>
|
|
64
|
+
|
|
65
|
+
<path d="M120 400L112 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round"/>
|
|
66
|
+
<path d="M400 400L408 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round"/>
|
|
67
|
+
</svg>
|
package/dist/index.html
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
|
6
|
+
|
|
7
|
+
<!-- Favicon -->
|
|
8
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
9
|
+
<link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32" />
|
|
10
|
+
<link rel="icon" type="image/png" href="/favicon-16.png" sizes="16x16" />
|
|
11
|
+
<link rel="mask-icon" href="/favicon.svg" color="#edb449" />
|
|
12
|
+
|
|
13
|
+
<!-- Apple touch icon - PNG format required for iOS PWA support -->
|
|
14
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png" />
|
|
15
|
+
<link rel="apple-touch-icon" sizes="167x167" href="/apple-touch-icon-167x167.png" />
|
|
16
|
+
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png" />
|
|
17
|
+
|
|
18
|
+
<!-- Preload Nerd Fonts for terminal icon display -->
|
|
19
|
+
<link rel="preload" href="https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff2"
|
|
20
|
+
as="font" type="font/woff2" crossorigin="anonymous">
|
|
21
|
+
<link rel="preload" href="https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff2"
|
|
22
|
+
as="font" type="font/woff2" crossorigin="anonymous">
|
|
23
|
+
|
|
24
|
+
<!-- Web app manifest (endpoint-first with data URL fallback) -->
|
|
25
|
+
<script>
|
|
26
|
+
const baseUrl = location.origin;
|
|
27
|
+
const defaultAppName = 'ArchCoder - AI Coding Assistant';
|
|
28
|
+
const defaultShortName = 'ArchCoder';
|
|
29
|
+
const pwaNameStorageKey = 'archcoder.pwaName';
|
|
30
|
+
const pwaRecentSessionsStorageKey = 'archcoder.pwaRecentSessions';
|
|
31
|
+
|
|
32
|
+
const normalizePwaName = (value, fallback) => {
|
|
33
|
+
if (typeof value !== 'string') {
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
const normalized = value.trim().replace(/\s+/g, ' ');
|
|
37
|
+
if (!normalized) {
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
return normalized.slice(0, 64);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const truncate = (value, maxLength) => {
|
|
44
|
+
if (typeof value !== 'string') {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getStoredInstallName = () => {
|
|
51
|
+
try {
|
|
52
|
+
const storedName = localStorage.getItem(pwaNameStorageKey);
|
|
53
|
+
return normalizePwaName(storedName, defaultAppName);
|
|
54
|
+
} catch {
|
|
55
|
+
return defaultAppName;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const setStoredInstallName = (value) => {
|
|
60
|
+
const normalizedName = normalizePwaName(value, '');
|
|
61
|
+
try {
|
|
62
|
+
if (normalizedName) {
|
|
63
|
+
localStorage.setItem(pwaNameStorageKey, normalizedName);
|
|
64
|
+
} else {
|
|
65
|
+
localStorage.removeItem(pwaNameStorageKey);
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
return defaultAppName;
|
|
69
|
+
}
|
|
70
|
+
return normalizedName || defaultAppName;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const getQueryInstallNameOverride = () => {
|
|
74
|
+
try {
|
|
75
|
+
const params = new URLSearchParams(location.search);
|
|
76
|
+
const queryName = params.get('pwa_name') ?? params.get('app_name') ?? params.get('appName');
|
|
77
|
+
if (queryName === null) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const normalizedQueryName = normalizePwaName(queryName, '');
|
|
82
|
+
if (normalizedQueryName) {
|
|
83
|
+
localStorage.setItem(pwaNameStorageKey, normalizedQueryName);
|
|
84
|
+
return normalizedQueryName;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
localStorage.removeItem(pwaNameStorageKey);
|
|
88
|
+
return defaultAppName;
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const parseRecentSessionShortcuts = () => {
|
|
95
|
+
try {
|
|
96
|
+
const raw = localStorage.getItem(pwaRecentSessionsStorageKey);
|
|
97
|
+
if (!raw) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const parsed = JSON.parse(raw);
|
|
102
|
+
if (!Array.isArray(parsed)) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
const recentSessions = [];
|
|
108
|
+
|
|
109
|
+
for (const item of parsed) {
|
|
110
|
+
if (!item || typeof item !== 'object') {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const sessionId = typeof item.sessionId === 'string' ? item.sessionId.trim().slice(0, 160) : '';
|
|
115
|
+
if (!sessionId || seen.has(sessionId)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const fallbackTitle = `Session ${recentSessions.length + 1}`;
|
|
120
|
+
const title = truncate(normalizePwaName(item.title, fallbackTitle), 48);
|
|
121
|
+
|
|
122
|
+
seen.add(sessionId);
|
|
123
|
+
recentSessions.push({ sessionId, title });
|
|
124
|
+
|
|
125
|
+
if (recentSessions.length >= 3) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return recentSessions;
|
|
131
|
+
} catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const buildShortcuts = (recentSessions) => {
|
|
137
|
+
const shortcuts = [
|
|
138
|
+
{
|
|
139
|
+
name: 'Appearance Settings',
|
|
140
|
+
short_name: 'Settings',
|
|
141
|
+
description: 'Open appearance settings',
|
|
142
|
+
url: `${baseUrl}/?settings=appearance`,
|
|
143
|
+
icons: [{ src: `${baseUrl}/pwa-192.png`, sizes: '192x192', type: 'image/png' }],
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
for (const session of recentSessions) {
|
|
148
|
+
const sessionTitle = truncate(session.title, 32);
|
|
149
|
+
shortcuts.push({
|
|
150
|
+
name: sessionTitle,
|
|
151
|
+
short_name: sessionTitle,
|
|
152
|
+
description: 'Open recent session',
|
|
153
|
+
url: `${baseUrl}/?session=${encodeURIComponent(session.sessionId)}`,
|
|
154
|
+
icons: [{ src: `${baseUrl}/pwa-192.png`, sizes: '192x192', type: 'image/png' }],
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return shortcuts;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const buildManifest = (appName, recentSessions) => {
|
|
162
|
+
const shortName = appName === defaultAppName ? defaultShortName : truncate(appName, 30);
|
|
163
|
+
return {
|
|
164
|
+
name: appName,
|
|
165
|
+
short_name: shortName,
|
|
166
|
+
description: 'Web interface companion for OpenCode AI coding agent',
|
|
167
|
+
id: `${baseUrl}/`,
|
|
168
|
+
start_url: `${baseUrl}/`,
|
|
169
|
+
scope: `${baseUrl}/`,
|
|
170
|
+
display: 'standalone',
|
|
171
|
+
orientation: 'portrait-primary',
|
|
172
|
+
background_color: '#151313',
|
|
173
|
+
theme_color: '#edb449',
|
|
174
|
+
icons: [
|
|
175
|
+
{ src: `${baseUrl}/pwa-192.png`, sizes: '192x192', type: 'image/png', purpose: 'any' },
|
|
176
|
+
{ src: `${baseUrl}/pwa-512.png`, sizes: '512x512', type: 'image/png', purpose: 'any' },
|
|
177
|
+
{ src: `${baseUrl}/pwa-maskable-192.png`, sizes: '192x192', type: 'image/png', purpose: 'any maskable' },
|
|
178
|
+
{ src: `${baseUrl}/pwa-maskable-512.png`, sizes: '512x512', type: 'image/png', purpose: 'any maskable' },
|
|
179
|
+
{ src: `${baseUrl}/apple-touch-icon-180x180.png`, sizes: '180x180', type: 'image/png', purpose: 'any' },
|
|
180
|
+
{ src: `${baseUrl}/apple-touch-icon-152x152.png`, sizes: '152x152', type: 'image/png', purpose: 'any' },
|
|
181
|
+
{ src: `${baseUrl}/favicon-32.png`, sizes: '32x32', type: 'image/png' },
|
|
182
|
+
{ src: `${baseUrl}/favicon-16.png`, sizes: '16x16', type: 'image/png' },
|
|
183
|
+
],
|
|
184
|
+
shortcuts: buildShortcuts(recentSessions),
|
|
185
|
+
categories: ['developer', 'tools', 'productivity'],
|
|
186
|
+
lang: 'en',
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const buildManifestEndpointUrl = (installNameOverride = null) => {
|
|
191
|
+
const params = new URLSearchParams();
|
|
192
|
+
if (typeof installNameOverride === 'string') {
|
|
193
|
+
params.set('appName', installNameOverride);
|
|
194
|
+
}
|
|
195
|
+
const search = params.toString();
|
|
196
|
+
return `${baseUrl}/manifest.webmanifest${search ? `?${search}` : ''}`;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const manifestLink = document.createElement('link');
|
|
200
|
+
manifestLink.rel = 'manifest';
|
|
201
|
+
document.head.appendChild(manifestLink);
|
|
202
|
+
|
|
203
|
+
let activeManifestBlobUrl = null;
|
|
204
|
+
let manifestRequestVersion = 0;
|
|
205
|
+
|
|
206
|
+
const setManifestFromBlob = (manifest) => {
|
|
207
|
+
if (activeManifestBlobUrl) {
|
|
208
|
+
URL.revokeObjectURL(activeManifestBlobUrl);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/manifest+json' });
|
|
212
|
+
activeManifestBlobUrl = URL.createObjectURL(manifestBlob);
|
|
213
|
+
manifestLink.href = activeManifestBlobUrl;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const setManifestFromEndpoint = (manifestUrl) => {
|
|
217
|
+
if (activeManifestBlobUrl) {
|
|
218
|
+
URL.revokeObjectURL(activeManifestBlobUrl);
|
|
219
|
+
activeManifestBlobUrl = null;
|
|
220
|
+
}
|
|
221
|
+
manifestLink.href = manifestUrl;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const canUseManifestEndpoint = async (manifestUrl, requestVersion) => {
|
|
225
|
+
if (typeof fetch !== 'function') {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const controller = typeof AbortController === 'function' ? new AbortController() : null;
|
|
230
|
+
const timeoutId = setTimeout(() => {
|
|
231
|
+
controller?.abort();
|
|
232
|
+
}, 1500);
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const response = await fetch(manifestUrl, {
|
|
236
|
+
credentials: 'include',
|
|
237
|
+
cache: 'no-store',
|
|
238
|
+
headers: {
|
|
239
|
+
Accept: 'application/manifest+json, application/json;q=0.9, */*;q=0.1',
|
|
240
|
+
},
|
|
241
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (requestVersion !== manifestRequestVersion || !response.ok) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const contentType = response.headers.get('content-type') || '';
|
|
249
|
+
return /manifest|json/i.test(contentType);
|
|
250
|
+
} catch {
|
|
251
|
+
return false;
|
|
252
|
+
} finally {
|
|
253
|
+
clearTimeout(timeoutId);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const updateManifest = async (installNameOverride = null) => {
|
|
258
|
+
const resolvedFallbackName = typeof installNameOverride === 'string' ? installNameOverride : getStoredInstallName();
|
|
259
|
+
const recentSessions = parseRecentSessionShortcuts();
|
|
260
|
+
const manifest = buildManifest(resolvedFallbackName, recentSessions);
|
|
261
|
+
const manifestUrl = buildManifestEndpointUrl(installNameOverride);
|
|
262
|
+
const requestVersion = ++manifestRequestVersion;
|
|
263
|
+
|
|
264
|
+
const useEndpoint = await canUseManifestEndpoint(manifestUrl, requestVersion);
|
|
265
|
+
if (requestVersion !== manifestRequestVersion) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (useEndpoint) {
|
|
270
|
+
setManifestFromEndpoint(manifestUrl);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
setManifestFromBlob(manifest);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const refreshManifestFromStorage = () => {
|
|
278
|
+
void updateManifest();
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const initialInstallNameOverride = getQueryInstallNameOverride();
|
|
282
|
+
void updateManifest(initialInstallNameOverride);
|
|
283
|
+
|
|
284
|
+
window.__OPENCHAMBER_GET_PWA_INSTALL_NAME__ = () => getStoredInstallName();
|
|
285
|
+
window.__OPENCHAMBER_SET_PWA_INSTALL_NAME__ = (value) => {
|
|
286
|
+
const resolvedName = setStoredInstallName(value);
|
|
287
|
+
void updateManifest(resolvedName);
|
|
288
|
+
return resolvedName;
|
|
289
|
+
};
|
|
290
|
+
window.__OPENCHAMBER_UPDATE_PWA_MANIFEST__ = () => {
|
|
291
|
+
refreshManifestFromStorage();
|
|
292
|
+
};
|
|
293
|
+
</script>
|
|
294
|
+
|
|
295
|
+
<script>
|
|
296
|
+
// Blocking script to detect and apply theme before first paint
|
|
297
|
+
(function() {
|
|
298
|
+
try {
|
|
299
|
+
var themeMode = localStorage.getItem('themeMode');
|
|
300
|
+
var variant = localStorage.getItem('selectedThemeVariant');
|
|
301
|
+
var useSystem = localStorage.getItem('useSystemTheme');
|
|
302
|
+
var isDark;
|
|
303
|
+
|
|
304
|
+
// Check themeMode first (new storage key)
|
|
305
|
+
if (themeMode === 'dark') {
|
|
306
|
+
isDark = true;
|
|
307
|
+
} else if (themeMode === 'light') {
|
|
308
|
+
isDark = false;
|
|
309
|
+
} else if (themeMode === 'system' || useSystem === null || useSystem === 'true') {
|
|
310
|
+
// System preference
|
|
311
|
+
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
312
|
+
} else if (variant === 'light' || variant === 'dark') {
|
|
313
|
+
// Legacy storage key fallback
|
|
314
|
+
isDark = variant === 'dark';
|
|
315
|
+
} else {
|
|
316
|
+
// Default to system
|
|
317
|
+
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Apply theme class and data attribute
|
|
321
|
+
document.documentElement.classList.add(isDark ? 'dark' : 'light');
|
|
322
|
+
document.documentElement.setAttribute('data-splash-variant', isDark ? 'dark' : 'light');
|
|
323
|
+
document.documentElement.style.setProperty('color-scheme', isDark ? 'dark' : 'light');
|
|
324
|
+
|
|
325
|
+
// Splash colors persisted by the app theme system
|
|
326
|
+
var splashBgLight = localStorage.getItem('splashBgLight');
|
|
327
|
+
var splashFgLight = localStorage.getItem('splashFgLight');
|
|
328
|
+
var splashBgDark = localStorage.getItem('splashBgDark');
|
|
329
|
+
var splashFgDark = localStorage.getItem('splashFgDark');
|
|
330
|
+
|
|
331
|
+
if (splashBgLight) document.documentElement.style.setProperty('--splash-background-light', splashBgLight);
|
|
332
|
+
if (splashFgLight) document.documentElement.style.setProperty('--splash-stroke-light', splashFgLight);
|
|
333
|
+
if (splashBgDark) document.documentElement.style.setProperty('--splash-background-dark', splashBgDark);
|
|
334
|
+
if (splashFgDark) document.documentElement.style.setProperty('--splash-stroke-dark', splashFgDark);
|
|
335
|
+
} catch (error) {
|
|
336
|
+
console.warn('Failed to apply theme:', error);
|
|
337
|
+
}
|
|
338
|
+
})();
|
|
339
|
+
</script>
|
|
340
|
+
|
|
341
|
+
<!-- Theme color - Safari iOS 26+ prioritizes CSS background-color over this, but keep as fallback -->
|
|
342
|
+
<meta name="theme-color" content="#151313" />
|
|
343
|
+
<meta name="theme-color" content="#151313" media="(prefers-color-scheme: dark)" />
|
|
344
|
+
|
|
345
|
+
<!-- iOS Safari PWA styling -->
|
|
346
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
347
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
348
|
+
<meta name="apple-mobile-web-app-title" content="OpenChamber" />
|
|
349
|
+
|
|
350
|
+
<title>ArchCoder - AI Coding Assistant</title>
|
|
351
|
+
<meta name="description" content="AI-powered code editor and coding assistant" />
|
|
352
|
+
<meta name="application-name" content="ArchCoder" />
|
|
353
|
+
<meta name="apple-mobile-web-app-title" content="ArchCoder" />
|
|
354
|
+
|
|
355
|
+
<!-- Inline CSS for loading screen and Nerd Fonts (before Tailwind loads) -->
|
|
356
|
+
<style>
|
|
357
|
+
/* Nerd Font @font-face declarations for terminal icon support */
|
|
358
|
+
@font-face {
|
|
359
|
+
font-family: 'JetBrainsMono Nerd Font';
|
|
360
|
+
src:
|
|
361
|
+
local('JetBrainsMono Nerd Font'),
|
|
362
|
+
url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff2') format('woff2'),
|
|
363
|
+
url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff') format('woff');
|
|
364
|
+
font-weight: normal;
|
|
365
|
+
font-style: normal;
|
|
366
|
+
font-display: swap;
|
|
367
|
+
unicode-range: U+E000-F8FF, U+F0000-FFFFF;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@font-face {
|
|
371
|
+
font-family: 'FiraCode Nerd Font';
|
|
372
|
+
src:
|
|
373
|
+
local('FiraCode Nerd Font'),
|
|
374
|
+
url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff2') format('woff2'),
|
|
375
|
+
url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff') format('woff');
|
|
376
|
+
font-weight: normal;
|
|
377
|
+
font-style: normal;
|
|
378
|
+
font-display: swap;
|
|
379
|
+
unicode-range: U+E000-F8FF, U+F0000-FFFFF;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
:root {
|
|
383
|
+
--splash-background-dark: #151313;
|
|
384
|
+
--splash-stroke-dark: #CECDC3;
|
|
385
|
+
--splash-background-light: #FFFCF0;
|
|
386
|
+
--splash-stroke-light: #100F0F;
|
|
387
|
+
|
|
388
|
+
--splash-background: var(--splash-background-dark);
|
|
389
|
+
--splash-stroke: var(--splash-stroke-dark);
|
|
390
|
+
|
|
391
|
+
/* Fallback fills (overridden below when supported) */
|
|
392
|
+
--splash-face-fill: rgba(255, 255, 255, 0.15);
|
|
393
|
+
--splash-cell-fill: rgba(255, 255, 255, 0.35);
|
|
394
|
+
--splash-logo-fill: var(--splash-stroke);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
html[data-splash-variant='light'] {
|
|
398
|
+
--splash-background: var(--splash-background-light);
|
|
399
|
+
--splash-stroke: var(--splash-stroke-light);
|
|
400
|
+
--splash-face-fill: rgba(0, 0, 0, 0.15);
|
|
401
|
+
--splash-cell-fill: rgba(0, 0, 0, 0.4);
|
|
402
|
+
--splash-logo-fill: var(--splash-stroke);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
html[data-splash-variant='dark'] {
|
|
406
|
+
--splash-background: var(--splash-background-dark);
|
|
407
|
+
--splash-stroke: var(--splash-stroke-dark);
|
|
408
|
+
--splash-logo-fill: var(--splash-stroke);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
@supports (color: color-mix(in srgb, white 50%, transparent)) {
|
|
412
|
+
:root {
|
|
413
|
+
--splash-face-fill: color-mix(in srgb, var(--splash-stroke) 15%, transparent);
|
|
414
|
+
--splash-cell-fill: color-mix(in srgb, var(--splash-stroke) 35%, transparent);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
@keyframes logo-pulse {
|
|
418
|
+
0%, 100% { opacity: 1; }
|
|
419
|
+
50% { opacity: 0.4; }
|
|
420
|
+
}
|
|
421
|
+
.logo-pulse {
|
|
422
|
+
animation: logo-pulse 3s ease-in-out infinite;
|
|
423
|
+
}
|
|
424
|
+
#initial-loading {
|
|
425
|
+
background-color: var(--splash-background);
|
|
426
|
+
color: var(--splash-foreground);
|
|
427
|
+
display: flex;
|
|
428
|
+
align-items: center;
|
|
429
|
+
justify-content: center;
|
|
430
|
+
height: 100vh;
|
|
431
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
432
|
+
transition: opacity 0.3s ease-out;
|
|
433
|
+
position: absolute;
|
|
434
|
+
width: 100%;
|
|
435
|
+
z-index: 9999;
|
|
436
|
+
}
|
|
437
|
+
#initial-loading.fade-out {
|
|
438
|
+
opacity: 0;
|
|
439
|
+
pointer-events: none;
|
|
440
|
+
}
|
|
441
|
+
</style>
|
|
442
|
+
<script type="module" crossorigin src="/assets/index-o_d2wtWC.js"></script>
|
|
443
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-.bun-HTKwyaEM.js">
|
|
444
|
+
<link rel="stylesheet" crossorigin href="/assets/vendor--DbVqbJpV.css">
|
|
445
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CtCEGYrr.css">
|
|
446
|
+
</head>
|
|
447
|
+
<body class="h-full bg-background text-foreground">
|
|
448
|
+
<div id="root" class="h-full">
|
|
449
|
+
<!-- Loading fallback while React initializes -->
|
|
450
|
+
<div id="initial-loading">
|
|
451
|
+
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px;">
|
|
452
|
+
<svg width="140" height="140" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="ArchCoder loading icon" class="logo-pulse">
|
|
453
|
+
<defs>
|
|
454
|
+
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
455
|
+
<stop offset="0%" stop-color="#ff6b00" stop-opacity="0.9" />
|
|
456
|
+
<stop offset="50%" stop-color="#ff6b00" />
|
|
457
|
+
<stop offset="100%" stop-color="#ff6b00" stop-opacity="0.8" />
|
|
458
|
+
</linearGradient>
|
|
459
|
+
</defs>
|
|
460
|
+
<g opacity="0.9">
|
|
461
|
+
<path d="M256 64L400 400H336L296 304H216L176 400H112L256 64Z" fill="var(--splash-stroke)"/>
|
|
462
|
+
</g>
|
|
463
|
+
<g opacity="0.8">
|
|
464
|
+
<path d="M256 128L288 208H224L256 128Z" fill="url(#accentGrad)"/>
|
|
465
|
+
<rect x="232" y="248" width="48" height="16" rx="8" fill="url(#accentGrad)" />
|
|
466
|
+
<circle cx="256" cy="64" r="16" fill="url(#accentGrad)" />
|
|
467
|
+
</g>
|
|
468
|
+
<g opacity="0.5">
|
|
469
|
+
<circle cx="136" cy="432" r="10" fill="#ff6b00" />
|
|
470
|
+
<circle cx="184" cy="432" r="8" fill="var(--splash-stroke)" opacity="0.4" />
|
|
471
|
+
<circle cx="256" cy="432" r="8" fill="var(--splash-stroke)" opacity="0.6" />
|
|
472
|
+
<circle cx="328" cy="432" r="8" fill="var(--splash-stroke)" opacity="0.4" />
|
|
473
|
+
<circle cx="376" cy="432" r="10" fill="#ff6b00" />
|
|
474
|
+
</g>
|
|
475
|
+
<path d="M120 400L112 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round" />
|
|
476
|
+
<path d="M400 400L408 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round" />
|
|
477
|
+
</svg>
|
|
478
|
+
<div style="font-size: 24px; font-weight: 600; color: var(--splash-stroke); letter-spacing: 0.5px;">ArchCoder</div>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<script>
|
|
484
|
+
// Fallback: hide loading screen after 10 seconds if React fails to load
|
|
485
|
+
setTimeout(function() {
|
|
486
|
+
const loading = document.getElementById('initial-loading');
|
|
487
|
+
if (loading) {
|
|
488
|
+
console.warn('Loading screen timeout - forcing hide after 10s');
|
|
489
|
+
loading.classList.add('fade-out');
|
|
490
|
+
setTimeout(function() {
|
|
491
|
+
loading.remove();
|
|
492
|
+
}, 300);
|
|
493
|
+
}
|
|
494
|
+
}, 10000);
|
|
495
|
+
</script>
|
|
496
|
+
|
|
497
|
+
<!-- CSS Font Loading API for reliable Nerd Font loading -->
|
|
498
|
+
<script>
|
|
499
|
+
(function() {
|
|
500
|
+
const fonts = [
|
|
501
|
+
{
|
|
502
|
+
name: 'JetBrainsMono Nerd Font',
|
|
503
|
+
url: 'https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff2'
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: 'FiraCode Nerd Font',
|
|
507
|
+
url: 'https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff2'
|
|
508
|
+
}
|
|
509
|
+
];
|
|
510
|
+
|
|
511
|
+
const fontPromises = fonts.map(font => {
|
|
512
|
+
const fontFace = new FontFace(font.name, `url(${font.url}) format('woff2')`);
|
|
513
|
+
document.fonts.add(fontFace);
|
|
514
|
+
return fontFace.load().catch(err => {
|
|
515
|
+
console.warn(`Failed to load font: ${font.name}`, err);
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
Promise.allSettled(fontPromises).then(() => {
|
|
520
|
+
document.documentElement.classList.add('fonts-loaded');
|
|
521
|
+
});
|
|
522
|
+
})();
|
|
523
|
+
</script>
|
|
524
|
+
|
|
525
|
+
<!-- Polyfill for process before loading React -->
|
|
526
|
+
<script>
|
|
527
|
+
if (typeof process === 'undefined') {
|
|
528
|
+
window.process = { env: {} };
|
|
529
|
+
}
|
|
530
|
+
</script>
|
|
531
|
+
|
|
532
|
+
</body>
|
|
533
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- OpenChamber logo - simplified, no background (for dark backgrounds) -->
|
|
3
|
+
<g transform="translate(256, 256) scale(4)">
|
|
4
|
+
<!-- Left face - simplified, no grid cells -->
|
|
5
|
+
<path d="M0 0 L-41.568 -24 L-41.568 24 L0 48 Z" fill="white" fill-opacity="0.2" stroke="white" stroke-width="3" stroke-linejoin="round"/>
|
|
6
|
+
<!-- Right face - simplified, no grid cells -->
|
|
7
|
+
<path d="M0 0 L41.568 -24 L41.568 24 L0 48 Z" fill="white" fill-opacity="0.35" stroke="white" stroke-width="3" stroke-linejoin="round"/>
|
|
8
|
+
<!-- Top face - open -->
|
|
9
|
+
<path d="M0 -48 L-41.568 -24 L0 0 L41.568 -24 Z" fill="none" stroke="white" stroke-width="3" stroke-linejoin="round"/>
|
|
10
|
+
<!-- OpenCode logo on top face -->
|
|
11
|
+
<g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, -24) scale(0.75)">
|
|
12
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M-16 -20 L16 -20 L16 20 L-16 20 Z M-8 -12 L-8 12 L8 12 L8 -12 Z" fill="white"/>
|
|
13
|
+
<path d="M-8 -4 L8 -4 L8 12 L-8 12 Z" fill="white" fill-opacity="0.4"/>
|
|
14
|
+
</g>
|
|
15
|
+
</g>
|
|
16
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- OpenChamber logo - simplified, no background (for light backgrounds) -->
|
|
3
|
+
<g transform="translate(256, 256) scale(4)">
|
|
4
|
+
<!-- Left face - simplified, no grid cells -->
|
|
5
|
+
<path d="M0 0 L-41.568 -24 L-41.568 24 L0 48 Z" fill="black" fill-opacity="0.2" stroke="black" stroke-width="3" stroke-linejoin="round"/>
|
|
6
|
+
<!-- Right face - simplified, no grid cells -->
|
|
7
|
+
<path d="M0 0 L41.568 -24 L41.568 24 L0 48 Z" fill="black" fill-opacity="0.35" stroke="black" stroke-width="3" stroke-linejoin="round"/>
|
|
8
|
+
<!-- Top face - open -->
|
|
9
|
+
<path d="M0 -48 L-41.568 -24 L0 0 L41.568 -24 Z" fill="none" stroke="black" stroke-width="3" stroke-linejoin="round"/>
|
|
10
|
+
<!-- OpenCode logo on top face -->
|
|
11
|
+
<g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, -24) scale(0.75)">
|
|
12
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M-16 -20 L16 -20 L16 20 L-16 20 Z M-8 -12 L-8 12 L8 12 L8 -12 Z" fill="black"/>
|
|
13
|
+
<path d="M-8 -4 L8 -4 L8 12 L-8 12 Z" fill="black" fill-opacity="0.4"/>
|
|
14
|
+
</g>
|
|
15
|
+
</g>
|
|
16
|
+
</svg>
|
package/dist/pwa-192.png
ADDED
|
Binary file
|
package/dist/pwa-512.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|