agentgui 1.0.115 → 1.0.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prd ADDED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.115",
3
+ "version": "1.0.116",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/static/index.html CHANGED
@@ -912,6 +912,717 @@
912
912
  @media (min-width: 769px) and (max-width: 1024px) {
913
913
  :root { --sidebar-width: 260px; }
914
914
  }
915
+
916
+ /* ===== SVG ICON SIZING (Tailwind utilities not loaded) ===== */
917
+ .block-text svg, .block-code svg, .block-thinking svg,
918
+ .block-tool-use svg, .block-tool-result svg, .block-image svg,
919
+ .block-bash svg, .block-system svg, .block-generic svg,
920
+ .block-error svg, .block-result svg,
921
+ .event-streaming-start svg, .event-streaming-complete svg,
922
+ .event-file-read svg, .event-file-write svg,
923
+ .event-git-status svg, .event-command svg, .event-error svg,
924
+ .event-tool-use svg, .event-generic svg {
925
+ width: 1.25rem;
926
+ height: 1.25rem;
927
+ flex-shrink: 0;
928
+ }
929
+
930
+ .icon-sm svg { width: 1rem !important; height: 1rem !important; }
931
+ .icon-lg svg { width: 1.5rem !important; height: 1.5rem !important; }
932
+
933
+ /* ===== STREAMING BLOCK STYLES ===== */
934
+ .block-text {
935
+ margin-bottom: 0.75rem;
936
+ padding: 1rem;
937
+ background: var(--color-bg-primary);
938
+ border-radius: 0.5rem;
939
+ border: 1px solid var(--color-border);
940
+ line-height: 1.7;
941
+ font-size: 0.9rem;
942
+ }
943
+
944
+ .block-code {
945
+ margin-bottom: 0.75rem;
946
+ border-radius: 0.5rem;
947
+ overflow: hidden;
948
+ border: 1px solid var(--color-border);
949
+ }
950
+
951
+ .block-code .code-header {
952
+ display: flex;
953
+ align-items: center;
954
+ justify-content: space-between;
955
+ gap: 0.5rem;
956
+ background: #1f2937;
957
+ padding: 0.5rem 1rem;
958
+ border-bottom: 1px solid #374151;
959
+ }
960
+
961
+ .block-code .code-header .lang-badge {
962
+ font-size: 0.7rem;
963
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
964
+ color: #9ca3af;
965
+ text-transform: uppercase;
966
+ letter-spacing: 0.05em;
967
+ }
968
+
969
+ .block-code .copy-code-btn {
970
+ background: none;
971
+ border: none;
972
+ color: #9ca3af;
973
+ cursor: pointer;
974
+ padding: 0.25rem;
975
+ border-radius: 0.25rem;
976
+ display: flex;
977
+ align-items: center;
978
+ transition: all 0.15s;
979
+ }
980
+
981
+ .block-code .copy-code-btn:hover { color: #e5e7eb; background: #374151; }
982
+
983
+ .block-code pre {
984
+ margin: 0;
985
+ padding: 1rem;
986
+ background: #111827;
987
+ color: #e5e7eb;
988
+ overflow-x: auto;
989
+ font-size: 0.8rem;
990
+ line-height: 1.5;
991
+ }
992
+
993
+ .block-thinking {
994
+ margin-bottom: 0.75rem;
995
+ border-radius: 0.5rem;
996
+ border: 2px solid #c4b5fd;
997
+ background: #f5f3ff;
998
+ overflow: hidden;
999
+ }
1000
+
1001
+ html.dark .block-thinking { border-color: #5b21b6; background: #1e1033; }
1002
+
1003
+ .block-thinking summary {
1004
+ padding: 0.75rem 1rem;
1005
+ cursor: pointer;
1006
+ display: flex;
1007
+ align-items: center;
1008
+ gap: 0.5rem;
1009
+ font-weight: 600;
1010
+ font-size: 0.85rem;
1011
+ color: #5b21b6;
1012
+ user-select: none;
1013
+ background: none;
1014
+ border-radius: 0;
1015
+ }
1016
+
1017
+ html.dark .block-thinking summary { color: #c4b5fd; }
1018
+ .block-thinking summary:hover { background: rgba(139,92,246,0.08); }
1019
+
1020
+ .block-thinking .thinking-content {
1021
+ padding: 0.75rem 1rem;
1022
+ border-top: 1px solid #c4b5fd;
1023
+ font-size: 0.85rem;
1024
+ color: #5b21b6;
1025
+ white-space: pre-wrap;
1026
+ line-height: 1.6;
1027
+ }
1028
+
1029
+ html.dark .block-thinking .thinking-content { border-color: #5b21b6; color: #c4b5fd; }
1030
+
1031
+ /* --- Tool Use Block --- */
1032
+ .block-tool-use {
1033
+ margin-bottom: 0.75rem;
1034
+ border-radius: 0.5rem;
1035
+ border: 1px solid #a5f3fc;
1036
+ background: #ecfeff;
1037
+ overflow: hidden;
1038
+ }
1039
+
1040
+ html.dark .block-tool-use { border-color: #155e75; background: #0c1a24; }
1041
+
1042
+ .block-tool-use .tool-header {
1043
+ padding: 0.625rem 1rem;
1044
+ border-bottom: 1px solid #a5f3fc;
1045
+ display: flex;
1046
+ align-items: center;
1047
+ gap: 0.5rem;
1048
+ background: #cffafe;
1049
+ }
1050
+
1051
+ html.dark .block-tool-use .tool-header { border-color: #155e75; background: #0e2a3a; }
1052
+
1053
+ .block-tool-use .tool-header .tool-icon {
1054
+ display: flex;
1055
+ align-items: center;
1056
+ color: #0891b2;
1057
+ }
1058
+
1059
+ html.dark .block-tool-use .tool-header .tool-icon { color: #22d3ee; }
1060
+
1061
+ .block-tool-use .tool-header .tool-name {
1062
+ font-weight: 600;
1063
+ font-size: 0.85rem;
1064
+ color: #155e75;
1065
+ }
1066
+
1067
+ html.dark .block-tool-use .tool-header .tool-name { color: #67e8f9; }
1068
+
1069
+ .block-tool-use .tool-header .tool-name code {
1070
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1071
+ background: #a5f3fc;
1072
+ padding: 0.125rem 0.5rem;
1073
+ border-radius: 0.25rem;
1074
+ font-size: 0.8rem;
1075
+ }
1076
+
1077
+ html.dark .block-tool-use .tool-header .tool-name code { background: #164e63; }
1078
+
1079
+ .block-tool-use .tool-params {
1080
+ padding: 0.75rem 1rem;
1081
+ }
1082
+
1083
+ .block-tool-use .tool-params .param-label {
1084
+ font-size: 0.7rem;
1085
+ text-transform: uppercase;
1086
+ letter-spacing: 0.05em;
1087
+ color: #0e7490;
1088
+ font-weight: 600;
1089
+ margin-bottom: 0.375rem;
1090
+ }
1091
+
1092
+ html.dark .block-tool-use .tool-params .param-label { color: #22d3ee; }
1093
+
1094
+ /* Tool parameter display styles */
1095
+ .tool-param-file {
1096
+ display: flex;
1097
+ align-items: center;
1098
+ gap: 0.5rem;
1099
+ padding: 0.5rem 0.75rem;
1100
+ background: var(--color-bg-primary);
1101
+ border: 1px solid #a5f3fc;
1102
+ border-radius: 0.375rem;
1103
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1104
+ font-size: 0.8rem;
1105
+ color: var(--color-text-primary);
1106
+ overflow-x: auto;
1107
+ }
1108
+
1109
+ html.dark .tool-param-file { border-color: #155e75; }
1110
+
1111
+ .tool-param-file .file-icon { color: #0891b2; flex-shrink: 0; font-size: 1rem; }
1112
+ .tool-param-file .file-dir { color: var(--color-text-secondary); }
1113
+ .tool-param-file .file-name { font-weight: 600; color: var(--color-text-primary); }
1114
+
1115
+ .tool-param-command {
1116
+ background: #111827;
1117
+ border-radius: 0.375rem;
1118
+ padding: 0.625rem 0.875rem;
1119
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1120
+ font-size: 0.8rem;
1121
+ color: #e5e7eb;
1122
+ overflow-x: auto;
1123
+ display: flex;
1124
+ align-items: flex-start;
1125
+ gap: 0.5rem;
1126
+ }
1127
+
1128
+ .tool-param-command .prompt-char { color: #34d399; font-weight: 700; flex-shrink: 0; }
1129
+ .tool-param-command .command-text { white-space: pre-wrap; word-break: break-all; }
1130
+
1131
+ .tool-param-diff {
1132
+ border-radius: 0.375rem;
1133
+ overflow: hidden;
1134
+ border: 1px solid var(--color-border);
1135
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1136
+ font-size: 0.75rem;
1137
+ }
1138
+
1139
+ .tool-param-diff .diff-header {
1140
+ padding: 0.375rem 0.75rem;
1141
+ background: var(--color-bg-secondary);
1142
+ border-bottom: 1px solid var(--color-border);
1143
+ font-weight: 600;
1144
+ font-size: 0.7rem;
1145
+ text-transform: uppercase;
1146
+ letter-spacing: 0.03em;
1147
+ color: var(--color-text-secondary);
1148
+ }
1149
+
1150
+ .tool-param-diff .diff-old {
1151
+ padding: 0.5rem 0.75rem;
1152
+ background: #fef2f2;
1153
+ color: #991b1b;
1154
+ border-bottom: 1px solid var(--color-border);
1155
+ white-space: pre-wrap;
1156
+ word-break: break-all;
1157
+ max-height: 200px;
1158
+ overflow-y: auto;
1159
+ }
1160
+
1161
+ html.dark .tool-param-diff .diff-old { background: #1c0f0f; color: #fca5a5; }
1162
+
1163
+ .tool-param-diff .diff-new {
1164
+ padding: 0.5rem 0.75rem;
1165
+ background: #f0fdf4;
1166
+ color: #166534;
1167
+ white-space: pre-wrap;
1168
+ word-break: break-all;
1169
+ max-height: 200px;
1170
+ overflow-y: auto;
1171
+ }
1172
+
1173
+ html.dark .tool-param-diff .diff-new { background: #0a1f0f; color: #86efac; }
1174
+
1175
+ .tool-param-query {
1176
+ display: flex;
1177
+ align-items: center;
1178
+ gap: 0.5rem;
1179
+ padding: 0.5rem 0.75rem;
1180
+ background: var(--color-bg-primary);
1181
+ border: 1px solid #a5f3fc;
1182
+ border-radius: 0.375rem;
1183
+ font-size: 0.85rem;
1184
+ }
1185
+
1186
+ html.dark .tool-param-query { border-color: #155e75; }
1187
+ .tool-param-query .query-icon { flex-shrink: 0; font-size: 1rem; }
1188
+
1189
+ .tool-param-url {
1190
+ display: flex;
1191
+ align-items: center;
1192
+ gap: 0.5rem;
1193
+ padding: 0.5rem 0.75rem;
1194
+ background: var(--color-bg-primary);
1195
+ border: 1px solid #a5f3fc;
1196
+ border-radius: 0.375rem;
1197
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1198
+ font-size: 0.8rem;
1199
+ color: #2563eb;
1200
+ word-break: break-all;
1201
+ }
1202
+
1203
+ html.dark .tool-param-url { border-color: #155e75; color: #60a5fa; }
1204
+ .tool-param-url .url-icon { flex-shrink: 0; font-size: 1rem; }
1205
+
1206
+ .tool-param-todos {
1207
+ display: flex;
1208
+ flex-direction: column;
1209
+ gap: 0.25rem;
1210
+ }
1211
+
1212
+ .tool-param-todos .todo-item {
1213
+ display: flex;
1214
+ align-items: flex-start;
1215
+ gap: 0.5rem;
1216
+ padding: 0.375rem 0.5rem;
1217
+ border-radius: 0.25rem;
1218
+ font-size: 0.8rem;
1219
+ background: var(--color-bg-primary);
1220
+ border: 1px solid var(--color-border);
1221
+ }
1222
+
1223
+ .tool-param-todos .todo-status {
1224
+ flex-shrink: 0;
1225
+ font-size: 0.85rem;
1226
+ margin-top: 0.05rem;
1227
+ }
1228
+
1229
+ .tool-param-todos .todo-text { flex: 1; line-height: 1.4; }
1230
+
1231
+ .tool-param-json {
1232
+ background: var(--color-bg-primary);
1233
+ border: 1px solid #a5f3fc;
1234
+ border-radius: 0.375rem;
1235
+ overflow: hidden;
1236
+ }
1237
+
1238
+ html.dark .tool-param-json { border-color: #155e75; }
1239
+
1240
+ .tool-param-json pre {
1241
+ margin: 0;
1242
+ padding: 0.625rem 0.875rem;
1243
+ font-size: 0.75rem;
1244
+ overflow-x: auto;
1245
+ max-height: 200px;
1246
+ overflow-y: auto;
1247
+ white-space: pre-wrap;
1248
+ word-break: break-all;
1249
+ }
1250
+
1251
+ .tool-param-content-preview {
1252
+ border-radius: 0.375rem;
1253
+ overflow: hidden;
1254
+ border: 1px solid var(--color-border);
1255
+ }
1256
+
1257
+ .tool-param-content-preview .preview-header {
1258
+ padding: 0.375rem 0.75rem;
1259
+ background: var(--color-bg-secondary);
1260
+ font-size: 0.7rem;
1261
+ font-weight: 600;
1262
+ text-transform: uppercase;
1263
+ letter-spacing: 0.03em;
1264
+ color: var(--color-text-secondary);
1265
+ border-bottom: 1px solid var(--color-border);
1266
+ display: flex;
1267
+ justify-content: space-between;
1268
+ align-items: center;
1269
+ }
1270
+
1271
+ .tool-param-content-preview .preview-body {
1272
+ padding: 0.5rem 0.75rem;
1273
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1274
+ font-size: 0.75rem;
1275
+ max-height: 200px;
1276
+ overflow-y: auto;
1277
+ white-space: pre-wrap;
1278
+ word-break: break-all;
1279
+ background: var(--color-bg-code);
1280
+ color: #d1d5db;
1281
+ }
1282
+
1283
+ .tool-param-content-preview .preview-truncated {
1284
+ padding: 0.25rem 0.75rem;
1285
+ font-size: 0.7rem;
1286
+ color: var(--color-text-secondary);
1287
+ background: var(--color-bg-secondary);
1288
+ text-align: center;
1289
+ border-top: 1px solid var(--color-border);
1290
+ }
1291
+
1292
+ /* --- Tool Result Block --- */
1293
+ .block-tool-result {
1294
+ margin-bottom: 0.75rem;
1295
+ border-radius: 0.5rem;
1296
+ overflow: hidden;
1297
+ }
1298
+
1299
+ .block-tool-result.result-success { border: 1px solid #86efac; background: #f0fdf4; }
1300
+ .block-tool-result.result-error { border: 1px solid #fca5a5; background: #fef2f2; }
1301
+ html.dark .block-tool-result.result-success { border-color: #166534; background: #0a1f0f; }
1302
+ html.dark .block-tool-result.result-error { border-color: #991b1b; background: #1c0f0f; }
1303
+
1304
+ .block-tool-result .result-header {
1305
+ padding: 0.5rem 1rem;
1306
+ display: flex;
1307
+ align-items: center;
1308
+ justify-content: space-between;
1309
+ }
1310
+
1311
+ .block-tool-result.result-success .result-header { background: #dcfce7; border-bottom: 1px solid #86efac; }
1312
+ .block-tool-result.result-error .result-header { background: #fee2e2; border-bottom: 1px solid #fca5a5; }
1313
+ html.dark .block-tool-result.result-success .result-header { background: #0f2b1a; border-color: #166534; }
1314
+ html.dark .block-tool-result.result-error .result-header { background: #2c1010; border-color: #991b1b; }
1315
+
1316
+ .block-tool-result .result-header .status-label {
1317
+ display: flex;
1318
+ align-items: center;
1319
+ gap: 0.375rem;
1320
+ font-weight: 600;
1321
+ font-size: 0.8rem;
1322
+ }
1323
+
1324
+ .block-tool-result.result-success .status-label { color: #166534; }
1325
+ .block-tool-result.result-error .status-label { color: #991b1b; }
1326
+ html.dark .block-tool-result.result-success .status-label { color: #86efac; }
1327
+ html.dark .block-tool-result.result-error .status-label { color: #fca5a5; }
1328
+
1329
+ .block-tool-result .result-body {
1330
+ padding: 0.625rem 1rem;
1331
+ font-size: 0.8rem;
1332
+ white-space: pre-wrap;
1333
+ word-break: break-all;
1334
+ overflow-x: auto;
1335
+ max-height: 400px;
1336
+ overflow-y: auto;
1337
+ line-height: 1.5;
1338
+ }
1339
+
1340
+ .block-tool-result.result-success .result-body { color: #14532d; }
1341
+ .block-tool-result.result-error .result-body { color: #7f1d1d; }
1342
+ html.dark .block-tool-result.result-success .result-body { color: #bbf7d0; }
1343
+ html.dark .block-tool-result.result-error .result-body { color: #fecaca; }
1344
+
1345
+ .block-tool-result .result-body.collapsed { max-height: 150px; position: relative; }
1346
+ .block-tool-result .result-body.collapsed::after {
1347
+ content: '';
1348
+ position: absolute;
1349
+ bottom: 0;
1350
+ left: 0;
1351
+ right: 0;
1352
+ height: 3rem;
1353
+ background: linear-gradient(transparent, var(--color-bg-primary));
1354
+ pointer-events: none;
1355
+ }
1356
+
1357
+ .block-tool-result .expand-btn {
1358
+ display: block;
1359
+ width: 100%;
1360
+ padding: 0.375rem;
1361
+ background: var(--color-bg-secondary);
1362
+ border: none;
1363
+ border-top: 1px solid var(--color-border);
1364
+ cursor: pointer;
1365
+ font-size: 0.75rem;
1366
+ color: var(--color-primary);
1367
+ font-weight: 500;
1368
+ text-align: center;
1369
+ }
1370
+
1371
+ .block-tool-result .expand-btn:hover { background: var(--color-border); }
1372
+
1373
+ .block-tool-result .result-id {
1374
+ font-size: 0.65rem;
1375
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1376
+ opacity: 0.6;
1377
+ }
1378
+
1379
+ /* --- Result Summary Block --- */
1380
+ .block-result {
1381
+ margin-bottom: 0.75rem;
1382
+ border-radius: 0.5rem;
1383
+ overflow: hidden;
1384
+ }
1385
+
1386
+ .block-result.result-ok {
1387
+ background: linear-gradient(135deg, #ecfdf5, #f0fdf4);
1388
+ border: 1px solid #86efac;
1389
+ }
1390
+
1391
+ .block-result.result-err {
1392
+ background: linear-gradient(135deg, #fef2f2, #fff1f2);
1393
+ border: 1px solid #fca5a5;
1394
+ }
1395
+
1396
+ html.dark .block-result.result-ok { background: linear-gradient(135deg, #0a1f0f, #0f2b1a); border-color: #166534; }
1397
+ html.dark .block-result.result-err { background: linear-gradient(135deg, #1c0f0f, #2c1010); border-color: #991b1b; }
1398
+
1399
+ .block-result .result-summary-header {
1400
+ padding: 0.625rem 1rem;
1401
+ display: flex;
1402
+ align-items: center;
1403
+ gap: 0.5rem;
1404
+ }
1405
+
1406
+ .block-result.result-ok .result-summary-header { color: #166534; }
1407
+ .block-result.result-err .result-summary-header { color: #991b1b; }
1408
+ html.dark .block-result.result-ok .result-summary-header { color: #86efac; }
1409
+ html.dark .block-result.result-err .result-summary-header { color: #fca5a5; }
1410
+
1411
+ .block-result .result-summary-header .result-title { font-weight: 700; font-size: 0.9rem; }
1412
+
1413
+ .block-result .result-stats {
1414
+ display: flex;
1415
+ gap: 1.5rem;
1416
+ padding: 0 1rem 0.625rem;
1417
+ font-size: 0.8rem;
1418
+ flex-wrap: wrap;
1419
+ }
1420
+
1421
+ .block-result .result-stat {
1422
+ display: flex;
1423
+ align-items: center;
1424
+ gap: 0.375rem;
1425
+ }
1426
+
1427
+ .block-result .result-stat .stat-icon { font-size: 0.9rem; opacity: 0.7; }
1428
+ .block-result .result-stat .stat-value { font-weight: 600; }
1429
+ .block-result .result-stat .stat-label { color: var(--color-text-secondary); font-size: 0.75rem; }
1430
+
1431
+ .block-result .result-content {
1432
+ padding: 0.625rem 1rem;
1433
+ border-top: 1px solid var(--color-border);
1434
+ font-size: 0.85rem;
1435
+ white-space: pre-wrap;
1436
+ word-break: break-word;
1437
+ max-height: 300px;
1438
+ overflow-y: auto;
1439
+ line-height: 1.5;
1440
+ }
1441
+
1442
+ /* --- System Block --- */
1443
+ .block-system {
1444
+ margin-bottom: 0.75rem;
1445
+ border-radius: 0.5rem;
1446
+ border: 1px solid #c7d2fe;
1447
+ background: #eef2ff;
1448
+ overflow: hidden;
1449
+ }
1450
+
1451
+ html.dark .block-system { border-color: #3730a3; background: #15103a; }
1452
+
1453
+ .block-system .system-header {
1454
+ padding: 0.625rem 1rem;
1455
+ background: #e0e7ff;
1456
+ border-bottom: 1px solid #c7d2fe;
1457
+ font-weight: 600;
1458
+ font-size: 0.85rem;
1459
+ color: #3730a3;
1460
+ }
1461
+
1462
+ html.dark .block-system .system-header { background: #1e1b4b; border-color: #3730a3; color: #a5b4fc; }
1463
+
1464
+ .block-system .system-body {
1465
+ padding: 0.75rem 1rem;
1466
+ font-size: 0.8rem;
1467
+ color: #3730a3;
1468
+ display: flex;
1469
+ flex-direction: column;
1470
+ gap: 0.375rem;
1471
+ }
1472
+
1473
+ html.dark .block-system .system-body { color: #c7d2fe; }
1474
+
1475
+ .block-system .system-body .sys-field { display: flex; align-items: baseline; gap: 0.5rem; }
1476
+ .block-system .system-body .sys-label { font-weight: 600; flex-shrink: 0; min-width: 5rem; }
1477
+ .block-system .system-body .sys-value { font-family: 'Monaco','Menlo','Ubuntu Mono', monospace; font-size: 0.75rem; }
1478
+ .block-system .system-body .sys-value code { background: #c7d2fe; padding: 0.0625rem 0.375rem; border-radius: 0.25rem; }
1479
+ html.dark .block-system .system-body .sys-value code { background: #312e81; }
1480
+
1481
+ .block-system .tools-list {
1482
+ display: flex;
1483
+ flex-wrap: wrap;
1484
+ gap: 0.25rem;
1485
+ margin-top: 0.25rem;
1486
+ }
1487
+
1488
+ .block-system .tools-list .tool-badge {
1489
+ display: inline-block;
1490
+ padding: 0.125rem 0.5rem;
1491
+ background: #c7d2fe;
1492
+ color: #3730a3;
1493
+ border-radius: 1rem;
1494
+ font-size: 0.65rem;
1495
+ font-weight: 500;
1496
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1497
+ }
1498
+
1499
+ html.dark .block-system .tools-list .tool-badge { background: #312e81; color: #a5b4fc; }
1500
+
1501
+ /* --- Bash Block --- */
1502
+ .block-bash {
1503
+ margin-bottom: 0.75rem;
1504
+ border-radius: 0.5rem;
1505
+ overflow: hidden;
1506
+ border: 1px solid #374151;
1507
+ background: #111827;
1508
+ }
1509
+
1510
+ .block-bash .bash-command {
1511
+ padding: 0.625rem 1rem;
1512
+ border-bottom: 1px solid #374151;
1513
+ display: flex;
1514
+ align-items: flex-start;
1515
+ gap: 0.5rem;
1516
+ }
1517
+
1518
+ .block-bash .bash-command .prompt { color: #34d399; font-weight: 700; flex-shrink: 0; }
1519
+ .block-bash .bash-command code {
1520
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1521
+ color: #e5e7eb;
1522
+ font-size: 0.8rem;
1523
+ overflow-x: auto;
1524
+ flex: 1;
1525
+ }
1526
+
1527
+ .block-bash .bash-output {
1528
+ padding: 0.625rem 1rem;
1529
+ color: #9ca3af;
1530
+ font-size: 0.75rem;
1531
+ overflow-x: auto;
1532
+ white-space: pre-wrap;
1533
+ max-height: 300px;
1534
+ overflow-y: auto;
1535
+ }
1536
+
1537
+ /* --- Generic Block --- */
1538
+ .block-generic {
1539
+ margin-bottom: 0.75rem;
1540
+ padding: 0.75rem 1rem;
1541
+ border-radius: 0.5rem;
1542
+ border: 1px solid var(--color-border);
1543
+ background: var(--color-bg-secondary);
1544
+ }
1545
+
1546
+ .block-generic .generic-type {
1547
+ font-size: 0.7rem;
1548
+ text-transform: uppercase;
1549
+ letter-spacing: 0.05em;
1550
+ color: var(--color-text-secondary);
1551
+ font-weight: 600;
1552
+ margin-bottom: 0.375rem;
1553
+ }
1554
+
1555
+ .block-generic .generic-fields {
1556
+ display: flex;
1557
+ flex-direction: column;
1558
+ gap: 0.25rem;
1559
+ font-size: 0.8rem;
1560
+ }
1561
+
1562
+ .block-generic .generic-field {
1563
+ display: flex;
1564
+ gap: 0.5rem;
1565
+ }
1566
+
1567
+ .block-generic .generic-field .field-key {
1568
+ font-weight: 600;
1569
+ color: var(--color-text-secondary);
1570
+ flex-shrink: 0;
1571
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1572
+ font-size: 0.75rem;
1573
+ }
1574
+
1575
+ .block-generic .generic-field .field-value {
1576
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1577
+ font-size: 0.75rem;
1578
+ word-break: break-all;
1579
+ }
1580
+
1581
+ /* --- Error Block --- */
1582
+ .block-error {
1583
+ margin-bottom: 0.75rem;
1584
+ padding: 0.75rem 1rem;
1585
+ border-radius: 0.5rem;
1586
+ border: 1px solid #fca5a5;
1587
+ background: #fef2f2;
1588
+ }
1589
+
1590
+ html.dark .block-error { border-color: #991b1b; background: #1c0f0f; }
1591
+
1592
+ /* --- Image Block --- */
1593
+ .block-image {
1594
+ margin-bottom: 0.75rem;
1595
+ border-radius: 0.5rem;
1596
+ overflow: hidden;
1597
+ border: 1px solid var(--color-border);
1598
+ }
1599
+
1600
+ .block-image img { max-width: 100%; height: auto; max-height: 24rem; object-fit: contain; display: block; }
1601
+ .block-image .image-caption { padding: 0.5rem 0.75rem; background: var(--color-bg-secondary); font-size: 0.8rem; color: var(--color-text-secondary); }
1602
+
1603
+ /* ===== STREAMING EVENTS ===== */
1604
+ .event-streaming-start {
1605
+ margin-bottom: 0.75rem;
1606
+ padding: 0.75rem 1rem;
1607
+ background: #eff6ff;
1608
+ border: 1px solid #bfdbfe;
1609
+ border-radius: 0.5rem;
1610
+ display: flex;
1611
+ align-items: center;
1612
+ gap: 0.625rem;
1613
+ }
1614
+
1615
+ html.dark .event-streaming-start { background: #0c1a2e; border-color: #1e40af; }
1616
+
1617
+ .event-streaming-complete {
1618
+ margin-bottom: 0.75rem;
1619
+ padding: 0.75rem 1rem;
1620
+ background: linear-gradient(135deg, #ecfdf5, #f0fdf4);
1621
+ border: 1px solid #86efac;
1622
+ border-radius: 0.5rem;
1623
+ }
1624
+
1625
+ html.dark .event-streaming-complete { background: linear-gradient(135deg, #0a1f0f, #0f2b1a); border-color: #166534; }
915
1626
  </style>
916
1627
  </head>
917
1628
  <body>
@@ -468,7 +468,15 @@ class AgentGUIClient {
468
468
  return this.escapeHtml(part.content);
469
469
  }).join('');
470
470
  }
471
- return this.escapeHtml(JSON.stringify(block));
471
+ // Fallback for unknown block types: show formatted key-value pairs
472
+ const fieldsHtml = Object.entries(block)
473
+ .filter(([key]) => key !== 'type')
474
+ .map(([key, value]) => {
475
+ let displayValue = typeof value === 'string' ? value : JSON.stringify(value);
476
+ if (displayValue.length > 100) displayValue = displayValue.substring(0, 100) + '...';
477
+ return `<div style="font-size:0.75rem;margin-bottom:0.25rem"><span style="font-weight:600">${this.escapeHtml(key)}:</span> <code>${this.escapeHtml(displayValue)}</code></div>`;
478
+ }).join('');
479
+ return `<div style="padding:0.5rem;background:var(--color-bg-secondary);border-radius:0.375rem;border:1px solid var(--color-border)"><div style="font-size:0.7rem;font-weight:600;text-transform:uppercase;margin-bottom:0.25rem">${this.escapeHtml(block.type)}</div>${fieldsHtml}</div>`;
472
480
  }
473
481
 
474
482
  scrollToBottom() {
@@ -708,7 +716,17 @@ class AgentGUIClient {
708
716
  html += '</div>';
709
717
  return html;
710
718
  } else {
711
- return `<div class="message-text">${this.escapeHtml(JSON.stringify(content))}</div>`;
719
+ // Fallback for non-array content: format as key-value pairs
720
+ if (typeof content === 'object' && content !== null) {
721
+ const fieldsHtml = Object.entries(content)
722
+ .map(([key, value]) => {
723
+ let displayValue = typeof value === 'string' ? value : JSON.stringify(value);
724
+ if (displayValue.length > 150) displayValue = displayValue.substring(0, 150) + '...';
725
+ return `<div style="font-size:0.8rem;margin-bottom:0.375rem"><span style="font-weight:600">${this.escapeHtml(key)}:</span> <code style="background:var(--color-bg-secondary);padding:0.125rem 0.25rem;border-radius:0.25rem">${this.escapeHtml(displayValue)}</code></div>`;
726
+ }).join('');
727
+ return `<div class="message-text" style="background:var(--color-bg-secondary);padding:0.75rem;border-radius:0.375rem">${fieldsHtml}</div>`;
728
+ }
729
+ return `<div class="message-text">${this.escapeHtml(String(content))}</div>`;
712
730
  }
713
731
  }
714
732
 
@@ -1223,7 +1241,18 @@ class AgentGUIClient {
1223
1241
  }
1224
1242
  contentHtml += '</div>';
1225
1243
  } else {
1226
- contentHtml = `<div class="message-text">${this.escapeHtml(JSON.stringify(msg.content))}</div>`;
1244
+ // Fallback for non-array msg.content: format as key-value pairs
1245
+ if (typeof msg.content === 'object' && msg.content !== null) {
1246
+ const fieldsHtml = Object.entries(msg.content)
1247
+ .map(([key, value]) => {
1248
+ let displayValue = typeof value === 'string' ? value : JSON.stringify(value);
1249
+ if (displayValue.length > 150) displayValue = displayValue.substring(0, 150) + '...';
1250
+ return `<div style="font-size:0.8rem;margin-bottom:0.375rem"><span style="font-weight:600">${this.escapeHtml(key)}:</span> <code style="background:var(--color-bg-secondary);padding:0.125rem 0.25rem;border-radius:0.25rem">${this.escapeHtml(displayValue)}</code></div>`;
1251
+ }).join('');
1252
+ contentHtml = `<div class="message-text" style="background:var(--color-bg-secondary);padding:0.75rem;border-radius:0.375rem">${fieldsHtml}</div>`;
1253
+ } else {
1254
+ contentHtml = `<div class="message-text">${this.escapeHtml(String(msg.content))}</div>`;
1255
+ }
1227
1256
  }
1228
1257
 
1229
1258
  return `
@@ -373,6 +373,8 @@ class StreamingRenderer {
373
373
  return this.renderBlockBash(block, context);
374
374
  case 'system':
375
375
  return this.renderBlockSystem(block, context);
376
+ case 'result':
377
+ return this.renderBlockResult(block, context);
376
378
  default:
377
379
  return this.renderBlockGeneric(block, context);
378
380
  }
@@ -387,11 +389,9 @@ class StreamingRenderer {
387
389
  */
388
390
  renderBlockText(block, context) {
389
391
  const div = document.createElement('div');
390
- div.className = 'block-text mb-4 p-4 bg-white dark:bg-gray-950 rounded-lg border border-gray-200 dark:border-gray-800 leading-relaxed';
392
+ div.className = 'block-text';
391
393
 
392
394
  const text = block.text || '';
393
-
394
- // Parse markdown code blocks and links
395
395
  const html = this.parseAndRenderMarkdown(text);
396
396
  div.innerHTML = html;
397
397
 
@@ -428,36 +428,30 @@ class StreamingRenderer {
428
428
  */
429
429
  renderBlockCode(block, context) {
430
430
  const div = document.createElement('div');
431
- div.className = 'block-code mb-4 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-800';
431
+ div.className = 'block-code';
432
432
 
433
433
  const code = block.code || '';
434
434
  const language = (block.language || 'plaintext').toLowerCase();
435
435
 
436
- // Create header with language badge
437
436
  const header = document.createElement('div');
438
- header.className = 'flex items-center justify-between gap-2 bg-gray-900 dark:bg-gray-950 px-4 py-3 border-b border-gray-800';
437
+ header.className = 'code-header';
439
438
  header.innerHTML = `
440
- <span class="text-xs font-mono text-gray-400 uppercase tracking-wider">${this.escapeHtml(language)}</span>
441
- <button class="copy-code-btn text-gray-400 hover:text-gray-200 transition-colors p-1 rounded hover:bg-gray-800" title="Copy code">
442
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
443
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
444
- </svg>
439
+ <span class="lang-badge">${this.escapeHtml(language)}</span>
440
+ <button class="copy-code-btn" title="Copy code">
441
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
445
442
  </button>
446
443
  `;
447
444
 
448
- // Add copy functionality
449
445
  const copyBtn = header.querySelector('.copy-code-btn');
450
446
  copyBtn.addEventListener('click', () => {
451
447
  navigator.clipboard.writeText(code).then(() => {
452
- const originalText = copyBtn.innerHTML;
453
- copyBtn.innerHTML = '<svg class="w-4 h-4 text-green-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path></svg>';
454
- setTimeout(() => { copyBtn.innerHTML = originalText; }, 2000);
448
+ const orig = copyBtn.innerHTML;
449
+ copyBtn.innerHTML = '<svg viewBox="0 0 20 20" fill="#34d399"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>';
450
+ setTimeout(() => { copyBtn.innerHTML = orig; }, 2000);
455
451
  });
456
452
  });
457
453
 
458
- // Create code container
459
454
  const codeContainer = document.createElement('pre');
460
- codeContainer.className = 'bg-gray-900 dark:bg-gray-950 text-gray-100 p-4 overflow-x-auto';
461
455
  codeContainer.innerHTML = `<code class="language-${this.escapeHtml(language)}">${this.escapeHtml(code)}</code>`;
462
456
 
463
457
  div.appendChild(header);
@@ -471,20 +465,16 @@ class StreamingRenderer {
471
465
  */
472
466
  renderBlockThinking(block, context) {
473
467
  const div = document.createElement('div');
474
- div.className = 'block-thinking mb-4 rounded-lg border-2 border-purple-200 dark:border-purple-800 bg-purple-50 dark:bg-purple-950';
468
+ div.className = 'block-thinking';
475
469
 
476
470
  const thinking = block.thinking || '';
477
471
  div.innerHTML = `
478
- <details class="group">
479
- <summary class="px-4 py-3 cursor-pointer flex items-center gap-2 font-semibold text-purple-900 dark:text-purple-200 select-none hover:bg-purple-100 dark:hover:bg-purple-900 transition-colors">
480
- <svg class="w-5 h-5 transition-transform group-open:rotate-90" fill="currentColor" viewBox="0 0 20 20">
481
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
482
- </svg>
483
- <span>Claude's Thinking Process</span>
472
+ <details>
473
+ <summary>
474
+ <svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/></svg>
475
+ <span>Thinking Process</span>
484
476
  </summary>
485
- <div class="px-4 py-3 border-t border-purple-200 dark:border-purple-800 text-sm text-purple-900 dark:text-purple-200 whitespace-pre-wrap leading-relaxed">
486
- ${this.escapeHtml(thinking)}
487
- </div>
477
+ <div class="thinking-content">${this.escapeHtml(thinking)}</div>
488
478
  </details>
489
479
  `;
490
480
 
@@ -492,69 +482,170 @@ class StreamingRenderer {
492
482
  }
493
483
 
494
484
  /**
495
- * Render tool use block
485
+ * Get a tool-specific icon SVG string
486
+ */
487
+ getToolIcon(toolName) {
488
+ const icons = {
489
+ Read: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z"/></svg>',
490
+ Write: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"/></svg>',
491
+ Edit: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"/><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd"/></svg>',
492
+ Bash: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M2 5a2 2 0 012-2h12a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2V5zm3.293 1.293a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 01-1.414-1.414L7.586 10 5.293 7.707a1 1 0 010-1.414zM11 12a1 1 0 100 2h3a1 1 0 100-2h-3z" clip-rule="evenodd"/></svg>',
493
+ Glob: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"/></svg>',
494
+ Grep: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd"/></svg>',
495
+ WebFetch: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM4.332 8.027a6.012 6.012 0 011.912-2.706C6.512 5.73 6.974 6 7.5 6A1.5 1.5 0 019 7.5V8a2 2 0 004 0 2 2 0 012 2v1a2 2 0 01-2 2 2 2 0 01-2 2v.5a6.003 6.003 0 01-6.668-7.473z" clip-rule="evenodd"/></svg>',
496
+ WebSearch: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd"/></svg>',
497
+ TodoWrite: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 011 1v3.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 111.414-1.414L6 11.586V8a1 1 0 011-1z" clip-rule="evenodd"/></svg>',
498
+ Task: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"/><path fill-rule="evenodd" d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z" clip-rule="evenodd"/></svg>',
499
+ NotebookEdit: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M9 4.804A7.968 7.968 0 005.5 4c-1.255 0-2.443.29-3.5.804v10A7.969 7.969 0 015.5 14c1.669 0 3.218.51 4.5 1.385A7.962 7.962 0 0114.5 14c1.255 0 2.443.29 3.5.804v-10A7.968 7.968 0 0014.5 4c-1.255 0-2.443.29-3.5.804V12a1 1 0 11-2 0V4.804z"/></svg>'
500
+ };
501
+ return icons[toolName] || '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10.666a1 1 0 11-1.64-1.118L9.687 10H5a1 1 0 01-.82-1.573l7-10.666a1 1 0 011.12-.373z" clip-rule="evenodd"/></svg>';
502
+ }
503
+
504
+ /**
505
+ * Render a file path with icon, directory breadcrumb, and filename
506
+ */
507
+ renderFilePath(filePath) {
508
+ if (!filePath) return '';
509
+ const parts = filePath.split('/');
510
+ const fileName = parts.pop();
511
+ const dir = parts.join('/');
512
+ return `<div class="tool-param-file"><span class="file-icon">&#128196;</span>${dir ? `<span class="file-dir">${this.escapeHtml(dir)}/</span>` : ''}<span class="file-name">${this.escapeHtml(fileName)}</span></div>`;
513
+ }
514
+
515
+ /**
516
+ * Render smart tool parameters based on tool type
517
+ */
518
+ renderSmartParams(toolName, input) {
519
+ if (!input || Object.keys(input).length === 0) return '';
520
+
521
+ const normalizedName = toolName.replace(/^mcp__[^_]+__/, '');
522
+
523
+ switch (normalizedName) {
524
+ case 'Read':
525
+ return `<div class="tool-params">${this.renderFilePath(input.file_path)}${input.offset ? `<div style="margin-top:0.375rem;font-size:0.75rem;color:var(--color-text-secondary)">Lines ${input.offset}${input.limit ? '–' + (input.offset + input.limit) : '+'}</div>` : ''}</div>`;
526
+
527
+ case 'Write':
528
+ return `<div class="tool-params">${this.renderFilePath(input.file_path)}${input.content ? this.renderContentPreview(input.content, 'Content') : ''}</div>`;
529
+
530
+ case 'Edit': {
531
+ let html = `<div class="tool-params">${this.renderFilePath(input.file_path)}`;
532
+ if (input.old_string || input.new_string) {
533
+ html += `<div class="tool-param-diff" style="margin-top:0.5rem">`;
534
+ if (input.old_string) {
535
+ html += `<div class="diff-header">Remove</div><div class="diff-old">${this.escapeHtml(this.truncateContent(input.old_string, 500))}</div>`;
536
+ }
537
+ if (input.new_string) {
538
+ html += `<div class="diff-header">Add</div><div class="diff-new">${this.escapeHtml(this.truncateContent(input.new_string, 500))}</div>`;
539
+ }
540
+ html += '</div>';
541
+ }
542
+ return html + '</div>';
543
+ }
544
+
545
+ case 'Bash': {
546
+ const cmd = input.command || input.commands || '';
547
+ const cmdText = typeof cmd === 'string' ? cmd : JSON.stringify(cmd);
548
+ let html = `<div class="tool-params"><div class="tool-param-command"><span class="prompt-char">$</span><span class="command-text">${this.escapeHtml(cmdText)}</span></div>`;
549
+ if (input.description) html += `<div style="margin-top:0.375rem;font-size:0.75rem;color:var(--color-text-secondary)">${this.escapeHtml(input.description)}</div>`;
550
+ return html + '</div>';
551
+ }
552
+
553
+ case 'Glob':
554
+ return `<div class="tool-params"><div class="tool-param-query"><span class="query-icon">&#128193;</span><code style="font-size:0.85rem">${this.escapeHtml(input.pattern || '')}</code></div>${input.path ? `<div style="margin-top:0.25rem;font-size:0.75rem;color:var(--color-text-secondary)">in ${this.escapeHtml(input.path)}</div>` : ''}</div>`;
555
+
556
+ case 'Grep':
557
+ return `<div class="tool-params"><div class="tool-param-query"><span class="query-icon">&#128269;</span><code style="font-size:0.85rem">${this.escapeHtml(input.pattern || '')}</code></div>${input.path ? `<div style="margin-top:0.25rem;font-size:0.75rem;color:var(--color-text-secondary)">in ${this.escapeHtml(input.path)}</div>` : ''}${input.glob ? `<div style="margin-top:0.125rem;font-size:0.7rem;color:var(--color-text-secondary)">files: ${this.escapeHtml(input.glob)}</div>` : ''}</div>`;
558
+
559
+ case 'WebFetch':
560
+ return `<div class="tool-params"><div class="tool-param-url"><span class="url-icon">&#127760;</span>${this.escapeHtml(input.url || '')}</div>${input.prompt ? `<div style="margin-top:0.375rem;font-size:0.8rem;color:var(--color-text-secondary)">${this.escapeHtml(this.truncateContent(input.prompt, 150))}</div>` : ''}</div>`;
561
+
562
+ case 'WebSearch':
563
+ return `<div class="tool-params"><div class="tool-param-query"><span class="query-icon">&#128269;</span><strong style="font-size:0.85rem">${this.escapeHtml(input.query || '')}</strong></div></div>`;
564
+
565
+ case 'TodoWrite':
566
+ if (input.todos && Array.isArray(input.todos)) {
567
+ const statusIcons = { completed: '&#9989;', in_progress: '&#9881;', pending: '&#9744;' };
568
+ const items = input.todos.map(t => `<div class="todo-item"><span class="todo-status">${statusIcons[t.status] || '&#9744;'}</span><span class="todo-text">${this.escapeHtml(t.content || '')}</span></div>`).join('');
569
+ return `<div class="tool-params"><div class="tool-param-todos">${items}</div></div>`;
570
+ }
571
+ return this.renderJsonParams(input);
572
+
573
+ case 'Task':
574
+ return `<div class="tool-params">${input.description ? `<div style="font-weight:600;font-size:0.85rem;margin-bottom:0.375rem">${this.escapeHtml(input.description)}</div>` : ''}${input.prompt ? `<div style="font-size:0.8rem;color:var(--color-text-secondary);max-height:100px;overflow-y:auto;white-space:pre-wrap;word-break:break-word">${this.escapeHtml(this.truncateContent(input.prompt, 300))}</div>` : ''}${input.subagent_type ? `<div style="margin-top:0.375rem;font-size:0.7rem"><code style="background:var(--color-bg-secondary);padding:0.125rem 0.375rem;border-radius:0.25rem">${this.escapeHtml(input.subagent_type)}</code></div>` : ''}</div>`;
575
+
576
+ case 'NotebookEdit':
577
+ return `<div class="tool-params">${this.renderFilePath(input.notebook_path)}${input.new_source ? this.renderContentPreview(input.new_source, 'Cell content') : ''}</div>`;
578
+
579
+ default:
580
+ return this.renderJsonParams(input);
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Render content preview with truncation
586
+ */
587
+ renderContentPreview(content, label) {
588
+ const maxLen = 500;
589
+ const truncated = content.length > maxLen;
590
+ const displayContent = truncated ? content.substring(0, maxLen) : content;
591
+ const lineCount = content.split('\n').length;
592
+ return `<div class="tool-param-content-preview" style="margin-top:0.5rem"><div class="preview-header"><span>${this.escapeHtml(label)}</span><span style="font-weight:400">${lineCount} lines${truncated ? ' (truncated)' : ''}</span></div><div class="preview-body">${this.escapeHtml(displayContent)}</div>${truncated ? '<div class="preview-truncated">... ' + (content.length - maxLen) + ' more characters</div>' : ''}</div>`;
593
+ }
594
+
595
+ /**
596
+ * Render params as formatted JSON (default fallback for unknown tools)
597
+ */
598
+ renderJsonParams(input) {
599
+ return `<div class="tool-params"><div class="tool-param-json"><pre>${this.escapeHtml(JSON.stringify(input, null, 2))}</pre></div></div>`;
600
+ }
601
+
602
+ /**
603
+ * Render tool use block with smart parameter display
496
604
  */
497
605
  renderBlockToolUse(block, context) {
498
606
  const div = document.createElement('div');
499
- div.className = 'block-tool-use mb-4 rounded-lg border border-cyan-200 dark:border-cyan-800 bg-cyan-50 dark:bg-cyan-950 overflow-hidden';
607
+ div.className = 'block-tool-use';
500
608
 
501
609
  const toolName = block.name || 'unknown';
502
610
  const input = block.input || {};
503
611
 
504
612
  div.innerHTML = `
505
- <div class="px-4 py-3 border-b border-cyan-200 dark:border-cyan-800 flex items-center gap-2 bg-cyan-100 dark:bg-cyan-900">
506
- <svg class="w-5 h-5 text-cyan-600 dark:text-cyan-400" fill="currentColor" viewBox="0 0 20 20">
507
- <path fill-rule="evenodd" d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10.666a1 1 0 11-1.64-1.118L9.687 10H5a1 1 0 01-.82-1.573l7-10.666a1 1 0 011.12-.373zM14.6 15.477l-5.223-7.912h-3.5l5.223 7.912h3.5z" clip-rule="evenodd"></path>
508
- </svg>
509
- <span class="font-semibold text-cyan-900 dark:text-cyan-200">Tool: <code class="font-mono bg-cyan-200 dark:bg-cyan-800 px-2 py-1 rounded text-sm">${this.escapeHtml(toolName)}</code></span>
613
+ <div class="tool-header">
614
+ <span class="tool-icon">${this.getToolIcon(toolName)}</span>
615
+ <span class="tool-name"><code>${this.escapeHtml(toolName)}</code></span>
510
616
  </div>
511
- ${Object.keys(input).length > 0 ? `
512
- <div class="px-4 py-3">
513
- <div class="text-xs uppercase tracking-wider text-cyan-700 dark:text-cyan-400 font-semibold mb-2">Parameters:</div>
514
- <pre class="bg-white dark:bg-gray-900 p-3 rounded border border-cyan-200 dark:border-cyan-800 text-xs overflow-x-auto"><code class="language-json">${this.escapeHtml(JSON.stringify(input, null, 2))}</code></pre>
515
- </div>
516
- ` : '<div class="px-4 py-2 text-sm text-cyan-700 dark:text-cyan-400">No parameters</div>'}
617
+ ${Object.keys(input).length > 0 ? this.renderSmartParams(toolName, input) : ''}
517
618
  `;
518
619
 
519
620
  return div;
520
621
  }
521
622
 
522
623
  /**
523
- * Render tool result block
624
+ * Render tool result block with smart content display
524
625
  */
525
626
  renderBlockToolResult(block, context) {
526
627
  const div = document.createElement('div');
527
628
  const isError = block.is_error || false;
528
- const className = isError
529
- ? 'block-tool-result mb-4 rounded-lg border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-950 overflow-hidden'
530
- : 'block-tool-result mb-4 rounded-lg border border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-950 overflow-hidden';
531
-
532
- div.className = className;
629
+ div.className = `block-tool-result ${isError ? 'result-error' : 'result-success'}`;
533
630
 
534
631
  const content = block.content || '';
632
+ const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
535
633
  const toolUseId = block.tool_use_id || '';
536
- const statusColor = isError ? 'red' : 'green';
634
+ const isLong = contentStr.length > 1500;
635
+
636
+ const iconSvg = isError
637
+ ? '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>'
638
+ : '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>';
639
+
640
+ const displayContent = isLong ? contentStr.substring(0, 1500) : contentStr;
537
641
 
538
642
  div.innerHTML = `
539
- <div class="px-4 py-3 border-b border-${statusColor}-200 dark:border-${statusColor}-800 flex items-center justify-between bg-${statusColor}-100 dark:bg-${statusColor}-900">
540
- <div class="flex items-center gap-2">
541
- ${isError ? `
542
- <svg class="w-5 h-5 text-${statusColor}-600 dark:text-${statusColor}-400" fill="currentColor" viewBox="0 0 20 20">
543
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
544
- </svg>
545
- <span class="font-semibold text-${statusColor}-900 dark:text-${statusColor}-200">Error</span>
546
- ` : `
547
- <svg class="w-5 h-5 text-${statusColor}-600 dark:text-${statusColor}-400" fill="currentColor" viewBox="0 0 20 20">
548
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
549
- </svg>
550
- <span class="font-semibold text-${statusColor}-900 dark:text-${statusColor}-200">Success</span>
551
- `}
552
- </div>
553
- ${toolUseId ? `<code class="text-xs text-${statusColor}-700 dark:text-${statusColor}-300">${this.escapeHtml(toolUseId)}</code>` : ''}
554
- </div>
555
- <div class="px-4 py-3 text-sm text-${statusColor}-900 dark:text-${statusColor}-200 whitespace-pre-wrap leading-relaxed overflow-x-auto">
556
- ${this.escapeHtml(typeof content === 'string' ? content : JSON.stringify(content, null, 2))}
643
+ <div class="result-header">
644
+ <span class="status-label">${iconSvg} ${isError ? 'Error' : 'Success'}</span>
645
+ ${toolUseId ? `<span class="result-id">${this.escapeHtml(toolUseId)}</span>` : ''}
557
646
  </div>
647
+ <div class="result-body${isLong ? ' collapsed' : ''}">${this.escapeHtml(displayContent)}</div>
648
+ ${isLong ? '<button class="expand-btn" onclick="this.previousElementSibling.classList.toggle(\'collapsed\');this.textContent=this.textContent===\'Show more\'?\'Show less\':\'Show more\'">Show more</button>' : ''}
558
649
  `;
559
650
 
560
651
  return div;
@@ -565,14 +656,19 @@ class StreamingRenderer {
565
656
  */
566
657
  renderBlockImage(block, context) {
567
658
  const div = document.createElement('div');
568
- div.className = 'block-image mb-4 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-800';
659
+ div.className = 'block-image';
569
660
 
570
- const src = block.image || block.src || '';
661
+ let src = block.image || block.src || '';
571
662
  const alt = block.alt || 'Image';
572
663
 
664
+ // Handle base64 data
665
+ if (block.data && block.media_type) {
666
+ src = `data:${block.media_type};base64,${block.data}`;
667
+ }
668
+
573
669
  div.innerHTML = `
574
- <img src="${this.escapeHtml(src)}" alt="${this.escapeHtml(alt)}" class="w-full h-auto max-h-96 object-cover">
575
- ${block.alt ? `<div class="px-4 py-2 bg-gray-50 dark:bg-gray-900 text-sm text-gray-700 dark:text-gray-300">${this.escapeHtml(alt)}</div>` : ''}
670
+ <img src="${this.escapeHtml(src)}" alt="${this.escapeHtml(alt)}" loading="lazy">
671
+ ${block.alt ? `<div class="image-caption">${this.escapeHtml(alt)}</div>` : ''}
576
672
  `;
577
673
 
578
674
  return div;
@@ -583,19 +679,14 @@ class StreamingRenderer {
583
679
  */
584
680
  renderBlockBash(block, context) {
585
681
  const div = document.createElement('div');
586
- div.className = 'block-bash mb-4 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-800 bg-gray-900 dark:bg-gray-950';
682
+ div.className = 'block-bash';
587
683
 
588
684
  const command = block.command || block.code || '';
589
685
  const output = block.output || '';
590
686
 
591
687
  div.innerHTML = `
592
- <div class="px-4 py-3 border-b border-gray-700 flex items-center gap-2">
593
- <span class="text-green-400 font-semibold">$</span>
594
- <code class="font-mono text-gray-200 text-sm overflow-x-auto w-full">${this.escapeHtml(command)}</code>
595
- </div>
596
- ${output ? `
597
- <pre class="px-4 py-3 text-gray-300 text-sm overflow-x-auto"><code>${this.escapeHtml(output)}</code></pre>
598
- ` : ''}
688
+ <div class="bash-command"><span class="prompt">$</span><code>${this.escapeHtml(command)}</code></div>
689
+ ${output ? `<pre class="bash-output"><code>${this.escapeHtml(output)}</code></pre>` : ''}
599
690
  `;
600
691
 
601
692
  return div;
@@ -606,40 +697,77 @@ class StreamingRenderer {
606
697
  */
607
698
  renderBlockSystem(block, context) {
608
699
  const div = document.createElement('div');
609
- div.className = 'block-system mb-4 rounded-lg border border-indigo-200 dark:border-indigo-800 bg-indigo-50 dark:bg-indigo-950 overflow-hidden';
700
+ div.className = 'block-system';
610
701
 
611
702
  div.innerHTML = `
612
- <div class="px-4 py-3 bg-indigo-100 dark:bg-indigo-900 border-b border-indigo-200 dark:border-indigo-800">
613
- <h4 class="font-semibold text-indigo-900 dark:text-indigo-200">Session Information</h4>
703
+ <div class="system-header">Session Information</div>
704
+ <div class="system-body">
705
+ ${block.model ? `<div class="sys-field"><span class="sys-label">Model</span><span class="sys-value"><code>${this.escapeHtml(block.model)}</code></span></div>` : ''}
706
+ ${block.cwd ? `<div class="sys-field"><span class="sys-label">Directory</span><span class="sys-value"><code>${this.escapeHtml(block.cwd)}</code></span></div>` : ''}
707
+ ${block.session_id ? `<div class="sys-field"><span class="sys-label">Session</span><span class="sys-value"><code>${this.escapeHtml(block.session_id)}</code></span></div>` : ''}
708
+ ${block.tools && Array.isArray(block.tools) ? `<div class="sys-field" style="flex-direction:column;gap:0.375rem"><span class="sys-label">Tools (${block.tools.length})</span><div class="tools-list">${block.tools.map(t => `<span class="tool-badge">${this.escapeHtml(t)}</span>`).join('')}</div></div>` : ''}
614
709
  </div>
615
- <div class="px-4 py-3 text-sm text-indigo-900 dark:text-indigo-200">
616
- ${block.model ? `<div class="mb-2"><strong>Model:</strong> ${this.escapeHtml(block.model)}</div>` : ''}
617
- ${block.cwd ? `<div class="mb-2"><strong>Directory:</strong> <code class="bg-indigo-200 dark:bg-indigo-800 px-1 rounded">${this.escapeHtml(block.cwd)}</code></div>` : ''}
618
- ${block.session_id ? `<div class="mb-2"><strong>Session:</strong> <code class="bg-indigo-200 dark:bg-indigo-800 px-1 rounded text-xs">${this.escapeHtml(block.session_id)}</code></div>` : ''}
619
- ${block.tools && Array.isArray(block.tools) ? `
620
- <div class="mb-2">
621
- <strong>Available Tools:</strong>
622
- <div class="mt-1 flex flex-wrap gap-1">
623
- ${block.tools.map(t => `<span class="badge badge-xs bg-indigo-200 dark:bg-indigo-800 text-indigo-900 dark:text-indigo-200">${this.escapeHtml(t)}</span>`).join('')}
624
- </div>
625
- </div>
626
- ` : ''}
710
+ `;
711
+
712
+ return div;
713
+ }
714
+
715
+ /**
716
+ * Render result block (execution summary)
717
+ */
718
+ renderBlockResult(block, context) {
719
+ const div = document.createElement('div');
720
+ const isError = block.is_error || false;
721
+ div.className = `block-result ${isError ? 'result-err' : 'result-ok'}`;
722
+
723
+ const duration = block.duration_ms ? (block.duration_ms / 1000).toFixed(1) + 's' : '';
724
+ const cost = block.total_cost_usd ? '$' + block.total_cost_usd.toFixed(4) : '';
725
+ const turns = block.num_turns || '';
726
+
727
+ div.innerHTML = `
728
+ <div class="result-summary-header">
729
+ ${isError
730
+ ? '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>'
731
+ : '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>'}
732
+ <span class="result-title">${isError ? 'Execution Failed' : 'Execution Complete'}</span>
627
733
  </div>
734
+ <div class="result-stats">
735
+ ${duration ? `<div class="result-stat"><span class="stat-icon">&#9202;</span><span class="stat-value">${this.escapeHtml(duration)}</span><span class="stat-label">duration</span></div>` : ''}
736
+ ${cost ? `<div class="result-stat"><span class="stat-icon">&#128176;</span><span class="stat-value">${this.escapeHtml(cost)}</span><span class="stat-label">cost</span></div>` : ''}
737
+ ${turns ? `<div class="result-stat"><span class="stat-icon">&#128260;</span><span class="stat-value">${this.escapeHtml(String(turns))}</span><span class="stat-label">turns</span></div>` : ''}
738
+ </div>
739
+ ${block.result ? `<div class="result-content">${this.escapeHtml(typeof block.result === 'string' ? block.result : JSON.stringify(block.result, null, 2))}</div>` : ''}
628
740
  `;
629
741
 
630
742
  return div;
631
743
  }
632
744
 
633
745
  /**
634
- * Render generic block
746
+ * Render generic block with formatted key-value pairs
635
747
  */
636
748
  renderBlockGeneric(block, context) {
637
749
  const div = document.createElement('div');
638
- div.className = 'block-generic mb-4 p-4 rounded-lg border border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900';
750
+ div.className = 'block-generic';
751
+
752
+ // Show key-value pairs instead of raw JSON
753
+ const fieldsHtml = Object.entries(block)
754
+ .filter(([key]) => key !== 'type')
755
+ .map(([key, value]) => {
756
+ let displayValue;
757
+ if (typeof value === 'string') {
758
+ displayValue = value.length > 200 ? value.substring(0, 200) + '...' : value;
759
+ } else if (typeof value === 'number' || typeof value === 'boolean') {
760
+ displayValue = String(value);
761
+ } else {
762
+ displayValue = JSON.stringify(value, null, 2);
763
+ if (displayValue.length > 200) displayValue = displayValue.substring(0, 200) + '...';
764
+ }
765
+ return `<div class="generic-field"><span class="field-key">${this.escapeHtml(key)}:</span><span class="field-value">${this.escapeHtml(displayValue)}</span></div>`;
766
+ }).join('');
639
767
 
640
768
  div.innerHTML = `
641
- <div class="text-xs uppercase tracking-wider text-gray-600 dark:text-gray-400 font-semibold mb-2">${this.escapeHtml(block.type)}</div>
642
- <pre class="text-xs overflow-x-auto bg-white dark:bg-gray-950 p-3 rounded border border-gray-200 dark:border-gray-800"><code>${this.escapeHtml(JSON.stringify(block, null, 2))}</code></pre>
769
+ <div class="generic-type">${this.escapeHtml(block.type)}</div>
770
+ <div class="generic-fields">${fieldsHtml}</div>
643
771
  `;
644
772
 
645
773
  return div;
@@ -650,16 +778,16 @@ class StreamingRenderer {
650
778
  */
651
779
  renderBlockError(block, error) {
652
780
  const div = document.createElement('div');
653
- div.className = 'block-error mb-4 p-4 rounded-lg border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-950';
781
+ div.className = 'block-error';
654
782
 
655
783
  div.innerHTML = `
656
- <div class="flex items-start gap-3">
657
- <svg class="w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
658
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
784
+ <div style="display:flex;align-items:flex-start;gap:0.625rem">
785
+ <svg viewBox="0 0 20 20" fill="currentColor" style="color:#ef4444;flex-shrink:0;margin-top:0.125rem">
786
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
659
787
  </svg>
660
- <div class="flex-1">
661
- <h4 class="font-semibold text-red-900 dark:text-red-200">Block Render Error</h4>
662
- <p class="text-sm text-red-800 dark:text-red-300 mt-1">${this.escapeHtml(error.message)}</p>
788
+ <div>
789
+ <div style="font-weight:600;color:#991b1b">Render Error</div>
790
+ <div style="font-size:0.85rem;color:#7f1d1d;margin-top:0.25rem">${this.escapeHtml(error.message)}</div>
663
791
  </div>
664
792
  </div>
665
793
  `;
@@ -1053,33 +1181,21 @@ class StreamingRenderer {
1053
1181
  * Render tool use event - for backward compatibility
1054
1182
  */
1055
1183
  renderToolUse(event) {
1056
- const div = document.createElement('div');
1057
- div.className = 'event-tool-use mb-3 rounded-lg border border-cyan-200 dark:border-cyan-800 bg-cyan-50 dark:bg-cyan-950 overflow-hidden';
1184
+ // Use the new block-based renderer for consistency
1185
+ const block = {
1186
+ type: 'tool_use',
1187
+ name: event.toolName || event.tool || 'unknown',
1188
+ input: event.input || {}
1189
+ };
1190
+ const div = this.renderBlockToolUse(block, event);
1191
+ div.className = 'event-tool-use mb-3';
1058
1192
  div.dataset.eventId = event.id || '';
1059
1193
  div.dataset.eventType = 'tool_use';
1060
-
1061
- const toolName = event.toolName || event.tool || 'unknown';
1062
- const input = event.input || {};
1063
-
1064
- div.innerHTML = `
1065
- <div class="px-4 py-3 border-b border-cyan-200 dark:border-cyan-800 flex items-center gap-2 bg-cyan-100 dark:bg-cyan-900">
1066
- <svg class="w-5 h-5 text-cyan-600 dark:text-cyan-400" fill="currentColor" viewBox="0 0 20 20">
1067
- <path fill-rule="evenodd" d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10.666a1 1 0 11-1.64-1.118L9.687 10H5a1 1 0 01-.82-1.573l7-10.666a1 1 0 011.12-.373zM14.6 15.477l-5.223-7.912h-3.5l5.223 7.912h3.5z" clip-rule="evenodd"></path>
1068
- </svg>
1069
- <span class="font-semibold text-cyan-900 dark:text-cyan-200">Tool Call: <code class="font-mono bg-cyan-200 dark:bg-cyan-800 px-2 py-1 rounded text-sm">${this.escapeHtml(toolName)}</code></span>
1070
- </div>
1071
- ${Object.keys(input).length > 0 ? `
1072
- <div class="px-4 py-3">
1073
- <div class="text-xs uppercase tracking-wider text-cyan-700 dark:text-cyan-400 font-semibold mb-2">Input Parameters:</div>
1074
- <pre class="bg-white dark:bg-gray-900 p-3 rounded border border-cyan-200 dark:border-cyan-800 text-xs overflow-x-auto"><code class="language-json">${this.escapeHtml(JSON.stringify(input, null, 2))}</code></pre>
1075
- </div>
1076
- ` : '<div class="px-4 py-2 text-sm text-cyan-700 dark:text-cyan-400">No input parameters</div>'}
1077
- `;
1078
1194
  return div;
1079
1195
  }
1080
1196
 
1081
1197
  /**
1082
- * Render generic event
1198
+ * Render generic event with formatted key-value pairs
1083
1199
  */
1084
1200
  renderGeneric(event) {
1085
1201
  const div = document.createElement('div');
@@ -1088,12 +1204,31 @@ class StreamingRenderer {
1088
1204
  div.dataset.eventType = event.type;
1089
1205
 
1090
1206
  const time = new Date(event.timestamp).toLocaleTimeString();
1207
+
1208
+ // Format event data as key-value pairs
1209
+ const fieldsHtml = Object.entries(event)
1210
+ .filter(([key]) => !['type', 'timestamp'].includes(key))
1211
+ .map(([key, value]) => {
1212
+ let displayValue;
1213
+ if (typeof value === 'string') {
1214
+ displayValue = value.length > 100 ? value.substring(0, 100) + '...' : value;
1215
+ } else if (typeof value === 'number' || typeof value === 'boolean') {
1216
+ displayValue = String(value);
1217
+ } else if (value === null) {
1218
+ displayValue = 'null';
1219
+ } else {
1220
+ displayValue = JSON.stringify(value);
1221
+ if (displayValue.length > 100) displayValue = displayValue.substring(0, 100) + '...';
1222
+ }
1223
+ return `<div style="font-size:0.75rem;margin-bottom:0.25rem"><span style="font-weight:600;color:var(--color-text-secondary)">${this.escapeHtml(key)}:</span> <span style="font-family:'Monaco','Menlo','Ubuntu Mono',monospace">${this.escapeHtml(displayValue)}</span></div>`;
1224
+ }).join('');
1225
+
1091
1226
  div.innerHTML = `
1092
- <div class="flex items-center justify-between mb-2">
1093
- <span class="font-semibold text-gray-900 dark:text-gray-100">${this.escapeHtml(event.type)}</span>
1094
- <span class="text-xs text-gray-600 dark:text-gray-400">${time}</span>
1227
+ <div style="display:flex;justify-content:space-between;margin-bottom:0.5rem">
1228
+ <span style="font-weight:600;color:var(--color-text-primary)">${this.escapeHtml(event.type)}</span>
1229
+ <span style="font-size:0.75rem;color:var(--color-text-secondary)">${time}</span>
1095
1230
  </div>
1096
- <pre class="text-xs overflow-x-auto"><code>${this.escapeHtml(JSON.stringify(event, null, 2))}</code></pre>
1231
+ <div>${fieldsHtml || '<span style="color:var(--color-text-secondary);font-size:0.75rem">No additional data</span>'}</div>
1097
1232
  `;
1098
1233
  return div;
1099
1234
  }