MySafeCode commited on
Commit
86bb156
·
verified ·
1 Parent(s): 2eeac5f

Delete index.html

Browse files
Files changed (1) hide show
  1. index.html +0 -1211
index.html DELETED
@@ -1,1211 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>StableCog Dashboard</title>
7
- <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎨</text></svg>">
8
-
9
- <style>
10
- :root {
11
- --primary-color: #667eea;
12
- --secondary-color: #764ba2;
13
- --success-color: #4CAF50;
14
- --warning-color: #FF9800;
15
- --danger-color: #F44336;
16
- --light-color: #90EE90;
17
- --dark-color: #333;
18
- --card-bg: rgba(255, 255, 255, 0.1);
19
- --text-light: #ffffff;
20
- --text-dark: #333333;
21
- --shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
22
- --border-radius: 15px;
23
- }
24
-
25
- * {
26
- margin: 0;
27
- padding: 0;
28
- box-sizing: border-box;
29
- }
30
-
31
- body {
32
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
33
- background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
34
- min-height: 100vh;
35
- padding: 20px;
36
- color: var(--text-light);
37
- }
38
-
39
- .container {
40
- max-width: 1200px;
41
- margin: 0 auto;
42
- }
43
-
44
- .header {
45
- text-align: center;
46
- margin-bottom: 30px;
47
- padding: 20px;
48
- }
49
-
50
- .header h1 {
51
- font-size: 2.8rem;
52
- margin-bottom: 10px;
53
- font-weight: 700;
54
- }
55
-
56
- .header p {
57
- font-size: 1.1rem;
58
- opacity: 0.9;
59
- max-width: 600px;
60
- margin: 0 auto;
61
- }
62
-
63
- .dashboard {
64
- display: grid;
65
- grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
66
- gap: 25px;
67
- margin-bottom: 30px;
68
- }
69
-
70
- .card {
71
- background: var(--card-bg);
72
- backdrop-filter: blur(10px);
73
- border-radius: var(--border-radius);
74
- padding: 25px;
75
- border: 1px solid rgba(255, 255, 255, 0.2);
76
- box-shadow: var(--shadow);
77
- transition: transform 0.3s ease;
78
- }
79
-
80
- .card:hover {
81
- transform: translateY(-5px);
82
- }
83
-
84
- .card-title {
85
- font-size: 1.5rem;
86
- margin-bottom: 20px;
87
- display: flex;
88
- align-items: center;
89
- gap: 10px;
90
- }
91
-
92
- .card-title i {
93
- font-size: 1.8rem;
94
- }
95
-
96
- .stat-grid {
97
- display: grid;
98
- grid-template-columns: repeat(2, 1fr);
99
- gap: 15px;
100
- margin-bottom: 20px;
101
- }
102
-
103
- .stat-box {
104
- background: rgba(255, 255, 255, 0.1);
105
- border-radius: 10px;
106
- padding: 15px;
107
- text-align: center;
108
- }
109
-
110
- .stat-value {
111
- font-size: 2.2rem;
112
- font-weight: bold;
113
- margin-bottom: 5px;
114
- }
115
-
116
- .stat-label {
117
- font-size: 0.9rem;
118
- opacity: 0.8;
119
- }
120
-
121
- .progress-container {
122
- margin: 20px 0;
123
- }
124
-
125
- .progress-header {
126
- display: flex;
127
- justify-content: space-between;
128
- margin-bottom: 8px;
129
- }
130
-
131
- .progress-bar {
132
- height: 22px;
133
- background: rgba(255, 255, 255, 0.15);
134
- border-radius: 11px;
135
- overflow: hidden;
136
- position: relative;
137
- }
138
-
139
- .progress-fill {
140
- height: 100%;
141
- border-radius: 11px;
142
- transition: width 0.5s ease-in-out;
143
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
144
- }
145
-
146
- .progress-text {
147
- position: absolute;
148
- right: 10px;
149
- top: 50%;
150
- transform: translateY(-50%);
151
- font-size: 12px;
152
- font-weight: bold;
153
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
154
- }
155
-
156
- .status-badge {
157
- display: inline-flex;
158
- align-items: center;
159
- gap: 8px;
160
- padding: 8px 16px;
161
- background: rgba(255, 255, 255, 0.1);
162
- border-radius: 20px;
163
- font-weight: bold;
164
- margin-top: 15px;
165
- }
166
-
167
- .models-grid {
168
- display: grid;
169
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
170
- gap: 15px;
171
- max-height: 500px;
172
- overflow-y: auto;
173
- padding-right: 10px;
174
- }
175
-
176
- .model-card {
177
- background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
178
- border-radius: 10px;
179
- padding: 15px;
180
- border-left: 4px solid var(--primary-color);
181
- }
182
-
183
- .model-header {
184
- display: flex;
185
- justify-content: space-between;
186
- align-items: center;
187
- margin-bottom: 8px;
188
- }
189
-
190
- .model-name {
191
- font-weight: bold;
192
- font-size: 1rem;
193
- display: flex;
194
- align-items: center;
195
- gap: 8px;
196
- }
197
-
198
- .model-badge {
199
- padding: 2px 8px;
200
- border-radius: 12px;
201
- font-size: 0.7rem;
202
- font-weight: bold;
203
- }
204
-
205
- .badge-default { background: var(--success-color); }
206
- .badge-public { background: #2196F3; }
207
- .badge-community { background: var(--danger-color); }
208
- .badge-other { background: #9E9E9E; }
209
-
210
- .model-id {
211
- font-size: 0.8rem;
212
- opacity: 0.7;
213
- }
214
-
215
- .model-description {
216
- font-size: 0.9rem;
217
- opacity: 0.9;
218
- margin-bottom: 10px;
219
- line-height: 1.4;
220
- }
221
-
222
- .model-meta {
223
- display: flex;
224
- gap: 10px;
225
- font-size: 0.8rem;
226
- opacity: 0.7;
227
- }
228
-
229
- .controls {
230
- display: flex;
231
- gap: 15px;
232
- margin-top: 25px;
233
- flex-wrap: wrap;
234
- }
235
-
236
- .btn {
237
- padding: 12px 24px;
238
- border: none;
239
- border-radius: 10px;
240
- font-size: 1rem;
241
- font-weight: 600;
242
- cursor: pointer;
243
- display: flex;
244
- align-items: center;
245
- gap: 8px;
246
- transition: all 0.3s ease;
247
- }
248
-
249
- .btn-primary {
250
- background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
251
- color: white;
252
- }
253
-
254
- .btn-secondary {
255
- background: rgba(255, 255, 255, 0.2);
256
- color: white;
257
- border: 1px solid rgba(255, 255, 255, 0.3);
258
- }
259
-
260
- .btn:hover {
261
- transform: translateY(-2px);
262
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
263
- }
264
-
265
- .btn:active {
266
- transform: translateY(0);
267
- }
268
-
269
- .api-key-input {
270
- display: flex;
271
- gap: 10px;
272
- margin-bottom: 20px;
273
- flex-wrap: wrap;
274
- }
275
-
276
- .api-key-input input {
277
- flex: 1;
278
- min-width: 300px;
279
- padding: 12px 15px;
280
- border: 1px solid rgba(255, 255, 255, 0.3);
281
- border-radius: 10px;
282
- background: rgba(255, 255, 255, 0.1);
283
- color: white;
284
- font-size: 1rem;
285
- }
286
-
287
- .api-key-input input::placeholder {
288
- color: rgba(255, 255, 255, 0.6);
289
- }
290
-
291
- .api-key-input input:focus {
292
- outline: none;
293
- border-color: var(--primary-color);
294
- box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.3);
295
- }
296
-
297
- .error-container {
298
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
299
- border-radius: var(--border-radius);
300
- padding: 25px;
301
- margin: 20px 0;
302
- text-align: center;
303
- }
304
-
305
- .error-title {
306
- font-size: 1.5rem;
307
- margin-bottom: 15px;
308
- }
309
-
310
- .error-content {
311
- background: rgba(255, 255, 255, 0.15);
312
- backdrop-filter: blur(10px);
313
- padding: 20px;
314
- border-radius: 10px;
315
- margin-bottom: 20px;
316
- border: 1px solid rgba(255, 255, 255, 0.2);
317
- }
318
-
319
- .loading {
320
- display: inline-block;
321
- width: 20px;
322
- height: 20px;
323
- border: 3px solid rgba(255, 255, 255, 0.3);
324
- border-radius: 50%;
325
- border-top-color: white;
326
- animation: spin 1s ease-in-out infinite;
327
- }
328
-
329
- @keyframes spin {
330
- to { transform: rotate(360deg); }
331
- }
332
-
333
- .credit-breakdown {
334
- margin-top: 20px;
335
- padding-top: 20px;
336
- border-top: 1px solid rgba(255, 255, 255, 0.2);
337
- }
338
-
339
- .credit-item {
340
- background: rgba(255, 255, 255, 0.05);
341
- border-radius: 8px;
342
- padding: 12px;
343
- margin-bottom: 10px;
344
- }
345
-
346
- .credit-header {
347
- display: flex;
348
- justify-content: space-between;
349
- margin-bottom: 5px;
350
- }
351
-
352
- .credit-name {
353
- font-weight: bold;
354
- }
355
-
356
- .credit-amount {
357
- font-weight: bold;
358
- }
359
-
360
- .credit-description {
361
- font-size: 0.8rem;
362
- opacity: 0.8;
363
- margin-bottom: 8px;
364
- }
365
-
366
- .credit-progress {
367
- height: 6px;
368
- background: rgba(255, 255, 255, 0.1);
369
- border-radius: 3px;
370
- overflow: hidden;
371
- }
372
-
373
- .credit-progress-fill {
374
- height: 100%;
375
- border-radius: 3px;
376
- }
377
-
378
- .last-updated {
379
- text-align: center;
380
- font-size: 0.9rem;
381
- opacity: 0.7;
382
- margin-top: 10px;
383
- }
384
-
385
- .tabs {
386
- display: flex;
387
- gap: 5px;
388
- margin-bottom: 25px;
389
- background: rgba(255, 255, 255, 0.1);
390
- border-radius: 12px;
391
- padding: 5px;
392
- }
393
-
394
- .tab {
395
- flex: 1;
396
- padding: 12px 20px;
397
- text-align: center;
398
- border-radius: 8px;
399
- cursor: pointer;
400
- font-weight: 600;
401
- transition: all 0.3s ease;
402
- }
403
-
404
- .tab.active {
405
- background: white;
406
- color: var(--primary-color);
407
- }
408
-
409
- .tab-content {
410
- display: none;
411
- }
412
-
413
- .tab-content.active {
414
- display: block;
415
- }
416
-
417
- .footer {
418
- text-align: center;
419
- margin-top: 40px;
420
- padding-top: 20px;
421
- border-top: 1px solid rgba(255, 255, 255, 0.2);
422
- font-size: 0.9rem;
423
- opacity: 0.7;
424
- }
425
-
426
- @media (max-width: 768px) {
427
- .dashboard {
428
- grid-template-columns: 1fr;
429
- }
430
-
431
- .stat-grid {
432
- grid-template-columns: 1fr;
433
- }
434
-
435
- .models-grid {
436
- grid-template-columns: 1fr;
437
- }
438
-
439
- .header h1 {
440
- font-size: 2rem;
441
- }
442
-
443
- .api-key-input input {
444
- min-width: 100%;
445
- }
446
-
447
- .controls {
448
- flex-direction: column;
449
- }
450
-
451
- .btn {
452
- width: 100%;
453
- justify-content: center;
454
- }
455
- }
456
- </style>
457
- </head>
458
- <body>
459
- <div class="container">
460
- <div class="header">
461
- <h1>🎯 StableCog Dashboard</h1>
462
- <p>Monitor your credits and explore available AI models for image generation</p>
463
- </div>
464
-
465
- <!-- API Key Input -->
466
- <div class="api-key-input">
467
- <input type="password"
468
- id="apiKeyInput"
469
- placeholder="Enter your StableCog API key (or leave empty for demo)"
470
- autocomplete="off">
471
- <button class="btn btn-primary" onclick="saveApiKey()">
472
- <span>🔑</span> Save API Key
473
- </button>
474
- <button class="btn btn-secondary" onclick="clearApiKey()">
475
- <span>🗑️</span> Clear
476
- </button>
477
- </div>
478
-
479
- <!-- Tabs -->
480
- <div class="tabs">
481
- <div class="tab active" onclick="switchTab('credits')">💰 Credits</div>
482
- <div class="tab" onclick="switchTab('models')">🤖 Models</div>
483
- <div class="tab" onclick="switchTab('info')">📚 Info</div>
484
- </div>
485
-
486
- <!-- Credits Tab -->
487
- <div id="creditsTab" class="tab-content active">
488
- <div class="dashboard">
489
- <!-- Credit Status Card -->
490
- <div class="card">
491
- <h2 class="card-title">🎨 Credit Status</h2>
492
-
493
- <div class="stat-grid">
494
- <div class="stat-box">
495
- <div class="stat-value" id="totalCredits">0</div>
496
- <div class="stat-label">Total Credits</div>
497
- </div>
498
- <div class="stat-box">
499
- <div class="stat-value" id="remainingCredits">0</div>
500
- <div class="stat-label">Remaining Credits</div>
501
- </div>
502
- <div class="stat-box">
503
- <div class="stat-value" id="usedCredits">0</div>
504
- <div class="stat-label">Used Credits</div>
505
- </div>
506
- <div class="stat-box">
507
- <div class="stat-value" id="usagePercentage">0%</div>
508
- <div class="stat-label">Usage</div>
509
- </div>
510
- </div>
511
-
512
- <div class="progress-container">
513
- <div class="progress-header">
514
- <span>Overall Usage</span>
515
- <span id="usageText">0%</span>
516
- </div>
517
- <div class="progress-bar">
518
- <div class="progress-fill" id="progressFill"></div>
519
- <div class="progress-text" id="progressText">0%</div>
520
- </div>
521
- </div>
522
-
523
- <div class="status-badge" id="statusBadge">
524
- <span id="statusEmoji">🔄</span>
525
- <span id="statusText">Loading...</span>
526
- </div>
527
-
528
- <!-- Credit Breakdown -->
529
- <div class="credit-breakdown" id="creditBreakdown">
530
- <h3 style="margin-bottom: 15px; font-size: 18px;">📊 Credit Breakdown</h3>
531
- <!-- Credit items will be inserted here -->
532
- </div>
533
-
534
- <div class="last-updated" id="creditsLastUpdated">
535
- ⏰ Last checked: Never
536
- </div>
537
- </div>
538
-
539
- <!-- Recommendation Card -->
540
- <div class="card">
541
- <h2 class="card-title">🎯 AI Recommendation</h2>
542
- <div id="recommendation" style="font-size: 1.1rem; line-height: 1.6; opacity: 0.9;">
543
- Enter your API key and check credits to see personalized recommendations.
544
- </div>
545
- </div>
546
- </div>
547
-
548
- <div class="controls">
549
- <button class="btn btn-primary" onclick="checkCredits()" id="checkCreditsBtn">
550
- <span id="creditsIcon">🔄</span>
551
- <span id="creditsText">Check Credits</span>
552
- </button>
553
- <button class="btn btn-secondary" onclick="viewRawResponse('credits')">
554
- <span>📋</span> View Raw Response
555
- </button>
556
- </div>
557
-
558
- <!-- Raw Response Modal -->
559
- <div id="creditsRawModal" style="display: none; margin-top: 20px;">
560
- <div class="card">
561
- <h2 class="card-title">📋 Raw API Response</h2>
562
- <pre id="creditsRawResponse" style="
563
- background: rgba(0, 0, 0, 0.2);
564
- padding: 15px;
565
- border-radius: 8px;
566
- overflow: auto;
567
- max-height: 300px;
568
- font-family: 'Courier New', monospace;
569
- font-size: 12px;
570
- white-space: pre-wrap;
571
- "></pre>
572
- <button class="btn btn-secondary" onclick="hideRawResponse('credits')" style="margin-top: 15px;">
573
- <span>✕</span> Close
574
- </button>
575
- </div>
576
- </div>
577
- </div>
578
-
579
- <!-- Models Tab -->
580
- <div id="modelsTab" class="tab-content">
581
- <div class="dashboard">
582
- <div class="card">
583
- <h2 class="card-title">🤖 Available Models</h2>
584
-
585
- <div class="stat-grid">
586
- <div class="stat-box">
587
- <div class="stat-value" id="totalModels">0</div>
588
- <div class="stat-label">Total Models</div>
589
- </div>
590
- <div class="stat-box">
591
- <div class="stat-value" id="publicModels">0</div>
592
- <div class="stat-label">Public Models</div>
593
- </div>
594
- <div class="stat-box">
595
- <div class="stat-value" id="defaultModels">0</div>
596
- <div class="stat-label">Default Models</div>
597
- </div>
598
- <div class="stat-box">
599
- <div class="stat-value" id="communityModels">0</div>
600
- <div class="stat-label">Community Models</div>
601
- </div>
602
- </div>
603
-
604
- <div class="models-grid" id="modelsList">
605
- <!-- Models will be inserted here -->
606
- <div style="text-align: center; padding: 40px; opacity: 0.7;">
607
- Click "Load Models" to see available models
608
- </div>
609
- </div>
610
-
611
- <div class="last-updated" id="modelsLastUpdated">
612
- ⏰ Last updated: Never
613
- </div>
614
- </div>
615
- </div>
616
-
617
- <div class="controls">
618
- <button class="btn btn-primary" onclick="loadModels()" id="loadModelsBtn">
619
- <span id="modelsIcon">🔄</span>
620
- <span id="modelsText">Load Models</span>
621
- </button>
622
- <button class="btn btn-secondary" onclick="viewRawResponse('models')">
623
- <span>📋</span> View Raw Response
624
- </button>
625
- </div>
626
-
627
- <!-- Raw Response Modal -->
628
- <div id="modelsRawModal" style="display: none; margin-top: 20px;">
629
- <div class="card">
630
- <h2 class="card-title">📋 Raw API Response</h2>
631
- <pre id="modelsRawResponse" style="
632
- background: rgba(0, 0, 0, 0.2);
633
- padding: 15px;
634
- border-radius: 8px;
635
- overflow: auto;
636
- max-height: 300px;
637
- font-family: 'Courier New', monospace;
638
- font-size: 12px;
639
- white-space: pre-wrap;
640
- "></pre>
641
- <button class="btn btn-secondary" onclick="hideRawResponse('models')" style="margin-top: 15px;">
642
- <span>✕</span> Close
643
- </button>
644
- </div>
645
- </div>
646
- </div>
647
-
648
- <!-- Info Tab -->
649
- <div id="infoTab" class="tab-content">
650
- <div class="card">
651
- <h2 class="card-title">📚 How to Use & Setup</h2>
652
-
653
- <div style="line-height: 1.6; opacity: 0.9;">
654
- <h3 style="margin: 20px 0 10px 0;">Getting Started:</h3>
655
- <ol style="margin-left: 20px; margin-bottom: 20px;">
656
- <li>Enter your StableCog API key in the input field above</li>
657
- <li>Click "Save API Key" to store it in your browser</li>
658
- <li>Switch to "Credits" tab and click "Check Credits"</li>
659
- <li>Switch to "Models" tab and click "Load Models"</li>
660
- </ol>
661
-
662
- <h3 style="margin: 20px 0 10px 0;">Understanding Credits:</h3>
663
- <ul style="margin-left: 20px; margin-bottom: 20px;">
664
- <li><strong>Total Credits:</strong> Sum of all initial credits received</li>
665
- <li><strong>Remaining Credits:</strong> Currently available credits</li>
666
- <li><strong>Used Credits:</strong> Credits spent on image generation</li>
667
- <li><strong>Credit Types:</strong> Different sources (Free, Refund, etc.)</li>
668
- </ul>
669
-
670
- <h3 style="margin: 20px 0 10px 0;">Privacy & Security:</h3>
671
- <ul style="margin-left: 20px; margin-bottom: 20px;">
672
- <li>Your API key is stored only in your browser's localStorage</li>
673
- <li>No data is sent to any server except StableCog API</li>
674
- <li>You can clear the API key anytime using the "Clear" button</li>
675
- <li>All API calls are made directly from your browser</li>
676
- </ul>
677
-
678
- <h3 style="margin: 20px 0 10px 0;">Troubleshooting:</h3>
679
- <ul style="margin-left: 20px;">
680
- <li>Ensure your API key is correct and has proper permissions</li>
681
- <li>Check your internet connection</li>
682
- <li>Verify StableCog API is available</li>
683
- <li>Use "View Raw Response" to see detailed error messages</li>
684
- </ul>
685
- </div>
686
- </div>
687
- </div>
688
-
689
- <!-- Error Container (hidden by default) -->
690
- <div id="errorContainer" class="error-container" style="display: none;">
691
- <h2 class="error-title" id="errorTitle">⚠️ API Error</h2>
692
- <div class="error-content">
693
- <p id="errorMessage"></p>
694
- </div>
695
- <button class="btn btn-secondary" onclick="hideError()">
696
- <span>✕</span> Dismiss
697
- </button>
698
- </div>
699
-
700
- <div class="footer">
701
- Built with ❤️ for StableCog users |
702
- <a href="https://stablecog.com/docs/api" target="_blank" style="color: var(--light-color); text-decoration: none;">
703
- API Documentation
704
- </a> |
705
- <a href="https://github.com/stability-ai/stablecog/issues" target="_blank" style="color: var(--light-color); text-decoration: none;">
706
- Report Issues
707
- </a>
708
- </div>
709
- </div>
710
-
711
- <script>
712
- // Configuration
713
- const API_HOST = 'https://api.stablecog.com';
714
- const CREDITS_ENDPOINT = '/v1/credits';
715
- const MODELS_ENDPOINT = '/v1/image/generation/models';
716
-
717
- // State
718
- let apiKey = '';
719
- let creditsData = null;
720
- let modelsData = null;
721
-
722
- // Initialize on page load
723
- document.addEventListener('DOMContentLoaded', function() {
724
- loadApiKey();
725
- // Check if API key exists, if so auto-check credits
726
- if (apiKey) {
727
- setTimeout(() => {
728
- checkCredits();
729
- loadModels();
730
- }, 500);
731
- }
732
- });
733
-
734
- // API Key Management
735
- function loadApiKey() {
736
- apiKey = localStorage.getItem('stablecog_api_key') || '';
737
- document.getElementById('apiKeyInput').value = apiKey;
738
- }
739
-
740
- function saveApiKey() {
741
- const input = document.getElementById('apiKeyInput');
742
- apiKey = input.value.trim();
743
- if (apiKey) {
744
- localStorage.setItem('stablecog_api_key', apiKey);
745
- showMessage('API key saved successfully!');
746
- // Auto-check after saving
747
- setTimeout(() => {
748
- checkCredits();
749
- loadModels();
750
- }, 500);
751
- } else {
752
- localStorage.removeItem('stablecog_api_key');
753
- showMessage('API key cleared. Using demo mode.');
754
- }
755
- }
756
-
757
- function clearApiKey() {
758
- document.getElementById('apiKeyInput').value = '';
759
- saveApiKey();
760
- }
761
-
762
- // Tab Switching
763
- function switchTab(tabName) {
764
- // Update tabs
765
- document.querySelectorAll('.tab').forEach(tab => {
766
- tab.classList.remove('active');
767
- });
768
- document.querySelectorAll('.tab-content').forEach(content => {
769
- content.classList.remove('active');
770
- });
771
-
772
- // Activate selected tab
773
- document.querySelector(`.tab[onclick="switchTab('${tabName}')"]`).classList.add('active');
774
- document.getElementById(`${tabName}Tab`).classList.add('active');
775
- }
776
-
777
- // API Calls
778
- async function checkCredits() {
779
- const btn = document.getElementById('checkCreditsBtn');
780
- const icon = document.getElementById('creditsIcon');
781
- const text = document.getElementById('creditsText');
782
-
783
- // Show loading state
784
- btn.disabled = true;
785
- icon.textContent = '';
786
- icon.innerHTML = '<div class="loading"></div>';
787
- text.textContent = 'Checking...';
788
-
789
- try {
790
- const response = await fetch(`${API_HOST}${CREDITS_ENDPOINT}`, {
791
- headers: {
792
- 'Authorization': `Bearer ${apiKey || 'demo'}`,
793
- 'Content-Type': 'application/json'
794
- }
795
- });
796
-
797
- if (!response.ok) {
798
- throw new Error(`API Error: ${response.status} - ${response.statusText}`);
799
- }
800
-
801
- const data = await response.json();
802
- creditsData = data;
803
- updateCreditsDisplay(data);
804
- hideError();
805
- } catch (error) {
806
- showError('Failed to check credits', error.message);
807
- // For demo purposes, show sample data
808
- if (!apiKey) {
809
- showSampleCredits();
810
- }
811
- } finally {
812
- // Reset button state
813
- btn.disabled = false;
814
- icon.textContent = '🔄';
815
- text.textContent = 'Check Credits';
816
- }
817
- }
818
-
819
- async function loadModels() {
820
- const btn = document.getElementById('loadModelsBtn');
821
- const icon = document.getElementById('modelsIcon');
822
- const text = document.getElementById('modelsText');
823
-
824
- // Show loading state
825
- btn.disabled = true;
826
- icon.textContent = '';
827
- icon.innerHTML = '<div class="loading"></div>';
828
- text.textContent = 'Loading...';
829
-
830
- try {
831
- const response = await fetch(`${API_HOST}${MODELS_ENDPOINT}`, {
832
- headers: {
833
- 'Authorization': `Bearer ${apiKey || 'demo'}`,
834
- 'Content-Type': 'application/json'
835
- }
836
- });
837
-
838
- if (!response.ok) {
839
- throw new Error(`API Error: ${response.status} - ${response.statusText}`);
840
- }
841
-
842
- const data = await response.json();
843
- modelsData = data;
844
- updateModelsDisplay(data);
845
- hideError();
846
- } catch (error) {
847
- showError('Failed to load models', error.message);
848
- // For demo purposes, show sample data
849
- if (!apiKey) {
850
- showSampleModels();
851
- }
852
- } finally {
853
- // Reset button state
854
- btn.disabled = false;
855
- icon.textContent = '🔄';
856
- text.textContent = 'Load Models';
857
- }
858
- }
859
-
860
- // Display Functions
861
- function updateCreditsDisplay(data) {
862
- // Calculate totals
863
- const totalRemaining = data.total_remaining_credits || 0;
864
- const creditsList = data.credits || [];
865
-
866
- let totalInitial = 0;
867
- let totalUsed = 0;
868
-
869
- creditsList.forEach(credit => {
870
- const initial = credit.type?.amount || 0;
871
- const remaining = credit.remaining_amount || 0;
872
- totalInitial += initial;
873
- totalUsed += (initial - remaining);
874
- });
875
-
876
- const totalCredits = Math.max(totalInitial, totalRemaining + totalUsed);
877
- const usagePercentage = totalCredits > 0 ? (totalUsed / totalCredits * 100) : 0;
878
-
879
- // Update UI
880
- document.getElementById('totalCredits').textContent = totalCredits;
881
- document.getElementById('remainingCredits').textContent = totalRemaining;
882
- document.getElementById('usedCredits').textContent = totalUsed;
883
- document.getElementById('usagePercentage').textContent = `${usagePercentage.toFixed(1)}%`;
884
- document.getElementById('usageText').textContent = `${usagePercentage.toFixed(1)}%`;
885
- document.getElementById('progressText').textContent = `${usagePercentage.toFixed(1)}%`;
886
-
887
- // Update progress bar
888
- const progressFill = document.getElementById('progressFill');
889
- progressFill.style.width = `${Math.min(usagePercentage, 100)}%`;
890
-
891
- // Set progress bar color based on usage
892
- if (usagePercentage < 50) {
893
- progressFill.style.background = '#4CAF50';
894
- } else if (usagePercentage < 80) {
895
- progressFill.style.background = '#FF9800';
896
- } else {
897
- progressFill.style.background = '#F44336';
898
- }
899
-
900
- // Update status
901
- const statusBadge = document.getElementById('statusBadge');
902
- const statusEmoji = document.getElementById('statusEmoji');
903
- const statusText = document.getElementById('statusText');
904
-
905
- if (totalRemaining === 0) {
906
- statusEmoji.textContent = '💸';
907
- statusText.textContent = 'No Credits';
908
- statusBadge.style.color = '#F44336';
909
- } else if (usagePercentage >= 90) {
910
- statusEmoji.textContent = '🔴';
911
- statusText.textContent = 'Critically Low';
912
- statusBadge.style.color = '#F44336';
913
- } else if (usagePercentage >= 75) {
914
- statusEmoji.textContent = '🟡';
915
- statusText.textContent = 'Running Low';
916
- statusBadge.style.color = '#FF9800';
917
- } else if (totalRemaining < 10) {
918
- statusEmoji.textContent = '📝';
919
- statusText.textContent = 'Limited';
920
- statusBadge.style.color = '#FF9800';
921
- } else if (totalRemaining < 50) {
922
- statusEmoji.textContent = '✨';
923
- statusText.textContent = 'Available';
924
- statusBadge.style.color = '#4CAF50';
925
- } else {
926
- statusEmoji.textContent = '🚀';
927
- statusText.textContent = 'Plenty';
928
- statusBadge.style.color = '#4CAF50';
929
- }
930
-
931
- // Update recommendation
932
- updateRecommendation(totalRemaining, usagePercentage);
933
-
934
- // Update credit breakdown
935
- updateCreditBreakdown(creditsList);
936
-
937
- // Update timestamp
938
- const now = new Date();
939
- document.getElementById('creditsLastUpdated').textContent =
940
- `⏰ Last checked: ${now.toLocaleString()}`;
941
- }
942
-
943
- function updateModelsDisplay(data) {
944
- const models = data.models || [];
945
-
946
- // Update stats
947
- document.getElementById('totalModels').textContent = models.length;
948
- document.getElementById('publicModels').textContent =
949
- models.filter(m => m.is_public).length;
950
- document.getElementById('defaultModels').textContent =
951
- models.filter(m => m.is_default).length;
952
- document.getElementById('communityModels').textContent =
953
- models.filter(m => m.is_community).length;
954
-
955
- // Update models list
956
- const modelsList = document.getElementById('modelsList');
957
- modelsList.innerHTML = '';
958
-
959
- models.sort((a, b) => {
960
- // Sort: default first, then public, then by name
961
- if (a.is_default !== b.is_default) return b.is_default - a.is_default;
962
- if (a.is_public !== b.is_public) return b.is_public - a.is_public;
963
- return a.name.localeCompare(b.name);
964
- });
965
-
966
- models.forEach((model, index) => {
967
- const modelCard = document.createElement('div');
968
- modelCard.className = 'model-card';
969
-
970
- // Determine badge
971
- let badgeClass = 'badge-other';
972
- let badgeText = model.type?.toUpperCase() || 'UNKNOWN';
973
-
974
- if (model.is_community) {
975
- badgeClass = 'badge-community';
976
- badgeText = 'COMMUNITY';
977
- } else if (model.is_default) {
978
- badgeClass = 'badge-default';
979
- badgeText = 'DEFAULT';
980
- } else if (model.is_public) {
981
- badgeClass = 'badge-public';
982
- badgeText = 'PUBLIC';
983
- }
984
-
985
- // Shorten ID for display
986
- const shortId = model.id.length > 8 ? model.id.substring(0, 8) + '...' : model.id;
987
-
988
- modelCard.innerHTML = `
989
- <div class="model-header">
990
- <div class="model-name">
991
- #${index + 1}. ${model.name}
992
- <span class="model-badge ${badgeClass}">${badgeText}</span>
993
- </div>
994
- <div class="model-id">ID: ${shortId}</div>
995
- </div>
996
- <div class="model-description">${model.description || 'No description available'}</div>
997
- <div class="model-meta">
998
- <span>📅 Created: ${model.created_at ? model.created_at.substring(0, 10) : 'Unknown'}</span>
999
- <span>🔄 Updated: ${model.updated_at ? model.updated_at.substring(0, 10) : 'Unknown'}</span>
1000
- </div>
1001
- `;
1002
-
1003
- modelsList.appendChild(modelCard);
1004
- });
1005
-
1006
- // Update timestamp
1007
- const now = new Date();
1008
- document.getElementById('modelsLastUpdated').textContent =
1009
- `⏰ Last updated: ${now.toLocaleString()}`;
1010
- }
1011
-
1012
- function updateRecommendation(remainingCredits, usagePercentage) {
1013
- const recommendation = document.getElementById('recommendation');
1014
-
1015
- if (remainingCredits === 0) {
1016
- recommendation.innerHTML = "💸 <strong>No credits remaining.</strong> Please add credits to continue using StableCog services.";
1017
- } else if (usagePercentage >= 90) {
1018
- recommendation.innerHTML = "🛑 <strong>Critically low credits!</strong> Consider purchasing more credits before starting new projects.";
1019
- } else if (usagePercentage >= 75) {
1020
- recommendation.innerHTML = "⚠️ <strong>Credits are running low.</strong> You can still do some work, but plan ahead for larger projects.";
1021
- } else if (remainingCredits < 10) {
1022
- recommendation.innerHTML = "📝 <strong>Limited credits available.</strong> Good for small tasks, testing, or single images.";
1023
- } else if (remainingCredits < 50) {
1024
- recommendation.innerHTML = "✨ <strong>Credits available!</strong> Suitable for several medium-sized projects or batch processing.";
1025
- } else {
1026
- recommendation.innerHTML = "🚀 <strong>Plenty of credits!</strong> Ready for extensive image generation work and experimentation.";
1027
- }
1028
- }
1029
-
1030
- function updateCreditBreakdown(creditsList) {
1031
- const breakdownContainer = document.getElementById('creditBreakdown');
1032
- const itemsContainer = breakdownContainer.querySelector('.credit-items') ||
1033
- document.createElement('div');
1034
- itemsContainer.className = 'credit-items';
1035
-
1036
- itemsContainer.innerHTML = '';
1037
-
1038
- creditsList.forEach(credit => {
1039
- const initial = credit.type?.amount || 0;
1040
- const remaining = credit.remaining_amount || 0;
1041
- const used = initial - remaining;
1042
- const percentage = initial > 0 ? (used / initial * 100) : 0;
1043
-
1044
- const item = document.createElement('div');
1045
- item.className = 'credit-item';
1046
-
1047
- item.innerHTML = `
1048
- <div class="credit-header">
1049
- <span class="credit-name">${credit.type?.name || 'Unknown'}</span>
1050
- <span class="credit-amount">${remaining} / ${initial} ⭐</span>
1051
- </div>
1052
- <div class="credit-description">${credit.type?.description || ''}</div>
1053
- <div class="credit-progress">
1054
- <div class="credit-progress-fill" style="width: ${Math.min(percentage, 100)}%; background: ${getProgressColor(percentage)};"></div>
1055
- </div>
1056
- `;
1057
-
1058
- itemsContainer.appendChild(item);
1059
- });
1060
-
1061
- if (!breakdownContainer.querySelector('.credit-items')) {
1062
- breakdownContainer.appendChild(itemsContainer);
1063
- }
1064
- }
1065
-
1066
- function getProgressColor(percentage) {
1067
- if (percentage < 50) return '#4CAF50';
1068
- if (percentage < 80) return '#FF9800';
1069
- return '#F44336';
1070
- }
1071
-
1072
- // Raw Response Viewer
1073
- function viewRawResponse(type) {
1074
- const modal = document.getElementById(`${type}RawModal`);
1075
- const pre = document.getElementById(`${type}RawResponse`);
1076
-
1077
- const data = type === 'credits' ? creditsData : modelsData;
1078
- pre.textContent = JSON.stringify(data, null, 2);
1079
- modal.style.display = 'block';
1080
- }
1081
-
1082
- function hideRawResponse(type) {
1083
- document.getElementById(`${type}RawModal`).style.display = 'none';
1084
- }
1085
-
1086
- // Error Handling
1087
- function showError(title, message) {
1088
- document.getElementById('errorTitle').textContent = title;
1089
- document.getElementById('errorMessage').textContent = message;
1090
- document.getElementById('errorContainer').style.display = 'block';
1091
- }
1092
-
1093
- function hideError() {
1094
- document.getElementById('errorContainer').style.display = 'none';
1095
- }
1096
-
1097
- function showMessage(message) {
1098
- // Simple toast notification
1099
- const toast = document.createElement('div');
1100
- toast.style.cssText = `
1101
- position: fixed;
1102
- top: 20px;
1103
- right: 20px;
1104
- background: rgba(76, 175, 80, 0.9);
1105
- color: white;
1106
- padding: 12px 20px;
1107
- border-radius: 8px;
1108
- box-shadow: 0 5px 15px rgba(0,0,0,0.3);
1109
- z-index: 1000;
1110
- animation: slideIn 0.3s ease;
1111
- `;
1112
- toast.textContent = message;
1113
- document.body.appendChild(toast);
1114
-
1115
- setTimeout(() => {
1116
- toast.style.animation = 'slideOut 0.3s ease';
1117
- setTimeout(() => toast.remove(), 300);
1118
- }, 3000);
1119
- }
1120
-
1121
- // Sample data for demo
1122
- function showSampleCredits() {
1123
- const sampleData = {
1124
- "total_remaining_credits": 5,
1125
- "credits": [
1126
- {
1127
- "id": "cd813ce3-b5fe-4a74-ae5c-e077ce49b984",
1128
- "remaining_amount": 0,
1129
- "expires_at": "2100-01-01T05:00:00Z",
1130
- "type": {
1131
- "id": "7ca94fd6-c201-4ca6-a9bf-4473c83e30b4",
1132
- "name": "Refund",
1133
- "amount": 0,
1134
- "description": "For generate/upscale failure refunds"
1135
- }
1136
- },
1137
- {
1138
- "id": "b8a508a2-a5dc-4329-ad25-015b50fbc51f",
1139
- "remaining_amount": 5,
1140
- "expires_at": "2100-01-01T05:00:00Z",
1141
- "type": {
1142
- "id": "3b12b23e-478b-4c18-8e34-70b3f0af1ee6",
1143
- "name": "Free",
1144
- "amount": 50,
1145
- "description": "Base free credits for user"
1146
- }
1147
- }
1148
- ]
1149
- };
1150
- creditsData = sampleData;
1151
- updateCreditsDisplay(sampleData);
1152
- }
1153
-
1154
- function showSampleModels() {
1155
- const sampleData = {
1156
- "models": [
1157
- {
1158
- "id": "sd-xl-1.0",
1159
- "name": "Stable Diffusion XL",
1160
- "description": "High-quality image generation model with excellent detail",
1161
- "type": "image",
1162
- "is_public": true,
1163
- "is_default": true,
1164
- "is_community": false,
1165
- "created_at": "2024-01-01T00:00:00Z",
1166
- "updated_at": "2024-06-01T00:00:00Z"
1167
- },
1168
- {
1169
- "id": "dreamshaper-v7",
1170
- "name": "DreamShaper v7",
1171
- "description": "Artistic style model for creative image generation",
1172
- "type": "image",
1173
- "is_public": true,
1174
- "is_default": false,
1175
- "is_community": false,
1176
- "created_at": "2024-02-01T00:00:00Z",
1177
- "updated_at": "2024-05-01T00:00:00Z"
1178
- },
1179
- {
1180
- "id": "anime-diffusion",
1181
- "name": "Anime Diffusion",
1182
- "description": "Specialized model for anime-style artwork",
1183
- "type": "image",
1184
- "is_public": true,
1185
- "is_default": false,
1186
- "is_community": true,
1187
- "created_at": "2024-03-01T00:00:00Z",
1188
- "updated_at": "2024-04-01T00:00:00Z"
1189
- }
1190
- ]
1191
- };
1192
- modelsData = sampleData;
1193
- updateModelsDisplay(sampleData);
1194
- }
1195
-
1196
- // Add CSS animations
1197
- const style = document.createElement('style');
1198
- style.textContent = `
1199
- @keyframes slideIn {
1200
- from { transform: translateX(100%); opacity: 0; }
1201
- to { transform: translateX(0); opacity: 1; }
1202
- }
1203
- @keyframes slideOut {
1204
- from { transform: translateX(0); opacity: 1; }
1205
- to { transform: translateX(100%); opacity: 0; }
1206
- }
1207
- `;
1208
- document.head.appendChild(style);
1209
- </script>
1210
- </body>
1211
- </html>