@tldiagram/core-ui 1.91.0 → 1.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +13 -1
- package/dist/components/ElementNode.d.ts +9 -0
- package/dist/config/runtime-vscode.d.ts +1 -0
- package/dist/config/runtime.d.ts +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +10063 -9512
- package/dist/pages/ViewEditor/hooks/useCanvasInteractions.d.ts +2 -1
- package/dist/pages/ViewEditor/hooks/useViewData.d.ts +20 -21
- package/dist/shims/empty-node-module.d.ts +2 -0
- package/dist/store/useStore.d.ts +78 -0
- package/dist/store/useStore.test.d.ts +1 -0
- package/package.json +7 -4
- package/src/App.tsx +0 -4
- package/src/api/client.ts +39 -1
- package/src/components/ElementNode.tsx +11 -58
- package/src/components/ElementPanel.tsx +2 -2
- package/src/components/LayoutSection.tsx +68 -93
- package/src/components/ViewGridNode.tsx +1 -4
- package/src/components/ZUI/renderer.ts +166 -66
- package/src/components/ZUI/useZUIInteraction.ts +235 -81
- package/src/config/runtime-vscode.ts +6 -0
- package/src/config/runtime.ts +4 -0
- package/src/index.ts +0 -1
- package/src/main.tsx +26 -14
- package/src/pages/ViewEditor/context.tsx +12 -4
- package/src/pages/ViewEditor/hooks/useCanvasInteractions.ts +172 -121
- package/src/pages/ViewEditor/hooks/useViewData.ts +455 -253
- package/src/pages/ViewEditor/index.tsx +45 -32
- package/src/shims/empty-node-module.ts +1 -0
- package/src/store/useStore.test.ts +272 -0
- package/src/store/useStore.ts +285 -0
- package/dist/demo/DemoPage.d.ts +0 -9
- package/dist/demo/seed.d.ts +0 -9
- package/dist/demo/store.d.ts +0 -137
- package/src/demo/DemoPage.tsx +0 -184
- package/src/demo/seed.ts +0 -67
- package/src/demo/store.ts +0 -536
|
@@ -30,6 +30,54 @@ interface DeepestNodeResult {
|
|
|
30
30
|
cumulativeScale: number
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
interface NodeSpatialIndex {
|
|
34
|
+
cellSize: number
|
|
35
|
+
cells: Map<string, LayoutNode[]>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const NODE_INDEX_CELL_SIZE = 320
|
|
39
|
+
const nodeSpatialIndexCache = new WeakMap<LayoutNode[], NodeSpatialIndex>()
|
|
40
|
+
|
|
41
|
+
function getNodeSpatialIndex(nodes: LayoutNode[]): NodeSpatialIndex {
|
|
42
|
+
const cached = nodeSpatialIndexCache.get(nodes)
|
|
43
|
+
if (cached) return cached
|
|
44
|
+
|
|
45
|
+
const index: NodeSpatialIndex = { cellSize: NODE_INDEX_CELL_SIZE, cells: new Map() }
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
const startX = Math.floor(node.worldX / index.cellSize)
|
|
48
|
+
const endX = Math.floor((node.worldX + node.worldW) / index.cellSize)
|
|
49
|
+
const startY = Math.floor(node.worldY / index.cellSize)
|
|
50
|
+
const endY = Math.floor((node.worldY + node.worldH) / index.cellSize)
|
|
51
|
+
|
|
52
|
+
for (let cx = startX; cx <= endX; cx++) {
|
|
53
|
+
for (let cy = startY; cy <= endY; cy++) {
|
|
54
|
+
const key = cellKey(cx, cy)
|
|
55
|
+
let bucket = index.cells.get(key)
|
|
56
|
+
if (!bucket) {
|
|
57
|
+
bucket = []
|
|
58
|
+
index.cells.set(key, bucket)
|
|
59
|
+
}
|
|
60
|
+
bucket.push(node)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
nodeSpatialIndexCache.set(nodes, index)
|
|
66
|
+
return index
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getNodesAtPoint(nodes: LayoutNode[], worldX: number, worldY: number): LayoutNode[] {
|
|
70
|
+
const index = getNodeSpatialIndex(nodes)
|
|
71
|
+
return index.cells.get(cellKey(Math.floor(worldX / index.cellSize), Math.floor(worldY / index.cellSize))) ?? []
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function warmNodeSpatialIndexes(nodes: LayoutNode[]): void {
|
|
75
|
+
getNodeSpatialIndex(nodes)
|
|
76
|
+
for (const node of nodes) {
|
|
77
|
+
if (node.children.length > 0) warmNodeSpatialIndexes(node.children)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
33
81
|
function findDeepestAt(worldX: number, worldY: number, groups: DiagramGroupLayout[], view: ZUIViewState, thresholds: { start: number, end: number }): DeepestNodeResult | null {
|
|
34
82
|
for (const group of groups) {
|
|
35
83
|
if (worldX >= group.worldX && worldX <= group.worldX + group.worldW &&
|
|
@@ -53,7 +101,8 @@ function findDeepestInNodes(
|
|
|
53
101
|
view: ZUIViewState,
|
|
54
102
|
thresholds: { start: number, end: number }
|
|
55
103
|
): DeepestNodeResult | null {
|
|
56
|
-
|
|
104
|
+
const candidates = getNodesAtPoint(nodes, worldX, worldY)
|
|
105
|
+
for (const node of candidates) {
|
|
57
106
|
if (worldX >= node.worldX && worldX <= node.worldX + node.worldW &&
|
|
58
107
|
worldY >= node.worldY && worldY <= node.worldY + node.worldH) {
|
|
59
108
|
|
|
@@ -120,13 +169,72 @@ function findHoveredGroup(worldX: number, worldY: number, groups: DiagramGroupLa
|
|
|
120
169
|
return null
|
|
121
170
|
}
|
|
122
171
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
172
|
+
type IndexedEdge =
|
|
173
|
+
| {
|
|
174
|
+
kind: 'edge'
|
|
175
|
+
x1: number
|
|
176
|
+
y1: number
|
|
177
|
+
x2: number
|
|
178
|
+
y2: number
|
|
179
|
+
midX: number
|
|
180
|
+
midY: number
|
|
181
|
+
sourceLabel: string
|
|
182
|
+
targetLabel: string
|
|
183
|
+
label: string
|
|
184
|
+
diagramId: number
|
|
185
|
+
sourceObjId: number
|
|
186
|
+
targetObjId: number
|
|
187
|
+
}
|
|
188
|
+
| {
|
|
189
|
+
kind: 'portal'
|
|
190
|
+
x1: number
|
|
191
|
+
y1: number
|
|
192
|
+
x2: number
|
|
193
|
+
y2: number
|
|
194
|
+
midX: number
|
|
195
|
+
midY: number
|
|
196
|
+
sourceLabel: string
|
|
197
|
+
targetLabel: string
|
|
198
|
+
diagramId: number
|
|
199
|
+
targetDiagId?: number
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
interface EdgeSpatialIndex {
|
|
203
|
+
cellSize: number
|
|
204
|
+
cells: Map<string, IndexedEdge[]>
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const EDGE_INDEX_CELL_SIZE = 360
|
|
208
|
+
|
|
209
|
+
function cellKey(cx: number, cy: number): string {
|
|
210
|
+
return `${cx},${cy}`
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function addEdgeToSpatialIndex(index: EdgeSpatialIndex, edge: IndexedEdge): void {
|
|
214
|
+
const minX = Math.min(edge.x1, edge.x2)
|
|
215
|
+
const maxX = Math.max(edge.x1, edge.x2)
|
|
216
|
+
const minY = Math.min(edge.y1, edge.y2)
|
|
217
|
+
const maxY = Math.max(edge.y1, edge.y2)
|
|
218
|
+
const startX = Math.floor(minX / index.cellSize)
|
|
219
|
+
const endX = Math.floor(maxX / index.cellSize)
|
|
220
|
+
const startY = Math.floor(minY / index.cellSize)
|
|
221
|
+
const endY = Math.floor(maxY / index.cellSize)
|
|
222
|
+
|
|
223
|
+
for (let cx = startX; cx <= endX; cx++) {
|
|
224
|
+
for (let cy = startY; cy <= endY; cy++) {
|
|
225
|
+
const key = cellKey(cx, cy)
|
|
226
|
+
let bucket = index.cells.get(key)
|
|
227
|
+
if (!bucket) {
|
|
228
|
+
bucket = []
|
|
229
|
+
index.cells.set(key, bucket)
|
|
230
|
+
}
|
|
231
|
+
bucket.push(edge)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function buildEdgeSpatialIndex(groups: DiagramGroupLayout[]): EdgeSpatialIndex {
|
|
237
|
+
const index: EdgeSpatialIndex = { cellSize: EDGE_INDEX_CELL_SIZE, cells: new Map() }
|
|
130
238
|
|
|
131
239
|
for (const group of groups) {
|
|
132
240
|
const nodeMap = new Map<string, LayoutNode>()
|
|
@@ -139,89 +247,122 @@ function findHoveredEdge(
|
|
|
139
247
|
const target = nodeMap.get(edge.targetId)
|
|
140
248
|
if (!source || !target) continue
|
|
141
249
|
|
|
142
|
-
// Node centers
|
|
143
250
|
const x1 = source.worldX + source.worldW / 2
|
|
144
251
|
const y1 = source.worldY + source.worldH / 2
|
|
145
252
|
const x2 = target.worldX + target.worldW / 2
|
|
146
253
|
const y2 = target.worldY + target.worldH / 2
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const nearestY = y1 + t * dy
|
|
163
|
-
const dist = Math.sqrt((worldX - nearestX) ** 2 + (worldY - nearestY) ** 2)
|
|
164
|
-
|
|
165
|
-
if (dist < threshold) {
|
|
166
|
-
return {
|
|
167
|
-
type: 'edge',
|
|
168
|
-
data: {
|
|
169
|
-
sourceId: source.label,
|
|
170
|
-
targetId: target.label,
|
|
171
|
-
label: edge.label || 'Connection',
|
|
172
|
-
diagramId: group.diagramId,
|
|
173
|
-
sourceObjId: source.elementId,
|
|
174
|
-
targetObjId: target.elementId
|
|
175
|
-
},
|
|
176
|
-
absX: midX,
|
|
177
|
-
absY: midY
|
|
178
|
-
}
|
|
179
|
-
}
|
|
254
|
+
addEdgeToSpatialIndex(index, {
|
|
255
|
+
kind: 'edge',
|
|
256
|
+
x1,
|
|
257
|
+
y1,
|
|
258
|
+
x2,
|
|
259
|
+
y2,
|
|
260
|
+
midX: (x1 + x2) / 2,
|
|
261
|
+
midY: (y1 + y2) / 2,
|
|
262
|
+
sourceLabel: source.label,
|
|
263
|
+
targetLabel: target.label,
|
|
264
|
+
label: edge.label || 'Connection',
|
|
265
|
+
diagramId: group.diagramId,
|
|
266
|
+
sourceObjId: source.elementId,
|
|
267
|
+
targetObjId: target.elementId,
|
|
268
|
+
})
|
|
180
269
|
}
|
|
181
270
|
|
|
182
|
-
// ── Squiggly lines to portal nodes ──
|
|
183
271
|
for (const node of group.nodes) {
|
|
184
|
-
if (node.isPortal)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
272
|
+
if (!node.isPortal) continue
|
|
273
|
+
const x1 = group.worldX + group.diagramX + group.diagramW / 2
|
|
274
|
+
const y1 = group.worldY + group.diagramY + group.diagramH
|
|
275
|
+
const x2 = node.worldX + node.worldW / 2
|
|
276
|
+
const y2 = node.worldY
|
|
277
|
+
addEdgeToSpatialIndex(index, {
|
|
278
|
+
kind: 'portal',
|
|
279
|
+
x1,
|
|
280
|
+
y1,
|
|
281
|
+
x2,
|
|
282
|
+
y2,
|
|
283
|
+
midX: (x1 + x2) / 2,
|
|
284
|
+
midY: (y1 + y2) / 2,
|
|
285
|
+
sourceLabel: group.label,
|
|
286
|
+
targetLabel: node.label,
|
|
287
|
+
diagramId: group.diagramId,
|
|
288
|
+
targetDiagId: node.linkedDiagramId,
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return index
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function findHoveredEdge(
|
|
297
|
+
worldX: number,
|
|
298
|
+
worldY: number,
|
|
299
|
+
index: EdgeSpatialIndex,
|
|
300
|
+
view: ZUIViewState
|
|
301
|
+
): HoveredItem | null {
|
|
302
|
+
const threshold = 18 / view.zoom // 18 screen pixels converted to world distance
|
|
303
|
+
const startX = Math.floor((worldX - threshold) / index.cellSize)
|
|
304
|
+
const endX = Math.floor((worldX + threshold) / index.cellSize)
|
|
305
|
+
const startY = Math.floor((worldY - threshold) / index.cellSize)
|
|
306
|
+
const endY = Math.floor((worldY + threshold) / index.cellSize)
|
|
307
|
+
const thresholdSquared = threshold * threshold
|
|
308
|
+
let bestEdge: IndexedEdge | null = null
|
|
309
|
+
let bestDistSquared = thresholdSquared
|
|
310
|
+
|
|
311
|
+
for (let cx = startX; cx <= endX; cx++) {
|
|
312
|
+
for (let cy = startY; cy <= endY; cy++) {
|
|
313
|
+
const bucket = index.cells.get(cellKey(cx, cy))
|
|
314
|
+
if (!bucket) continue
|
|
315
|
+
|
|
316
|
+
for (const edge of bucket) {
|
|
317
|
+
const dx = edge.x2 - edge.x1
|
|
318
|
+
const dy = edge.y2 - edge.y1
|
|
196
319
|
const l2 = dx * dx + dy * dy
|
|
197
320
|
if (l2 === 0) continue
|
|
198
321
|
|
|
199
|
-
let t = ((worldX - x1) * dx + (worldY - y1) * dy) / l2
|
|
322
|
+
let t = ((worldX - edge.x1) * dx + (worldY - edge.y1) * dy) / l2
|
|
200
323
|
t = Math.max(0, Math.min(1, t))
|
|
201
324
|
|
|
202
|
-
const nearestX = x1 + t * dx
|
|
203
|
-
const nearestY = y1 + t * dy
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type: 'edge',
|
|
209
|
-
data: {
|
|
210
|
-
sourceId: group.label,
|
|
211
|
-
targetId: node.label,
|
|
212
|
-
label: '',
|
|
213
|
-
diagramId: group.diagramId,
|
|
214
|
-
targetDiagId: node.linkedDiagramId,
|
|
215
|
-
isPortalConn: true
|
|
216
|
-
},
|
|
217
|
-
absX: midX,
|
|
218
|
-
absY: midY
|
|
219
|
-
}
|
|
325
|
+
const nearestX = edge.x1 + t * dx
|
|
326
|
+
const nearestY = edge.y1 + t * dy
|
|
327
|
+
const distSquared = (worldX - nearestX) ** 2 + (worldY - nearestY) ** 2
|
|
328
|
+
if (distSquared < bestDistSquared) {
|
|
329
|
+
bestDistSquared = distSquared
|
|
330
|
+
bestEdge = edge
|
|
220
331
|
}
|
|
221
332
|
}
|
|
222
333
|
}
|
|
223
334
|
}
|
|
224
|
-
|
|
335
|
+
|
|
336
|
+
if (!bestEdge) return null
|
|
337
|
+
if (bestEdge.kind === 'portal') {
|
|
338
|
+
return {
|
|
339
|
+
type: 'edge',
|
|
340
|
+
data: {
|
|
341
|
+
sourceId: bestEdge.sourceLabel,
|
|
342
|
+
targetId: bestEdge.targetLabel,
|
|
343
|
+
label: '',
|
|
344
|
+
diagramId: bestEdge.diagramId,
|
|
345
|
+
targetDiagId: bestEdge.targetDiagId,
|
|
346
|
+
isPortalConn: true
|
|
347
|
+
},
|
|
348
|
+
absX: bestEdge.midX,
|
|
349
|
+
absY: bestEdge.midY
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
type: 'edge',
|
|
355
|
+
data: {
|
|
356
|
+
sourceId: bestEdge.sourceLabel,
|
|
357
|
+
targetId: bestEdge.targetLabel,
|
|
358
|
+
label: bestEdge.label,
|
|
359
|
+
diagramId: bestEdge.diagramId,
|
|
360
|
+
sourceObjId: bestEdge.sourceObjId,
|
|
361
|
+
targetObjId: bestEdge.targetObjId
|
|
362
|
+
},
|
|
363
|
+
absX: bestEdge.midX,
|
|
364
|
+
absY: bestEdge.midY
|
|
365
|
+
}
|
|
225
366
|
}
|
|
226
367
|
|
|
227
368
|
export function calculateMaxZoom(groups: DiagramGroupLayout[], canvasW: number): number {
|
|
@@ -350,12 +491,20 @@ export function useZUIInteraction(
|
|
|
350
491
|
// ── Refs for stable event handlers ──────────────────────────────
|
|
351
492
|
const viewStateRef = useRef<ZUIViewState>(initialView)
|
|
352
493
|
const groupsRef = useRef<DiagramGroupLayout[]>(groups)
|
|
494
|
+
const edgeSpatialIndexRef = useRef<EdgeSpatialIndex | null>(null)
|
|
495
|
+
if (edgeSpatialIndexRef.current === null) {
|
|
496
|
+
edgeSpatialIndexRef.current = buildEdgeSpatialIndex(groups)
|
|
497
|
+
}
|
|
353
498
|
const bboxRef = useRef<BBox | undefined>(bbox)
|
|
354
499
|
const onZoomRef = useRef(onZoom)
|
|
355
500
|
const onPanRef = useRef(onPan)
|
|
356
501
|
|
|
357
502
|
useEffect(() => {
|
|
358
503
|
groupsRef.current = groups
|
|
504
|
+
edgeSpatialIndexRef.current = buildEdgeSpatialIndex(groups)
|
|
505
|
+
for (const group of groups) {
|
|
506
|
+
warmNodeSpatialIndexes(group.nodes)
|
|
507
|
+
}
|
|
359
508
|
bboxRef.current = bbox
|
|
360
509
|
onZoomRef.current = onZoom
|
|
361
510
|
onPanRef.current = onPan
|
|
@@ -503,7 +652,8 @@ export function useZUIInteraction(
|
|
|
503
652
|
function onMouseDown(e: MouseEvent) {
|
|
504
653
|
if (e.button !== 0) return
|
|
505
654
|
dragging.current = true
|
|
506
|
-
lastMouse.current =
|
|
655
|
+
lastMouse.current.x = e.clientX
|
|
656
|
+
lastMouse.current.y = e.clientY
|
|
507
657
|
el!.style.cursor = 'grabbing'
|
|
508
658
|
setHoveredItem(null, true) // Hide popover immediately while dragging
|
|
509
659
|
}
|
|
@@ -518,7 +668,8 @@ export function useZUIInteraction(
|
|
|
518
668
|
if (dragging.current) {
|
|
519
669
|
const dx = e.clientX - lastMouse.current.x
|
|
520
670
|
const dy = e.clientY - lastMouse.current.y
|
|
521
|
-
lastMouse.current =
|
|
671
|
+
lastMouse.current.x = e.clientX
|
|
672
|
+
lastMouse.current.y = e.clientY
|
|
522
673
|
setViewState((prev) => ({ ...prev, x: prev.x + dx, y: prev.y + dy }))
|
|
523
674
|
onPanRef.current?.()
|
|
524
675
|
return
|
|
@@ -547,7 +698,7 @@ export function useZUIInteraction(
|
|
|
547
698
|
setHoveredItem(proxyEdge)
|
|
548
699
|
return
|
|
549
700
|
}
|
|
550
|
-
const edge = findHoveredEdge(worldX, worldY,
|
|
701
|
+
const edge = findHoveredEdge(worldX, worldY, edgeSpatialIndexRef.current!, view)
|
|
551
702
|
if (edge) {
|
|
552
703
|
setHoveredItem(edge)
|
|
553
704
|
} else {
|
|
@@ -619,7 +770,8 @@ export function useZUIInteraction(
|
|
|
619
770
|
e.preventDefault()
|
|
620
771
|
if (e.touches.length === 1) {
|
|
621
772
|
dragging.current = true
|
|
622
|
-
lastMouse.current =
|
|
773
|
+
lastMouse.current.x = e.touches[0].clientX
|
|
774
|
+
lastMouse.current.y = e.touches[0].clientY
|
|
623
775
|
lastPinchDist.current = null
|
|
624
776
|
} else if (e.touches.length >= 2) {
|
|
625
777
|
dragging.current = false
|
|
@@ -635,7 +787,8 @@ export function useZUIInteraction(
|
|
|
635
787
|
if (e.touches.length === 1 && dragging.current) {
|
|
636
788
|
const dx = e.touches[0].clientX - lastMouse.current.x
|
|
637
789
|
const dy = e.touches[0].clientY - lastMouse.current.y
|
|
638
|
-
lastMouse.current =
|
|
790
|
+
lastMouse.current.x = e.touches[0].clientX
|
|
791
|
+
lastMouse.current.y = e.touches[0].clientY
|
|
639
792
|
setViewState((prev) => ({ ...prev, x: prev.x + dx, y: prev.y + dy }))
|
|
640
793
|
onPanRef.current?.()
|
|
641
794
|
} else if (e.touches.length >= 2) {
|
|
@@ -677,7 +830,8 @@ export function useZUIInteraction(
|
|
|
677
830
|
} else if (e.touches.length === 1) {
|
|
678
831
|
// Transition back to dragging with the single remaining finger
|
|
679
832
|
dragging.current = true
|
|
680
|
-
lastMouse.current =
|
|
833
|
+
lastMouse.current.x = e.touches[0].clientX
|
|
834
|
+
lastMouse.current.y = e.touches[0].clientY
|
|
681
835
|
lastPinchDist.current = null
|
|
682
836
|
} else {
|
|
683
837
|
// Still have multiple fingers, reset baseline to avoid jumps
|
|
@@ -28,6 +28,12 @@ export function apiUrl(path: string): string {
|
|
|
28
28
|
return `${apiBase}${path.startsWith('/') ? path : `/${path}`}`
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export function fetchApiAsset(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
32
|
+
const headers = new Headers(init?.headers)
|
|
33
|
+
if (window.__TLD_API_KEY__) headers.set('Authorization', `Bearer ${window.__TLD_API_KEY__}`)
|
|
34
|
+
return fetch(input, { ...init, headers })
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
export function oauthGoogleStartUrl(): string {
|
|
32
38
|
return apiUrl('/auth/oauth/google')
|
|
33
39
|
}
|
package/src/config/runtime.ts
CHANGED
|
@@ -28,3 +28,7 @@ export const apiBase = trimTrailingSlash(
|
|
|
28
28
|
export function apiUrl(path: string): string {
|
|
29
29
|
return `${apiBase}${path.startsWith("/") ? path : `/${path}`}`
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
export function fetchApiAsset(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
33
|
+
return fetch(input, init)
|
|
34
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -148,7 +148,6 @@ export * from './crossBranch/store'
|
|
|
148
148
|
export * from './crossBranch/types'
|
|
149
149
|
|
|
150
150
|
// ─── Demo ────────────────────────────────────────────────────────────────────
|
|
151
|
-
export { default as DemoPage, DemoNavigator } from './demo/DemoPage'
|
|
152
151
|
export * from './demo/viewEditor'
|
|
153
152
|
|
|
154
153
|
// ─── Utilities ───────────────────────────────────────────────────────────────
|
package/src/main.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StrictMode } from "react"
|
|
2
2
|
import { createRoot } from "react-dom/client"
|
|
3
3
|
import { ChakraProvider } from "@chakra-ui/react"
|
|
4
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
|
4
5
|
import { BrowserRouter } from "react-router-dom"
|
|
5
6
|
import App from "./App"
|
|
6
7
|
import theme from "./theme"
|
|
@@ -10,6 +11,15 @@ import { PlatformProvider } from "./platform/PlatformContext"
|
|
|
10
11
|
import { platform as localPlatform } from "./platform/local"
|
|
11
12
|
import "./index.css"
|
|
12
13
|
|
|
14
|
+
const queryClient = new QueryClient({
|
|
15
|
+
defaultOptions: {
|
|
16
|
+
queries: {
|
|
17
|
+
staleTime: 5_000,
|
|
18
|
+
refetchOnWindowFocus: false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
13
23
|
if (typeof window !== "undefined") {
|
|
14
24
|
document.addEventListener(
|
|
15
25
|
"wheel",
|
|
@@ -28,19 +38,21 @@ if (typeof window !== "undefined") {
|
|
|
28
38
|
|
|
29
39
|
createRoot(document.getElementById("root")!).render(
|
|
30
40
|
<StrictMode>
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
<QueryClientProvider client={queryClient}>
|
|
42
|
+
<ChakraProvider theme={theme}>
|
|
43
|
+
<PlatformProvider platform={localPlatform}>
|
|
44
|
+
<BrowserRouter
|
|
45
|
+
basename={routerBasename}
|
|
46
|
+
future={{
|
|
47
|
+
v7_startTransition: false,
|
|
48
|
+
v7_relativeSplatPath: true,
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
<App />
|
|
52
|
+
</BrowserRouter>
|
|
53
|
+
<ToastContainer />
|
|
54
|
+
</PlatformProvider>
|
|
55
|
+
</ChakraProvider>
|
|
56
|
+
</QueryClientProvider>
|
|
45
57
|
</StrictMode>,
|
|
46
58
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createContext
|
|
1
|
+
import { createContext } from 'react'
|
|
2
2
|
import type { LibraryElement, Connector } from '../../types'
|
|
3
|
+
import { useStore } from '../../store/useStore'
|
|
3
4
|
|
|
4
5
|
export interface ViewEditorContextValue {
|
|
5
6
|
viewId: number | null
|
|
@@ -15,7 +16,14 @@ export interface ViewEditorContextValue {
|
|
|
15
16
|
export const ViewEditorContext = createContext<ViewEditorContextValue | null>(null)
|
|
16
17
|
|
|
17
18
|
export function useViewEditorContext(): ViewEditorContextValue {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const viewId = useStore((state) => state.viewId)
|
|
20
|
+
const canEdit = useStore((state) => state.canEdit)
|
|
21
|
+
const isOwner = useStore((state) => state.isOwner)
|
|
22
|
+
const isFreePlan = useStore((state) => state.isFreePlan)
|
|
23
|
+
const snapToGrid = useStore((state) => state.snapToGrid)
|
|
24
|
+
const setSnapToGrid = useStore((state) => state.setSnapToGrid)
|
|
25
|
+
const selectedElement = useStore((state) => state.selectedElement)
|
|
26
|
+
const selectedConnector = useStore((state) => state.selectedConnector)
|
|
27
|
+
|
|
28
|
+
return { viewId, canEdit, isOwner, isFreePlan, snapToGrid, setSnapToGrid, selectedElement, selectedConnector }
|
|
21
29
|
}
|