icebear0828 Claude Opus 4.6 commited on
Commit
90172f5
·
1 Parent(s): bb065fa

ui: replace icon font + Google Fonts with inline SVGs and system fonts

Browse files

Eliminates all Google Fonts CDN dependencies (Material Symbols, Inter,
JetBrains Mono) to remove FOUC entirely. Icons are now inline SVGs that
render instantly; text uses system font stack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. public/dashboard.html +24 -26
public/dashboard.html CHANGED
@@ -4,8 +4,6 @@
4
  <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
5
  <title>Codex Proxy Developer Dashboard</title>
6
  <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
7
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=JetBrains+Mono:wght@400;500&amp;display=swap" rel="stylesheet"/>
8
- <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
9
  <script>
10
  tailwind.config = {
11
  darkMode: "class",
@@ -22,8 +20,8 @@
22
  "text-dim": "#8b949e",
23
  },
24
  fontFamily: {
25
- "display": ["Inter", "sans-serif"],
26
- "mono": ["JetBrains Mono", "monospace"],
27
  },
28
  borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "full": "9999px"},
29
  },
@@ -34,10 +32,6 @@
34
  :root { --primary: 16 162 53; --primary-hover: 14 140 46; }
35
  .dark { --primary: 16 163 127; --primary-hover: 14 140 108; }
36
 
37
- /* Hide icon ligature text until font loads */
38
- .material-symbols-outlined { opacity: 0; }
39
- .fonts-ready .material-symbols-outlined { opacity: 1; transition: opacity 0.15s; }
40
-
41
  /* Dark scrollbar for code blocks */
42
  pre::-webkit-scrollbar { height: 8px; }
43
  pre::-webkit-scrollbar-track { background: #0d1117; }
@@ -51,7 +45,6 @@
51
  if (t === 'dark' || (!t && window.matchMedia('(prefers-color-scheme: dark)').matches))
52
  document.documentElement.classList.add('dark');
53
  } catch {}
54
- document.fonts.ready.then(() => document.documentElement.classList.add('fonts-ready'));
55
  </script>
56
  </head>
57
  <body class="bg-bg-light dark:bg-bg-dark font-display text-slate-900 dark:text-text-main antialiased min-h-screen flex flex-col transition-colors">
@@ -62,7 +55,7 @@
62
  <!-- Logo & Title -->
63
  <div class="flex items-center gap-3">
64
  <div class="flex items-center justify-center size-8 rounded-full bg-primary/10 text-primary border border-primary/20">
65
- <span class="material-symbols-outlined text-xl font-bold">check_circle</span>
66
  </div>
67
  <h1 class="text-[0.9rem] font-bold tracking-tight">Codex Proxy</h1>
68
  </div>
@@ -77,10 +70,10 @@
77
  </div>
78
  <!-- Theme Toggle -->
79
  <button id="themeToggle" onclick="toggleTheme()" class="p-2 rounded-lg text-slate-500 dark:text-text-dim hover:bg-slate-100 dark:hover:bg-border-dark transition-colors" title="Toggle theme">
80
- <span class="material-symbols-outlined text-xl" id="themeIcon">dark_mode</span>
81
  </button>
82
  <button id="addAccountBtn" onclick="startAddAccount()" class="flex items-center gap-2 px-4 py-2 bg-primary hover:bg-primary-hover text-white text-xs font-semibold rounded-lg transition-colors shadow-sm active:scale-95">
83
- <span class="material-symbols-outlined text-sm">add</span>
84
  <span>Add Account</span>
85
  </button>
86
  </div>
@@ -119,7 +112,7 @@
119
  <section class="bg-white dark:bg-card-dark border border-gray-200 dark:border-border-dark rounded-xl p-5 shadow-sm transition-colors">
120
  <div class="flex items-center justify-between mb-6 border-b border-slate-100 dark:border-border-dark pb-4">
121
  <div class="flex items-center gap-2">
122
- <span class="material-symbols-outlined text-primary">settings</span>
123
  <h2 class="text-[0.95rem] font-bold">API Configuration</h2>
124
  </div>
125
  <button onclick="resetConfigDefaults()" class="text-xs text-slate-400 dark:text-text-dim hover:text-slate-600 dark:hover:text-text-main transition-colors">Reset to defaults</button>
@@ -131,7 +124,7 @@
131
  <div class="relative flex items-center">
132
  <input id="baseUrl" class="w-full pl-3 pr-10 py-2.5 bg-slate-50 dark:bg-bg-dark border border-gray-200 dark:border-border-dark rounded-lg text-[0.78rem] font-mono text-slate-600 dark:text-text-main focus:ring-1 focus:ring-primary focus:border-primary outline-none transition-all" type="text" value="Loading..."/>
133
  <button onclick="copyField('baseUrl', this)" class="absolute right-2 p-1.5 text-slate-400 dark:text-text-dim hover:text-primary transition-colors rounded-md hover:bg-slate-100 dark:hover:bg-border-dark" title="Copy URL">
134
- <span class="material-symbols-outlined text-lg">content_copy</span>
135
  </button>
136
  </div>
137
  </div>
@@ -143,7 +136,7 @@
143
  <option value="codex">codex</option>
144
  </select>
145
  <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-slate-500 dark:text-text-dim">
146
- <span class="material-symbols-outlined text-lg">expand_more</span>
147
  </div>
148
  </div>
149
  </div>
@@ -152,11 +145,11 @@
152
  <label class="text-xs font-semibold text-slate-700 dark:text-text-main">Your API Key</label>
153
  <div class="relative flex items-center">
154
  <div class="absolute left-3 text-slate-400 dark:text-text-dim">
155
- <span class="material-symbols-outlined text-lg">key</span>
156
  </div>
157
  <input id="apiKey" class="w-full pl-10 pr-10 py-2.5 bg-slate-50 dark:bg-bg-dark border border-gray-200 dark:border-border-dark rounded-lg text-[0.78rem] font-mono text-slate-600 dark:text-text-main focus:ring-1 focus:ring-primary focus:border-primary outline-none transition-all tracking-wider" type="text" value="Loading..."/>
158
  <button onclick="copyField('apiKey', this)" class="absolute right-2 p-1.5 text-slate-400 dark:text-text-dim hover:text-primary transition-colors rounded-md hover:bg-slate-100 dark:hover:bg-border-dark" title="Copy API Key">
159
- <span class="material-symbols-outlined text-lg">content_copy</span>
160
  </button>
161
  </div>
162
  <p class="text-xs text-slate-400 dark:text-text-dim mt-1">Use this key to authenticate requests to the proxy. Do not share it.</p>
@@ -187,7 +180,7 @@
187
  <div id="codeContainer" class="relative group rounded-lg overflow-hidden bg-[#0d1117] text-slate-300 font-mono text-xs border border-slate-800 dark:border-border-dark">
188
  <div class="absolute right-2 top-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
189
  <button onclick="copyCurrentCode()" class="flex items-center gap-1.5 px-3 py-1.5 bg-slate-700 hover:bg-slate-600 text-white rounded text-xs font-medium transition-colors">
190
- <span class="material-symbols-outlined text-sm">content_copy</span> Copy
191
  </button>
192
  </div>
193
  <div class="p-4 overflow-x-auto">
@@ -231,10 +224,15 @@ function toggleTheme() {
231
  updateThemeIcon();
232
  }
233
 
 
 
 
 
 
234
  function updateThemeIcon() {
235
- const icon = document.getElementById('themeIcon');
236
- if (!icon) return;
237
- icon.textContent = document.documentElement.classList.contains('dark') ? 'light_mode' : 'dark_mode';
238
  }
239
  updateThemeIcon();
240
 
@@ -342,7 +340,7 @@ function renderAccounts(accounts) {
342
  return `
343
  <div class="group bg-white dark:bg-card-dark border border-gray-200 dark:border-border-dark rounded-xl p-4 shadow-sm hover:shadow-md transition-all hover:border-primary/30 dark:hover:border-primary/50 relative">
344
  <button onclick="deleteAccount('${escapeHtml(acct.id)}')" class="absolute top-3 right-3 opacity-0 group-hover:opacity-100 p-1.5 text-slate-300 dark:text-text-dim hover:text-red-500 transition-all rounded-md hover:bg-red-50 dark:hover:bg-red-900/20" title="Delete account">
345
- <span class="material-symbols-outlined text-lg">delete</span>
346
  </button>
347
  <div class="flex justify-between items-start mb-4">
348
  <div class="flex items-center gap-3">
@@ -673,11 +671,11 @@ function copyField(id, btn) {
673
  const el = document.getElementById(id);
674
  const text = el.value !== undefined ? el.value : el.textContent;
675
  navigator.clipboard.writeText(text);
676
- const icon = btn.querySelector('.material-symbols-outlined');
677
- if (icon) {
678
- icon.textContent = 'check';
679
  btn.classList.add('text-primary');
680
- setTimeout(() => { icon.textContent = 'content_copy'; btn.classList.remove('text-primary'); }, 2000);
681
  }
682
  }
683
 
 
4
  <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
5
  <title>Codex Proxy Developer Dashboard</title>
6
  <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
 
 
7
  <script>
8
  tailwind.config = {
9
  darkMode: "class",
 
20
  "text-dim": "#8b949e",
21
  },
22
  fontFamily: {
23
+ "display": ["system-ui", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "sans-serif"],
24
+ "mono": ["ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas", "Liberation Mono", "monospace"],
25
  },
26
  borderRadius: {"DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "full": "9999px"},
27
  },
 
32
  :root { --primary: 16 162 53; --primary-hover: 14 140 46; }
33
  .dark { --primary: 16 163 127; --primary-hover: 14 140 108; }
34
 
 
 
 
 
35
  /* Dark scrollbar for code blocks */
36
  pre::-webkit-scrollbar { height: 8px; }
37
  pre::-webkit-scrollbar-track { background: #0d1117; }
 
45
  if (t === 'dark' || (!t && window.matchMedia('(prefers-color-scheme: dark)').matches))
46
  document.documentElement.classList.add('dark');
47
  } catch {}
 
48
  </script>
49
  </head>
50
  <body class="bg-bg-light dark:bg-bg-dark font-display text-slate-900 dark:text-text-main antialiased min-h-screen flex flex-col transition-colors">
 
55
  <!-- Logo & Title -->
56
  <div class="flex items-center gap-3">
57
  <div class="flex items-center justify-center size-8 rounded-full bg-primary/10 text-primary border border-primary/20">
58
+ <svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
59
  </div>
60
  <h1 class="text-[0.9rem] font-bold tracking-tight">Codex Proxy</h1>
61
  </div>
 
70
  </div>
71
  <!-- Theme Toggle -->
72
  <button id="themeToggle" onclick="toggleTheme()" class="p-2 rounded-lg text-slate-500 dark:text-text-dim hover:bg-slate-100 dark:hover:bg-border-dark transition-colors" title="Toggle theme">
73
+ <span id="themeIcon"><svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z"/></svg></span>
74
  </button>
75
  <button id="addAccountBtn" onclick="startAddAccount()" class="flex items-center gap-2 px-4 py-2 bg-primary hover:bg-primary-hover text-white text-xs font-semibold rounded-lg transition-colors shadow-sm active:scale-95">
76
+ <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
77
  <span>Add Account</span>
78
  </button>
79
  </div>
 
112
  <section class="bg-white dark:bg-card-dark border border-gray-200 dark:border-border-dark rounded-xl p-5 shadow-sm transition-colors">
113
  <div class="flex items-center justify-between mb-6 border-b border-slate-100 dark:border-border-dark pb-4">
114
  <div class="flex items-center gap-2">
115
+ <svg class="size-5 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 010 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 010-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
116
  <h2 class="text-[0.95rem] font-bold">API Configuration</h2>
117
  </div>
118
  <button onclick="resetConfigDefaults()" class="text-xs text-slate-400 dark:text-text-dim hover:text-slate-600 dark:hover:text-text-main transition-colors">Reset to defaults</button>
 
124
  <div class="relative flex items-center">
125
  <input id="baseUrl" class="w-full pl-3 pr-10 py-2.5 bg-slate-50 dark:bg-bg-dark border border-gray-200 dark:border-border-dark rounded-lg text-[0.78rem] font-mono text-slate-600 dark:text-text-main focus:ring-1 focus:ring-primary focus:border-primary outline-none transition-all" type="text" value="Loading..."/>
126
  <button onclick="copyField('baseUrl', this)" class="absolute right-2 p-1.5 text-slate-400 dark:text-text-dim hover:text-primary transition-colors rounded-md hover:bg-slate-100 dark:hover:bg-border-dark" title="Copy URL">
127
+ <svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
128
  </button>
129
  </div>
130
  </div>
 
136
  <option value="codex">codex</option>
137
  </select>
138
  <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-slate-500 dark:text-text-dim">
139
+ <svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/></svg>
140
  </div>
141
  </div>
142
  </div>
 
145
  <label class="text-xs font-semibold text-slate-700 dark:text-text-main">Your API Key</label>
146
  <div class="relative flex items-center">
147
  <div class="absolute left-3 text-slate-400 dark:text-text-dim">
148
+ <svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"/></svg>
149
  </div>
150
  <input id="apiKey" class="w-full pl-10 pr-10 py-2.5 bg-slate-50 dark:bg-bg-dark border border-gray-200 dark:border-border-dark rounded-lg text-[0.78rem] font-mono text-slate-600 dark:text-text-main focus:ring-1 focus:ring-primary focus:border-primary outline-none transition-all tracking-wider" type="text" value="Loading..."/>
151
  <button onclick="copyField('apiKey', this)" class="absolute right-2 p-1.5 text-slate-400 dark:text-text-dim hover:text-primary transition-colors rounded-md hover:bg-slate-100 dark:hover:bg-border-dark" title="Copy API Key">
152
+ <svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
153
  </button>
154
  </div>
155
  <p class="text-xs text-slate-400 dark:text-text-dim mt-1">Use this key to authenticate requests to the proxy. Do not share it.</p>
 
180
  <div id="codeContainer" class="relative group rounded-lg overflow-hidden bg-[#0d1117] text-slate-300 font-mono text-xs border border-slate-800 dark:border-border-dark">
181
  <div class="absolute right-2 top-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
182
  <button onclick="copyCurrentCode()" class="flex items-center gap-1.5 px-3 py-1.5 bg-slate-700 hover:bg-slate-600 text-white rounded text-xs font-medium transition-colors">
183
+ <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg> Copy
184
  </button>
185
  </div>
186
  <div class="p-4 overflow-x-auto">
 
224
  updateThemeIcon();
225
  }
226
 
227
+ const SVG_MOON = '<svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z"/></svg>';
228
+ const SVG_SUN = '<svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"/></svg>';
229
+ const SVG_COPY = '<svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>';
230
+ const SVG_CHECK = '<svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>';
231
+
232
  function updateThemeIcon() {
233
+ const el = document.getElementById('themeIcon');
234
+ if (!el) return;
235
+ el.innerHTML = document.documentElement.classList.contains('dark') ? SVG_SUN : SVG_MOON;
236
  }
237
  updateThemeIcon();
238
 
 
340
  return `
341
  <div class="group bg-white dark:bg-card-dark border border-gray-200 dark:border-border-dark rounded-xl p-4 shadow-sm hover:shadow-md transition-all hover:border-primary/30 dark:hover:border-primary/50 relative">
342
  <button onclick="deleteAccount('${escapeHtml(acct.id)}')" class="absolute top-3 right-3 opacity-0 group-hover:opacity-100 p-1.5 text-slate-300 dark:text-text-dim hover:text-red-500 transition-all rounded-md hover:bg-red-50 dark:hover:bg-red-900/20" title="Delete account">
343
+ <svg class="size-[18px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"/></svg>
344
  </button>
345
  <div class="flex justify-between items-start mb-4">
346
  <div class="flex items-center gap-3">
 
671
  const el = document.getElementById(id);
672
  const text = el.value !== undefined ? el.value : el.textContent;
673
  navigator.clipboard.writeText(text);
674
+ const svg = btn.querySelector('svg');
675
+ if (svg) {
676
+ btn.innerHTML = SVG_CHECK;
677
  btn.classList.add('text-primary');
678
+ setTimeout(() => { btn.innerHTML = SVG_COPY; btn.classList.remove('text-primary'); }, 2000);
679
  }
680
  }
681