@lvce-editor/chat-debug-view 7.4.0 → 8.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.
@@ -1128,9 +1128,10 @@ const Text = 12;
1128
1128
  const Th = 13;
1129
1129
  const THead = 14;
1130
1130
  const Tr = 15;
1131
+ const Img = 17;
1132
+ const H2 = 22;
1131
1133
  const Section = 41;
1132
1134
  const Search = 42;
1133
- const Label = 66;
1134
1135
  const Reference = 100;
1135
1136
 
1136
1137
  const Button = 'event.button';
@@ -1139,7 +1140,7 @@ const ClientY = 'event.clientY';
1139
1140
  const TargetName = 'event.target.name';
1140
1141
  const TargetValue = 'event.target.value';
1141
1142
 
1142
- const None = 0;
1143
+ const None$1 = 0;
1143
1144
  const Checked = 2;
1144
1145
  const Unchecked = 3;
1145
1146
 
@@ -1170,6 +1171,9 @@ const showContextMenu2 = async (uid, menuId, x, y, args) => {
1170
1171
  const sendMessagePortToChatStorageWorker$1 = async port => {
1171
1172
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatStorageWorker', port, 'HandleMessagePort.handleMessagePort');
1172
1173
  };
1174
+ const writeClipBoardText = async text => {
1175
+ await invoke('ClipBoard.writeText', /* text */text);
1176
+ };
1173
1177
 
1174
1178
  const toCommandId = key => {
1175
1179
  const dotIndex = key.indexOf('.');
@@ -1301,11 +1305,56 @@ const loadSelectedEvent$1 = async (sessionId, eventId, type) => {
1301
1305
  return invoke$1('ChatStorage.loadSelectedEvent', sessionId, eventId, type);
1302
1306
  };
1303
1307
 
1304
- const appendStoredEventForTestDependencies = {
1305
- appendEvent: appendEvent
1306
- };
1307
1308
  const appendStoredEventForTest = async (state, event) => {
1308
- await appendStoredEventForTestDependencies.appendEvent(event);
1309
+ await appendEvent(event);
1310
+ return state;
1311
+ };
1312
+
1313
+ const decodeBase64 = value => {
1314
+ const decoded = atob(value);
1315
+ const bytes = new Uint8Array(decoded.length);
1316
+ for (let i = 0; i < decoded.length; i++) {
1317
+ bytes[i] = decoded.codePointAt(i) || 0;
1318
+ }
1319
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
1320
+ };
1321
+ const createCanvasBlob = async mimeType => {
1322
+ const canvas = new OffscreenCanvas(2, 2);
1323
+ const context = canvas.getContext('2d');
1324
+ if (!context) {
1325
+ throw new Error('2d canvas context is not available');
1326
+ }
1327
+ context.fillStyle = '#0b6';
1328
+ context.fillRect(0, 0, 2, 2);
1329
+ return canvas.convertToBlob({
1330
+ type: mimeType
1331
+ });
1332
+ };
1333
+ const createBlob = async (mimeType, contentKind, content) => {
1334
+ if (contentKind === 'canvas') {
1335
+ return createCanvasBlob(mimeType);
1336
+ }
1337
+ if (contentKind === 'base64') {
1338
+ return new Blob([decodeBase64(content)], {
1339
+ type: mimeType
1340
+ });
1341
+ }
1342
+ return new Blob([content], {
1343
+ type: mimeType
1344
+ });
1345
+ };
1346
+ const appendStoredImageAttachmentForTest = async (state, sessionId, eventId, mimeType, name, contentKind, content, timestamp) => {
1347
+ const blob = await createBlob(mimeType, contentKind, content);
1348
+ await appendEvent({
1349
+ attachmentId: `attachment-${eventId}`,
1350
+ blob,
1351
+ eventId,
1352
+ mimeType,
1353
+ name,
1354
+ sessionId,
1355
+ timestamp,
1356
+ type: 'chat-attachment-added'
1357
+ });
1309
1358
  return state;
1310
1359
  };
1311
1360
 
@@ -1331,6 +1380,7 @@ const FailedToLoadChatDebugSession = 'Failed to load chat debug session "{PH1}".
1331
1380
  const FailedToLoadChatDebugSessionWithError = 'Failed to load chat debug session "{PH1}": {PH2}';
1332
1381
  const FilterEvents = 'Filter events';
1333
1382
  const FromSeconds = 'from {PH1}s';
1383
+ const ImageCouldNotBeLoaded = 'image could not be loaded';
1334
1384
  const InvalidSessionId = 'Invalid session id';
1335
1385
  const InvalidUriEncoding = 'Invalid URI encoding';
1336
1386
  const InvalidUriFormat = 'Invalid URI format';
@@ -1340,6 +1390,7 @@ const NoChatSessionFound = 'No chat session found for sessionId "{PH1}".';
1340
1390
  const NoEventsFound = 'No events have been found';
1341
1391
  const NoEventsFoundMatching = 'No events found matching {PH1}';
1342
1392
  const NoToolCallEvents = 'No tool call events.';
1393
+ const Payload$1 = 'Payload';
1343
1394
  const Preview$1 = 'Preview';
1344
1395
  const Refresh$1 = 'Refresh';
1345
1396
  const RefreshEvents = 'Refresh events';
@@ -1401,6 +1452,9 @@ const fromSeconds = seconds => {
1401
1452
  const invalidSessionId = () => {
1402
1453
  return i18nString(InvalidSessionId);
1403
1454
  };
1455
+ const imageCouldNotBeLoaded = () => {
1456
+ return i18nString(ImageCouldNotBeLoaded);
1457
+ };
1404
1458
  const invalidUriEncoding = () => {
1405
1459
  return i18nString(InvalidUriEncoding);
1406
1460
  };
@@ -1432,6 +1486,9 @@ const noToolCallEvents = () => {
1432
1486
  const preview = () => {
1433
1487
  return i18nString(Preview$1);
1434
1488
  };
1489
+ const payload = () => {
1490
+ return i18nString(Payload$1);
1491
+ };
1435
1492
  const refresh$1 = () => {
1436
1493
  return i18nString(Refresh$1);
1437
1494
  };
@@ -1489,40 +1546,101 @@ const windowSummary = (start, end, duration) => {
1489
1546
  });
1490
1547
  };
1491
1548
 
1549
+ const Filter = 'filter';
1550
+ const EventCategoryFilter = 'eventCategoryFilter';
1551
+ const ShowEventStreamFinishedEvents = 'showEventStreamFinishedEvents';
1552
+ const ShowInputEvents = 'showInputEvents';
1553
+ const ShowResponsePartEvents = 'showResponsePartEvents';
1554
+ const UseDevtoolsLayout = 'useDevtoolsLayout';
1555
+ const SelectedEventIndex = 'selectedEventIndex';
1556
+ const CloseDetails = 'closeDetails';
1557
+ const DetailTab = 'detailTab';
1558
+ const TimelineStartSeconds = 'timelineStartSeconds';
1559
+ const TimelineEndSeconds = 'timelineEndSeconds';
1560
+ const TimelineRangePreset = 'timelineRangePreset';
1561
+ const Refresh = 'refresh';
1562
+ const All = 'all';
1563
+ const Tools = 'tools';
1564
+ const Network = 'network';
1565
+ const Ui = 'ui';
1566
+ const Stream = 'stream';
1492
1567
  const Response = 'response';
1493
1568
  const Preview = 'preview';
1569
+ const Payload = 'payload';
1494
1570
  const Timing = 'timing';
1495
- const detailTabs = [Preview, Response, Timing];
1571
+
1496
1572
  const isDetailTab = value => {
1497
- return value === Response || value === Preview || value === Timing;
1573
+ return value === Response || value === Preview || value === Payload || value === Timing;
1498
1574
  };
1499
- const getDetailTabLabel = value => {
1500
- if (value === Preview) {
1501
- return preview();
1502
- }
1503
- if (value === Timing) {
1504
- return timing();
1505
- }
1506
- return response();
1575
+
1576
+ const getSafeSelectedDetailTab = selectedDetailTab => {
1577
+ return isDetailTab(selectedDetailTab) ? selectedDetailTab : Response;
1507
1578
  };
1508
1579
 
1509
- const createEventCategoryFilterOptions = () => {
1510
- return [{
1511
- label: all(),
1512
- value: All
1513
- }, {
1514
- label: tools(),
1515
- value: Tools
1516
- }, {
1517
- label: network(),
1518
- value: Network
1580
+ const hasTimingDetails = event => {
1581
+ const hasDuration = typeof event.duration === 'number' || typeof event.durationMs === 'number';
1582
+ const hasStart = event.started !== undefined || event.startTime !== undefined || event.startTimestamp !== undefined;
1583
+ const hasEnd = event.ended !== undefined || event.endTime !== undefined || event.endTimestamp !== undefined;
1584
+ return hasDuration || hasStart && hasEnd;
1585
+ };
1586
+
1587
+ const createDetailTabs = (selectedDetailTab = Response, event) => {
1588
+ const hasTimingTab = event ? hasTimingDetails(event) : true;
1589
+ const safeSelectedDetailTab = getSafeSelectedDetailTab(selectedDetailTab);
1590
+ const normalizedSelectedDetailTab = hasTimingTab || safeSelectedDetailTab !== Timing ? safeSelectedDetailTab : Response;
1591
+ const detailTabs = [{
1592
+ isSelected: normalizedSelectedDetailTab === Preview,
1593
+ label: preview(),
1594
+ name: Preview
1519
1595
  }, {
1520
- label: ui(),
1521
- value: Ui
1596
+ isSelected: normalizedSelectedDetailTab === Payload,
1597
+ label: payload(),
1598
+ name: Payload
1522
1599
  }, {
1523
- label: stream(),
1524
- value: Stream
1600
+ isSelected: normalizedSelectedDetailTab === Response,
1601
+ label: response(),
1602
+ name: Response
1525
1603
  }];
1604
+ if (hasTimingTab) {
1605
+ detailTabs.push({
1606
+ isSelected: normalizedSelectedDetailTab === Timing,
1607
+ label: timing(),
1608
+ name: Timing
1609
+ });
1610
+ }
1611
+ return detailTabs;
1612
+ };
1613
+
1614
+ const getSelectedDetailTab = detailTabs => {
1615
+ const selectedDetailTab = detailTabs.find(detailTab => detailTab.isSelected);
1616
+ if (selectedDetailTab) {
1617
+ return selectedDetailTab.name;
1618
+ }
1619
+ const responseTab = detailTabs.find(detailTab => detailTab.name === Response);
1620
+ if (responseTab) {
1621
+ return responseTab.name;
1622
+ }
1623
+ return detailTabs[0]?.name ?? Response;
1624
+ };
1625
+
1626
+ const hasDetailTab = (detailTabs, value) => {
1627
+ return detailTabs.some(detailTab => detailTab.name === value);
1628
+ };
1629
+
1630
+ const selectDetailTab$1 = (detailTabs, selectedDetailTab) => {
1631
+ if (!hasDetailTab(detailTabs, selectedDetailTab)) {
1632
+ return detailTabs;
1633
+ }
1634
+ return detailTabs.map(detailTab => {
1635
+ const isSelectedProperty = detailTab.name === selectedDetailTab;
1636
+ if (detailTab.isSelected === isSelectedProperty) {
1637
+ return detailTab;
1638
+ }
1639
+ return {
1640
+ ...detailTab,
1641
+ isSelected: isSelectedProperty
1642
+ };
1643
+ });
1526
1644
  };
1527
1645
 
1528
1646
  const getEventCategoryFilterLabel = eventCategoryFilter => {
@@ -1540,11 +1658,91 @@ const getEventCategoryFilterLabel = eventCategoryFilter => {
1540
1658
  }
1541
1659
  };
1542
1660
 
1543
- const All = 'all';
1544
- const Tools = 'tools';
1545
- const Network = 'network';
1546
- const Ui = 'ui';
1547
- const Stream = 'stream';
1661
+ const eventCategoryFilters = [All, Tools, Network, Ui, Stream];
1662
+ const normalizeSelectedEventCategoryFilters = (selectedEventCategoryFilter = All) => {
1663
+ const selectedEventCategoryFilters = Array.isArray(selectedEventCategoryFilter) ? selectedEventCategoryFilter : [selectedEventCategoryFilter];
1664
+ const uniqueSelectedEventCategoryFilters = [...new Set(selectedEventCategoryFilters)];
1665
+ const validSelectedEventCategoryFilters = uniqueSelectedEventCategoryFilters.filter(value => {
1666
+ return eventCategoryFilters.includes(value);
1667
+ });
1668
+ if (validSelectedEventCategoryFilters.length === 0) {
1669
+ return [All];
1670
+ }
1671
+ if (validSelectedEventCategoryFilters.includes(All)) {
1672
+ return [All];
1673
+ }
1674
+ return validSelectedEventCategoryFilters;
1675
+ };
1676
+ const createCategoryFilters$1 = (selectedEventCategoryFilter = All) => {
1677
+ const selectedEventCategoryFilters = normalizeSelectedEventCategoryFilters(selectedEventCategoryFilter);
1678
+ return [{
1679
+ isSelected: selectedEventCategoryFilters.includes(All),
1680
+ label: all(),
1681
+ name: All
1682
+ }, {
1683
+ isSelected: selectedEventCategoryFilters.includes(Tools),
1684
+ label: tools(),
1685
+ name: Tools
1686
+ }, {
1687
+ isSelected: selectedEventCategoryFilters.includes(Network),
1688
+ label: network(),
1689
+ name: Network
1690
+ }, {
1691
+ isSelected: selectedEventCategoryFilters.includes(Ui),
1692
+ label: ui(),
1693
+ name: Ui
1694
+ }, {
1695
+ isSelected: selectedEventCategoryFilters.includes(Stream),
1696
+ label: stream(),
1697
+ name: Stream
1698
+ }];
1699
+ };
1700
+
1701
+ const createCategoryFilters = (selectedEventCategoryFilter = All) => {
1702
+ return createCategoryFilters$1(selectedEventCategoryFilter);
1703
+ };
1704
+ const isEventCategoryFilter = value => {
1705
+ return value === All || value === Tools || value === Network || value === Ui || value === Stream;
1706
+ };
1707
+ const getSelectedEventCategoryFilters = categoryFilters => {
1708
+ const selectedCategoryFilters = categoryFilters.filter(categoryFilter => categoryFilter.isSelected);
1709
+ const selectedEventCategoryFilters = selectedCategoryFilters.map(categoryFilter => categoryFilter.name).filter(name => isEventCategoryFilter(name));
1710
+ return normalizeSelectedEventCategoryFilters(selectedEventCategoryFilters);
1711
+ };
1712
+ const getSelectedEventCategoryFilter = categoryFilters => {
1713
+ const selectedEventCategoryFilters = getSelectedEventCategoryFilters(categoryFilters);
1714
+ if (selectedEventCategoryFilters.length === 1) {
1715
+ return selectedEventCategoryFilters[0];
1716
+ }
1717
+ return All;
1718
+ };
1719
+ const selectCategoryFilters = (categoryFilters, selectedEventCategoryFilters) => {
1720
+ const normalizedSelectedEventCategoryFilters = normalizeSelectedEventCategoryFilters(selectedEventCategoryFilters.filter(value => isEventCategoryFilter(value)));
1721
+ return categoryFilters.map(categoryFilter => {
1722
+ const isSelected = normalizedSelectedEventCategoryFilters.includes(categoryFilter.name);
1723
+ if (categoryFilter.isSelected === isSelected) {
1724
+ return categoryFilter;
1725
+ }
1726
+ return {
1727
+ ...categoryFilter,
1728
+ isSelected
1729
+ };
1730
+ });
1731
+ };
1732
+ const selectCategoryFilter = (categoryFilters, selectedEventCategoryFilter, additive = false) => {
1733
+ if (!isEventCategoryFilter(selectedEventCategoryFilter)) {
1734
+ return categoryFilters;
1735
+ }
1736
+ if (!additive || selectedEventCategoryFilter === All) {
1737
+ return selectCategoryFilters(categoryFilters, [selectedEventCategoryFilter]);
1738
+ }
1739
+ const selectedEventCategoryFilters = getSelectedEventCategoryFilters(categoryFilters).filter(value => value !== All);
1740
+ const nextSelectedEventCategoryFilters = selectedEventCategoryFilters.includes(selectedEventCategoryFilter) ? selectedEventCategoryFilters.filter(value => value !== selectedEventCategoryFilter) : [...selectedEventCategoryFilters, selectedEventCategoryFilter];
1741
+ if (nextSelectedEventCategoryFilters.length === 0) {
1742
+ return selectCategoryFilters(categoryFilters, [All]);
1743
+ }
1744
+ return selectCategoryFilters(categoryFilters, nextSelectedEventCategoryFilters);
1745
+ };
1548
1746
 
1549
1747
  const RE_SPACE = /\s+/;
1550
1748
  const tokenToEventCategoryFilter = new Map([['@tools', Tools], ['@network', Network], ['@ui', Ui], ['@stream', Stream]]);
@@ -1568,57 +1766,52 @@ const parseFilterValue = filterValue => {
1568
1766
  const Type = 'type';
1569
1767
  const Duration = 'duration';
1570
1768
  const Status = 'status';
1571
- const tableColumns = [Type, Duration, Status];
1572
- const defaultVisibleTableColumns = tableColumns;
1769
+ const tableColumnNames = [Type, Duration, Status];
1770
+ const createTableColumns = () => {
1771
+ return [{
1772
+ label: type(),
1773
+ name: Type
1774
+ }, {
1775
+ label: duration(),
1776
+ name: Duration
1777
+ }, {
1778
+ label: status(),
1779
+ name: Status
1780
+ }];
1781
+ };
1782
+ const defaultVisibleTableColumns = tableColumnNames;
1573
1783
  const isTableColumn = value => {
1574
- return tableColumns.includes(value);
1784
+ return tableColumnNames.includes(value);
1575
1785
  };
1576
- const getOrderedVisibleTableColumns = values => {
1786
+ const getOrderedVisibleTableColumns = (values, tableColumns = createTableColumns()) => {
1577
1787
  const visibleColumns = new Set(values.filter(isTableColumn));
1578
- return tableColumns.filter(column => visibleColumns.has(column));
1788
+ return tableColumns.map(column => column.name).filter(column => visibleColumns.has(column));
1579
1789
  };
1580
1790
  const isVisibleTableColumn = (visibleTableColumns, column) => {
1581
1791
  return visibleTableColumns.includes(column);
1582
1792
  };
1583
-
1584
- const validEventCategoryFilters = new Set([All, Network, Stream, Tools, Ui]);
1585
- const isSavedState = value => {
1586
- return typeof value === 'object' && value !== null;
1587
- };
1588
- const getRestoredEventCategoryFilter = (savedState, currentEventCategoryFilter) => {
1589
- if (typeof savedState.eventCategoryFilter === 'string' && validEventCategoryFilters.has(savedState.eventCategoryFilter)) {
1590
- return savedState.eventCategoryFilter;
1591
- }
1592
- if (typeof savedState.filterValue === 'string') {
1593
- return parseFilterValue(savedState.filterValue).eventCategoryFilter;
1594
- }
1595
- return currentEventCategoryFilter;
1596
- };
1597
- const restoreSavedState = (state, savedState) => {
1598
- if (!isSavedState(savedState)) {
1599
- return state;
1793
+ const getTableColumnLabel = (tableColumns, name) => {
1794
+ const match = tableColumns.find(column => column.name === name);
1795
+ if (match) {
1796
+ return match.label;
1797
+ }
1798
+ switch (name) {
1799
+ case Duration:
1800
+ return duration();
1801
+ case Status:
1802
+ return status();
1803
+ case Type:
1804
+ return type();
1805
+ default:
1806
+ return name;
1600
1807
  }
1601
- const restoredVisibleTableColumns = Array.isArray(savedState.visibleTableColumns) ? getOrderedVisibleTableColumns(savedState.visibleTableColumns.filter(value => typeof value === 'string')) : state.visibleTableColumns;
1602
- return {
1603
- ...state,
1604
- eventCategoryFilter: getRestoredEventCategoryFilter(savedState, state.eventCategoryFilter),
1605
- filterValue: typeof savedState.filterValue === 'string' ? savedState.filterValue : state.filterValue,
1606
- selectedDetailTab: typeof savedState.selectedDetailTab === 'string' && isDetailTab(savedState.selectedDetailTab) ? savedState.selectedDetailTab : state.selectedDetailTab,
1607
- selectedEventId: typeof savedState.selectedEventId === 'number' || savedState.selectedEventId === null ? savedState.selectedEventId : state.selectedEventId,
1608
- timelineEndSeconds: typeof savedState.timelineEndSeconds === 'string' ? savedState.timelineEndSeconds : state.timelineEndSeconds,
1609
- timelineStartSeconds: typeof savedState.timelineStartSeconds === 'string' ? savedState.timelineStartSeconds : state.timelineStartSeconds,
1610
- visibleTableColumns: restoredVisibleTableColumns
1611
- };
1612
1808
  };
1613
1809
 
1614
- const {
1615
- get,
1616
- getCommandIds,
1617
- registerCommands,
1618
- set,
1619
- wrapCommand,
1620
- wrapGetter
1621
- } = create$1();
1810
+ const defaultTableColumnWidths = {
1811
+ duration: 110,
1812
+ status: 110,
1813
+ type: 260
1814
+ };
1622
1815
 
1623
1816
  const defaultTableWidth = 480;
1624
1817
  const minTableWidth = 240;
@@ -1647,134 +1840,445 @@ const getTableWidthFromClientX = (viewX, width, clientX) => {
1647
1840
  return clampTableWidth(width, nextTableWidth);
1648
1841
  };
1649
1842
 
1650
- const createDefaultState = () => {
1843
+ const minimumTableColumnWidth = 80;
1844
+
1845
+ const getTableColumnLayout = (tableWidth, visibleTableColumns, tableColumnWidths) => {
1846
+ const visibleColumns = getOrderedVisibleTableColumns(visibleTableColumns);
1847
+ if (visibleColumns.length === 0) {
1848
+ return {
1849
+ fixedColumns: [],
1850
+ resizerLefts: [],
1851
+ visibleColumns,
1852
+ visibleColumnWidths: []
1853
+ };
1854
+ }
1855
+ const visibleColumnWidths = [];
1856
+ let remainingWidth = tableWidth;
1857
+ let remainingColumnCount = visibleColumns.length;
1858
+ for (let index = 0; index < visibleColumns.length; index++) {
1859
+ const column = visibleColumns[index];
1860
+ if (index === visibleColumns.length - 1) {
1861
+ visibleColumnWidths.push(Math.max(0, remainingWidth));
1862
+ continue;
1863
+ }
1864
+ const maxWidth = Math.max(minimumTableColumnWidth, remainingWidth - minimumTableColumnWidth * (remainingColumnCount - 1));
1865
+ const preferredWidth = tableColumnWidths[column];
1866
+ const clampedWidth = Math.max(minimumTableColumnWidth, Math.min(preferredWidth, maxWidth));
1867
+ visibleColumnWidths.push(clampedWidth);
1868
+ remainingWidth -= clampedWidth;
1869
+ remainingColumnCount -= 1;
1870
+ }
1871
+ const resizerLefts = [];
1872
+ let cumulativeWidth = 0;
1873
+ for (let index = 0; index < visibleColumnWidths.length - 1; index++) {
1874
+ cumulativeWidth += visibleColumnWidths[index];
1875
+ resizerLefts.push(cumulativeWidth);
1876
+ }
1651
1877
  return {
1652
- assetDir: '',
1653
- databaseName: 'lvce-chat-view-sessions',
1654
- dataBaseVersion: 2,
1655
- errorMessage: '',
1656
- eventCategoryFilter: All,
1657
- eventCategoryFilterOptions: createEventCategoryFilterOptions(),
1658
- events: [],
1659
- eventStoreName: 'chat-view-events',
1660
- filterValue: '',
1661
- height: 0,
1662
- initial: false,
1663
- platform: 0,
1664
- selectedDetailTab: Response,
1665
- selectedEvent: null,
1666
- selectedEventId: null,
1667
- selectedEventIndex: null,
1668
- sessionId: '',
1669
- sessionIdIndexName: 'sessionId',
1670
- showEventStreamFinishedEvents: false,
1671
- showInputEvents: false,
1672
- showResponsePartEvents: false,
1673
- tableWidth: defaultTableWidth,
1674
- timelineEndSeconds: '',
1675
- timelineSelectionActive: false,
1676
- timelineSelectionAnchorSeconds: '',
1677
- timelineSelectionFocusSeconds: '',
1678
- timelineStartSeconds: '',
1679
- uid: 0,
1680
- uri: '',
1681
- useDevtoolsLayout: true,
1682
- visibleTableColumns: defaultVisibleTableColumns,
1683
- width: 0,
1684
- x: 0,
1685
- y: 0
1878
+ fixedColumns: visibleColumns.slice(0, -1),
1879
+ resizerLefts,
1880
+ visibleColumns,
1881
+ visibleColumnWidths
1686
1882
  };
1687
1883
  };
1688
1884
 
1689
- 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 = {}) => {
1690
- const defaultState = createDefaultState();
1691
- const state = {
1692
- ...restoreSavedState(defaultState, savedState),
1693
- assetDir,
1694
- databaseName,
1695
- dataBaseVersion,
1696
- eventStoreName,
1697
- height,
1698
- initial: true,
1699
- platform,
1700
- sessionId,
1701
- sessionIdIndexName,
1702
- uid,
1703
- uri,
1704
- width,
1705
- x,
1706
- y
1885
+ const getResizedTableColumnWidths = (width, tableWidth, visibleTableColumns, tableColumnWidths, viewX, clientX, resizerDownId) => {
1886
+ const clampedTableWidth = clampTableWidth(width, tableWidth);
1887
+ const layout = getTableColumnLayout(clampedTableWidth, visibleTableColumns, tableColumnWidths);
1888
+ if (resizerDownId < 1 || resizerDownId >= layout.visibleColumns.length) {
1889
+ return tableColumnWidths;
1890
+ }
1891
+ const boundaryIndex = resizerDownId - 1;
1892
+ const precedingWidth = layout.visibleColumnWidths.slice(0, boundaryIndex).reduce((total, current) => total + current, 0);
1893
+ const remainingVisibleColumnCount = layout.visibleColumns.length - boundaryIndex - 1;
1894
+ const maxWidth = Math.max(minimumTableColumnWidth, clampedTableWidth - precedingWidth - minimumTableColumnWidth * remainingVisibleColumnCount);
1895
+ const nextWidth = clientX - viewX - leftPadding - precedingWidth;
1896
+ const clampedWidth = Math.max(minimumTableColumnWidth, Math.min(nextWidth, maxWidth));
1897
+ const resizedColumn = layout.visibleColumns[boundaryIndex];
1898
+ return {
1899
+ ...tableColumnWidths,
1900
+ [resizedColumn]: clampedWidth
1707
1901
  };
1708
- set(uid, state, state);
1709
1902
  };
1710
1903
 
1711
- const RenderItems = 1;
1712
- const RenderCss = 2;
1713
- const RenderIncremental = 3;
1714
-
1715
- const diff = (oldState, newState) => {
1716
- 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.tableWidth !== newState.tableWidth || oldState.timelineEndSeconds !== newState.timelineEndSeconds || oldState.timelineSelectionActive !== newState.timelineSelectionActive || oldState.timelineSelectionAnchorSeconds !== newState.timelineSelectionAnchorSeconds || oldState.timelineSelectionFocusSeconds !== newState.timelineSelectionFocusSeconds || oldState.timelineStartSeconds !== newState.timelineStartSeconds || oldState.useDevtoolsLayout !== newState.useDevtoolsLayout || oldState.visibleTableColumns !== newState.visibleTableColumns || oldState.selectedDetailTab !== newState.selectedDetailTab || oldState.selectedEvent !== newState.selectedEvent || oldState.selectedEventIndex !== newState.selectedEventIndex || oldState.width !== newState.width || oldState.uid !== newState.uid) {
1717
- return [RenderIncremental, RenderCss];
1718
- }
1719
- return [];
1904
+ const isSameTableColumnWidths = (first, second) => {
1905
+ return first.type === second.type && first.duration === second.duration && first.status === second.status;
1720
1906
  };
1721
1907
 
1722
- const diff2 = uid => {
1723
- const {
1724
- newState,
1725
- oldState
1726
- } = get(uid);
1727
- return diff(oldState, newState);
1908
+ const isFiniteNumber = value => {
1909
+ return typeof value === 'number' && Number.isFinite(value);
1728
1910
  };
1729
1911
 
1730
- const MenuChatDebugTableHeader = 2189;
1731
- const handleHeaderContextMenu = async (state, eventX, eventY) => {
1732
- await showContextMenu2(state.uid, MenuChatDebugTableHeader, eventX, eventY, {
1733
- menuId: MenuChatDebugTableHeader
1734
- });
1735
- return state;
1912
+ const isTableColumnWidths = value => {
1913
+ if (typeof value !== 'object' || value === null) {
1914
+ return false;
1915
+ }
1916
+ const record = value;
1917
+ return isFiniteNumber(record.type) && isFiniteNumber(record.duration) && isFiniteNumber(record.status);
1736
1918
  };
1737
1919
 
1738
- const hasMatchingToolName = (startedEvent, finishedEvent) => {
1739
- if (typeof startedEvent.toolName === 'string' && typeof finishedEvent.toolName === 'string') {
1740
- return startedEvent.toolName === finishedEvent.toolName;
1920
+ const validEventCategoryFilters = new Set([All, Network, Stream, Tools, Ui]);
1921
+ const isSavedState = value => {
1922
+ return typeof value === 'object' && value !== null;
1923
+ };
1924
+ const restoreCategoryFilters = (savedState, currentCategoryFilters) => {
1925
+ if (Array.isArray(savedState.eventCategoryFilters)) {
1926
+ return selectCategoryFilters(currentCategoryFilters, savedState.eventCategoryFilters.filter(value => typeof value === 'string'));
1741
1927
  }
1742
- return true;
1928
+ if (typeof savedState.eventCategoryFilter === 'string' && validEventCategoryFilters.has(savedState.eventCategoryFilter)) {
1929
+ return selectCategoryFilter(currentCategoryFilters, savedState.eventCategoryFilter);
1930
+ }
1931
+ if (typeof savedState.filterValue === 'string') {
1932
+ return selectCategoryFilter(currentCategoryFilters, parseFilterValue(savedState.filterValue).eventCategoryFilter);
1933
+ }
1934
+ return currentCategoryFilters;
1743
1935
  };
1744
-
1745
- const isMatchingToolExecutionPair = (startedEvent, finishedEvent) => {
1746
- return startedEvent.sessionId === finishedEvent.sessionId && hasMatchingToolName(startedEvent, finishedEvent);
1936
+ const restoreFilterValue = (savedState, currentFilterValue) => {
1937
+ return typeof savedState.filterValue === 'string' ? savedState.filterValue : currentFilterValue;
1747
1938
  };
1748
-
1749
- const startedEventType = 'tool-execution-started';
1750
- const finishedEventType = 'tool-execution-finished';
1751
- const mergedEventType = 'tool-execution';
1752
-
1753
- const isToolExecutionFinishedEvent = event => {
1754
- return event.type === finishedEventType;
1939
+ const restoreDetailTabs = (savedState, currentDetailTabs) => {
1940
+ return typeof savedState.selectedDetailTab === 'string' && isDetailTab(savedState.selectedDetailTab) ? selectDetailTab$1(currentDetailTabs, savedState.selectedDetailTab) : currentDetailTabs;
1755
1941
  };
1756
-
1757
- const isToolExecutionStartedEvent = event => {
1758
- return event.type === startedEventType;
1942
+ const restoreSelectedEventId = (savedState, currentSelectedEventId) => {
1943
+ return typeof savedState.selectedEventId === 'number' || savedState.selectedEventId === null ? savedState.selectedEventId : currentSelectedEventId;
1759
1944
  };
1760
-
1761
- const getTimestamp = value => {
1762
- return typeof value === 'string' || typeof value === 'number' ? value : undefined;
1945
+ const restoreTimelineEndSeconds = (savedState, currentTimelineEndSeconds) => {
1946
+ return typeof savedState.timelineEndSeconds === 'string' ? savedState.timelineEndSeconds : currentTimelineEndSeconds;
1763
1947
  };
1764
-
1765
- const getEndedTimestamp = event => {
1766
- return getTimestamp(event.ended) ?? getTimestamp(event.endTime) ?? getTimestamp(event.endTimestamp) ?? getTimestamp(event.timestamp);
1948
+ const restoreTimelineStartSeconds = (savedState, currentTimelineStartSeconds) => {
1949
+ return typeof savedState.timelineStartSeconds === 'string' ? savedState.timelineStartSeconds : currentTimelineStartSeconds;
1767
1950
  };
1768
-
1769
- const getStartedTimestamp = event => {
1770
- return getTimestamp(event.started) ?? getTimestamp(event.startTime) ?? getTimestamp(event.startTimestamp) ?? getTimestamp(event.timestamp);
1951
+ const restoreVisibleTableColumns = (savedState, currentVisibleTableColumns) => {
1952
+ if (!Array.isArray(savedState.visibleTableColumns)) {
1953
+ return currentVisibleTableColumns;
1954
+ }
1955
+ const visibleTableColumns = savedState.visibleTableColumns.filter(value => typeof value === 'string');
1956
+ return getOrderedVisibleTableColumns(visibleTableColumns);
1771
1957
  };
1772
-
1773
- const mergeToolExecutionEvents = (startedEvent, finishedEvent) => {
1774
- const ended = getEndedTimestamp(finishedEvent);
1775
- const {
1776
- eventId
1777
- } = startedEvent;
1958
+ const restoreTableColumnWidths = (savedState, currentTableColumnWidths) => {
1959
+ return isTableColumnWidths(savedState.tableColumnWidths) ? savedState.tableColumnWidths : currentTableColumnWidths;
1960
+ };
1961
+ const restoreSavedState = (state, savedState) => {
1962
+ if (!isSavedState(savedState)) {
1963
+ return state;
1964
+ }
1965
+ return {
1966
+ ...state,
1967
+ categoryFilters: restoreCategoryFilters(savedState, state.categoryFilters),
1968
+ detailTabs: restoreDetailTabs(savedState, state.detailTabs),
1969
+ filterValue: restoreFilterValue(savedState, state.filterValue),
1970
+ selectedEventId: restoreSelectedEventId(savedState, state.selectedEventId),
1971
+ tableColumnWidths: restoreTableColumnWidths(savedState, state.tableColumnWidths),
1972
+ timelineEndSeconds: restoreTimelineEndSeconds(savedState, state.timelineEndSeconds),
1973
+ timelineStartSeconds: restoreTimelineStartSeconds(savedState, state.timelineStartSeconds),
1974
+ visibleTableColumns: restoreVisibleTableColumns(savedState, state.visibleTableColumns)
1975
+ };
1976
+ };
1977
+
1978
+ const {
1979
+ get,
1980
+ getCommandIds,
1981
+ registerCommands,
1982
+ set,
1983
+ wrapCommand,
1984
+ wrapGetter
1985
+ } = create$1();
1986
+
1987
+ const toTimeNumber = value => {
1988
+ if (typeof value === 'number' && Number.isFinite(value)) {
1989
+ return value;
1990
+ }
1991
+ if (typeof value === 'string') {
1992
+ const timestamp = Date.parse(value);
1993
+ if (!Number.isNaN(timestamp)) {
1994
+ return timestamp;
1995
+ }
1996
+ }
1997
+ return undefined;
1998
+ };
1999
+
2000
+ const getEventTime = event => {
2001
+ return toTimeNumber(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
2002
+ };
2003
+
2004
+ const getEventsWithTime = events => {
2005
+ return events.flatMap(event => {
2006
+ const time = getEventTime(event);
2007
+ if (time === undefined) {
2008
+ return [];
2009
+ }
2010
+ return [{
2011
+ event,
2012
+ time
2013
+ }];
2014
+ });
2015
+ };
2016
+
2017
+ const parseTimelineSeconds = value => {
2018
+ const trimmed = value.trim();
2019
+ if (!trimmed) {
2020
+ return undefined;
2021
+ }
2022
+ const parsed = Number.parseFloat(trimmed);
2023
+ if (!Number.isFinite(parsed) || parsed < 0) {
2024
+ return undefined;
2025
+ }
2026
+ return parsed;
2027
+ };
2028
+
2029
+ const roundSeconds = value => {
2030
+ return Number(value.toFixed(3));
2031
+ };
2032
+
2033
+ const getNormalizedRange = (durationSeconds, startValue, endValue) => {
2034
+ const parsedStart = parseTimelineSeconds(startValue);
2035
+ const parsedEnd = parseTimelineSeconds(endValue);
2036
+ if (parsedStart === undefined && parsedEnd === undefined) {
2037
+ return {
2038
+ endSeconds: null,
2039
+ hasSelection: false,
2040
+ startSeconds: null
2041
+ };
2042
+ }
2043
+ const rawStart = parsedStart ?? 0;
2044
+ const rawEnd = parsedEnd ?? durationSeconds;
2045
+ const normalizedStart = Math.max(0, Math.min(durationSeconds, Math.min(rawStart, rawEnd)));
2046
+ const normalizedEnd = Math.max(0, Math.min(durationSeconds, Math.max(rawStart, rawEnd)));
2047
+ return {
2048
+ endSeconds: roundSeconds(normalizedEnd),
2049
+ hasSelection: true,
2050
+ startSeconds: roundSeconds(normalizedStart)
2051
+ };
2052
+ };
2053
+
2054
+ const getSelectionPercent = (value, durationSeconds) => {
2055
+ if (durationSeconds <= 0) {
2056
+ return 0;
2057
+ }
2058
+ return Number((value / durationSeconds * 100).toFixed(3));
2059
+ };
2060
+
2061
+ const maxBarUnits = 8;
2062
+ const emptyTimelineInfo = {
2063
+ buckets: [],
2064
+ durationSeconds: 0,
2065
+ endSeconds: null,
2066
+ hasSelection: false,
2067
+ selectionEndPercent: null,
2068
+ selectionStartPercent: null,
2069
+ startSeconds: null
2070
+ };
2071
+ const getTimelineInfo = (events, startValue, endValue) => {
2072
+ const eventsWithTime = getEventsWithTime(events);
2073
+ if (eventsWithTime.length === 0) {
2074
+ return emptyTimelineInfo;
2075
+ }
2076
+ const baseTime = eventsWithTime[0].time;
2077
+ const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
2078
+ const durationMs = Math.max(0, lastTime - baseTime);
2079
+ const durationSeconds = roundSeconds(durationMs / 1000);
2080
+ const range = getNormalizedRange(durationSeconds, startValue, endValue);
2081
+ const bucketCount = durationSeconds === 0 ? 1 : Math.max(12, Math.min(48, Math.ceil(durationSeconds)));
2082
+ const bucketDurationMs = durationMs === 0 ? 1000 : durationMs / bucketCount;
2083
+ const counts = Array.from({
2084
+ length: bucketCount
2085
+ }).fill(0);
2086
+ for (const item of eventsWithTime) {
2087
+ const offsetMs = item.time - baseTime;
2088
+ const index = durationMs === 0 ? 0 : Math.min(bucketCount - 1, Math.floor(offsetMs / durationMs * bucketCount));
2089
+ counts[index] += 1;
2090
+ }
2091
+ const maxCount = Math.max(...counts);
2092
+ const selectionStartPercent = range.hasSelection && range.startSeconds !== null ? getSelectionPercent(range.startSeconds, durationSeconds) : null;
2093
+ const selectionEndPercent = range.hasSelection && range.endSeconds !== null ? getSelectionPercent(range.endSeconds, durationSeconds) : null;
2094
+ const buckets = counts.map((count, index) => {
2095
+ const bucketStartMs = index * bucketDurationMs;
2096
+ const bucketEndMs = index === bucketCount - 1 ? durationMs : (index + 1) * bucketDurationMs;
2097
+ const hasSelection = range.hasSelection && range.startSeconds !== null && range.endSeconds !== null;
2098
+ const selectionStartMs = hasSelection ? range.startSeconds * 1000 : 0;
2099
+ const selectionEndMs = hasSelection ? range.endSeconds * 1000 : 0;
2100
+ return {
2101
+ count,
2102
+ endSeconds: roundSeconds(bucketEndMs / 1000),
2103
+ isSelected: hasSelection && bucketEndMs >= selectionStartMs && bucketStartMs <= selectionEndMs,
2104
+ startSeconds: roundSeconds(bucketStartMs / 1000),
2105
+ unitCount: count === 0 ? 0 : Math.max(1, Math.round(count / maxCount * maxBarUnits))
2106
+ };
2107
+ });
2108
+ return {
2109
+ buckets,
2110
+ durationSeconds,
2111
+ endSeconds: range.endSeconds,
2112
+ hasSelection: range.hasSelection,
2113
+ selectionEndPercent,
2114
+ selectionStartPercent,
2115
+ startSeconds: range.startSeconds
2116
+ };
2117
+ };
2118
+
2119
+ const createDefaultState = () => {
2120
+ return {
2121
+ assetDir: '',
2122
+ categoryFilters: createCategoryFilters(),
2123
+ databaseName: 'lvce-chat-view-sessions',
2124
+ dataBaseVersion: 2,
2125
+ detailTabs: createDetailTabs(),
2126
+ errorMessage: '',
2127
+ events: [],
2128
+ eventStoreName: 'chat-view-events',
2129
+ filterValue: '',
2130
+ height: 0,
2131
+ initial: false,
2132
+ platform: 0,
2133
+ sashPointerActive: false,
2134
+ selectedEvent: null,
2135
+ selectedEventId: null,
2136
+ selectedEventIndex: null,
2137
+ sessionId: '',
2138
+ sessionIdIndexName: 'sessionId',
2139
+ showEventStreamFinishedEvents: false,
2140
+ showInputEvents: false,
2141
+ showResponsePartEvents: false,
2142
+ tableColumns: [],
2143
+ tableColumnWidths: defaultTableColumnWidths,
2144
+ tableResizerDownId: 0,
2145
+ tableWidth: defaultTableWidth,
2146
+ timelineEndSeconds: '',
2147
+ timelineEvents: [],
2148
+ timelineInfo: emptyTimelineInfo,
2149
+ timelineSelectionActive: false,
2150
+ timelineSelectionAnchorSeconds: '',
2151
+ timelineSelectionFocusSeconds: '',
2152
+ timelineStartSeconds: '',
2153
+ uid: 0,
2154
+ uri: '',
2155
+ useDevtoolsLayout: true,
2156
+ visibleTableColumns: defaultVisibleTableColumns,
2157
+ width: 0,
2158
+ x: 0,
2159
+ y: 0
2160
+ };
2161
+ };
2162
+
2163
+ 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 = {}) => {
2164
+ const defaultState = createDefaultState();
2165
+ const state = {
2166
+ ...restoreSavedState(defaultState, savedState),
2167
+ assetDir,
2168
+ databaseName,
2169
+ dataBaseVersion,
2170
+ eventStoreName,
2171
+ height,
2172
+ initial: true,
2173
+ platform,
2174
+ sessionId,
2175
+ sessionIdIndexName,
2176
+ tableColumns: [],
2177
+ uid,
2178
+ uri,
2179
+ width,
2180
+ x,
2181
+ y
2182
+ };
2183
+ set(uid, state, state);
2184
+ };
2185
+
2186
+ const RenderItems = 1;
2187
+ const RenderCss = 2;
2188
+ const RenderIncremental = 3;
2189
+
2190
+ const diff = (oldState, newState) => {
2191
+ if (oldState.categoryFilters !== newState.categoryFilters || oldState.detailTabs !== newState.detailTabs || 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.tableColumnWidths !== newState.tableColumnWidths || oldState.tableWidth !== newState.tableWidth || oldState.timelineEndSeconds !== newState.timelineEndSeconds || oldState.timelineSelectionActive !== newState.timelineSelectionActive || oldState.timelineSelectionAnchorSeconds !== newState.timelineSelectionAnchorSeconds || oldState.timelineSelectionFocusSeconds !== newState.timelineSelectionFocusSeconds || oldState.timelineStartSeconds !== newState.timelineStartSeconds || oldState.useDevtoolsLayout !== newState.useDevtoolsLayout || oldState.visibleTableColumns !== newState.visibleTableColumns || oldState.selectedEvent !== newState.selectedEvent || oldState.selectedEventIndex !== newState.selectedEventIndex || oldState.width !== newState.width || oldState.uid !== newState.uid) {
2192
+ return [RenderIncremental, RenderCss];
2193
+ }
2194
+ return [];
2195
+ };
2196
+
2197
+ const diff2 = uid => {
2198
+ const {
2199
+ newState,
2200
+ oldState
2201
+ } = get(uid);
2202
+ return diff(oldState, newState);
2203
+ };
2204
+
2205
+ const getColumnVisibilityFlags = (state, column) => {
2206
+ return isVisibleTableColumn(state.visibleTableColumns, column) ? Checked : Unchecked;
2207
+ };
2208
+
2209
+ const MenuChatDebugTableHeader = 2189;
2210
+ const handleHeaderContextMenu = async (state, eventX, eventY) => {
2211
+ await showContextMenu2(state.uid, MenuChatDebugTableHeader, eventX, eventY, {
2212
+ menuId: MenuChatDebugTableHeader
2213
+ });
2214
+ return state;
2215
+ };
2216
+
2217
+ const devtoolsRootGap = 4;
2218
+ const devtoolsTopHeight = 28;
2219
+ const devtoolsTimelineHeight = 88;
2220
+ const devtoolsTableHeaderHeight = 24;
2221
+ const getTableBodyY = (state, hasTimeline) => {
2222
+ return state.y + viewPadding + devtoolsTopHeight + devtoolsRootGap + (hasTimeline ? devtoolsTimelineHeight : 0) + devtoolsTableHeaderHeight;
2223
+ };
2224
+
2225
+ const filterEventsByTimelineRange = (events, startValue, endValue) => {
2226
+ const eventsWithTime = getEventsWithTime(events);
2227
+ if (eventsWithTime.length === 0) {
2228
+ return events;
2229
+ }
2230
+ const baseTime = eventsWithTime[0].time;
2231
+ const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
2232
+ const durationSeconds = roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
2233
+ const range = getNormalizedRange(durationSeconds, startValue, endValue);
2234
+ if (!range.hasSelection || range.startSeconds === null || range.endSeconds === null) {
2235
+ return events;
2236
+ }
2237
+ const startTime = baseTime + range.startSeconds * 1000;
2238
+ const endTime = baseTime + range.endSeconds * 1000;
2239
+ return eventsWithTime.filter(item => item.time >= startTime && item.time <= endTime).map(item => item.event);
2240
+ };
2241
+
2242
+ const hasMatchingToolName = (startedEvent, finishedEvent) => {
2243
+ if (typeof startedEvent.toolName === 'string' && typeof finishedEvent.toolName === 'string') {
2244
+ return startedEvent.toolName === finishedEvent.toolName;
2245
+ }
2246
+ return true;
2247
+ };
2248
+
2249
+ const isMatchingToolExecutionPair = (startedEvent, finishedEvent) => {
2250
+ return startedEvent.sessionId === finishedEvent.sessionId && hasMatchingToolName(startedEvent, finishedEvent);
2251
+ };
2252
+
2253
+ const startedEventType = 'tool-execution-started';
2254
+ const finishedEventType = 'tool-execution-finished';
2255
+ const mergedEventType = 'tool-execution';
2256
+
2257
+ const isToolExecutionFinishedEvent = event => {
2258
+ return event.type === finishedEventType;
2259
+ };
2260
+
2261
+ const isToolExecutionStartedEvent = event => {
2262
+ return event.type === startedEventType;
2263
+ };
2264
+
2265
+ const getTimestamp = value => {
2266
+ return typeof value === 'string' || typeof value === 'number' ? value : undefined;
2267
+ };
2268
+
2269
+ const getEndedTimestamp = event => {
2270
+ return getTimestamp(event.ended) ?? getTimestamp(event.endTime) ?? getTimestamp(event.endTimestamp) ?? getTimestamp(event.timestamp);
2271
+ };
2272
+
2273
+ const getStartedTimestamp = event => {
2274
+ return getTimestamp(event.started) ?? getTimestamp(event.startTime) ?? getTimestamp(event.startTimestamp) ?? getTimestamp(event.timestamp);
2275
+ };
2276
+
2277
+ const mergeToolExecutionEvents = (startedEvent, finishedEvent) => {
2278
+ const ended = getEndedTimestamp(finishedEvent);
2279
+ const {
2280
+ eventId
2281
+ } = startedEvent;
1778
2282
  const started = getStartedTimestamp(startedEvent);
1779
2283
  const mergedEvent = {
1780
2284
  ...startedEvent,
@@ -1847,7 +2351,7 @@ const isUiEvent = event => {
1847
2351
  return event.type.startsWith('handle-') && event.type !== 'handle-response';
1848
2352
  };
1849
2353
 
1850
- const matchesEventCategoryFilter = (event, eventCategoryFilter) => {
2354
+ const matchesSingleEventCategoryFilter = (event, eventCategoryFilter) => {
1851
2355
  switch (eventCategoryFilter) {
1852
2356
  case Network:
1853
2357
  return isNetworkEvent(event);
@@ -1861,13 +2365,19 @@ const matchesEventCategoryFilter = (event, eventCategoryFilter) => {
1861
2365
  return true;
1862
2366
  }
1863
2367
  };
2368
+ const matchesEventCategoryFilter = (event, eventCategoryFilters) => {
2369
+ if (eventCategoryFilters.length === 0 || eventCategoryFilters.includes(All)) {
2370
+ return true;
2371
+ }
2372
+ return eventCategoryFilters.some(eventCategoryFilter => matchesSingleEventCategoryFilter(event, eventCategoryFilter));
2373
+ };
1864
2374
 
1865
- const getFilteredEvents = (events, filterValue, eventCategoryFilter, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
2375
+ const getFilteredEvents = (events, filterValue, eventCategoryFilters, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents) => {
1866
2376
  const visibleEvents = getVisibleEvents(events, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
1867
2377
  const collapsedEvents = collapseToolExecutionEvents(visibleEvents);
1868
2378
  const parsedFilter = parseFilterValue(filterValue);
1869
- const activeEventCategoryFilter = parsedFilter.eventCategoryFilter === All ? eventCategoryFilter : parsedFilter.eventCategoryFilter;
1870
- const filteredByCategory = collapsedEvents.filter(event => matchesEventCategoryFilter(event, activeEventCategoryFilter));
2379
+ const activeEventCategoryFilters = parsedFilter.eventCategoryFilter === All ? eventCategoryFilters : [parsedFilter.eventCategoryFilter];
2380
+ const filteredByCategory = collapsedEvents.filter(event => matchesEventCategoryFilter(event, activeEventCategoryFilters));
1871
2381
  const {
1872
2382
  filterText
1873
2383
  } = parsedFilter;
@@ -1877,162 +2387,112 @@ const getFilteredEvents = (events, filterValue, eventCategoryFilter, showInputEv
1877
2387
  return filteredByCategory.filter(event => JSON.stringify(event).toLowerCase().includes(filterText));
1878
2388
  };
1879
2389
 
1880
- const toTimeNumber = value => {
1881
- if (typeof value === 'number' && Number.isFinite(value)) {
1882
- return value;
1883
- }
1884
- if (typeof value === 'string') {
1885
- const timestamp = Date.parse(value);
1886
- if (!Number.isNaN(timestamp)) {
1887
- return timestamp;
1888
- }
1889
- }
1890
- return undefined;
1891
- };
1892
-
1893
- const getEventTime = event => {
1894
- return toTimeNumber(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
2390
+ const loadSelectedEvent = async (_databaseName, _dataBaseVersion, _eventStoreName, sessionId, _sessionIdIndexName, eventId, type) => {
2391
+ return loadSelectedEvent$1(sessionId, eventId, type);
1895
2392
  };
1896
2393
 
1897
- const maxBarUnits = 8;
1898
- const parseTimelineSeconds = value => {
1899
- const trimmed = value.trim();
1900
- if (!trimmed) {
1901
- return undefined;
1902
- }
1903
- const parsed = Number.parseFloat(trimmed);
1904
- if (!Number.isFinite(parsed) || parsed < 0) {
1905
- return undefined;
2394
+ const getBlob = event => {
2395
+ const {
2396
+ blob
2397
+ } = event;
2398
+ if (typeof blob === 'object' && blob !== null && typeof blob.arrayBuffer === 'function' && typeof blob.slice === 'function' && typeof blob.type === 'string') {
2399
+ return blob;
1906
2400
  }
1907
- return parsed;
2401
+ return undefined;
1908
2402
  };
1909
- const roundSeconds = value => {
1910
- return Number(value.toFixed(3));
2403
+ const getMimeType = event => {
2404
+ return typeof event.mimeType === 'string' ? event.mimeType : undefined;
1911
2405
  };
1912
- const getEventsWithTime = events => {
1913
- return events.flatMap(event => {
1914
- const time = getEventTime(event);
1915
- if (time === undefined) {
1916
- return [];
1917
- }
1918
- return [{
1919
- event,
1920
- time
1921
- }];
1922
- });
2406
+ const getAltText = event => {
2407
+ return typeof event.name === 'string' && event.name ? event.name : 'image preview';
1923
2408
  };
1924
- const getTimelineDurationSeconds = events => {
1925
- const eventsWithTime = getEventsWithTime(events);
1926
- if (eventsWithTime.length === 0) {
1927
- return 0;
1928
- }
1929
- const baseTime = eventsWithTime[0].time;
1930
- const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
1931
- return roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
1932
- };
1933
- const getSelectionPercent = (value, durationSeconds) => {
1934
- if (durationSeconds <= 0) {
1935
- return 0;
1936
- }
1937
- return Number((value / durationSeconds * 100).toFixed(3));
2409
+ const isImageMimeType = mimeType => {
2410
+ return typeof mimeType === 'string' && mimeType.startsWith('image/');
1938
2411
  };
1939
- const getNormalizedRange = (durationSeconds, startValue, endValue) => {
1940
- const parsedStart = parseTimelineSeconds(startValue);
1941
- const parsedEnd = parseTimelineSeconds(endValue);
1942
- if (parsedStart === undefined && parsedEnd === undefined) {
1943
- return {
1944
- endSeconds: null,
1945
- hasSelection: false,
1946
- startSeconds: null
1947
- };
1948
- }
1949
- const rawStart = parsedStart ?? 0;
1950
- const rawEnd = parsedEnd ?? durationSeconds;
1951
- const normalizedStart = Math.max(0, Math.min(durationSeconds, Math.min(rawStart, rawEnd)));
1952
- const normalizedEnd = Math.max(0, Math.min(durationSeconds, Math.max(rawStart, rawEnd)));
1953
- return {
1954
- endSeconds: roundSeconds(normalizedEnd),
1955
- hasSelection: true,
1956
- startSeconds: roundSeconds(normalizedStart)
1957
- };
2412
+ const shouldValidateImage = mimeType => {
2413
+ return mimeType !== 'image/svg+xml';
1958
2414
  };
1959
- const filterEventsByTimelineRange = (events, startValue, endValue) => {
1960
- const eventsWithTime = getEventsWithTime(events);
1961
- if (eventsWithTime.length === 0) {
1962
- return events;
2415
+ const readBlobAsPreviewUrl = blob => {
2416
+ if (typeof FileReaderSync === 'function') {
2417
+ const reader = new FileReaderSync();
2418
+ return reader.readAsDataURL(blob);
1963
2419
  }
1964
- const baseTime = eventsWithTime[0].time;
1965
- const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
1966
- const durationSeconds = roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
1967
- const range = getNormalizedRange(durationSeconds, startValue, endValue);
1968
- if (!range.hasSelection || range.startSeconds === null || range.endSeconds === null) {
1969
- return events;
2420
+ if (typeof URL.createObjectURL === 'function') {
2421
+ return URL.createObjectURL(blob);
1970
2422
  }
1971
- const startTime = baseTime + range.startSeconds * 1000;
1972
- const endTime = baseTime + range.endSeconds * 1000;
1973
- return eventsWithTime.filter(item => item.time >= startTime && item.time <= endTime).map(item => item.event);
2423
+ throw new Error('image preview reader is not available');
1974
2424
  };
1975
- const getTimelineInfo = (events, startValue, endValue) => {
1976
- const eventsWithTime = getEventsWithTime(events);
1977
- if (eventsWithTime.length === 0) {
1978
- return {
1979
- buckets: [],
1980
- durationSeconds: 0,
1981
- endSeconds: null,
1982
- hasSelection: false,
1983
- selectionEndPercent: null,
1984
- selectionStartPercent: null,
1985
- startSeconds: null
1986
- };
1987
- }
1988
- const baseTime = eventsWithTime[0].time;
1989
- const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
1990
- const durationMs = Math.max(0, lastTime - baseTime);
1991
- const durationSeconds = roundSeconds(durationMs / 1000);
1992
- const range = getNormalizedRange(durationSeconds, startValue, endValue);
1993
- const bucketCount = durationSeconds === 0 ? 1 : Math.max(12, Math.min(48, Math.ceil(durationSeconds)));
1994
- const bucketDurationMs = durationMs === 0 ? 1000 : durationMs / bucketCount;
1995
- const counts = Array.from({
1996
- length: bucketCount
1997
- }).fill(0);
1998
- for (const item of eventsWithTime) {
1999
- const offsetMs = item.time - baseTime;
2000
- const index = durationMs === 0 ? 0 : Math.min(bucketCount - 1, Math.floor(offsetMs / durationMs * bucketCount));
2001
- counts[index] += 1;
2425
+ const validateImage = async blob => {
2426
+ if (typeof createImageBitmap !== 'function') {
2427
+ return;
2002
2428
  }
2003
- const maxCount = Math.max(...counts);
2004
- const selectionStartPercent = range.hasSelection && range.startSeconds !== null ? getSelectionPercent(range.startSeconds, durationSeconds) : null;
2005
- const selectionEndPercent = range.hasSelection && range.endSeconds !== null ? getSelectionPercent(range.endSeconds, durationSeconds) : null;
2006
- const buckets = counts.map((count, index) => {
2007
- const bucketStartMs = index * bucketDurationMs;
2008
- const bucketEndMs = index === bucketCount - 1 ? durationMs : (index + 1) * bucketDurationMs;
2009
- const hasSelection = range.hasSelection && range.startSeconds !== null && range.endSeconds !== null;
2010
- const selectionStartMs = hasSelection ? range.startSeconds * 1000 : 0;
2011
- const selectionEndMs = hasSelection ? range.endSeconds * 1000 : 0;
2429
+ const bitmap = await createImageBitmap(blob);
2430
+ bitmap.close?.();
2431
+ };
2432
+ const getAttachmentImagePreview = async event => {
2433
+ if (event.type !== 'chat-attachment-added') {
2434
+ return undefined;
2435
+ }
2436
+ const blob = getBlob(event);
2437
+ const mimeType = getMimeType(event);
2438
+ if (!blob || !isImageMimeType(mimeType)) {
2439
+ return undefined;
2440
+ }
2441
+ try {
2442
+ if (shouldValidateImage(mimeType)) {
2443
+ await validateImage(blob);
2444
+ }
2012
2445
  return {
2013
- count,
2014
- endSeconds: roundSeconds(bucketEndMs / 1000),
2015
- isSelected: hasSelection && bucketEndMs >= selectionStartMs && bucketStartMs <= selectionEndMs,
2016
- startSeconds: roundSeconds(bucketStartMs / 1000),
2017
- unitCount: count === 0 ? 0 : Math.max(1, Math.round(count / maxCount * maxBarUnits))
2446
+ alt: getAltText(event),
2447
+ previewType: 'image',
2448
+ src: readBlobAsPreviewUrl(blob)
2018
2449
  };
2450
+ } catch {
2451
+ return imageCouldNotBeLoaded();
2452
+ }
2453
+ };
2454
+
2455
+ const selectedEventPreviewSymbol = Symbol('selectedEventPreview');
2456
+ const getSelectedEventPreview = event => {
2457
+ return event[selectedEventPreviewSymbol];
2458
+ };
2459
+ const setSelectedEventPreview = (event, preview) => {
2460
+ if (preview === undefined) {
2461
+ return event;
2462
+ }
2463
+ Object.defineProperty(event, selectedEventPreviewSymbol, {
2464
+ configurable: true,
2465
+ enumerable: false,
2466
+ value: preview,
2467
+ writable: true
2019
2468
  });
2020
- return {
2021
- buckets,
2022
- durationSeconds,
2023
- endSeconds: range.endSeconds,
2024
- hasSelection: range.hasSelection,
2025
- selectionEndPercent,
2026
- selectionStartPercent,
2027
- startSeconds: range.startSeconds
2028
- };
2469
+ return event;
2470
+ };
2471
+
2472
+ const withPreparedSelectedEventPreview = async event => {
2473
+ const preview = await getAttachmentImagePreview(event);
2474
+ return setSelectedEventPreview(event, preview);
2029
2475
  };
2030
2476
 
2477
+ const selectEventAtIndexDependencies = {
2478
+ loadSelectedEvent: loadSelectedEvent
2479
+ };
2031
2480
  const getCurrentEvents$3 = state => {
2032
- const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
2033
- return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
2481
+ const {
2482
+ events,
2483
+ filterValue,
2484
+ showEventStreamFinishedEvents,
2485
+ showInputEvents,
2486
+ showResponsePartEvents,
2487
+ timelineEndSeconds,
2488
+ timelineStartSeconds
2489
+ } = state;
2490
+ const eventCategoryFilters = getSelectedEventCategoryFilters(state.categoryFilters);
2491
+ const filteredEvents = getFilteredEvents(events, filterValue, eventCategoryFilters, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
2492
+ return filterEventsByTimelineRange(filteredEvents, timelineStartSeconds, timelineEndSeconds);
2034
2493
  };
2035
- const selectEventAtIndex = async (state, selectedEventIndex, dependencies) => {
2494
+ const selectEventAtIndex = async (state, selectedEventIndex, dependencies = selectEventAtIndexDependencies) => {
2495
+ const selectedDetailTab = getSelectedDetailTab(state.detailTabs);
2036
2496
  const currentEvents = getCurrentEvents$3(state);
2037
2497
  const selectedEvent = currentEvents[selectedEventIndex];
2038
2498
  if (!selectedEvent) {
@@ -2052,22 +2512,17 @@ const selectEventAtIndex = async (state, selectedEventIndex, dependencies) => {
2052
2512
  };
2053
2513
  }
2054
2514
  const selectedEventDetails = await dependencies.loadSelectedEvent(state.databaseName, state.dataBaseVersion, state.eventStoreName, state.sessionId, state.sessionIdIndexName, selectedEvent.eventId, selectedEvent.type);
2515
+ const resolvedSelectedEvent = await withPreparedSelectedEventPreview(selectedEventDetails ?? selectedEvent);
2055
2516
  return {
2056
2517
  ...state,
2057
- selectedEvent: selectedEventDetails ?? selectedEvent,
2518
+ detailTabs: createDetailTabs(selectedDetailTab, resolvedSelectedEvent),
2519
+ selectedEvent: resolvedSelectedEvent,
2058
2520
  selectedEventId: selectedEvent.eventId,
2059
2521
  selectedEventIndex
2060
2522
  };
2061
2523
  };
2062
2524
 
2063
- const devtoolsRootGap = 4;
2064
- const devtoolsTopHeight = 28;
2065
- const devtoolsTimelineHeight = 88;
2066
- const devtoolsTableHeaderHeight = 24;
2067
2525
  const devtoolsTableRowHeight = 24;
2068
- const getTableBodyY = (state, hasTimeline) => {
2069
- return state.y + viewPadding + devtoolsTopHeight + devtoolsRootGap + (hasTimeline ? devtoolsTimelineHeight : 0) + devtoolsTableHeaderHeight;
2070
- };
2071
2526
  const getTableBodyEventIndex = (state, eventX, eventY) => {
2072
2527
  if (!state.useDevtoolsLayout) {
2073
2528
  return -1;
@@ -2102,45 +2557,48 @@ const handleTableBodyContextMenu = async (state, eventX, eventY) => {
2102
2557
  return state;
2103
2558
  };
2104
2559
 
2105
- const getColumnVisibilityFlags = (state, column) => {
2106
- return isVisibleTableColumn(state.visibleTableColumns, column) ? Checked : Unchecked;
2560
+ const getMenuEntriesTableHeader = state => {
2561
+ return [{
2562
+ args: [Type],
2563
+ command: 'ChatDebug.toggleTableColumnVisibility',
2564
+ flags: getColumnVisibilityFlags(state, Type),
2565
+ id: 'type',
2566
+ label: type()
2567
+ }, {
2568
+ args: [Duration],
2569
+ command: 'ChatDebug.toggleTableColumnVisibility',
2570
+ flags: getColumnVisibilityFlags(state, Duration),
2571
+ id: 'duration',
2572
+ label: duration()
2573
+ }, {
2574
+ args: [Status],
2575
+ command: 'ChatDebug.toggleTableColumnVisibility',
2576
+ flags: getColumnVisibilityFlags(state, Status),
2577
+ id: 'status',
2578
+ label: status()
2579
+ }, {
2580
+ args: [],
2581
+ command: 'ChatDebug.resetTableColumns',
2582
+ flags: None$1,
2583
+ id: 'reset-columns',
2584
+ label: resetColumns()
2585
+ }];
2586
+ };
2587
+ const getMenuEntriesTableBody = props => {
2588
+ return [{
2589
+ args: [props.eventIndex],
2590
+ command: 'ChatDebug.handleTableRowCopy',
2591
+ flags: None$1,
2592
+ id: 'copy',
2593
+ label: copy()
2594
+ }];
2107
2595
  };
2108
2596
  const getMenuEntries2 = (state, props) => {
2109
2597
  if (props.menuId === MenuChatDebugTableHeader) {
2110
- return [{
2111
- args: [Type],
2112
- command: 'ChatDebug.toggleTableColumnVisibility',
2113
- flags: getColumnVisibilityFlags(state, Type),
2114
- id: 'type',
2115
- label: type()
2116
- }, {
2117
- args: [Duration],
2118
- command: 'ChatDebug.toggleTableColumnVisibility',
2119
- flags: getColumnVisibilityFlags(state, Duration),
2120
- id: 'duration',
2121
- label: duration()
2122
- }, {
2123
- args: [Status],
2124
- command: 'ChatDebug.toggleTableColumnVisibility',
2125
- flags: getColumnVisibilityFlags(state, Status),
2126
- id: 'status',
2127
- label: status()
2128
- }, {
2129
- args: [],
2130
- command: 'ChatDebug.resetTableColumns',
2131
- flags: None,
2132
- id: 'reset-columns',
2133
- label: resetColumns()
2134
- }];
2598
+ return getMenuEntriesTableHeader(state);
2135
2599
  }
2136
2600
  if (props.menuId === MenuChatDebugTableBody) {
2137
- return [{
2138
- args: [],
2139
- command: 'ChatDebug.copy',
2140
- flags: None,
2141
- id: 'copy',
2142
- label: copy()
2143
- }];
2601
+ return getMenuEntriesTableBody(props);
2144
2602
  }
2145
2603
  return [];
2146
2604
  };
@@ -2149,50 +2607,9 @@ const getMenuIds = () => {
2149
2607
  return [MenuChatDebugTableHeader, MenuChatDebugTableBody, 556, 557];
2150
2608
  };
2151
2609
 
2152
- const getErrorMessage = error => {
2153
- if (error instanceof Error) {
2154
- return error.message;
2155
- }
2156
- if (typeof error === 'string') {
2157
- return error;
2158
- }
2159
- if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
2160
- return error.message;
2161
- }
2162
- return undefined;
2163
- };
2164
- const getFailedToLoadMessage = (sessionId, error) => {
2165
- const errorMessage = getErrorMessage(error);
2166
- if (errorMessage) {
2167
- return failedToLoadChatDebugSessionWithError(sessionId, errorMessage);
2168
- }
2169
- return failedToLoadChatDebugSession(sessionId);
2170
- };
2171
-
2172
- const ParseChatDebugUriErrorCode = {
2173
- InvalidSessionId: 'invalid-session-id',
2174
- InvalidUriEncoding: 'invalid-uri-encoding',
2175
- InvalidUriFormat: 'invalid-uri-format',
2176
- MissingUri: 'missing-uri'
2177
- };
2178
-
2179
- const getInvalidUriMessage = (uri, code) => {
2180
- if (code === ParseChatDebugUriErrorCode.MissingUri) {
2181
- return unableToLoadDebugSessionMissingUri();
2182
- }
2183
- return unableToLoadDebugSessionInvalidUri(uri);
2184
- };
2185
-
2186
- const getSessionNotFoundMessage = sessionId => {
2187
- return noChatSessionFound(sessionId);
2188
- };
2189
-
2190
- const listChatViewEventsDependencies = {
2191
- listChatViewEventsFromWorker: listChatViewEvents$1
2192
- };
2193
2610
  const listChatViewEvents = async (sessionId, _databaseName, _dataBaseVersion, _eventStoreName, _sessionIdIndexName) => {
2194
2611
  try {
2195
- return await listChatViewEventsDependencies.listChatViewEventsFromWorker(sessionId);
2612
+ return await listChatViewEvents$1(sessionId);
2196
2613
  } catch (error) {
2197
2614
  return {
2198
2615
  error,
@@ -2201,11 +2618,21 @@ const listChatViewEvents = async (sessionId, _databaseName, _dataBaseVersion, _e
2201
2618
  }
2202
2619
  };
2203
2620
 
2204
- const loadSelectedEventDependencies = {
2205
- loadSelectedEventFromWorker: loadSelectedEvent$1
2621
+ const loadEventsDependencies = {
2622
+ listChatViewEvents: listChatViewEvents,
2623
+ loadSelectedEvent: loadSelectedEvent
2624
+ };
2625
+
2626
+ const ParseChatDebugUriErrorCode = {
2627
+ InvalidSessionId: 1,
2628
+ InvalidUriEncoding: 2,
2629
+ InvalidUriFormat: 3,
2630
+ MissingUri: 4
2206
2631
  };
2207
- const loadSelectedEvent = async (_databaseName, _dataBaseVersion, _eventStoreName, sessionId, _sessionIdIndexName, eventId, type) => {
2208
- return loadSelectedEventDependencies.loadSelectedEventFromWorker(sessionId, eventId, type);
2632
+
2633
+ const ParseChatDebugUriResultType = {
2634
+ Error: 2,
2635
+ Success: 1
2209
2636
  };
2210
2637
 
2211
2638
  const chatDebugUriPattern = /^chat-debug:\/\/([^/?#]+)$/;
@@ -2215,7 +2642,7 @@ const parseChatDebugUri = uri => {
2215
2642
  return {
2216
2643
  code: ParseChatDebugUriErrorCode.MissingUri,
2217
2644
  message: missingUri(),
2218
- type: 'error'
2645
+ type: ParseChatDebugUriResultType.Error
2219
2646
  };
2220
2647
  }
2221
2648
  const match = uri.match(chatDebugUriPattern);
@@ -2223,7 +2650,7 @@ const parseChatDebugUri = uri => {
2223
2650
  return {
2224
2651
  code: ParseChatDebugUriErrorCode.InvalidUriFormat,
2225
2652
  message: invalidUriFormat(),
2226
- type: 'error'
2653
+ type: ParseChatDebugUriResultType.Error
2227
2654
  };
2228
2655
  }
2229
2656
  const encodedSessionId = match[1];
@@ -2234,30 +2661,120 @@ const parseChatDebugUri = uri => {
2234
2661
  return {
2235
2662
  code: ParseChatDebugUriErrorCode.InvalidUriEncoding,
2236
2663
  message: invalidUriEncoding(),
2237
- type: 'error'
2664
+ type: ParseChatDebugUriResultType.Error
2238
2665
  };
2239
2666
  }
2240
2667
  if (!sessionId || invalidSessionIdPattern.test(sessionId)) {
2241
2668
  return {
2242
2669
  code: ParseChatDebugUriErrorCode.InvalidSessionId,
2243
2670
  message: invalidSessionId(),
2244
- type: 'error'
2671
+ type: ParseChatDebugUriResultType.Error
2245
2672
  };
2246
2673
  }
2247
2674
  return {
2248
2675
  sessionId,
2249
- type: 'success'
2676
+ type: ParseChatDebugUriResultType.Success
2250
2677
  };
2251
2678
  };
2252
2679
 
2253
- const loadEventsDependencies = {
2254
- listChatViewEvents: listChatViewEvents,
2255
- loadSelectedEvent: loadSelectedEvent
2680
+ const getSessionIdFromUri = state => {
2681
+ const parsed = parseChatDebugUri(state.uri);
2682
+ if (parsed.type === ParseChatDebugUriResultType.Error) {
2683
+ return undefined;
2684
+ }
2685
+ return parsed.sessionId;
2686
+ };
2687
+
2688
+ const getInvalidUriMessage = (uri, code) => {
2689
+ if (code === ParseChatDebugUriErrorCode.MissingUri) {
2690
+ return unableToLoadDebugSessionMissingUri();
2691
+ }
2692
+ return unableToLoadDebugSessionInvalidUri(uri);
2693
+ };
2694
+
2695
+ const getEffectiveTimelineRange = (timelineStartSeconds, timelineEndSeconds, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds) => {
2696
+ if (!timelineSelectionActive) {
2697
+ return {
2698
+ endSeconds: timelineEndSeconds,
2699
+ startSeconds: timelineStartSeconds
2700
+ };
2701
+ }
2702
+ return {
2703
+ endSeconds: timelineSelectionFocusSeconds,
2704
+ startSeconds: timelineSelectionAnchorSeconds
2705
+ };
2706
+ };
2707
+
2708
+ const getTimelineEvents = state => {
2709
+ const {
2710
+ events,
2711
+ filterValue,
2712
+ showEventStreamFinishedEvents,
2713
+ showInputEvents,
2714
+ showResponsePartEvents
2715
+ } = state;
2716
+ const eventCategoryFilters = getSelectedEventCategoryFilters(state.categoryFilters);
2717
+ return getFilteredEvents(events, filterValue, eventCategoryFilters, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
2718
+ };
2719
+
2720
+ const getStateWithTimelineInfo = state => {
2721
+ const timelineEvents = getTimelineEvents(state);
2722
+ const effectiveRange = getEffectiveTimelineRange(state.timelineStartSeconds, state.timelineEndSeconds, state.timelineSelectionActive, state.timelineSelectionAnchorSeconds, state.timelineSelectionFocusSeconds);
2723
+ const timelineInfo = getTimelineInfo(timelineEvents, effectiveRange.startSeconds, effectiveRange.endSeconds);
2724
+ return {
2725
+ ...state,
2726
+ timelineEvents,
2727
+ timelineInfo
2728
+ };
2729
+ };
2730
+
2731
+ const getStateWithInvalidUri = state => {
2732
+ const parsed = parseChatDebugUri(state.uri);
2733
+ if (parsed.type !== ParseChatDebugUriResultType.Error) {
2734
+ return state;
2735
+ }
2736
+ return getStateWithTimelineInfo({
2737
+ ...state,
2738
+ errorMessage: getInvalidUriMessage(state.uri, parsed.code),
2739
+ events: [],
2740
+ initial: false,
2741
+ selectedEvent: null,
2742
+ selectedEventId: null,
2743
+ selectedEventIndex: null,
2744
+ sessionId: ''
2745
+ });
2746
+ };
2747
+
2748
+ const getErrorMessage = error => {
2749
+ if (error instanceof Error) {
2750
+ return error.message;
2751
+ }
2752
+ if (typeof error === 'string') {
2753
+ return error;
2754
+ }
2755
+ if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
2756
+ return error.message;
2757
+ }
2758
+ return undefined;
2759
+ };
2760
+ const getFailedToLoadMessage = (sessionId, error) => {
2761
+ const errorMessage = getErrorMessage(error);
2762
+ if (errorMessage) {
2763
+ return failedToLoadChatDebugSessionWithError(sessionId, errorMessage);
2764
+ }
2765
+ return failedToLoadChatDebugSession(sessionId);
2766
+ };
2767
+
2768
+ const getSessionNotFoundMessage = sessionId => {
2769
+ return noChatSessionFound(sessionId);
2256
2770
  };
2771
+
2257
2772
  const getCurrentEvents$2 = state => {
2258
- const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
2773
+ const eventCategoryFilters = getSelectedEventCategoryFilters(state.categoryFilters);
2774
+ const filteredEvents = getFilteredEvents(state.events, state.filterValue, eventCategoryFilters, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
2259
2775
  return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
2260
2776
  };
2777
+
2261
2778
  const restoreSelectedEvent = async state => {
2262
2779
  if (state.selectedEventId === null) {
2263
2780
  return {
@@ -2286,36 +2803,15 @@ const restoreSelectedEvent = async state => {
2286
2803
  };
2287
2804
  }
2288
2805
  const selectedEventDetails = await loadEventsDependencies.loadSelectedEvent(state.databaseName, state.dataBaseVersion, state.eventStoreName, state.sessionId, state.sessionIdIndexName, selectedEvent.eventId, selectedEvent.type);
2806
+ const resolvedSelectedEvent = selectedEventDetails ? await withPreparedSelectedEventPreview(selectedEventDetails) : null;
2289
2807
  return {
2290
2808
  ...state,
2291
- selectedEvent: selectedEventDetails,
2809
+ selectedEvent: resolvedSelectedEvent,
2292
2810
  selectedEventId: selectedEvent.eventId,
2293
2811
  selectedEventIndex
2294
2812
  };
2295
2813
  };
2296
- const getStateWithInvalidUri = state => {
2297
- const parsed = parseChatDebugUri(state.uri);
2298
- if (parsed.type !== 'error') {
2299
- return state;
2300
- }
2301
- return {
2302
- ...state,
2303
- errorMessage: getInvalidUriMessage(state.uri, parsed.code),
2304
- events: [],
2305
- initial: false,
2306
- selectedEvent: null,
2307
- selectedEventId: null,
2308
- selectedEventIndex: null,
2309
- sessionId: ''
2310
- };
2311
- };
2312
- const getSessionIdFromUri = state => {
2313
- const parsed = parseChatDebugUri(state.uri);
2314
- if (parsed.type === 'error') {
2315
- return undefined;
2316
- }
2317
- return parsed.sessionId;
2318
- };
2814
+
2319
2815
  const loadEventsForSessionId = async (state, sessionId) => {
2320
2816
  const {
2321
2817
  databaseName,
@@ -2325,7 +2821,7 @@ const loadEventsForSessionId = async (state, sessionId) => {
2325
2821
  } = state;
2326
2822
  const result = await loadEventsDependencies.listChatViewEvents(sessionId, databaseName, dataBaseVersion, eventStoreName, sessionIdIndexName);
2327
2823
  if (result.type === 'error') {
2328
- return {
2824
+ return getStateWithTimelineInfo({
2329
2825
  ...state,
2330
2826
  errorMessage: getFailedToLoadMessage(sessionId, result.error),
2331
2827
  events: [],
@@ -2334,13 +2830,13 @@ const loadEventsForSessionId = async (state, sessionId) => {
2334
2830
  selectedEventId: null,
2335
2831
  selectedEventIndex: null,
2336
2832
  sessionId
2337
- };
2833
+ });
2338
2834
  }
2339
2835
  const {
2340
2836
  events
2341
2837
  } = result;
2342
2838
  if (events.length === 0) {
2343
- return {
2839
+ return getStateWithTimelineInfo({
2344
2840
  ...state,
2345
2841
  errorMessage: getSessionNotFoundMessage(sessionId),
2346
2842
  events: [],
@@ -2349,17 +2845,18 @@ const loadEventsForSessionId = async (state, sessionId) => {
2349
2845
  selectedEventId: null,
2350
2846
  selectedEventIndex: null,
2351
2847
  sessionId
2352
- };
2848
+ });
2353
2849
  }
2354
- const nextState = {
2850
+ const nextState = getStateWithTimelineInfo({
2355
2851
  ...state,
2356
2852
  errorMessage: '',
2357
2853
  events,
2358
2854
  initial: false,
2359
2855
  sessionId
2360
- };
2856
+ });
2361
2857
  return restoreSelectedEvent(nextState);
2362
2858
  };
2859
+
2363
2860
  const loadEventsFromUri = async state => {
2364
2861
  const sessionId = getSessionIdFromUri(state);
2365
2862
  if (!sessionId) {
@@ -2367,6 +2864,7 @@ const loadEventsFromUri = async state => {
2367
2864
  }
2368
2865
  return loadEventsForSessionId(state, sessionId);
2369
2866
  };
2867
+
2370
2868
  const refreshEvents = async state => {
2371
2869
  const sessionId = state.sessionId || getSessionIdFromUri(state);
2372
2870
  if (!sessionId) {
@@ -2403,23 +2901,30 @@ const handleDetailsTopContextMenu = state => {
2403
2901
  return state;
2404
2902
  };
2405
2903
 
2406
- const handleDetailTab = (state, value) => {
2904
+ const selectDetailTab = (state, value) => {
2407
2905
  if (!isDetailTab(value)) {
2408
2906
  return state;
2409
2907
  }
2908
+ const detailTabs = selectDetailTab$1(state.detailTabs, value);
2909
+ if (detailTabs === state.detailTabs) {
2910
+ return state;
2911
+ }
2410
2912
  return {
2411
2913
  ...state,
2412
- selectedDetailTab: value
2914
+ detailTabs
2413
2915
  };
2414
2916
  };
2415
2917
 
2416
2918
  const getCurrentEvents$1 = state => {
2417
- const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
2919
+ const eventCategoryFilters = getSelectedEventCategoryFilters(state.categoryFilters);
2920
+ const filteredEvents = getFilteredEvents(state.events, state.filterValue, eventCategoryFilters, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
2418
2921
  return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
2419
2922
  };
2923
+
2420
2924
  const getEventIndexByStableId$1 = (events, event) => {
2421
2925
  return events.findIndex(candidate => candidate.eventId === event.eventId);
2422
2926
  };
2927
+
2423
2928
  const getSelectedEventIndex$1 = state => {
2424
2929
  const {
2425
2930
  selectedEventIndex
@@ -2438,6 +2943,7 @@ const getSelectedEventIndex$1 = state => {
2438
2943
  }
2439
2944
  return newIndex;
2440
2945
  };
2946
+
2441
2947
  const getPreservedSelectedEventIndex$1 = (oldState, newState) => {
2442
2948
  const {
2443
2949
  selectedEventIndex
@@ -2457,30 +2963,30 @@ const getPreservedSelectedEventIndex$1 = (oldState, newState) => {
2457
2963
  }
2458
2964
  return newIndex;
2459
2965
  };
2966
+
2460
2967
  const withPreservedSelection$1 = (state, nextState) => {
2461
- const selectedEventIndex = getPreservedSelectedEventIndex$1(state, nextState);
2968
+ const nextStateWithTimelineInfo = getStateWithTimelineInfo(nextState);
2969
+ const selectedEventIndex = getPreservedSelectedEventIndex$1(state, nextStateWithTimelineInfo);
2462
2970
  return {
2463
- ...nextState,
2971
+ ...nextStateWithTimelineInfo,
2464
2972
  selectedEvent: selectedEventIndex === null ? null : state.selectedEvent,
2465
2973
  selectedEventId: selectedEventIndex === null ? null : state.selectedEventId,
2466
2974
  selectedEventIndex
2467
2975
  };
2468
2976
  };
2469
2977
 
2470
- const handleEventCategoryFilter = (state, value) => {
2978
+ const handleEventCategoryFilter = (state, value, ctrlKey = false, metaKey = false) => {
2979
+ const categoryFilters = selectCategoryFilter(state.categoryFilters, value || All, ctrlKey || metaKey);
2980
+ if (categoryFilters === state.categoryFilters) {
2981
+ return state;
2982
+ }
2471
2983
  const nextState = {
2472
2984
  ...state,
2473
- eventCategoryFilter: value || All
2985
+ categoryFilters
2474
2986
  };
2475
2987
  return withPreservedSelection$1(state, nextState);
2476
2988
  };
2477
2989
 
2478
- const handleEventRowClickDependencies = {
2479
- loadSelectedEvent: loadSelectedEvent
2480
- };
2481
- const isPrimaryButton = button => {
2482
- return button === 0;
2483
- };
2484
2990
  const parseSelectedEventIndex$1 = value => {
2485
2991
  const parsed = Number.parseInt(value, 10);
2486
2992
  if (Number.isNaN(parsed) || parsed < 0) {
@@ -2488,6 +2994,13 @@ const parseSelectedEventIndex$1 = value => {
2488
2994
  }
2489
2995
  return parsed;
2490
2996
  };
2997
+
2998
+ const handleEventRowClickDependencies = {
2999
+ loadSelectedEvent: loadSelectedEvent
3000
+ };
3001
+ const isPrimaryButton = button => {
3002
+ return button === 0;
3003
+ };
2491
3004
  const handleEventRowClick = async (state, value, button = 0) => {
2492
3005
  if (!isPrimaryButton(button)) {
2493
3006
  return state;
@@ -2503,22 +3016,9 @@ const getBoolean = value => {
2503
3016
  return value === true || value === 'true' || value === 'on' || value === '1';
2504
3017
  };
2505
3018
 
2506
- const Filter = 'filter';
2507
- const EventCategoryFilter = 'eventCategoryFilter';
2508
- const ShowEventStreamFinishedEvents = 'showEventStreamFinishedEvents';
2509
- const ShowInputEvents = 'showInputEvents';
2510
- const ShowResponsePartEvents = 'showResponsePartEvents';
2511
- const UseDevtoolsLayout = 'useDevtoolsLayout';
2512
- const SelectedEventIndex = 'selectedEventIndex';
2513
- const CloseDetails = 'closeDetails';
2514
- const DetailTab = 'detailTab';
2515
- const TimelineStartSeconds = 'timelineStartSeconds';
2516
- const TimelineEndSeconds = 'timelineEndSeconds';
2517
- const TimelineRangePreset = 'timelineRangePreset';
2518
- const Refresh = 'refresh';
2519
-
2520
3019
  const getCurrentEvents = state => {
2521
- const filteredEvents = getFilteredEvents(state.events, state.filterValue, state.eventCategoryFilter, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
3020
+ const eventCategoryFilters = getSelectedEventCategoryFilters(state.categoryFilters);
3021
+ const filteredEvents = getFilteredEvents(state.events, state.filterValue, eventCategoryFilters, state.showInputEvents, state.showResponsePartEvents, state.showEventStreamFinishedEvents);
2522
3022
  return filterEventsByTimelineRange(filteredEvents, state.timelineStartSeconds, state.timelineEndSeconds);
2523
3023
  };
2524
3024
  const parseTimelineRangePreset$1 = value => {
@@ -2582,9 +3082,10 @@ const parseSelectedEventIndex = value => {
2582
3082
  return parsed;
2583
3083
  };
2584
3084
  const withPreservedSelection = (state, nextState) => {
2585
- const selectedEventIndex = getPreservedSelectedEventIndex(state, nextState);
3085
+ const nextStateWithTimelineInfo = getStateWithTimelineInfo(nextState);
3086
+ const selectedEventIndex = getPreservedSelectedEventIndex(state, nextStateWithTimelineInfo);
2586
3087
  return {
2587
- ...nextState,
3088
+ ...nextStateWithTimelineInfo,
2588
3089
  selectedEvent: selectedEventIndex === null ? null : state.selectedEvent,
2589
3090
  selectedEventId: selectedEventIndex === null ? null : state.selectedEventId,
2590
3091
  selectedEventIndex
@@ -2599,9 +3100,13 @@ const handleInput = (state, name, value, checked) => {
2599
3100
  return withPreservedSelection(state, nextState);
2600
3101
  }
2601
3102
  if (name === EventCategoryFilter) {
3103
+ const categoryFilters = selectCategoryFilter(state.categoryFilters, value || All);
3104
+ if (categoryFilters === state.categoryFilters) {
3105
+ return state;
3106
+ }
2602
3107
  const nextState = {
2603
3108
  ...state,
2604
- eventCategoryFilter: value || All
3109
+ categoryFilters
2605
3110
  };
2606
3111
  return withPreservedSelection(state, nextState);
2607
3112
  }
@@ -2679,19 +3184,32 @@ const handleInput = (state, name, value, checked) => {
2679
3184
  if (!isDetailTab(value)) {
2680
3185
  return state;
2681
3186
  }
3187
+ const detailTabs = selectDetailTab$1(state.detailTabs, value);
3188
+ if (detailTabs === state.detailTabs) {
3189
+ return state;
3190
+ }
2682
3191
  return {
2683
3192
  ...state,
2684
- selectedDetailTab: value
3193
+ detailTabs
2685
3194
  };
2686
3195
  }
2687
3196
  return state;
2688
3197
  };
2689
3198
 
2690
3199
  const handleSashPointerDown = (state, eventX, eventY) => {
2691
- return state;
3200
+ if (state.sashPointerActive) {
3201
+ return state;
3202
+ }
3203
+ return {
3204
+ ...state,
3205
+ sashPointerActive: true
3206
+ };
2692
3207
  };
2693
3208
 
2694
3209
  const handleSashPointerMove = (state, eventX, eventY) => {
3210
+ if (!state.sashPointerActive) {
3211
+ return state;
3212
+ }
2695
3213
  return {
2696
3214
  ...state,
2697
3215
  tableWidth: getTableWidthFromClientX(state.x, state.width, eventX)
@@ -2699,6 +3217,60 @@ const handleSashPointerMove = (state, eventX, eventY) => {
2699
3217
  };
2700
3218
 
2701
3219
  const handleSashPointerUp = (state, eventX, eventY) => {
3220
+ if (!state.sashPointerActive) {
3221
+ return state;
3222
+ }
3223
+ return {
3224
+ ...state,
3225
+ sashPointerActive: false
3226
+ };
3227
+ };
3228
+
3229
+ const getTableResizerId = name => {
3230
+ switch (name) {
3231
+ case 'ResizerOne':
3232
+ return 1;
3233
+ case 'ResizerTwo':
3234
+ return 2;
3235
+ default:
3236
+ return 0;
3237
+ }
3238
+ };
3239
+ const handleTableResizerPointerDown = (state, name, clientX) => {
3240
+ return {
3241
+ ...state,
3242
+ tableResizerDownId: getTableResizerId(name)
3243
+ };
3244
+ };
3245
+
3246
+ const handleTableResizerPointerMove = (state, clientX) => {
3247
+ if (!state.tableResizerDownId) {
3248
+ return state;
3249
+ }
3250
+ return {
3251
+ ...state,
3252
+ tableColumnWidths: getResizedTableColumnWidths(state.width, state.tableWidth, state.visibleTableColumns, state.tableColumnWidths, state.x, clientX, state.tableResizerDownId)
3253
+ };
3254
+ };
3255
+
3256
+ const handleTableResizerPointerUp = state => {
3257
+ if (!state.tableResizerDownId) {
3258
+ return state;
3259
+ }
3260
+ return {
3261
+ ...state,
3262
+ tableResizerDownId: 0
3263
+ };
3264
+ };
3265
+
3266
+ const handleTableRowCopy = async (state, eventIndex) => {
3267
+ const currentEvents = getCurrentEvents$3(state);
3268
+ const event = currentEvents[eventIndex];
3269
+ if (!event) {
3270
+ return state;
3271
+ }
3272
+ const text = JSON.stringify(event, null, 2);
3273
+ await writeClipBoardText(text);
2702
3274
  return state;
2703
3275
  };
2704
3276
 
@@ -2707,12 +3279,12 @@ const handleTimelineContextMenu = state => {
2707
3279
  };
2708
3280
 
2709
3281
  const clearTimelineSelectionState = state => {
2710
- return {
3282
+ return getStateWithTimelineInfo({
2711
3283
  ...state,
2712
3284
  timelineSelectionActive: false,
2713
3285
  timelineSelectionAnchorSeconds: '',
2714
3286
  timelineSelectionFocusSeconds: ''
2715
- };
3287
+ });
2716
3288
  };
2717
3289
 
2718
3290
  const parseTimelineRangePreset = value => {
@@ -2755,16 +3327,10 @@ const handleTimelineDoubleClick = state => {
2755
3327
  return clearTimelineSelectionState(nextState);
2756
3328
  };
2757
3329
 
2758
- const getTimelineEvents = state => {
2759
- const {
2760
- eventCategoryFilter,
2761
- events,
2762
- filterValue,
2763
- showEventStreamFinishedEvents,
2764
- showInputEvents,
2765
- showResponsePartEvents
2766
- } = state;
2767
- return getFilteredEvents(events, filterValue, eventCategoryFilter, showInputEvents, showResponsePartEvents, showEventStreamFinishedEvents);
3330
+ const trailingZeroFractionRegex = /\.0+$/;
3331
+ const trailingFractionZeroRegex = /(\.\d*?)0+$/;
3332
+ const formatTimelinePresetValue = value => {
3333
+ return value.toFixed(3).replace(trailingZeroFractionRegex, '').replace(trailingFractionZeroRegex, '$1');
2768
3334
  };
2769
3335
 
2770
3336
  const getTimelineLeft = state => {
@@ -2774,10 +3340,14 @@ const getTimelineWidth = state => {
2774
3340
  return Math.max(0, getMainWidth(state.width) - timelineHorizontalPadding * 2);
2775
3341
  };
2776
3342
 
2777
- const trailingZeroFractionRegex = /\.0+$/;
2778
- const trailingFractionZeroRegex = /(\.\d*?)0+$/;
2779
- const formatTimelinePresetValue = value => {
2780
- return value.toFixed(3).replace(trailingZeroFractionRegex, '').replace(trailingFractionZeroRegex, '$1');
3343
+ const getTimelineDurationSeconds = events => {
3344
+ const eventsWithTime = getEventsWithTime(events);
3345
+ if (eventsWithTime.length === 0) {
3346
+ return 0;
3347
+ }
3348
+ const baseTime = eventsWithTime[0].time;
3349
+ const lastTime = eventsWithTime.at(-1)?.time ?? baseTime;
3350
+ return roundSeconds(Math.max(0, lastTime - baseTime) / 1000);
2781
3351
  };
2782
3352
 
2783
3353
  const getTimelineSecondsFromClientX = (events, eventX, timelineLeft, timelineWidth) => {
@@ -2790,50 +3360,76 @@ const getTimelineSecondsFromClientX = (events, eventX, timelineLeft, timelineWid
2790
3360
  return formatTimelinePresetValue(durationSeconds * ratio);
2791
3361
  };
2792
3362
 
2793
- const handleTimelinePointerDown = (state, eventX) => {
2794
- const timelineEvents = getTimelineEvents(state);
3363
+ const Start = 'TimelineSelectionStartHandle';
3364
+ const End = 'TimelineSelectionEndHandle';
3365
+
3366
+ const getResizeState = (state, name) => {
3367
+ if (state.timelineInfo.startSeconds === null || state.timelineInfo.endSeconds === null) {
3368
+ return undefined;
3369
+ }
3370
+ if (name === Start) {
3371
+ return getStateWithTimelineInfo({
3372
+ ...state,
3373
+ timelineSelectionActive: true,
3374
+ timelineSelectionAnchorSeconds: formatTimelinePresetValue(state.timelineInfo.endSeconds),
3375
+ timelineSelectionFocusSeconds: formatTimelinePresetValue(state.timelineInfo.startSeconds)
3376
+ });
3377
+ }
3378
+ if (name === End) {
3379
+ return getStateWithTimelineInfo({
3380
+ ...state,
3381
+ timelineSelectionActive: true,
3382
+ timelineSelectionAnchorSeconds: formatTimelinePresetValue(state.timelineInfo.startSeconds),
3383
+ timelineSelectionFocusSeconds: formatTimelinePresetValue(state.timelineInfo.endSeconds)
3384
+ });
3385
+ }
3386
+ return undefined;
3387
+ };
3388
+ const handleTimelinePointerDown = (state, name, eventX) => {
3389
+ const resizeState = getResizeState(state, name);
3390
+ if (resizeState) {
3391
+ return resizeState;
3392
+ }
2795
3393
  const timelineLeft = getTimelineLeft(state);
2796
3394
  const timelineWidth = getTimelineWidth(state);
2797
3395
  const clientX = state.x + eventX;
2798
- const seconds = getTimelineSecondsFromClientX(timelineEvents, clientX, timelineLeft, timelineWidth);
3396
+ const seconds = getTimelineSecondsFromClientX(state.timelineEvents, clientX, timelineLeft, timelineWidth);
2799
3397
  if (seconds === undefined) {
2800
3398
  return state;
2801
3399
  }
2802
- return {
3400
+ return getStateWithTimelineInfo({
2803
3401
  ...state,
2804
3402
  timelineSelectionActive: true,
2805
3403
  timelineSelectionAnchorSeconds: seconds,
2806
3404
  timelineSelectionFocusSeconds: seconds
2807
- };
3405
+ });
2808
3406
  };
2809
3407
 
2810
3408
  const handleTimelinePointerMove = (state, eventX) => {
2811
3409
  if (!state.timelineSelectionActive) {
2812
3410
  return state;
2813
3411
  }
2814
- const timelineEvents = getTimelineEvents(state);
2815
3412
  const timelineLeft = getTimelineLeft(state);
2816
3413
  const timelineWidth = getTimelineWidth(state);
2817
3414
  const clientX = state.x + eventX;
2818
- const seconds = getTimelineSecondsFromClientX(timelineEvents, clientX, timelineLeft, timelineWidth);
3415
+ const seconds = getTimelineSecondsFromClientX(state.timelineEvents, clientX, timelineLeft, timelineWidth);
2819
3416
  if (seconds === undefined) {
2820
3417
  return state;
2821
3418
  }
2822
- return {
3419
+ return getStateWithTimelineInfo({
2823
3420
  ...state,
2824
3421
  timelineSelectionFocusSeconds: seconds
2825
- };
3422
+ });
2826
3423
  };
2827
3424
 
2828
3425
  const handleTimelinePointerUp = (state, eventX) => {
2829
3426
  if (!state.timelineSelectionActive) {
2830
3427
  return state;
2831
3428
  }
2832
- const timelineEvents = getTimelineEvents(state);
2833
3429
  const timelineLeft = getTimelineLeft(state);
2834
3430
  const timelineWidth = getTimelineWidth(state);
2835
3431
  const clientX = state.x + eventX;
2836
- const focusSeconds = getTimelineSecondsFromClientX(timelineEvents, clientX, timelineLeft, timelineWidth);
3432
+ const focusSeconds = getTimelineSecondsFromClientX(state.timelineEvents, clientX, timelineLeft, timelineWidth);
2837
3433
  if (focusSeconds === undefined) {
2838
3434
  return clearTimelineSelectionState(state);
2839
3435
  }
@@ -2845,7 +3441,7 @@ const handleTimelinePointerUp = (state, eventX) => {
2845
3441
  return clearTimelineSelectionState(nextState);
2846
3442
  };
2847
3443
 
2848
- const handleUseDevtoolsLayout = (state, checked) => {
3444
+ const setUseDevtoolsLayout = (state, checked) => {
2849
3445
  const useDevtoolsLayout = getBoolean(checked);
2850
3446
  const selectedEventIndex = useDevtoolsLayout ? getSelectedEventIndex$1(state) : null;
2851
3447
  return {
@@ -2880,42 +3476,164 @@ const handleShowResponsePartEvents = (state, checked) => {
2880
3476
  };
2881
3477
 
2882
3478
  const loadContent = async (state, savedState) => {
2883
- return loadEventsFromUri(restoreSavedState(state, savedState));
3479
+ const nextState = await loadEventsFromUri(restoreSavedState(state, savedState));
3480
+ return {
3481
+ ...nextState,
3482
+ categoryFilters: createCategoryFilters(getSelectedEventCategoryFilters(nextState.categoryFilters)),
3483
+ detailTabs: createDetailTabs(getSelectedDetailTab(nextState.detailTabs), nextState.selectedEvent),
3484
+ tableColumns: createTableColumns()
3485
+ };
2884
3486
  };
2885
3487
 
2886
3488
  const getCss = state => {
2887
- const tableWidth = clampTableWidth(state.width, state.tableWidth);
2888
- const detailsWidth = getDetailsWidth(state.width, state.tableWidth);
3489
+ const hasSelectedEvent = !!state.selectedEvent;
3490
+ const tableWidth = hasSelectedEvent ? clampTableWidth(state.width, state.tableWidth) : getMainWidth(state.width);
3491
+ const detailsWidth = hasSelectedEvent ? getDetailsWidth(state.width, state.tableWidth) : 0;
3492
+ const tableColumnLayout = getTableColumnLayout(tableWidth, state.visibleTableColumns, state.tableColumnWidths);
3493
+ const resizerOneLeft = tableColumnLayout.resizerLefts[0] || 0;
3494
+ const resizerTwoLeft = tableColumnLayout.resizerLefts[1] || 0;
2889
3495
  return `
2890
3496
  .ChatDebugView {
2891
3497
  --ChatDebugViewDetailsWidth: ${detailsWidth}px;
3498
+ --ChatDebugViewDurationColumnWidth: ${state.tableColumnWidths.duration}px;
3499
+ --ChatDebugViewResizerOneLeft: ${resizerOneLeft}px;
3500
+ --ChatDebugViewResizerTwoLeft: ${resizerTwoLeft}px;
2892
3501
  --ChatDebugViewSashWidth: ${sashWidth}px;
2893
3502
  --ChatDebugViewTableWidth: ${tableWidth}px;
3503
+ --ChatDebugViewTypeColumnWidth: ${state.tableColumnWidths.type}px;
2894
3504
  padding: ${viewPadding}px;
2895
3505
  }
2896
3506
 
3507
+ .ChatDebugViewTop {
3508
+ display: flex;
3509
+ align-items: center;
3510
+ gap: 8px;
3511
+ min-width: 0;
3512
+ }
3513
+
3514
+ .ChatDebugViewFilterInput {
3515
+ flex: 1;
3516
+ min-width: 0;
3517
+ }
2897
3518
 
2898
- .ChatDebugViewDetails {
2899
- contain: strict;
3519
+ .ChatDebugViewFilterInput--devtools {
3520
+ flex: 1 1 220px;
3521
+ min-width: 180px;
2900
3522
  }
2901
3523
 
2902
- .ChatDebugViewDetailsTop {
2903
- height: 33px;
2904
- contain: strict;
3524
+ .ChatDebugViewTableWrapper {
3525
+ position: relative;
3526
+ width: min(100%, var(--ChatDebugViewTableWidth));
3527
+ max-width: 100%;
3528
+ flex:1;
3529
+ display:flex
3530
+ }
3531
+
3532
+ .ChatDebugViewTable {
3533
+ width: 100%;
3534
+ table-layout: fixed;
3535
+ border-collapse: collapse;
3536
+ flex: 1;
2905
3537
  }
2906
3538
 
2907
- .ChatDebugViewDetailsBottom {
3539
+ .ChatDebugViewHeaderCell,
3540
+ .ChatDebugViewCell {
3541
+ overflow: hidden;
3542
+ text-overflow: ellipsis;
3543
+ white-space: nowrap;
3544
+ }
3545
+
3546
+ .ChatDebugViewHeaderCellType.ChatDebugViewColumnFixed,
3547
+ .ChatDebugViewCellType.ChatDebugViewColumnFixed {
3548
+ width: var(--ChatDebugViewTypeColumnWidth);
3549
+ max-width: var(--ChatDebugViewTypeColumnWidth);
3550
+ }
3551
+
3552
+ .ChatDebugViewHeaderCellDuration.ChatDebugViewColumnFixed,
3553
+ .ChatDebugViewCellDuration.ChatDebugViewColumnFixed {
3554
+ width: var(--ChatDebugViewDurationColumnWidth);
3555
+ max-width: var(--ChatDebugViewDurationColumnWidth);
3556
+ }
3557
+
3558
+ .ChatDebugViewResizers {
3559
+ position: absolute;
3560
+ inset: 0;
3561
+ pointer-events: none;
3562
+ }
3563
+
3564
+ .ChatDebugViewResizer {
3565
+ position: absolute;
3566
+ top: 0;
3567
+ bottom: 0;
3568
+ width: 12px;
3569
+ margin: 0;
3570
+ padding: 0;
3571
+ border: 0;
3572
+ background: transparent;
3573
+ pointer-events: auto;
3574
+ cursor: col-resize;
3575
+ }
3576
+
3577
+ .ChatDebugViewResizerOne {
3578
+ left: var(--ChatDebugViewResizerOneLeft);
3579
+ transform: translateX(-50%);
3580
+ }
3581
+
3582
+ .ChatDebugViewResizerTwo {
3583
+ left: var(--ChatDebugViewResizerTwoLeft);
3584
+ transform: translateX(-50%);
3585
+ }
3586
+
3587
+ .ChatDebugViewResizerInner {
3588
+ position: absolute;
3589
+ top: 0;
3590
+ bottom: 0;
3591
+ left: 50%;
3592
+ width: 1px;
3593
+ transform: translateX(-50%);
3594
+ background: var(--vscode-widget-border, rgba(255, 255, 255, 0.18));
3595
+ }
3596
+
3597
+
3598
+ .ChatDebugViewDetails {
3599
+ margin-left: auto;
3600
+ min-height: 26px;
3601
+ }
3602
+ border: 1px solid transparent;
3603
+ border-radius: 4px;
3604
+ background: transparent;
3605
+ color: var(--vscode-descriptionForeground, inherit);
3606
+ }
3607
+ font-size: 11px;
3608
+ font-weight: 600;
3609
+ letter-spacing: 0.02em;
2908
3610
  display: flex;
2909
3611
  contain: strict;
2910
- flex:1
3612
+ flex:1;
3613
+ transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, transform 120ms ease;
2911
3614
 
2912
3615
  }
2913
3616
  .ChatDebugViewEvent {
2914
- contain: content
3617
+ border-color: var(--vscode-widget-border, rgba(255, 255, 255, 0.14));
3618
+ background: var(--vscode-toolbar-hoverBackground, rgba(255, 255, 255, 0.06));
3619
+ color: var(--vscode-foreground, inherit);
3620
+
3621
+ .ChatDebugViewEventLineContent {
3622
+ flex: 1;
3623
+ background: var(--vscode-toolbar-activeBackground, rgba(255, 255, 255, 0.1));
3624
+ min-width: 0;
3625
+ overflow-wrap: anywhere;
3626
+ white-space: pre-wrap;
3627
+ word-break: break-word;
3628
+ outline: 1px solid var(--vscode-focusBorder, rgba(255, 255, 255, 0.4));
3629
+
3630
+ .ChatDebugViewEventLineNumber {
3631
+ flex: none;
2915
3632
  }
2916
3633
 
2917
3634
  .row {
2918
3635
  flex-shrink: 0;
3636
+ min-width: 0;
2919
3637
  }
2920
3638
 
2921
3639
  .ChatDebugViewRefreshButton {
@@ -2923,6 +3641,7 @@ const getCss = state => {
2923
3641
  align-items: center;
2924
3642
  justify-content: center;
2925
3643
  flex: none;
3644
+ margin-left: auto;
2926
3645
  min-height: 28px;
2927
3646
  padding: 0 10px;
2928
3647
  border: 1px solid rgba(255, 255, 255, 0.16);
@@ -2956,6 +3675,183 @@ const getCss = state => {
2956
3675
  background: var(--ListHoverBackground);
2957
3676
  color: var(--ListHoverForeground);
2958
3677
  }
3678
+
3679
+ .ChatDebugViewImagePreview {
3680
+ display: flex;
3681
+ flex-direction: column;
3682
+ align-items: flex-start;
3683
+ gap: 8px;
3684
+ max-width: 100%;
3685
+ }
3686
+
3687
+ .ChatDebugViewImagePreviewImage {
3688
+ display: block;
3689
+ max-width: 100%;
3690
+ max-height: 320px;
3691
+ border: 1px solid var(--vscode-widget-border, rgba(255, 255, 255, 0.14));
3692
+ border-radius: 6px;
3693
+ object-fit: contain;
3694
+ }
3695
+
3696
+ .ChatDebugViewImagePreviewLabel {
3697
+ color: var(--vscode-descriptionForeground, inherit);
3698
+ }
3699
+
3700
+ .ChatDebugViewTimeline {
3701
+ display: flex;
3702
+ flex-direction: column;
3703
+ gap: 8px;
3704
+ }
3705
+
3706
+ .ChatDebugViewTimelineTop {
3707
+ display: flex;
3708
+ align-items: center;
3709
+ min-width: 0;
3710
+ }
3711
+
3712
+ .ChatDebugViewTimelineSummary {
3713
+ margin: 0;
3714
+ color: var(--vscode-descriptionForeground, inherit);
3715
+ font-size: 11px;
3716
+ font-weight: 600;
3717
+ }
3718
+
3719
+ .ChatDebugViewTimelineInteractive {
3720
+ position: relative;
3721
+ height: 54px;
3722
+ overflow: hidden;
3723
+ border: 1px solid var(--vscode-widget-border, rgba(255, 255, 255, 0.14));
3724
+ border-radius: 8px;
3725
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02));
3726
+ contain: strict;
3727
+ user-select: none;
3728
+ }
3729
+
3730
+ .ChatDebugViewTimelineBuckets {
3731
+ position: absolute;
3732
+ inset: 18px 8px 8px;
3733
+ display: flex;
3734
+ align-items: flex-end;
3735
+ gap: 2px;
3736
+ }
3737
+
3738
+ .ChatDebugViewTimelineBucket {
3739
+ display: flex;
3740
+ flex: 1;
3741
+ align-items: flex-end;
3742
+ height: 100%;
3743
+ min-width: 0;
3744
+ cursor: pointer;
3745
+ }
3746
+
3747
+ .ChatDebugViewTimelineBucketBar {
3748
+ display: flex;
3749
+ flex: 1;
3750
+ flex-direction: column;
3751
+ justify-content: flex-end;
3752
+ gap: 2px;
3753
+ height: 100%;
3754
+ }
3755
+
3756
+ .ChatDebugViewTimelineBucketUnit {
3757
+ flex: none;
3758
+ height: 4px;
3759
+ border-radius: 999px;
3760
+ background: var(--vscode-charts-blue, rgba(91, 151, 255, 0.9));
3761
+ }
3762
+
3763
+ .ChatDebugViewTimelineBucketUnitEmpty {
3764
+ opacity: 0.18;
3765
+ }
3766
+
3767
+ .ChatDebugViewTimelineBucketSelected .ChatDebugViewTimelineBucketUnit,
3768
+ .ChatDebugViewTimelineBucketBarSelected .ChatDebugViewTimelineBucketUnit {
3769
+ background: var(--vscode-charts-orange, rgba(255, 174, 0, 0.95));
3770
+ }
3771
+
3772
+ .ChatDebugViewTimelineSelectionOverlay {
3773
+ position: absolute;
3774
+ inset: 0;
3775
+ pointer-events: none;
3776
+ }
3777
+
3778
+ .ChatDebugViewTimelineSelectionRange {
3779
+ position: absolute;
3780
+ top: 18px;
3781
+ bottom: 8px;
3782
+ border-radius: 6px;
3783
+ background: color-mix(in srgb, var(--vscode-charts-orange, rgba(255, 174, 0, 0.95)) 18%, transparent);
3784
+ outline: 1px solid color-mix(in srgb, var(--vscode-charts-orange, rgba(255, 174, 0, 0.95)) 55%, transparent);
3785
+ }
3786
+
3787
+ .ChatDebugViewTimelineSelectionMarker {
3788
+ position: absolute;
3789
+ top: 0;
3790
+ bottom: 0;
3791
+ width: 14px;
3792
+ margin-left: -7px;
3793
+ padding: 0;
3794
+ border: 0;
3795
+ background: linear-gradient(
3796
+ 90deg,
3797
+ transparent calc(50% - 1px),
3798
+ var(--vscode-charts-orange, rgba(255, 174, 0, 0.95)) calc(50% - 1px),
3799
+ var(--vscode-charts-orange, rgba(255, 174, 0, 0.95)) calc(50% + 1px),
3800
+ transparent calc(50% + 1px)
3801
+ );
3802
+ }
3803
+
3804
+ .ChatDebugViewTimelineSelectionHandle {
3805
+ pointer-events: auto;
3806
+ cursor: ew-resize;
3807
+ }
3808
+
3809
+ .ChatDebugViewTimelineSelectionHandle::before {
3810
+ content: '';
3811
+ position: absolute;
3812
+ top: 0;
3813
+ left: 50%;
3814
+ width: 14px;
3815
+ height: 16px;
3816
+ transform: translateX(-50%);
3817
+ border: 1px solid var(--vscode-widget-border, rgba(255, 255, 255, 0.22));
3818
+ border-radius: 4px;
3819
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.08));
3820
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
3821
+ }
3822
+
3823
+ .ChatDebugViewTimelineSelectionHandle::after {
3824
+ content: '';
3825
+ position: absolute;
3826
+ top: 5px;
3827
+ left: 50%;
3828
+ width: 7px;
3829
+ height: 6px;
3830
+ transform: translateX(-50%);
3831
+ background: linear-gradient(
3832
+ 90deg,
3833
+ transparent 0,
3834
+ transparent 1px,
3835
+ var(--vscode-foreground, rgba(255, 255, 255, 0.88)) 1px,
3836
+ var(--vscode-foreground, rgba(255, 255, 255, 0.88)) 2px,
3837
+ transparent 2px,
3838
+ transparent 4px,
3839
+ var(--vscode-foreground, rgba(255, 255, 255, 0.88)) 4px,
3840
+ var(--vscode-foreground, rgba(255, 255, 255, 0.88)) 5px,
3841
+ transparent 5px,
3842
+ transparent 100%
3843
+ );
3844
+ opacity: 0.8;
3845
+ }
3846
+
3847
+ .ChatDebugViewTimelineSelectionHandle:hover::before {
3848
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.12));
3849
+ }
3850
+
3851
+ .ChatDebugViewTimelineSelectionHandle:focus-visible {
3852
+ outline: 2px solid var(--vscode-focusBorder, rgba(255, 255, 255, 0.45));
3853
+ outline-offset: 1px;
3854
+ }
2959
3855
  `;
2960
3856
  };
2961
3857
 
@@ -2964,6 +3860,10 @@ const renderCss = (oldState, newState) => {
2964
3860
  return [SetCss, newState.uid, css];
2965
3861
  };
2966
3862
 
3863
+ const mergeClassNames = (...classNames) => {
3864
+ return classNames.filter(Boolean).join(' ');
3865
+ };
3866
+
2967
3867
  const text = data => {
2968
3868
  return {
2969
3869
  childCount: 0,
@@ -3283,8 +4183,13 @@ const ChatDebugViewEventsFullWidth = 'ChatDebugViewEventsFullWidth';
3283
4183
  const ChatDebugViewFilterInput = 'ChatDebugViewFilterInput';
3284
4184
  const ChatDebugViewFilterInputDevtools = 'ChatDebugViewFilterInput--devtools';
3285
4185
  const ChatDebugViewHeaderCell = 'ChatDebugViewHeaderCell';
4186
+ const ChatDebugViewHeaderCellDuration = 'ChatDebugViewHeaderCellDuration';
4187
+ const ChatDebugViewHeaderCellStatus = 'ChatDebugViewHeaderCellStatus';
4188
+ const ChatDebugViewHeaderCellType = 'ChatDebugViewHeaderCellType';
4189
+ const ChatDebugViewImagePreview = 'ChatDebugViewImagePreview';
4190
+ const ChatDebugViewImagePreviewImage = 'ChatDebugViewImagePreviewImage';
4191
+ const ChatDebugViewImagePreviewLabel = 'ChatDebugViewImagePreviewLabel';
3286
4192
  const ChatDebugViewRefreshButton = 'ChatDebugViewRefreshButton';
3287
- const ChatDebugViewQuickFilterInput = 'ChatDebugViewQuickFilterInput';
3288
4193
  const ChatDebugViewQuickFilterPill = 'ChatDebugViewQuickFilterPill';
3289
4194
  const ChatDebugViewQuickFilterPillSelected = 'ChatDebugViewQuickFilterPillSelected';
3290
4195
  const ChatDebugViewQuickFilters = 'ChatDebugViewQuickFilters';
@@ -3294,6 +4199,12 @@ const ChatDebugViewTable = 'ChatDebugViewTable';
3294
4199
  const ChatDebugViewTableBody = 'ChatDebugViewTableBody';
3295
4200
  const ChatDebugViewTableHeader = 'ChatDebugViewTableHeader';
3296
4201
  const ChatDebugViewTableHeaderRow = 'ChatDebugViewTableHeaderRow';
4202
+ const ChatDebugViewTableWrapper = 'ChatDebugViewTableWrapper';
4203
+ const ChatDebugViewResizer = 'ChatDebugViewResizer';
4204
+ const ChatDebugViewResizerInner = 'ChatDebugViewResizerInner';
4205
+ const ChatDebugViewResizerOne = 'ChatDebugViewResizerOne';
4206
+ const ChatDebugViewResizerTwo = 'ChatDebugViewResizerTwo';
4207
+ const ChatDebugViewResizers = 'ChatDebugViewResizers';
3297
4208
  const TableRowEven = 'TableRowEven';
3298
4209
  const ChatDebugViewTimeline = 'ChatDebugViewTimeline';
3299
4210
  const ChatDebugViewTimelineBucket = 'ChatDebugViewTimelineBucket';
@@ -3304,6 +4215,7 @@ const ChatDebugViewTimelineBucketUnit = 'ChatDebugViewTimelineBucketUnit';
3304
4215
  const ChatDebugViewTimelineBucketUnitEmpty = 'ChatDebugViewTimelineBucketUnitEmpty';
3305
4216
  const ChatDebugViewTimelineBuckets = 'ChatDebugViewTimelineBuckets';
3306
4217
  const ChatDebugViewTimelineInteractive = 'ChatDebugViewTimelineInteractive';
4218
+ const ChatDebugViewTimelineSelectionHandle = 'ChatDebugViewTimelineSelectionHandle';
3307
4219
  const ChatDebugViewTimelineSelectionMarker = 'ChatDebugViewTimelineSelectionMarker';
3308
4220
  const ChatDebugViewTimelineSelectionMarkerEnd = 'ChatDebugViewTimelineSelectionMarkerEnd';
3309
4221
  const ChatDebugViewTimelineSelectionMarkerStart = 'ChatDebugViewTimelineSelectionMarkerStart';
@@ -3318,6 +4230,7 @@ const ChatDebugViewTimingValue = 'ChatDebugViewTimingValue';
3318
4230
  const ChatDebugViewTop = 'ChatDebugViewTop';
3319
4231
  const ChatDebugViewTopDevtools = 'ChatDebugViewTop--devtools';
3320
4232
  const ChatDebugViewCell = 'ChatDebugViewCell';
4233
+ const ChatDebugViewColumnFixed = 'ChatDebugViewColumnFixed';
3321
4234
  const ChatDebugViewCellDuration = 'ChatDebugViewCellDuration';
3322
4235
  const ChatDebugViewCellStatus = 'ChatDebugViewCellStatus';
3323
4236
  const ChatDebugViewCellStatusError = 'ChatDebugViewCellStatusError';
@@ -3329,66 +4242,69 @@ const TokenKey = 'Token TokenKey';
3329
4242
  const TokenNumeric = 'Token TokenNumeric';
3330
4243
  const TokenString = 'Token TokenString';
3331
4244
  const TokenText = 'Token TokenText';
3332
- const joinClassNames = (...classNames) => {
3333
- return classNames.filter(Boolean).join(' ');
3334
- };
3335
4245
 
4246
+ const debugErrorRootNode = {
4247
+ childCount: 1,
4248
+ className: ChatDebugView,
4249
+ type: Div
4250
+ };
4251
+ const debugErrorMessageNode = {
4252
+ childCount: 1,
4253
+ className: ChatDebugViewError,
4254
+ type: Div
4255
+ };
3336
4256
  const getDebugErrorDom = errorMessage => {
3337
- return [{
3338
- childCount: 1,
3339
- className: ChatDebugView,
3340
- type: Div
3341
- }, {
3342
- childCount: 1,
3343
- className: ChatDebugViewError,
3344
- type: Div
3345
- }, text(errorMessage)];
4257
+ return [debugErrorRootNode, debugErrorMessageNode, text(errorMessage)];
3346
4258
  };
3347
4259
 
3348
4260
  const HandleEventCategoryFilter = 4;
3349
4261
  const HandleFilterInput = 5;
3350
- const HandleDetailTab = 6;
4262
+ const SelectDetailTab = 6;
3351
4263
  const HandleEventRowClick = 7;
3352
4264
  const HandleHeaderContextMenu = 8;
3353
4265
  const HandleSashPointerDown = 9;
3354
4266
  const HandleSashPointerMove = 10;
3355
4267
  const HandleSashPointerUp = 11;
3356
- const HandleTableBodyContextMenu = 12;
3357
- const HandleDetailsContextMenu = 13;
3358
- const HandleTimelinePointerDown = 14;
3359
- const HandleTimelinePointerMove = 15;
3360
- const HandleTimelinePointerUp = 16;
3361
- const HandleTimelineDoubleClick = 17;
3362
- const HandleTableKeyDown = 18;
3363
- const HandleTimelineRangePreset = 19;
3364
- const HandleCloseDetails = 20;
3365
- const HandleClickRefresh = 21;
3366
- const HandleDetailsTopContextMenu = 22;
3367
- const HandleTimelineContextMenu = 23;
3368
-
4268
+ const HandleTableResizerPointerDown = 12;
4269
+ const HandleTableResizerPointerMove = 13;
4270
+ const HandleTableResizerPointerUp = 14;
4271
+ const HandleTableBodyContextMenu = 15;
4272
+ const HandleDetailsContextMenu = 16;
4273
+ const HandleTimelinePointerDown = 17;
4274
+ const HandleTimelinePointerMove = 18;
4275
+ const HandleTimelinePointerUp = 19;
4276
+ const HandleTimelineDoubleClick = 20;
4277
+ const HandleTimelineRangePreset = 22;
4278
+ const HandleCloseDetails = 23;
4279
+ const HandleClickRefresh = 24;
4280
+ const HandleDetailsTopContextMenu = 25;
4281
+ const HandleTimelineContextMenu = 26;
4282
+
4283
+ const refreshButtonDom = [{
4284
+ 'aria-label': refreshEvents$1(),
4285
+ childCount: 1,
4286
+ className: ChatDebugViewRefreshButton,
4287
+ name: Refresh,
4288
+ onClick: HandleClickRefresh,
4289
+ type: Button$1,
4290
+ value: Refresh
4291
+ }, text(refresh$1())];
3369
4292
  const getRefreshButtonDom = () => {
3370
- return [{
3371
- 'aria-label': refreshEvents$1(),
3372
- childCount: 1,
3373
- className: ChatDebugViewRefreshButton,
3374
- name: Refresh,
3375
- onClick: HandleClickRefresh,
3376
- type: Button$1,
3377
- value: Refresh
3378
- }, text(refresh$1())];
4293
+ return refreshButtonDom;
3379
4294
  };
4295
+
3380
4296
  const getDebugViewTopDom = (filterValue, useDevtoolsLayout, quickFilterNodes) => {
3381
4297
  const refreshButtonDom = getRefreshButtonDom();
3382
4298
  if (useDevtoolsLayout) {
3383
4299
  return [{
3384
4300
  childCount: 2 + (quickFilterNodes.length > 0 ? 1 : 0),
3385
- className: joinClassNames(ChatDebugViewTop, ChatDebugViewTopDevtools),
4301
+ className: mergeClassNames(ChatDebugViewTop, ChatDebugViewTopDevtools),
3386
4302
  onContextMenu: HandleHeaderContextMenu,
3387
4303
  type: Search
3388
4304
  }, {
3389
4305
  autocomplete: 'off',
3390
4306
  childCount: 0,
3391
- className: joinClassNames(InputBox, ChatDebugViewFilterInput, ChatDebugViewFilterInputDevtools),
4307
+ className: mergeClassNames(InputBox, ChatDebugViewFilterInput, ChatDebugViewFilterInputDevtools),
3392
4308
  inputType: 'search',
3393
4309
  name: Filter,
3394
4310
  onInput: HandleFilterInput,
@@ -3405,7 +4321,7 @@ const getDebugViewTopDom = (filterValue, useDevtoolsLayout, quickFilterNodes) =>
3405
4321
  }, {
3406
4322
  autocomplete: 'off',
3407
4323
  childCount: 0,
3408
- className: joinClassNames(InputBox, ChatDebugViewFilterInput),
4324
+ className: mergeClassNames(InputBox, ChatDebugViewFilterInput),
3409
4325
  inputType: 'search',
3410
4326
  name: Filter,
3411
4327
  onInput: HandleFilterInput,
@@ -3415,6 +4331,46 @@ const getDebugViewTopDom = (filterValue, useDevtoolsLayout, quickFilterNodes) =>
3415
4331
  }, ...refreshButtonDom];
3416
4332
  };
3417
4333
 
4334
+ const None = 'none';
4335
+
4336
+ const getPanelId = detailTab => {
4337
+ return `ChatDebugViewDetailsPanel-${detailTab}`;
4338
+ };
4339
+
4340
+ const getTabId = detailTab => {
4341
+ return `ChatDebugViewDetailsTab-${detailTab}`;
4342
+ };
4343
+
4344
+ const getDetailTabDom = detailTab => {
4345
+ const {
4346
+ isSelected
4347
+ } = detailTab;
4348
+ return [{
4349
+ 'aria-controls': getPanelId(detailTab.name),
4350
+ 'aria-selected': isSelected,
4351
+ childCount: 1,
4352
+ className: mergeClassNames(ChatDebugViewDetailsTab, isSelected ? ChatDebugViewDetailsTabSelected : ''),
4353
+ id: getTabId(detailTab.name),
4354
+ name: DetailTab,
4355
+ onChange: SelectDetailTab,
4356
+ onClick: SelectDetailTab,
4357
+ role: 'tab',
4358
+ tabIndex: isSelected ? 0 : -1,
4359
+ type: Button$1,
4360
+ value: detailTab.name
4361
+ }, text(detailTab.label)];
4362
+ };
4363
+
4364
+ const getTabNodes = detailTabs => {
4365
+ return [{
4366
+ 'aria-label': detailSections(),
4367
+ childCount: detailTabs.length,
4368
+ className: ChatDebugViewDetailsTabs,
4369
+ role: 'tablist',
4370
+ type: Div
4371
+ }, ...detailTabs.flatMap(getDetailTabDom)];
4372
+ };
4373
+
3418
4374
  const getDurationText = event => {
3419
4375
  const explicitDuration = event.durationMs ?? event.duration;
3420
4376
  if (typeof explicitDuration === 'number' && Number.isFinite(explicitDuration)) {
@@ -3464,22 +4420,26 @@ const getEndText = event => {
3464
4420
  const getStartText = event => {
3465
4421
  return getTimestampText(event.started ?? event.startTime ?? event.startTimestamp ?? event.timestamp);
3466
4422
  };
3467
-
3468
- const getTimingRowDom = (label, value) => {
3469
- return [{
3470
- childCount: 2,
3471
- className: ChatDebugViewTimingRow,
3472
- type: Div
3473
- }, {
3474
- childCount: 1,
3475
- className: ChatDebugViewTimingLabel,
3476
- type: Span
3477
- }, text(label), {
3478
- childCount: 1,
3479
- className: ChatDebugViewTimingValue,
3480
- type: Span
3481
- }, text(value)];
4423
+
4424
+ const timingRowNode = {
4425
+ childCount: 2,
4426
+ className: ChatDebugViewTimingRow,
4427
+ type: Div
4428
+ };
4429
+ const timingLabelNode = {
4430
+ childCount: 1,
4431
+ className: ChatDebugViewTimingLabel,
4432
+ type: Span
4433
+ };
4434
+ const timingValueNode = {
4435
+ childCount: 1,
4436
+ className: ChatDebugViewTimingValue,
4437
+ type: Span
4438
+ };
4439
+ const getTimingRowDom = (label, value) => {
4440
+ return [timingRowNode, timingLabelNode, text(label), timingValueNode, text(value)];
3482
4441
  };
4442
+
3483
4443
  const getTimingDetailsDom = event => {
3484
4444
  return [{
3485
4445
  childCount: 3,
@@ -3488,36 +4448,23 @@ const getTimingDetailsDom = event => {
3488
4448
  }, ...getTimingRowDom(started(), getStartText(event)), ...getTimingRowDom(ended(), getEndText(event)), ...getTimingRowDom(duration(), getDurationText(event))];
3489
4449
  };
3490
4450
 
3491
- const getTabId = detailTab => {
3492
- return `ChatDebugViewDetailsTab-${detailTab}`;
3493
- };
3494
- const getPanelId = detailTab => {
3495
- return `ChatDebugViewDetailsPanel-${detailTab}`;
3496
- };
3497
- const getTabNodes = selectedDetailTab => {
3498
- return detailTabs.flatMap(detailTab => {
3499
- const isSelected = detailTab === selectedDetailTab;
3500
- return [{
3501
- 'aria-controls': getPanelId(detailTab),
3502
- 'aria-selected': isSelected,
3503
- childCount: 1,
3504
- className: joinClassNames(ChatDebugViewDetailsTab, isSelected && ChatDebugViewDetailsTabSelected),
3505
- id: getTabId(detailTab),
3506
- name: DetailTab,
3507
- onChange: HandleDetailTab,
3508
- onClick: HandleDetailTab,
3509
- role: 'tab',
3510
- tabIndex: isSelected ? 0 : -1,
3511
- type: Button$1,
3512
- value: detailTab
3513
- }, text(getDetailTabLabel(detailTab))];
3514
- });
4451
+ const getContentNode = (previewEventNodes, payloadEventNodes, responseEventNodes, selectedEvent, detailTabs) => {
4452
+ const safeSelectedDetailTab = getSelectedDetailTab(detailTabs);
4453
+ const contentNodes = safeSelectedDetailTab === Timing && selectedEvent ? getTimingDetailsDom(selectedEvent) : safeSelectedDetailTab === Preview ? previewEventNodes : safeSelectedDetailTab === Payload ? payloadEventNodes : responseEventNodes;
4454
+ return {
4455
+ contentNodes,
4456
+ safeSelectedDetailTab
4457
+ };
3515
4458
  };
3516
- const getDetailsDom = (previewEventNodes, responseEventNodes = previewEventNodes, selectedEvent = null, selectedDetailTab = Response) => {
3517
- if (previewEventNodes.length === 0 && responseEventNodes.length === 0) {
4459
+ const getDetailsDom = (previewEventNodes, payloadEventNodes = previewEventNodes, responseEventNodes = payloadEventNodes, selectedEvent = null, detailTabs = createDetailTabs()) => {
4460
+ if (previewEventNodes.length === 0 && payloadEventNodes.length === 0 && responseEventNodes.length === 0) {
3518
4461
  return [];
3519
4462
  }
3520
- const contentNodes = selectedDetailTab === Timing && selectedEvent ? getTimingDetailsDom(selectedEvent) : selectedDetailTab === Preview ? previewEventNodes : responseEventNodes;
4463
+ const normalizedDetailTabs = selectedEvent ? createDetailTabs(getSelectedDetailTab(detailTabs), selectedEvent) : detailTabs;
4464
+ const {
4465
+ contentNodes,
4466
+ safeSelectedDetailTab
4467
+ } = getContentNode(previewEventNodes, payloadEventNodes, responseEventNodes, selectedEvent, normalizedDetailTabs);
3521
4468
  return [{
3522
4469
  childCount: 2,
3523
4470
  className: ChatDebugViewDetails,
@@ -3536,17 +4483,11 @@ const getDetailsDom = (previewEventNodes, responseEventNodes = previewEventNodes
3536
4483
  onClick: HandleCloseDetails,
3537
4484
  type: Button$1,
3538
4485
  value: 'close'
3539
- }, {
3540
- 'aria-label': detailSections(),
3541
- childCount: detailTabs.length,
3542
- className: ChatDebugViewDetailsTabs,
3543
- role: 'tablist',
3544
- type: Div
3545
- }, ...getTabNodes(selectedDetailTab), {
3546
- 'aria-labelledby': getTabId(selectedDetailTab),
4486
+ }, ...getTabNodes(normalizedDetailTabs), {
4487
+ 'aria-labelledby': getTabId(safeSelectedDetailTab),
3547
4488
  childCount: 1,
3548
4489
  className: ChatDebugViewDetailsBottom,
3549
- id: getPanelId(selectedDetailTab),
4490
+ id: getPanelId(safeSelectedDetailTab),
3550
4491
  onContextMenu: HandleDetailsContextMenu,
3551
4492
  role: 'tabpanel',
3552
4493
  type: Div
@@ -3586,9 +4527,6 @@ const getEventTypeLabel = event => {
3586
4527
  return `${event.type}, ${toolName}`;
3587
4528
  };
3588
4529
 
3589
- const isRecord = value => {
3590
- return typeof value === 'object' && value !== null;
3591
- };
3592
4530
  const isErrorStatusCode = value => {
3593
4531
  if (typeof value === 'number') {
3594
4532
  return value >= 400;
@@ -3599,6 +4537,11 @@ const isErrorStatusCode = value => {
3599
4537
  }
3600
4538
  return false;
3601
4539
  };
4540
+
4541
+ const isRecord = value => {
4542
+ return typeof value === 'object' && value !== null;
4543
+ };
4544
+
3602
4545
  const hasErrorStatus = event => {
3603
4546
  if (event.type === 'error') {
3604
4547
  return true;
@@ -3634,24 +4577,26 @@ const getStatusText = event => {
3634
4577
  };
3635
4578
 
3636
4579
  const getRowCellNodes = (event, isErrorStatus, visibleTableColumns) => {
3637
- return visibleTableColumns.flatMap(column => {
4580
+ const orderedVisibleTableColumns = getOrderedVisibleTableColumns(visibleTableColumns);
4581
+ return orderedVisibleTableColumns.flatMap((column, index) => {
4582
+ const isFixed = index < orderedVisibleTableColumns.length - 1;
3638
4583
  switch (column) {
3639
4584
  case Duration:
3640
4585
  return [{
3641
4586
  childCount: 1,
3642
- className: joinClassNames(ChatDebugViewCell, ChatDebugViewCellDuration),
4587
+ className: mergeClassNames(ChatDebugViewCell, ChatDebugViewCellDuration, isFixed ? ChatDebugViewColumnFixed : ''),
3643
4588
  type: Td
3644
4589
  }, text(getDurationText(event))];
3645
4590
  case Status:
3646
4591
  return [{
3647
4592
  childCount: 1,
3648
- className: joinClassNames(ChatDebugViewCell, ChatDebugViewCellStatus, isErrorStatus && ChatDebugViewCellStatusError),
4593
+ className: mergeClassNames(ChatDebugViewCell, ChatDebugViewCellStatus, isErrorStatus ? ChatDebugViewCellStatusError : '', isFixed ? ChatDebugViewColumnFixed : ''),
3649
4594
  type: Td
3650
4595
  }, text(getStatusText(event))];
3651
4596
  case Type:
3652
4597
  return [{
3653
4598
  childCount: 1,
3654
- className: joinClassNames(ChatDebugViewCell, ChatDebugViewCellType),
4599
+ className: mergeClassNames(ChatDebugViewCell, ChatDebugViewCellType, isFixed ? ChatDebugViewColumnFixed : ''),
3655
4600
  type: Td
3656
4601
  }, text(getEventTypeLabel(event))];
3657
4602
  default:
@@ -3659,6 +4604,7 @@ const getRowCellNodes = (event, isErrorStatus, visibleTableColumns) => {
3659
4604
  }
3660
4605
  });
3661
4606
  };
4607
+
3662
4608
  const getDevtoolsRows = (events, selectedEventIndex, visibleTableColumns = defaultVisibleTableColumns) => {
3663
4609
  return events.flatMap((event, i) => {
3664
4610
  const isEvenRow = i % 2 === 1;
@@ -3668,19 +4614,39 @@ const getDevtoolsRows = (events, selectedEventIndex, visibleTableColumns = defau
3668
4614
  const rowCellNodes = getRowCellNodes(event, isErrorStatus, visibleTableColumns);
3669
4615
  return [{
3670
4616
  childCount: visibleTableColumns.length,
3671
- className: joinClassNames(ChatDebugViewEventRow, isEvenRow && TableRowEven, isSelected && ChatDebugViewEventRowSelected),
4617
+ className: mergeClassNames(ChatDebugViewEventRow, isEvenRow ? TableRowEven : '', isSelected ? ChatDebugViewEventRowSelected : ''),
3672
4618
  'data-index': rowIndex,
3673
4619
  type: Tr
3674
4620
  }, ...rowCellNodes];
3675
4621
  });
3676
4622
  };
3677
4623
 
4624
+ const emptyStateNode = {
4625
+ childCount: 1,
4626
+ className: ChatDebugViewEmpty,
4627
+ type: Div
4628
+ };
3678
4629
  const getEmptyStateDom = emptyMessage => {
4630
+ return [emptyStateNode, text(emptyMessage)];
4631
+ };
4632
+
4633
+ const getLineNodeDom = (line, index) => {
3679
4634
  return [{
3680
- childCount: 1,
3681
- className: ChatDebugViewEmpty,
4635
+ childCount: 2,
4636
+ className: Row,
3682
4637
  type: Div
3683
- }, text(emptyMessage)];
4638
+ }, {
4639
+ childCount: 1,
4640
+ className: ChatDebugViewEventLineNumber,
4641
+ type: Span
4642
+ }, text(String(index + 1)), {
4643
+ childCount: line.childCount,
4644
+ className: ChatDebugViewEventLineContent,
4645
+ type: Span
4646
+ }, ...line.nodes];
4647
+ };
4648
+ const getLineNodes = lines => {
4649
+ return lines.flatMap(getLineNodeDom);
3684
4650
  };
3685
4651
 
3686
4652
  const isDigit = character => {
@@ -3830,24 +4796,6 @@ const getLineContentNodes = line => {
3830
4796
  }, text(segment.value)];
3831
4797
  });
3832
4798
  };
3833
- const getLineNodes = lines => {
3834
- return lines.flatMap((line, index) => {
3835
- const lineContentNodes = getLineContentNodes(line);
3836
- return [{
3837
- childCount: 2,
3838
- className: Row,
3839
- type: Div
3840
- }, {
3841
- childCount: 1,
3842
- className: ChatDebugViewEventLineNumber,
3843
- type: Span
3844
- }, text(String(index + 1)), {
3845
- childCount: lineContentNodes.length / 2,
3846
- className: ChatDebugViewEventLineContent,
3847
- type: Span
3848
- }, ...lineContentNodes];
3849
- });
3850
- };
3851
4799
  const isChatViewEvent = value => {
3852
4800
  return typeof value === 'object' && value !== null && typeof value.type === 'string';
3853
4801
  };
@@ -3857,7 +4805,13 @@ const getEventNode = value => {
3857
4805
  type: getEventTypeLabel(value)
3858
4806
  } : value;
3859
4807
  const lines = getJsonLines(renderedValue);
3860
- const lineNodes = getLineNodes(lines);
4808
+ const lineNodes = getLineNodes(lines.map(line => {
4809
+ const lineContentNodes = getLineContentNodes(line);
4810
+ return {
4811
+ childCount: lineContentNodes.length / 2,
4812
+ nodes: lineContentNodes
4813
+ };
4814
+ }));
3861
4815
  return [{
3862
4816
  childCount: lines.length,
3863
4817
  className: ChatDebugViewEvent,
@@ -3866,19 +4820,61 @@ const getEventNode = value => {
3866
4820
  };
3867
4821
 
3868
4822
  const getEventsClassName = hasSelectedEvent => {
3869
- const widthClassName = joinClassNames(ChatDebugViewEvents, !hasSelectedEvent && ChatDebugViewEventsFullWidth);
4823
+ const widthClassName = mergeClassNames(ChatDebugViewEvents, hasSelectedEvent ? '' : ChatDebugViewEventsFullWidth);
3870
4824
  return widthClassName;
3871
4825
  };
3872
4826
 
4827
+ const getPreviewName = event => {
4828
+ if (typeof event.name === 'string' && event.name) {
4829
+ return event.name;
4830
+ }
4831
+ if (typeof event.toolName === 'string' && event.toolName) {
4832
+ return event.toolName;
4833
+ }
4834
+ return undefined;
4835
+ };
4836
+
3873
4837
  const hasOwn = (event, key) => {
3874
4838
  return Object.hasOwn(event, key);
3875
4839
  };
3876
- const isChatMessageUpdatedEvent = event => {
3877
- return event.type === 'chat-message-updated';
4840
+
4841
+ const shouldIncludeArguments = (event, name) => {
4842
+ if (!hasOwn(event, 'arguments')) {
4843
+ return false;
4844
+ }
4845
+ if (name === 'getWorkspaceUri') {
4846
+ return false;
4847
+ }
4848
+ return true;
4849
+ };
4850
+
4851
+ const getPayloadEvent = event => {
4852
+ const name = getPreviewName(event);
4853
+ const payloadEvent = {
4854
+ ...(name === undefined ? {} : {
4855
+ name
4856
+ }),
4857
+ ...(shouldIncludeArguments(event, name) ? {
4858
+ arguments: event.arguments
4859
+ } : {}),
4860
+ ...(hasOwn(event, 'result') ? {
4861
+ result: event.result
4862
+ } : {})
4863
+ };
4864
+ if (Object.keys(payloadEvent).length > 0) {
4865
+ return payloadEvent;
4866
+ }
4867
+ return event;
3878
4868
  };
4869
+
3879
4870
  const isChatMessageAddedEvent = event => {
3880
4871
  return event.type === 'chat-message-added';
3881
4872
  };
4873
+
4874
+ const isChatMessageUpdatedEvent = event => {
4875
+ return event.type === 'chat-message-updated';
4876
+ };
4877
+
3882
4878
  const getPreviewMessageText = event => {
3883
4879
  if (isChatMessageUpdatedEvent(event) && typeof event.text === 'string') {
3884
4880
  return event.text;
@@ -3903,61 +4899,112 @@ const getPreviewMessageText = event => {
3903
4899
  }
3904
4900
  return text;
3905
4901
  };
3906
- const getPreviewName = event => {
3907
- if (typeof event.name === 'string' && event.name) {
3908
- return event.name;
3909
- }
3910
- if (typeof event.toolName === 'string' && event.toolName) {
3911
- return event.toolName;
4902
+
4903
+ const getWriteFilePreviewText = (event, name) => {
4904
+ if (name !== 'write_file') {
4905
+ return undefined;
3912
4906
  }
3913
- return undefined;
3914
- };
3915
- const shouldIncludeArguments = (event, name) => {
3916
- if (!hasOwn(event, 'arguments')) {
3917
- return false;
4907
+ const {
4908
+ arguments: toolArguments
4909
+ } = event;
4910
+ if (typeof toolArguments !== 'object' || toolArguments === null || !Object.hasOwn(toolArguments, 'content')) {
4911
+ return undefined;
3918
4912
  }
3919
- if (name === 'getWorkspaceUri') {
3920
- return false;
4913
+ const {
4914
+ content
4915
+ } = toolArguments;
4916
+ if (typeof content !== 'string') {
4917
+ return undefined;
3921
4918
  }
3922
- return true;
4919
+ return content;
3923
4920
  };
4921
+
3924
4922
  const getPreviewEvent = event => {
4923
+ const selectedEventPreview = getSelectedEventPreview(event);
4924
+ if (selectedEventPreview !== undefined) {
4925
+ return selectedEventPreview;
4926
+ }
3925
4927
  const previewMessageText = getPreviewMessageText(event);
3926
4928
  if (previewMessageText !== undefined) {
3927
4929
  return previewMessageText;
3928
4930
  }
3929
4931
  const name = getPreviewName(event);
3930
- const previewEvent = {
3931
- ...(name === undefined ? {} : {
3932
- name
3933
- }),
3934
- ...(shouldIncludeArguments(event, name) ? {
3935
- arguments: event.arguments
3936
- } : {}),
3937
- ...(hasOwn(event, 'result') ? {
3938
- result: event.result
3939
- } : {})
3940
- };
3941
- if (Object.keys(previewEvent).length > 0) {
3942
- return previewEvent;
4932
+ const writeFilePreviewText = getWriteFilePreviewText(event, name);
4933
+ if (writeFilePreviewText !== undefined) {
4934
+ return writeFilePreviewText;
3943
4935
  }
3944
- return event;
4936
+ return getPayloadEvent(event);
3945
4937
  };
3946
4938
 
3947
- const getSashNodesDom = hasSelectedEvent => {
3948
- if (!hasSelectedEvent) {
3949
- return [];
3950
- }
4939
+ const isAttachmentImagePreview = value => {
4940
+ return typeof value === 'object' && value !== null && value.previewType === 'image';
4941
+ };
4942
+
4943
+ const getImagePreviewDom = preview => {
3951
4944
  return [{
3952
- childCount: 1,
3953
- className: ChatDebugViewSash,
3954
- onPointerDown: HandleSashPointerDown,
4945
+ childCount: 2,
4946
+ className: ChatDebugViewImagePreview,
3955
4947
  type: Div
3956
4948
  }, {
4949
+ alt: preview.alt,
3957
4950
  childCount: 0,
3958
- className: ChatDebugViewSashLine,
4951
+ className: ChatDebugViewImagePreviewImage,
4952
+ src: preview.src,
4953
+ type: Img
4954
+ }, {
4955
+ childCount: 1,
4956
+ className: ChatDebugViewImagePreviewLabel,
4957
+ type: Span
4958
+ }, text(preview.alt)];
4959
+ };
4960
+
4961
+ const getTextNode = value => {
4962
+ const lines = value.split('\n');
4963
+ const lineNodes = getLineNodes(lines.map(line => {
4964
+ return {
4965
+ childCount: 1,
4966
+ nodes: [{
4967
+ childCount: 1,
4968
+ className: TokenText,
4969
+ type: Span
4970
+ }, text(line)]
4971
+ };
4972
+ }));
4973
+ return [{
4974
+ childCount: lines.length,
4975
+ className: ChatDebugViewEvent,
3959
4976
  type: Div
3960
- }];
4977
+ }, ...lineNodes];
4978
+ };
4979
+
4980
+ const getPreviewEventNodes = previewEvent => {
4981
+ if (typeof previewEvent === 'string') {
4982
+ return getTextNode(previewEvent);
4983
+ }
4984
+ if (previewEvent === undefined) {
4985
+ return [];
4986
+ }
4987
+ if (isAttachmentImagePreview(previewEvent)) {
4988
+ return getImagePreviewDom(previewEvent);
4989
+ }
4990
+ return getEventNode(previewEvent);
4991
+ };
4992
+
4993
+ const sashNodesDom = [{
4994
+ childCount: 1,
4995
+ className: ChatDebugViewSash,
4996
+ onPointerDown: HandleSashPointerDown,
4997
+ type: Div
4998
+ }, {
4999
+ childCount: 0,
5000
+ className: ChatDebugViewSashLine,
5001
+ type: Div
5002
+ }];
5003
+ const getSashNodesDom = hasSelectedEvent => {
5004
+ if (!hasSelectedEvent) {
5005
+ return [];
5006
+ }
5007
+ return sashNodesDom;
3961
5008
  };
3962
5009
 
3963
5010
  const getTableBodyDom = (rowNodes, eventCount) => {
@@ -3970,37 +5017,40 @@ const getTableBodyDom = (rowNodes, eventCount) => {
3970
5017
  }, ...rowNodes];
3971
5018
  };
3972
5019
 
3973
- const getHeaderCellNodes = visibleTableColumns => {
3974
- return visibleTableColumns.flatMap(column => {
5020
+ const getHeaderCellNodes = (visibleTableColumns, tableColumns = createTableColumns()) => {
5021
+ const orderedVisibleTableColumns = getOrderedVisibleTableColumns(visibleTableColumns, tableColumns);
5022
+ return orderedVisibleTableColumns.flatMap((column, index) => {
5023
+ const isFixed = index < orderedVisibleTableColumns.length - 1;
3975
5024
  switch (column) {
3976
5025
  case Duration:
3977
5026
  return [{
3978
5027
  childCount: 1,
3979
- className: ChatDebugViewHeaderCell,
5028
+ className: mergeClassNames(ChatDebugViewHeaderCell, ChatDebugViewHeaderCellDuration, isFixed ? ChatDebugViewColumnFixed : ''),
3980
5029
  scope: 'col',
3981
5030
  type: Th
3982
- }, text(duration())];
5031
+ }, text(getTableColumnLabel(tableColumns, column))];
3983
5032
  case Status:
3984
5033
  return [{
3985
5034
  childCount: 1,
3986
- className: ChatDebugViewHeaderCell,
5035
+ className: mergeClassNames(ChatDebugViewHeaderCell, ChatDebugViewHeaderCellStatus, isFixed ? ChatDebugViewColumnFixed : ''),
3987
5036
  scope: 'col',
3988
5037
  type: Th
3989
- }, text(status())];
5038
+ }, text(getTableColumnLabel(tableColumns, column))];
3990
5039
  case Type:
3991
5040
  return [{
3992
5041
  childCount: 1,
3993
- className: ChatDebugViewHeaderCell,
5042
+ className: mergeClassNames(ChatDebugViewHeaderCell, ChatDebugViewHeaderCellType, isFixed ? ChatDebugViewColumnFixed : ''),
3994
5043
  scope: 'col',
3995
5044
  type: Th
3996
- }, text(type())];
5045
+ }, text(getTableColumnLabel(tableColumns, column))];
3997
5046
  default:
3998
5047
  return [];
3999
5048
  }
4000
5049
  });
4001
5050
  };
4002
- const getTableHeaderDom = (visibleTableColumns = defaultVisibleTableColumns) => {
4003
- const headerCellNodes = getHeaderCellNodes(visibleTableColumns);
5051
+
5052
+ const getTableHeaderDom = (visibleTableColumns = defaultVisibleTableColumns, tableColumns = createTableColumns()) => {
5053
+ const headerCellNodes = getHeaderCellNodes(visibleTableColumns, tableColumns);
4004
5054
  return [{
4005
5055
  childCount: 1,
4006
5056
  className: ChatDebugViewTableHeader,
@@ -4013,16 +5063,45 @@ const getTableHeaderDom = (visibleTableColumns = defaultVisibleTableColumns) =>
4013
5063
  }, ...headerCellNodes];
4014
5064
  };
4015
5065
 
4016
- const getTableDom = (rowNodes, eventCount, visibleTableColumns = defaultVisibleTableColumns) => {
5066
+ const resizerNames = ['ResizerOne', 'ResizerTwo'];
5067
+ const resizerClassNames = [ChatDebugViewResizerOne, ChatDebugViewResizerTwo];
5068
+ const getTableResizersDom = visibleTableColumns => {
5069
+ const visibleColumnCount = getOrderedVisibleTableColumns(visibleTableColumns).length;
5070
+ const resizerCount = Math.max(0, visibleColumnCount - 1);
5071
+ if (resizerCount === 0) {
5072
+ return [];
5073
+ }
5074
+ const visibleResizerClassNames = resizerClassNames.slice(0, resizerCount);
5075
+ const resizerNodes = visibleResizerClassNames.flatMap((resizerClassName, index) => [{
5076
+ childCount: 1,
5077
+ className: `${ChatDebugViewResizer} ${resizerClassName}`,
5078
+ name: resizerNames[index],
5079
+ onPointerDown: HandleTableResizerPointerDown,
5080
+ role: None,
5081
+ type: Button$1
5082
+ }, {
5083
+ childCount: 0,
5084
+ className: ChatDebugViewResizerInner,
5085
+ type: Div
5086
+ }]);
5087
+ return [{
5088
+ childCount: resizerCount,
5089
+ className: ChatDebugViewResizers,
5090
+ type: Div
5091
+ }, ...resizerNodes];
5092
+ };
5093
+
5094
+ const getTableDom = (rowNodes, eventCount, visibleTableColumns = defaultVisibleTableColumns, tableColumns = createTableColumns()) => {
5095
+ const resizerNodes = getTableResizersDom(visibleTableColumns);
4017
5096
  return [{
5097
+ childCount: 1 + (resizerNodes.length > 0 ? 1 : 0),
5098
+ className: ChatDebugViewTableWrapper,
5099
+ type: Div
5100
+ }, {
4018
5101
  childCount: 2,
4019
5102
  className: ChatDebugViewTable,
4020
5103
  type: Table
4021
- }, ...getTableHeaderDom(visibleTableColumns), ...getTableBodyDom(rowNodes, eventCount)];
4022
- };
4023
-
4024
- const formatPercent = value => {
4025
- return `${Number(value.toFixed(3))}%`;
5104
+ }, ...getTableHeaderDom(visibleTableColumns, tableColumns), ...getTableBodyDom(rowNodes, eventCount), ...resizerNodes];
4026
5105
  };
4027
5106
 
4028
5107
  const getBucketUnitDom = (unitCount, presetValue) => {
@@ -4032,7 +5111,7 @@ const getBucketUnitDom = (unitCount, presetValue) => {
4032
5111
  'data-value': presetValue
4033
5112
  } : {}),
4034
5113
  childCount: 0,
4035
- className: joinClassNames(ChatDebugViewTimelineBucketUnit, ChatDebugViewTimelineBucketUnitEmpty),
5114
+ className: mergeClassNames(ChatDebugViewTimelineBucketUnit, ChatDebugViewTimelineBucketUnitEmpty),
4036
5115
  type: Div
4037
5116
  }];
4038
5117
  }
@@ -4052,29 +5131,46 @@ const getBucketDom = bucket => {
4052
5131
  const presetValue = `${formatTimelinePresetValue(bucket.startSeconds)}:${formatTimelinePresetValue(bucket.endSeconds)}`;
4053
5132
  return [{
4054
5133
  childCount: 1,
4055
- className: joinClassNames(ChatDebugViewTimelineBucket, bucket.isSelected && ChatDebugViewTimelineBucketSelected),
5134
+ className: mergeClassNames(ChatDebugViewTimelineBucket, bucket.isSelected ? ChatDebugViewTimelineBucketSelected : ''),
4056
5135
  'data-value': presetValue,
4057
5136
  onClick: HandleTimelineRangePreset,
4058
5137
  type: Div
4059
5138
  }, {
4060
5139
  childCount: bucket.unitCount === 0 ? 1 : bucket.unitCount,
4061
- className: joinClassNames(ChatDebugViewTimelineBucketBar, bucket.isSelected && ChatDebugViewTimelineBucketBarSelected),
5140
+ className: mergeClassNames(ChatDebugViewTimelineBucketBar, bucket.isSelected ? ChatDebugViewTimelineBucketBarSelected : ''),
4062
5141
  'data-value': presetValue,
4063
5142
  type: Div
4064
5143
  }, ...getBucketUnitDom(bucket.unitCount, presetValue)];
4065
5144
  };
4066
5145
 
4067
- const getEffectiveTimelineRange = (timelineStartSeconds, timelineEndSeconds, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds) => {
4068
- if (!timelineSelectionActive) {
4069
- return {
4070
- endSeconds: timelineEndSeconds,
4071
- startSeconds: timelineStartSeconds
4072
- };
5146
+ const formatPercent = value => {
5147
+ return `${Number(value.toFixed(3))}%`;
5148
+ };
5149
+
5150
+ const getSelectionNodesDom = (hasSelection, selectionStartPercent, selectionEndPercent) => {
5151
+ if (!hasSelection || selectionStartPercent === null || selectionEndPercent === null) {
5152
+ return [];
4073
5153
  }
4074
- return {
4075
- endSeconds: timelineSelectionFocusSeconds,
4076
- startSeconds: timelineSelectionAnchorSeconds
4077
- };
5154
+ return [{
5155
+ childCount: 0,
5156
+ className: ChatDebugViewTimelineSelectionRange,
5157
+ style: `left:${formatPercent(selectionStartPercent)};width:${formatPercent(selectionEndPercent - selectionStartPercent)};`,
5158
+ type: Div
5159
+ }, {
5160
+ childCount: 0,
5161
+ className: mergeClassNames(ChatDebugViewTimelineSelectionHandle, ChatDebugViewTimelineSelectionMarker, ChatDebugViewTimelineSelectionMarkerStart),
5162
+ name: Start,
5163
+ role: None,
5164
+ style: `left:${formatPercent(selectionStartPercent)};`,
5165
+ type: Button$1
5166
+ }, {
5167
+ childCount: 0,
5168
+ className: mergeClassNames(ChatDebugViewTimelineSelectionHandle, ChatDebugViewTimelineSelectionMarker, ChatDebugViewTimelineSelectionMarkerEnd),
5169
+ name: End,
5170
+ role: None,
5171
+ style: `left:${formatPercent(selectionEndPercent)};`,
5172
+ type: Button$1
5173
+ }];
4078
5174
  };
4079
5175
 
4080
5176
  const formatTimelineSeconds = value => {
@@ -4084,36 +5180,18 @@ const formatTimelineSeconds = value => {
4084
5180
  return `${Number(value.toFixed(1))}s`;
4085
5181
  };
4086
5182
 
4087
- const getTimelineSummary = (timelineEvents, timelineStartSeconds, timelineEndSeconds) => {
4088
- const timelineInfo = getTimelineInfo(timelineEvents, timelineStartSeconds, timelineEndSeconds);
5183
+ const getTimelineSummary = timelineInfo => {
4089
5184
  if (timelineInfo.hasSelection && timelineInfo.startSeconds !== null && timelineInfo.endSeconds !== null) {
4090
5185
  return windowSummary(formatTimelineSeconds(timelineInfo.startSeconds), formatTimelineSeconds(timelineInfo.endSeconds), formatTimelineSeconds(timelineInfo.durationSeconds));
4091
5186
  }
4092
5187
  return windowSummary('0s', formatTimelineSeconds(timelineInfo.durationSeconds), formatTimelineSeconds(timelineInfo.durationSeconds));
4093
5188
  };
4094
5189
 
4095
- const getTimelineNodes = (timelineEvents, timelineStartSeconds, timelineEndSeconds, timelineSelectionActive = false, timelineSelectionAnchorSeconds = '', timelineSelectionFocusSeconds = '') => {
4096
- const effectiveRange = getEffectiveTimelineRange(timelineStartSeconds, timelineEndSeconds, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds);
4097
- const timelineInfo = getTimelineInfo(timelineEvents, effectiveRange.startSeconds, effectiveRange.endSeconds);
5190
+ const getTimelineNodes = timelineInfo => {
4098
5191
  if (timelineInfo.buckets.length === 0) {
4099
5192
  return [];
4100
5193
  }
4101
- const selectionNodes = timelineInfo.hasSelection && timelineInfo.selectionStartPercent !== null && timelineInfo.selectionEndPercent !== null ? [{
4102
- childCount: 0,
4103
- className: ChatDebugViewTimelineSelectionRange,
4104
- style: `left:${formatPercent(timelineInfo.selectionStartPercent)};width:${formatPercent(timelineInfo.selectionEndPercent - timelineInfo.selectionStartPercent)};`,
4105
- type: Div
4106
- }, {
4107
- childCount: 0,
4108
- className: joinClassNames(ChatDebugViewTimelineSelectionMarker, ChatDebugViewTimelineSelectionMarkerStart),
4109
- style: `left:${formatPercent(timelineInfo.selectionStartPercent)};`,
4110
- type: Div
4111
- }, {
4112
- childCount: 0,
4113
- className: joinClassNames(ChatDebugViewTimelineSelectionMarker, ChatDebugViewTimelineSelectionMarkerEnd),
4114
- style: `left:${formatPercent(timelineInfo.selectionEndPercent)};`,
4115
- type: Div
4116
- }] : [];
5194
+ const selectionNodes = getSelectionNodesDom(timelineInfo.hasSelection, timelineInfo.selectionStartPercent, timelineInfo.selectionEndPercent);
4117
5195
  return [{
4118
5196
  childCount: 2,
4119
5197
  className: ChatDebugViewTimeline,
@@ -4126,8 +5204,8 @@ const getTimelineNodes = (timelineEvents, timelineStartSeconds, timelineEndSecon
4126
5204
  }, {
4127
5205
  childCount: 1,
4128
5206
  className: ChatDebugViewTimelineSummary,
4129
- type: Div
4130
- }, text(getTimelineSummary(timelineEvents, effectiveRange.startSeconds, effectiveRange.endSeconds)), {
5207
+ type: H2
5208
+ }, text(getTimelineSummary(timelineInfo)), {
4131
5209
  childCount: 2,
4132
5210
  className: ChatDebugViewTimelineInteractive,
4133
5211
  onDoubleClick: HandleTimelineDoubleClick,
@@ -4144,38 +5222,48 @@ const getTimelineNodes = (timelineEvents, timelineStartSeconds, timelineEndSecon
4144
5222
  }, ...selectionNodes];
4145
5223
  };
4146
5224
 
4147
- const getDevtoolsDom = (events, selectedEvent, selectedEventIndex, timelineEvents, timelineStartSeconds, timelineEndSeconds, emptyMessage = noEventsFound(), timelineSelectionActive = false, timelineSelectionAnchorSeconds = '', timelineSelectionFocusSeconds = '', selectedDetailTab = Response, visibleTableColumns = defaultVisibleTableColumns) => {
5225
+ const getDevtoolsDom = (events, selectedEvent, selectedEventIndex, timelineEvents, timelineStartSeconds, timelineEndSeconds, emptyMessage = noEventsFound(), timelineSelectionActive = false, timelineSelectionAnchorSeconds = '', timelineSelectionFocusSeconds = '', visibleTableColumns = defaultVisibleTableColumns, detailTabs = createDetailTabs(), tableColumns = createTableColumns(), timelineInfo) => {
4148
5226
  const rowNodes = getDevtoolsRows(events, selectedEventIndex, visibleTableColumns);
4149
- const timelineNodes = getTimelineNodes(timelineEvents, timelineStartSeconds, timelineEndSeconds, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds);
4150
- const previewEventNodes = selectedEvent ? getEventNode(getPreviewEvent(selectedEvent)) : [];
5227
+ const effectiveRange = getEffectiveTimelineRange(timelineStartSeconds, timelineEndSeconds, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds);
5228
+ const resolvedTimelineInfo = timelineInfo || getTimelineInfo(timelineEvents, effectiveRange.startSeconds, effectiveRange.endSeconds);
5229
+ const timelineNodes = getTimelineNodes(resolvedTimelineInfo);
5230
+ const previewEvent = selectedEvent ? getPreviewEvent(selectedEvent) : undefined;
5231
+ const previewEventNodes = getPreviewEventNodes(previewEvent);
5232
+ const payloadEventNodes = selectedEvent ? getEventNode(getPayloadEvent(selectedEvent)) : [];
4151
5233
  const responseEventNodes = selectedEvent ? getEventNode(selectedEvent) : [];
4152
5234
  const hasSelectedEvent = responseEventNodes.length > 0;
4153
- const tableNodes = events.length === 0 ? getEmptyStateDom(emptyMessage) : getTableDom(rowNodes, events.length, visibleTableColumns);
5235
+ const tableNodes = events.length === 0 ? getEmptyStateDom(emptyMessage) : getTableDom(rowNodes, events.length, visibleTableColumns, tableColumns);
4154
5236
  const eventsClassName = getEventsClassName(hasSelectedEvent);
4155
- const detailsNodes = getDetailsDom(previewEventNodes, responseEventNodes, selectedEvent, isDetailTab(selectedDetailTab) ? selectedDetailTab : Response);
5237
+ const detailsNodes = getDetailsDom(previewEventNodes, payloadEventNodes, responseEventNodes, selectedEvent, detailTabs);
4156
5238
  const sashNodes = getSashNodesDom(hasSelectedEvent);
4157
5239
  const splitChildCount = hasSelectedEvent ? 3 : 1;
4158
5240
  const mainChildCount = 1 + (timelineNodes.length > 0 ? 1 : 0);
4159
5241
  return [{
4160
5242
  childCount: mainChildCount,
4161
5243
  className: ChatDebugViewDevtoolsMain,
4162
- role: 'none',
5244
+ role: None,
4163
5245
  type: Div
4164
5246
  }, ...timelineNodes, {
4165
5247
  childCount: splitChildCount,
4166
5248
  className: ChatDebugViewDevtoolsSplit,
4167
- role: 'none',
5249
+ role: None,
4168
5250
  type: Div
4169
5251
  }, {
4170
5252
  childCount: 1,
4171
5253
  className: eventsClassName,
4172
- onKeyDown: HandleTableKeyDown,
4173
5254
  role: 'application',
4174
5255
  tabIndex: 0,
4175
5256
  type: Div
4176
5257
  }, ...tableNodes, ...sashNodes, ...detailsNodes];
4177
5258
  };
4178
5259
 
5260
+ const getEmptyMessage = (eventCount, hasFilterValue, useNoToolCallEventsMessage, noFilteredEventsMessage) => {
5261
+ if (eventCount === 0 && hasFilterValue) {
5262
+ return useNoToolCallEventsMessage ? noToolCallEvents() : noFilteredEventsMessage;
5263
+ }
5264
+ return noEventsFound();
5265
+ };
5266
+
4179
5267
  const getLegacyEventsDom = (errorMessage, emptyMessage, eventNodes) => {
4180
5268
  return [{
4181
5269
  childCount: eventNodes.length === 0 ? 1 : eventNodes.length,
@@ -4190,27 +5278,30 @@ const getLegacyEventsDom = (errorMessage, emptyMessage, eventNodes) => {
4190
5278
  }, text(errorMessage || emptyMessage)] : eventNodes)];
4191
5279
  };
4192
5280
 
4193
- const getQuickFilterNodes = (eventCategoryFilter, eventCategoryFilterOptions) => {
5281
+ // cspell:ignore multiselectable
5282
+ const getQuickFilterNodes = categoryFilters => {
4194
5283
  return [{
4195
- childCount: eventCategoryFilterOptions.length,
5284
+ 'aria-multiselectable': true,
5285
+ childCount: categoryFilters.length,
4196
5286
  className: ChatDebugViewQuickFilters,
5287
+ onClick: HandleEventCategoryFilter,
5288
+ role: 'listbox',
4197
5289
  type: Div
4198
- }, ...eventCategoryFilterOptions.flatMap(option => {
4199
- const isSelected = option.value === eventCategoryFilter;
5290
+ }, ...categoryFilters.flatMap(categoryFilter => {
5291
+ const {
5292
+ isSelected,
5293
+ label,
5294
+ name
5295
+ } = categoryFilter;
4200
5296
  return [{
4201
- childCount: 2,
4202
- className: joinClassNames(ChatDebugViewQuickFilterPill, isSelected && ChatDebugViewQuickFilterPillSelected),
4203
- type: Label
4204
- }, {
4205
- checked: isSelected,
4206
- childCount: 0,
4207
- className: ChatDebugViewQuickFilterInput,
4208
- inputType: 'radio',
4209
- name: EventCategoryFilter,
4210
- onChange: HandleEventCategoryFilter,
4211
- type: Input,
4212
- value: option.value
4213
- }, text(option.label)];
5297
+ 'aria-selected': isSelected,
5298
+ childCount: 1,
5299
+ className: mergeClassNames(ChatDebugViewQuickFilterPill, isSelected ? ChatDebugViewQuickFilterPillSelected : ''),
5300
+ 'data-value': name,
5301
+ onClick: HandleEventCategoryFilter,
5302
+ role: 'option',
5303
+ type: Div
5304
+ }, text(label)];
4214
5305
  })];
4215
5306
  };
4216
5307
 
@@ -4229,14 +5320,21 @@ const getTimelineFilterDescription = (timelineStartSeconds, timelineEndSeconds)
4229
5320
  return '';
4230
5321
  };
4231
5322
 
4232
- const getChatDebugViewDom = (errorMessage, filterValue, eventCategoryFilter, eventCategoryFilterOptions, _showEventStreamFinishedEvents, _showInputEvents, _showResponsePartEvents, useDevtoolsLayout, selectedEvent, selectedEventIndex, timelineStartSeconds, timelineEndSeconds, timelineEvents, events, timelineSelectionActive = false, timelineSelectionAnchorSeconds = '', timelineSelectionFocusSeconds = '', selectedDetailTab = Response, visibleTableColumns = defaultVisibleTableColumns) => {
5323
+ const getEventCategoryFilterDescription = eventCategoryFilters => {
5324
+ if (eventCategoryFilters.length === 0 || eventCategoryFilters.includes(All)) {
5325
+ return '';
5326
+ }
5327
+ return eventCategoryFilters.map(eventCategoryFilter => getEventCategoryFilterLabel(eventCategoryFilter).toLowerCase()).join(', ');
5328
+ };
5329
+ const getChatDebugViewDom = (errorMessage, filterValue, eventCategoryFilters, categoryFilters, _showEventStreamFinishedEvents, _showInputEvents, _showResponsePartEvents, useDevtoolsLayout, selectedEvent, selectedEventIndex, timelineStartSeconds, timelineEndSeconds, timelineEvents, events, timelineSelectionActive = false, timelineSelectionAnchorSeconds = '', timelineSelectionFocusSeconds = '', visibleTableColumns = defaultVisibleTableColumns, detailTabs = createDetailTabs(), tableColumns = createTableColumns(), timelineInfo) => {
4233
5330
  if (errorMessage) {
4234
5331
  return getDebugErrorDom(errorMessage);
4235
5332
  }
4236
5333
  const trimmedFilterValue = filterValue.trim();
4237
5334
  const filterDescriptionParts = [];
4238
- if (eventCategoryFilter !== All) {
4239
- filterDescriptionParts.push(getEventCategoryFilterLabel(eventCategoryFilter).toLowerCase());
5335
+ const eventCategoryFilterDescription = getEventCategoryFilterDescription(eventCategoryFilters);
5336
+ if (eventCategoryFilterDescription) {
5337
+ filterDescriptionParts.push(eventCategoryFilterDescription);
4240
5338
  }
4241
5339
  if (trimmedFilterValue) {
4242
5340
  filterDescriptionParts.push(trimmedFilterValue);
@@ -4249,16 +5347,16 @@ const getChatDebugViewDom = (errorMessage, filterValue, eventCategoryFilter, eve
4249
5347
  const hasFilterValue = filterDescriptionParts.length > 0;
4250
5348
  const filterDescription = filterDescriptionParts.join(' ');
4251
5349
  const noFilteredEventsMessage = noEventsFoundMatching(filterDescription);
4252
- const useNoToolCallEventsMessage = eventCategoryFilter === Tools && !trimmedFilterValue && !hasTimelineFilter;
4253
- const emptyMessage = events.length === 0 && hasFilterValue ? useNoToolCallEventsMessage ? noToolCallEvents() : noFilteredEventsMessage : noEventsFound();
5350
+ const useNoToolCallEventsMessage = eventCategoryFilters.length === 1 && eventCategoryFilters[0] === Tools && !trimmedFilterValue && !hasTimelineFilter;
5351
+ const emptyMessage = getEmptyMessage(events.length, hasFilterValue, useNoToolCallEventsMessage, noFilteredEventsMessage);
4254
5352
  const safeSelectedEventIndex = selectedEventIndex === null || selectedEventIndex < 0 || selectedEventIndex >= events.length ? null : selectedEventIndex;
4255
- const contentNodes = useDevtoolsLayout ? getDevtoolsDom(events, selectedEvent, safeSelectedEventIndex, timelineEvents, timelineStartSeconds, timelineEndSeconds, emptyMessage, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds, isDetailTab(selectedDetailTab) ? selectedDetailTab : Response, visibleTableColumns) : getLegacyEventsDom(errorMessage, emptyMessage, events.flatMap(getEventNode));
4256
- const quickFilterNodes = useDevtoolsLayout ? getQuickFilterNodes(eventCategoryFilter, eventCategoryFilterOptions) : [];
5353
+ const contentNodes = useDevtoolsLayout ? getDevtoolsDom(events, selectedEvent, safeSelectedEventIndex, timelineEvents, timelineStartSeconds, timelineEndSeconds, emptyMessage, timelineSelectionActive, timelineSelectionAnchorSeconds, timelineSelectionFocusSeconds, visibleTableColumns, detailTabs, tableColumns, timelineInfo) : getLegacyEventsDom(errorMessage, emptyMessage, events.flatMap(getEventNode));
5354
+ const quickFilterNodes = useDevtoolsLayout ? getQuickFilterNodes(categoryFilters) : [];
4257
5355
  const debugViewTopDom = getDebugViewTopDom(filterValue, useDevtoolsLayout, quickFilterNodes);
4258
5356
  const rootChildCount = 2;
4259
5357
  return [{
4260
5358
  childCount: rootChildCount,
4261
- className: joinClassNames(ChatDebugView, useDevtoolsLayout && ChatDebugViewDevtools),
5359
+ className: mergeClassNames(ChatDebugView, useDevtoolsLayout ? ChatDebugViewDevtools : ''),
4262
5360
  type: Div
4263
5361
  }, ...debugViewTopDom, ...contentNodes];
4264
5362
  };
@@ -4275,9 +5373,8 @@ const renderItems = (oldState, newState) => {
4275
5373
  if (newState.initial) {
4276
5374
  return [SetDom2, newState.uid, []];
4277
5375
  }
4278
- const timelineEvents = getTimelineEvents(newState);
4279
- const filteredEvents = filterEventsByTimelineRange(timelineEvents, newState.timelineStartSeconds, newState.timelineEndSeconds);
4280
- const dom = getChatDebugViewDom(newState.errorMessage, newState.filterValue, newState.eventCategoryFilter, newState.eventCategoryFilterOptions, newState.showEventStreamFinishedEvents, newState.showInputEvents, newState.showResponsePartEvents, newState.useDevtoolsLayout, newState.selectedEvent, newState.selectedEventIndex, newState.timelineStartSeconds, newState.timelineEndSeconds, withSessionEventIds(timelineEvents), withSessionEventIds(filteredEvents), newState.timelineSelectionActive, newState.timelineSelectionAnchorSeconds, newState.timelineSelectionFocusSeconds, newState.selectedDetailTab, newState.visibleTableColumns);
5376
+ const filteredEvents = filterEventsByTimelineRange(newState.timelineEvents, newState.timelineStartSeconds, newState.timelineEndSeconds);
5377
+ const dom = getChatDebugViewDom(newState.errorMessage, newState.filterValue, getSelectedEventCategoryFilters(newState.categoryFilters), newState.categoryFilters, newState.showEventStreamFinishedEvents, newState.showInputEvents, newState.showResponsePartEvents, newState.useDevtoolsLayout, newState.selectedEvent, newState.selectedEventIndex, newState.timelineStartSeconds, newState.timelineEndSeconds, withSessionEventIds(newState.timelineEvents), withSessionEventIds(filteredEvents), newState.timelineSelectionActive, newState.timelineSelectionAnchorSeconds, newState.timelineSelectionFocusSeconds, newState.visibleTableColumns, newState.detailTabs, newState.tableColumns, newState.timelineInfo);
4281
5378
  return [SetDom2, newState.uid, dom];
4282
5379
  };
4283
5380
 
@@ -4351,10 +5448,10 @@ const renderEventListeners = () => {
4351
5448
  params: ['handleInput', TargetName, TargetValue]
4352
5449
  }, {
4353
5450
  name: HandleEventCategoryFilter,
4354
- params: ['handleEventCategoryFilter', TargetValue]
5451
+ params: ['handleEventCategoryFilter', 'event.target.dataset.value', 'event.ctrlKey', 'event.metaKey']
4355
5452
  }, {
4356
- name: HandleDetailTab,
4357
- params: ['handleDetailTab', TargetValue]
5453
+ name: SelectDetailTab,
5454
+ params: ['selectDetailTab', TargetValue]
4358
5455
  }, {
4359
5456
  name: HandleTimelineRangePreset,
4360
5457
  params: ['handleTimelineRangePreset', 'event.target.dataset.value']
@@ -4364,9 +5461,6 @@ const renderEventListeners = () => {
4364
5461
  }, {
4365
5462
  name: HandleClickRefresh,
4366
5463
  params: ['handleClickRefresh']
4367
- }, {
4368
- name: HandleTableKeyDown,
4369
- params: ['handleTableKeyDown', 'event.key']
4370
5464
  }, {
4371
5465
  name: HandleSashPointerDown,
4372
5466
  params: ['handleSashPointerDown', ClientX, ClientY],
@@ -4377,9 +5471,19 @@ const renderEventListeners = () => {
4377
5471
  }, {
4378
5472
  name: HandleSashPointerUp,
4379
5473
  params: ['handleSashPointerUp', ClientX, ClientY]
5474
+ }, {
5475
+ name: HandleTableResizerPointerDown,
5476
+ params: ['handleTableResizerPointerDown', TargetName, ClientX],
5477
+ trackPointerEvents: [HandleTableResizerPointerMove, HandleTableResizerPointerUp]
5478
+ }, {
5479
+ name: HandleTableResizerPointerMove,
5480
+ params: ['handleTableResizerPointerMove', ClientX]
5481
+ }, {
5482
+ name: HandleTableResizerPointerUp,
5483
+ params: ['handleTableResizerPointerUp']
4380
5484
  }, {
4381
5485
  name: HandleTimelinePointerDown,
4382
- params: ['handleTimelinePointerDown', ClientX],
5486
+ params: ['handleTimelinePointerDown', TargetName, ClientX],
4383
5487
  trackPointerEvents: [HandleTimelinePointerMove, HandleTimelinePointerUp]
4384
5488
  }, {
4385
5489
  name: HandleTimelinePointerMove,
@@ -4400,12 +5504,14 @@ const rerender = state => {
4400
5504
  const isSameVisibleTableColumns = (a, b) => {
4401
5505
  return a.length === b.length && a.every((value, index) => value === b[index]);
4402
5506
  };
5507
+
4403
5508
  const resetTableColumns = state => {
4404
- if (isSameVisibleTableColumns(state.visibleTableColumns, defaultVisibleTableColumns)) {
5509
+ if (isSameVisibleTableColumns(state.visibleTableColumns, defaultVisibleTableColumns) && isSameTableColumnWidths(state.tableColumnWidths, defaultTableColumnWidths)) {
4405
5510
  return state;
4406
5511
  }
4407
5512
  return {
4408
5513
  ...state,
5514
+ tableColumnWidths: defaultTableColumnWidths,
4409
5515
  visibleTableColumns: defaultVisibleTableColumns
4410
5516
  };
4411
5517
  };
@@ -4433,21 +5539,24 @@ const resize = (state, dimensions) => {
4433
5539
 
4434
5540
  const saveState = state => {
4435
5541
  const {
4436
- eventCategoryFilter,
5542
+ categoryFilters,
5543
+ detailTabs,
4437
5544
  filterValue,
4438
- selectedDetailTab,
4439
5545
  selectedEventId,
4440
5546
  sessionId,
5547
+ tableColumnWidths,
4441
5548
  timelineEndSeconds,
4442
5549
  timelineStartSeconds,
4443
5550
  visibleTableColumns
4444
5551
  } = state;
4445
5552
  return {
4446
- eventCategoryFilter,
5553
+ eventCategoryFilter: getSelectedEventCategoryFilter(categoryFilters),
5554
+ eventCategoryFilters: getSelectedEventCategoryFilters(categoryFilters),
4447
5555
  filterValue,
4448
- selectedDetailTab,
5556
+ selectedDetailTab: getSelectedDetailTab(detailTabs),
4449
5557
  selectedEventId,
4450
5558
  sessionId,
5559
+ tableColumnWidths,
4451
5560
  timelineEndSeconds,
4452
5561
  timelineStartSeconds,
4453
5562
  visibleTableColumns
@@ -4455,7 +5564,7 @@ const saveState = state => {
4455
5564
  };
4456
5565
 
4457
5566
  const setEvents = (state, events) => {
4458
- return {
5567
+ return getStateWithTimelineInfo({
4459
5568
  ...state,
4460
5569
  errorMessage: '',
4461
5570
  events,
@@ -4463,14 +5572,20 @@ const setEvents = (state, events) => {
4463
5572
  selectedEvent: null,
4464
5573
  selectedEventId: null,
4465
5574
  selectedEventIndex: null
4466
- };
5575
+ });
4467
5576
  };
4468
5577
 
4469
5578
  const setSessionIdDependencies = {
4470
5579
  listChatViewEvents: listChatViewEvents
4471
5580
  };
4472
5581
  const setSessionId = async (state, sessionId) => {
4473
- const result = await setSessionIdDependencies.listChatViewEvents(sessionId, state.databaseName, state.dataBaseVersion, state.eventStoreName, state.sessionIdIndexName);
5582
+ const {
5583
+ databaseName,
5584
+ dataBaseVersion,
5585
+ eventStoreName,
5586
+ sessionIdIndexName
5587
+ } = state;
5588
+ const result = await setSessionIdDependencies.listChatViewEvents(sessionId, databaseName, dataBaseVersion, eventStoreName, sessionIdIndexName);
4474
5589
  if (result.type === 'error') {
4475
5590
  return {
4476
5591
  ...state,
@@ -4502,20 +5617,16 @@ const toggleTableColumnVisibility = (state, column) => {
4502
5617
  if (!isTableColumn(column)) {
4503
5618
  return state;
4504
5619
  }
4505
- const nextVisibleColumns = new Set(state.visibleTableColumns);
4506
- if (nextVisibleColumns.has(column)) {
4507
- nextVisibleColumns.delete(column);
4508
- } else {
4509
- nextVisibleColumns.add(column);
4510
- }
5620
+ const nextVisibleColumns = state.visibleTableColumns.includes(column) ? state.visibleTableColumns.filter(visibleColumn => visibleColumn !== column) : [...state.visibleTableColumns, column];
4511
5621
  return {
4512
5622
  ...state,
4513
- visibleTableColumns: getOrderedVisibleTableColumns([...nextVisibleColumns])
5623
+ visibleTableColumns: getOrderedVisibleTableColumns(nextVisibleColumns)
4514
5624
  };
4515
5625
  };
4516
5626
 
4517
5627
  const commandMap = {
4518
5628
  'ChatDebug.appendStoredEventForTest': wrapCommand(appendStoredEventForTest),
5629
+ 'ChatDebug.appendStoredImageAttachmentForTest': wrapCommand(appendStoredImageAttachmentForTest),
4519
5630
  'ChatDebug.create': create,
4520
5631
  'ChatDebug.diff2': diff2,
4521
5632
  'ChatDebug.getCommandIds': getCommandIds,
@@ -4525,7 +5636,6 @@ const commandMap = {
4525
5636
  'ChatDebug.handleCloseDetails': wrapCommand(handleCloseDetails),
4526
5637
  'ChatDebug.handleDetailsContextMenu': wrapCommand(handleDetailsContextMenu),
4527
5638
  'ChatDebug.handleDetailsTopContextMenu': wrapCommand(handleDetailsTopContextMenu),
4528
- 'ChatDebug.handleDetailTab': wrapCommand(handleDetailTab),
4529
5639
  'ChatDebug.handleEventCategoryFilter': wrapCommand(handleEventCategoryFilter),
4530
5640
  'ChatDebug.handleEventRowClick': wrapCommand(handleEventRowClick),
4531
5641
  'ChatDebug.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
@@ -4537,6 +5647,10 @@ const commandMap = {
4537
5647
  'ChatDebug.handleShowInputEvents': wrapCommand(handleShowInputEvents),
4538
5648
  'ChatDebug.handleShowResponsePartEvents': wrapCommand(handleShowResponsePartEvents),
4539
5649
  'ChatDebug.handleTableBodyContextMenu': wrapCommand(handleTableBodyContextMenu),
5650
+ 'ChatDebug.handleTableResizerPointerDown': wrapCommand(handleTableResizerPointerDown),
5651
+ 'ChatDebug.handleTableResizerPointerMove': wrapCommand(handleTableResizerPointerMove),
5652
+ 'ChatDebug.handleTableResizerPointerUp': wrapCommand(handleTableResizerPointerUp),
5653
+ 'ChatDebug.handleTableRowCopy': wrapCommand(handleTableRowCopy),
4540
5654
  'ChatDebug.handleTimelineContextMenu': wrapCommand(handleTimelineContextMenu),
4541
5655
  'ChatDebug.handleTimelineDoubleClick': wrapCommand(handleTimelineDoubleClick),
4542
5656
  'ChatDebug.handleTimelineEndSeconds': wrapCommand(handleTimelineEndSeconds),
@@ -4545,7 +5659,7 @@ const commandMap = {
4545
5659
  'ChatDebug.handleTimelinePointerUp': wrapCommand(handleTimelinePointerUp),
4546
5660
  'ChatDebug.handleTimelineRangePreset': wrapCommand(handleTimelineRangePreset),
4547
5661
  'ChatDebug.handleTimelineStartSeconds': wrapCommand(handleTimelineStartSeconds),
4548
- 'ChatDebug.handleUseDevtoolsLayout': wrapCommand(handleUseDevtoolsLayout),
5662
+ 'ChatDebug.handleUseDevtoolsLayout': wrapCommand(setUseDevtoolsLayout),
4549
5663
  'ChatDebug.loadContent': wrapCommand(loadContent),
4550
5664
  'ChatDebug.loadContent2': wrapCommand(loadContent),
4551
5665
  'ChatDebug.refresh': wrapCommand(refresh),
@@ -4555,6 +5669,7 @@ const commandMap = {
4555
5669
  'ChatDebug.resetTableColumns': wrapCommand(resetTableColumns),
4556
5670
  'ChatDebug.resize': wrapCommand(resize),
4557
5671
  'ChatDebug.saveState': wrapGetter(saveState),
5672
+ 'ChatDebug.selectDetailTab': wrapCommand(selectDetailTab),
4558
5673
  'ChatDebug.setEvents': wrapCommand(setEvents),
4559
5674
  'ChatDebug.setSessionId': wrapCommand(setSessionId),
4560
5675
  'ChatDebug.terminate': terminate,