fredmo commited on
Commit
3c68fa7
·
verified ·
1 Parent(s): 555743a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +583 -19
index.html CHANGED
@@ -1,19 +1,583 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta author="fredmo">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>GPU Memory Configurator</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
9
+ <script>
10
+ document.addEventListener('DOMContentLoaded', () => {
11
+
12
+ // --- Data Definitions ---
13
+ const gpuData = { /* ... Keep as is ... */
14
+ 'L4': { name: 'L4', capacity: 24 },
15
+ 'A100-40': { name: 'A100 40GB', capacity: 40 },
16
+ 'A100-80': { name: 'A100 80GB', capacity: 80 },
17
+ 'H100': { name: 'H100 80GB', capacity: 80 }
18
+ };
19
+
20
+ const modelData = { /* ... Remove promptBaseSize ... */
21
+ 'gemma-1b': { name: 'Gemma 3 1B', baseModelSize: 3 }, // Removed kvCacheFactor, handled by utilization now
22
+ 'gemma-4b': { name: 'Gemma 3 4B', baseModelSize: 8 },
23
+ 'gemma-12b': { name: 'Gemma 3 12B', baseModelSize: 20 },
24
+ 'gemma-27b': { name: 'Gemma 3 27B', baseModelSize: 45 }
25
+ };
26
+
27
+ const quantizationData = { /* ... Keep as is ... */
28
+ 'fp32': { name: 'FP32', modifier: 1.0 },
29
+ 'bf16': { name: 'BF16', modifier: 0.5 },
30
+ 'int4': { name: 'INT4', modifier: 0.25 }
31
+ };
32
+
33
+ // NEW: GPU Memory Utilization Data
34
+ const gpuMemoryUtilizationData = {
35
+ 'util-0.5': { name: '50% KV Util', factor: 0.5 },
36
+ 'util-0.7': { name: '70% KV Util', factor: 0.7 },
37
+ 'util-0.9': { name: '90% KV Util', factor: 0.9 }
38
+ };
39
+
40
+ // --- State Variables ---
41
+ let selectedGpuId = 'A100-80';
42
+ let selectedModelId = 'gemma-1b';
43
+ let selectedQuantizationId = 'fp32';
44
+ let selectedUtilizationId = 'util-0.7'; // Default to 70%
45
+
46
+ // --- DOM References ---
47
+ const gpuSelectorDiv = document.getElementById('gpu-selector');
48
+ const modelSelectorDiv = document.getElementById('model-selector');
49
+ const quantizationSelectorDiv = document.getElementById('quantization-selector');
50
+ const utilizationSelectorDiv = document.getElementById('utilization-selector'); // New
51
+
52
+ const gpuCapacityLabel = document.getElementById('gpu-capacity-label');
53
+ const visualizationArea = document.getElementById('gpu-visualization-area');
54
+ // Removed promptBar reference
55
+ const modelBar = document.getElementById('model-bar');
56
+ const kvcacheBar = document.getElementById('kvcache-bar');
57
+
58
+ // Removed promptUsageSpan reference
59
+ const modelUsageSpan = document.getElementById('model-usage');
60
+ const kvcacheUsageSpan = document.getElementById('kvcache-usage');
61
+ const kvcachePromptInfoSpan = document.getElementById('kvcache-prompt-info'); // New span for info
62
+ const totalUsageSpan = document.getElementById('total-usage');
63
+ const fitStatusSpan = document.getElementById('fit-status');
64
+
65
+ // --- Initialization ---
66
+ function initializeControls() {
67
+ createButtons(gpuData, gpuSelectorDiv, handleGpuSelect, selectedGpuId);
68
+ createButtons(modelData, modelSelectorDiv, handleModelSelect, selectedModelId);
69
+ createButtons(quantizationData, quantizationSelectorDiv, handleQuantizationSelect, selectedQuantizationId);
70
+ createButtons(gpuMemoryUtilizationData, utilizationSelectorDiv, handleUtilizationSelect, selectedUtilizationId); // Create Util buttons
71
+
72
+ updateCalculationAndDisplay(); // Initial calculation
73
+ }
74
+
75
+ function createButtons(data, container, clickHandler, activeId) {
76
+ // Clear existing buttons first
77
+ const existingButtons = container.querySelectorAll('button');
78
+ existingButtons.forEach(btn => btn.remove());
79
+
80
+ Object.keys(data).forEach(id => {
81
+ const item = data[id];
82
+ const button = document.createElement('button');
83
+ button.textContent = item.name;
84
+ button.dataset.id = id;
85
+ button.addEventListener('click', () => clickHandler(id, container));
86
+ if (id === activeId) {
87
+ button.classList.add('active');
88
+ }
89
+ container.appendChild(button);
90
+ });
91
+ }
92
+
93
+ // --- Event Handlers ---
94
+ function handleGpuSelect(id, container) {
95
+ selectedGpuId = id;
96
+ updateActiveButton(container, id);
97
+ updateCalculationAndDisplay();
98
+ }
99
+
100
+ function handleModelSelect(id, container) {
101
+ selectedModelId = id;
102
+ updateActiveButton(container, id);
103
+ updateCalculationAndDisplay();
104
+ }
105
+
106
+ function handleQuantizationSelect(id, container) {
107
+ selectedQuantizationId = id;
108
+ updateActiveButton(container, id);
109
+ updateCalculationAndDisplay();
110
+ }
111
+
112
+ function handleUtilizationSelect(id, container) { // New handler
113
+ selectedUtilizationId = id;
114
+ updateActiveButton(container, id);
115
+ updateCalculationAndDisplay();
116
+ }
117
+
118
+ function updateActiveButton(container, activeId) {
119
+ const buttons = container.querySelectorAll('button');
120
+ buttons.forEach(button => {
121
+ button.classList.toggle('active', button.dataset.id === activeId);
122
+ });
123
+ }
124
+
125
+ // --- Calculation Logic ---
126
+ function calculateSizes() {
127
+ const gpu = gpuData[selectedGpuId];
128
+ const model = modelData[selectedModelId];
129
+ const quantization = quantizationData[selectedQuantizationId];
130
+ const utilization = gpuMemoryUtilizationData[selectedUtilizationId]; // Get utilization info
131
+
132
+ const quantModifier = quantization.modifier;
133
+
134
+ // 1. Calculate Model Size (quantized)
135
+ const modelSize = Math.max(1, Math.ceil(model.baseModelSize * quantModifier));
136
+
137
+ // 2. Calculate Remaining Space
138
+ const remainingSpace = gpu.capacity - modelSize;
139
+
140
+ // 3. Calculate KV Cache Size based on utilization of *remaining* space
141
+ let kvCacheSize = 0;
142
+ if (remainingSpace > 0) {
143
+ // Use floor to ensure KV Cache doesn't exceed the allocated percentage
144
+ kvCacheSize = Math.floor(remainingSpace * utilization.factor);
145
+ kvCacheSize = Math.max(0, kvCacheSize); // Ensure it's not negative if factor is weirdly small
146
+ }
147
+ // No minimum of 1 for KV cache - it can be 0 if no space remains
148
+
149
+ // 4. Calculate Total Size
150
+ const totalSize = modelSize + kvCacheSize;
151
+
152
+ return {
153
+ gpuCapacity: gpu.capacity,
154
+ // promptSize removed
155
+ modelSize,
156
+ kvCacheSize,
157
+ totalSize,
158
+ fits: totalSize <= gpu.capacity && modelSize <= gpu.capacity // Also check if model itself fits
159
+ };
160
+ }
161
+
162
+ // --- Display Logic ---
163
+ function updateCalculationAndDisplay() {
164
+ const sizes = calculateSizes();
165
+
166
+ // Update Text Details
167
+ // promptUsageSpan removed
168
+ modelUsageSpan.textContent = sizes.modelSize;
169
+ kvcacheUsageSpan.textContent = sizes.kvCacheSize;
170
+ kvcachePromptInfoSpan.textContent = sizes.kvCacheSize > 0 ? "(incl. prompts)" : ""; // Add info text
171
+ totalUsageSpan.textContent = sizes.totalSize;
172
+ gpuCapacityLabel.textContent = `${sizes.gpuCapacity} Blocks`;
173
+
174
+ // Update Status Message
175
+ if (sizes.modelSize > sizes.gpuCapacity) {
176
+ fitStatusSpan.textContent = "Model alone exceeds GPU capacity!";
177
+ fitStatusSpan.className = 'status-no-fit status-error'; // Add extra error class
178
+ } else if (sizes.fits) {
179
+ fitStatusSpan.textContent = "Fits!";
180
+ fitStatusSpan.className = 'status-fits';
181
+ } else {
182
+ fitStatusSpan.textContent = "Does Not Fit (Model + KV Cache)!";
183
+ fitStatusSpan.className = 'status-no-fit';
184
+ }
185
+ // Update visualization border based on overall fit status
186
+ visualizationArea.style.borderColor = sizes.fits ? 'var(--border-glow-color)' : 'var(--error-color)';
187
+
188
+
189
+ // Update Visual Bars
190
+ updateVisualBars(sizes);
191
+ }
192
+
193
+ function updateVisualBars(sizes) {
194
+ const { modelSize, kvCacheSize, gpuCapacity } = sizes;
195
+
196
+ // Calculate percentages relative to GPU capacity
197
+ const modelPercent = (modelSize / gpuCapacity) * 100;
198
+ const kvCachePercent = (kvCacheSize / gpuCapacity) * 100;
199
+
200
+ // Calculate bottom offsets for stacking (Model is now at the bottom)
201
+ const kvCacheBottomPercent = modelPercent;
202
+
203
+ // Apply styles - Cap percentages and handle potential overflow
204
+ modelBar.style.height = `${Math.min(100, modelPercent)}%`;
205
+ modelBar.style.bottom = `0%`;
206
+
207
+ kvcacheBar.style.height = `${Math.min(100 - modelPercent, kvCachePercent)}%`; // Height is capped by remaining space
208
+ kvcacheBar.style.bottom = `${Math.min(100, modelPercent)}%`; // Starts above the model bar
209
+
210
+ // Update data-label attributes
211
+ modelBar.dataset.label = `Model (${modelSize})`;
212
+ // Clarify KV cache label
213
+ kvcacheBar.dataset.label = `KV Cache (${kvCacheSize}) ${sizes.kvCacheSize > 0 ? '[+Prompts]' : ''}`;
214
+ }
215
+
216
+ // --- Start the app ---
217
+ initializeControls();
218
+
219
+ }); // End DOMContentLoaded
220
+ </script>
221
+ <style>
222
+ /* --- Keep all existing styles from the previous version --- */
223
+ :root {
224
+ --background-color: #1a0a2d; /* Deep purple/blue */
225
+ --primary-color: #ff00ff; /* Magenta/Pink */
226
+ --secondary-color: #00ffff; /* Cyan */
227
+ --accent-color: #f8f8f8; /* Bright White/Off-white */
228
+ --border-glow-color: #7f00ff; /* Purple Glow */
229
+
230
+ --prompt-color: #00ff00; /* Bright Green */
231
+ --model-color: var(--secondary-color); /* Cyan */
232
+ --kvcache-color: #ffff00; /* Yellow */
233
+
234
+ --gpu-area-bg: #0a0514; /* Very dark for GPU viz */
235
+ --control-bg: rgba(58, 26, 90, 0.5); /* Darker purple for controls */
236
+
237
+ --error-color: #ff4136; /* Red for errors/no fit */
238
+ --success-color: #39cccc; /* Teal/Cyan for success/fit */
239
+
240
+ --glow-primary: 0 0 5px var(--primary-color), 0 0 10px var(--primary-color), 0 0 15px var(--primary-color);
241
+ --glow-secondary: 0 0 5px var(--secondary-color), 0 0 10px var(--secondary-color), 0 0 15px var(--secondary-color);
242
+ --glow-border: 0 0 8px var(--border-glow-color), 0 0 15px var(--border-glow-color);
243
+ --text-glow: 0 0 3px var(--accent-color), 0 0 5px var(--primary-color);
244
+ }
245
+
246
+ body {
247
+ background-color: var(--background-color);
248
+ color: var(--accent-color);
249
+ font-family: 'Orbitron', sans-serif;
250
+ margin: 0;
251
+ padding: 20px;
252
+ display: flex;
253
+ justify-content: center;
254
+ align-items: flex-start;
255
+ min-height: 100vh;
256
+ background-image:
257
+ linear-gradient(rgba(26, 10, 45, 0.9), rgba(26, 10, 45, 0.9)),
258
+ /* Grid overlay */
259
+ linear-gradient(var(--border-glow-color) 1px, transparent 1px),
260
+ linear-gradient(90deg, var(--border-glow-color) 1px, transparent 1px);
261
+ background-size: 100% 100%, 40px 40px, 40px 40px; /* Adjust grid size */
262
+ background-position: 0 0, -1px -1px, -1px -1px; /* Align grid */
263
+ }
264
+
265
+ #configurator-container {
266
+ background-color: rgba(10, 5, 20, 0.85);
267
+ border: 2px solid var(--border-glow-color);
268
+ box-shadow: var(--glow-border);
269
+ padding: 25px;
270
+ border-radius: 10px;
271
+ width: 95%;
272
+ max-width: 1200px; /* Wider layout */
273
+ text-align: center;
274
+ }
275
+
276
+ header h1 {
277
+ color: var(--primary-color);
278
+ text-shadow: var(--text-glow);
279
+ margin-bottom: 5px;
280
+ }
281
+
282
+ header p {
283
+ color: var(--secondary-color);
284
+ margin-bottom: 25px;
285
+ }
286
+
287
+ #main-layout {
288
+ display: flex;
289
+ justify-content: space-between;
290
+ gap: 25px; /* Space between columns */
291
+ text-align: left;
292
+ }
293
+
294
+ /* Column Styling */
295
+ #gpu-visualization-column {
296
+ flex: 2; /* Takes more space */
297
+ display: flex;
298
+ flex-direction: column;
299
+ }
300
+
301
+ #spacer-column {
302
+ flex: 0.1; /* Thin spacer */
303
+ }
304
+
305
+ #controls-column {
306
+ flex: 1; /* Takes less space */
307
+ display: flex;
308
+ flex-direction: column;
309
+ gap: 20px; /* Space between control groups */
310
+ }
311
+
312
+ /* Left Column: Visualization */
313
+ #gpu-visualization-column h2, #controls-column h3 {
314
+ color: var(--secondary-color);
315
+ text-shadow: 0 0 5px var(--secondary-color);
316
+ margin-top: 0;
317
+ margin-bottom: 15px;
318
+ border-bottom: 1px solid var(--secondary-color);
319
+ padding-bottom: 5px;
320
+ text-align: center;
321
+ }
322
+
323
+ #gpu-visualization-area {
324
+ flex-grow: 1;
325
+ background-color: var(--gpu-area-bg);
326
+ border: 2px solid var(--border-glow-color);
327
+ border-radius: 8px;
328
+ position: relative; /* For absolute positioning of bars and grid */
329
+ min-height: 400px; /* Ensure enough height */
330
+ overflow: hidden; /* Hide overflow */
331
+ box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
332
+ }
333
+
334
+ #gpu-grid-overlay {
335
+ position: absolute;
336
+ top: 0; left: 0; right: 0; bottom: 0;
337
+ background-image:
338
+ linear-gradient(rgba(127, 0, 255, 0.3) 1px, transparent 1px), /* Fainter grid lines */
339
+ linear-gradient(90deg, rgba(127, 0, 255, 0.3) 1px, transparent 1px);
340
+ background-size: 10% 10%; /* 10x10 grid visually */
341
+ pointer-events: none; /* Allow clicks through */
342
+ z-index: 1;
343
+ }
344
+
345
+
346
+ #gpu-capacity-indicator {
347
+ position: absolute;
348
+ top: 5px;
349
+ right: 10px;
350
+ color: var(--accent-color);
351
+ background: rgba(0,0,0,0.5);
352
+ padding: 3px 8px;
353
+ border-radius: 4px;
354
+ font-size: 0.8em;
355
+ z-index: 3; /* Above bars */
356
+ }
357
+
358
+ .usage-bar {
359
+ position: absolute;
360
+ bottom: 0;
361
+ left: 0;
362
+ width: 100%;
363
+ height: 0; /* Initial height */
364
+ transition: height 0.5s ease-out, bottom 0.5s ease-out; /* Animate changes */
365
+ box-shadow: inset 0 2px 5px rgba(0,0,0,0.5);
366
+ z-index: 2; /* Below capacity indicator */
367
+ display: flex;
368
+ align-items: flex-start; /* Label at the top */
369
+ justify-content: center;
370
+ overflow: hidden; /* Prevent label overflow */
371
+ }
372
+
373
+ .usage-bar::before { /* Add label inside the bar */
374
+ content: attr(data-label);
375
+ position: absolute;
376
+ top: 5px; /* Position label near the top */
377
+ left: 10px;
378
+ color: #000; /* Black label for contrast */
379
+ font-size: 0.9em;
380
+ font-weight: bold;
381
+ text-shadow: 0 0 2px rgba(255, 255, 255, 0.7);
382
+ opacity: 0; /* Initially hidden */
383
+ transition: opacity 0.3s ease-in 0.3s; /* Fade in after resize */
384
+ }
385
+
386
+ .usage-bar[style*="height: 0"]::before {
387
+ opacity: 0 !important; /* Keep hidden if height is 0 */
388
+ }
389
+ .usage-bar:not([style*="height: 0"])::before {
390
+ opacity: 1; /* Show if height is not 0 */
391
+ }
392
+
393
+
394
+ /*#prompt-bar { background-color: var(--prompt-color); }*/
395
+ #model-bar { background-color: var(--model-color); }
396
+ #kvcache-bar { background-color: var(--kvcache-color); }
397
+
398
+
399
+ #usage-details {
400
+ margin-top: 15px;
401
+ padding: 10px;
402
+ background-color: var(--control-bg);
403
+ border: 1px dashed var(--secondary-color);
404
+ border-radius: 5px;
405
+ font-size: 0.9em;
406
+ }
407
+ #usage-details p { margin: 4px 0; }
408
+ #usage-details span { font-weight: bold; color: var(--primary-color); }
409
+ #usage-details .total-usage span { color: var(--accent-color); }
410
+
411
+
412
+ /* Right Column: Controls */
413
+ .control-group {
414
+ background-color: var(--control-bg);
415
+ border: 1px solid var(--primary-color);
416
+ border-radius: 6px;
417
+ padding: 15px;
418
+ }
419
+
420
+ .control-group h3 {
421
+ border-bottom: none; /* Remove border from h3 inside group */
422
+ margin-bottom: 10px;
423
+ color: var(--primary-color);
424
+ }
425
+
426
+ .control-group button {
427
+ display: block; /* Stack buttons vertically */
428
+ width: 100%;
429
+ background-color: var(--background-color);
430
+ color: var(--accent-color);
431
+ border: 1px solid var(--secondary-color);
432
+ padding: 10px 15px;
433
+ border-radius: 4px;
434
+ font-family: 'Orbitron', sans-serif;
435
+ cursor: pointer;
436
+ margin-bottom: 8px;
437
+ transition: background-color 0.3s, box-shadow 0.3s, border-color 0.3s;
438
+ text-align: center;
439
+ }
440
+ .control-group button:last-child { margin-bottom: 0; }
441
+
442
+ .control-group button:hover {
443
+ background-color: var(--secondary-color);
444
+ color: var(--background-color);
445
+ border-color: var(--secondary-color);
446
+ box-shadow: var(--glow-secondary);
447
+ }
448
+
449
+ .control-group button.active {
450
+ background-color: var(--primary-color);
451
+ color: var(--background-color);
452
+ border-color: var(--primary-color);
453
+ box-shadow: var(--glow-primary);
454
+ font-weight: bold;
455
+ }
456
+
457
+ /* Status Message */
458
+ #status-message {
459
+ margin-top: auto; /* Push to bottom */
460
+ padding: 15px;
461
+ border: 1px dashed var(--border-glow-color);
462
+ border-radius: 5px;
463
+ background-color: rgba(0,0,0, 0.3);
464
+ text-align: center;
465
+ }
466
+ #status-message p { margin: 0; }
467
+ #status-message span { font-weight: bold; }
468
+
469
+ #status-message .status-fits { color: var(--success-color); }
470
+ #status-message .status-no-fit { color: var(--error-color); }
471
+ #status-message .status-error { color: var(--error-color); font-weight: bold;}
472
+
473
+ footer {
474
+ margin-top: 30px;
475
+ color: var(--secondary-color);
476
+ font-size: 0.8em;
477
+ opacity: 0.7;
478
+ text-align: center;
479
+ width: 100%;
480
+ }
481
+
482
+ /* --- Remove or Comment Out --- */
483
+ /* #prompt-bar { background-color: var(--prompt-color); } */
484
+
485
+ /* --- Add or Modify --- */
486
+ #usage-details #kvcache-prompt-info {
487
+ font-size: 0.85em;
488
+ color: var(--secondary-color);
489
+ opacity: 0.8;
490
+ margin-left: 5px;
491
+ }
492
+
493
+ /* Adjust grid lines maybe? */
494
+ #gpu-grid-overlay {
495
+ background-image:
496
+ linear-gradient(rgba(127, 0, 255, 0.2) 1px, transparent 1px), /* Make grid lines fainter */
497
+ linear-gradient(90deg, rgba(127, 0, 255, 0.2) 1px, transparent 1px);
498
+ background-size: 10% 10%;
499
+ /* ... keep rest */
500
+ }
501
+
502
+ /* Ensure label contrast is okay on yellow */
503
+ #kvcache-bar::before {
504
+ color: #333; /* Darker gray/black for yellow background */
505
+ text-shadow: 0 0 2px rgba(255, 255, 255, 0.5);
506
+ }
507
+
508
+
509
+ /* Make sure control group spacing is okay */
510
+ #controls-column {
511
+ gap: 15px; /* Slightly reduce gap if needed */
512
+ }
513
+ </style>
514
+ </head>
515
+ <body>
516
+ <div id="configurator-container">
517
+
518
+ <header>
519
+ <h1>GPU Memory Configurator</h1>
520
+ <p>Select GPU, Model, Quantization & Utilization to estimate VRAM usage</p>
521
+ </header>
522
+
523
+ <div id="main-layout">
524
+
525
+ <!-- Left Column: Visualization -->
526
+ <div id="gpu-visualization-column">
527
+ <h2>GPU Usage</h2>
528
+ <div id="gpu-visualization-area">
529
+ <div id="gpu-capacity-indicator">
530
+ <span id="gpu-capacity-label">80 Blocks</span>
531
+ </div>
532
+ <!-- Removed Prompt Bar -->
533
+ <div id="model-bar" class="usage-bar" data-label="Model"></div>
534
+ <div id="kvcache-bar" class="usage-bar" data-label="KV Cache"></div>
535
+ <div id="gpu-grid-overlay"></div>
536
+ </div>
537
+ <div id="usage-details">
538
+ <!-- Removed Prompt Usage Line -->
539
+ <p>Model: <span id="model-usage">0</span> Blocks</p>
540
+ <p>KV Cache: <span id="kvcache-usage">0</span> Blocks <span id="kvcache-prompt-info"></span></p>
541
+ <p class="total-usage">Total: <span id="total-usage">0</span> Blocks</p>
542
+ </div>
543
+ </div>
544
+
545
+ <!-- Middle Column: Spacer -->
546
+ <div id="spacer-column"></div>
547
+
548
+ <!-- Right Column: Controls -->
549
+ <div id="controls-column">
550
+ <div class="control-group" id="gpu-selector">
551
+ <h3>GPU</h3>
552
+ <!-- Buttons will be generated by JS -->
553
+ </div>
554
+ <div class="control-group" id="model-selector">
555
+ <h3>Model</h3>
556
+ <!-- Buttons will be generated by JS -->
557
+ </div>
558
+ <div class="control-group" id="quantization-selector">
559
+ <h3>Quantization</h3>
560
+ <!-- Buttons will be generated by JS -->
561
+ </div>
562
+ <!-- New Utilization Selector -->
563
+ <div class="control-group" id="utilization-selector">
564
+ <h3>KV Cache Util</h3>
565
+ <!-- Buttons will be generated by JS -->
566
+ </div>
567
+
568
+ <div id="status-message">
569
+ <p>Status: <span id="fit-status">-</span></p>
570
+ </div>
571
+ </div>
572
+
573
+ </div>
574
+
575
+ <footer>
576
+ Synthwave VRAM Estimator
577
+ </footer>
578
+
579
+ </div>
580
+
581
+ <script src="script.js"></script>
582
+ </body>
583
+ </html>