@lvce-editor/chat-debug-view 3.4.0 → 4.0.0
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/README.md +2 -2
- package/dist/chatDebugViewWorkerMain.js +1496 -142
- package/package.json +1 -1
|
@@ -964,39 +964,82 @@ const {
|
|
|
964
964
|
wrapGetter
|
|
965
965
|
} = create$1();
|
|
966
966
|
|
|
967
|
+
const All = 'all';
|
|
968
|
+
const Tools = 'tools';
|
|
969
|
+
const Network = 'network';
|
|
970
|
+
const Ui = 'ui';
|
|
971
|
+
const Stream = 'stream';
|
|
972
|
+
const options = [{
|
|
973
|
+
label: 'All',
|
|
974
|
+
value: All
|
|
975
|
+
}, {
|
|
976
|
+
label: 'Tools',
|
|
977
|
+
value: Tools
|
|
978
|
+
}, {
|
|
979
|
+
label: 'Network',
|
|
980
|
+
value: Network
|
|
981
|
+
}, {
|
|
982
|
+
label: 'UI',
|
|
983
|
+
value: Ui
|
|
984
|
+
}, {
|
|
985
|
+
label: 'Stream',
|
|
986
|
+
value: Stream
|
|
987
|
+
}];
|
|
988
|
+
const getEventCategoryFilterLabel = eventCategoryFilter => {
|
|
989
|
+
switch (eventCategoryFilter) {
|
|
990
|
+
case Network:
|
|
991
|
+
return 'Network';
|
|
992
|
+
case Stream:
|
|
993
|
+
return 'Stream';
|
|
994
|
+
case Tools:
|
|
995
|
+
return 'Tools';
|
|
996
|
+
case Ui:
|
|
997
|
+
return 'UI';
|
|
998
|
+
default:
|
|
999
|
+
return 'All';
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
|
|
967
1003
|
const createDefaultState = () => {
|
|
968
1004
|
return {
|
|
969
1005
|
assetDir: '',
|
|
970
1006
|
databaseName: 'lvce-chat-view-sessions',
|
|
971
1007
|
dataBaseVersion: 2,
|
|
972
1008
|
errorMessage: '',
|
|
1009
|
+
eventCategoryFilter: All,
|
|
973
1010
|
events: [],
|
|
974
1011
|
eventStoreName: 'chat-view-events',
|
|
975
1012
|
filterValue: '',
|
|
976
1013
|
height: 0,
|
|
977
|
-
initial:
|
|
1014
|
+
initial: false,
|
|
978
1015
|
platform: 0,
|
|
1016
|
+
selectedEventIndex: null,
|
|
979
1017
|
sessionId: '',
|
|
980
1018
|
sessionIdIndexName: 'sessionId',
|
|
981
1019
|
showEventStreamFinishedEvents: false,
|
|
982
1020
|
showInputEvents: false,
|
|
983
1021
|
showResponsePartEvents: false,
|
|
1022
|
+
timelineEndSeconds: '',
|
|
1023
|
+
timelineStartSeconds: '',
|
|
984
1024
|
uid: 0,
|
|
985
1025
|
uri: '',
|
|
1026
|
+
useDevtoolsLayout: false,
|
|
986
1027
|
width: 0,
|
|
987
1028
|
x: 0,
|
|
988
1029
|
y: 0
|
|
989
1030
|
};
|
|
990
1031
|
};
|
|
991
1032
|
|
|
992
|
-
const create = (uid, uri, x, y, width, height, platform, assetDir, sessionId = '', databaseName = 'lvce-chat-view-sessions', dataBaseVersion = 2, eventStoreName = 'chat-view-events', sessionIdIndexName = 'sessionId') => {
|
|
1033
|
+
const create = (uid, uri, x, y, width, height, platform, assetDir, sessionId = '', databaseName = 'lvce-chat-view-sessions', dataBaseVersion = 2, eventStoreName = 'chat-view-events', sessionIdIndexName = 'sessionId', savedState = {}) => {
|
|
993
1034
|
const state = {
|
|
994
1035
|
...createDefaultState(),
|
|
1036
|
+
...savedState,
|
|
995
1037
|
assetDir,
|
|
996
1038
|
databaseName,
|
|
997
1039
|
dataBaseVersion,
|
|
998
1040
|
eventStoreName,
|
|
999
1041
|
height,
|
|
1042
|
+
initial: true,
|
|
1000
1043
|
platform,
|
|
1001
1044
|
sessionId,
|
|
1002
1045
|
sessionIdIndexName,
|
|
@@ -1011,13 +1054,11 @@ const create = (uid, uri, x, y, width, height, platform, assetDir, sessionId = '
|
|
|
1011
1054
|
|
|
1012
1055
|
const RenderItems = 1;
|
|
1013
1056
|
const RenderCss = 2;
|
|
1057
|
+
const RenderIncremental = 3;
|
|
1014
1058
|
|
|
1015
1059
|
const diff = (oldState, newState) => {
|
|
1016
|
-
if (oldState.
|
|
1017
|
-
return [
|
|
1018
|
-
}
|
|
1019
|
-
if (oldState.errorMessage !== newState.errorMessage || oldState.events !== newState.events || oldState.filterValue !== newState.filterValue || oldState.sessionId !== newState.sessionId || oldState.showEventStreamFinishedEvents !== newState.showEventStreamFinishedEvents || oldState.showInputEvents !== newState.showInputEvents || oldState.showResponsePartEvents !== newState.showResponsePartEvents || oldState.uid !== newState.uid) {
|
|
1020
|
-
return [RenderItems];
|
|
1060
|
+
if (oldState.errorMessage !== newState.errorMessage || oldState.eventCategoryFilter !== newState.eventCategoryFilter || oldState.events !== newState.events || oldState.filterValue !== newState.filterValue || oldState.sessionId !== newState.sessionId || oldState.showEventStreamFinishedEvents !== newState.showEventStreamFinishedEvents || oldState.showInputEvents !== newState.showInputEvents || oldState.showResponsePartEvents !== newState.showResponsePartEvents || oldState.timelineEndSeconds !== newState.timelineEndSeconds || oldState.timelineStartSeconds !== newState.timelineStartSeconds || oldState.useDevtoolsLayout !== newState.useDevtoolsLayout || oldState.selectedEventIndex !== newState.selectedEventIndex || oldState.uid !== newState.uid) {
|
|
1061
|
+
return [RenderIncremental, RenderCss];
|
|
1021
1062
|
}
|
|
1022
1063
|
return [];
|
|
1023
1064
|
};
|
|
@@ -1034,35 +1075,388 @@ const getBoolean = value => {
|
|
|
1034
1075
|
return value === true || value === 'true' || value === 'on' || value === '1';
|
|
1035
1076
|
};
|
|
1036
1077
|
|
|
1078
|
+
const RE_SPACE = /\s+/;
|
|
1079
|
+
const tokenToEventCategoryFilter = new Map([['@tools', Tools], ['@network', Network], ['@ui', Ui], ['@stream', Stream]]);
|
|
1080
|
+
const parseFilterValue = filterValue => {
|
|
1081
|
+
const normalizedFilter = filterValue.trim().toLowerCase();
|
|
1082
|
+
if (!normalizedFilter) {
|
|
1083
|
+
return {
|
|
1084
|
+
eventCategoryFilter: All,
|
|
1085
|
+
filterText: ''
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
const parts = normalizedFilter.split(RE_SPACE);
|
|
1089
|
+
const eventCategoryFilter = parts.map(part => tokenToEventCategoryFilter.get(part)).find(Boolean) || All;
|
|
1090
|
+
const filterText = parts.filter(part => !tokenToEventCategoryFilter.has(part)).join(' ');
|
|
1091
|
+
return {
|
|
1092
|
+
eventCategoryFilter,
|
|
1093
|
+
filterText
|
|
1094
|
+
};
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
const toolEventTypePrefix = 'tool-execution-';
|
|
1098
|
+
const isToolEvent = event => {
|
|
1099
|
+
return event.type.startsWith(toolEventTypePrefix);
|
|
1100
|
+
};
|
|
1101
|
+
const isNetworkEvent = event => {
|
|
1102
|
+
const normalizedType = event.type.toLowerCase();
|
|
1103
|
+
return normalizedType === 'request' || normalizedType === 'response' || normalizedType === 'handle-response' || normalizedType.includes('fetch') || normalizedType.includes('xhr');
|
|
1104
|
+
};
|
|
1105
|
+
const isUiEvent = event => {
|
|
1106
|
+
return event.type.startsWith('handle-') && event.type !== 'handle-response';
|
|
1107
|
+
};
|
|
1108
|
+
const isStreamEvent = event => {
|
|
1109
|
+
return event.type === 'sse-response-part' || event.type === 'event-stream-finished';
|
|
1110
|
+
};
|
|
1111
|
+
const matchesEventCategoryFilter = (event, eventCategoryFilter) => {
|
|
1112
|
+
switch (eventCategoryFilter) {
|
|
1113
|
+
case Network:
|
|
1114
|
+
return isNetworkEvent(event);
|
|
1115
|
+
case Stream:
|
|
1116
|
+
return isStreamEvent(event);
|
|
1117
|
+
case Tools:
|
|
1118
|
+
return isToolEvent(event);
|
|
1119
|
+
case Ui:
|
|
1120
|
+
return isUiEvent(event);
|
|
1121
|
+
default:
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
const getVisibleEvents = (events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1126
|
+
return events.filter(event => {
|
|
1127
|
+
if (!showInputEvents && (event.type === 'handle-input' || event.type === 'handle-submit')) {
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
if (!showResponsePartEvents && event.type === 'sse-response-part') {
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
if (!showEventStreamFinishedEvents && event.type === 'event-stream-finished') {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
// hide session creation events by default — not useful in the debug view
|
|
1137
|
+
if (event.type === 'chat-session-created') {
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
return true;
|
|
1141
|
+
});
|
|
1142
|
+
};
|
|
1143
|
+
const getFilteredEvents = (events, filterValue, eventCategoryFilter, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1144
|
+
const visibleEvents = getVisibleEvents(events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
|
|
1145
|
+
const parsedFilter = parseFilterValue(filterValue);
|
|
1146
|
+
const activeEventCategoryFilter = parsedFilter.eventCategoryFilter === All ? eventCategoryFilter : parsedFilter.eventCategoryFilter;
|
|
1147
|
+
const filteredByCategory = visibleEvents.filter(event => matchesEventCategoryFilter(event, activeEventCategoryFilter));
|
|
1148
|
+
const {
|
|
1149
|
+
filterText
|
|
1150
|
+
} = parsedFilter;
|
|
1151
|
+
if (!filterText) {
|
|
1152
|
+
return filteredByCategory;
|
|
1153
|
+
}
|
|
1154
|
+
return filteredByCategory.filter(event => JSON.stringify(event).toLowerCase().includes(filterText));
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
const toTimeNumber = value => {
|
|
1158
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1159
|
+
return value;
|
|
1160
|
+
}
|
|
1161
|
+
if (typeof value === 'string') {
|
|
1162
|
+
const timestamp = Date.parse(value);
|
|
1163
|
+
if (!Number.isNaN(timestamp)) {
|
|
1164
|
+
return timestamp;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
return undefined;
|
|
1168
|
+
};
|
|
1169
|
+
|
|
1170
|
+
const getEventTime = event => {
|
|
1171
|
+
return toTimeNumber(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
const maxBarUnits = 8;
|
|
1175
|
+
const parseTimelineSeconds = value => {
|
|
1176
|
+
const trimmed = value.trim();
|
|
1177
|
+
if (!trimmed) {
|
|
1178
|
+
return undefined;
|
|
1179
|
+
}
|
|
1180
|
+
const parsed = Number.parseFloat(trimmed);
|
|
1181
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1182
|
+
return undefined;
|
|
1183
|
+
}
|
|
1184
|
+
return parsed;
|
|
1185
|
+
};
|
|
1186
|
+
const roundSeconds = value => {
|
|
1187
|
+
return Number(value.toFixed(3));
|
|
1188
|
+
};
|
|
1189
|
+
const getEventsWithTime = events => {
|
|
1190
|
+
return events.flatMap(event => {
|
|
1191
|
+
const time = getEventTime(event);
|
|
1192
|
+
if (time === undefined) {
|
|
1193
|
+
return [];
|
|
1194
|
+
}
|
|
1195
|
+
return [{
|
|
1196
|
+
event,
|
|
1197
|
+
time
|
|
1198
|
+
}];
|
|
1199
|
+
});
|
|
1200
|
+
};
|
|
1201
|
+
const getNormalizedRange = (durationSeconds, startValue, endValue) => {
|
|
1202
|
+
const parsedStart = parseTimelineSeconds(startValue);
|
|
1203
|
+
const parsedEnd = parseTimelineSeconds(endValue);
|
|
1204
|
+
if (parsedStart === undefined && parsedEnd === undefined) {
|
|
1205
|
+
return {
|
|
1206
|
+
endSeconds: null,
|
|
1207
|
+
hasSelection: false,
|
|
1208
|
+
startSeconds: null
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
const rawStart = parsedStart ?? 0;
|
|
1212
|
+
const rawEnd = parsedEnd ?? durationSeconds;
|
|
1213
|
+
const normalizedStart = Math.max(0, Math.min(durationSeconds, Math.min(rawStart, rawEnd)));
|
|
1214
|
+
const normalizedEnd = Math.max(0, Math.min(durationSeconds, Math.max(rawStart, rawEnd)));
|
|
1215
|
+
return {
|
|
1216
|
+
endSeconds: roundSeconds(normalizedEnd),
|
|
1217
|
+
hasSelection: true,
|
|
1218
|
+
startSeconds: roundSeconds(normalizedStart)
|
|
1219
|
+
};
|
|
1220
|
+
};
|
|
1221
|
+
const filterEventsByTimelineRange = (events, startValue, endValue) => {
|
|
1222
|
+
const eventsWithTime = getEventsWithTime(events);
|
|
1223
|
+
if (eventsWithTime.length === 0) {
|
|
1224
|
+
return events;
|
|
1225
|
+
}
|
|
1226
|
+
const baseTime = eventsWithTime[0].time;
|
|
1227
|
+
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1228
|
+
const durationSeconds = roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
|
|
1229
|
+
const range = getNormalizedRange(durationSeconds, startValue, endValue);
|
|
1230
|
+
if (!range.hasSelection || range.startSeconds === null || range.endSeconds === null) {
|
|
1231
|
+
return events;
|
|
1232
|
+
}
|
|
1233
|
+
const startTime = baseTime + range.startSeconds * 1000;
|
|
1234
|
+
const endTime = baseTime + range.endSeconds * 1000;
|
|
1235
|
+
return eventsWithTime.filter(item => item.time >= startTime && item.time <= endTime).map(item => item.event);
|
|
1236
|
+
};
|
|
1237
|
+
const getTimelineInfo = (events, startValue, endValue) => {
|
|
1238
|
+
const eventsWithTime = getEventsWithTime(events);
|
|
1239
|
+
if (eventsWithTime.length === 0) {
|
|
1240
|
+
return {
|
|
1241
|
+
buckets: [],
|
|
1242
|
+
durationSeconds: 0,
|
|
1243
|
+
endSeconds: null,
|
|
1244
|
+
hasSelection: false,
|
|
1245
|
+
startSeconds: null
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
const baseTime = eventsWithTime[0].time;
|
|
1249
|
+
const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
|
|
1250
|
+
const durationMs = Math.max(0, lastTime - baseTime);
|
|
1251
|
+
const durationSeconds = roundSeconds(durationMs / 1000);
|
|
1252
|
+
const range = getNormalizedRange(durationSeconds, startValue, endValue);
|
|
1253
|
+
const bucketCount = durationSeconds === 0 ? 1 : Math.max(12, Math.min(48, Math.ceil(durationSeconds)));
|
|
1254
|
+
const bucketDurationMs = durationMs === 0 ? 1000 : durationMs / bucketCount;
|
|
1255
|
+
const counts = Array.from({
|
|
1256
|
+
length: bucketCount
|
|
1257
|
+
}).fill(0);
|
|
1258
|
+
for (const item of eventsWithTime) {
|
|
1259
|
+
const offsetMs = item.time - baseTime;
|
|
1260
|
+
const index = durationMs === 0 ? 0 : Math.min(bucketCount - 1, Math.floor(offsetMs / durationMs * bucketCount));
|
|
1261
|
+
counts[index] += 1;
|
|
1262
|
+
}
|
|
1263
|
+
const maxCount = Math.max(...counts);
|
|
1264
|
+
const buckets = counts.map((count, index) => {
|
|
1265
|
+
const bucketStartMs = index * bucketDurationMs;
|
|
1266
|
+
const bucketEndMs = index === bucketCount - 1 ? durationMs : (index + 1) * bucketDurationMs;
|
|
1267
|
+
const hasSelection = range.hasSelection && range.startSeconds !== null && range.endSeconds !== null;
|
|
1268
|
+
const selectionStartMs = hasSelection ? range.startSeconds * 1000 : 0;
|
|
1269
|
+
const selectionEndMs = hasSelection ? range.endSeconds * 1000 : 0;
|
|
1270
|
+
return {
|
|
1271
|
+
count,
|
|
1272
|
+
endSeconds: roundSeconds(bucketEndMs / 1000),
|
|
1273
|
+
isSelected: hasSelection && bucketEndMs >= selectionStartMs && bucketStartMs <= selectionEndMs,
|
|
1274
|
+
startSeconds: roundSeconds(bucketStartMs / 1000),
|
|
1275
|
+
unitCount: count === 0 ? 0 : Math.max(1, Math.round(count / maxCount * maxBarUnits))
|
|
1276
|
+
};
|
|
1277
|
+
});
|
|
1278
|
+
return {
|
|
1279
|
+
buckets,
|
|
1280
|
+
durationSeconds,
|
|
1281
|
+
endSeconds: range.endSeconds,
|
|
1282
|
+
hasSelection: range.hasSelection,
|
|
1283
|
+
startSeconds: range.startSeconds
|
|
1284
|
+
};
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1037
1287
|
const Filter = 'filter';
|
|
1288
|
+
const EventCategoryFilter = 'eventCategoryFilter';
|
|
1038
1289
|
const ShowEventStreamFinishedEvents = 'showEventStreamFinishedEvents';
|
|
1039
1290
|
const ShowInputEvents = 'showInputEvents';
|
|
1040
1291
|
const ShowResponsePartEvents = 'showResponsePartEvents';
|
|
1041
|
-
|
|
1292
|
+
const UseDevtoolsLayout = 'useDevtoolsLayout';
|
|
1293
|
+
const SelectedEventIndex = 'selectedEventIndex';
|
|
1294
|
+
const CloseDetails = 'closeDetails';
|
|
1295
|
+
const TimelineStartSeconds = 'timelineStartSeconds';
|
|
1296
|
+
const TimelineEndSeconds = 'timelineEndSeconds';
|
|
1297
|
+
const TimelineRangePreset = 'timelineRangePreset';
|
|
1298
|
+
|
|
1299
|
+
const getCurrentEvents = state => {
|
|
1300
|
+
const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
|
|
1301
|
+
return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
|
|
1302
|
+
};
|
|
1303
|
+
const parseTimelineRangePreset = value => {
|
|
1304
|
+
if (!value) {
|
|
1305
|
+
return {
|
|
1306
|
+
timelineEndSeconds: '',
|
|
1307
|
+
timelineStartSeconds: ''
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
const [timelineStartSeconds = '', timelineEndSeconds = ''] = value.split(':', 2);
|
|
1311
|
+
return {
|
|
1312
|
+
timelineEndSeconds,
|
|
1313
|
+
timelineStartSeconds
|
|
1314
|
+
};
|
|
1315
|
+
};
|
|
1316
|
+
const getSelectedEventIndex = state => {
|
|
1317
|
+
const {
|
|
1318
|
+
selectedEventIndex
|
|
1319
|
+
} = state;
|
|
1320
|
+
if (selectedEventIndex === null) {
|
|
1321
|
+
return null;
|
|
1322
|
+
}
|
|
1323
|
+
const filteredEvents = getCurrentEvents(state);
|
|
1324
|
+
const selectedEvent = filteredEvents[selectedEventIndex];
|
|
1325
|
+
if (!selectedEvent) {
|
|
1326
|
+
return null;
|
|
1327
|
+
}
|
|
1328
|
+
const newIndex = filteredEvents.indexOf(selectedEvent);
|
|
1329
|
+
if (newIndex === -1) {
|
|
1330
|
+
return null;
|
|
1331
|
+
}
|
|
1332
|
+
return newIndex;
|
|
1333
|
+
};
|
|
1334
|
+
const getPreservedSelectedEventIndex = (oldState, newState) => {
|
|
1335
|
+
const {
|
|
1336
|
+
selectedEventIndex
|
|
1337
|
+
} = oldState;
|
|
1338
|
+
if (selectedEventIndex === null) {
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
const oldFilteredEvents = getCurrentEvents(oldState);
|
|
1342
|
+
const selectedEvent = oldFilteredEvents[selectedEventIndex];
|
|
1343
|
+
if (!selectedEvent) {
|
|
1344
|
+
return null;
|
|
1345
|
+
}
|
|
1346
|
+
const newFilteredEvents = getCurrentEvents(newState);
|
|
1347
|
+
const newIndex = newFilteredEvents.indexOf(selectedEvent);
|
|
1348
|
+
if (newIndex === -1) {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
return newIndex;
|
|
1352
|
+
};
|
|
1353
|
+
const parseSelectedEventIndex = value => {
|
|
1354
|
+
const parsed = Number.parseInt(value, 10);
|
|
1355
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
1356
|
+
return null;
|
|
1357
|
+
}
|
|
1358
|
+
return parsed;
|
|
1359
|
+
};
|
|
1042
1360
|
const handleInput = (state, name, value, checked) => {
|
|
1043
1361
|
if (name === Filter) {
|
|
1044
|
-
|
|
1362
|
+
const nextState = {
|
|
1045
1363
|
...state,
|
|
1046
1364
|
filterValue: value
|
|
1047
1365
|
};
|
|
1366
|
+
return {
|
|
1367
|
+
...nextState,
|
|
1368
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1369
|
+
};
|
|
1048
1370
|
}
|
|
1049
|
-
if (name ===
|
|
1371
|
+
if (name === EventCategoryFilter) {
|
|
1372
|
+
const nextState = {
|
|
1373
|
+
...state,
|
|
1374
|
+
eventCategoryFilter: value || All
|
|
1375
|
+
};
|
|
1050
1376
|
return {
|
|
1377
|
+
...nextState,
|
|
1378
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
if (name === ShowEventStreamFinishedEvents) {
|
|
1382
|
+
const nextState = {
|
|
1051
1383
|
...state,
|
|
1052
1384
|
showEventStreamFinishedEvents: getBoolean(checked)
|
|
1053
1385
|
};
|
|
1386
|
+
return {
|
|
1387
|
+
...nextState,
|
|
1388
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1389
|
+
};
|
|
1054
1390
|
}
|
|
1055
1391
|
if (name === ShowInputEvents) {
|
|
1056
|
-
|
|
1392
|
+
const nextState = {
|
|
1057
1393
|
...state,
|
|
1058
1394
|
showInputEvents: getBoolean(checked)
|
|
1059
1395
|
};
|
|
1396
|
+
return {
|
|
1397
|
+
...nextState,
|
|
1398
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1399
|
+
};
|
|
1060
1400
|
}
|
|
1061
1401
|
if (name === ShowResponsePartEvents) {
|
|
1062
|
-
|
|
1402
|
+
const nextState = {
|
|
1063
1403
|
...state,
|
|
1064
1404
|
showResponsePartEvents: getBoolean(checked)
|
|
1065
1405
|
};
|
|
1406
|
+
return {
|
|
1407
|
+
...nextState,
|
|
1408
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
if (name === UseDevtoolsLayout) {
|
|
1412
|
+
const useDevtoolsLayout = getBoolean(checked);
|
|
1413
|
+
return {
|
|
1414
|
+
...state,
|
|
1415
|
+
selectedEventIndex: useDevtoolsLayout ? getSelectedEventIndex(state) : null,
|
|
1416
|
+
useDevtoolsLayout
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
if (name === SelectedEventIndex) {
|
|
1420
|
+
return {
|
|
1421
|
+
...state,
|
|
1422
|
+
selectedEventIndex: parseSelectedEventIndex(value)
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
if (name === TimelineStartSeconds) {
|
|
1426
|
+
const nextState = {
|
|
1427
|
+
...state,
|
|
1428
|
+
timelineStartSeconds: value
|
|
1429
|
+
};
|
|
1430
|
+
return {
|
|
1431
|
+
...nextState,
|
|
1432
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
if (name === TimelineEndSeconds) {
|
|
1436
|
+
const nextState = {
|
|
1437
|
+
...state,
|
|
1438
|
+
timelineEndSeconds: value
|
|
1439
|
+
};
|
|
1440
|
+
return {
|
|
1441
|
+
...nextState,
|
|
1442
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
if (name === TimelineRangePreset) {
|
|
1446
|
+
const nextState = {
|
|
1447
|
+
...state,
|
|
1448
|
+
...parseTimelineRangePreset(value)
|
|
1449
|
+
};
|
|
1450
|
+
return {
|
|
1451
|
+
...nextState,
|
|
1452
|
+
selectedEventIndex: getPreservedSelectedEventIndex(state, nextState)
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
if (name === CloseDetails) {
|
|
1456
|
+
return {
|
|
1457
|
+
...state,
|
|
1458
|
+
selectedEventIndex: null
|
|
1459
|
+
};
|
|
1066
1460
|
}
|
|
1067
1461
|
return state;
|
|
1068
1462
|
};
|
|
@@ -1448,6 +1842,7 @@ const loadContent = async state => {
|
|
|
1448
1842
|
errorMessage: getInvalidUriMessage(uri, parsed.code),
|
|
1449
1843
|
events: [],
|
|
1450
1844
|
initial: false,
|
|
1845
|
+
selectedEventIndex: null,
|
|
1451
1846
|
sessionId: ''
|
|
1452
1847
|
};
|
|
1453
1848
|
}
|
|
@@ -1462,6 +1857,7 @@ const loadContent = async state => {
|
|
|
1462
1857
|
errorMessage: getSessionNotFoundMessage(sessionId),
|
|
1463
1858
|
events: [],
|
|
1464
1859
|
initial: false,
|
|
1860
|
+
selectedEventIndex: null,
|
|
1465
1861
|
sessionId
|
|
1466
1862
|
};
|
|
1467
1863
|
}
|
|
@@ -1470,6 +1866,7 @@ const loadContent = async state => {
|
|
|
1470
1866
|
errorMessage: '',
|
|
1471
1867
|
events,
|
|
1472
1868
|
initial: false,
|
|
1869
|
+
selectedEventIndex: null,
|
|
1473
1870
|
sessionId
|
|
1474
1871
|
};
|
|
1475
1872
|
} catch {
|
|
@@ -1478,6 +1875,7 @@ const loadContent = async state => {
|
|
|
1478
1875
|
errorMessage: getFailedToLoadMessage(sessionId),
|
|
1479
1876
|
events: [],
|
|
1480
1877
|
initial: false,
|
|
1878
|
+
selectedEventIndex: null,
|
|
1481
1879
|
sessionId
|
|
1482
1880
|
};
|
|
1483
1881
|
}
|
|
@@ -1489,7 +1887,8 @@ const refresh = async state => {
|
|
|
1489
1887
|
...state,
|
|
1490
1888
|
errorMessage: '',
|
|
1491
1889
|
events,
|
|
1492
|
-
initial: false
|
|
1890
|
+
initial: false,
|
|
1891
|
+
selectedEventIndex: null
|
|
1493
1892
|
};
|
|
1494
1893
|
};
|
|
1495
1894
|
|
|
@@ -1499,6 +1898,7 @@ const TargetValue = 'event.target.value';
|
|
|
1499
1898
|
|
|
1500
1899
|
const SetCss = 'Viewlet.setCss';
|
|
1501
1900
|
const SetDom2 = 'Viewlet.setDom2';
|
|
1901
|
+
const SetPatches = 'Viewlet.setPatches';
|
|
1502
1902
|
|
|
1503
1903
|
const getCss = () => {
|
|
1504
1904
|
return `
|
|
@@ -1511,10 +1911,15 @@ const getCss = () => {
|
|
|
1511
1911
|
gap: 8px;
|
|
1512
1912
|
}
|
|
1513
1913
|
|
|
1914
|
+
.ChatDebugView--devtools {
|
|
1915
|
+
gap: 4px;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1514
1918
|
.ChatDebugViewTop {
|
|
1515
1919
|
display: flex;
|
|
1516
1920
|
align-items: center;
|
|
1517
1921
|
gap: 12px;
|
|
1922
|
+
flex-wrap: wrap;
|
|
1518
1923
|
}
|
|
1519
1924
|
|
|
1520
1925
|
.ChatDebugViewTop .InputBox {
|
|
@@ -1539,107 +1944,406 @@ const getCss = () => {
|
|
|
1539
1944
|
opacity: 0.8;
|
|
1540
1945
|
}
|
|
1541
1946
|
|
|
1947
|
+
.ChatDebugViewQuickFilters {
|
|
1948
|
+
display: flex;
|
|
1949
|
+
gap: 8px;
|
|
1950
|
+
flex-wrap: wrap;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
.ChatDebugViewQuickFilterPill {
|
|
1954
|
+
display: inline-flex;
|
|
1955
|
+
align-items: center;
|
|
1956
|
+
justify-content: center;
|
|
1957
|
+
min-height: 28px;
|
|
1958
|
+
padding: 0 12px;
|
|
1959
|
+
border: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
1960
|
+
border-radius: 999px;
|
|
1961
|
+
background: var(--vscode-editorWidget-background, transparent);
|
|
1962
|
+
cursor: pointer;
|
|
1963
|
+
font-size: 12px;
|
|
1964
|
+
line-height: 1;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
.ChatDebugViewQuickFilterPillSelected {
|
|
1968
|
+
border-color: var(--vscode-focusBorder, #007fd4);
|
|
1969
|
+
background: var(--vscode-list-activeSelectionBackground, rgba(14, 99, 156, 0.35));
|
|
1970
|
+
color: var(--vscode-list-activeSelectionForeground, inherit);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
.ChatDebugViewQuickFilterInput {
|
|
1974
|
+
position: absolute;
|
|
1975
|
+
opacity: 0;
|
|
1976
|
+
pointer-events: none;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1542
1979
|
.ChatDebugViewEvents {
|
|
1980
|
+
display: grid;
|
|
1981
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
1543
1982
|
overflow: auto;
|
|
1983
|
+
min-width: 0;
|
|
1984
|
+
min-height: 0;
|
|
1544
1985
|
scrollbar-width: thin;
|
|
1545
1986
|
scrollbar-color: var(--vscode-scrollbarSlider-background, rgba(121, 121, 121, 0.4)) transparent;
|
|
1546
1987
|
}
|
|
1547
1988
|
|
|
1548
|
-
.ChatDebugViewEvents
|
|
1549
|
-
|
|
1550
|
-
height: 10px;
|
|
1989
|
+
.ChatDebugViewEvents--timeline {
|
|
1990
|
+
grid-template-rows: auto auto minmax(0, 1fr);
|
|
1551
1991
|
}
|
|
1552
1992
|
|
|
1553
|
-
.ChatDebugViewEvents
|
|
1554
|
-
|
|
1993
|
+
.ChatDebugView--devtools .ChatDebugViewEvents {
|
|
1994
|
+
border: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
1995
|
+
border-radius: 6px;
|
|
1996
|
+
margin-bottom: 0;
|
|
1555
1997
|
}
|
|
1556
1998
|
|
|
1557
|
-
.
|
|
1558
|
-
|
|
1559
|
-
border-radius: 999px;
|
|
1560
|
-
border: 2px solid transparent;
|
|
1561
|
-
background-clip: content-box;
|
|
1999
|
+
.ChatDebugViewEventsFullWidth {
|
|
2000
|
+
grid-column: 1 / -1;
|
|
1562
2001
|
}
|
|
1563
2002
|
|
|
1564
|
-
.
|
|
1565
|
-
|
|
2003
|
+
.ChatDebugViewDevtoolsMain {
|
|
2004
|
+
display: grid;
|
|
2005
|
+
grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
|
|
2006
|
+
gap: 8px;
|
|
2007
|
+
min-width: 0;
|
|
2008
|
+
min-height: 0;
|
|
2009
|
+
overflow: hidden;
|
|
1566
2010
|
}
|
|
1567
2011
|
|
|
1568
|
-
.
|
|
1569
|
-
|
|
2012
|
+
.ChatDebugViewTimeline {
|
|
2013
|
+
display: grid;
|
|
2014
|
+
gap: 8px;
|
|
2015
|
+
padding: 10px;
|
|
2016
|
+
border-bottom: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2017
|
+
background: color-mix(in srgb, var(--vscode-editorWidget-background, transparent) 82%, var(--vscode-list-hoverBackground, rgba(90, 93, 94, 0.12)) 18%);
|
|
1570
2018
|
}
|
|
1571
2019
|
|
|
1572
|
-
.
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
2020
|
+
.ChatDebugViewTimelineTop {
|
|
2021
|
+
display: flex;
|
|
2022
|
+
align-items: baseline;
|
|
2023
|
+
justify-content: space-between;
|
|
2024
|
+
gap: 8px;
|
|
2025
|
+
flex-wrap: wrap;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
.ChatDebugViewTimelineTitle {
|
|
1581
2029
|
font-size: 12px;
|
|
1582
|
-
|
|
2030
|
+
font-weight: 600;
|
|
1583
2031
|
}
|
|
1584
2032
|
|
|
1585
|
-
.
|
|
2033
|
+
.ChatDebugViewTimelineSummary {
|
|
2034
|
+
font-size: 12px;
|
|
1586
2035
|
opacity: 0.8;
|
|
1587
2036
|
}
|
|
1588
2037
|
|
|
1589
|
-
.
|
|
1590
|
-
|
|
1591
|
-
|
|
2038
|
+
.ChatDebugViewTimelineControls {
|
|
2039
|
+
display: flex;
|
|
2040
|
+
align-items: center;
|
|
2041
|
+
gap: 8px;
|
|
2042
|
+
flex-wrap: wrap;
|
|
1592
2043
|
}
|
|
1593
2044
|
|
|
1594
|
-
.
|
|
1595
|
-
|
|
2045
|
+
.ChatDebugViewTimelineField {
|
|
2046
|
+
display: inline-flex;
|
|
2047
|
+
align-items: center;
|
|
2048
|
+
gap: 6px;
|
|
2049
|
+
font-size: 12px;
|
|
1596
2050
|
}
|
|
1597
2051
|
|
|
1598
|
-
.
|
|
1599
|
-
|
|
2052
|
+
.ChatDebugViewTimelineInput {
|
|
2053
|
+
width: 84px;
|
|
1600
2054
|
}
|
|
1601
2055
|
|
|
1602
|
-
.
|
|
1603
|
-
|
|
2056
|
+
.ChatDebugViewTimelineReset {
|
|
2057
|
+
display: inline-flex;
|
|
2058
|
+
align-items: center;
|
|
2059
|
+
justify-content: center;
|
|
2060
|
+
min-height: 28px;
|
|
2061
|
+
padding: 0 12px;
|
|
2062
|
+
border: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2063
|
+
border-radius: 999px;
|
|
2064
|
+
cursor: pointer;
|
|
2065
|
+
font-size: 12px;
|
|
1604
2066
|
}
|
|
1605
2067
|
|
|
1606
|
-
.
|
|
1607
|
-
color: var(--vscode-
|
|
2068
|
+
.ChatDebugViewTimelineResetSelected {
|
|
2069
|
+
border-color: var(--vscode-focusBorder, #007fd4);
|
|
2070
|
+
background: var(--vscode-list-activeSelectionBackground, rgba(14, 99, 156, 0.35));
|
|
2071
|
+
color: var(--vscode-list-activeSelectionForeground, inherit);
|
|
1608
2072
|
}
|
|
1609
2073
|
|
|
1610
|
-
.
|
|
1611
|
-
|
|
2074
|
+
.ChatDebugViewTimelineBuckets {
|
|
2075
|
+
display: grid;
|
|
2076
|
+
grid-template-columns: repeat(auto-fit, minmax(10px, 1fr));
|
|
2077
|
+
align-items: end;
|
|
2078
|
+
gap: 4px;
|
|
2079
|
+
min-height: 60px;
|
|
1612
2080
|
}
|
|
1613
2081
|
|
|
1614
|
-
.
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2082
|
+
.ChatDebugViewTimelineBucket {
|
|
2083
|
+
display: flex;
|
|
2084
|
+
align-items: stretch;
|
|
2085
|
+
min-height: 60px;
|
|
2086
|
+
cursor: pointer;
|
|
1618
2087
|
}
|
|
1619
2088
|
|
|
1620
|
-
.
|
|
1621
|
-
|
|
1622
|
-
|
|
2089
|
+
.ChatDebugViewTimelinePresetInput {
|
|
2090
|
+
position: absolute;
|
|
2091
|
+
opacity: 0;
|
|
2092
|
+
pointer-events: none;
|
|
1623
2093
|
}
|
|
1624
2094
|
|
|
1625
|
-
.
|
|
1626
|
-
|
|
1627
|
-
|
|
2095
|
+
.ChatDebugViewTimelineBucketBar {
|
|
2096
|
+
width: 100%;
|
|
2097
|
+
display: flex;
|
|
2098
|
+
flex-direction: column;
|
|
2099
|
+
justify-content: flex-end;
|
|
2100
|
+
gap: 2px;
|
|
2101
|
+
padding: 4px 2px;
|
|
2102
|
+
border: 1px solid transparent;
|
|
2103
|
+
border-radius: 4px;
|
|
2104
|
+
background: color-mix(in srgb, var(--vscode-list-hoverBackground, rgba(90, 93, 94, 0.18)) 68%, transparent 32%);
|
|
1628
2105
|
}
|
|
1629
|
-
`;
|
|
1630
|
-
};
|
|
1631
2106
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
2107
|
+
.ChatDebugViewTimelineBucketSelected .ChatDebugViewTimelineBucketBar,
|
|
2108
|
+
.ChatDebugViewTimelineBucketBarSelected {
|
|
2109
|
+
background: color-mix(in srgb, var(--vscode-charts-blue, #75beff) 72%, transparent 28%);
|
|
2110
|
+
border-color: var(--vscode-focusBorder, #007fd4);
|
|
2111
|
+
}
|
|
1636
2112
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
2113
|
+
.ChatDebugViewTimelineBucketUnit {
|
|
2114
|
+
width: 100%;
|
|
2115
|
+
height: 4px;
|
|
2116
|
+
border-radius: 999px;
|
|
2117
|
+
background: var(--vscode-charts-blue, #75beff);
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
.ChatDebugViewTimelineBucketUnitEmpty {
|
|
2121
|
+
opacity: 0.35;
|
|
2122
|
+
background: var(--vscode-editorWidget-border, #454545);
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
.ChatDebugViewTableHeader,
|
|
2126
|
+
.ChatDebugViewEventRow {
|
|
2127
|
+
display: grid;
|
|
2128
|
+
grid-template-columns: minmax(140px, 1fr) minmax(180px, 1fr) minmax(180px, 1fr) 90px 64px;
|
|
2129
|
+
align-items: center;
|
|
2130
|
+
gap: 8px;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
.ChatDebugViewTableHeader {
|
|
2134
|
+
padding: 8px;
|
|
2135
|
+
border-bottom: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2136
|
+
background: var(--vscode-editorWidget-background, transparent);
|
|
2137
|
+
position: sticky;
|
|
2138
|
+
top: 0;
|
|
2139
|
+
z-index: 1;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
.ChatDebugViewHeaderCell {
|
|
2143
|
+
font-size: 11px;
|
|
2144
|
+
text-transform: uppercase;
|
|
2145
|
+
letter-spacing: 0.04em;
|
|
2146
|
+
opacity: 0.8;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
.ChatDebugViewTableBody {
|
|
2150
|
+
overflow: auto;
|
|
2151
|
+
min-height: 0;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
.ChatDebugViewEventRowLabel {
|
|
2155
|
+
display: block;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
.ChatDebugViewEventRowLabelSelected .ChatDebugViewEventRow,
|
|
2159
|
+
.ChatDebugViewEventRowSelected {
|
|
2160
|
+
background: var(--vscode-list-activeSelectionBackground, rgba(14, 99, 156, 0.35));
|
|
2161
|
+
color: var(--vscode-list-activeSelectionForeground, inherit);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
.ChatDebugViewEventRowInput {
|
|
2165
|
+
position: absolute;
|
|
2166
|
+
opacity: 0;
|
|
2167
|
+
pointer-events: none;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
.ChatDebugViewEventRow {
|
|
2171
|
+
padding: 8px;
|
|
2172
|
+
border-bottom: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2173
|
+
cursor: pointer;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
.ChatDebugViewEventRow:hover {
|
|
2177
|
+
background: var(--vscode-list-hoverBackground, rgba(90, 93, 94, 0.31));
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
.ChatDebugViewCell {
|
|
2181
|
+
overflow: hidden;
|
|
2182
|
+
text-overflow: ellipsis;
|
|
2183
|
+
white-space: nowrap;
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
.ChatDebugViewCellDuration {
|
|
2187
|
+
text-align: right;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
.ChatDebugViewCellStatus {
|
|
2191
|
+
text-align: right;
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
.ChatDebugViewDetails {
|
|
2195
|
+
border: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2196
|
+
border-radius: 6px;
|
|
2197
|
+
overflow: hidden;
|
|
2198
|
+
min-width: 0;
|
|
2199
|
+
min-height: 0;
|
|
2200
|
+
display: grid;
|
|
2201
|
+
grid-template-rows: auto 1fr;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
.ChatDebugViewDetailsTop {
|
|
2205
|
+
display: flex;
|
|
2206
|
+
align-items: center;
|
|
2207
|
+
justify-content: space-between;
|
|
2208
|
+
padding: 8px;
|
|
2209
|
+
border-bottom: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
.ChatDebugViewDetailsTitle {
|
|
2213
|
+
font-size: 12px;
|
|
2214
|
+
font-weight: 600;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
.ChatDebugViewDetailsClose {
|
|
2218
|
+
width: 18px;
|
|
2219
|
+
height: 18px;
|
|
2220
|
+
appearance: none;
|
|
2221
|
+
border: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2222
|
+
border-radius: 4px;
|
|
2223
|
+
cursor: pointer;
|
|
2224
|
+
position: relative;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
.ChatDebugViewDetailsClose::before,
|
|
2228
|
+
.ChatDebugViewDetailsClose::after {
|
|
2229
|
+
content: '';
|
|
2230
|
+
position: absolute;
|
|
2231
|
+
left: 50%;
|
|
2232
|
+
top: 50%;
|
|
2233
|
+
width: 10px;
|
|
2234
|
+
height: 1px;
|
|
2235
|
+
background: currentColor;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
.ChatDebugViewDetailsClose::before {
|
|
2239
|
+
transform: translate(-50%, -50%) rotate(45deg);
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
.ChatDebugViewDetailsClose::after {
|
|
2243
|
+
transform: translate(-50%, -50%) rotate(-45deg);
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
.ChatDebugViewDetailsBody {
|
|
2247
|
+
overflow: auto;
|
|
2248
|
+
padding: 8px;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
.ChatDebugViewEvents::-webkit-scrollbar {
|
|
2252
|
+
width: 10px;
|
|
2253
|
+
height: 10px;
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
.ChatDebugViewEvents::-webkit-scrollbar-track {
|
|
2257
|
+
background: transparent;
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
.ChatDebugViewEvents::-webkit-scrollbar-thumb {
|
|
2261
|
+
background: var(--vscode-scrollbarSlider-background, rgba(121, 121, 121, 0.4));
|
|
2262
|
+
border-radius: 999px;
|
|
2263
|
+
border: 2px solid transparent;
|
|
2264
|
+
background-clip: content-box;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
.ChatDebugViewEvents::-webkit-scrollbar-thumb:hover {
|
|
2268
|
+
background: var(--vscode-scrollbarSlider-hoverBackground, rgba(100, 100, 100, 0.7));
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
.ChatDebugViewEvents::-webkit-scrollbar-thumb:active {
|
|
2272
|
+
background: var(--vscode-scrollbarSlider-activeBackground, rgba(191, 191, 191, 0.4));
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
.ChatDebugViewEvent {
|
|
2276
|
+
margin: 0;
|
|
2277
|
+
padding: 8px;
|
|
2278
|
+
border: 1px solid var(--vscode-editorWidget-border, #454545);
|
|
2279
|
+
border-radius: 6px;
|
|
2280
|
+
margin-bottom: 8px;
|
|
2281
|
+
white-space: pre-wrap;
|
|
2282
|
+
word-break: break-word;
|
|
2283
|
+
font-family: var(--vscode-editor-font-family, monospace);
|
|
2284
|
+
font-size: 12px;
|
|
2285
|
+
user-select: text;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
.ChatDebugViewEmpty {
|
|
2289
|
+
opacity: 0.8;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
.ChatDebugViewError {
|
|
2293
|
+
color: var(--vscode-errorForeground, #f14c4c);
|
|
2294
|
+
white-space: normal;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
.TokenText {
|
|
2298
|
+
color: var(--vscode-editor-foreground, inherit);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
.TokenKey {
|
|
2302
|
+
color: var(--vscode-symbolIcon-propertyForeground, var(--vscode-editor-foreground, inherit));
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
.TokenString {
|
|
2306
|
+
color: var(--vscode-debugTokenExpression-string, var(--vscode-charts-green, #89d185));
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
.TokenNumeric {
|
|
2310
|
+
color: var(--vscode-debugTokenExpression-number, var(--vscode-charts-blue, #75beff));
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
.TokenBoolean {
|
|
2314
|
+
color: var(--vscode-debugTokenExpression-boolean, var(--vscode-charts-yellow, #dcdcaa));
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
.ChatOrderedList{
|
|
2318
|
+
margin:0;
|
|
2319
|
+
padding:0;
|
|
2320
|
+
padding-left:10px;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
.ChatOrderedListItem{
|
|
2324
|
+
margin:0;
|
|
2325
|
+
padding:0;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
.ChatToolCalls{
|
|
2329
|
+
margin:0;
|
|
2330
|
+
padding:0;
|
|
2331
|
+
}
|
|
2332
|
+
`;
|
|
2333
|
+
};
|
|
2334
|
+
|
|
2335
|
+
const renderCss = (oldState, newState) => {
|
|
2336
|
+
const css = getCss();
|
|
2337
|
+
return [SetCss, newState.uid, css];
|
|
2338
|
+
};
|
|
2339
|
+
|
|
2340
|
+
const Div = 4;
|
|
2341
|
+
const Input = 6;
|
|
2342
|
+
const Span = 8;
|
|
2343
|
+
const Text = 12;
|
|
1641
2344
|
const Pre = 51;
|
|
1642
2345
|
const Label = 66;
|
|
2346
|
+
const Reference = 100;
|
|
1643
2347
|
|
|
1644
2348
|
const text = data => {
|
|
1645
2349
|
return {
|
|
@@ -1649,8 +2353,427 @@ const text = data => {
|
|
|
1649
2353
|
};
|
|
1650
2354
|
};
|
|
1651
2355
|
|
|
2356
|
+
const SetText = 1;
|
|
2357
|
+
const Replace = 2;
|
|
2358
|
+
const SetAttribute = 3;
|
|
2359
|
+
const RemoveAttribute = 4;
|
|
2360
|
+
const Add = 6;
|
|
2361
|
+
const NavigateChild = 7;
|
|
2362
|
+
const NavigateParent = 8;
|
|
2363
|
+
const RemoveChild = 9;
|
|
2364
|
+
const NavigateSibling = 10;
|
|
2365
|
+
const SetReferenceNodeUid = 11;
|
|
2366
|
+
|
|
2367
|
+
const isKey = key => {
|
|
2368
|
+
return key !== 'type' && key !== 'childCount';
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2371
|
+
const getKeys = node => {
|
|
2372
|
+
const keys = Object.keys(node).filter(isKey);
|
|
2373
|
+
return keys;
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
const arrayToTree = nodes => {
|
|
2377
|
+
const result = [];
|
|
2378
|
+
let i = 0;
|
|
2379
|
+
while (i < nodes.length) {
|
|
2380
|
+
const node = nodes[i];
|
|
2381
|
+
const {
|
|
2382
|
+
children,
|
|
2383
|
+
nodesConsumed
|
|
2384
|
+
} = getChildrenWithCount(nodes, i + 1, node.childCount || 0);
|
|
2385
|
+
result.push({
|
|
2386
|
+
node,
|
|
2387
|
+
children
|
|
2388
|
+
});
|
|
2389
|
+
i += 1 + nodesConsumed;
|
|
2390
|
+
}
|
|
2391
|
+
return result;
|
|
2392
|
+
};
|
|
2393
|
+
const getChildrenWithCount = (nodes, startIndex, childCount) => {
|
|
2394
|
+
if (childCount === 0) {
|
|
2395
|
+
return {
|
|
2396
|
+
children: [],
|
|
2397
|
+
nodesConsumed: 0
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
const children = [];
|
|
2401
|
+
let i = startIndex;
|
|
2402
|
+
let remaining = childCount;
|
|
2403
|
+
let totalConsumed = 0;
|
|
2404
|
+
while (remaining > 0 && i < nodes.length) {
|
|
2405
|
+
const node = nodes[i];
|
|
2406
|
+
const nodeChildCount = node.childCount || 0;
|
|
2407
|
+
const {
|
|
2408
|
+
children: nodeChildren,
|
|
2409
|
+
nodesConsumed
|
|
2410
|
+
} = getChildrenWithCount(nodes, i + 1, nodeChildCount);
|
|
2411
|
+
children.push({
|
|
2412
|
+
node,
|
|
2413
|
+
children: nodeChildren
|
|
2414
|
+
});
|
|
2415
|
+
const nodeSize = 1 + nodesConsumed;
|
|
2416
|
+
i += nodeSize;
|
|
2417
|
+
totalConsumed += nodeSize;
|
|
2418
|
+
remaining--;
|
|
2419
|
+
}
|
|
2420
|
+
return {
|
|
2421
|
+
children,
|
|
2422
|
+
nodesConsumed: totalConsumed
|
|
2423
|
+
};
|
|
2424
|
+
};
|
|
2425
|
+
|
|
2426
|
+
const compareNodes = (oldNode, newNode) => {
|
|
2427
|
+
const patches = [];
|
|
2428
|
+
// Check if node type changed - return null to signal incompatible nodes
|
|
2429
|
+
// (caller should handle this with a Replace operation)
|
|
2430
|
+
if (oldNode.type !== newNode.type) {
|
|
2431
|
+
return null;
|
|
2432
|
+
}
|
|
2433
|
+
// Handle reference nodes - special handling for uid changes
|
|
2434
|
+
if (oldNode.type === Reference) {
|
|
2435
|
+
if (oldNode.uid !== newNode.uid) {
|
|
2436
|
+
patches.push({
|
|
2437
|
+
type: SetReferenceNodeUid,
|
|
2438
|
+
uid: newNode.uid
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
return patches;
|
|
2442
|
+
}
|
|
2443
|
+
// Handle text nodes
|
|
2444
|
+
if (oldNode.type === Text && newNode.type === Text) {
|
|
2445
|
+
if (oldNode.text !== newNode.text) {
|
|
2446
|
+
patches.push({
|
|
2447
|
+
type: SetText,
|
|
2448
|
+
value: newNode.text
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2451
|
+
return patches;
|
|
2452
|
+
}
|
|
2453
|
+
// Compare attributes
|
|
2454
|
+
const oldKeys = getKeys(oldNode);
|
|
2455
|
+
const newKeys = getKeys(newNode);
|
|
2456
|
+
// Check for attribute changes
|
|
2457
|
+
for (const key of newKeys) {
|
|
2458
|
+
if (oldNode[key] !== newNode[key]) {
|
|
2459
|
+
patches.push({
|
|
2460
|
+
type: SetAttribute,
|
|
2461
|
+
key,
|
|
2462
|
+
value: newNode[key]
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
// Check for removed attributes
|
|
2467
|
+
for (const key of oldKeys) {
|
|
2468
|
+
if (!(key in newNode)) {
|
|
2469
|
+
patches.push({
|
|
2470
|
+
type: RemoveAttribute,
|
|
2471
|
+
key
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
return patches;
|
|
2476
|
+
};
|
|
2477
|
+
|
|
2478
|
+
const treeToArray = node => {
|
|
2479
|
+
const result = [node.node];
|
|
2480
|
+
for (const child of node.children) {
|
|
2481
|
+
result.push(...treeToArray(child));
|
|
2482
|
+
}
|
|
2483
|
+
return result;
|
|
2484
|
+
};
|
|
2485
|
+
|
|
2486
|
+
const diffChildren = (oldChildren, newChildren, patches) => {
|
|
2487
|
+
const maxLength = Math.max(oldChildren.length, newChildren.length);
|
|
2488
|
+
// Track where we are: -1 means at parent, >= 0 means at child index
|
|
2489
|
+
let currentChildIndex = -1;
|
|
2490
|
+
// Collect indices of children to remove (we'll add these patches at the end in reverse order)
|
|
2491
|
+
const indicesToRemove = [];
|
|
2492
|
+
for (let i = 0; i < maxLength; i++) {
|
|
2493
|
+
const oldNode = oldChildren[i];
|
|
2494
|
+
const newNode = newChildren[i];
|
|
2495
|
+
if (!oldNode && !newNode) {
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
if (!oldNode) {
|
|
2499
|
+
// Add new node - we should be at the parent
|
|
2500
|
+
if (currentChildIndex >= 0) {
|
|
2501
|
+
// Navigate back to parent
|
|
2502
|
+
patches.push({
|
|
2503
|
+
type: NavigateParent
|
|
2504
|
+
});
|
|
2505
|
+
currentChildIndex = -1;
|
|
2506
|
+
}
|
|
2507
|
+
// Flatten the entire subtree so renderInternal can handle it
|
|
2508
|
+
const flatNodes = treeToArray(newNode);
|
|
2509
|
+
patches.push({
|
|
2510
|
+
type: Add,
|
|
2511
|
+
nodes: flatNodes
|
|
2512
|
+
});
|
|
2513
|
+
} else if (newNode) {
|
|
2514
|
+
// Compare nodes to see if we need any patches
|
|
2515
|
+
const nodePatches = compareNodes(oldNode.node, newNode.node);
|
|
2516
|
+
// If nodePatches is null, the node types are incompatible - need to replace
|
|
2517
|
+
if (nodePatches === null) {
|
|
2518
|
+
// Navigate to this child
|
|
2519
|
+
if (currentChildIndex === -1) {
|
|
2520
|
+
patches.push({
|
|
2521
|
+
type: NavigateChild,
|
|
2522
|
+
index: i
|
|
2523
|
+
});
|
|
2524
|
+
currentChildIndex = i;
|
|
2525
|
+
} else if (currentChildIndex !== i) {
|
|
2526
|
+
patches.push({
|
|
2527
|
+
type: NavigateSibling,
|
|
2528
|
+
index: i
|
|
2529
|
+
});
|
|
2530
|
+
currentChildIndex = i;
|
|
2531
|
+
}
|
|
2532
|
+
// Replace the entire subtree
|
|
2533
|
+
const flatNodes = treeToArray(newNode);
|
|
2534
|
+
patches.push({
|
|
2535
|
+
type: Replace,
|
|
2536
|
+
nodes: flatNodes
|
|
2537
|
+
});
|
|
2538
|
+
// After replace, we're at the new element (same position)
|
|
2539
|
+
continue;
|
|
2540
|
+
}
|
|
2541
|
+
// Check if we need to recurse into children
|
|
2542
|
+
const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
|
|
2543
|
+
// Only navigate to this element if we need to do something
|
|
2544
|
+
if (nodePatches.length > 0 || hasChildrenToCompare) {
|
|
2545
|
+
// Navigate to this child if not already there
|
|
2546
|
+
if (currentChildIndex === -1) {
|
|
2547
|
+
patches.push({
|
|
2548
|
+
type: NavigateChild,
|
|
2549
|
+
index: i
|
|
2550
|
+
});
|
|
2551
|
+
currentChildIndex = i;
|
|
2552
|
+
} else if (currentChildIndex !== i) {
|
|
2553
|
+
patches.push({
|
|
2554
|
+
type: NavigateSibling,
|
|
2555
|
+
index: i
|
|
2556
|
+
});
|
|
2557
|
+
currentChildIndex = i;
|
|
2558
|
+
}
|
|
2559
|
+
// Apply node patches (these apply to the current element, not children)
|
|
2560
|
+
if (nodePatches.length > 0) {
|
|
2561
|
+
patches.push(...nodePatches);
|
|
2562
|
+
}
|
|
2563
|
+
// Compare children recursively
|
|
2564
|
+
if (hasChildrenToCompare) {
|
|
2565
|
+
diffChildren(oldNode.children, newNode.children, patches);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
} else {
|
|
2569
|
+
// Remove old node - collect the index for later removal
|
|
2570
|
+
indicesToRemove.push(i);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
// Navigate back to parent if we ended at a child
|
|
2574
|
+
if (currentChildIndex >= 0) {
|
|
2575
|
+
patches.push({
|
|
2576
|
+
type: NavigateParent
|
|
2577
|
+
});
|
|
2578
|
+
currentChildIndex = -1;
|
|
2579
|
+
}
|
|
2580
|
+
// Add remove patches in reverse order (highest index first)
|
|
2581
|
+
// This ensures indices remain valid as we remove
|
|
2582
|
+
for (let j = indicesToRemove.length - 1; j >= 0; j--) {
|
|
2583
|
+
patches.push({
|
|
2584
|
+
type: RemoveChild,
|
|
2585
|
+
index: indicesToRemove[j]
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
const diffTrees = (oldTree, newTree, patches, path) => {
|
|
2590
|
+
// At the root level (path.length === 0), we're already AT the element
|
|
2591
|
+
// So we compare the root node directly, then compare its children
|
|
2592
|
+
if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
|
|
2593
|
+
const oldNode = oldTree[0];
|
|
2594
|
+
const newNode = newTree[0];
|
|
2595
|
+
// Compare root nodes
|
|
2596
|
+
const nodePatches = compareNodes(oldNode.node, newNode.node);
|
|
2597
|
+
// If nodePatches is null, the root node types are incompatible - need to replace
|
|
2598
|
+
if (nodePatches === null) {
|
|
2599
|
+
const flatNodes = treeToArray(newNode);
|
|
2600
|
+
patches.push({
|
|
2601
|
+
type: Replace,
|
|
2602
|
+
nodes: flatNodes
|
|
2603
|
+
});
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
if (nodePatches.length > 0) {
|
|
2607
|
+
patches.push(...nodePatches);
|
|
2608
|
+
}
|
|
2609
|
+
// Compare children
|
|
2610
|
+
if (oldNode.children.length > 0 || newNode.children.length > 0) {
|
|
2611
|
+
diffChildren(oldNode.children, newNode.children, patches);
|
|
2612
|
+
}
|
|
2613
|
+
} else {
|
|
2614
|
+
// Non-root level or multiple root elements - use the regular comparison
|
|
2615
|
+
diffChildren(oldTree, newTree, patches);
|
|
2616
|
+
}
|
|
2617
|
+
};
|
|
2618
|
+
|
|
2619
|
+
const removeTrailingNavigationPatches = patches => {
|
|
2620
|
+
// Find the last non-navigation patch
|
|
2621
|
+
let lastNonNavigationIndex = -1;
|
|
2622
|
+
for (let i = patches.length - 1; i >= 0; i--) {
|
|
2623
|
+
const patch = patches[i];
|
|
2624
|
+
if (patch.type !== NavigateChild && patch.type !== NavigateParent && patch.type !== NavigateSibling) {
|
|
2625
|
+
lastNonNavigationIndex = i;
|
|
2626
|
+
break;
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
// Return patches up to and including the last non-navigation patch
|
|
2630
|
+
return lastNonNavigationIndex === -1 ? [] : patches.slice(0, lastNonNavigationIndex + 1);
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
const diffTree = (oldNodes, newNodes) => {
|
|
2634
|
+
// Step 1: Convert flat arrays to tree structures
|
|
2635
|
+
const oldTree = arrayToTree(oldNodes);
|
|
2636
|
+
const newTree = arrayToTree(newNodes);
|
|
2637
|
+
// Step 3: Compare the trees
|
|
2638
|
+
const patches = [];
|
|
2639
|
+
diffTrees(oldTree, newTree, patches, []);
|
|
2640
|
+
// Remove trailing navigation patches since they serve no purpose
|
|
2641
|
+
return removeTrailingNavigationPatches(patches);
|
|
2642
|
+
};
|
|
2643
|
+
|
|
1652
2644
|
const HandleInput = 4;
|
|
1653
2645
|
const HandleFilterInput = 5;
|
|
2646
|
+
const HandleSimpleInput = 6;
|
|
2647
|
+
|
|
2648
|
+
const getDurationText = event => {
|
|
2649
|
+
const explicitDuration = event.durationMs ?? event.duration;
|
|
2650
|
+
if (typeof explicitDuration === 'number' && Number.isFinite(explicitDuration)) {
|
|
2651
|
+
return `${explicitDuration}ms`;
|
|
2652
|
+
}
|
|
2653
|
+
const start = toTimeNumber(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
|
|
2654
|
+
const end = toTimeNumber(event.ended ?? event.endTime ?? event.endTimestamp ?? event.timestamp);
|
|
2655
|
+
if (start === undefined || end === undefined || end < start) {
|
|
2656
|
+
return '-';
|
|
2657
|
+
}
|
|
2658
|
+
return `${end - start}ms`;
|
|
2659
|
+
};
|
|
2660
|
+
|
|
2661
|
+
const timestampFormatter = new Intl.DateTimeFormat('en-US', {
|
|
2662
|
+
day: '2-digit',
|
|
2663
|
+
fractionalSecondDigits: 3,
|
|
2664
|
+
hour: '2-digit',
|
|
2665
|
+
hourCycle: 'h23',
|
|
2666
|
+
minute: '2-digit',
|
|
2667
|
+
month: 'short',
|
|
2668
|
+
second: '2-digit',
|
|
2669
|
+
timeZone: 'UTC',
|
|
2670
|
+
year: 'numeric'
|
|
2671
|
+
});
|
|
2672
|
+
const formatTimestamp = date => {
|
|
2673
|
+
return `${timestampFormatter.format(date)} UTC`;
|
|
2674
|
+
};
|
|
2675
|
+
|
|
2676
|
+
const getTimestampText = value => {
|
|
2677
|
+
if (typeof value === 'string') {
|
|
2678
|
+
const timestamp = Date.parse(value);
|
|
2679
|
+
if (!Number.isNaN(timestamp)) {
|
|
2680
|
+
return formatTimestamp(new Date(timestamp));
|
|
2681
|
+
}
|
|
2682
|
+
return value;
|
|
2683
|
+
}
|
|
2684
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
2685
|
+
return formatTimestamp(new Date(value));
|
|
2686
|
+
}
|
|
2687
|
+
return '-';
|
|
2688
|
+
};
|
|
2689
|
+
|
|
2690
|
+
const getEndText = event => {
|
|
2691
|
+
return getTimestampText(event.ended ?? event.endTime ?? event.endTimestamp ?? event.timestamp);
|
|
2692
|
+
};
|
|
2693
|
+
|
|
2694
|
+
const getStartText = event => {
|
|
2695
|
+
return getTimestampText(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
const hasErrorStatus = event => {
|
|
2699
|
+
if (event.type === 'error') {
|
|
2700
|
+
return true;
|
|
2701
|
+
}
|
|
2702
|
+
if (event.success === false || event.ok === false) {
|
|
2703
|
+
return true;
|
|
2704
|
+
}
|
|
2705
|
+
const {
|
|
2706
|
+
status
|
|
2707
|
+
} = event;
|
|
2708
|
+
if (typeof status === 'number' && status >= 400) {
|
|
2709
|
+
return true;
|
|
2710
|
+
}
|
|
2711
|
+
if (typeof status === 'string') {
|
|
2712
|
+
const parsedStatus = Number(status);
|
|
2713
|
+
if (Number.isFinite(parsedStatus) && parsedStatus >= 400) {
|
|
2714
|
+
return true;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
return typeof event.error === 'string' || typeof event.errorMessage === 'string' || typeof event.exception === 'string';
|
|
2718
|
+
};
|
|
2719
|
+
|
|
2720
|
+
const getStatusText = event => {
|
|
2721
|
+
return hasErrorStatus(event) ? '400' : '200';
|
|
2722
|
+
};
|
|
2723
|
+
|
|
2724
|
+
const getDevtoolsRows = (events, selectedEventIndex) => {
|
|
2725
|
+
if (events.length === 0) {
|
|
2726
|
+
return [{
|
|
2727
|
+
childCount: 1,
|
|
2728
|
+
className: 'ChatDebugViewEmpty',
|
|
2729
|
+
type: Div
|
|
2730
|
+
}, text('No events')];
|
|
2731
|
+
}
|
|
2732
|
+
const rows = [];
|
|
2733
|
+
for (let i = 0; i < events.length; i++) {
|
|
2734
|
+
const event = events[i];
|
|
2735
|
+
const isSelected = selectedEventIndex === i;
|
|
2736
|
+
rows.push({
|
|
2737
|
+
childCount: 2,
|
|
2738
|
+
className: `ChatDebugViewEventRowLabel${isSelected ? ' ChatDebugViewEventRowLabelSelected' : ''}`,
|
|
2739
|
+
type: Label
|
|
2740
|
+
}, {
|
|
2741
|
+
checked: isSelected,
|
|
2742
|
+
childCount: 0,
|
|
2743
|
+
className: 'ChatDebugViewEventRowInput',
|
|
2744
|
+
inputType: 'radio',
|
|
2745
|
+
name: SelectedEventIndex,
|
|
2746
|
+
onChange: HandleSimpleInput,
|
|
2747
|
+
type: Input,
|
|
2748
|
+
value: String(i)
|
|
2749
|
+
}, {
|
|
2750
|
+
childCount: 5,
|
|
2751
|
+
className: `ChatDebugViewEventRow${isSelected ? ' ChatDebugViewEventRowSelected' : ''}`,
|
|
2752
|
+
type: Div
|
|
2753
|
+
}, {
|
|
2754
|
+
childCount: 1,
|
|
2755
|
+
className: 'ChatDebugViewCell ChatDebugViewCellType',
|
|
2756
|
+
type: Div
|
|
2757
|
+
}, text(event.type), {
|
|
2758
|
+
childCount: 1,
|
|
2759
|
+
className: 'ChatDebugViewCell',
|
|
2760
|
+
type: Div
|
|
2761
|
+
}, text(getStartText(event)), {
|
|
2762
|
+
childCount: 1,
|
|
2763
|
+
className: 'ChatDebugViewCell',
|
|
2764
|
+
type: Div
|
|
2765
|
+
}, text(getEndText(event)), {
|
|
2766
|
+
childCount: 1,
|
|
2767
|
+
className: 'ChatDebugViewCell ChatDebugViewCellDuration',
|
|
2768
|
+
type: Div
|
|
2769
|
+
}, text(getDurationText(event)), {
|
|
2770
|
+
childCount: 1,
|
|
2771
|
+
className: 'ChatDebugViewCell ChatDebugViewCellStatus',
|
|
2772
|
+
type: Div
|
|
2773
|
+
}, text(getStatusText(event)));
|
|
2774
|
+
}
|
|
2775
|
+
return rows;
|
|
2776
|
+
};
|
|
1654
2777
|
|
|
1655
2778
|
const numberRegex = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/;
|
|
1656
2779
|
const whitespaceRegex = /\s/u;
|
|
@@ -1755,7 +2878,247 @@ const getEventNode = event => {
|
|
|
1755
2878
|
}, ...tokenNodes];
|
|
1756
2879
|
};
|
|
1757
2880
|
|
|
1758
|
-
const
|
|
2881
|
+
const trailingZeroFractionRegex = /\.0+$/;
|
|
2882
|
+
const trailingFractionZeroRegex = /(\.\d*?)0+$/;
|
|
2883
|
+
const formatTimelinePresetValue = value => {
|
|
2884
|
+
return value.toFixed(3).replace(trailingZeroFractionRegex, '').replace(trailingFractionZeroRegex, '$1');
|
|
2885
|
+
};
|
|
2886
|
+
|
|
2887
|
+
const formatTimelineSeconds = value => {
|
|
2888
|
+
if (Number.isInteger(value)) {
|
|
2889
|
+
return `${value}s`;
|
|
2890
|
+
}
|
|
2891
|
+
return `${Number(value.toFixed(1))}s`;
|
|
2892
|
+
};
|
|
2893
|
+
const getTimelineSummary = (timelineEvents, timelineStartSeconds, timelineEndSeconds) => {
|
|
2894
|
+
const timelineInfo = getTimelineInfo(timelineEvents, timelineStartSeconds, timelineEndSeconds);
|
|
2895
|
+
if (timelineInfo.hasSelection && timelineInfo.startSeconds !== null && timelineInfo.endSeconds !== null) {
|
|
2896
|
+
return `Window ${formatTimelineSeconds(timelineInfo.startSeconds)}-${formatTimelineSeconds(timelineInfo.endSeconds)} of ${formatTimelineSeconds(timelineInfo.durationSeconds)}`;
|
|
2897
|
+
}
|
|
2898
|
+
return `Window 0s-${formatTimelineSeconds(timelineInfo.durationSeconds)} of ${formatTimelineSeconds(timelineInfo.durationSeconds)}`;
|
|
2899
|
+
};
|
|
2900
|
+
|
|
2901
|
+
const getTimelineNodes = (timelineEvents, timelineStartSeconds, timelineEndSeconds) => {
|
|
2902
|
+
const timelineInfo = getTimelineInfo(timelineEvents, timelineStartSeconds, timelineEndSeconds);
|
|
2903
|
+
if (timelineInfo.buckets.length === 0) {
|
|
2904
|
+
return [];
|
|
2905
|
+
}
|
|
2906
|
+
return [{
|
|
2907
|
+
childCount: 3,
|
|
2908
|
+
className: 'ChatDebugViewTimeline',
|
|
2909
|
+
type: Div
|
|
2910
|
+
}, {
|
|
2911
|
+
childCount: 2,
|
|
2912
|
+
className: 'ChatDebugViewTimelineTop',
|
|
2913
|
+
type: Div
|
|
2914
|
+
}, {
|
|
2915
|
+
childCount: 1,
|
|
2916
|
+
className: 'ChatDebugViewTimelineTitle',
|
|
2917
|
+
type: Div
|
|
2918
|
+
}, text('Timeline'), {
|
|
2919
|
+
childCount: 1,
|
|
2920
|
+
className: 'ChatDebugViewTimelineSummary',
|
|
2921
|
+
type: Div
|
|
2922
|
+
}, text(getTimelineSummary(timelineEvents, timelineStartSeconds, timelineEndSeconds)), {
|
|
2923
|
+
childCount: 3,
|
|
2924
|
+
className: 'ChatDebugViewTimelineControls',
|
|
2925
|
+
type: Div
|
|
2926
|
+
}, {
|
|
2927
|
+
childCount: 2,
|
|
2928
|
+
className: 'ChatDebugViewTimelineField',
|
|
2929
|
+
type: Label
|
|
2930
|
+
}, text('From'), {
|
|
2931
|
+
childCount: 0,
|
|
2932
|
+
className: 'InputBox ChatDebugViewTimelineInput',
|
|
2933
|
+
inputType: 'number',
|
|
2934
|
+
name: TimelineStartSeconds,
|
|
2935
|
+
onInput: HandleFilterInput,
|
|
2936
|
+
placeholder: '0',
|
|
2937
|
+
type: Input,
|
|
2938
|
+
value: timelineStartSeconds
|
|
2939
|
+
}, {
|
|
2940
|
+
childCount: 2,
|
|
2941
|
+
className: 'ChatDebugViewTimelineField',
|
|
2942
|
+
type: Label
|
|
2943
|
+
}, text('To'), {
|
|
2944
|
+
childCount: 0,
|
|
2945
|
+
className: 'InputBox ChatDebugViewTimelineInput',
|
|
2946
|
+
inputType: 'number',
|
|
2947
|
+
name: TimelineEndSeconds,
|
|
2948
|
+
onInput: HandleFilterInput,
|
|
2949
|
+
placeholder: formatTimelinePresetValue(timelineInfo.durationSeconds),
|
|
2950
|
+
type: Input,
|
|
2951
|
+
value: timelineEndSeconds
|
|
2952
|
+
}, {
|
|
2953
|
+
childCount: 2,
|
|
2954
|
+
className: `ChatDebugViewTimelineReset${timelineInfo.hasSelection ? '' : ' ChatDebugViewTimelineResetSelected'}`,
|
|
2955
|
+
type: Label
|
|
2956
|
+
}, {
|
|
2957
|
+
checked: !timelineInfo.hasSelection,
|
|
2958
|
+
childCount: 0,
|
|
2959
|
+
className: 'ChatDebugViewTimelinePresetInput',
|
|
2960
|
+
inputType: 'radio',
|
|
2961
|
+
name: TimelineRangePreset,
|
|
2962
|
+
onChange: HandleSimpleInput,
|
|
2963
|
+
type: Input,
|
|
2964
|
+
value: ''
|
|
2965
|
+
}, text('All'), {
|
|
2966
|
+
childCount: timelineInfo.buckets.length,
|
|
2967
|
+
className: 'ChatDebugViewTimelineBuckets',
|
|
2968
|
+
type: Div
|
|
2969
|
+
}, ...timelineInfo.buckets.flatMap(bucket => {
|
|
2970
|
+
const presetValue = `${formatTimelinePresetValue(bucket.startSeconds)}:${formatTimelinePresetValue(bucket.endSeconds)}`;
|
|
2971
|
+
return [{
|
|
2972
|
+
childCount: 2,
|
|
2973
|
+
className: `ChatDebugViewTimelineBucket${bucket.isSelected ? ' ChatDebugViewTimelineBucketSelected' : ''}`,
|
|
2974
|
+
type: Label
|
|
2975
|
+
}, {
|
|
2976
|
+
checked: false,
|
|
2977
|
+
childCount: 0,
|
|
2978
|
+
className: 'ChatDebugViewTimelinePresetInput',
|
|
2979
|
+
inputType: 'radio',
|
|
2980
|
+
name: TimelineRangePreset,
|
|
2981
|
+
onChange: HandleSimpleInput,
|
|
2982
|
+
type: Input,
|
|
2983
|
+
value: presetValue
|
|
2984
|
+
}, {
|
|
2985
|
+
childCount: bucket.unitCount === 0 ? 1 : bucket.unitCount,
|
|
2986
|
+
className: `ChatDebugViewTimelineBucketBar${bucket.isSelected ? ' ChatDebugViewTimelineBucketBarSelected' : ''}`,
|
|
2987
|
+
type: Div
|
|
2988
|
+
}, ...(bucket.unitCount === 0 ? [{
|
|
2989
|
+
childCount: 0,
|
|
2990
|
+
className: 'ChatDebugViewTimelineBucketUnit ChatDebugViewTimelineBucketUnitEmpty',
|
|
2991
|
+
type: Div
|
|
2992
|
+
}] : Array.from({
|
|
2993
|
+
length: bucket.unitCount
|
|
2994
|
+
}).fill({
|
|
2995
|
+
childCount: 0,
|
|
2996
|
+
className: 'ChatDebugViewTimelineBucketUnit',
|
|
2997
|
+
type: Div
|
|
2998
|
+
}))];
|
|
2999
|
+
})];
|
|
3000
|
+
};
|
|
3001
|
+
|
|
3002
|
+
const getDevtoolsDom = (events, selectedEventIndex, timelineEvents, timelineStartSeconds, timelineEndSeconds) => {
|
|
3003
|
+
const rowNodes = getDevtoolsRows(events, selectedEventIndex);
|
|
3004
|
+
const timelineNodes = getTimelineNodes(timelineEvents, timelineStartSeconds, timelineEndSeconds);
|
|
3005
|
+
const selectedEvent = selectedEventIndex === null ? undefined : events[selectedEventIndex];
|
|
3006
|
+
const selectedEventNodes = selectedEvent ? getEventNode(selectedEvent) : [];
|
|
3007
|
+
const hasSelectedEvent = selectedEventNodes.length > 0;
|
|
3008
|
+
const eventsClassName = `${hasSelectedEvent ? 'ChatDebugViewEvents' : 'ChatDebugViewEvents ChatDebugViewEventsFullWidth'}${timelineNodes.length > 0 ? ' ChatDebugViewEvents--timeline' : ''}`;
|
|
3009
|
+
const detailsNodes = hasSelectedEvent ? [{
|
|
3010
|
+
childCount: 2,
|
|
3011
|
+
className: 'ChatDebugViewDetails',
|
|
3012
|
+
type: Div
|
|
3013
|
+
}, {
|
|
3014
|
+
childCount: 2,
|
|
3015
|
+
className: 'ChatDebugViewDetailsTop',
|
|
3016
|
+
type: Div
|
|
3017
|
+
}, {
|
|
3018
|
+
childCount: 1,
|
|
3019
|
+
className: 'ChatDebugViewDetailsTitle',
|
|
3020
|
+
type: Div
|
|
3021
|
+
}, text('Details'), {
|
|
3022
|
+
childCount: 0,
|
|
3023
|
+
className: 'ChatDebugViewDetailsClose',
|
|
3024
|
+
inputType: 'checkbox',
|
|
3025
|
+
name: CloseDetails,
|
|
3026
|
+
onChange: HandleSimpleInput,
|
|
3027
|
+
type: Input,
|
|
3028
|
+
value: 'close'
|
|
3029
|
+
}, {
|
|
3030
|
+
childCount: selectedEventNodes.length,
|
|
3031
|
+
className: 'ChatDebugViewDetailsBody',
|
|
3032
|
+
type: Div
|
|
3033
|
+
}, ...selectedEventNodes] : [];
|
|
3034
|
+
return [{
|
|
3035
|
+
childCount: hasSelectedEvent ? 2 : 1,
|
|
3036
|
+
className: 'ChatDebugViewDevtoolsMain',
|
|
3037
|
+
type: Div
|
|
3038
|
+
}, {
|
|
3039
|
+
childCount: timelineNodes.length > 0 ? 3 : 2,
|
|
3040
|
+
className: eventsClassName,
|
|
3041
|
+
type: Div
|
|
3042
|
+
}, ...timelineNodes, {
|
|
3043
|
+
childCount: 5,
|
|
3044
|
+
className: 'ChatDebugViewTableHeader',
|
|
3045
|
+
type: Div
|
|
3046
|
+
}, {
|
|
3047
|
+
childCount: 1,
|
|
3048
|
+
className: 'ChatDebugViewHeaderCell ChatDebugViewCellType',
|
|
3049
|
+
type: Div
|
|
3050
|
+
}, text('Type'), {
|
|
3051
|
+
childCount: 1,
|
|
3052
|
+
className: 'ChatDebugViewHeaderCell',
|
|
3053
|
+
type: Div
|
|
3054
|
+
}, text('Started'), {
|
|
3055
|
+
childCount: 1,
|
|
3056
|
+
className: 'ChatDebugViewHeaderCell',
|
|
3057
|
+
type: Div
|
|
3058
|
+
}, text('Ended'), {
|
|
3059
|
+
childCount: 1,
|
|
3060
|
+
className: 'ChatDebugViewHeaderCell ChatDebugViewCellDuration',
|
|
3061
|
+
type: Div
|
|
3062
|
+
}, text('Duration'), {
|
|
3063
|
+
childCount: 1,
|
|
3064
|
+
className: 'ChatDebugViewHeaderCell ChatDebugViewCellStatus',
|
|
3065
|
+
type: Div
|
|
3066
|
+
}, text('Status'), {
|
|
3067
|
+
childCount: rowNodes.length === 0 ? 1 : rowNodes.length,
|
|
3068
|
+
className: 'ChatDebugViewTableBody',
|
|
3069
|
+
type: Div
|
|
3070
|
+
}, ...rowNodes, ...detailsNodes];
|
|
3071
|
+
};
|
|
3072
|
+
|
|
3073
|
+
const getLegacyEventsDom = (errorMessage, emptyMessage, eventNodes) => {
|
|
3074
|
+
return [{
|
|
3075
|
+
childCount: eventNodes.length === 0 ? 1 : eventNodes.length,
|
|
3076
|
+
className: 'ChatDebugViewEvents',
|
|
3077
|
+
type: Div
|
|
3078
|
+
}, ...(eventNodes.length === 0 ? [{
|
|
3079
|
+
childCount: 1,
|
|
3080
|
+
className: errorMessage ? 'ChatDebugViewError' : 'ChatDebugViewEmpty',
|
|
3081
|
+
type: Div
|
|
3082
|
+
}, text(errorMessage || emptyMessage)] : eventNodes)];
|
|
3083
|
+
};
|
|
3084
|
+
const getQuickFilterNodes = eventCategoryFilter => {
|
|
3085
|
+
return [{
|
|
3086
|
+
childCount: options.length,
|
|
3087
|
+
className: 'ChatDebugViewQuickFilters',
|
|
3088
|
+
type: Div
|
|
3089
|
+
}, ...options.flatMap(option => {
|
|
3090
|
+
const isSelected = option.value === eventCategoryFilter;
|
|
3091
|
+
return [{
|
|
3092
|
+
childCount: 2,
|
|
3093
|
+
className: `ChatDebugViewQuickFilterPill${isSelected ? ' ChatDebugViewQuickFilterPillSelected' : ''}`,
|
|
3094
|
+
type: Label
|
|
3095
|
+
}, {
|
|
3096
|
+
checked: isSelected,
|
|
3097
|
+
childCount: 0,
|
|
3098
|
+
className: 'ChatDebugViewQuickFilterInput',
|
|
3099
|
+
inputType: 'radio',
|
|
3100
|
+
name: EventCategoryFilter,
|
|
3101
|
+
onChange: HandleInput,
|
|
3102
|
+
type: Input,
|
|
3103
|
+
value: option.value
|
|
3104
|
+
}, text(option.label)];
|
|
3105
|
+
})];
|
|
3106
|
+
};
|
|
3107
|
+
const getTimelineFilterDescription = (timelineStartSeconds, timelineEndSeconds) => {
|
|
3108
|
+
const trimmedStart = timelineStartSeconds.trim();
|
|
3109
|
+
const trimmedEnd = timelineEndSeconds.trim();
|
|
3110
|
+
if (trimmedStart && trimmedEnd) {
|
|
3111
|
+
return `${trimmedStart}s-${trimmedEnd}s`;
|
|
3112
|
+
}
|
|
3113
|
+
if (trimmedStart) {
|
|
3114
|
+
return `from ${trimmedStart}s`;
|
|
3115
|
+
}
|
|
3116
|
+
if (trimmedEnd) {
|
|
3117
|
+
return `to ${trimmedEnd}s`;
|
|
3118
|
+
}
|
|
3119
|
+
return '';
|
|
3120
|
+
};
|
|
3121
|
+
const getChatDebugViewDom = (errorMessage, filterValue, eventCategoryFilter, showEventStreamFinishedEvents, showInputEvents, showResponsePartEvents, useDevtoolsLayout, selectedEventIndex, timelineStartSeconds, timelineEndSeconds, timelineEvents, events) => {
|
|
1759
3122
|
if (errorMessage) {
|
|
1760
3123
|
return [{
|
|
1761
3124
|
childCount: 1,
|
|
@@ -1769,13 +3132,29 @@ const getChatDebugViewDom = (errorMessage, filterValue, showEventStreamFinishedE
|
|
|
1769
3132
|
}
|
|
1770
3133
|
const eventNodes = events.flatMap(getEventNode);
|
|
1771
3134
|
const trimmedFilterValue = filterValue.trim();
|
|
1772
|
-
const
|
|
1773
|
-
|
|
3135
|
+
const filterDescriptionParts = [];
|
|
3136
|
+
if (eventCategoryFilter !== All) {
|
|
3137
|
+
filterDescriptionParts.push(getEventCategoryFilterLabel(eventCategoryFilter).toLowerCase());
|
|
3138
|
+
}
|
|
3139
|
+
if (trimmedFilterValue) {
|
|
3140
|
+
filterDescriptionParts.push(trimmedFilterValue);
|
|
3141
|
+
}
|
|
3142
|
+
const timelineFilterDescription = getTimelineFilterDescription(timelineStartSeconds, timelineEndSeconds);
|
|
3143
|
+
if (timelineFilterDescription) {
|
|
3144
|
+
filterDescriptionParts.push(timelineFilterDescription);
|
|
3145
|
+
}
|
|
3146
|
+
const hasFilterValue = filterDescriptionParts.length > 0;
|
|
3147
|
+
const filterDescription = filterDescriptionParts.join(' ');
|
|
3148
|
+
const noFilteredEventsMessage = `no events found matching ${filterDescription}`;
|
|
1774
3149
|
const eventCountText = events.length === 0 && hasFilterValue ? noFilteredEventsMessage : `${events.length} event${events.length === 1 ? '' : 's'}`;
|
|
1775
3150
|
const emptyMessage = events.length === 0 && hasFilterValue ? noFilteredEventsMessage : 'No events';
|
|
3151
|
+
const safeSelectedEventIndex = selectedEventIndex === null || selectedEventIndex < 0 || selectedEventIndex >= events.length ? null : selectedEventIndex;
|
|
3152
|
+
const contentNodes = useDevtoolsLayout ? getDevtoolsDom(events, safeSelectedEventIndex, timelineEvents, timelineStartSeconds, timelineEndSeconds) : getLegacyEventsDom(errorMessage, emptyMessage, eventNodes);
|
|
3153
|
+
const quickFilterNodes = useDevtoolsLayout ? getQuickFilterNodes(eventCategoryFilter) : [];
|
|
3154
|
+
const rootChildCount = useDevtoolsLayout ? 4 : 3;
|
|
1776
3155
|
return [{
|
|
1777
|
-
childCount:
|
|
1778
|
-
className: 'ChatDebugView',
|
|
3156
|
+
childCount: rootChildCount,
|
|
3157
|
+
className: useDevtoolsLayout ? 'ChatDebugView ChatDebugView--devtools' : 'ChatDebugView',
|
|
1779
3158
|
type: Div
|
|
1780
3159
|
}, {
|
|
1781
3160
|
childCount: 2,
|
|
@@ -1792,7 +3171,7 @@ const getChatDebugViewDom = (errorMessage, filterValue, showEventStreamFinishedE
|
|
|
1792
3171
|
type: Input,
|
|
1793
3172
|
value: filterValue
|
|
1794
3173
|
}, {
|
|
1795
|
-
childCount:
|
|
3174
|
+
childCount: 4,
|
|
1796
3175
|
className: 'ChatDebugViewToggle',
|
|
1797
3176
|
type: Div
|
|
1798
3177
|
}, {
|
|
@@ -1829,71 +3208,21 @@ const getChatDebugViewDom = (errorMessage, filterValue, showEventStreamFinishedE
|
|
|
1829
3208
|
onChange: HandleInput,
|
|
1830
3209
|
type: Input
|
|
1831
3210
|
}, text('Show response part events'), {
|
|
3211
|
+
childCount: 2,
|
|
3212
|
+
className: 'ChatDebugViewToggleLabel',
|
|
3213
|
+
type: Label
|
|
3214
|
+
}, {
|
|
3215
|
+
checked: useDevtoolsLayout,
|
|
3216
|
+
childCount: 0,
|
|
3217
|
+
inputType: 'checkbox',
|
|
3218
|
+
name: UseDevtoolsLayout,
|
|
3219
|
+
onChange: HandleInput,
|
|
3220
|
+
type: Input
|
|
3221
|
+
}, text('Use devtools layout'), ...quickFilterNodes, {
|
|
1832
3222
|
childCount: 1,
|
|
1833
3223
|
className: 'ChatDebugViewEventCount',
|
|
1834
3224
|
type: Div
|
|
1835
|
-
}, text(eventCountText),
|
|
1836
|
-
childCount: eventNodes.length === 0 ? 1 : eventNodes.length,
|
|
1837
|
-
className: 'ChatDebugViewEvents',
|
|
1838
|
-
type: Div
|
|
1839
|
-
}, ...(eventNodes.length === 0 ? [{
|
|
1840
|
-
childCount: 1,
|
|
1841
|
-
className: errorMessage ? 'ChatDebugViewError' : 'ChatDebugViewEmpty',
|
|
1842
|
-
type: Div
|
|
1843
|
-
}, text(errorMessage || emptyMessage)] : eventNodes)];
|
|
1844
|
-
};
|
|
1845
|
-
|
|
1846
|
-
const RE_SPACE = /\s+/;
|
|
1847
|
-
const parseFilterValue = filterValue => {
|
|
1848
|
-
const normalizedFilter = filterValue.trim().toLowerCase();
|
|
1849
|
-
if (!normalizedFilter) {
|
|
1850
|
-
return {
|
|
1851
|
-
filterText: '',
|
|
1852
|
-
toolsOnly: false
|
|
1853
|
-
};
|
|
1854
|
-
}
|
|
1855
|
-
const parts = normalizedFilter.split(RE_SPACE);
|
|
1856
|
-
const toolsOnly = parts.includes('@tools');
|
|
1857
|
-
const filterText = parts.filter(part => part !== '@tools').join(' ');
|
|
1858
|
-
return {
|
|
1859
|
-
filterText,
|
|
1860
|
-
toolsOnly
|
|
1861
|
-
};
|
|
1862
|
-
};
|
|
1863
|
-
|
|
1864
|
-
const toolEventTypePrefix = 'tool-execution-';
|
|
1865
|
-
const isToolEvent = event => {
|
|
1866
|
-
return event.type.startsWith(toolEventTypePrefix);
|
|
1867
|
-
};
|
|
1868
|
-
const getVisibleEvents = (events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1869
|
-
return events.filter(event => {
|
|
1870
|
-
if (!showInputEvents && (event.type === 'handle-input' || event.type === 'handle-submit')) {
|
|
1871
|
-
return false;
|
|
1872
|
-
}
|
|
1873
|
-
if (!showResponsePartEvents && event.type === 'sse-response-part') {
|
|
1874
|
-
return false;
|
|
1875
|
-
}
|
|
1876
|
-
if (!showEventStreamFinishedEvents && event.type === 'event-stream-finished') {
|
|
1877
|
-
return false;
|
|
1878
|
-
}
|
|
1879
|
-
// hide session creation events by default — not useful in the debug view
|
|
1880
|
-
if (event.type === 'chat-session-created') {
|
|
1881
|
-
return false;
|
|
1882
|
-
}
|
|
1883
|
-
return true;
|
|
1884
|
-
});
|
|
1885
|
-
};
|
|
1886
|
-
const getFilteredEvents = (events, filterValue, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
|
|
1887
|
-
const visibleEvents = getVisibleEvents(events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
|
|
1888
|
-
const {
|
|
1889
|
-
filterText,
|
|
1890
|
-
toolsOnly
|
|
1891
|
-
} = parseFilterValue(filterValue);
|
|
1892
|
-
const filteredBySyntax = toolsOnly ? visibleEvents.filter(isToolEvent) : visibleEvents;
|
|
1893
|
-
if (!filterText) {
|
|
1894
|
-
return filteredBySyntax;
|
|
1895
|
-
}
|
|
1896
|
-
return filteredBySyntax.filter(event => JSON.stringify(event).toLowerCase().includes(filterText));
|
|
3225
|
+
}, text(eventCountText), ...contentNodes];
|
|
1897
3226
|
};
|
|
1898
3227
|
|
|
1899
3228
|
const withSessionEventIds = events => {
|
|
@@ -1905,16 +3234,29 @@ const withSessionEventIds = events => {
|
|
|
1905
3234
|
});
|
|
1906
3235
|
};
|
|
1907
3236
|
const renderItems = (oldState, newState) => {
|
|
3237
|
+
if (newState.initial) {
|
|
3238
|
+
return [SetDom2, newState.uid, []];
|
|
3239
|
+
}
|
|
1908
3240
|
const eventsWithIds = withSessionEventIds(newState.events);
|
|
1909
|
-
const
|
|
1910
|
-
const
|
|
3241
|
+
const timelineEvents = getFilteredEvents(eventsWithIds, newState.filterValue, newState.eventCategoryFilter, newState.showInputEvents, newState.showResponsePartEvents, newState.showEventStreamFinishedEvents);
|
|
3242
|
+
const filteredEvents = filterEventsByTimelineRange(timelineEvents, newState.timelineStartSeconds, newState.timelineEndSeconds);
|
|
3243
|
+
const dom = getChatDebugViewDom(newState.errorMessage, newState.filterValue, newState.eventCategoryFilter, newState.showEventStreamFinishedEvents, newState.showInputEvents, newState.showResponsePartEvents, newState.useDevtoolsLayout, newState.selectedEventIndex, newState.timelineStartSeconds, newState.timelineEndSeconds, timelineEvents, filteredEvents);
|
|
1911
3244
|
return [SetDom2, newState.uid, dom];
|
|
1912
3245
|
};
|
|
1913
3246
|
|
|
3247
|
+
const renderIncremental = (oldState, newState) => {
|
|
3248
|
+
const oldDom = renderItems(oldState, oldState)[2];
|
|
3249
|
+
const newDom = renderItems(newState, newState)[2];
|
|
3250
|
+
const patches = diffTree(oldDom, newDom);
|
|
3251
|
+
return [SetPatches, newState.uid, patches];
|
|
3252
|
+
};
|
|
3253
|
+
|
|
1914
3254
|
const getRenderer = diffType => {
|
|
1915
3255
|
switch (diffType) {
|
|
1916
3256
|
case RenderCss:
|
|
1917
3257
|
return renderCss;
|
|
3258
|
+
case RenderIncremental:
|
|
3259
|
+
return renderIncremental;
|
|
1918
3260
|
case RenderItems:
|
|
1919
3261
|
return renderItems;
|
|
1920
3262
|
default:
|
|
@@ -1950,6 +3292,9 @@ const renderEventListeners = () => {
|
|
|
1950
3292
|
}, {
|
|
1951
3293
|
name: HandleInput,
|
|
1952
3294
|
params: ['handleInput', TargetName, TargetValue, TargetChecked]
|
|
3295
|
+
}, {
|
|
3296
|
+
name: HandleSimpleInput,
|
|
3297
|
+
params: ['handleInput', TargetName, TargetValue]
|
|
1953
3298
|
}];
|
|
1954
3299
|
};
|
|
1955
3300
|
|
|
@@ -1966,23 +3311,31 @@ const resize = (state, dimensions) => {
|
|
|
1966
3311
|
|
|
1967
3312
|
const saveState = state => {
|
|
1968
3313
|
const {
|
|
3314
|
+
eventCategoryFilter,
|
|
1969
3315
|
filterValue,
|
|
1970
3316
|
height,
|
|
1971
3317
|
sessionId,
|
|
1972
3318
|
showEventStreamFinishedEvents,
|
|
1973
3319
|
showInputEvents,
|
|
1974
3320
|
showResponsePartEvents,
|
|
3321
|
+
timelineEndSeconds,
|
|
3322
|
+
timelineStartSeconds,
|
|
3323
|
+
useDevtoolsLayout,
|
|
1975
3324
|
width,
|
|
1976
3325
|
x,
|
|
1977
3326
|
y
|
|
1978
3327
|
} = state;
|
|
1979
3328
|
return {
|
|
3329
|
+
eventCategoryFilter,
|
|
1980
3330
|
filterValue,
|
|
1981
3331
|
height,
|
|
1982
3332
|
sessionId,
|
|
1983
3333
|
showEventStreamFinishedEvents,
|
|
1984
3334
|
showInputEvents,
|
|
1985
3335
|
showResponsePartEvents,
|
|
3336
|
+
timelineEndSeconds,
|
|
3337
|
+
timelineStartSeconds,
|
|
3338
|
+
useDevtoolsLayout,
|
|
1986
3339
|
width,
|
|
1987
3340
|
x,
|
|
1988
3341
|
y
|
|
@@ -1994,7 +3347,8 @@ const setEvents = (state, events) => {
|
|
|
1994
3347
|
...state,
|
|
1995
3348
|
errorMessage: '',
|
|
1996
3349
|
events,
|
|
1997
|
-
initial: false
|
|
3350
|
+
initial: false,
|
|
3351
|
+
selectedEventIndex: null
|
|
1998
3352
|
};
|
|
1999
3353
|
};
|
|
2000
3354
|
|