@stealthscale/component-graphs 0.1.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.
@@ -0,0 +1,632 @@
1
+ import { defineSlotRecipe, dense, onSlots, ratioVariants, truncate } from "@stealthscale/theme/authoring";
2
+ //#region src/graph/disc.ts
3
+ /**
4
+ * Styles a network's node for the graph recipe: a disc with the node's icon, and its name under
5
+ * it.
6
+ *
7
+ * @remarks
8
+ * The disc is as wide as its `--graph-disc`, which the preset writes from the node's weight, and
9
+ * is a circle from the theme's panel with a hairline edge. The name is one line on a pill in the
10
+ * panel's color, so an edge under it does not cross the letters. The node's box is the disc and
11
+ * the name, so the view's fit and the overview map include the name. The node's handles are in a
12
+ * hub, a line of no height half a handle below the disc's middle, so the top of each handle, the
13
+ * point React Flow starts a top handle's edges at, is the disc's centre. The hub is hidden, and
14
+ * React Flow still measures the handles in it. A selected disc takes the focus ink on its edge,
15
+ * and a dimmed node, `data-dimmed`, recedes by a dashed edge and a muted name, not by opacity on
16
+ * its words.
17
+ */
18
+ /**
19
+ * Styles of the node's box: the disc above its name, centred.
20
+ */
21
+ const DISC_NODE = {
22
+ alignItems: "center",
23
+ display: "flex",
24
+ flexDirection: "column",
25
+ gap: "1",
26
+ position: "relative"
27
+ };
28
+ /**
29
+ * Styles of the disc: a circle as wide as `--graph-disc` with the node's icon in its middle.
30
+ */
31
+ const DISC = {
32
+ ".react-flow__node.selected &": {
33
+ _highContrast: { borderColor: "Highlight" },
34
+ borderColor: "border.focus",
35
+ boxShadow: "0 0 0 {borderWidths.hairline} {colors.border.focus}"
36
+ },
37
+ "[data-dimmed] > &": {
38
+ borderStyle: "dashed",
39
+ boxShadow: "none"
40
+ },
41
+ "& > svg": { boxSize: "icon.sm" },
42
+ alignItems: "center",
43
+ background: "bg.panel",
44
+ borderColor: "border",
45
+ borderRadius: "full",
46
+ borderStyle: "solid",
47
+ borderWidth: "hairline",
48
+ boxShadow: "xs",
49
+ boxSize: "var(--graph-disc)",
50
+ color: "fg.muted",
51
+ display: "flex",
52
+ justifyContent: "center",
53
+ position: "relative"
54
+ };
55
+ /**
56
+ * Styles of the hub the handles are in: a hidden line of no height, half a handle below the disc's
57
+ * middle.
58
+ */
59
+ const DISC_HUB = {
60
+ blockSize: "0",
61
+ insetBlockStart: "calc(50% + {sizes.2.5} / 2)",
62
+ insetInline: "0",
63
+ position: "absolute",
64
+ visibility: "hidden"
65
+ };
66
+ /**
67
+ * Styles of the name under the disc: one line on a pill in the panel's color.
68
+ */
69
+ const DISC_LABEL = {
70
+ "[data-dimmed] > &": {
71
+ color: "fg.muted",
72
+ fontWeight: "normal"
73
+ },
74
+ background: "bg.panel",
75
+ borderRadius: "l1",
76
+ color: "fg",
77
+ fontWeight: "medium",
78
+ paddingInline: "1",
79
+ textStyle: "body.xs",
80
+ whiteSpace: "nowrap"
81
+ };
82
+ //#endregion
83
+ //#region src/graph/flow.ts
84
+ /**
85
+ * Restyles the classes React Flow writes, for the graph recipe's canvas.
86
+ *
87
+ * @remarks
88
+ * React Flow places its layers by these rules and ships them as `base.css`. A component imports
89
+ * no stylesheet, so the canvas states the rules the kit needs, with the theme's tokens for every
90
+ * color: the pane under the viewport, the nodes and edges above it, the label layer the edges'
91
+ * controls render in, the corner panels, and the attribution link. The layers keep React Flow's
92
+ * own order inside the canvas, which isolates them from the page. An edge is the muted ink, and a
93
+ * selected or focused edge is the focus ink twice as wide. An edge on a traced path,
94
+ * `data-trace="on"`, is the ink twice as wide, and an edge off it fades to the theme's backdrop
95
+ * opacity. An added edge, `data-change="added"`, is the success ink twice as wide, a removed edge
96
+ * the error ink dashed and twice as wide, and an unchanged edge fades, so the dash tells a
97
+ * removed edge apart under forced colors too. An edge's `--graph-strength` multiplies both
98
+ * widths, 1 unless set, so a network's stronger link renders wider. A handle is a dot in the
99
+ * muted ink inside a ring in the panel's color, and a handle no edge meets, `data-unused`, is
100
+ * invisible and still measured. Under forced colors the edges and the handles take `CanvasText`,
101
+ * and a selected edge and a handle that takes connections `Highlight`. The viewport is hidden
102
+ * while React Flow's element has `data-placing`, which a preset sets until its nodes are laid out
103
+ * by their measured sizes.
104
+ */
105
+ /**
106
+ * Rules of React Flow's own layers: where each is placed, what takes the pointer, and in which
107
+ * order.
108
+ */
109
+ const LAYERS = {
110
+ "& .react-flow": {
111
+ direction: "ltr",
112
+ inlineSize: "full",
113
+ position: "relative"
114
+ },
115
+ "& .react-flow__background": {
116
+ pointerEvents: "none",
117
+ zIndex: "-1"
118
+ },
119
+ "& .react-flow__background-pattern.dots": {
120
+ _highContrast: { fill: "GrayText" },
121
+ fill: "border"
122
+ },
123
+ "& .react-flow__container": {
124
+ blockSize: "full",
125
+ inlineSize: "full",
126
+ insetBlockStart: "0",
127
+ insetInlineStart: "0",
128
+ position: "absolute"
129
+ },
130
+ "& .react-flow__edgelabel-renderer": {
131
+ blockSize: "full",
132
+ inlineSize: "full",
133
+ insetBlockStart: "0",
134
+ insetInlineStart: "0",
135
+ pointerEvents: "none",
136
+ position: "absolute",
137
+ userSelect: "none"
138
+ },
139
+ "& .react-flow__nodes": {
140
+ pointerEvents: "none",
141
+ transformOrigin: "0 0"
142
+ },
143
+ "& .react-flow__pane": {
144
+ touchAction: "none",
145
+ zIndex: "1"
146
+ },
147
+ "& .react-flow__pane.draggable": { cursor: "pan" },
148
+ "& .react-flow__pane.dragging": { cursor: "dragging" },
149
+ "& .react-flow__renderer": { zIndex: "4" },
150
+ "& .react-flow__selection": { zIndex: "6" },
151
+ "& .react-flow__viewport": {
152
+ pointerEvents: "none",
153
+ transformOrigin: "0 0",
154
+ zIndex: "2"
155
+ },
156
+ "& .react-flow[data-placing] .react-flow__viewport": { visibility: "hidden" }
157
+ };
158
+ /**
159
+ * Rules of the nodes: React Flow's wrapper places each node, takes the pointer and the focus ring.
160
+ */
161
+ const NODES = {
162
+ "& .react-flow__node": {
163
+ boxSizing: "border-box",
164
+ focusRingColor: "border.focus",
165
+ focusVisibleRing: "outside",
166
+ pointerEvents: "all",
167
+ position: "absolute",
168
+ transformOrigin: "0 0",
169
+ userSelect: "none"
170
+ },
171
+ "& .react-flow__node.draggable": { cursor: "drag" },
172
+ "& .react-flow__node.draggable.dragging": { cursor: "dragging" },
173
+ "& .react-flow__node.selectable": { cursor: "button" }
174
+ };
175
+ /**
176
+ * Rules of the edges and of the line a connection in progress renders.
177
+ */
178
+ const EDGES = {
179
+ "& .react-flow__arrowhead polyline": {
180
+ _highContrast: { stroke: "CanvasText" },
181
+ stroke: "fg.muted"
182
+ },
183
+ "& .react-flow__arrowhead polyline.arrowclosed": {
184
+ _highContrast: { fill: "CanvasText" },
185
+ fill: "fg.muted"
186
+ },
187
+ "& .react-flow__connection": { pointerEvents: "none" },
188
+ "& .react-flow__connection-path": {
189
+ _highContrast: { stroke: "Highlight" },
190
+ fill: "none",
191
+ stroke: "border.focus",
192
+ strokeWidth: "ring"
193
+ },
194
+ "& .react-flow__connectionline": {
195
+ overflow: "visible",
196
+ position: "absolute",
197
+ zIndex: "1001"
198
+ },
199
+ "& .react-flow__edge": {
200
+ outline: "none",
201
+ pointerEvents: "visibleStroke"
202
+ },
203
+ "& .react-flow__edge-path": {
204
+ _highContrast: { stroke: "CanvasText" },
205
+ fill: "none",
206
+ stroke: "fg.muted",
207
+ strokeWidth: "calc({borderWidths.hairline} * var(--graph-strength, 1))",
208
+ transition: "stroke {durations.fast} {easings.out}, opacity {durations.fast} {easings.out}"
209
+ },
210
+ "& .react-flow__edge:is(.selected, :focus-visible) .react-flow__edge-path": {
211
+ _highContrast: { stroke: "Highlight" },
212
+ stroke: "border.focus",
213
+ strokeWidth: "ring"
214
+ },
215
+ "& .react-flow__edge.inactive": { pointerEvents: "none" },
216
+ "& .react-flow__edge.selectable": { cursor: "button" },
217
+ "& .react-flow__edge[data-change=added] .react-flow__edge-path": {
218
+ _highContrast: { stroke: "CanvasText" },
219
+ stroke: "border.success",
220
+ strokeWidth: "calc({borderWidths.ring} * var(--graph-strength, 1))"
221
+ },
222
+ "& .react-flow__edge[data-change=removed] .react-flow__edge-path": {
223
+ _highContrast: { stroke: "CanvasText" },
224
+ stroke: "border.error",
225
+ strokeDasharray: "{spacing.1.5} {spacing.1}",
226
+ strokeWidth: "calc({borderWidths.ring} * var(--graph-strength, 1))"
227
+ },
228
+ "& .react-flow__edge[data-change=unchanged]": { opacity: "backdrop" },
229
+ "& .react-flow__edge[data-trace=off]": { opacity: "backdrop" },
230
+ "& .react-flow__edge[data-trace=on] .react-flow__edge-path": {
231
+ _highContrast: { stroke: "CanvasText" },
232
+ stroke: "fg",
233
+ strokeWidth: "calc({borderWidths.ring} * var(--graph-strength, 1))"
234
+ },
235
+ "& .react-flow .react-flow__edges": { position: "absolute" },
236
+ "& .react-flow .react-flow__edges svg": {
237
+ overflow: "visible",
238
+ pointerEvents: "none",
239
+ position: "absolute"
240
+ }
241
+ };
242
+ /**
243
+ * Rules of a handle, which React Flow places on the side of its node that `position` names.
244
+ */
245
+ const HANDLES = {
246
+ "& .react-flow__handle": {
247
+ _highContrast: {
248
+ background: "CanvasText",
249
+ borderColor: "Canvas"
250
+ },
251
+ background: "fg.muted",
252
+ borderColor: "bg.panel",
253
+ borderRadius: "full",
254
+ borderStyle: "solid",
255
+ borderWidth: "ring",
256
+ boxSize: "2.5",
257
+ pointerEvents: "none",
258
+ position: "absolute"
259
+ },
260
+ "& .react-flow__handle-bottom": {
261
+ insetBlockEnd: "0",
262
+ left: "var(--graph-port-along, 50%)",
263
+ transform: "translate(-50%, 50%)"
264
+ },
265
+ "& .react-flow__handle-left": {
266
+ left: "0",
267
+ top: "var(--graph-port-along, 50%)",
268
+ transform: "translate(-50%, -50%)"
269
+ },
270
+ "& .react-flow__handle-right": {
271
+ right: "0",
272
+ top: "var(--graph-port-along, 50%)",
273
+ transform: "translate(50%, -50%)"
274
+ },
275
+ "& .react-flow__handle-top": {
276
+ insetBlockStart: "0",
277
+ left: "var(--graph-port-along, 50%)",
278
+ transform: "translate(-50%, -50%)"
279
+ },
280
+ "& .react-flow__handle.connectable": {
281
+ _highContrast: { background: "Highlight" },
282
+ background: "border.focus",
283
+ pointerEvents: "all"
284
+ },
285
+ "& .react-flow__handle.connectionindicator": { cursor: "connect" },
286
+ "& .react-flow__handle[data-unused]": { visibility: "hidden" }
287
+ };
288
+ /**
289
+ * Rules of the corner panels: the attribution and any panel of the caller's.
290
+ */
291
+ const PANELS = {
292
+ "& .react-flow__attribution": {
293
+ background: "bg.panel",
294
+ color: "fg.muted",
295
+ margin: "0",
296
+ paddingInline: "1",
297
+ textStyle: "body.xs"
298
+ },
299
+ "& .react-flow__attribution a": {
300
+ color: "fg.muted",
301
+ textDecoration: "none"
302
+ },
303
+ "& .react-flow__panel": {
304
+ margin: "3",
305
+ position: "absolute",
306
+ zIndex: "5"
307
+ },
308
+ "& .react-flow__panel.bottom": { insetBlockEnd: "0" },
309
+ "& .react-flow__panel.left": { left: "0" },
310
+ "& .react-flow__panel.right": { right: "0" },
311
+ "& .react-flow__panel.top": { insetBlockStart: "0" }
312
+ };
313
+ /**
314
+ * Rules the canvas slot takes: React Flow's layers, nodes, edges, handles and panels.
315
+ */
316
+ const FLOW = {
317
+ ...LAYERS,
318
+ ...NODES,
319
+ ...EDGES,
320
+ ...HANDLES,
321
+ ...PANELS
322
+ };
323
+ //#endregion
324
+ //#region src/graph/recipe.ts
325
+ /**
326
+ * Styles a graph's figure, canvas, controls, overview map, summary, empty state and nodes, and
327
+ * restyles the classes React Flow writes with the theme's tokens.
328
+ *
329
+ * @remarks
330
+ * The canvas is a panel as wide as the figure and as tall as its ratio, because React Flow
331
+ * measures its box after the first render and renders nothing in a box 0 pixels high. The empty
332
+ * state takes the canvas's place at the same ratio. The controls are a row at the figure's start
333
+ * above the canvas, and the overview map a panel at its end below the canvas, so neither covers a
334
+ * node. A node is a card 256px wide, the width `layoutGraph` places a node by, with a header of
335
+ * an icon, a title, a subtitle, a status and actions, a body, and a problem line in the error
336
+ * ink. A tag renders above the card's top edge at its end, and a branch control straddles the
337
+ * middle of its bottom edge, where the card keeps half the control's height free. A network's
338
+ * node is a disc with its name under it instead of a card. A selected node takes the focus ink
339
+ * on its edge, and a node the canvas dims recedes by a dashed edge and a muted title, not by
340
+ * opacity on its words. A port's name is written beside its handle, outside
341
+ * the node, on the side the handle is on. The recipe has no `palette` axis, because a graph is
342
+ * not a control and its colors are the theme's roles.
343
+ */
344
+ /**
345
+ * Styles of the overview map's box: a panel 176 by 112 pixels at the end of the figure, the map's
346
+ * nodes in the muted ink and the part out of view behind a wash.
347
+ */
348
+ const OVERVIEW = {
349
+ "& .react-flow__minimap-mask": {
350
+ fill: "bg.muted",
351
+ fillOpacity: "{opacity.muted}",
352
+ stroke: "none"
353
+ },
354
+ "& .react-flow__minimap-node": {
355
+ _highContrast: { fill: "CanvasText" },
356
+ fill: "fg.muted"
357
+ },
358
+ "& .react-flow__minimap-svg": {
359
+ blockSize: "28",
360
+ display: "block",
361
+ inlineSize: "44"
362
+ },
363
+ background: "bg.panel",
364
+ borderColor: "border",
365
+ borderRadius: "l2",
366
+ borderStyle: "solid",
367
+ borderWidth: "hairline",
368
+ marginInlineStart: "auto",
369
+ overflow: "hidden"
370
+ };
371
+ /**
372
+ * Rule of the hairline that parts a node's body and problem line from its header.
373
+ */
374
+ const RULED = {
375
+ borderBlockStartColor: "border",
376
+ borderBlockStartStyle: "solid",
377
+ borderBlockStartWidth: "hairline",
378
+ paddingBlock: dense("{spacing.inset.xs}"),
379
+ paddingInline: dense("{spacing.inset.sm}"),
380
+ textStyle: "body.xs"
381
+ };
382
+ /**
383
+ * Defines the graph recipe: a figure whose canvas is as wide as the figure and as tall as its
384
+ * ratio, by default the video ratio.
385
+ */
386
+ const recipe = defineSlotRecipe({
387
+ base: {
388
+ canvas: {
389
+ ...FLOW,
390
+ background: "bg.panel",
391
+ borderColor: "border",
392
+ borderRadius: "l2",
393
+ borderStyle: "solid",
394
+ borderWidth: "hairline",
395
+ inlineSize: "full",
396
+ isolation: "isolate",
397
+ minInlineSize: "0",
398
+ overflow: "hidden",
399
+ position: "relative"
400
+ },
401
+ caption: {
402
+ color: "fg.muted",
403
+ textStyle: "body.sm"
404
+ },
405
+ controls: {
406
+ alignItems: "center",
407
+ alignSelf: "flex-start",
408
+ display: "flex",
409
+ gap: dense("{spacing.gap.xs}"),
410
+ maxInlineSize: "full"
411
+ },
412
+ disc: DISC,
413
+ discHub: DISC_HUB,
414
+ discLabel: DISC_LABEL,
415
+ discNode: DISC_NODE,
416
+ empty: {
417
+ alignItems: "center",
418
+ background: "bg.panel",
419
+ borderColor: "border",
420
+ borderRadius: "l2",
421
+ borderStyle: "dashed",
422
+ borderWidth: "hairline",
423
+ color: "fg.muted",
424
+ display: "flex",
425
+ inlineSize: "full",
426
+ justifyContent: "center",
427
+ padding: dense("{spacing.inset.md}"),
428
+ textAlign: "center",
429
+ textStyle: "body.sm"
430
+ },
431
+ level: {
432
+ color: "fg.muted",
433
+ fontVariantNumeric: "tabular-nums",
434
+ minInlineSize: "10",
435
+ paddingInline: dense("{spacing.inset.xs}"),
436
+ textAlign: "center",
437
+ textStyle: "body.xs"
438
+ },
439
+ node: {
440
+ ".react-flow__node.selected > &": {
441
+ _highContrast: { borderColor: "Highlight" },
442
+ borderColor: "border.focus",
443
+ boxShadow: "0 0 0 {borderWidths.hairline} {colors.border.focus}"
444
+ },
445
+ "&:has(> .graph__nodeBranch)": { paddingBlockEnd: `calc(${dense("{sizes.control.xs}")} / 2)` },
446
+ "&[data-dimmed]": {
447
+ borderStyle: "dashed",
448
+ boxShadow: "none"
449
+ },
450
+ "&[data-invalid]": { borderColor: "border.error" },
451
+ background: "bg.panel",
452
+ borderColor: "border",
453
+ borderRadius: "l2",
454
+ borderStyle: "solid",
455
+ borderWidth: "hairline",
456
+ boxShadow: "xs",
457
+ color: "fg",
458
+ display: "flex",
459
+ flexDirection: "column",
460
+ inlineSize: "64",
461
+ position: "relative",
462
+ textAlign: "start",
463
+ textStyle: "body.sm"
464
+ },
465
+ nodeActions: {
466
+ alignItems: "center",
467
+ display: "flex",
468
+ flexShrink: "0"
469
+ },
470
+ nodeBody: {
471
+ ...RULED,
472
+ color: "fg.muted"
473
+ },
474
+ nodeBranch: {
475
+ display: "inline-flex",
476
+ insetBlockEnd: "0",
477
+ left: "50%",
478
+ position: "absolute",
479
+ translate: "-50% 50%"
480
+ },
481
+ nodeHeader: {
482
+ alignItems: "flex-start",
483
+ display: "flex",
484
+ gap: dense("{spacing.gap.sm}"),
485
+ paddingBlock: dense("{spacing.inset.xs}"),
486
+ paddingInline: dense("{spacing.inset.sm}")
487
+ },
488
+ nodeIcon: {
489
+ "& > svg": { boxSize: "icon.sm" },
490
+ color: "fg.muted",
491
+ display: "inline-flex",
492
+ flexShrink: "0",
493
+ marginBlockStart: "0.5"
494
+ },
495
+ nodeProblem: {
496
+ ...RULED,
497
+ color: "fg.error"
498
+ },
499
+ nodeSubtitle: {
500
+ ...truncate(),
501
+ color: "fg.muted",
502
+ textStyle: "body.xs"
503
+ },
504
+ nodeTag: {
505
+ display: "inline-flex",
506
+ insetBlockEnd: "100%",
507
+ insetInlineEnd: dense("{spacing.inset.sm}"),
508
+ marginBlockEnd: "1",
509
+ pointerEvents: "none",
510
+ position: "absolute"
511
+ },
512
+ nodeText: {
513
+ display: "flex",
514
+ flex: "1",
515
+ flexDirection: "column",
516
+ minInlineSize: "0"
517
+ },
518
+ nodeTitle: {
519
+ ...truncate(),
520
+ "[data-dimmed] &": {
521
+ color: "fg.muted",
522
+ fontWeight: "normal"
523
+ },
524
+ fontWeight: "medium"
525
+ },
526
+ overview: OVERVIEW,
527
+ portName: {
528
+ ".react-flow__handle-bottom > &": {
529
+ insetBlockStart: "100%",
530
+ left: "50%",
531
+ marginBlockStart: "1",
532
+ translate: "-50% 0"
533
+ },
534
+ ".react-flow__handle-left > &": {
535
+ marginInlineEnd: "1.5",
536
+ right: "100%",
537
+ top: "50%",
538
+ translate: "0 -50%"
539
+ },
540
+ ".react-flow__handle-right > &": {
541
+ left: "100%",
542
+ marginInlineStart: "1.5",
543
+ top: "50%",
544
+ translate: "0 -50%"
545
+ },
546
+ ".react-flow__handle-top > &": {
547
+ insetBlockEnd: "100%",
548
+ left: "50%",
549
+ marginBlockEnd: "1",
550
+ translate: "-50% 0"
551
+ },
552
+ "&[data-hidden]": { srOnly: true },
553
+ background: "bg.panel",
554
+ borderRadius: "l1",
555
+ color: "fg.muted",
556
+ paddingInline: "1",
557
+ pointerEvents: "none",
558
+ position: "absolute",
559
+ textStyle: "body.xs",
560
+ whiteSpace: "nowrap"
561
+ },
562
+ remove: {
563
+ display: "inline-flex",
564
+ left: "0",
565
+ pointerEvents: "all",
566
+ position: "absolute",
567
+ top: "0",
568
+ transform: "translate(-50%, -50%) translate(var(--graph-edge-x), var(--graph-edge-y))"
569
+ },
570
+ root: {
571
+ display: "flex",
572
+ flexDirection: "column",
573
+ gap: dense("{spacing.gap.md}"),
574
+ inlineSize: "full",
575
+ margin: "0",
576
+ minInlineSize: "0"
577
+ },
578
+ summary: {
579
+ alignItems: "center",
580
+ color: "fg.muted",
581
+ display: "flex",
582
+ flexWrap: "wrap",
583
+ gap: dense("{spacing.gap.sm}"),
584
+ textStyle: "body.sm"
585
+ }
586
+ },
587
+ className: "graph",
588
+ defaultVariants: { ratio: "video" },
589
+ jsx: [/^Graph(\.\w+)?$/u],
590
+ slots: [
591
+ "root",
592
+ "canvas",
593
+ "controls",
594
+ "level",
595
+ "caption",
596
+ "node",
597
+ "nodeHeader",
598
+ "nodeIcon",
599
+ "nodeText",
600
+ "nodeTitle",
601
+ "nodeSubtitle",
602
+ "nodeActions",
603
+ "nodeBody",
604
+ "nodeProblem",
605
+ "nodeTag",
606
+ "nodeBranch",
607
+ "discNode",
608
+ "disc",
609
+ "discHub",
610
+ "discLabel",
611
+ "portName",
612
+ "remove",
613
+ "overview",
614
+ "summary",
615
+ "empty"
616
+ ],
617
+ variants: {
618
+ /**
619
+ * Aspect ratio of the canvas, one of the theme's ratios, which the empty state takes in the
620
+ * canvas's place.
621
+ *
622
+ * @remarks
623
+ * React Flow measures the canvas's box after the first render and renders nothing in a box 0
624
+ * pixels high. The ratio gives the box its height before that measurement.
625
+ */
626
+ ratio: onSlots({
627
+ canvas: ratioVariants(),
628
+ empty: ratioVariants()
629
+ }) }
630
+ });
631
+ //#endregion
632
+ export { recipe as t };
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -0,0 +1,11 @@
1
+ //#region src/theme.d.ts
2
+ /**
3
+ * Registers every recipe in the package in the preset an application's style compiler installs.
4
+ *
5
+ * @remarks
6
+ * The list is written by hand. `theme.spec.ts` fails when a recipe file is missing from it, so no
7
+ * generator writes it.
8
+ */
9
+ declare const _default: import("@stealthscale/theme/authoring").Preset;
10
+ //#endregion
11
+ export { _default as default };
package/dist/theme.js ADDED
@@ -0,0 +1,16 @@
1
+ import { t as recipe } from "./recipe-BkmwNoaQ.js";
2
+ import { definePreset } from "@stealthscale/theme/authoring";
3
+ //#region src/theme.ts
4
+ /**
5
+ * Registers every recipe in the package in the preset an application's style compiler installs.
6
+ *
7
+ * @remarks
8
+ * The list is written by hand. `theme.spec.ts` fails when a recipe file is missing from it, so no
9
+ * generator writes it.
10
+ */
11
+ var theme_default = definePreset({
12
+ name: "@stealthscale/component-graphs",
13
+ theme: { extend: { slotRecipes: { graph: recipe } } }
14
+ });
15
+ //#endregion
16
+ export { theme_default as default };