@you-agent-factory/factory-visualizers 0.0.2 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -15
- package/dist/factory-emulator-controls.d.ts +2 -2
- package/dist/factory-emulator-error-boundary.d.ts +1 -1
- package/dist/factory-emulator-view.d.ts +1 -1
- package/dist/factory-recording-topology-replay.d.ts +2 -4
- package/dist/factory-timeline-scrubber.d.ts +1 -1
- package/dist/factory-topology-replay.d.ts +13 -17
- package/dist/factory-topology-state.d.ts +4 -5
- package/dist/index.d.ts +2 -3
- package/dist/index.js +29 -705
- package/dist/styles.css +25 -320
- package/dist/visualizer-error.d.ts +1 -8
- package/dist/work-progress-visualizer.d.ts +1 -1
- package/package.json +12 -10
- package/dist/factory-topology-active-work.d.ts +0 -11
- package/dist/factory-topology-chrome.d.ts +0 -14
- package/dist/factory-topology-flow-projection.d.ts +0 -12
- package/dist/factory-topology-replay-nodes.d.ts +0 -32
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx, jsxs
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FactoryEmulatorControls as FactoryEmulatorControls$1 } from "@you-agent-factory/components/factory-emulator";
|
|
3
|
+
import { Text, Button, Heading } from "@you-agent-factory/components/primitives";
|
|
4
|
+
import { Component, useId, useMemo, useRef, useEffect, useState } from "react";
|
|
5
|
+
import { FactoryGraphReplaySurface, createFactoryGraphSource } from "@you-agent-factory/factory-graph";
|
|
6
|
+
import { safeParseFactoryRecording } from "@you-agent-factory/client";
|
|
7
7
|
import { canonicalizeFactoryEvents, projectFactoryTopologyAtTick, projectFactoryWorkProgressAtTick, projectFactoryLoadAtTick, projectFactoryActivityAtTick } from "@you-agent-factory/factory-replay";
|
|
8
8
|
const ERROR_MESSAGES = {
|
|
9
9
|
endpoint: "The prepared topology contains invalid edge endpoints.",
|
|
@@ -248,445 +248,6 @@ function FactoryEmulatorControls({
|
|
|
248
248
|
}
|
|
249
249
|
);
|
|
250
250
|
}
|
|
251
|
-
const VISIBLE_ACTIVE_WORK_ROWS = 3;
|
|
252
|
-
function projectFactoryTopologyActiveWork(activity) {
|
|
253
|
-
const durationByWorkId = /* @__PURE__ */ new Map();
|
|
254
|
-
for (const overlay of activity.activeDispatchOverlays) {
|
|
255
|
-
const durationTicks = Math.max(
|
|
256
|
-
0,
|
|
257
|
-
activity.selectedTick - overlay.startedTick
|
|
258
|
-
);
|
|
259
|
-
for (const workId of overlay.workIds ?? []) {
|
|
260
|
-
const previousDuration = durationByWorkId.get(workId);
|
|
261
|
-
if (previousDuration === void 0 || durationTicks > previousDuration)
|
|
262
|
-
durationByWorkId.set(workId, durationTicks);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
const rows = [...durationByWorkId].sort(([left], [right]) => left.localeCompare(right)).map(([id, durationTicks]) => ({ durationTicks, id }));
|
|
266
|
-
return {
|
|
267
|
-
overflowCount: Math.max(0, rows.length - VISIBLE_ACTIVE_WORK_ROWS),
|
|
268
|
-
rows: rows.slice(0, VISIBLE_ACTIVE_WORK_ROWS)
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
const DEFAULT_FACTORY_TOPOLOGY_CHROME_PRESET = "full";
|
|
272
|
-
const FACTORY_TOPOLOGY_CHROME_PRESETS = {
|
|
273
|
-
full: {
|
|
274
|
-
background: true,
|
|
275
|
-
legend: true,
|
|
276
|
-
viewportControls: true,
|
|
277
|
-
visibilityControls: true
|
|
278
|
-
},
|
|
279
|
-
minimal: {
|
|
280
|
-
background: true,
|
|
281
|
-
legend: false,
|
|
282
|
-
viewportControls: true,
|
|
283
|
-
visibilityControls: false
|
|
284
|
-
},
|
|
285
|
-
none: {
|
|
286
|
-
background: false,
|
|
287
|
-
legend: false,
|
|
288
|
-
viewportControls: false,
|
|
289
|
-
visibilityControls: false
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
function resolveFactoryTopologyChrome(configuration = {}) {
|
|
293
|
-
const preset = configuration.preset ?? DEFAULT_FACTORY_TOPOLOGY_CHROME_PRESET;
|
|
294
|
-
const resolvedPreset = FACTORY_TOPOLOGY_CHROME_PRESETS[preset];
|
|
295
|
-
return {
|
|
296
|
-
background: configuration.background ?? resolvedPreset.background,
|
|
297
|
-
legend: configuration.legend ?? resolvedPreset.legend,
|
|
298
|
-
viewportControls: configuration.viewportControls ?? resolvedPreset.viewportControls,
|
|
299
|
-
visibilityControls: configuration.visibilityControls ?? resolvedPreset.visibilityControls
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
const columnByKind = {
|
|
303
|
-
resource: 0,
|
|
304
|
-
worker: 1,
|
|
305
|
-
"work-type": 2,
|
|
306
|
-
"work-state": 3,
|
|
307
|
-
workstation: 4
|
|
308
|
-
};
|
|
309
|
-
const DEFAULT_ANNOTATION_WIDTH = 240;
|
|
310
|
-
function projectFactoryTopologyFlow(projection, messages, selectedNodeId, onSelectNode, prefersReducedMotion = false, layout) {
|
|
311
|
-
try {
|
|
312
|
-
const { connections, nodes: topologyNodes } = projection.topology;
|
|
313
|
-
const nodeById = new Map(topologyNodes.map((node) => [node.id, node]));
|
|
314
|
-
const validEndpoints = connections.every(
|
|
315
|
-
(connection) => connectionHasRenderedEndpoints(connection, nodeById)
|
|
316
|
-
);
|
|
317
|
-
const nodeData = nodePresentationData(projection, connections, layout);
|
|
318
|
-
return {
|
|
319
|
-
edges: validEndpoints ? projectEdges(connections, projection.activity, prefersReducedMotion) : [],
|
|
320
|
-
nodes: [
|
|
321
|
-
...projectTopologyNodes(
|
|
322
|
-
topologyNodes,
|
|
323
|
-
messages,
|
|
324
|
-
selectedNodeId,
|
|
325
|
-
onSelectNode,
|
|
326
|
-
nodeData
|
|
327
|
-
),
|
|
328
|
-
...projectAnnotations(layout, messages)
|
|
329
|
-
],
|
|
330
|
-
validEndpoints
|
|
331
|
-
};
|
|
332
|
-
} catch (error) {
|
|
333
|
-
if (error instanceof FactoryVisualizerInternalError) throw error;
|
|
334
|
-
throw new FactoryVisualizerInternalError("projection", error);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
function nodePresentationData(projection, connections, layout) {
|
|
338
|
-
return {
|
|
339
|
-
activeDetailNodeIds: activityDetailNodeIds(
|
|
340
|
-
projection.activity,
|
|
341
|
-
connections,
|
|
342
|
-
projection.load.resourceOccupancy,
|
|
343
|
-
projection.load.workStateCounts
|
|
344
|
-
),
|
|
345
|
-
activityCountByNode: activityCounts(projection.activity),
|
|
346
|
-
emptyStateByNode: new Map(
|
|
347
|
-
(layout?.nodeEmptyStates ?? []).map((state) => [
|
|
348
|
-
state.nodeId,
|
|
349
|
-
state.content
|
|
350
|
-
])
|
|
351
|
-
),
|
|
352
|
-
occupancyByNode: new Map(
|
|
353
|
-
projection.load.resourceOccupancy.map((occupancy) => [
|
|
354
|
-
occupancy.resourceNodeId,
|
|
355
|
-
occupancy
|
|
356
|
-
])
|
|
357
|
-
),
|
|
358
|
-
workStateCountByNode: new Map(
|
|
359
|
-
projection.load.workStateCounts.map((count) => [
|
|
360
|
-
count.workStateNodeId,
|
|
361
|
-
count
|
|
362
|
-
])
|
|
363
|
-
)
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
function projectTopologyNodes(topologyNodes, messages, selectedNodeId, onSelectNode, data) {
|
|
367
|
-
const rowByKind = /* @__PURE__ */ new Map();
|
|
368
|
-
return topologyNodes.map((node) => {
|
|
369
|
-
const row = rowByKind.get(node.kind) ?? 0;
|
|
370
|
-
rowByKind.set(node.kind, row + 1);
|
|
371
|
-
const occupancy = data.occupancyByNode.get(node.id);
|
|
372
|
-
const workStateCount = data.workStateCountByNode.get(node.id);
|
|
373
|
-
return {
|
|
374
|
-
data: {
|
|
375
|
-
activityCount: data.activityCountByNode.get(node.id) ?? 0,
|
|
376
|
-
...data.activeDetailNodeIds.has(node.id) ? {} : { emptyState: data.emptyStateByNode.get(node.id) },
|
|
377
|
-
messages,
|
|
378
|
-
node,
|
|
379
|
-
...occupancy ? {
|
|
380
|
-
occupancy: {
|
|
381
|
-
capacity: occupancy.capacity,
|
|
382
|
-
evidence: occupancy.evidence,
|
|
383
|
-
occupied: occupancy.occupiedQuantity
|
|
384
|
-
}
|
|
385
|
-
} : {},
|
|
386
|
-
onSelectNode,
|
|
387
|
-
selected: selectedNodeId === node.id,
|
|
388
|
-
...workStateCount ? {
|
|
389
|
-
workStateCount: {
|
|
390
|
-
count: workStateCount.count,
|
|
391
|
-
evidence: workStateCount.evidence
|
|
392
|
-
}
|
|
393
|
-
} : {}
|
|
394
|
-
},
|
|
395
|
-
draggable: false,
|
|
396
|
-
id: node.id,
|
|
397
|
-
position: layoutNode(node.kind, row),
|
|
398
|
-
selectable: false,
|
|
399
|
-
type: "factoryTopologyNode"
|
|
400
|
-
};
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
function projectAnnotations(layout, messages) {
|
|
404
|
-
return (layout?.annotations ?? []).map((annotation) => ({
|
|
405
|
-
data: { annotation, messages },
|
|
406
|
-
draggable: false,
|
|
407
|
-
id: `annotation:${annotation.id}`,
|
|
408
|
-
position: annotation.position,
|
|
409
|
-
selectable: false,
|
|
410
|
-
type: "factoryTopologyAnnotation",
|
|
411
|
-
style: {
|
|
412
|
-
...annotation.size ? { height: annotation.size.height } : {},
|
|
413
|
-
width: annotation.size?.width ?? DEFAULT_ANNOTATION_WIDTH
|
|
414
|
-
}
|
|
415
|
-
}));
|
|
416
|
-
}
|
|
417
|
-
function projectEdges(connections, activity, prefersReducedMotion) {
|
|
418
|
-
return connections.map((connection) => ({
|
|
419
|
-
animated: !prefersReducedMotion && activity.activeDispatchOverlays.some(
|
|
420
|
-
(overlay) => overlay.connectionIds.includes(connection.id)
|
|
421
|
-
),
|
|
422
|
-
data: { relationship: connection.kind },
|
|
423
|
-
id: connection.id,
|
|
424
|
-
source: connection.source.nodeId,
|
|
425
|
-
sourceHandle: connection.source.handleId,
|
|
426
|
-
target: connection.target.nodeId,
|
|
427
|
-
targetHandle: connection.target.handleId
|
|
428
|
-
}));
|
|
429
|
-
}
|
|
430
|
-
function connectionHasRenderedEndpoints(connection, nodeById) {
|
|
431
|
-
const source = nodeById.get(connection.source.nodeId);
|
|
432
|
-
const target = nodeById.get(connection.target.nodeId);
|
|
433
|
-
return Boolean(
|
|
434
|
-
source?.handles.some(
|
|
435
|
-
(handle) => handle.id === connection.source.handleId && handle.role === "source"
|
|
436
|
-
) && target?.handles.some(
|
|
437
|
-
(handle) => handle.id === connection.target.handleId && handle.role === "target"
|
|
438
|
-
)
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
function activityCounts(activity) {
|
|
442
|
-
const counts = /* @__PURE__ */ new Map();
|
|
443
|
-
for (const overlay of activity.activeDispatchOverlays) {
|
|
444
|
-
for (const nodeId of /* @__PURE__ */ new Set([
|
|
445
|
-
overlay.workerNodeId,
|
|
446
|
-
overlay.workstationNodeId,
|
|
447
|
-
...overlay.resourceNodeIds ?? []
|
|
448
|
-
])) {
|
|
449
|
-
if (nodeId) counts.set(nodeId, (counts.get(nodeId) ?? 0) + 1);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return counts;
|
|
453
|
-
}
|
|
454
|
-
function activityDetailNodeIds(activity, connections, resourceOccupancy, workStateCounts) {
|
|
455
|
-
const nodeIds = new Set(activity.activeWorkstationNodeIds);
|
|
456
|
-
const connectionById = new Map(
|
|
457
|
-
connections.map((connection) => [connection.id, connection])
|
|
458
|
-
);
|
|
459
|
-
for (const occupancy of resourceOccupancy)
|
|
460
|
-
if (occupancy.evidence === "known") nodeIds.add(occupancy.resourceNodeId);
|
|
461
|
-
for (const state of workStateCounts)
|
|
462
|
-
if (state.evidence === "known" && typeof state.count === "number" && state.count > 0)
|
|
463
|
-
nodeIds.add(state.workStateNodeId);
|
|
464
|
-
for (const overlay of activity.activeDispatchOverlays) {
|
|
465
|
-
for (const nodeId of [
|
|
466
|
-
overlay.workerNodeId,
|
|
467
|
-
overlay.workstationNodeId,
|
|
468
|
-
...overlay.resourceNodeIds ?? []
|
|
469
|
-
])
|
|
470
|
-
if (nodeId) nodeIds.add(nodeId);
|
|
471
|
-
for (const connectionId of overlay.connectionIds) {
|
|
472
|
-
const connection = connectionById.get(connectionId);
|
|
473
|
-
if (connection) {
|
|
474
|
-
nodeIds.add(connection.source.nodeId);
|
|
475
|
-
nodeIds.add(connection.target.nodeId);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return nodeIds;
|
|
480
|
-
}
|
|
481
|
-
function layoutNode(kind, row) {
|
|
482
|
-
const column = columnByKind[kind];
|
|
483
|
-
if (column === void 0 || !Number.isSafeInteger(row) || row < 0)
|
|
484
|
-
throw new FactoryVisualizerInternalError("layout");
|
|
485
|
-
return { x: column * 260, y: row * 170 };
|
|
486
|
-
}
|
|
487
|
-
const nodeTypes = {
|
|
488
|
-
factoryTopologyAnnotation: FactoryTopologyAnnotationView,
|
|
489
|
-
factoryTopologyNode: FactoryTopologyNodeView
|
|
490
|
-
};
|
|
491
|
-
function FactoryTopologyAnnotationView({
|
|
492
|
-
data
|
|
493
|
-
}) {
|
|
494
|
-
const { annotation, messages } = data;
|
|
495
|
-
if (annotation.kind === "image") {
|
|
496
|
-
return /* @__PURE__ */ jsx(
|
|
497
|
-
FactoryTopologyAnnotationImage,
|
|
498
|
-
{
|
|
499
|
-
annotation,
|
|
500
|
-
messages
|
|
501
|
-
}
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
return /* @__PURE__ */ jsxs(
|
|
505
|
-
"aside",
|
|
506
|
-
{
|
|
507
|
-
className: "factory-topology-replay__annotation",
|
|
508
|
-
"data-tone": annotation.tone ?? "neutral",
|
|
509
|
-
children: [
|
|
510
|
-
annotation.title ? /* @__PURE__ */ jsx("strong", { className: "factory-topology-replay__annotation-title", children: annotation.title }) : null,
|
|
511
|
-
/* @__PURE__ */ jsx("span", { className: "factory-topology-replay__annotation-body", children: annotation.body })
|
|
512
|
-
]
|
|
513
|
-
}
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
function FactoryTopologyAnnotationImage({
|
|
517
|
-
annotation,
|
|
518
|
-
messages
|
|
519
|
-
}) {
|
|
520
|
-
const image = useEmbeddedImageUrl(annotation.source);
|
|
521
|
-
return /* @__PURE__ */ jsx("figure", { className: "factory-topology-replay__annotation factory-topology-replay__annotation--image", children: image.status === "ready" ? /* @__PURE__ */ jsx(
|
|
522
|
-
"img",
|
|
523
|
-
{
|
|
524
|
-
alt: annotation.altText,
|
|
525
|
-
className: "factory-topology-replay__annotation-image",
|
|
526
|
-
onError: image.fail,
|
|
527
|
-
src: image.url
|
|
528
|
-
}
|
|
529
|
-
) : /* @__PURE__ */ jsxs(
|
|
530
|
-
"div",
|
|
531
|
-
{
|
|
532
|
-
className: "factory-topology-replay__annotation-image-state",
|
|
533
|
-
role: image.status === "failed" ? "alert" : "status",
|
|
534
|
-
children: [
|
|
535
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: annotation.altText }),
|
|
536
|
-
image.status === "failed" ? messages.imageFailed : messages.imageLoading
|
|
537
|
-
]
|
|
538
|
-
}
|
|
539
|
-
) });
|
|
540
|
-
}
|
|
541
|
-
function FactoryTopologyNodeView({ data }) {
|
|
542
|
-
const {
|
|
543
|
-
activityCount,
|
|
544
|
-
emptyState,
|
|
545
|
-
messages,
|
|
546
|
-
node,
|
|
547
|
-
occupancy,
|
|
548
|
-
onSelectNode,
|
|
549
|
-
selected,
|
|
550
|
-
workStateCount
|
|
551
|
-
} = data;
|
|
552
|
-
const state = selected ? "selected" : "default";
|
|
553
|
-
const handles = node.handles.map((handle) => ({
|
|
554
|
-
connectable: false,
|
|
555
|
-
id: handle.id,
|
|
556
|
-
label: handle.id,
|
|
557
|
-
side: handle.role === "target" ? "left" : "right",
|
|
558
|
-
type: handle.role
|
|
559
|
-
}));
|
|
560
|
-
const content = /* @__PURE__ */ jsxs(
|
|
561
|
-
GraphNodeShell,
|
|
562
|
-
{
|
|
563
|
-
className: activityCount > 0 ? "factory-topology-replay__node--active" : "",
|
|
564
|
-
"data-dispatch-activity": activityCount > 0 ? "active" : "inactive",
|
|
565
|
-
handles,
|
|
566
|
-
nodeKind: node.kind,
|
|
567
|
-
showStateIndicator: false,
|
|
568
|
-
state,
|
|
569
|
-
children: [
|
|
570
|
-
/* @__PURE__ */ jsx("strong", { className: "factory-topology-replay__node-title", children: node.label }),
|
|
571
|
-
/* @__PURE__ */ jsx("span", { className: "factory-topology-replay__node-kind", children: node.kind }),
|
|
572
|
-
/* @__PURE__ */ jsx("div", { className: "factory-topology-replay__node-activity-detail", children: emptyState ? /* @__PURE__ */ jsx(
|
|
573
|
-
FactoryTopologyNodeEmptyStateView,
|
|
574
|
-
{
|
|
575
|
-
content: emptyState,
|
|
576
|
-
messages
|
|
577
|
-
}
|
|
578
|
-
) : /* @__PURE__ */ jsxs("span", { className: "factory-topology-replay__node-cue", children: [
|
|
579
|
-
activityCount > 0 ? "●" : "○",
|
|
580
|
-
" ",
|
|
581
|
-
activityCount > 0 ? messages.activeDispatches(activityCount) : messages.inactiveDispatches
|
|
582
|
-
] }) }),
|
|
583
|
-
node.kind === "resource" ? /* @__PURE__ */ jsxs("span", { className: "factory-topology-replay__node-cue", children: [
|
|
584
|
-
"◫",
|
|
585
|
-
" ",
|
|
586
|
-
occupancy?.evidence === "known" && occupancy.occupied !== void 0 && occupancy.capacity !== void 0 ? messages.resourceOccupancy(occupancy.occupied, occupancy.capacity) : messages.resourceOccupancyUnavailable
|
|
587
|
-
] }) : null,
|
|
588
|
-
node.kind === "work-state" ? /* @__PURE__ */ jsxs("span", { className: "factory-topology-replay__node-cue", children: [
|
|
589
|
-
"∑",
|
|
590
|
-
" ",
|
|
591
|
-
workStateCount?.evidence === "known" && workStateCount.count !== void 0 ? messages.workStateCount(workStateCount.count) : messages.workStateCountUnavailable
|
|
592
|
-
] }) : null,
|
|
593
|
-
selected ? /* @__PURE__ */ jsxs("span", { className: "factory-topology-replay__node-cue", children: [
|
|
594
|
-
"✓ ",
|
|
595
|
-
messages.selectedNode
|
|
596
|
-
] }) : null
|
|
597
|
-
]
|
|
598
|
-
}
|
|
599
|
-
);
|
|
600
|
-
return onSelectNode ? /* @__PURE__ */ jsx(
|
|
601
|
-
GraphNodeButton,
|
|
602
|
-
{
|
|
603
|
-
"aria-label": messages.nodeLabel(node.kind, node.label),
|
|
604
|
-
className: "factory-topology-replay__node-button",
|
|
605
|
-
graphState: state,
|
|
606
|
-
onClick: () => onSelectNode(node),
|
|
607
|
-
children: content
|
|
608
|
-
}
|
|
609
|
-
) : /* @__PURE__ */ jsx(
|
|
610
|
-
"figure",
|
|
611
|
-
{
|
|
612
|
-
"aria-label": messages.nodeLabel(node.kind, node.label),
|
|
613
|
-
className: "factory-topology-replay__node-static",
|
|
614
|
-
children: content
|
|
615
|
-
}
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
function FactoryTopologyNodeEmptyStateView({
|
|
619
|
-
content,
|
|
620
|
-
messages
|
|
621
|
-
}) {
|
|
622
|
-
return content.kind === "image" ? /* @__PURE__ */ jsx(FactoryTopologyEmptyStateImage, { content, messages }) : /* @__PURE__ */ jsx("span", { className: "factory-topology-replay__node-empty-state", children: content.text });
|
|
623
|
-
}
|
|
624
|
-
function FactoryTopologyEmptyStateImage({
|
|
625
|
-
content,
|
|
626
|
-
messages
|
|
627
|
-
}) {
|
|
628
|
-
const image = useEmbeddedImageUrl(content.source);
|
|
629
|
-
return image.status === "ready" ? /* @__PURE__ */ jsx(
|
|
630
|
-
"img",
|
|
631
|
-
{
|
|
632
|
-
alt: content.altText,
|
|
633
|
-
className: "factory-topology-replay__node-empty-state-image",
|
|
634
|
-
onError: image.fail,
|
|
635
|
-
src: image.url
|
|
636
|
-
}
|
|
637
|
-
) : /* @__PURE__ */ jsxs(
|
|
638
|
-
"span",
|
|
639
|
-
{
|
|
640
|
-
className: "factory-topology-replay__node-empty-state",
|
|
641
|
-
role: image.status === "failed" ? "alert" : "status",
|
|
642
|
-
children: [
|
|
643
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: content.altText }),
|
|
644
|
-
image.status === "failed" ? messages.imageFailed : messages.imageLoading
|
|
645
|
-
]
|
|
646
|
-
}
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
function useEmbeddedImageUrl(source) {
|
|
650
|
-
const urlRef = useRef(void 0);
|
|
651
|
-
const [state, setState] = useState({ status: "loading" });
|
|
652
|
-
useEffect(() => {
|
|
653
|
-
try {
|
|
654
|
-
const url = URL.createObjectURL(
|
|
655
|
-
new Blob([decodeEmbeddedImage(source.base64)], {
|
|
656
|
-
type: source.mediaType
|
|
657
|
-
})
|
|
658
|
-
);
|
|
659
|
-
urlRef.current = url;
|
|
660
|
-
setState({ status: "ready", url });
|
|
661
|
-
return () => {
|
|
662
|
-
if (urlRef.current === url) {
|
|
663
|
-
URL.revokeObjectURL(url);
|
|
664
|
-
urlRef.current = void 0;
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
} catch {
|
|
668
|
-
setState({ status: "failed" });
|
|
669
|
-
return void 0;
|
|
670
|
-
}
|
|
671
|
-
}, [source]);
|
|
672
|
-
return {
|
|
673
|
-
fail: () => {
|
|
674
|
-
if (urlRef.current) {
|
|
675
|
-
URL.revokeObjectURL(urlRef.current);
|
|
676
|
-
urlRef.current = void 0;
|
|
677
|
-
}
|
|
678
|
-
setState({ status: "failed" });
|
|
679
|
-
},
|
|
680
|
-
...state
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
function decodeEmbeddedImage(base64) {
|
|
684
|
-
const decoded = atob(base64);
|
|
685
|
-
const bytes = new Uint8Array(decoded.length);
|
|
686
|
-
for (let index = 0; index < decoded.length; index += 1)
|
|
687
|
-
bytes[index] = decoded.charCodeAt(index);
|
|
688
|
-
return bytes.buffer;
|
|
689
|
-
}
|
|
690
251
|
function FactoryTopologyStateRegion({
|
|
691
252
|
messages,
|
|
692
253
|
onRetry,
|
|
@@ -771,18 +332,6 @@ class FactoryTopologyErrorBoundary extends Component {
|
|
|
771
332
|
this.props.onError?.(error);
|
|
772
333
|
}
|
|
773
334
|
}
|
|
774
|
-
function useDistinctTopologyErrorReport(error, onError) {
|
|
775
|
-
const reportedErrors = useRef(/* @__PURE__ */ new Set());
|
|
776
|
-
useEffect(() => {
|
|
777
|
-
if (!error) return;
|
|
778
|
-
const key = error.kind === "layout-validation" ? error.issues.map(
|
|
779
|
-
(issue) => `${issue.category}:${issue.code}:${issue.path.join(".")}`
|
|
780
|
-
).join("|") : factoryVisualizerErrorKey(error);
|
|
781
|
-
if (reportedErrors.current.has(key)) return;
|
|
782
|
-
reportedErrors.current.add(key);
|
|
783
|
-
onError?.(error);
|
|
784
|
-
}, [error, onError]);
|
|
785
|
-
}
|
|
786
335
|
function resetKeysChanged(previous, current) {
|
|
787
336
|
return previous.length !== current.length || previous.some((value, index) => value !== current[index]);
|
|
788
337
|
}
|
|
@@ -800,10 +349,7 @@ function FactoryTopologyReplay(props) {
|
|
|
800
349
|
);
|
|
801
350
|
}
|
|
802
351
|
function FactoryTopologyReplayContent({
|
|
803
|
-
chrome,
|
|
804
352
|
messages,
|
|
805
|
-
onError,
|
|
806
|
-
layout,
|
|
807
353
|
onRetry,
|
|
808
354
|
onSelectNode,
|
|
809
355
|
selectedNodeId,
|
|
@@ -814,93 +360,8 @@ function FactoryTopologyReplayContent({
|
|
|
814
360
|
FactoryTopologyStateRegion,
|
|
815
361
|
{
|
|
816
362
|
messages,
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
}
|
|
820
|
-
);
|
|
821
|
-
}
|
|
822
|
-
return /* @__PURE__ */ jsx(
|
|
823
|
-
PreparedTopology,
|
|
824
|
-
{
|
|
825
|
-
chrome,
|
|
826
|
-
messages,
|
|
827
|
-
onError,
|
|
828
|
-
onRetry,
|
|
829
|
-
onSelectNode,
|
|
830
|
-
layout,
|
|
831
|
-
projection: state.projection,
|
|
832
|
-
selectedNodeId
|
|
833
|
-
}
|
|
834
|
-
);
|
|
835
|
-
}
|
|
836
|
-
function PreparedTopology({
|
|
837
|
-
chrome,
|
|
838
|
-
messages,
|
|
839
|
-
onError,
|
|
840
|
-
layout,
|
|
841
|
-
onRetry,
|
|
842
|
-
onSelectNode,
|
|
843
|
-
projection,
|
|
844
|
-
selectedNodeId
|
|
845
|
-
}) {
|
|
846
|
-
const prefersReducedMotion = usePrefersReducedMotion();
|
|
847
|
-
const resolvedChrome = resolveFactoryTopologyChrome(chrome);
|
|
848
|
-
const prepared = useMemo(() => {
|
|
849
|
-
try {
|
|
850
|
-
const parsedLayout = layout === void 0 ? void 0 : safeParseFactoryVisualizationLayout(layout, {
|
|
851
|
-
canonicalNodeIds: new Set(
|
|
852
|
-
projection.topology.nodes.map((node) => node.id)
|
|
853
|
-
)
|
|
854
|
-
});
|
|
855
|
-
if (parsedLayout && !parsedLayout.success) {
|
|
856
|
-
return {
|
|
857
|
-
error: {
|
|
858
|
-
issues: parsedLayout.issues.map(({ category, code, path }) => ({
|
|
859
|
-
category,
|
|
860
|
-
code,
|
|
861
|
-
path
|
|
862
|
-
})),
|
|
863
|
-
kind: "layout-validation",
|
|
864
|
-
message: "The topology layout could not be prepared.",
|
|
865
|
-
recoverable: true
|
|
866
|
-
},
|
|
867
|
-
status: "failed"
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
const flow = projectFactoryTopologyFlow(
|
|
871
|
-
projection,
|
|
872
|
-
messages,
|
|
873
|
-
selectedNodeId,
|
|
874
|
-
onSelectNode,
|
|
875
|
-
prefersReducedMotion,
|
|
876
|
-
parsedLayout?.data
|
|
877
|
-
);
|
|
878
|
-
return flow.validEndpoints ? { flow, status: "ready" } : { error: toFactoryVisualizerError("endpoint"), status: "failed" };
|
|
879
|
-
} catch (error) {
|
|
880
|
-
return {
|
|
881
|
-
error: normalizeFactoryVisualizerError(error, "projection"),
|
|
882
|
-
status: "failed"
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
}, [
|
|
886
|
-
messages,
|
|
887
|
-
onSelectNode,
|
|
888
|
-
prefersReducedMotion,
|
|
889
|
-
projection,
|
|
890
|
-
layout,
|
|
891
|
-
selectedNodeId
|
|
892
|
-
]);
|
|
893
|
-
useDistinctTopologyErrorReport(
|
|
894
|
-
prepared.status === "failed" ? prepared.error : void 0,
|
|
895
|
-
onError
|
|
896
|
-
);
|
|
897
|
-
if (prepared.status === "failed") {
|
|
898
|
-
return /* @__PURE__ */ jsx(
|
|
899
|
-
FactoryTopologyStateRegion,
|
|
900
|
-
{
|
|
901
|
-
messages,
|
|
902
|
-
state: "failed",
|
|
903
|
-
onRetry
|
|
363
|
+
onRetry,
|
|
364
|
+
state: state.status
|
|
904
365
|
}
|
|
905
366
|
);
|
|
906
367
|
}
|
|
@@ -909,156 +370,22 @@ function PreparedTopology({
|
|
|
909
370
|
{
|
|
910
371
|
"aria-label": messages.regionLabel,
|
|
911
372
|
className: "factory-topology-replay",
|
|
912
|
-
"data-endpoints-valid": "true",
|
|
913
|
-
"data-reduced-motion": prefersReducedMotion ? "true" : "false",
|
|
914
373
|
children: /* @__PURE__ */ jsx(
|
|
915
|
-
|
|
374
|
+
FactoryGraphReplaySurface,
|
|
916
375
|
{
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
{
|
|
926
|
-
activity: projection.activity,
|
|
927
|
-
chrome: resolvedChrome,
|
|
928
|
-
flow: prepared.flow,
|
|
929
|
-
messages
|
|
930
|
-
}
|
|
931
|
-
)
|
|
376
|
+
onSelectNode: onSelectNode ? (nodeId) => {
|
|
377
|
+
const node = state.source.runtime.topology.nodes.find(
|
|
378
|
+
(entry) => entry.id === nodeId
|
|
379
|
+
);
|
|
380
|
+
if (node) onSelectNode(node);
|
|
381
|
+
} : void 0,
|
|
382
|
+
selectedNodeId,
|
|
383
|
+
source: state.source
|
|
932
384
|
}
|
|
933
385
|
)
|
|
934
386
|
}
|
|
935
387
|
);
|
|
936
388
|
}
|
|
937
|
-
function ReactFlowCanvas({
|
|
938
|
-
activity,
|
|
939
|
-
chrome,
|
|
940
|
-
flow,
|
|
941
|
-
messages
|
|
942
|
-
}) {
|
|
943
|
-
const chromeMessages = resolveTopologyChromeMessages(messages);
|
|
944
|
-
const [annotationsVisible, setAnnotationsVisible] = useState(true);
|
|
945
|
-
const visibleNodes = annotationsVisible ? flow.nodes : flow.nodes.filter((node) => node.type !== "factoryTopologyAnnotation");
|
|
946
|
-
const hasAnnotations = flow.nodes.some(
|
|
947
|
-
(node) => node.type === "factoryTopologyAnnotation"
|
|
948
|
-
);
|
|
949
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
950
|
-
/* @__PURE__ */ jsx(ActiveWorkSummary, { activity, messages }),
|
|
951
|
-
chrome.legend ? /* @__PURE__ */ jsx(TopologyLegend, { messages: chromeMessages }) : null,
|
|
952
|
-
chrome.visibilityControls && hasAnnotations ? /* @__PURE__ */ jsx(
|
|
953
|
-
"button",
|
|
954
|
-
{
|
|
955
|
-
"aria-pressed": annotationsVisible,
|
|
956
|
-
className: "factory-topology-replay__annotation-toggle",
|
|
957
|
-
onClick: () => setAnnotationsVisible((visible) => !visible),
|
|
958
|
-
type: "button",
|
|
959
|
-
children: annotationsVisible ? messages.annotationsVisible : messages.annotationsHidden
|
|
960
|
-
}
|
|
961
|
-
) : null,
|
|
962
|
-
/* @__PURE__ */ jsxs(
|
|
963
|
-
ReactFlow,
|
|
964
|
-
{
|
|
965
|
-
edges: flow.edges,
|
|
966
|
-
edgesFocusable: false,
|
|
967
|
-
elementsSelectable: false,
|
|
968
|
-
fitView: true,
|
|
969
|
-
fitViewOptions: { includeHiddenNodes: false },
|
|
970
|
-
nodes: visibleNodes,
|
|
971
|
-
nodesConnectable: false,
|
|
972
|
-
nodesDraggable: false,
|
|
973
|
-
nodeTypes,
|
|
974
|
-
onNodeClick: preserveNestedNodePointerEvents,
|
|
975
|
-
panOnDrag: true,
|
|
976
|
-
proOptions: { hideAttribution: true },
|
|
977
|
-
children: [
|
|
978
|
-
chrome.background ? /* @__PURE__ */ jsx(Background, {}) : null,
|
|
979
|
-
chrome.viewportControls ? /* @__PURE__ */ jsx(
|
|
980
|
-
Controls,
|
|
981
|
-
{
|
|
982
|
-
"aria-label": chromeMessages.viewportControlsLabel,
|
|
983
|
-
showInteractive: false
|
|
984
|
-
}
|
|
985
|
-
) : null
|
|
986
|
-
]
|
|
987
|
-
},
|
|
988
|
-
annotationsVisible ? "annotations-visible" : "annotations-hidden"
|
|
989
|
-
)
|
|
990
|
-
] });
|
|
991
|
-
}
|
|
992
|
-
function ActiveWorkSummary({
|
|
993
|
-
activity,
|
|
994
|
-
messages
|
|
995
|
-
}) {
|
|
996
|
-
const activeWork = projectFactoryTopologyActiveWork(activity);
|
|
997
|
-
if (activeWork.rows.length === 0) return null;
|
|
998
|
-
return /* @__PURE__ */ jsxs("fieldset", { className: "factory-topology-replay__active-work", children: [
|
|
999
|
-
/* @__PURE__ */ jsx("legend", { children: messages.activeWorkRegionLabel ?? "Active Work" }),
|
|
1000
|
-
/* @__PURE__ */ jsx("ul", { children: activeWork.rows.map((work) => /* @__PURE__ */ jsxs("li", { children: [
|
|
1001
|
-
/* @__PURE__ */ jsx("span", { children: work.id }),
|
|
1002
|
-
/* @__PURE__ */ jsx("span", { children: messages.activeWorkDuration?.(work.durationTicks) ?? `Active for ${work.durationTicks} ticks` })
|
|
1003
|
-
] }, work.id)) }),
|
|
1004
|
-
activeWork.overflowCount > 0 ? /* @__PURE__ */ jsx("p", { children: messages.activeWorkOverflow?.(activeWork.overflowCount) ?? `${activeWork.overflowCount} more active Work` }) : null
|
|
1005
|
-
] });
|
|
1006
|
-
}
|
|
1007
|
-
function TopologyLegend({ messages }) {
|
|
1008
|
-
return /* @__PURE__ */ jsxs("fieldset", { className: "factory-topology-replay__legend", children: [
|
|
1009
|
-
/* @__PURE__ */ jsx("legend", { children: messages.legendLabel }),
|
|
1010
|
-
/* @__PURE__ */ jsxs("ul", { children: [
|
|
1011
|
-
/* @__PURE__ */ jsxs("li", { children: [
|
|
1012
|
-
/* @__PURE__ */ jsx(
|
|
1013
|
-
"span",
|
|
1014
|
-
{
|
|
1015
|
-
"aria-hidden": "true",
|
|
1016
|
-
className: "factory-topology-replay__legend-swatch factory-topology-replay__legend-swatch--active"
|
|
1017
|
-
}
|
|
1018
|
-
),
|
|
1019
|
-
messages.legendActiveRoute
|
|
1020
|
-
] }),
|
|
1021
|
-
/* @__PURE__ */ jsxs("li", { children: [
|
|
1022
|
-
/* @__PURE__ */ jsx(
|
|
1023
|
-
"span",
|
|
1024
|
-
{
|
|
1025
|
-
"aria-hidden": "true",
|
|
1026
|
-
className: "factory-topology-replay__legend-swatch"
|
|
1027
|
-
}
|
|
1028
|
-
),
|
|
1029
|
-
messages.legendInactiveRoute
|
|
1030
|
-
] })
|
|
1031
|
-
] })
|
|
1032
|
-
] });
|
|
1033
|
-
}
|
|
1034
|
-
function resolveTopologyChromeMessages(messages) {
|
|
1035
|
-
return {
|
|
1036
|
-
legendActiveRoute: messages.legendActiveRoute ?? "Active route",
|
|
1037
|
-
legendInactiveRoute: messages.legendInactiveRoute ?? "Inactive route",
|
|
1038
|
-
legendLabel: messages.legendLabel ?? "Topology legend",
|
|
1039
|
-
viewportControlsLabel: messages.viewportControlsLabel ?? "Topology viewport controls"
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
function preserveNestedNodePointerEvents() {
|
|
1043
|
-
}
|
|
1044
|
-
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
1045
|
-
function usePrefersReducedMotion() {
|
|
1046
|
-
const [prefersReducedMotion, setPrefersReducedMotion] = useState(
|
|
1047
|
-
() => reducedMotionMediaQuery()?.matches ?? false
|
|
1048
|
-
);
|
|
1049
|
-
useEffect(() => {
|
|
1050
|
-
const mediaQuery = reducedMotionMediaQuery();
|
|
1051
|
-
if (!mediaQuery) return;
|
|
1052
|
-
const updatePreference = () => setPrefersReducedMotion(mediaQuery.matches);
|
|
1053
|
-
updatePreference();
|
|
1054
|
-
mediaQuery.addEventListener("change", updatePreference);
|
|
1055
|
-
return () => mediaQuery.removeEventListener("change", updatePreference);
|
|
1056
|
-
}, []);
|
|
1057
|
-
return prefersReducedMotion;
|
|
1058
|
-
}
|
|
1059
|
-
function reducedMotionMediaQuery() {
|
|
1060
|
-
return typeof window === "undefined" || typeof window.matchMedia !== "function" ? void 0 : window.matchMedia(REDUCED_MOTION_QUERY);
|
|
1061
|
-
}
|
|
1062
389
|
const CATEGORY_PRESENTATION = {
|
|
1063
390
|
queued: { cue: "○" },
|
|
1064
391
|
active: { cue: "▶" },
|
|
@@ -1241,7 +568,6 @@ function createRecordingProjectionCache() {
|
|
|
1241
568
|
function FactoryRecordingTopologyReplay({
|
|
1242
569
|
defaultSelectedTick,
|
|
1243
570
|
formatNumber,
|
|
1244
|
-
layout,
|
|
1245
571
|
messages,
|
|
1246
572
|
onError,
|
|
1247
573
|
onSelectNode,
|
|
@@ -1285,7 +611,6 @@ function FactoryRecordingTopologyReplay({
|
|
|
1285
611
|
{
|
|
1286
612
|
defaultSelectedTick,
|
|
1287
613
|
formatNumber,
|
|
1288
|
-
layout,
|
|
1289
614
|
messages,
|
|
1290
615
|
onError,
|
|
1291
616
|
onSelectNode,
|
|
@@ -1298,7 +623,6 @@ function FactoryRecordingTopologyReplay({
|
|
|
1298
623
|
function ValidatedRecordingReplay({
|
|
1299
624
|
defaultSelectedTick,
|
|
1300
625
|
formatNumber,
|
|
1301
|
-
layout,
|
|
1302
626
|
messages,
|
|
1303
627
|
onError,
|
|
1304
628
|
onSelectNode,
|
|
@@ -1364,20 +688,23 @@ function ValidatedRecordingReplay({
|
|
|
1364
688
|
/* @__PURE__ */ jsx(
|
|
1365
689
|
FactoryTopologyReplay,
|
|
1366
690
|
{
|
|
1367
|
-
layout,
|
|
1368
691
|
messages: messages.topology,
|
|
1369
692
|
onError,
|
|
1370
693
|
onSelectNode,
|
|
1371
694
|
selectedNodeId,
|
|
1372
695
|
state: {
|
|
1373
|
-
...prepared.topology.nodes.length === 0 ? { status: "empty" } : {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
696
|
+
...prepared.topology.nodes.length === 0 ? { status: "empty" } : recording.factory ? {
|
|
697
|
+
source: createFactoryGraphSource({
|
|
698
|
+
factory: recording.factory,
|
|
699
|
+
runtime: {
|
|
700
|
+
activity: prepared.activity,
|
|
701
|
+
load: prepared.load,
|
|
702
|
+
topology: prepared.topology
|
|
703
|
+
},
|
|
704
|
+
selectedTick
|
|
705
|
+
}),
|
|
1379
706
|
status: "ready"
|
|
1380
|
-
}
|
|
707
|
+
} : { status: "failed" }
|
|
1381
708
|
}
|
|
1382
709
|
}
|
|
1383
710
|
),
|
|
@@ -1457,13 +784,10 @@ function useDistinctVisualizerErrorReport(error, onError) {
|
|
|
1457
784
|
}, [error, onError]);
|
|
1458
785
|
}
|
|
1459
786
|
export {
|
|
1460
|
-
DEFAULT_FACTORY_TOPOLOGY_CHROME_PRESET,
|
|
1461
787
|
FactoryEmulatorControls,
|
|
1462
788
|
FactoryEmulatorView,
|
|
1463
789
|
FactoryRecordingTopologyReplay,
|
|
1464
790
|
FactoryTimelineScrubber,
|
|
1465
791
|
FactoryTopologyReplay,
|
|
1466
|
-
WorkProgressVisualizer
|
|
1467
|
-
projectFactoryTopologyFlow,
|
|
1468
|
-
resolveFactoryTopologyChrome
|
|
792
|
+
WorkProgressVisualizer
|
|
1469
793
|
};
|