riazmo commited on
Commit
1bc5237
·
verified ·
1 Parent(s): 5857f75

Upload preview_generator.py

Browse files
Files changed (1) hide show
  1. core/preview_generator.py +365 -26
core/preview_generator.py CHANGED
@@ -941,7 +941,7 @@ def generate_color_ramps_preview_html(
941
  <style>
942
  .color-ramps-preview {{
943
  font-family: system-ui, -apple-system, sans-serif;
944
- background: {background};
945
  border-radius: 12px;
946
  padding: 20px;
947
  overflow-x: auto;
@@ -951,28 +951,28 @@ def generate_color_ramps_preview_html(
951
  display: flex;
952
  align-items: center;
953
  margin-bottom: 16px;
954
- padding-bottom: 16px;
955
- border-bottom: 1px solid #E8E8E8;
 
 
956
  }}
957
 
958
  .color-row:last-child {{
959
- border-bottom: none;
960
  margin-bottom: 0;
961
- padding-bottom: 0;
962
  }}
963
 
964
  .color-info {{
965
  display: flex;
966
  align-items: center;
967
- min-width: 140px;
968
  margin-right: 20px;
969
  }}
970
 
971
  .color-swatch {{
972
- width: 40px;
973
- height: 40px;
974
  border-radius: 8px;
975
- border: 2px solid rgba(0,0,0,0.15);
976
  margin-right: 12px;
977
  flex-shrink: 0;
978
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
@@ -986,19 +986,20 @@ def generate_color_ramps_preview_html(
986
  .color-name {{
987
  font-weight: 700;
988
  font-size: 13px;
989
- color: #1A1A1A;
990
- margin-bottom: 2px;
991
- background: #E8E8E8;
992
- padding: 3px 8px;
993
  border-radius: 4px;
994
  display: inline-block;
995
  }}
996
 
997
  .color-hex {{
998
- font-size: 11px;
999
- color: #444;
1000
- font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
1001
  margin-top: 4px;
 
1002
  }}
1003
 
1004
  .color-ramp {{
@@ -1018,7 +1019,7 @@ def generate_color_ramps_preview_html(
1018
  position: relative;
1019
  cursor: pointer;
1020
  transition: transform 0.15s;
1021
- border: 1px solid rgba(0,0,0,0.1);
1022
  }}
1023
 
1024
  .shade-cell:hover {{
@@ -1039,11 +1040,11 @@ def generate_color_ramps_preview_html(
1039
  }}
1040
 
1041
  .aa-pass {{
1042
- color: #16A34A;
1043
  }}
1044
 
1045
  .aa-fail {{
1046
- color: #DC2626;
1047
  }}
1048
 
1049
  .shade-cell:hover .shade-label,
@@ -1055,25 +1056,30 @@ def generate_color_ramps_preview_html(
1055
  .ramp-header {{
1056
  display: flex;
1057
  margin-bottom: 12px;
1058
- padding-left: 160px;
 
 
 
 
1059
  }}
1060
 
1061
  .ramp-header-label {{
1062
  width: 48px;
1063
  text-align: center;
1064
- font-size: 11px;
1065
- font-weight: 600;
1066
- color: #666;
1067
  margin-right: 4px;
1068
  }}
1069
 
1070
  .ramps-header-info {{
1071
  font-size: 14px;
1072
- color: #666;
1073
  margin-bottom: 16px;
1074
- padding: 8px 12px;
1075
- background: #f0f0f0;
1076
  border-radius: 6px;
 
1077
  }}
1078
  </style>
1079
 
@@ -1099,6 +1105,339 @@ def generate_color_ramps_preview_html(
1099
  return html
1100
 
1101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1102
  # =============================================================================
1103
  # COMBINED PREVIEW
1104
  # =============================================================================
 
941
  <style>
942
  .color-ramps-preview {{
943
  font-family: system-ui, -apple-system, sans-serif;
944
+ background: #f5f5f5 !important;
945
  border-radius: 12px;
946
  padding: 20px;
947
  overflow-x: auto;
 
951
  display: flex;
952
  align-items: center;
953
  margin-bottom: 16px;
954
+ padding: 12px;
955
+ background: #ffffff !important;
956
+ border-radius: 8px;
957
+ border: 1px solid #d0d0d0 !important;
958
  }}
959
 
960
  .color-row:last-child {{
 
961
  margin-bottom: 0;
 
962
  }}
963
 
964
  .color-info {{
965
  display: flex;
966
  align-items: center;
967
+ min-width: 160px;
968
  margin-right: 20px;
969
  }}
970
 
971
  .color-swatch {{
972
+ width: 44px;
973
+ height: 44px;
974
  border-radius: 8px;
975
+ border: 2px solid rgba(0,0,0,0.15) !important;
976
  margin-right: 12px;
977
  flex-shrink: 0;
978
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 
986
  .color-name {{
987
  font-weight: 700;
988
  font-size: 13px;
989
+ color: #1a1a1a !important;
990
+ margin-bottom: 4px;
991
+ background: #e0e0e0 !important;
992
+ padding: 4px 10px;
993
  border-radius: 4px;
994
  display: inline-block;
995
  }}
996
 
997
  .color-hex {{
998
+ font-size: 12px;
999
+ color: #333 !important;
1000
+ font-family: 'SF Mono', Monaco, monospace;
1001
  margin-top: 4px;
1002
+ font-weight: 500;
1003
  }}
1004
 
1005
  .color-ramp {{
 
1019
  position: relative;
1020
  cursor: pointer;
1021
  transition: transform 0.15s;
1022
+ border: 1px solid rgba(0,0,0,0.1) !important;
1023
  }}
1024
 
1025
  .shade-cell:hover {{
 
1040
  }}
1041
 
1042
  .aa-pass {{
1043
+ color: #166534 !important;
1044
  }}
1045
 
1046
  .aa-fail {{
1047
+ color: #991b1b !important;
1048
  }}
1049
 
1050
  .shade-cell:hover .shade-label,
 
1056
  .ramp-header {{
1057
  display: flex;
1058
  margin-bottom: 12px;
1059
+ padding-left: 180px;
1060
+ background: #e8e8e8 !important;
1061
+ padding-top: 8px;
1062
+ padding-bottom: 8px;
1063
+ border-radius: 6px;
1064
  }}
1065
 
1066
  .ramp-header-label {{
1067
  width: 48px;
1068
  text-align: center;
1069
+ font-size: 12px;
1070
+ font-weight: 700;
1071
+ color: #333 !important;
1072
  margin-right: 4px;
1073
  }}
1074
 
1075
  .ramps-header-info {{
1076
  font-size: 14px;
1077
+ color: #333 !important;
1078
  margin-bottom: 16px;
1079
+ padding: 10px 14px;
1080
+ background: #e0e0e0 !important;
1081
  border-radius: 6px;
1082
+ font-weight: 500;
1083
  }}
1084
  </style>
1085
 
 
1105
  return html
1106
 
1107
 
1108
+ # =============================================================================
1109
+ # SEMANTIC COLOR RAMPS WITH LLM RECOMMENDATIONS (Stage 2)
1110
+ # =============================================================================
1111
+
1112
+ def generate_semantic_color_ramps_html(
1113
+ semantic_analysis: dict,
1114
+ color_tokens: dict,
1115
+ llm_recommendations: dict = None,
1116
+ background: str = "#F5F5F5"
1117
+ ) -> str:
1118
+ """
1119
+ Generate HTML preview for colors organized by semantic role with LLM recommendations.
1120
+
1121
+ Args:
1122
+ semantic_analysis: Output from SemanticColorAnalyzer
1123
+ color_tokens: Dict of all color tokens
1124
+ llm_recommendations: LLM suggestions for color improvements
1125
+ background: Background color
1126
+
1127
+ Returns:
1128
+ HTML string for Gradio HTML component
1129
+ """
1130
+
1131
+ def generate_single_ramp(hex_val: str) -> str:
1132
+ """Generate a single color ramp HTML."""
1133
+ ramp = generate_color_ramp(hex_val)
1134
+ if not ramp:
1135
+ return ""
1136
+
1137
+ shades_html = ""
1138
+ for shade_info in ramp:
1139
+ shade = shade_info["shade"]
1140
+ hex_color = shade_info["hex"]
1141
+ aa_white = shade_info["aa_on_white"]
1142
+ aa_black = shade_info["aa_on_black"]
1143
+
1144
+ text_color = "#000" if shade < 500 else "#FFF"
1145
+ aa_indicator = "✓" if aa_white or aa_black else ""
1146
+
1147
+ shades_html += f'''
1148
+ <div class="sem-shade" style="background-color: {hex_color};">
1149
+ <span class="sem-shade-num" style="color: {text_color};">{shade}</span>
1150
+ <span class="sem-shade-aa" style="color: {text_color};">{aa_indicator}</span>
1151
+ </div>
1152
+ '''
1153
+ return shades_html
1154
+
1155
+ def color_row_with_recommendation(hex_val: str, role: str, role_display: str, recommendation: dict = None) -> str:
1156
+ """Generate a color row with optional LLM recommendation."""
1157
+ ramp_html = generate_single_ramp(hex_val)
1158
+
1159
+ # Calculate contrast
1160
+ try:
1161
+ from core.color_utils import get_contrast_with_white
1162
+ contrast = get_contrast_with_white(hex_val)
1163
+ aa_status = "✓ AA" if contrast >= 4.5 else f"⚠️ {contrast:.1f}:1"
1164
+ aa_class = "aa-ok" if contrast >= 4.5 else "aa-warn"
1165
+ except:
1166
+ aa_status = ""
1167
+ aa_class = ""
1168
+
1169
+ # LLM recommendation display
1170
+ rec_html = ""
1171
+ if recommendation:
1172
+ suggested = recommendation.get("suggested", "")
1173
+ issue = recommendation.get("issue", "")
1174
+ if suggested and suggested != hex_val:
1175
+ rec_html = f'''
1176
+ <div class="llm-rec">
1177
+ <span class="rec-label">💡 LLM:</span>
1178
+ <span class="rec-issue">{issue}</span>
1179
+ <span class="rec-arrow">→</span>
1180
+ <span class="rec-suggested" style="background-color: {suggested};">{suggested}</span>
1181
+ </div>
1182
+ '''
1183
+
1184
+ return f'''
1185
+ <div class="sem-color-row">
1186
+ <div class="sem-color-info">
1187
+ <div class="sem-swatch" style="background-color: {hex_val};"></div>
1188
+ <div class="sem-details">
1189
+ <div class="sem-role">{role_display}</div>
1190
+ <div class="sem-hex">{hex_val} <span class="{aa_class}">{aa_status}</span></div>
1191
+ </div>
1192
+ </div>
1193
+ <div class="sem-ramp">{ramp_html}</div>
1194
+ {rec_html}
1195
+ </div>
1196
+ '''
1197
+
1198
+ def category_section(title: str, icon: str, colors: dict, category_key: str) -> str:
1199
+ """Generate a category section with color rows."""
1200
+ if not colors:
1201
+ return ""
1202
+
1203
+ rows_html = ""
1204
+ for role, data in colors.items():
1205
+ if data and isinstance(data, dict) and "hex" in data:
1206
+ # Get LLM recommendation for this role
1207
+ rec = None
1208
+ if llm_recommendations:
1209
+ color_recs = llm_recommendations.get("color_recommendations", {})
1210
+ rec = color_recs.get(f"{category_key}.{role}", {})
1211
+
1212
+ role_display = role.replace("_", " ").title()
1213
+ rows_html += color_row_with_recommendation(
1214
+ data["hex"],
1215
+ f"{category_key}.{role}",
1216
+ role_display,
1217
+ rec
1218
+ )
1219
+
1220
+ if not rows_html:
1221
+ return ""
1222
+
1223
+ return f'''
1224
+ <div class="sem-category">
1225
+ <h3 class="sem-cat-title">{icon} {title}</h3>
1226
+ {rows_html}
1227
+ </div>
1228
+ '''
1229
+
1230
+ # Handle empty analysis
1231
+ if not semantic_analysis:
1232
+ return '''
1233
+ <div style="padding: 40px; text-align: center; background: #fff3cd !important; border-radius: 8px;">
1234
+ <p style="color: #856404 !important; font-size: 14px;">⚠️ No semantic analysis available.</p>
1235
+ </div>
1236
+ '''
1237
+
1238
+ # Build sections
1239
+ sections_html = ""
1240
+ sections_html += category_section("Brand Colors", "🎨", semantic_analysis.get("brand", {}), "brand")
1241
+ sections_html += category_section("Text Colors", "📝", semantic_analysis.get("text", {}), "text")
1242
+ sections_html += category_section("Background Colors", "🖼️", semantic_analysis.get("background", {}), "background")
1243
+ sections_html += category_section("Border Colors", "📏", semantic_analysis.get("border", {}), "border")
1244
+ sections_html += category_section("Feedback Colors", "🚨", semantic_analysis.get("feedback", {}), "feedback")
1245
+
1246
+ # LLM Impact Summary
1247
+ llm_summary = ""
1248
+ if llm_recommendations:
1249
+ changes = llm_recommendations.get("changes_made", [])
1250
+ if changes:
1251
+ changes_html = "".join([f"<li>{c}</li>" for c in changes[:5]])
1252
+ llm_summary = f'''
1253
+ <div class="llm-summary">
1254
+ <h4>🤖 LLM Recommendations Applied:</h4>
1255
+ <ul>{changes_html}</ul>
1256
+ </div>
1257
+ '''
1258
+
1259
+ html = f'''
1260
+ <style>
1261
+ .sem-ramps-preview {{
1262
+ font-family: system-ui, -apple-system, sans-serif;
1263
+ background: #f5f5f5 !important;
1264
+ border-radius: 12px;
1265
+ padding: 20px;
1266
+ }}
1267
+
1268
+ .sem-category {{
1269
+ background: #ffffff !important;
1270
+ border-radius: 8px;
1271
+ padding: 16px;
1272
+ margin-bottom: 20px;
1273
+ border: 1px solid #d0d0d0 !important;
1274
+ }}
1275
+
1276
+ .sem-cat-title {{
1277
+ font-size: 16px;
1278
+ font-weight: 700;
1279
+ color: #1a1a1a !important;
1280
+ margin: 0 0 16px 0;
1281
+ padding-bottom: 8px;
1282
+ border-bottom: 2px solid #e0e0e0 !important;
1283
+ }}
1284
+
1285
+ .sem-color-row {{
1286
+ display: flex;
1287
+ flex-wrap: wrap;
1288
+ align-items: center;
1289
+ padding: 12px;
1290
+ background: #f8f8f8 !important;
1291
+ border-radius: 6px;
1292
+ margin-bottom: 12px;
1293
+ border: 1px solid #e0e0e0 !important;
1294
+ }}
1295
+
1296
+ .sem-color-row:last-child {{
1297
+ margin-bottom: 0;
1298
+ }}
1299
+
1300
+ .sem-color-info {{
1301
+ display: flex;
1302
+ align-items: center;
1303
+ min-width: 180px;
1304
+ margin-right: 16px;
1305
+ }}
1306
+
1307
+ .sem-swatch {{
1308
+ width: 48px;
1309
+ height: 48px;
1310
+ border-radius: 8px;
1311
+ border: 2px solid rgba(0,0,0,0.15) !important;
1312
+ margin-right: 12px;
1313
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
1314
+ }}
1315
+
1316
+ .sem-details {{
1317
+ flex: 1;
1318
+ }}
1319
+
1320
+ .sem-role {{
1321
+ font-weight: 700;
1322
+ font-size: 14px;
1323
+ color: #1a1a1a !important;
1324
+ margin-bottom: 4px;
1325
+ }}
1326
+
1327
+ .sem-hex {{
1328
+ font-size: 12px;
1329
+ font-family: 'SF Mono', Monaco, monospace;
1330
+ color: #333 !important;
1331
+ }}
1332
+
1333
+ .aa-ok {{
1334
+ color: #166534 !important;
1335
+ font-weight: 600;
1336
+ }}
1337
+
1338
+ .aa-warn {{
1339
+ color: #b45309 !important;
1340
+ font-weight: 600;
1341
+ }}
1342
+
1343
+ .sem-ramp {{
1344
+ display: flex;
1345
+ gap: 3px;
1346
+ flex: 1;
1347
+ min-width: 400px;
1348
+ }}
1349
+
1350
+ .sem-shade {{
1351
+ width: 36px;
1352
+ height: 36px;
1353
+ border-radius: 4px;
1354
+ display: flex;
1355
+ flex-direction: column;
1356
+ align-items: center;
1357
+ justify-content: center;
1358
+ border: 1px solid rgba(0,0,0,0.1) !important;
1359
+ }}
1360
+
1361
+ .sem-shade-num {{
1362
+ font-size: 9px;
1363
+ font-weight: 700;
1364
+ }}
1365
+
1366
+ .sem-shade-aa {{
1367
+ font-size: 10px;
1368
+ }}
1369
+
1370
+ .llm-rec {{
1371
+ width: 100%;
1372
+ margin-top: 10px;
1373
+ padding: 8px 12px;
1374
+ background: #fef3c7 !important;
1375
+ border-radius: 4px;
1376
+ display: flex;
1377
+ align-items: center;
1378
+ gap: 8px;
1379
+ border: 1px solid #f59e0b !important;
1380
+ }}
1381
+
1382
+ .rec-label {{
1383
+ font-weight: 600;
1384
+ color: #92400e !important;
1385
+ }}
1386
+
1387
+ .rec-issue {{
1388
+ color: #78350f !important;
1389
+ font-size: 13px;
1390
+ }}
1391
+
1392
+ .rec-arrow {{
1393
+ color: #92400e !important;
1394
+ }}
1395
+
1396
+ .rec-suggested {{
1397
+ padding: 4px 10px;
1398
+ border-radius: 4px;
1399
+ font-family: 'SF Mono', Monaco, monospace;
1400
+ font-size: 12px;
1401
+ font-weight: 600;
1402
+ color: #fff !important;
1403
+ text-shadow: 0 1px 2px rgba(0,0,0,0.3);
1404
+ }}
1405
+
1406
+ .llm-summary {{
1407
+ background: #dbeafe !important;
1408
+ border: 1px solid #3b82f6 !important;
1409
+ border-radius: 8px;
1410
+ padding: 16px;
1411
+ margin-top: 20px;
1412
+ }}
1413
+
1414
+ .llm-summary h4 {{
1415
+ color: #1e40af !important;
1416
+ margin: 0 0 12px 0;
1417
+ font-size: 14px;
1418
+ }}
1419
+
1420
+ .llm-summary ul {{
1421
+ margin: 0;
1422
+ padding-left: 20px;
1423
+ color: #1e3a8a !important;
1424
+ }}
1425
+
1426
+ .llm-summary li {{
1427
+ margin-bottom: 4px;
1428
+ font-size: 13px;
1429
+ }}
1430
+ </style>
1431
+
1432
+ <div class="sem-ramps-preview">
1433
+ {sections_html}
1434
+ {llm_summary}
1435
+ </div>
1436
+ '''
1437
+
1438
+ return html
1439
+
1440
+
1441
  # =============================================================================
1442
  # COMBINED PREVIEW
1443
  # =============================================================================