Really-amin commited on
Commit
0cefe26
·
verified ·
1 Parent(s): a8b6f1e

Upload 46 files

Browse files
Files changed (2) hide show
  1. static/css/main.css +579 -0
  2. static/js/app.js +172 -38
static/css/main.css CHANGED
@@ -63,6 +63,7 @@ body {
63
  line-height: 1.6;
64
  min-height: 100vh;
65
  overflow-x: hidden;
 
66
  }
67
 
68
  /* Animated background particles */
@@ -284,6 +285,11 @@ body::before {
284
  font-size: 18px;
285
  font-weight: 700;
286
  color: var(--text-primary);
 
 
 
 
 
287
  }
288
 
289
  /* Sidebar Overlay for Mobile */
@@ -564,6 +570,11 @@ body::before {
564
  font-weight: 800;
565
  color: var(--primary);
566
  margin-bottom: 5px;
 
 
 
 
 
567
  }
568
 
569
  .stat-label {
@@ -870,6 +881,540 @@ table tr:hover {
870
  }
871
  }
872
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  /* =============================================================================
874
  Light Theme
875
  ============================================================================= */
@@ -1240,6 +1785,40 @@ body.light-theme ::-webkit-scrollbar-track {
1240
  vertical-align: middle;
1241
  }
1242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1243
  /* Responsive adjustments for news cards */
1244
  @media (max-width: 768px) {
1245
  .news-card {
 
63
  line-height: 1.6;
64
  min-height: 100vh;
65
  overflow-x: hidden;
66
+ font-size: 15px;
67
  }
68
 
69
  /* Animated background particles */
 
285
  font-size: 18px;
286
  font-weight: 700;
287
  color: var(--text-primary);
288
+ display: block;
289
+ visibility: visible;
290
+ opacity: 1;
291
+ min-height: 1.2em;
292
+ line-height: 1.2;
293
  }
294
 
295
  /* Sidebar Overlay for Mobile */
 
570
  font-weight: 800;
571
  color: var(--primary);
572
  margin-bottom: 5px;
573
+ display: block;
574
+ visibility: visible;
575
+ opacity: 1;
576
+ min-height: 1.2em;
577
+ line-height: 1.2;
578
  }
579
 
580
  .stat-label {
 
881
  }
882
  }
883
 
884
+ /* =============================================================================
885
+ Large Screen Optimizations (1440px+)
886
+ ============================================================================= */
887
+
888
+ @media (min-width: 1440px) {
889
+ :root {
890
+ --sidebar-width: 320px;
891
+ }
892
+
893
+ body {
894
+ font-size: 16px;
895
+ }
896
+
897
+ .sidebar-header {
898
+ padding: 30px 25px;
899
+ }
900
+
901
+ .sidebar-logo .logo-icon {
902
+ width: 50px;
903
+ height: 50px;
904
+ font-size: 24px;
905
+ }
906
+
907
+ .sidebar-logo .logo-text h1 {
908
+ font-size: 22px;
909
+ }
910
+
911
+ .sidebar-logo .logo-text p {
912
+ font-size: 12px;
913
+ }
914
+
915
+ .nav-item {
916
+ padding: 16px 20px;
917
+ font-size: 16px;
918
+ gap: 14px;
919
+ }
920
+
921
+ .nav-item svg {
922
+ width: 22px;
923
+ height: 22px;
924
+ }
925
+
926
+ .top-header {
927
+ padding: 25px 40px;
928
+ }
929
+
930
+ .header-title h2 {
931
+ font-size: 28px;
932
+ }
933
+
934
+ .header-title p {
935
+ font-size: 14px;
936
+ }
937
+
938
+ .icon-btn {
939
+ width: 44px;
940
+ height: 44px;
941
+ font-size: 18px;
942
+ }
943
+
944
+ .content-area {
945
+ padding: 40px;
946
+ }
947
+
948
+ .glass-card {
949
+ padding: 30px;
950
+ border-radius: 18px;
951
+ margin-bottom: 25px;
952
+ }
953
+
954
+ .glass-card h3 {
955
+ font-size: 24px;
956
+ margin-bottom: 25px;
957
+ padding-bottom: 12px;
958
+ }
959
+
960
+ .stats-grid {
961
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
962
+ gap: 25px;
963
+ }
964
+
965
+ .stat-card {
966
+ padding: 30px;
967
+ border-radius: 18px;
968
+ }
969
+
970
+ .stat-icon {
971
+ width: 80px;
972
+ height: 80px;
973
+ font-size: 36px;
974
+ }
975
+
976
+ .stat-value {
977
+ font-size: 42px;
978
+ }
979
+
980
+ .stat-label {
981
+ font-size: 15px;
982
+ }
983
+
984
+ .stat-trend {
985
+ font-size: 13px;
986
+ }
987
+
988
+ .grid-2 {
989
+ grid-template-columns: repeat(auto-fit, minmax(450px, 1fr));
990
+ gap: 25px;
991
+ }
992
+
993
+ .form-group {
994
+ margin-bottom: 25px;
995
+ }
996
+
997
+ .form-group label {
998
+ font-size: 15px;
999
+ margin-bottom: 10px;
1000
+ }
1001
+
1002
+ .form-group input,
1003
+ .form-group textarea,
1004
+ .form-group select {
1005
+ padding: 14px;
1006
+ font-size: 15px;
1007
+ border-radius: 12px;
1008
+ }
1009
+
1010
+ .btn-primary, .btn-refresh {
1011
+ padding: 14px 28px;
1012
+ font-size: 15px;
1013
+ border-radius: 12px;
1014
+ }
1015
+
1016
+ .news-card {
1017
+ padding: 25px;
1018
+ border-radius: 18px;
1019
+ gap: 25px;
1020
+ }
1021
+
1022
+ .news-card-image {
1023
+ width: 140px;
1024
+ height: 140px;
1025
+ font-size: 36px;
1026
+ }
1027
+
1028
+ .news-card-title {
1029
+ font-size: 20px;
1030
+ }
1031
+
1032
+ .news-card-excerpt {
1033
+ font-size: 15px;
1034
+ }
1035
+
1036
+ .sentiment-gauge-container {
1037
+ width: 350px;
1038
+ height: 175px;
1039
+ }
1040
+
1041
+ .ai-result-card {
1042
+ padding: 30px;
1043
+ }
1044
+
1045
+ .ai-result-header h4 {
1046
+ font-size: 24px;
1047
+ }
1048
+
1049
+ .ai-result-metric-value {
1050
+ font-size: 42px;
1051
+ }
1052
+ }
1053
+
1054
+ /* =============================================================================
1055
+ Extra Large Screen Optimizations (1920px+)
1056
+ ============================================================================= */
1057
+
1058
+ @media (min-width: 1920px) {
1059
+ :root {
1060
+ --sidebar-width: 360px;
1061
+ }
1062
+
1063
+ body {
1064
+ font-size: 17px;
1065
+ }
1066
+
1067
+ .sidebar-header {
1068
+ padding: 35px 30px;
1069
+ }
1070
+
1071
+ .sidebar-logo .logo-icon {
1072
+ width: 55px;
1073
+ height: 55px;
1074
+ font-size: 26px;
1075
+ }
1076
+
1077
+ .sidebar-logo .logo-text h1 {
1078
+ font-size: 24px;
1079
+ }
1080
+
1081
+ .sidebar-logo .logo-text p {
1082
+ font-size: 13px;
1083
+ }
1084
+
1085
+ .nav-item {
1086
+ padding: 18px 24px;
1087
+ font-size: 17px;
1088
+ gap: 16px;
1089
+ margin-bottom: 10px;
1090
+ }
1091
+
1092
+ .nav-item svg {
1093
+ width: 24px;
1094
+ height: 24px;
1095
+ }
1096
+
1097
+ .top-header {
1098
+ padding: 30px 50px;
1099
+ }
1100
+
1101
+ .header-title h2 {
1102
+ font-size: 32px;
1103
+ }
1104
+
1105
+ .header-title p {
1106
+ font-size: 15px;
1107
+ }
1108
+
1109
+ .icon-btn {
1110
+ width: 48px;
1111
+ height: 48px;
1112
+ font-size: 20px;
1113
+ }
1114
+
1115
+ .content-area {
1116
+ padding: 50px;
1117
+ max-width: 1920px;
1118
+ margin: 0 auto;
1119
+ }
1120
+
1121
+ .glass-card {
1122
+ padding: 35px;
1123
+ border-radius: 20px;
1124
+ margin-bottom: 30px;
1125
+ }
1126
+
1127
+ .glass-card h3 {
1128
+ font-size: 26px;
1129
+ margin-bottom: 30px;
1130
+ padding-bottom: 15px;
1131
+ }
1132
+
1133
+ .stats-grid {
1134
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
1135
+ gap: 30px;
1136
+ margin-bottom: 40px;
1137
+ }
1138
+
1139
+ .stat-card {
1140
+ padding: 35px;
1141
+ border-radius: 20px;
1142
+ gap: 25px;
1143
+ }
1144
+
1145
+ .stat-icon {
1146
+ width: 90px;
1147
+ height: 90px;
1148
+ font-size: 40px;
1149
+ }
1150
+
1151
+ .stat-value {
1152
+ font-size: 48px;
1153
+ }
1154
+
1155
+ .stat-label {
1156
+ font-size: 16px;
1157
+ }
1158
+
1159
+ .stat-trend {
1160
+ font-size: 14px;
1161
+ }
1162
+
1163
+ .grid-2 {
1164
+ grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
1165
+ gap: 30px;
1166
+ }
1167
+
1168
+ .form-group {
1169
+ margin-bottom: 30px;
1170
+ }
1171
+
1172
+ .form-group label {
1173
+ font-size: 16px;
1174
+ margin-bottom: 12px;
1175
+ }
1176
+
1177
+ .form-group input,
1178
+ .form-group textarea,
1179
+ .form-group select {
1180
+ padding: 16px;
1181
+ font-size: 16px;
1182
+ border-radius: 14px;
1183
+ }
1184
+
1185
+ .btn-primary, .btn-refresh {
1186
+ padding: 16px 32px;
1187
+ font-size: 16px;
1188
+ border-radius: 14px;
1189
+ }
1190
+
1191
+ .news-card {
1192
+ padding: 30px;
1193
+ border-radius: 20px;
1194
+ gap: 30px;
1195
+ margin-bottom: 25px;
1196
+ }
1197
+
1198
+ .news-card-image {
1199
+ width: 160px;
1200
+ height: 160px;
1201
+ font-size: 40px;
1202
+ }
1203
+
1204
+ .news-card-title {
1205
+ font-size: 22px;
1206
+ }
1207
+
1208
+ .news-card-excerpt {
1209
+ font-size: 16px;
1210
+ }
1211
+
1212
+ .sentiment-gauge-container {
1213
+ width: 400px;
1214
+ height: 200px;
1215
+ }
1216
+
1217
+ .ai-result-card {
1218
+ padding: 35px;
1219
+ }
1220
+
1221
+ .ai-result-header h4 {
1222
+ font-size: 26px;
1223
+ }
1224
+
1225
+ .ai-result-metric-value {
1226
+ font-size: 48px;
1227
+ }
1228
+
1229
+ table th,
1230
+ table td {
1231
+ padding: 16px;
1232
+ font-size: 16px;
1233
+ }
1234
+ }
1235
+
1236
+ /* =============================================================================
1237
+ Ultra-Wide Screen Optimizations (2560px+)
1238
+ ============================================================================= */
1239
+
1240
+ @media (min-width: 2560px) {
1241
+ :root {
1242
+ --sidebar-width: 400px;
1243
+ }
1244
+
1245
+ body {
1246
+ font-size: 18px;
1247
+ }
1248
+
1249
+ .sidebar-header {
1250
+ padding: 40px 35px;
1251
+ }
1252
+
1253
+ .sidebar-logo .logo-icon {
1254
+ width: 60px;
1255
+ height: 60px;
1256
+ font-size: 28px;
1257
+ }
1258
+
1259
+ .sidebar-logo .logo-text h1 {
1260
+ font-size: 26px;
1261
+ }
1262
+
1263
+ .sidebar-logo .logo-text p {
1264
+ font-size: 14px;
1265
+ }
1266
+
1267
+ .nav-item {
1268
+ padding: 20px 28px;
1269
+ font-size: 18px;
1270
+ gap: 18px;
1271
+ margin-bottom: 12px;
1272
+ border-radius: 14px;
1273
+ }
1274
+
1275
+ .nav-item svg {
1276
+ width: 26px;
1277
+ height: 26px;
1278
+ }
1279
+
1280
+ .top-header {
1281
+ padding: 35px 60px;
1282
+ }
1283
+
1284
+ .header-title h2 {
1285
+ font-size: 36px;
1286
+ }
1287
+
1288
+ .header-title p {
1289
+ font-size: 16px;
1290
+ }
1291
+
1292
+ .icon-btn {
1293
+ width: 52px;
1294
+ height: 52px;
1295
+ font-size: 22px;
1296
+ }
1297
+
1298
+ .content-area {
1299
+ padding: 60px;
1300
+ max-width: 2400px;
1301
+ }
1302
+
1303
+ .glass-card {
1304
+ padding: 40px;
1305
+ border-radius: 24px;
1306
+ margin-bottom: 35px;
1307
+ }
1308
+
1309
+ .glass-card h3 {
1310
+ font-size: 28px;
1311
+ margin-bottom: 35px;
1312
+ padding-bottom: 18px;
1313
+ }
1314
+
1315
+ .stats-grid {
1316
+ grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
1317
+ gap: 35px;
1318
+ margin-bottom: 50px;
1319
+ }
1320
+
1321
+ .stat-card {
1322
+ padding: 40px;
1323
+ border-radius: 24px;
1324
+ gap: 30px;
1325
+ }
1326
+
1327
+ .stat-icon {
1328
+ width: 100px;
1329
+ height: 100px;
1330
+ font-size: 44px;
1331
+ }
1332
+
1333
+ .stat-value {
1334
+ font-size: 54px;
1335
+ }
1336
+
1337
+ .stat-label {
1338
+ font-size: 17px;
1339
+ }
1340
+
1341
+ .stat-trend {
1342
+ font-size: 15px;
1343
+ }
1344
+
1345
+ .grid-2 {
1346
+ grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
1347
+ gap: 35px;
1348
+ }
1349
+
1350
+ .form-group {
1351
+ margin-bottom: 35px;
1352
+ }
1353
+
1354
+ .form-group label {
1355
+ font-size: 17px;
1356
+ margin-bottom: 14px;
1357
+ }
1358
+
1359
+ .form-group input,
1360
+ .form-group textarea,
1361
+ .form-group select {
1362
+ padding: 18px;
1363
+ font-size: 17px;
1364
+ border-radius: 16px;
1365
+ }
1366
+
1367
+ .btn-primary, .btn-refresh {
1368
+ padding: 18px 36px;
1369
+ font-size: 17px;
1370
+ border-radius: 16px;
1371
+ }
1372
+
1373
+ .news-card {
1374
+ padding: 35px;
1375
+ border-radius: 24px;
1376
+ gap: 35px;
1377
+ margin-bottom: 30px;
1378
+ }
1379
+
1380
+ .news-card-image {
1381
+ width: 180px;
1382
+ height: 180px;
1383
+ font-size: 44px;
1384
+ }
1385
+
1386
+ .news-card-title {
1387
+ font-size: 24px;
1388
+ }
1389
+
1390
+ .news-card-excerpt {
1391
+ font-size: 17px;
1392
+ }
1393
+
1394
+ .sentiment-gauge-container {
1395
+ width: 450px;
1396
+ height: 225px;
1397
+ }
1398
+
1399
+ .ai-result-card {
1400
+ padding: 40px;
1401
+ }
1402
+
1403
+ .ai-result-header h4 {
1404
+ font-size: 28px;
1405
+ }
1406
+
1407
+ .ai-result-metric-value {
1408
+ font-size: 54px;
1409
+ }
1410
+
1411
+ table th,
1412
+ table td {
1413
+ padding: 18px;
1414
+ font-size: 17px;
1415
+ }
1416
+ }
1417
+
1418
  /* =============================================================================
1419
  Light Theme
1420
  ============================================================================= */
 
1785
  vertical-align: middle;
1786
  }
1787
 
1788
+ /* Model Status Styles */
1789
+ .model-status {
1790
+ display: inline-block;
1791
+ padding: 4px 10px;
1792
+ border-radius: 6px;
1793
+ font-size: 12px;
1794
+ font-weight: 600;
1795
+ text-transform: uppercase;
1796
+ letter-spacing: 0.5px;
1797
+ }
1798
+
1799
+ .model-status.available {
1800
+ background: rgba(16, 185, 129, 0.2);
1801
+ border: 1px solid rgba(16, 185, 129, 0.4);
1802
+ color: var(--success);
1803
+ }
1804
+
1805
+ .model-status.unavailable {
1806
+ background: rgba(239, 68, 68, 0.2);
1807
+ border: 1px solid rgba(239, 68, 68, 0.4);
1808
+ color: var(--danger);
1809
+ }
1810
+
1811
+ /* Ensure stat values are always visible */
1812
+ .stat-value:empty::before {
1813
+ content: '0';
1814
+ opacity: 0.5;
1815
+ }
1816
+
1817
+ .stat-mini span:empty::before {
1818
+ content: '-';
1819
+ opacity: 0.5;
1820
+ }
1821
+
1822
  /* Responsive adjustments for news cards */
1823
  @media (max-width: 768px) {
1824
  .news-card {
static/js/app.js CHANGED
@@ -1,6 +1,29 @@
1
  // Crypto Intelligence Hub - Main JavaScript with Sidebar Navigation
2
  // Enhanced Pro Trading Terminal UI
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  // =============================================================================
5
  // Toast Notification System
6
  // =============================================================================
@@ -325,36 +348,97 @@ async function loadDashboard() {
325
  }
326
 
327
  try {
328
- // Load resources
329
- const resourcesRes = await fetch('/api/resources');
330
  if (!resourcesRes.ok) {
331
  throw new Error(`Resources API returned ${resourcesRes.status}`);
332
  }
333
- const resourcesData = await resourcesRes.json();
334
 
335
  console.log('Resources data:', resourcesData);
336
 
337
- if (resourcesData.success && resourcesData.summary) {
338
- const totalResources = resourcesData.summary.total_resources || 0;
339
- const freeResources = resourcesData.summary.free_resources || 0;
340
- const modelsAvailable = resourcesData.summary.models_available || 0;
341
-
342
- document.getElementById('stat-total-resources').textContent = totalResources;
343
- document.getElementById('stat-free-resources').textContent = freeResources;
344
- document.getElementById('stat-models').textContent = modelsAvailable;
345
-
346
- // Update sidebar stats
347
- const sidebarResources = document.getElementById('sidebar-resources');
348
- const sidebarModels = document.getElementById('sidebar-models');
349
- if (sidebarResources) sidebarResources.textContent = totalResources;
350
- if (sidebarModels) sidebarModels.textContent = modelsAvailable;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
 
352
- // Load categories chart
353
- if (resourcesData.summary.categories) {
354
- createCategoriesChart(resourcesData.summary.categories);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  }
356
  } else {
357
- console.warn('Resources data format unexpected:', resourcesData);
 
 
 
 
 
358
  document.getElementById('stat-total-resources').textContent = '0';
359
  document.getElementById('stat-free-resources').textContent = '0';
360
  document.getElementById('stat-models').textContent = '0';
@@ -364,10 +448,20 @@ async function loadDashboard() {
364
  try {
365
  const statusRes = await fetch('/api/status');
366
  if (statusRes.ok) {
367
- const statusData = await statusRes.json();
368
-
369
- const providers = statusData.total_apis || statusData.total_providers || 0;
370
- document.getElementById('stat-providers').textContent = providers;
 
 
 
 
 
 
 
 
 
 
371
 
372
  // Display system status
373
  const systemStatusDiv = document.getElementById('system-status');
@@ -791,15 +885,34 @@ async function loadModels() {
791
  if (statusRes.ok) {
792
  const statusData = await statusRes.json();
793
 
 
 
 
 
 
 
 
 
 
794
  modelsStatusDiv.innerHTML = `
795
- <div class="alert ${statusData.ok ? 'alert-success' : 'alert-warning'}">
796
- <strong>Status:</strong> ${statusData.ok ? '✅ Active' : '⚠️ Partial'}<br>
797
- <strong>Pipelines Loaded:</strong> ${statusData.pipelines_loaded || 0}<br>
798
- <strong>Available Models:</strong> ${statusData.available_models?.length || 0}<br>
799
- <strong>HF Mode:</strong> ${statusData.hf_mode || 'unknown'}<br>
800
- <strong>Transformers Available:</strong> ${statusData.transformers_available ? '✅' : '❌'}
801
  </div>
802
  `;
 
 
 
 
 
 
 
 
 
 
803
  } else {
804
  throw new Error('Models status endpoint not available');
805
  }
@@ -809,6 +922,19 @@ async function loadModels() {
809
  if (listRes.ok) {
810
  const listData = await listRes.json();
811
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  if (listData.models && listData.models.length > 0) {
813
  modelsListDiv.innerHTML = `
814
  <div style="overflow-x: auto;">
@@ -822,14 +948,22 @@ async function loadModels() {
822
  </tr>
823
  </thead>
824
  <tbody>
825
- ${listData.models.map(model => `
 
 
 
 
 
 
 
826
  <tr>
827
- <td><strong>${model.model_id || model.key}</strong></td>
828
- <td>${model.task || 'N/A'}</td>
829
- <td>${model.category || 'N/A'}</td>
830
- <td><span class="model-status ${model.loaded ? 'available' : 'unavailable'}">${model.loaded ? '✅ Loaded' : '❌ Not Loaded'}</span></td>
831
- </tr>
832
- `).join('')}
 
833
  </tbody>
834
  </table>
835
  </div>
 
1
  // Crypto Intelligence Hub - Main JavaScript with Sidebar Navigation
2
  // Enhanced Pro Trading Terminal UI
3
 
4
+ // =============================================================================
5
+ // Console Warning Filter (suppress external server warnings)
6
+ // =============================================================================
7
+ (function() {
8
+ const originalWarn = console.warn;
9
+ console.warn = function(...args) {
10
+ const message = args.join(' ');
11
+ // Suppress Permissions-Policy warnings from external servers (e.g., Hugging Face)
12
+ if (message.includes('Unrecognized feature:') &&
13
+ (message.includes('ambient-light-sensor') ||
14
+ message.includes('battery') ||
15
+ message.includes('document-domain') ||
16
+ message.includes('layout-animations') ||
17
+ message.includes('legacy-image-formats') ||
18
+ message.includes('oversized-images') ||
19
+ message.includes('vr') ||
20
+ message.includes('wake-lock'))) {
21
+ return; // Suppress these warnings
22
+ }
23
+ originalWarn.apply(console, args);
24
+ };
25
+ })();
26
+
27
  // =============================================================================
28
  // Toast Notification System
29
  // =============================================================================
 
348
  }
349
 
350
  try {
351
+ // Load resources summary (use the correct endpoint)
352
+ const resourcesRes = await fetch('/api/resources/summary');
353
  if (!resourcesRes.ok) {
354
  throw new Error(`Resources API returned ${resourcesRes.status}`);
355
  }
356
+ let resourcesData = await resourcesRes.json();
357
 
358
  console.log('Resources data:', resourcesData);
359
 
360
+ // Check if data is an array (unexpected format - might be from wrong endpoint)
361
+ if (Array.isArray(resourcesData)) {
362
+ // Try to extract summary from array if it contains objects with summary property
363
+ const summaryObj = resourcesData.find(item => item && typeof item === 'object' && !Array.isArray(item) && item.summary);
364
+ if (summaryObj && summaryObj.summary) {
365
+ resourcesData = summaryObj;
366
+ console.log('Extracted summary from array response');
367
+ } else {
368
+ // Fallback: use array length as total resources estimate
369
+ const totalResources = resourcesData.length;
370
+ console.log(`Using array length (${totalResources}) as resource count estimate`);
371
+ document.getElementById('stat-total-resources').textContent = totalResources;
372
+ document.getElementById('stat-free-resources').textContent = Math.floor(totalResources * 0.8); // Estimate 80% free
373
+ document.getElementById('stat-models').textContent = '0';
374
+
375
+ // Update sidebar stats
376
+ const sidebarResources = document.getElementById('sidebar-resources');
377
+ const sidebarModels = document.getElementById('sidebar-models');
378
+ if (sidebarResources) sidebarResources.textContent = totalResources;
379
+ if (sidebarModels) sidebarModels.textContent = '0';
380
+ return; // Exit early since we can't process array format properly
381
+ }
382
+ }
383
+
384
+ // Check if we have the summary object
385
+ let summary = null;
386
+ if (resourcesData && typeof resourcesData === 'object' && !Array.isArray(resourcesData)) {
387
+ summary = resourcesData.summary || resourcesData;
388
+ }
389
+
390
+ // Validate that summary is an object with expected properties
391
+ if (summary && typeof summary === 'object' && !Array.isArray(summary)) {
392
+ // Check if it has at least one of the expected properties
393
+ const hasExpectedProperties = summary.total_resources !== undefined ||
394
+ summary.free_resources !== undefined ||
395
+ summary.models_available !== undefined ||
396
+ (resourcesData.success !== false && resourcesData.success !== undefined);
397
 
398
+ if (hasExpectedProperties || resourcesData.success === true) {
399
+ const totalResources = summary.total_resources || 0;
400
+ const freeResources = summary.free_resources || 0;
401
+ const modelsAvailable = summary.models_available || 0;
402
+
403
+ // Update metric cards - ensure elements exist before updating
404
+ const totalResourcesEl = document.getElementById('stat-total-resources');
405
+ const freeResourcesEl = document.getElementById('stat-free-resources');
406
+ const modelsEl = document.getElementById('stat-models');
407
+
408
+ if (totalResourcesEl) totalResourcesEl.textContent = totalResources;
409
+ if (freeResourcesEl) freeResourcesEl.textContent = freeResources;
410
+ if (modelsEl) modelsEl.textContent = modelsAvailable;
411
+
412
+ // Update sidebar stats
413
+ const sidebarResources = document.getElementById('sidebar-resources');
414
+ const sidebarModels = document.getElementById('sidebar-models');
415
+ if (sidebarResources) sidebarResources.textContent = totalResources;
416
+ if (sidebarModels) sidebarModels.textContent = modelsAvailable;
417
+
418
+ // Load categories chart - handle both object and simple count format
419
+ if (summary.categories && typeof summary.categories === 'object' && !Array.isArray(summary.categories)) {
420
+ const categories = summary.categories;
421
+ // Convert {category: {count: N}} to {category: N} for chart
422
+ const chartData = {};
423
+ for (const [key, value] of Object.entries(categories)) {
424
+ chartData[key] = typeof value === 'object' && value !== null ? (value.count || value) : value;
425
+ }
426
+ createCategoriesChart(chartData);
427
+ }
428
+ } else {
429
+ // Data structure exists but doesn't have expected properties
430
+ console.warn('Resources data missing expected properties:', resourcesData);
431
+ document.getElementById('stat-total-resources').textContent = '0';
432
+ document.getElementById('stat-free-resources').textContent = '0';
433
+ document.getElementById('stat-models').textContent = '0';
434
  }
435
  } else {
436
+ // Invalid data format - log minimal info to avoid console spam
437
+ if (Array.isArray(resourcesData)) {
438
+ console.log(`Resources API returned array (${resourcesData.length} items) instead of summary object`);
439
+ } else {
440
+ console.log('Resources data format unexpected - not a valid object:', typeof resourcesData);
441
+ }
442
  document.getElementById('stat-total-resources').textContent = '0';
443
  document.getElementById('stat-free-resources').textContent = '0';
444
  document.getElementById('stat-models').textContent = '0';
 
448
  try {
449
  const statusRes = await fetch('/api/status');
450
  if (statusRes.ok) {
451
+ const statusData = await statusRes.json();
452
+
453
+ // Handle different response formats
454
+ let providers = 0;
455
+ if (statusData.providers && typeof statusData.providers === 'object') {
456
+ providers = statusData.providers.total || 0;
457
+ } else {
458
+ providers = statusData.total_apis || statusData.total_providers || statusData.providers || 0;
459
+ }
460
+
461
+ const providersEl = document.getElementById('stat-providers');
462
+ if (providersEl) {
463
+ providersEl.textContent = providers;
464
+ }
465
 
466
  // Display system status
467
  const systemStatusDiv = document.getElementById('system-status');
 
885
  if (statusRes.ok) {
886
  const statusData = await statusRes.json();
887
 
888
+ // Handle different response formats from API
889
+ const isOk = statusData.ok || statusData.success || statusData.status === 'ok';
890
+ const pipelinesLoaded = statusData.pipelines_loaded || statusData.models_loaded || 0;
891
+ const availableModels = statusData.available_models || statusData.loaded_models || [];
892
+ const modelsCount = Array.isArray(availableModels) ? availableModels.length : (availableModels || 0);
893
+ const hfMode = statusData.hf_mode || 'unknown';
894
+ const transformersAvailable = statusData.transformers_available !== undefined ? statusData.transformers_available : false;
895
+ const statusMessage = statusData.status_message || (isOk ? 'Active' : 'Partial');
896
+
897
  modelsStatusDiv.innerHTML = `
898
+ <div class="alert ${isOk ? 'alert-success' : 'alert-warning'}">
899
+ <strong>Status:</strong> ${isOk ? '✅ ' : '⚠️ '}${statusMessage}<br>
900
+ <strong>Pipelines Loaded:</strong> ${pipelinesLoaded}<br>
901
+ <strong>Available Models:</strong> ${modelsCount}<br>
902
+ <strong>HF Mode:</strong> ${hfMode}<br>
903
+ <strong>Transformers Available:</strong> ${transformersAvailable ? '✅' : '❌'}
904
  </div>
905
  `;
906
+
907
+ // Update stat-models in dashboard and sidebar
908
+ const modelsStatEl = document.getElementById('stat-models');
909
+ const sidebarModelsEl = document.getElementById('sidebar-models');
910
+ if (modelsStatEl) {
911
+ modelsStatEl.textContent = modelsCount;
912
+ }
913
+ if (sidebarModelsEl) {
914
+ sidebarModelsEl.textContent = modelsCount;
915
+ }
916
  } else {
917
  throw new Error('Models status endpoint not available');
918
  }
 
922
  if (listRes.ok) {
923
  const listData = await listRes.json();
924
 
925
+ // Update sidebar models count
926
+ const sidebarModels = document.getElementById('sidebar-models');
927
+ const modelsStatEl = document.getElementById('stat-models');
928
+ const totalModels = listData.total_models || (listData.models ? listData.models.length : 0);
929
+ const loadedModels = listData.models ? listData.models.filter(m => m.loaded).length : 0;
930
+
931
+ if (sidebarModels) {
932
+ sidebarModels.textContent = loadedModels > 0 ? loadedModels : totalModels;
933
+ }
934
+ if (modelsStatEl) {
935
+ modelsStatEl.textContent = loadedModels > 0 ? loadedModels : totalModels;
936
+ }
937
+
938
  if (listData.models && listData.models.length > 0) {
939
  modelsListDiv.innerHTML = `
940
  <div style="overflow-x: auto;">
 
948
  </tr>
949
  </thead>
950
  <tbody>
951
+ ${listData.models.map(model => {
952
+ const modelId = model.model_id || model.id || model.key || 'N/A';
953
+ const task = model.task || 'N/A';
954
+ const category = model.category || 'N/A';
955
+ const isLoaded = model.loaded === true;
956
+ const statusClass = isLoaded ? 'available' : 'unavailable';
957
+ const statusText = isLoaded ? '✅ Loaded' : '❌ Not Loaded';
958
+ return `
959
  <tr>
960
+ <td><strong>${modelId}</strong></td>
961
+ <td>${task}</td>
962
+ <td>${category}</td>
963
+ <td><span class="model-status ${statusClass}">${statusText}</span></td>
964
+ </tr>
965
+ `;
966
+ }).join('')}
967
  </tbody>
968
  </table>
969
  </div>