@tldiagram/core-ui 2.0.6 → 2.0.7

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.
@@ -0,0 +1,47 @@
1
+ import type { WatchDiff } from '../api/client';
2
+ import type { ExploreData } from '../types';
3
+ import { type WatchChangeType } from './watchDiffSummary';
4
+ export interface ExploreDiffLineDelta {
5
+ added: number;
6
+ removed: number;
7
+ }
8
+ export interface ExploreDiffDetail {
9
+ key: string;
10
+ resourceType: string;
11
+ resourceId?: number;
12
+ changeType: WatchChangeType;
13
+ summary?: string;
14
+ ownerType: string;
15
+ ownerKey: string;
16
+ language?: string;
17
+ addedLines: number;
18
+ removedLines: number;
19
+ sourcePath?: string;
20
+ line?: number;
21
+ }
22
+ export interface ExploreDiffTarget extends ExploreDiffDetail {
23
+ label: string;
24
+ viewId?: number;
25
+ viewName?: string;
26
+ unplaced: boolean;
27
+ }
28
+ export interface ExploreDiffLens {
29
+ versionId: number;
30
+ elementChanges: Map<number, WatchChangeType>;
31
+ connectorChanges: Map<number, WatchChangeType>;
32
+ elementLineDeltas: Map<number, ExploreDiffLineDelta>;
33
+ diffDetailsByResource: Map<string, ExploreDiffDetail>;
34
+ orderedTargets: ExploreDiffTarget[];
35
+ unplacedTargets: ExploreDiffTarget[];
36
+ changedElementIds: Set<number>;
37
+ changedConnectorIds: Set<number>;
38
+ ancestorElementIds: Set<number>;
39
+ siblingElementIds: Set<number>;
40
+ contextElementIds: Set<number>;
41
+ contextConnectorIds: Set<number>;
42
+ totalAddedLines: number;
43
+ totalRemovedLines: number;
44
+ }
45
+ export declare function diffResourceKey(resourceType: string | null | undefined, resourceId: number | null | undefined): string;
46
+ export declare function sourcePathFromDiff(diff: Pick<WatchDiff, 'owner_type' | 'owner_key'>): string | undefined;
47
+ export declare function buildExploreDiffLens(data: ExploreData, diffs: WatchDiff[] | null | undefined, versionId: number): ExploreDiffLens;
@@ -0,0 +1 @@
1
+ export {};
@@ -26,9 +26,13 @@ export interface WatchDiffSummary {
26
26
  elements: WatchResourceStat;
27
27
  connectors: WatchResourceStat;
28
28
  }
29
+ export declare function changedResourceCount(stat: WatchResourceStat): number;
30
+ export declare function totalResourceCount(stat: WatchResourceStat): number;
29
31
  export declare function normalizeWatchChangeType(value: string): WatchChangeType;
32
+ export declare function isWatchDiffChange(value: string | null | undefined): boolean;
30
33
  export declare function emptyWatchResourceStat(): WatchResourceStat;
31
34
  export declare function summarizeWatchDiffs(diffs: WatchDiff[] | null | undefined): WatchDiffSummary;
32
35
  export declare function formatStatLine(label: string, stat: WatchResourceStat): string;
36
+ export declare function formatDiagramResourceSummary(summary: WatchDiffSummary): string;
33
37
  export declare function formatTldStatLine(summary: WatchDiffSummary): string;
34
38
  export declare function buildWatchDiffLocations(data: ExploreData, diffs: WatchDiff[] | null | undefined): WatchDiffLocation[];
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tldiagram/core-ui",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,10 @@ import {
35
35
  import { buildWorkspaceVersionPreview, useWorkspaceVersionPreview } from '../context/WorkspaceVersionContext'
36
36
  import {
37
37
  buildWatchDiffLocations,
38
+ formatDiagramResourceSummary,
39
+ isWatchDiffChange,
38
40
  summarizeWatchDiffs,
41
+ totalResourceCount,
39
42
  type WatchDiffLocation,
40
43
  type WatchDiffSummary,
41
44
  } from '../utils/watchDiffSummary'
@@ -127,9 +130,7 @@ function ResourceCountDisplay({ summary }: { summary: WatchDiffSummary }) {
127
130
  { label: 'Elements', stat: summary.elements },
128
131
  { label: 'Connectors', stat: summary.connectors },
129
132
  ]
130
- const total = rows.reduce((sum, row) => (
131
- sum + row.stat.added + row.stat.updated + row.stat.deleted + row.stat.initialized
132
- ), 0)
133
+ const total = rows.reduce((sum, row) => sum + totalResourceCount(row.stat), 0)
133
134
  const changes = [
134
135
  { key: 'added', label: 'added', color: 'green.300' },
135
136
  { key: 'updated', label: 'updated', color: 'yellow.300' },
@@ -165,7 +166,7 @@ function ResourceCountDisplay({ summary }: { summary: WatchDiffSummary }) {
165
166
  </Text>
166
167
  ) : null
167
168
  })}
168
- {row.stat.added + row.stat.updated + row.stat.deleted + row.stat.initialized === 0 && (
169
+ {totalResourceCount(row.stat) === 0 && (
169
170
  <Text fontSize="11px" color="gray.600" fontFamily="mono">none</Text>
170
171
  )}
171
172
  </HStack>
@@ -408,10 +409,17 @@ export default function WorkspacePanel() {
408
409
  }, [activeDiffLocationIndex, navigableDiffLocations, navigateToDiffLocation])
409
410
 
410
411
  const activeVersion = preview?.version ?? selectedVersion
412
+
413
+ const navigateToDiffMap = useCallback(() => {
414
+ const targetVersion = activeVersion ?? selectedVersion
415
+ if (!targetVersion) return
416
+ navigate(`/views?view=explore&diffVersion=${targetVersion.id}`)
417
+ }, [activeVersion, navigate, selectedVersion])
418
+
411
419
  const diffSummary = useMemo(() => summarizeWatchDiffs(diffs), [diffs])
412
- const totalFileChanges = diffSummary.files.added + diffSummary.files.updated + diffSummary.files.deleted + diffSummary.files.initialized
413
- const totalTldChanges = diffSummary.elements.added + diffSummary.elements.updated + diffSummary.elements.deleted + diffSummary.elements.initialized +
414
- diffSummary.connectors.added + diffSummary.connectors.updated + diffSummary.connectors.deleted + diffSummary.connectors.initialized
420
+ const totalFileChanges = totalResourceCount(diffSummary.files)
421
+ const diagramResourceSummary = formatDiagramResourceSummary(diffSummary)
422
+ const hasDiffMapTargets = useMemo(() => diffs.some((diff) => isWatchDiffChange(diff.change_type)), [diffs])
415
423
  const activeDiffLocation = activeDiffLocationIndex >= 0 ? navigableDiffLocations[activeDiffLocationIndex] : null
416
424
  const headerAddedLines = activeDiffLocation?.addedLines ?? diffSummary.elements.addedLines + diffSummary.connectors.addedLines
417
425
  const headerRemovedLines = activeDiffLocation?.removedLines ?? diffSummary.elements.removedLines + diffSummary.connectors.removedLines
@@ -716,10 +724,28 @@ export default function WorkspacePanel() {
716
724
  <Text fontSize="12px" color="gray.400" fontWeight="500" noOfLines={1} flex={1}>
717
725
  {activeDiffLocation
718
726
  ? `${activeDiffLocationIndex + 1} of ${navigableDiffLocations.length}: ${activeDiffLocation.label}`
719
- : `${totalTldChanges} changed elements`}
727
+ : diagramResourceSummary}
720
728
  </Text>
721
729
  </HStack>
722
730
  <HStack spacing={1} flexShrink={0}>
731
+ <Tooltip label="Open diff map" placement="top">
732
+ <Button
733
+ size="sm"
734
+ h="32px"
735
+ px={3}
736
+ variant="solid"
737
+ bg="whiteAlpha.200"
738
+ _hover={{ bg: 'whiteAlpha.300' }}
739
+ _active={{ bg: 'whiteAlpha.400' }}
740
+ leftIcon={<ViewIcon boxSize={3.5} />}
741
+ fontSize="12px"
742
+ fontWeight="600"
743
+ isDisabled={!activeVersion || !hasDiffMapTargets}
744
+ onClick={navigateToDiffMap}
745
+ >
746
+ Diff map
747
+ </Button>
748
+ </Tooltip>
723
749
  <Tooltip label="Previous element" placement="top">
724
750
  <IconButton
725
751
  aria-label="Previous"
@@ -26,6 +26,7 @@ import {
26
26
  import { Link as RouterLink } from 'react-router-dom'
27
27
  import { ExternalLinkIcon } from '@chakra-ui/icons'
28
28
  import type { ExploreData } from '../../types'
29
+ import { api } from '../../api/client'
29
30
  import { computeLayout } from './layout'
30
31
  import { renderFrame, getExpandThresholds, getCameraRebase, rawCameraView, screenToWorldX, screenToWorldY, worldToScreenX, worldToScreenY, setOnImageLoadCallback, setHighlightedTags as setRendererHighlightedTags, setHiddenTags as setRendererHiddenTags, setHighlightColor as setRendererHighlightColor, setVersionDiff as setRendererVersionDiff } from './renderer'
31
32
  import { useZUIInteraction } from './useZUIInteraction'
@@ -35,6 +36,9 @@ import { buildWorkspaceGraphSnapshot } from '../../crossBranch/graph'
35
36
  import type { CrossBranchContextSettings } from '../../crossBranch/types'
36
37
  import { DEFAULT_MIN_CONNECTOR_ANCHOR_ALPHA } from '../../crossBranch/settings'
37
38
  import type { WorkspaceVersionFollowTarget, WorkspaceVersionPreview } from '../../context/WorkspaceVersionContext'
39
+ import { diffResourceKey, type ExploreDiffDetail, type ExploreDiffLens } from '../../utils/exploreDiffLens'
40
+ import { getSourceEditor } from '../../utils/sourceEditor'
41
+ import { toast } from '../../utils/toast'
38
42
  import {
39
43
  buildProxyConnectorSpatialIndex,
40
44
  buildVisibleProxyConnectors,
@@ -71,6 +75,7 @@ interface Props {
71
75
  hiddenTags?: string[]
72
76
  versionPreview?: WorkspaceVersionPreview | null
73
77
  versionFollowTarget?: WorkspaceVersionFollowTarget | null
78
+ diffLens?: ExploreDiffLens | null
74
79
  crossBranchSettings: CrossBranchContextSettings
75
80
  hoverLocked?: boolean
76
81
  }
@@ -244,6 +249,60 @@ function fitWorldRect(
244
249
  }
245
250
  }
246
251
 
252
+ function diffColorScheme(change: string | undefined): 'green' | 'red' | 'yellow' | 'blue' {
253
+ if (change === 'added') return 'green'
254
+ if (change === 'deleted') return 'red'
255
+ if (change === 'initialized') return 'blue'
256
+ return 'yellow'
257
+ }
258
+
259
+ function DiffDetailBlock({
260
+ detail,
261
+ onOpenSource,
262
+ }: {
263
+ detail: ExploreDiffDetail | null
264
+ onOpenSource: (detail: ExploreDiffDetail) => void
265
+ }) {
266
+ if (!detail) return null
267
+ const hasLines = detail.addedLines > 0 || detail.removedLines > 0
268
+ return (
269
+ <VStack align="stretch" spacing={2} mb={3}>
270
+ <HStack spacing={2} minW={0}>
271
+ <Badge colorScheme={diffColorScheme(detail.changeType)} variant="subtle" fontSize="2xs">
272
+ {detail.changeType}
273
+ </Badge>
274
+ {hasLines && (
275
+ <HStack spacing={1.5} fontSize="xs" fontFamily="mono">
276
+ {detail.addedLines > 0 && <Text color="green.300">+{detail.addedLines}</Text>}
277
+ {detail.removedLines > 0 && <Text color="red.300">-{detail.removedLines}</Text>}
278
+ </HStack>
279
+ )}
280
+ </HStack>
281
+ {detail.summary && (
282
+ <Text fontSize="xs" color="gray.200" noOfLines={3}>{detail.summary}</Text>
283
+ )}
284
+ {detail.sourcePath && (
285
+ <Text fontSize="11px" color="gray.500" fontFamily="mono" noOfLines={2}>{detail.sourcePath}</Text>
286
+ )}
287
+ {detail.sourcePath && (
288
+ <Button
289
+ size="xs"
290
+ variant="outline"
291
+ colorScheme="blue"
292
+ alignSelf="flex-start"
293
+ onClick={(event) => {
294
+ event.stopPropagation()
295
+ onOpenSource(detail)
296
+ }}
297
+ >
298
+ Open Source
299
+ </Button>
300
+ )}
301
+ <Divider borderColor="whiteAlpha.200" />
302
+ </VStack>
303
+ )
304
+ }
305
+
247
306
  function findFirstExpandableNode(groups: DiagramGroupLayout[]): PathItem | null {
248
307
  for (const group of groups) {
249
308
  const found = findFirstExpandableNodeInTree(group.nodes, 0, 0, 1, 0, 0)
@@ -292,7 +351,7 @@ function findFirstExpandableNodeInTree(
292
351
  return null
293
352
  }
294
353
 
295
- export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({ data, onReady, onZoom, onPan, initialCameraFrame, highlightedTags, highlightColor, hiddenTags, versionPreview, versionFollowTarget, crossBranchSettings, hoverLocked = false }, ref) {
354
+ export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({ data, onReady, onZoom, onPan, initialCameraFrame, highlightedTags, highlightColor, hiddenTags, versionPreview, versionFollowTarget, diffLens, crossBranchSettings, hoverLocked = false }, ref) {
296
355
  const canvasRef = useRef<HTMLCanvasElement>(null)
297
356
  const containerRef = useRef<HTMLDivElement>(null)
298
357
  const cameraTransitionRef = useRef<number | null>(null)
@@ -466,6 +525,34 @@ export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({
466
525
  )
467
526
  }, [hoveredScreenRect, containerSize])
468
527
 
528
+ const hoveredDiffDetail = useMemo(() => {
529
+ if (!hoveredItem || !diffLens) return null
530
+ if (hoveredItem.type === 'node') {
531
+ return diffLens.diffDetailsByResource.get(diffResourceKey('element', hoveredItem.data.elementId)) ?? null
532
+ }
533
+ if (hoveredItem.type === 'edge' && !hoveredItem.data.isProxy) {
534
+ return hoveredItem.data.id
535
+ ? diffLens.diffDetailsByResource.get(diffResourceKey('connector', hoveredItem.data.id)) ?? null
536
+ : null
537
+ }
538
+ return null
539
+ }, [diffLens, hoveredItem])
540
+
541
+ const handleOpenSource = useCallback((detail: ExploreDiffDetail) => {
542
+ if (!detail.sourcePath) return
543
+ api.editor.open({
544
+ editor: getSourceEditor(),
545
+ file_path: detail.sourcePath,
546
+ line: detail.line ?? null,
547
+ }).catch((error: unknown) => {
548
+ toast({
549
+ title: 'Could not open source',
550
+ description: error instanceof Error ? error.message : 'The source editor command failed.',
551
+ status: 'error',
552
+ })
553
+ })
554
+ }, [])
555
+
469
556
  // Debounce breadcrumb computation so getPathAt doesn't run on every scroll tick
470
557
  const [breadcrumbView, setBreadcrumbView] = useState(viewState)
471
558
  const breadcrumbTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
@@ -820,6 +907,18 @@ export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({
820
907
  }, [hiddenTags])
821
908
 
822
909
  useEffect(() => {
910
+ if (diffLens) {
911
+ setRendererVersionDiff(
912
+ diffLens.elementChanges,
913
+ diffLens.connectorChanges,
914
+ diffLens.elementLineDeltas,
915
+ diffLens.contextElementIds,
916
+ diffLens.contextConnectorIds,
917
+ true,
918
+ )
919
+ needsRedrawRef.current = true
920
+ return
921
+ }
823
922
  const pulsedElementChanges = new Map<number, string>()
824
923
  const pulsedElementLineDeltas = new Map<number, { added: number; removed: number }>()
825
924
  if (versionFollowTarget?.resourceType === 'element' && versionFollowTarget.resourceId) {
@@ -832,7 +931,7 @@ export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({
832
931
  versionPreview?.elementLineDeltas ?? pulsedElementLineDeltas,
833
932
  )
834
933
  needsRedrawRef.current = true
835
- }, [versionPreview, versionFollowTarget])
934
+ }, [diffLens, versionPreview, versionFollowTarget])
836
935
 
837
936
  useEffect(() => {
838
937
  if (!initialized || !versionFollowTarget?.viewId) return
@@ -979,6 +1078,7 @@ export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({
979
1078
  </PopoverHeader>
980
1079
  <PopoverBody px={4} py={3}>
981
1080
  <VStack align="start" spacing={3}>
1081
+ <DiffDetailBlock detail={hoveredDiffDetail} onOpenSource={handleOpenSource} />
982
1082
  {hoveredItem.data.technology && (
983
1083
  <Box>
984
1084
  <Text color="gray.400" fontSize="xs" fontWeight="600" mb={0.5} letterSpacing="wider">TECHNOLOGY</Text>
@@ -1033,6 +1133,7 @@ export const ZUICanvas = forwardRef<ZUICanvasHandle, Props>(function ZUICanvas({
1033
1133
  </PopoverHeader>
1034
1134
  <PopoverBody px={4} py={3}>
1035
1135
  <VStack align="start" spacing={3}>
1136
+ <DiffDetailBlock detail={hoveredDiffDetail} onOpenSource={handleOpenSource} />
1036
1137
  <VStack align="start" spacing={1}>
1037
1138
  <Text color="gray.400" fontSize="2xs" fontWeight="600" letterSpacing="wider">BETWEEN</Text>
1038
1139
  <Text fontSize="xs" color="gray.200">
@@ -139,14 +139,23 @@ export function setHiddenTags(tags: Set<string>): void {
139
139
  let currentVersionElementChanges: Map<number, string> = new Map()
140
140
  let currentVersionConnectorChanges: Map<number, string> = new Map()
141
141
  let currentVersionElementLineDeltas: Map<number, { added: number; removed: number }> = new Map()
142
+ let currentDiffContextElementIds: Set<number> = new Set()
143
+ let currentDiffContextConnectorIds: Set<number> = new Set()
144
+ let currentDiffLensActive = false
142
145
  export function setVersionDiff(
143
146
  elementChanges: Map<number, string>,
144
147
  connectorChanges: Map<number, string>,
145
148
  elementLineDeltas: Map<number, { added: number; removed: number }> = new Map(),
149
+ contextElementIds: Set<number> = new Set(),
150
+ contextConnectorIds: Set<number> = new Set(),
151
+ diffLensActive = false,
146
152
  ): void {
147
153
  currentVersionElementChanges = elementChanges
148
154
  currentVersionConnectorChanges = connectorChanges
149
155
  currentVersionElementLineDeltas = elementLineDeltas
156
+ currentDiffContextElementIds = contextElementIds
157
+ currentDiffContextConnectorIds = contextConnectorIds
158
+ currentDiffLensActive = diffLensActive
150
159
  }
151
160
 
152
161
  /**
@@ -1078,10 +1087,20 @@ function drawNode(
1078
1087
  const change = currentVersionElementChanges.get(node.elementId)
1079
1088
  if (!change) {
1080
1089
  ctx.save()
1081
- ctx.globalAlpha = parentAlpha * 0.9
1090
+ const isContext = currentDiffLensActive && currentDiffContextElementIds.has(node.elementId)
1091
+ ctx.globalAlpha = parentAlpha * (isContext ? 0.45 : 0.9)
1082
1092
  ctx.fillStyle = canvasBg
1083
1093
  traceShape()
1084
1094
  ctx.fill()
1095
+ if (isContext && drawScreenW > 40) {
1096
+ ctx.globalAlpha = parentAlpha * 0.55
1097
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.18)'
1098
+ ctx.lineWidth = 1.5 / drawZoom
1099
+ ctx.setLineDash([4 / drawZoom, 4 / drawZoom])
1100
+ traceShape()
1101
+ ctx.stroke()
1102
+ ctx.setLineDash([])
1103
+ }
1085
1104
  ctx.restore()
1086
1105
  } else {
1087
1106
  const color = change === 'added' ? '#68d391' : change === 'deleted' ? '#fc8181' : '#f6e05e'
@@ -1286,9 +1305,26 @@ function drawEdges(
1286
1305
  ctx.save()
1287
1306
  const edgeChange = currentVersionConnectorChanges.get(edge.id)
1288
1307
  const versionPreviewActive = currentVersionElementChanges.size > 0 || currentVersionConnectorChanges.size > 0
1289
- ctx.globalAlpha = versionPreviewActive && !edgeChange ? Math.max(alpha * 0.18, 0.08) : connectorAlpha(alpha)
1290
- ctx.strokeStyle = accent
1291
- ctx.lineWidth = CONNECTOR_LINE_PX / zoom
1308
+ const edgeContext = currentDiffLensActive && (
1309
+ currentDiffContextConnectorIds.has(edge.id) ||
1310
+ currentDiffContextElementIds.has(node.elementId) ||
1311
+ currentDiffContextElementIds.has(target.elementId) ||
1312
+ currentVersionElementChanges.has(node.elementId) ||
1313
+ currentVersionElementChanges.has(target.elementId)
1314
+ )
1315
+ ctx.globalAlpha = versionPreviewActive && !edgeChange
1316
+ ? edgeContext
1317
+ ? Math.max(alpha * 0.28, 0.12)
1318
+ : Math.max(alpha * 0.08, 0.04)
1319
+ : connectorAlpha(alpha)
1320
+ ctx.strokeStyle = edgeChange === 'added'
1321
+ ? '#68d391'
1322
+ : edgeChange === 'deleted'
1323
+ ? '#fc8181'
1324
+ : edgeChange
1325
+ ? '#f6e05e'
1326
+ : accent
1327
+ ctx.lineWidth = (edgeChange ? CONNECTOR_LINE_PX * 1.35 : CONNECTOR_LINE_PX) / zoom
1292
1328
 
1293
1329
  let midX = (sH.x + tH.x) / 2
1294
1330
  let midY = (sH.y + tH.y) / 2
@@ -139,6 +139,7 @@ export type HoveredItem =
139
139
  | {
140
140
  type: 'edge';
141
141
  data: {
142
+ id?: number;
142
143
  sourceId: string;
143
144
  targetId: string;
144
145
  label: string;
@@ -205,6 +205,7 @@ type IndexedEdge =
205
205
  sourceLabel: string
206
206
  targetLabel: string
207
207
  label: string
208
+ id: number
208
209
  diagramId: number
209
210
  sourceObjId: number
210
211
  targetObjId: number
@@ -410,6 +411,7 @@ function buildEdgeSpatialIndex(groups: DiagramGroupLayout[]): EdgeSpatialIndex {
410
411
  sourceLabel: source.label,
411
412
  targetLabel: target.label,
412
413
  label: edge.label || 'Connection',
414
+ id: edge.id,
413
415
  diagramId: group.diagramId,
414
416
  sourceObjId: source.elementId,
415
417
  targetObjId: target.elementId,
@@ -495,6 +497,7 @@ function findHoveredEdge(
495
497
  sourceId: bestEdge.sourceLabel,
496
498
  targetId: bestEdge.targetLabel,
497
499
  label: bestEdge.label,
500
+ id: bestEdge.id,
498
501
  diagramId: bestEdge.diagramId,
499
502
  sourceObjId: bestEdge.sourceObjId,
500
503
  targetObjId: bestEdge.targetObjId
@@ -1,6 +1,6 @@
1
1
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
2
2
  import type { WatchDiff, WatchRepository, WatchVersion, WorkspaceVersion } from '../api/client'
3
- import { normalizeWatchChangeType } from '../utils/watchDiffSummary'
3
+ import { isWatchDiffChange, normalizeWatchChangeType } from '../utils/watchDiffSummary'
4
4
 
5
5
  export type VersionChangeType = 'added' | 'updated' | 'deleted' | 'initialized'
6
6
 
@@ -62,16 +62,16 @@ export function buildWorkspaceVersionPreview(args: {
62
62
  const change = normalizeWatchChangeType(diff.change_type)
63
63
  summary[change] += 1
64
64
  if (diff.resource_type === 'element' && diff.resource_id) {
65
- elementChanges.set(diff.resource_id, change)
65
+ if (isWatchDiffChange(diff.change_type)) elementChanges.set(diff.resource_id, change)
66
66
  const added = Math.max(0, diff.added_lines ?? 0)
67
67
  const removed = Math.max(0, diff.removed_lines ?? 0)
68
- if (added > 0 || removed > 0) {
68
+ if (isWatchDiffChange(diff.change_type) && (added > 0 || removed > 0)) {
69
69
  elementLineDeltas.set(diff.resource_id, { added, removed })
70
70
  }
71
71
  summary.elements += 1
72
72
  }
73
73
  if (diff.resource_type === 'connector' && diff.resource_id) {
74
- connectorChanges.set(diff.resource_id, change)
74
+ if (isWatchDiffChange(diff.change_type)) connectorChanges.set(diff.resource_id, change)
75
75
  summary.connectors += 1
76
76
  }
77
77
  })