@dickpy/dsh-imagegen 1.5.8 → 1.5.9

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.
@@ -1,1189 +1,1187 @@
1
- /* Infinite canvas workspace — node-graph canvas styled after
2
- * basketikun/infinite-canvas: floating docks, card nodes, wire connections. */
3
-
4
- .root {
5
- position: relative;
6
- display: flex;
7
- flex: 1 1 auto;
8
- flex-direction: column;
9
- height: 100%;
10
- min-width: 0;
11
- min-height: 0;
12
- overflow: hidden;
13
- border: 1px solid var(--dsw-alias-border-l1);
14
- border-radius: 10px;
15
- background: var(--dsw-alias-bg-layer-1);
16
- color: var(--dsw-alias-label-primary);
17
- font-family: var(--dsw-font-family, inherit);
18
- user-select: none;
19
- }
20
-
21
- .selectCursor { cursor: default; }
22
- .panCursor { cursor: grab; }
23
- .panCursor:active { cursor: grabbing; }
24
-
25
- /* ------------------------------------------------------------- top bar */
26
-
27
- .topBar {
28
- display: flex;
29
- align-items: center;
30
- gap: 8px;
31
- padding: 8px 12px;
32
- border-bottom: 1px solid var(--dsw-alias-border-l1);
33
- flex: none;
34
- z-index: 40;
35
- background: var(--dsw-alias-bg-layer-1);
36
- }
37
-
38
- .projectSelect {
39
- max-width: 180px;
40
- height: 30px;
41
- padding: 0 8px;
42
- border-radius: 8px;
43
- border: 1px solid var(--dsw-alias-border-l1);
44
- background: transparent;
45
- color: var(--dsw-alias-label-primary);
46
- font-size: 12.5px;
47
- }
48
-
49
- .titleButton {
50
- height: 30px;
51
- padding: 0 10px;
52
- border: 1px solid transparent;
53
- border-radius: 8px;
54
- background: transparent;
55
- color: var(--dsw-alias-label-primary);
56
- font-size: 13px;
57
- font-weight: 600;
58
- max-width: 260px;
59
- overflow: hidden;
60
- text-overflow: ellipsis;
61
- white-space: nowrap;
62
- cursor: text;
63
- }
64
-
65
- .titleButton:hover { border-color: var(--dsw-alias-border-l1); }
66
-
67
- .titleInput {
68
- height: 30px;
69
- padding: 0 10px;
70
- border-radius: 8px;
71
- border: 1px solid var(--dsw-alias-brand-primary);
72
- background: transparent;
73
- color: var(--dsw-alias-label-primary);
74
- font-size: 13px;
75
- font-weight: 600;
76
- width: 240px;
77
- outline: none;
78
- }
79
-
80
- .topBarSpacer { flex: 1; }
81
-
82
- .saveState {
83
- font-size: 11.5px;
84
- color: var(--dsw-alias-label-tertiary);
85
- white-space: nowrap;
86
- }
87
-
88
- .saveState[data-state="error"] { color: var(--dsw-alias-label-error); }
89
- .saveState[data-state="saving"] { color: var(--dsw-alias-state-warn-primary, var(--dsw-alias-label-secondary)); }
90
-
91
- /* ---------------------------------------------------------- icon button */
92
-
93
- .iconButton {
94
- display: inline-flex;
95
- align-items: center;
96
- justify-content: center;
97
- gap: 5px;
98
- width: 30px;
99
- height: 30px;
100
- flex: none;
101
- border: none;
102
- border-radius: 8px;
103
- background: transparent;
104
- color: var(--dsw-alias-label-secondary);
105
- cursor: pointer;
106
- padding: 0;
107
- }
108
-
109
- .iconButton:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
110
- .iconButton[data-active] { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 16%, transparent); color: var(--dsw-alias-brand-primary); }
111
- .iconButton:disabled { opacity: 0.35; cursor: not-allowed; }
112
- .iconButton:disabled:hover { background: transparent; color: var(--dsw-alias-label-secondary); }
113
-
114
- /* (node tools moved to the bottom-center dock above) */
115
-
116
- /* ------------------------------------------------------------- viewport */
117
-
118
- .viewport {
119
- position: relative;
120
- flex: 1;
121
- min-height: 0;
122
- overflow: hidden;
123
- touch-action: none;
124
- }
125
-
126
- .grid {
127
- position: absolute;
128
- inset: 0;
129
- pointer-events: none;
130
- opacity: 0.5;
131
- }
132
-
133
- /* Interactive fluid flowmap dot field lives in a <canvas>; aurora is pure CSS.
134
- * Both render themselves at full strength (the 0.5 grid dimming stays off). */
135
- .flowCanvas {
136
- position: absolute;
137
- inset: 0;
138
- width: 100%;
139
- height: 100%;
140
- }
141
-
142
- .grid[data-mode="flow"],
143
- .grid[data-mode="aurora"] {
144
- opacity: 1;
145
- }
146
-
147
- .grid[data-mode="aurora"] {
148
- background-image:
149
- radial-gradient(560px 380px at 22% 28%, color-mix(in srgb, var(--dsw-alias-brand-primary) 24%, transparent), transparent 72%),
150
- radial-gradient(680px 480px at 78% 62%, rgb(167 139 250 / 20%), transparent 72%),
151
- radial-gradient(520px 400px at 48% 88%, rgb(34 211 238 / 17%), transparent 74%);
152
- background-size: 220% 220%;
153
- background-position: 0% 0%;
154
- animation: auroraDrift 20s ease-in-out infinite alternate;
155
- }
156
-
157
- @keyframes auroraDrift {
158
- from { background-position: 0% 0%; }
159
- to { background-position: 100% 100%; }
160
- }
161
-
162
- @media (prefers-reduced-motion: reduce) {
163
- .grid[data-mode="aurora"] { animation: none; }
164
- }
165
-
166
- .grid[data-mode="dots"] {
167
- background-image: radial-gradient(circle, color-mix(in srgb, var(--dsw-alias-label-primary) 30%, transparent) 1.1px, transparent 1.4px);
168
- }
169
-
170
- .grid[data-mode="lines"] {
171
- background-image:
172
- linear-gradient(color-mix(in srgb, var(--dsw-alias-label-primary) 9%, transparent) 1px, transparent 1px),
173
- linear-gradient(90deg, color-mix(in srgb, var(--dsw-alias-label-primary) 9%, transparent) 1px, transparent 1px);
174
- }
175
-
176
- .grid[data-mode="blank"] { background-image: none; }
177
-
178
- .grid[data-mode="diagonal"] {
179
- background-image: repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent) 0 1px, transparent 1px 16px);
180
- }
181
-
182
- .grid[data-mode="checker"] {
183
- background-image:
184
- conic-gradient(color-mix(in srgb, var(--dsw-alias-label-primary) 7%, transparent) 90deg, transparent 90deg 180deg, color-mix(in srgb, var(--dsw-alias-label-primary) 7%, transparent) 180deg 270deg, transparent 270deg);
185
- background-size: 48px 48px;
186
- }
187
-
188
- .grid[data-mode="image"] { background-image: none; }
189
-
190
- .gridScrim {
191
- position: absolute;
192
- inset: 0;
193
- background: var(--dsw-alias-bg-layer-1);
194
- opacity: 0.6;
195
- }
196
-
197
- /* ------------------------------------------------ background menu */
198
-
199
- .backgroundMenu {
200
- position: absolute;
201
- transform: translate(-50%, -100%);
202
- z-index: 45;
203
- min-width: 168px;
204
- padding: 5px;
205
- border-radius: 11px;
206
- border: 1px solid var(--dsw-alias-border-l1);
207
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 96%, transparent);
208
- box-shadow: 0 16px 40px rgb(15 23 42 / 20%);
209
- display: flex;
210
- flex-direction: column;
211
- }
212
-
213
- .backgroundMenu button {
214
- display: flex;
215
- align-items: center;
216
- justify-content: flex-start;
217
- border: none;
218
- border-radius: 7px;
219
- background: transparent;
220
- color: var(--dsw-alias-label-primary);
221
- font-size: 12.5px;
222
- padding: 7px 10px;
223
- cursor: pointer;
224
- text-align: left;
225
- }
226
-
227
- .backgroundMenu button:hover { background: var(--dsw-alias-interactive-bg-hover); }
228
-
229
- .backgroundMenu button[data-active] {
230
- color: var(--dsw-alias-brand-primary);
231
- background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);
232
- }
233
-
234
- .backgroundMenuDivider {
235
- height: 1px;
236
- margin: 4px 6px;
237
- background: var(--dsw-alias-border-l1);
238
- }
239
-
240
- .world {
241
- position: absolute;
242
- left: 0;
243
- top: 0;
244
- transform-origin: 0 0;
245
- }
246
-
247
- .marquee {
248
- position: absolute;
249
- z-index: 20;
250
- border: 1px solid var(--dsw-alias-brand-primary);
251
- background: color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent);
252
- border-radius: 4px;
253
- pointer-events: none;
254
- }
255
-
256
- /* ------------------------------------------------------------ nodes */
257
-
258
- .node {
259
- position: absolute;
260
- display: flex;
261
- flex-direction: column;
262
- border-radius: 14px;
263
- border: 1.5px solid var(--dsw-alias-border-l1);
264
- background: var(--dsw-alias-bg-layer-1);
265
- box-shadow: 0 8px 24px rgb(15 23 42 / 12%);
266
- overflow: visible;
267
- cursor: grab;
268
- touch-action: none;
269
- }
270
-
271
- .node:active { cursor: grabbing; }
272
-
273
- .nodeGlow {
274
- position: absolute;
275
- inset: -6px;
276
- border-radius: 18px;
277
- pointer-events: none;
278
- opacity: 0;
279
- transition: opacity 0.15s ease;
280
- }
281
-
282
- .nodeSelected {
283
- border-color: var(--dsw-alias-brand-primary);
284
- z-index: 2;
285
- }
286
-
287
- .nodeSelected .nodeGlow {
288
- opacity: 1;
289
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent);
290
- }
291
-
292
- .nodeRelated { border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent); }
293
-
294
- .nodeConnectTarget {
295
- border-color: var(--dsw-alias-brand-primary);
296
- box-shadow: 0 0 0 4px color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent);
297
- }
298
-
299
- .nodeHeader {
300
- display: flex;
301
- align-items: center;
302
- gap: 6px;
303
- padding: 7px 12px 5px;
304
- font-size: 11.5px;
305
- color: var(--dsw-alias-label-secondary);
306
- cursor: inherit;
307
- flex: none;
308
- }
309
-
310
- .nodeTitle {
311
- font-weight: 600;
312
- overflow: hidden;
313
- text-overflow: ellipsis;
314
- white-space: nowrap;
315
- }
316
-
317
- .nodeSize {
318
- margin-left: auto;
319
- font-size: 10.5px;
320
- color: var(--dsw-alias-label-tertiary);
321
- font-variant-numeric: tabular-nums;
322
- }
323
-
324
- .nodeSpinner {
325
- width: 11px;
326
- height: 11px;
327
- flex: none;
328
- border-radius: 50%;
329
- border: 1.5px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 30%, transparent);
330
- border-top-color: var(--dsw-alias-brand-primary);
331
- animation: canvasSpin 0.8s linear infinite;
332
- }
333
-
334
- .nodeBody {
335
- flex: 1;
336
- min-height: 0;
337
- margin: 0 8px 8px;
338
- border-radius: 9px;
339
- overflow: hidden;
340
- background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);
341
- display: flex;
342
- align-items: center;
343
- justify-content: center;
344
- pointer-events: none;
345
- }
346
-
347
- .nodeBody img {
348
- width: 100%;
349
- height: 100%;
350
- object-fit: contain;
351
- display: block;
352
- }
353
-
354
- .imageInfo {
355
- position: absolute;
356
- top: 6px;
357
- z-index: 2;
358
- padding: 3px 8px;
359
- border-radius: 6px;
360
- background: color-mix(in srgb, var(--dsw-alias-label-primary) 62%, transparent);
361
- color: var(--dsw-alias-bg-layer-1);
362
- font-size: 10.5px;
363
- line-height: 1.4;
364
- pointer-events: none;
365
- backdrop-filter: blur(3px);
366
- max-width: calc(100% - 90px);
367
- overflow: hidden;
368
- text-overflow: ellipsis;
369
- white-space: nowrap;
370
- }
371
-
372
- .imageInfoLeft { left: 6px; font-weight: 600; }
373
-
374
- .imageInfoRight {
375
- right: 6px;
376
- left: auto;
377
- font-variant-numeric: tabular-nums;
378
- opacity: 0.85;
379
- }
380
-
381
- .nodeState,
382
- .nodeStateError {
383
- display: flex;
384
- flex-direction: column;
385
- align-items: center;
386
- gap: 8px;
387
- font-size: 12px;
388
- color: var(--dsw-alias-label-secondary);
389
- padding: 12px;
390
- text-align: center;
391
- pointer-events: auto;
392
- }
393
-
394
- .nodeStateError { color: var(--dsw-alias-label-error); }
395
-
396
- .nodeStateError button {
397
- border: none;
398
- border-radius: 7px;
399
- padding: 4px 12px;
400
- background: var(--dsw-alias-interactive-bg-hover);
401
- color: var(--dsw-alias-label-primary);
402
- font-size: 12px;
403
- cursor: pointer;
404
- }
405
-
406
- .nodeEmpty {
407
- display: flex;
408
- flex-direction: column;
409
- align-items: center;
410
- gap: 8px;
411
- border: 1.5px dashed color-mix(in srgb, var(--dsw-alias-label-primary) 24%, transparent);
412
- border-radius: 9px;
413
- margin: 6px;
414
- padding: 14px;
415
- background: transparent;
416
- color: var(--dsw-alias-label-tertiary);
417
- font-size: 12px;
418
- cursor: pointer;
419
- /* .nodeBody disables pointer events for node dragging; the upload button
420
- * inside must opt back in or its onClick never fires. */
421
- pointer-events: auto;
422
- }
423
-
424
- .nodeEmpty:hover {
425
- border-color: var(--dsw-alias-brand-primary);
426
- color: var(--dsw-alias-brand-primary);
427
- }
428
-
429
- .textNode { cursor: default; }
430
-
431
- /* image nodes render as bare cards: the picture fills the frame */
432
- .imageNode .nodeBody { margin: 6px; }
433
-
434
- .textArea {
435
- flex: 1;
436
- min-height: 0;
437
- margin: 0 8px 8px;
438
- border: none;
439
- resize: none;
440
- outline: none;
441
- background: transparent;
442
- color: var(--dsw-alias-label-primary);
443
- font-size: 13px;
444
- line-height: 1.55;
445
- padding: 4px 6px;
446
- cursor: text;
447
- pointer-events: auto;
448
- }
449
-
450
- .textArea::placeholder { color: var(--dsw-alias-label-tertiary); }
451
-
452
- /* --------------------------------------------------- bottom dock */
453
-
454
- .dock {
455
- position: absolute;
456
- left: 50%;
457
- bottom: 14px;
458
- transform: translateX(-50%);
459
- z-index: 30;
460
- display: flex;
461
- align-items: center;
462
- gap: 2px;
463
- padding: 6px;
464
- border-radius: 13px;
465
- border: 1px solid var(--dsw-alias-border-l1);
466
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);
467
- box-shadow: 0 10px 30px rgb(15 23 42 / 14%);
468
- backdrop-filter: blur(10px);
469
- }
470
-
471
- /* macOS-dock magnification: the pointermove handler on .dock feeds a gaussian
472
- * falloff into --dock-scale / --dock-lift, so the hovered icon swells and lifts
473
- * while neighbours rise and fall like a zipper as the pointer slides across. */
474
- .dock .iconButton {
475
- position: relative;
476
- width: 40px;
477
- height: 40px;
478
- border-radius: 11px;
479
- transform: translateY(var(--dock-lift, 0px)) scale(var(--dock-scale, 1));
480
- transform-origin: 50% 100%;
481
- transition: transform 0.16s cubic-bezier(0.33, 1, 0.68, 1), background 0.15s ease, color 0.15s ease, box-shadow 0.18s ease;
482
- }
483
-
484
- .dock .iconButton:not(:disabled):hover {
485
- z-index: 2;
486
- box-shadow: 0 6px 14px color-mix(in srgb, var(--dsw-alias-label-primary) 20%, transparent);
487
- }
488
-
489
- .dock .iconButton:not(:disabled):active {
490
- transform: translateY(var(--dock-lift, 0px)) scale(0.94);
491
- }
492
-
493
- .dock .iconButton[data-active] {
494
- background: var(--dsw-alias-label-primary);
495
- color: var(--dsw-alias-bg-layer-1);
496
- }
497
-
498
- .dock .iconButton[data-active]:hover { background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-1); }
499
-
500
- .dockDivider {
501
- width: 1px;
502
- height: 18px;
503
- margin: 0 4px;
504
- background: var(--dsw-alias-border-l1);
505
- }
506
-
507
- /* ------------------------------------------------- floating composer */
508
-
509
- .composer {
510
- position: absolute;
511
- z-index: 35;
512
- width: 560px;
513
- display: flex;
514
- flex-direction: column;
515
- gap: 8px;
516
- padding: 10px;
517
- border-radius: 14px;
518
- border: 1px solid var(--dsw-alias-border-l1);
519
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 96%, transparent);
520
- box-shadow: 0 16px 44px rgb(15 23 42 / 18%);
521
- backdrop-filter: blur(10px);
522
- }
523
-
524
- .composerPrompt {
525
- width: 100%;
526
- min-height: 38px;
527
- max-height: 120px;
528
- resize: none;
529
- border: none;
530
- outline: none;
531
- background: transparent;
532
- color: var(--dsw-alias-label-primary);
533
- font-size: 13px;
534
- line-height: 1.5;
535
- padding: 2px 4px;
536
- cursor: text;
537
- }
538
-
539
- .composerPrompt::placeholder { color: var(--dsw-alias-label-tertiary); }
540
-
541
- .composerMeta { display: flex; }
542
-
543
- .composerChip {
544
- display: inline-flex;
545
- align-items: center;
546
- gap: 5px;
547
- font-size: 11px;
548
- padding: 2px 9px;
549
- border-radius: 999px;
550
- border: 1px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent);
551
- color: var(--dsw-alias-brand-primary);
552
- background: color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent);
553
- }
554
-
555
- .composerControls {
556
- display: flex;
557
- align-items: center;
558
- gap: 6px;
559
- }
560
-
561
- .composerControls select {
562
- height: 28px;
563
- border-radius: 999px;
564
- border: 1px solid var(--dsw-alias-border-l1);
565
- background: transparent;
566
- color: var(--dsw-alias-label-primary);
567
- font-size: 11.5px;
568
- padding: 0 8px;
569
- min-width: 0;
570
- max-width: 128px;
571
- }
572
-
573
- /* custom dropdown button + popup (styled replacement for native <select>) */
574
- .composerSelect {
575
- display: inline-flex;
576
- align-items: center;
577
- gap: 6px;
578
- height: 28px;
579
- padding: 0 8px 0 11px;
580
- border-radius: 999px;
581
- border: 1px solid var(--dsw-alias-border-l1);
582
- background: transparent;
583
- color: var(--dsw-alias-label-primary);
584
- font-size: 11.5px;
585
- min-width: 0;
586
- max-width: 138px;
587
- cursor: pointer;
588
- transition: background 0.15s ease, border-color 0.15s ease;
589
- }
590
-
591
- .composerSelect:hover { background: var(--dsw-alias-interactive-bg-hover); }
592
- .composerSelect[data-open] { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); }
593
- .composerSelect svg { flex: none; opacity: 0.65; }
594
- .composerSelect[data-open] svg { opacity: 0.9; }
595
-
596
- .composerSelectValue {
597
- overflow: hidden;
598
- text-overflow: ellipsis;
599
- white-space: nowrap;
600
- min-width: 0;
601
- }
602
-
603
- .composerSelectMenu {
604
- position: fixed;
605
- z-index: 95;
606
- max-height: 264px;
607
- overflow: auto;
608
- padding: 5px;
609
- border-radius: 11px;
610
- border: 1px solid var(--dsw-alias-border-l1);
611
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 97%, transparent);
612
- box-shadow: 0 14px 38px rgb(15 23 42 / 18%);
613
- backdrop-filter: blur(10px);
614
- }
615
-
616
- .composerSelectMenu button {
617
- display: block;
618
- width: 100%;
619
- border: none;
620
- border-radius: 7px;
621
- background: transparent;
622
- color: var(--dsw-alias-label-primary);
623
- font-size: 12.5px;
624
- line-height: 1.45;
625
- padding: 7px 10px;
626
- cursor: pointer;
627
- text-align: left;
628
- white-space: nowrap;
629
- }
630
-
631
- .composerSelectMenu button:hover { background: var(--dsw-alias-interactive-bg-hover); }
632
- .composerSelectMenu button[data-selected] {
633
- color: var(--dsw-alias-brand-primary);
634
- background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);
635
- }
636
-
637
- .composerSend {
638
- margin-left: auto;
639
- width: 34px;
640
- height: 34px;
641
- flex: none;
642
- border: none;
643
- border-radius: 50%;
644
- background: var(--dsw-alias-label-primary);
645
- color: var(--dsw-alias-bg-layer-1);
646
- cursor: pointer;
647
- display: inline-flex;
648
- align-items: center;
649
- justify-content: center;
650
- }
651
-
652
- .composerSend:hover { opacity: 0.88; }
653
- .composerSend:disabled { opacity: 0.35; cursor: not-allowed; }
654
-
655
- /* ------------------------------------------------------ config node */
656
-
657
- .configNode {
658
- padding: 0 14px 14px;
659
- cursor: grab;
660
- }
661
-
662
- .configNode .nodeHeader { padding: 10px 2px 6px; font-size: 12px; }
663
-
664
- .configLinks {
665
- display: flex;
666
- align-items: center;
667
- margin: 4px 0 10px;
668
- flex: none;
669
- }
670
-
671
- .configLinks .composerChip {
672
- font-size: 11px;
673
- padding: 3px 10px;
674
- }
675
-
676
- .configHint {
677
- flex: 1;
678
- min-height: 0;
679
- margin: 0;
680
- font-size: 11.5px;
681
- line-height: 1.7;
682
- color: var(--dsw-alias-label-tertiary);
683
- overflow: auto;
684
- }
685
-
686
- /* connection handles */
687
-
688
- .handle {
689
- position: absolute;
690
- top: 50%;
691
- width: 12px;
692
- height: 12px;
693
- border-radius: 50%;
694
- transform: translateY(-50%) scale(0.6);
695
- background: var(--dsw-alias-bg-layer-1);
696
- border: 2px solid var(--dsw-alias-brand-primary);
697
- opacity: 0;
698
- transition: opacity 0.12s ease, transform 0.12s ease;
699
- cursor: crosshair;
700
- z-index: 3;
701
- }
702
-
703
- .handleLeft { left: -7px; }
704
- .handleRight { right: -7px; }
705
-
706
- .node:hover .handle,
707
- .nodeSelected .handle,
708
- .nodeConnectTarget .handle {
709
- opacity: 1;
710
- transform: translateY(-50%) scale(1);
711
- }
712
-
713
- .handle:hover { background: var(--dsw-alias-brand-primary); }
714
-
715
- /* The source handle doubles as a quick-add button (hover opens the node
716
- * picker, drag connects) so it reads as a Dify-style big ⊕. */
717
- .handleRight {
718
- display: flex;
719
- align-items: center;
720
- justify-content: center;
721
- width: 20px;
722
- height: 20px;
723
- right: -10px;
724
- font-size: 13px;
725
- font-weight: 600;
726
- line-height: 1;
727
- color: var(--dsw-alias-brand-primary);
728
- }
729
-
730
- .handleRight::after { content: '+'; }
731
-
732
- .handleRight:hover { color: var(--dsw-alias-bg-layer-1); transform: translateY(-50%) scale(1.12); }
733
-
734
- .resizeHandle {
735
- position: absolute;
736
- right: -5px;
737
- bottom: -5px;
738
- width: 14px;
739
- height: 14px;
740
- border-radius: 4px;
741
- background: var(--dsw-alias-brand-primary);
742
- border: 2px solid var(--dsw-alias-bg-layer-1);
743
- cursor: nwse-resize;
744
- z-index: 3;
745
- }
746
-
747
- /* hover toolbar */
748
-
749
- .hoverToolbar {
750
- position: absolute;
751
- top: -36px;
752
- left: 50%;
753
- transform: translateX(-50%);
754
- display: flex;
755
- gap: 2px;
756
- padding: 4px;
757
- border-radius: 10px;
758
- border: 1px solid var(--dsw-alias-border-l1);
759
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);
760
- box-shadow: 0 8px 22px rgb(15 23 42 / 14%);
761
- opacity: 0;
762
- pointer-events: none;
763
- transition: opacity 0.12s ease;
764
- z-index: 4;
765
- }
766
-
767
- .node:hover .hoverToolbar,
768
- .nodeSelected .hoverToolbar {
769
- opacity: 1;
770
- pointer-events: auto;
771
- }
772
-
773
- /* -------------------------------------------------------- connections */
774
-
775
- .connectionLayer {
776
- position: absolute;
777
- overflow: visible;
778
- pointer-events: none;
779
- z-index: 0;
780
- }
781
-
782
- .connectionPath {
783
- fill: none;
784
- stroke-width: 1.8;
785
- stroke-opacity: 0.9;
786
- pointer-events: none;
787
- }
788
-
789
- .connectionActive {
790
- stroke-width: 2.6;
791
- stroke-opacity: 1;
792
- filter: drop-shadow(0 0 6px color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent));
793
- }
794
-
795
- .connectionPreview {
796
- fill: none;
797
- stroke: var(--dsw-alias-brand-primary);
798
- stroke-width: 2;
799
- stroke-dasharray: 5 5;
800
- pointer-events: none;
801
- }
802
-
803
- /* Flow overlay: a soft light band glides from the path start (source) toward
804
- * its end (target), echoing the direction of the gradient and arrowhead. */
805
- .connectionFlow {
806
- fill: none;
807
- stroke: var(--dsw-alias-brand-primary);
808
- stroke-width: 2;
809
- stroke-linecap: round;
810
- stroke-dasharray: 30 90;
811
- opacity: 0.4;
812
- pointer-events: none;
813
- animation: connectionFlow 1.6s linear infinite;
814
- }
815
-
816
- .connectionFlowActive {
817
- stroke: var(--dsw-alias-brand-primary);
818
- opacity: 0.85;
819
- }
820
-
821
- @keyframes connectionFlow {
822
- to { stroke-dashoffset: -120; }
823
- }
824
-
825
- @media (prefers-reduced-motion: reduce) {
826
- .connectionFlow { animation: none; opacity: 0.35; }
827
- }
828
-
829
- /* --------------------------------------------------------- empty hint */
830
-
831
- .emptyHint {
832
- position: absolute;
833
- left: 50%;
834
- top: 44%;
835
- transform: translate(-50%, -50%);
836
- display: flex;
837
- flex-direction: column;
838
- align-items: center;
839
- gap: 6px;
840
- text-align: center;
841
- color: var(--dsw-alias-label-tertiary);
842
- pointer-events: none;
843
- z-index: 5;
844
- }
845
-
846
- .emptyHint strong {
847
- font-size: 16px;
848
- color: var(--dsw-alias-label-secondary);
849
- }
850
-
851
- .emptyHint span { font-size: 12.5px; max-width: 320px; line-height: 1.6; }
852
-
853
- /* ------------------------------------------------------------ zoom dock */
854
-
855
- .zoomDock {
856
- position: absolute;
857
- left: 14px;
858
- bottom: 14px;
859
- z-index: 30;
860
- display: flex;
861
- align-items: center;
862
- gap: 6px;
863
- padding: 6px 8px;
864
- border-radius: 12px;
865
- border: 1px solid var(--dsw-alias-border-l1);
866
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 88%, transparent);
867
- box-shadow: 0 10px 30px rgb(15 23 42 / 12%);
868
- backdrop-filter: blur(10px);
869
- }
870
-
871
- .zoomDock input[type="range"] {
872
- width: 96px;
873
- accent-color: var(--dsw-alias-brand-primary);
874
- }
875
-
876
- .zoomValue {
877
- width: 40px;
878
- text-align: right;
879
- font-size: 11.5px;
880
- color: var(--dsw-alias-label-secondary);
881
- font-variant-numeric: tabular-nums;
882
- }
883
-
884
- /* -------------------------------------------------------------- minimap */
885
-
886
- .minimap {
887
- position: absolute;
888
- left: 14px;
889
- bottom: 62px;
890
- z-index: 30;
891
- width: 220px;
892
- height: 150px;
893
- border-radius: 12px;
894
- border: 1px solid var(--dsw-alias-border-l1);
895
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 88%, transparent);
896
- box-shadow: 0 10px 30px rgb(15 23 42 / 12%);
897
- overflow: hidden;
898
- backdrop-filter: blur(10px);
899
- }
900
-
901
- .minimapCanvas {
902
- position: relative;
903
- width: 100%;
904
- height: 100%;
905
- cursor: crosshair;
906
- }
907
-
908
- .minimapNode {
909
- position: absolute;
910
- border-radius: 2px;
911
- opacity: 0.8;
912
- }
913
-
914
- .minimapImage { background: var(--dsw-alias-brand-primary); }
915
- .minimapText { background: color-mix(in srgb, var(--dsw-alias-label-primary) 40%, transparent); }
916
- .minimapSelected { outline: 1.5px solid var(--dsw-alias-brand-primary); }
917
-
918
- .minimapViewport {
919
- position: absolute;
920
- border: 1.5px solid var(--dsw-alias-brand-primary);
921
- background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);
922
- border-radius: 2px;
923
- pointer-events: none;
924
- }
925
-
926
- /* ------------------------------------------------------------ inspector */
927
-
928
- .inspectorRow {
929
- display: flex;
930
- gap: 8px;
931
- }
932
-
933
- .inspectorRow > * { flex: 1; min-width: 0; }
934
-
935
- /* --------------------------------------------------------- context menu */
936
-
937
- .contextMenu {
938
- position: fixed;
939
- z-index: 90;
940
- min-width: 176px;
941
- padding: 5px;
942
- border-radius: 11px;
943
- border: 1px solid var(--dsw-alias-border-l1);
944
- background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 96%, transparent);
945
- box-shadow: 0 16px 40px rgb(15 23 42 / 20%);
946
- display: flex;
947
- flex-direction: column;
948
- }
949
-
950
- .contextMenu button {
951
- display: flex;
952
- align-items: center;
953
- gap: 8px;
954
- border: none;
955
- border-radius: 7px;
956
- background: transparent;
957
- color: var(--dsw-alias-label-primary);
958
- font-size: 12.5px;
959
- padding: 7px 10px;
960
- cursor: pointer;
961
- text-align: left;
962
- }
963
-
964
- .contextMenu button:hover { background: var(--dsw-alias-interactive-bg-hover); }
965
- .contextMenu button[data-danger] { color: var(--dsw-alias-label-error); }
966
-
967
- /* ---------------------------------------------------------- error toast */
968
-
969
- .errorToast {
970
- position: absolute;
971
- left: 50%;
972
- bottom: 18px;
973
- transform: translateX(-50%);
974
- z-index: 60;
975
- display: flex;
976
- align-items: center;
977
- gap: 10px;
978
- max-width: min(560px, 80%);
979
- padding: 9px 12px;
980
- border-radius: 10px;
981
- border: 1px solid color-mix(in srgb, var(--dsw-alias-label-error) 35%, transparent);
982
- background: color-mix(in srgb, var(--dsw-alias-label-error) 12%, var(--dsw-alias-bg-layer-1));
983
- color: var(--dsw-alias-label-error);
984
- font-size: 12.5px;
985
- box-shadow: 0 12px 30px rgb(15 23 42 / 18%);
986
- }
987
-
988
- .errorToast button {
989
- border: none;
990
- background: transparent;
991
- color: inherit;
992
- cursor: pointer;
993
- padding: 2px;
994
- display: inline-flex;
995
- flex: none;
996
- }
997
-
998
- /* ---------------------------------------------------------- picker modal */
999
-
1000
- .modalBackdrop {
1001
- position: absolute;
1002
- inset: 0;
1003
- z-index: 70;
1004
- background: color-mix(in srgb, var(--dsw-alias-label-primary) 32%, transparent);
1005
- display: flex;
1006
- align-items: center;
1007
- justify-content: center;
1008
- }
1009
-
1010
- .picker {
1011
- width: min(720px, 92%);
1012
- max-height: 84%;
1013
- display: flex;
1014
- flex-direction: column;
1015
- border-radius: 14px;
1016
- border: 1px solid var(--dsw-alias-border-l1);
1017
- background: var(--dsw-alias-bg-layer-1);
1018
- box-shadow: 0 24px 60px rgb(15 23 42 / 26%);
1019
- overflow: hidden;
1020
- }
1021
-
1022
- .pickerHeader {
1023
- display: flex;
1024
- align-items: center;
1025
- justify-content: space-between;
1026
- padding: 12px 14px;
1027
- font-size: 14px;
1028
- border-bottom: 1px solid var(--dsw-alias-border-l1);
1029
- }
1030
-
1031
- .pickerHeader button {
1032
- border: none;
1033
- background: transparent;
1034
- color: var(--dsw-alias-label-secondary);
1035
- font-size: 18px;
1036
- cursor: pointer;
1037
- padding: 0 4px;
1038
- }
1039
-
1040
- .pickerTabs {
1041
- display: flex;
1042
- gap: 4px;
1043
- padding: 10px 14px 0;
1044
- }
1045
-
1046
- .pickerTabs button {
1047
- border: none;
1048
- border-radius: 8px 8px 0 0;
1049
- background: transparent;
1050
- color: var(--dsw-alias-label-secondary);
1051
- font-size: 12.5px;
1052
- padding: 7px 12px;
1053
- cursor: pointer;
1054
- }
1055
-
1056
- .pickerTabs button[data-active] {
1057
- background: var(--dsw-alias-interactive-bg-hover);
1058
- color: var(--dsw-alias-brand-primary);
1059
- }
1060
-
1061
- .pickerBody {
1062
- flex: 1;
1063
- min-height: 0;
1064
- overflow: auto;
1065
- padding: 14px;
1066
- }
1067
-
1068
- .pickerGrid {
1069
- display: grid;
1070
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1071
- gap: 10px;
1072
- }
1073
-
1074
- .pickerCard {
1075
- display: flex;
1076
- flex-direction: column;
1077
- gap: 6px;
1078
- border: 1.5px solid var(--dsw-alias-border-l1);
1079
- border-radius: 10px;
1080
- background: transparent;
1081
- padding: 6px;
1082
- cursor: pointer;
1083
- text-align: left;
1084
- color: var(--dsw-alias-label-primary);
1085
- }
1086
-
1087
- .pickerCard[data-selected] {
1088
- border-color: var(--dsw-alias-brand-primary);
1089
- box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 25%, transparent);
1090
- }
1091
-
1092
- .pickerCard img {
1093
- width: 100%;
1094
- height: 110px;
1095
- object-fit: cover;
1096
- border-radius: 7px;
1097
- }
1098
-
1099
- .pickerCardPrompt {
1100
- font-size: 11.5px;
1101
- overflow: hidden;
1102
- text-overflow: ellipsis;
1103
- white-space: nowrap;
1104
- }
1105
-
1106
- .pickerCard small {
1107
- color: var(--dsw-alias-label-tertiary);
1108
- font-size: 10.5px;
1109
- }
1110
-
1111
- .pickerFooter {
1112
- display: flex;
1113
- align-items: center;
1114
- justify-content: space-between;
1115
- padding-top: 12px;
1116
- font-size: 12.5px;
1117
- color: var(--dsw-alias-label-secondary);
1118
- }
1119
-
1120
- .pickerFooter button,
1121
- .generateForm button {
1122
- height: 32px;
1123
- padding: 0 16px;
1124
- border: none;
1125
- border-radius: 8px;
1126
- background: var(--dsw-alias-brand-primary);
1127
- color: var(--dsw-alias-bg-base, #fff);
1128
- font-size: 12.5px;
1129
- font-weight: 600;
1130
- cursor: pointer;
1131
- display: inline-flex;
1132
- align-items: center;
1133
- gap: 6px;
1134
- }
1135
-
1136
- .pickerFooter button:disabled,
1137
- .generateForm button:disabled { opacity: 0.45; cursor: not-allowed; }
1138
-
1139
- .uploadBox {
1140
- display: flex;
1141
- flex-direction: column;
1142
- align-items: center;
1143
- justify-content: center;
1144
- gap: 8px;
1145
- min-height: 220px;
1146
- border: 1.5px dashed color-mix(in srgb, var(--dsw-alias-label-primary) 22%, transparent);
1147
- border-radius: 12px;
1148
- color: var(--dsw-alias-label-secondary);
1149
- cursor: pointer;
1150
- text-align: center;
1151
- }
1152
-
1153
- .uploadBox:hover { border-color: var(--dsw-alias-brand-primary); }
1154
- .uploadBox input { display: none; }
1155
- .uploadBox small { color: var(--dsw-alias-label-tertiary); font-size: 11px; }
1156
-
1157
- .uploadIcon { color: var(--dsw-alias-brand-primary); }
1158
-
1159
- .generateForm {
1160
- display: flex;
1161
- flex-direction: column;
1162
- gap: 10px;
1163
- }
1164
-
1165
- .generateForm textarea {
1166
- min-height: 110px;
1167
- resize: vertical;
1168
- border-radius: 9px;
1169
- border: 1px solid var(--dsw-alias-border-l1);
1170
- background: transparent;
1171
- color: var(--dsw-alias-label-primary);
1172
- font-size: 12.5px;
1173
- padding: 8px 10px;
1174
- outline: none;
1175
- }
1176
-
1177
- .generateForm select {
1178
- height: 30px;
1179
- border-radius: 8px;
1180
- border: 1px solid var(--dsw-alias-border-l1);
1181
- background: transparent;
1182
- color: var(--dsw-alias-label-primary);
1183
- font-size: 12.5px;
1184
- padding: 0 8px;
1185
- }
1186
-
1187
- @keyframes canvasSpin {
1188
- to { transform: rotate(360deg); }
1189
- }
1
+ /* Infinite canvas workspace — node-graph canvas styled after
2
+ * basketikun/infinite-canvas: floating docks, card nodes, wire connections. */
3
+
4
+ .root {
5
+ position: relative;
6
+ display: flex;
7
+ flex: 1 1 auto;
8
+ flex-direction: column;
9
+ height: 100%;
10
+ min-width: 0;
11
+ min-height: 0;
12
+ overflow: hidden;
13
+ border: 1px solid var(--dsw-alias-border-l1);
14
+ border-radius: 10px;
15
+ background: var(--dsw-alias-bg-layer-1);
16
+ color: var(--dsw-alias-label-primary);
17
+ font-family: var(--dsw-font-family, inherit);
18
+ user-select: none;
19
+ }
20
+
21
+ .selectCursor { cursor: default; }
22
+ .panCursor { cursor: grab; }
23
+ .panCursor:active { cursor: grabbing; }
24
+
25
+ /* ------------------------------------------------------------- top bar */
26
+
27
+ .topBar {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 8px;
31
+ padding: 8px 12px;
32
+ border-bottom: 1px solid var(--dsw-alias-border-l1);
33
+ flex: none;
34
+ z-index: 40;
35
+ background: var(--dsw-alias-bg-layer-1);
36
+ }
37
+
38
+ .projectSelect {
39
+ max-width: 180px;
40
+ height: 30px;
41
+ padding: 0 8px;
42
+ border-radius: 8px;
43
+ border: 1px solid var(--dsw-alias-border-l1);
44
+ background: transparent;
45
+ color: var(--dsw-alias-label-primary);
46
+ font-size: 12.5px;
47
+ }
48
+
49
+ .titleButton {
50
+ height: 30px;
51
+ padding: 0 10px;
52
+ border: 1px solid transparent;
53
+ border-radius: 8px;
54
+ background: transparent;
55
+ color: var(--dsw-alias-label-primary);
56
+ font-size: 13px;
57
+ font-weight: 600;
58
+ max-width: 260px;
59
+ overflow: hidden;
60
+ text-overflow: ellipsis;
61
+ white-space: nowrap;
62
+ cursor: text;
63
+ }
64
+
65
+ .titleButton:hover { border-color: var(--dsw-alias-border-l1); }
66
+
67
+ .titleInput {
68
+ height: 30px;
69
+ padding: 0 10px;
70
+ border-radius: 8px;
71
+ border: 1px solid var(--dsw-alias-brand-primary);
72
+ background: transparent;
73
+ color: var(--dsw-alias-label-primary);
74
+ font-size: 13px;
75
+ font-weight: 600;
76
+ width: 240px;
77
+ outline: none;
78
+ }
79
+
80
+ .topBarSpacer { flex: 1; }
81
+
82
+ .saveState {
83
+ font-size: 11.5px;
84
+ color: var(--dsw-alias-label-tertiary);
85
+ white-space: nowrap;
86
+ }
87
+
88
+ .saveState[data-state="error"] { color: var(--dsw-alias-label-error); }
89
+ .saveState[data-state="saving"] { color: var(--dsw-alias-state-warn-primary, var(--dsw-alias-label-secondary)); }
90
+
91
+ /* ---------------------------------------------------------- icon button */
92
+
93
+ .iconButton {
94
+ display: inline-flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ gap: 5px;
98
+ width: 30px;
99
+ height: 30px;
100
+ flex: none;
101
+ border: none;
102
+ border-radius: 8px;
103
+ background: transparent;
104
+ color: var(--dsw-alias-label-secondary);
105
+ cursor: pointer;
106
+ padding: 0;
107
+ }
108
+
109
+ .iconButton:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
110
+ .iconButton[data-active] { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 16%, transparent); color: var(--dsw-alias-brand-primary); }
111
+ .iconButton:disabled { opacity: 0.35; cursor: not-allowed; }
112
+ .iconButton:disabled:hover { background: transparent; color: var(--dsw-alias-label-secondary); }
113
+
114
+ /* (node tools moved to the bottom-center dock above) */
115
+
116
+ /* ------------------------------------------------------------- viewport */
117
+
118
+ .viewport {
119
+ position: relative;
120
+ flex: 1;
121
+ min-height: 0;
122
+ overflow: hidden;
123
+ touch-action: none;
124
+ }
125
+
126
+ .grid {
127
+ position: absolute;
128
+ inset: 0;
129
+ pointer-events: none;
130
+ opacity: 0.5;
131
+ }
132
+
133
+ /* Interactive backdrops live in <canvas> elements (the fluid flowmap dot
134
+ * field, the WebGL fluid sim, and the reactbits-style shader backdrops);
135
+ * they render themselves at full strength (the 0.5 grid dimming stays off). */
136
+ .flowCanvas {
137
+ position: absolute;
138
+ inset: 0;
139
+ width: 100%;
140
+ height: 100%;
141
+ }
142
+
143
+ /* WebGL "liquid ether" fluid simulation, same placement as the flow canvas. */
144
+ .liquidCanvas {
145
+ position: absolute;
146
+ inset: 0;
147
+ width: 100%;
148
+ height: 100%;
149
+ }
150
+
151
+ /* Fullscreen-shader backdrops (floating lines / galaxy / silk) and the 2D
152
+ * wave lines share one placement; floating lines screen-blends over dark
153
+ * surfaces (set from JS, the light theme uses its own light backdrop). */
154
+ .sceneCanvas {
155
+ position: absolute;
156
+ inset: 0;
157
+ width: 100%;
158
+ height: 100%;
159
+ }
160
+
161
+ .grid[data-mode="flow"],
162
+ .grid[data-mode="liquid"],
163
+ .grid[data-mode="floatingLines"],
164
+ .grid[data-mode="galaxy"],
165
+ .grid[data-mode="silk"],
166
+ .grid[data-mode="waves"],
167
+ .grid[data-mode="faultyTerminal"],
168
+ .grid[data-mode="dotField"],
169
+ .grid[data-mode="dotGrid"],
170
+ .grid[data-mode="shapeGrid"] {
171
+ opacity: 1;
172
+ }
173
+
174
+ .grid[data-mode="dots"] {
175
+ background-image: radial-gradient(circle, color-mix(in srgb, var(--dsw-alias-label-primary) 30%, transparent) 1.1px, transparent 1.4px);
176
+ }
177
+
178
+ .grid[data-mode="lines"] {
179
+ background-image:
180
+ linear-gradient(color-mix(in srgb, var(--dsw-alias-label-primary) 9%, transparent) 1px, transparent 1px),
181
+ linear-gradient(90deg, color-mix(in srgb, var(--dsw-alias-label-primary) 9%, transparent) 1px, transparent 1px);
182
+ }
183
+
184
+ .grid[data-mode="blank"] { background-image: none; }
185
+
186
+ .grid[data-mode="image"] { background-image: none; }
187
+
188
+ .gridScrim {
189
+ position: absolute;
190
+ inset: 0;
191
+ background: var(--dsw-alias-bg-layer-1);
192
+ opacity: 0.6;
193
+ }
194
+
195
+ /* ------------------------------------------------ background menu */
196
+
197
+ .backgroundMenu {
198
+ position: absolute;
199
+ transform: translate(-50%, -100%);
200
+ z-index: 45;
201
+ min-width: 168px;
202
+ padding: 5px;
203
+ border-radius: 11px;
204
+ border: 1px solid var(--dsw-alias-border-l1);
205
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 96%, transparent);
206
+ box-shadow: 0 16px 40px rgb(15 23 42 / 20%);
207
+ display: flex;
208
+ flex-direction: column;
209
+ }
210
+
211
+ .backgroundMenu button {
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: flex-start;
215
+ border: none;
216
+ border-radius: 7px;
217
+ background: transparent;
218
+ color: var(--dsw-alias-label-primary);
219
+ font-size: 12.5px;
220
+ padding: 7px 10px;
221
+ cursor: pointer;
222
+ text-align: left;
223
+ }
224
+
225
+ .backgroundMenu button:hover { background: var(--dsw-alias-interactive-bg-hover); }
226
+
227
+ .backgroundMenu button[data-active] {
228
+ color: var(--dsw-alias-brand-primary);
229
+ background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);
230
+ }
231
+
232
+ .backgroundMenuDivider {
233
+ height: 1px;
234
+ margin: 4px 6px;
235
+ background: var(--dsw-alias-border-l1);
236
+ }
237
+
238
+ .world {
239
+ position: absolute;
240
+ left: 0;
241
+ top: 0;
242
+ transform-origin: 0 0;
243
+ }
244
+
245
+ .marquee {
246
+ position: absolute;
247
+ z-index: 20;
248
+ border: 1px solid var(--dsw-alias-brand-primary);
249
+ background: color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent);
250
+ border-radius: 4px;
251
+ pointer-events: none;
252
+ }
253
+
254
+ /* ------------------------------------------------------------ nodes */
255
+
256
+ .node {
257
+ position: absolute;
258
+ display: flex;
259
+ flex-direction: column;
260
+ border-radius: 14px;
261
+ border: 1.5px solid var(--dsw-alias-border-l1);
262
+ background: var(--dsw-alias-bg-layer-1);
263
+ box-shadow: 0 8px 24px rgb(15 23 42 / 12%);
264
+ overflow: visible;
265
+ cursor: grab;
266
+ touch-action: none;
267
+ }
268
+
269
+ .node:active { cursor: grabbing; }
270
+
271
+ .nodeGlow {
272
+ position: absolute;
273
+ inset: -6px;
274
+ border-radius: 18px;
275
+ pointer-events: none;
276
+ opacity: 0;
277
+ transition: opacity 0.15s ease;
278
+ }
279
+
280
+ .nodeSelected {
281
+ border-color: var(--dsw-alias-brand-primary);
282
+ z-index: 2;
283
+ }
284
+
285
+ .nodeSelected .nodeGlow {
286
+ opacity: 1;
287
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent);
288
+ }
289
+
290
+ .nodeRelated { border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent); }
291
+
292
+ .nodeConnectTarget {
293
+ border-color: var(--dsw-alias-brand-primary);
294
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent);
295
+ }
296
+
297
+ .nodeHeader {
298
+ display: flex;
299
+ align-items: center;
300
+ gap: 6px;
301
+ padding: 7px 12px 5px;
302
+ font-size: 11.5px;
303
+ color: var(--dsw-alias-label-secondary);
304
+ cursor: inherit;
305
+ flex: none;
306
+ }
307
+
308
+ .nodeTitle {
309
+ font-weight: 600;
310
+ overflow: hidden;
311
+ text-overflow: ellipsis;
312
+ white-space: nowrap;
313
+ }
314
+
315
+ .nodeSize {
316
+ margin-left: auto;
317
+ font-size: 10.5px;
318
+ color: var(--dsw-alias-label-tertiary);
319
+ font-variant-numeric: tabular-nums;
320
+ }
321
+
322
+ .nodeSpinner {
323
+ width: 11px;
324
+ height: 11px;
325
+ flex: none;
326
+ border-radius: 50%;
327
+ border: 1.5px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 30%, transparent);
328
+ border-top-color: var(--dsw-alias-brand-primary);
329
+ animation: canvasSpin 0.8s linear infinite;
330
+ }
331
+
332
+ .nodeBody {
333
+ flex: 1;
334
+ min-height: 0;
335
+ margin: 0 8px 8px;
336
+ border-radius: 9px;
337
+ overflow: hidden;
338
+ background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);
339
+ display: flex;
340
+ align-items: center;
341
+ justify-content: center;
342
+ pointer-events: none;
343
+ }
344
+
345
+ .nodeBody img {
346
+ width: 100%;
347
+ height: 100%;
348
+ object-fit: contain;
349
+ display: block;
350
+ }
351
+
352
+ .imageInfo {
353
+ position: absolute;
354
+ top: 6px;
355
+ z-index: 2;
356
+ padding: 3px 8px;
357
+ border-radius: 6px;
358
+ background: color-mix(in srgb, var(--dsw-alias-label-primary) 62%, transparent);
359
+ color: var(--dsw-alias-bg-layer-1);
360
+ font-size: 10.5px;
361
+ line-height: 1.4;
362
+ pointer-events: none;
363
+ backdrop-filter: blur(3px);
364
+ max-width: calc(100% - 90px);
365
+ overflow: hidden;
366
+ text-overflow: ellipsis;
367
+ white-space: nowrap;
368
+ }
369
+
370
+ .imageInfoLeft { left: 6px; font-weight: 600; }
371
+
372
+ .imageInfoRight {
373
+ right: 6px;
374
+ left: auto;
375
+ font-variant-numeric: tabular-nums;
376
+ opacity: 0.85;
377
+ }
378
+
379
+ .nodeState,
380
+ .nodeStateError {
381
+ display: flex;
382
+ flex-direction: column;
383
+ align-items: center;
384
+ gap: 8px;
385
+ font-size: 12px;
386
+ color: var(--dsw-alias-label-secondary);
387
+ padding: 12px;
388
+ text-align: center;
389
+ pointer-events: auto;
390
+ }
391
+
392
+ .nodeStateError { color: var(--dsw-alias-label-error); }
393
+
394
+ .nodeStateError button {
395
+ border: none;
396
+ border-radius: 7px;
397
+ padding: 4px 12px;
398
+ background: var(--dsw-alias-interactive-bg-hover);
399
+ color: var(--dsw-alias-label-primary);
400
+ font-size: 12px;
401
+ cursor: pointer;
402
+ }
403
+
404
+ .nodeEmpty {
405
+ display: flex;
406
+ flex-direction: column;
407
+ align-items: center;
408
+ gap: 8px;
409
+ border: 1.5px dashed color-mix(in srgb, var(--dsw-alias-label-primary) 24%, transparent);
410
+ border-radius: 9px;
411
+ margin: 6px;
412
+ padding: 14px;
413
+ background: transparent;
414
+ color: var(--dsw-alias-label-tertiary);
415
+ font-size: 12px;
416
+ cursor: pointer;
417
+ /* .nodeBody disables pointer events for node dragging; the upload button
418
+ * inside must opt back in or its onClick never fires. */
419
+ pointer-events: auto;
420
+ }
421
+
422
+ .nodeEmpty:hover {
423
+ border-color: var(--dsw-alias-brand-primary);
424
+ color: var(--dsw-alias-brand-primary);
425
+ }
426
+
427
+ .textNode { cursor: default; }
428
+
429
+ /* image nodes render as bare cards: the picture fills the frame */
430
+ .imageNode .nodeBody { margin: 6px; }
431
+
432
+ .textArea {
433
+ flex: 1;
434
+ min-height: 0;
435
+ margin: 0 8px 8px;
436
+ border: none;
437
+ resize: none;
438
+ outline: none;
439
+ background: transparent;
440
+ color: var(--dsw-alias-label-primary);
441
+ font-size: 13px;
442
+ line-height: 1.55;
443
+ padding: 4px 6px;
444
+ cursor: text;
445
+ pointer-events: auto;
446
+ }
447
+
448
+ .textArea::placeholder { color: var(--dsw-alias-label-tertiary); }
449
+
450
+ /* --------------------------------------------------- bottom dock */
451
+
452
+ .dock {
453
+ position: absolute;
454
+ left: 50%;
455
+ bottom: 14px;
456
+ transform: translateX(-50%);
457
+ z-index: 30;
458
+ display: flex;
459
+ align-items: center;
460
+ gap: 2px;
461
+ padding: 6px;
462
+ border-radius: 13px;
463
+ border: 1px solid var(--dsw-alias-border-l1);
464
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);
465
+ box-shadow: 0 10px 30px rgb(15 23 42 / 14%);
466
+ backdrop-filter: blur(10px);
467
+ }
468
+
469
+ /* macOS-dock magnification: the pointermove handler on .dock feeds a gaussian
470
+ * falloff into --dock-scale / --dock-lift, so the hovered icon swells and lifts
471
+ * while neighbours rise and fall like a zipper as the pointer slides across. */
472
+ .dock .iconButton {
473
+ position: relative;
474
+ width: 40px;
475
+ height: 40px;
476
+ border-radius: 11px;
477
+ transform: translateY(var(--dock-lift, 0px)) scale(var(--dock-scale, 1));
478
+ transform-origin: 50% 100%;
479
+ transition: transform 0.16s cubic-bezier(0.33, 1, 0.68, 1), background 0.15s ease, color 0.15s ease, box-shadow 0.18s ease;
480
+ }
481
+
482
+ .dock .iconButton:not(:disabled):hover {
483
+ z-index: 2;
484
+ box-shadow: 0 6px 14px color-mix(in srgb, var(--dsw-alias-label-primary) 20%, transparent);
485
+ }
486
+
487
+ .dock .iconButton:not(:disabled):active {
488
+ transform: translateY(var(--dock-lift, 0px)) scale(0.94);
489
+ }
490
+
491
+ .dock .iconButton[data-active] {
492
+ background: var(--dsw-alias-label-primary);
493
+ color: var(--dsw-alias-bg-layer-1);
494
+ }
495
+
496
+ .dock .iconButton[data-active]:hover { background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-1); }
497
+
498
+ .dockDivider {
499
+ width: 1px;
500
+ height: 18px;
501
+ margin: 0 4px;
502
+ background: var(--dsw-alias-border-l1);
503
+ }
504
+
505
+ /* ------------------------------------------------- floating composer */
506
+
507
+ .composer {
508
+ position: absolute;
509
+ z-index: 35;
510
+ width: 560px;
511
+ display: flex;
512
+ flex-direction: column;
513
+ gap: 8px;
514
+ padding: 10px;
515
+ border-radius: 14px;
516
+ border: 1px solid var(--dsw-alias-border-l1);
517
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 96%, transparent);
518
+ box-shadow: 0 16px 44px rgb(15 23 42 / 18%);
519
+ backdrop-filter: blur(10px);
520
+ }
521
+
522
+ .composerPrompt {
523
+ width: 100%;
524
+ min-height: 38px;
525
+ max-height: 120px;
526
+ resize: none;
527
+ border: none;
528
+ outline: none;
529
+ background: transparent;
530
+ color: var(--dsw-alias-label-primary);
531
+ font-size: 13px;
532
+ line-height: 1.5;
533
+ padding: 2px 4px;
534
+ cursor: text;
535
+ }
536
+
537
+ .composerPrompt::placeholder { color: var(--dsw-alias-label-tertiary); }
538
+
539
+ .composerMeta { display: flex; }
540
+
541
+ .composerChip {
542
+ display: inline-flex;
543
+ align-items: center;
544
+ gap: 5px;
545
+ font-size: 11px;
546
+ padding: 2px 9px;
547
+ border-radius: 999px;
548
+ border: 1px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent);
549
+ color: var(--dsw-alias-brand-primary);
550
+ background: color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent);
551
+ }
552
+
553
+ .composerControls {
554
+ display: flex;
555
+ align-items: center;
556
+ gap: 6px;
557
+ }
558
+
559
+ .composerControls select {
560
+ height: 28px;
561
+ border-radius: 999px;
562
+ border: 1px solid var(--dsw-alias-border-l1);
563
+ background: transparent;
564
+ color: var(--dsw-alias-label-primary);
565
+ font-size: 11.5px;
566
+ padding: 0 8px;
567
+ min-width: 0;
568
+ max-width: 128px;
569
+ }
570
+
571
+ /* custom dropdown button + popup (styled replacement for native <select>) */
572
+ .composerSelect {
573
+ display: inline-flex;
574
+ align-items: center;
575
+ gap: 6px;
576
+ height: 28px;
577
+ padding: 0 8px 0 11px;
578
+ border-radius: 999px;
579
+ border: 1px solid var(--dsw-alias-border-l1);
580
+ background: transparent;
581
+ color: var(--dsw-alias-label-primary);
582
+ font-size: 11.5px;
583
+ min-width: 0;
584
+ max-width: 138px;
585
+ cursor: pointer;
586
+ transition: background 0.15s ease, border-color 0.15s ease;
587
+ }
588
+
589
+ .composerSelect:hover { background: var(--dsw-alias-interactive-bg-hover); }
590
+ .composerSelect[data-open] { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); }
591
+ .composerSelect svg { flex: none; opacity: 0.65; }
592
+ .composerSelect[data-open] svg { opacity: 0.9; }
593
+
594
+ .composerSelectValue {
595
+ overflow: hidden;
596
+ text-overflow: ellipsis;
597
+ white-space: nowrap;
598
+ min-width: 0;
599
+ }
600
+
601
+ .composerSelectMenu {
602
+ position: fixed;
603
+ z-index: 95;
604
+ max-height: 264px;
605
+ overflow: auto;
606
+ padding: 5px;
607
+ border-radius: 11px;
608
+ border: 1px solid var(--dsw-alias-border-l1);
609
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 97%, transparent);
610
+ box-shadow: 0 14px 38px rgb(15 23 42 / 18%);
611
+ backdrop-filter: blur(10px);
612
+ }
613
+
614
+ .composerSelectMenu button {
615
+ display: block;
616
+ width: 100%;
617
+ border: none;
618
+ border-radius: 7px;
619
+ background: transparent;
620
+ color: var(--dsw-alias-label-primary);
621
+ font-size: 12.5px;
622
+ line-height: 1.45;
623
+ padding: 7px 10px;
624
+ cursor: pointer;
625
+ text-align: left;
626
+ white-space: nowrap;
627
+ }
628
+
629
+ .composerSelectMenu button:hover { background: var(--dsw-alias-interactive-bg-hover); }
630
+ .composerSelectMenu button[data-selected] {
631
+ color: var(--dsw-alias-brand-primary);
632
+ background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);
633
+ }
634
+
635
+ .composerSend {
636
+ margin-left: auto;
637
+ width: 34px;
638
+ height: 34px;
639
+ flex: none;
640
+ border: none;
641
+ border-radius: 50%;
642
+ background: var(--dsw-alias-label-primary);
643
+ color: var(--dsw-alias-bg-layer-1);
644
+ cursor: pointer;
645
+ display: inline-flex;
646
+ align-items: center;
647
+ justify-content: center;
648
+ }
649
+
650
+ .composerSend:hover { opacity: 0.88; }
651
+ .composerSend:disabled { opacity: 0.35; cursor: not-allowed; }
652
+
653
+ /* ------------------------------------------------------ config node */
654
+
655
+ .configNode {
656
+ padding: 0 14px 14px;
657
+ cursor: grab;
658
+ }
659
+
660
+ .configNode .nodeHeader { padding: 10px 2px 6px; font-size: 12px; }
661
+
662
+ .configLinks {
663
+ display: flex;
664
+ align-items: center;
665
+ margin: 4px 0 10px;
666
+ flex: none;
667
+ }
668
+
669
+ .configLinks .composerChip {
670
+ font-size: 11px;
671
+ padding: 3px 10px;
672
+ }
673
+
674
+ .configHint {
675
+ flex: 1;
676
+ min-height: 0;
677
+ margin: 0;
678
+ font-size: 11.5px;
679
+ line-height: 1.7;
680
+ color: var(--dsw-alias-label-tertiary);
681
+ overflow: auto;
682
+ }
683
+
684
+ /* connection handles */
685
+
686
+ .handle {
687
+ position: absolute;
688
+ top: 50%;
689
+ width: 12px;
690
+ height: 12px;
691
+ border-radius: 50%;
692
+ transform: translateY(-50%) scale(0.6);
693
+ background: var(--dsw-alias-bg-layer-1);
694
+ border: 2px solid var(--dsw-alias-brand-primary);
695
+ opacity: 0;
696
+ transition: opacity 0.12s ease, transform 0.12s ease;
697
+ cursor: crosshair;
698
+ z-index: 3;
699
+ }
700
+
701
+ .handleLeft { left: -7px; }
702
+ .handleRight { right: -7px; }
703
+
704
+ .node:hover .handle,
705
+ .nodeSelected .handle,
706
+ .nodeConnectTarget .handle {
707
+ opacity: 1;
708
+ transform: translateY(-50%) scale(1);
709
+ }
710
+
711
+ .handle:hover { background: var(--dsw-alias-brand-primary); }
712
+
713
+ /* The source handle doubles as a quick-add button (hover opens the node
714
+ * picker, drag connects) so it reads as a Dify-style big ⊕. */
715
+ .handleRight {
716
+ display: flex;
717
+ align-items: center;
718
+ justify-content: center;
719
+ width: 20px;
720
+ height: 20px;
721
+ right: -10px;
722
+ font-size: 13px;
723
+ font-weight: 600;
724
+ line-height: 1;
725
+ color: var(--dsw-alias-brand-primary);
726
+ }
727
+
728
+ .handleRight::after { content: '+'; }
729
+
730
+ .handleRight:hover { color: var(--dsw-alias-bg-layer-1); transform: translateY(-50%) scale(1.12); }
731
+
732
+ .resizeHandle {
733
+ position: absolute;
734
+ right: -5px;
735
+ bottom: -5px;
736
+ width: 14px;
737
+ height: 14px;
738
+ border-radius: 4px;
739
+ background: var(--dsw-alias-brand-primary);
740
+ border: 2px solid var(--dsw-alias-bg-layer-1);
741
+ cursor: nwse-resize;
742
+ z-index: 3;
743
+ }
744
+
745
+ /* hover toolbar */
746
+
747
+ .hoverToolbar {
748
+ position: absolute;
749
+ top: -36px;
750
+ left: 50%;
751
+ transform: translateX(-50%);
752
+ display: flex;
753
+ gap: 2px;
754
+ padding: 4px;
755
+ border-radius: 10px;
756
+ border: 1px solid var(--dsw-alias-border-l1);
757
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);
758
+ box-shadow: 0 8px 22px rgb(15 23 42 / 14%);
759
+ opacity: 0;
760
+ pointer-events: none;
761
+ transition: opacity 0.12s ease;
762
+ z-index: 4;
763
+ }
764
+
765
+ .node:hover .hoverToolbar,
766
+ .nodeSelected .hoverToolbar {
767
+ opacity: 1;
768
+ pointer-events: auto;
769
+ }
770
+
771
+ /* -------------------------------------------------------- connections */
772
+
773
+ .connectionLayer {
774
+ position: absolute;
775
+ overflow: visible;
776
+ pointer-events: none;
777
+ z-index: 0;
778
+ }
779
+
780
+ .connectionPath {
781
+ fill: none;
782
+ stroke-width: 1.8;
783
+ stroke-opacity: 0.9;
784
+ pointer-events: none;
785
+ }
786
+
787
+ .connectionActive {
788
+ stroke-width: 2.6;
789
+ stroke-opacity: 1;
790
+ filter: drop-shadow(0 0 6px color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent));
791
+ }
792
+
793
+ .connectionPreview {
794
+ fill: none;
795
+ stroke: var(--dsw-alias-brand-primary);
796
+ stroke-width: 2;
797
+ stroke-dasharray: 5 5;
798
+ pointer-events: none;
799
+ }
800
+
801
+ /* Flow overlay: a soft light band glides from the path start (source) toward
802
+ * its end (target), echoing the direction of the gradient and arrowhead. */
803
+ .connectionFlow {
804
+ fill: none;
805
+ stroke: var(--dsw-alias-brand-primary);
806
+ stroke-width: 2;
807
+ stroke-linecap: round;
808
+ stroke-dasharray: 30 90;
809
+ opacity: 0.4;
810
+ pointer-events: none;
811
+ animation: connectionFlow 1.6s linear infinite;
812
+ }
813
+
814
+ .connectionFlowActive {
815
+ stroke: var(--dsw-alias-brand-primary);
816
+ opacity: 0.85;
817
+ }
818
+
819
+ @keyframes connectionFlow {
820
+ to { stroke-dashoffset: -120; }
821
+ }
822
+
823
+ @media (prefers-reduced-motion: reduce) {
824
+ .connectionFlow { animation: none; opacity: 0.35; }
825
+ }
826
+
827
+ /* --------------------------------------------------------- empty hint */
828
+
829
+ .emptyHint {
830
+ position: absolute;
831
+ left: 50%;
832
+ top: 44%;
833
+ transform: translate(-50%, -50%);
834
+ display: flex;
835
+ flex-direction: column;
836
+ align-items: center;
837
+ gap: 6px;
838
+ text-align: center;
839
+ color: var(--dsw-alias-label-tertiary);
840
+ pointer-events: none;
841
+ z-index: 5;
842
+ }
843
+
844
+ .emptyHint strong {
845
+ font-size: 16px;
846
+ color: var(--dsw-alias-label-secondary);
847
+ }
848
+
849
+ .emptyHint span { font-size: 12.5px; max-width: 320px; line-height: 1.6; }
850
+
851
+ /* ------------------------------------------------------------ zoom dock */
852
+
853
+ .zoomDock {
854
+ position: absolute;
855
+ left: 14px;
856
+ bottom: 14px;
857
+ z-index: 30;
858
+ display: flex;
859
+ align-items: center;
860
+ gap: 6px;
861
+ padding: 6px 8px;
862
+ border-radius: 12px;
863
+ border: 1px solid var(--dsw-alias-border-l1);
864
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 88%, transparent);
865
+ box-shadow: 0 10px 30px rgb(15 23 42 / 12%);
866
+ backdrop-filter: blur(10px);
867
+ }
868
+
869
+ .zoomDock input[type="range"] {
870
+ width: 96px;
871
+ accent-color: var(--dsw-alias-brand-primary);
872
+ }
873
+
874
+ .zoomValue {
875
+ width: 40px;
876
+ text-align: right;
877
+ font-size: 11.5px;
878
+ color: var(--dsw-alias-label-secondary);
879
+ font-variant-numeric: tabular-nums;
880
+ }
881
+
882
+ /* -------------------------------------------------------------- minimap */
883
+
884
+ .minimap {
885
+ position: absolute;
886
+ left: 14px;
887
+ bottom: 62px;
888
+ z-index: 30;
889
+ width: 220px;
890
+ height: 150px;
891
+ border-radius: 12px;
892
+ border: 1px solid var(--dsw-alias-border-l1);
893
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 88%, transparent);
894
+ box-shadow: 0 10px 30px rgb(15 23 42 / 12%);
895
+ overflow: hidden;
896
+ backdrop-filter: blur(10px);
897
+ }
898
+
899
+ .minimapCanvas {
900
+ position: relative;
901
+ width: 100%;
902
+ height: 100%;
903
+ cursor: crosshair;
904
+ }
905
+
906
+ .minimapNode {
907
+ position: absolute;
908
+ border-radius: 2px;
909
+ opacity: 0.8;
910
+ }
911
+
912
+ .minimapImage { background: var(--dsw-alias-brand-primary); }
913
+ .minimapText { background: color-mix(in srgb, var(--dsw-alias-label-primary) 40%, transparent); }
914
+ .minimapSelected { outline: 1.5px solid var(--dsw-alias-brand-primary); }
915
+
916
+ .minimapViewport {
917
+ position: absolute;
918
+ border: 1.5px solid var(--dsw-alias-brand-primary);
919
+ background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);
920
+ border-radius: 2px;
921
+ pointer-events: none;
922
+ }
923
+
924
+ /* ------------------------------------------------------------ inspector */
925
+
926
+ .inspectorRow {
927
+ display: flex;
928
+ gap: 8px;
929
+ }
930
+
931
+ .inspectorRow > * { flex: 1; min-width: 0; }
932
+
933
+ /* --------------------------------------------------------- context menu */
934
+
935
+ .contextMenu {
936
+ position: fixed;
937
+ z-index: 90;
938
+ min-width: 176px;
939
+ padding: 5px;
940
+ border-radius: 11px;
941
+ border: 1px solid var(--dsw-alias-border-l1);
942
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 96%, transparent);
943
+ box-shadow: 0 16px 40px rgb(15 23 42 / 20%);
944
+ display: flex;
945
+ flex-direction: column;
946
+ }
947
+
948
+ .contextMenu button {
949
+ display: flex;
950
+ align-items: center;
951
+ gap: 8px;
952
+ border: none;
953
+ border-radius: 7px;
954
+ background: transparent;
955
+ color: var(--dsw-alias-label-primary);
956
+ font-size: 12.5px;
957
+ padding: 7px 10px;
958
+ cursor: pointer;
959
+ text-align: left;
960
+ }
961
+
962
+ .contextMenu button:hover { background: var(--dsw-alias-interactive-bg-hover); }
963
+ .contextMenu button[data-danger] { color: var(--dsw-alias-label-error); }
964
+
965
+ /* ---------------------------------------------------------- error toast */
966
+
967
+ .errorToast {
968
+ position: absolute;
969
+ left: 50%;
970
+ bottom: 18px;
971
+ transform: translateX(-50%);
972
+ z-index: 60;
973
+ display: flex;
974
+ align-items: center;
975
+ gap: 10px;
976
+ max-width: min(560px, 80%);
977
+ padding: 9px 12px;
978
+ border-radius: 10px;
979
+ border: 1px solid color-mix(in srgb, var(--dsw-alias-label-error) 35%, transparent);
980
+ background: color-mix(in srgb, var(--dsw-alias-label-error) 12%, var(--dsw-alias-bg-layer-1));
981
+ color: var(--dsw-alias-label-error);
982
+ font-size: 12.5px;
983
+ box-shadow: 0 12px 30px rgb(15 23 42 / 18%);
984
+ }
985
+
986
+ .errorToast button {
987
+ border: none;
988
+ background: transparent;
989
+ color: inherit;
990
+ cursor: pointer;
991
+ padding: 2px;
992
+ display: inline-flex;
993
+ flex: none;
994
+ }
995
+
996
+ /* ---------------------------------------------------------- picker modal */
997
+
998
+ .modalBackdrop {
999
+ position: absolute;
1000
+ inset: 0;
1001
+ z-index: 70;
1002
+ background: color-mix(in srgb, var(--dsw-alias-label-primary) 32%, transparent);
1003
+ display: flex;
1004
+ align-items: center;
1005
+ justify-content: center;
1006
+ }
1007
+
1008
+ .picker {
1009
+ width: min(720px, 92%);
1010
+ max-height: 84%;
1011
+ display: flex;
1012
+ flex-direction: column;
1013
+ border-radius: 14px;
1014
+ border: 1px solid var(--dsw-alias-border-l1);
1015
+ background: var(--dsw-alias-bg-layer-1);
1016
+ box-shadow: 0 24px 60px rgb(15 23 42 / 26%);
1017
+ overflow: hidden;
1018
+ }
1019
+
1020
+ .pickerHeader {
1021
+ display: flex;
1022
+ align-items: center;
1023
+ justify-content: space-between;
1024
+ padding: 12px 14px;
1025
+ font-size: 14px;
1026
+ border-bottom: 1px solid var(--dsw-alias-border-l1);
1027
+ }
1028
+
1029
+ .pickerHeader button {
1030
+ border: none;
1031
+ background: transparent;
1032
+ color: var(--dsw-alias-label-secondary);
1033
+ font-size: 18px;
1034
+ cursor: pointer;
1035
+ padding: 0 4px;
1036
+ }
1037
+
1038
+ .pickerTabs {
1039
+ display: flex;
1040
+ gap: 4px;
1041
+ padding: 10px 14px 0;
1042
+ }
1043
+
1044
+ .pickerTabs button {
1045
+ border: none;
1046
+ border-radius: 8px 8px 0 0;
1047
+ background: transparent;
1048
+ color: var(--dsw-alias-label-secondary);
1049
+ font-size: 12.5px;
1050
+ padding: 7px 12px;
1051
+ cursor: pointer;
1052
+ }
1053
+
1054
+ .pickerTabs button[data-active] {
1055
+ background: var(--dsw-alias-interactive-bg-hover);
1056
+ color: var(--dsw-alias-brand-primary);
1057
+ }
1058
+
1059
+ .pickerBody {
1060
+ flex: 1;
1061
+ min-height: 0;
1062
+ overflow: auto;
1063
+ padding: 14px;
1064
+ }
1065
+
1066
+ .pickerGrid {
1067
+ display: grid;
1068
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1069
+ gap: 10px;
1070
+ }
1071
+
1072
+ .pickerCard {
1073
+ display: flex;
1074
+ flex-direction: column;
1075
+ gap: 6px;
1076
+ border: 1.5px solid var(--dsw-alias-border-l1);
1077
+ border-radius: 10px;
1078
+ background: transparent;
1079
+ padding: 6px;
1080
+ cursor: pointer;
1081
+ text-align: left;
1082
+ color: var(--dsw-alias-label-primary);
1083
+ }
1084
+
1085
+ .pickerCard[data-selected] {
1086
+ border-color: var(--dsw-alias-brand-primary);
1087
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 25%, transparent);
1088
+ }
1089
+
1090
+ .pickerCard img {
1091
+ width: 100%;
1092
+ height: 110px;
1093
+ object-fit: cover;
1094
+ border-radius: 7px;
1095
+ }
1096
+
1097
+ .pickerCardPrompt {
1098
+ font-size: 11.5px;
1099
+ overflow: hidden;
1100
+ text-overflow: ellipsis;
1101
+ white-space: nowrap;
1102
+ }
1103
+
1104
+ .pickerCard small {
1105
+ color: var(--dsw-alias-label-tertiary);
1106
+ font-size: 10.5px;
1107
+ }
1108
+
1109
+ .pickerFooter {
1110
+ display: flex;
1111
+ align-items: center;
1112
+ justify-content: space-between;
1113
+ padding-top: 12px;
1114
+ font-size: 12.5px;
1115
+ color: var(--dsw-alias-label-secondary);
1116
+ }
1117
+
1118
+ .pickerFooter button,
1119
+ .generateForm button {
1120
+ height: 32px;
1121
+ padding: 0 16px;
1122
+ border: none;
1123
+ border-radius: 8px;
1124
+ background: var(--dsw-alias-brand-primary);
1125
+ color: var(--dsw-alias-bg-base, #fff);
1126
+ font-size: 12.5px;
1127
+ font-weight: 600;
1128
+ cursor: pointer;
1129
+ display: inline-flex;
1130
+ align-items: center;
1131
+ gap: 6px;
1132
+ }
1133
+
1134
+ .pickerFooter button:disabled,
1135
+ .generateForm button:disabled { opacity: 0.45; cursor: not-allowed; }
1136
+
1137
+ .uploadBox {
1138
+ display: flex;
1139
+ flex-direction: column;
1140
+ align-items: center;
1141
+ justify-content: center;
1142
+ gap: 8px;
1143
+ min-height: 220px;
1144
+ border: 1.5px dashed color-mix(in srgb, var(--dsw-alias-label-primary) 22%, transparent);
1145
+ border-radius: 12px;
1146
+ color: var(--dsw-alias-label-secondary);
1147
+ cursor: pointer;
1148
+ text-align: center;
1149
+ }
1150
+
1151
+ .uploadBox:hover { border-color: var(--dsw-alias-brand-primary); }
1152
+ .uploadBox input { display: none; }
1153
+ .uploadBox small { color: var(--dsw-alias-label-tertiary); font-size: 11px; }
1154
+
1155
+ .uploadIcon { color: var(--dsw-alias-brand-primary); }
1156
+
1157
+ .generateForm {
1158
+ display: flex;
1159
+ flex-direction: column;
1160
+ gap: 10px;
1161
+ }
1162
+
1163
+ .generateForm textarea {
1164
+ min-height: 110px;
1165
+ resize: vertical;
1166
+ border-radius: 9px;
1167
+ border: 1px solid var(--dsw-alias-border-l1);
1168
+ background: transparent;
1169
+ color: var(--dsw-alias-label-primary);
1170
+ font-size: 12.5px;
1171
+ padding: 8px 10px;
1172
+ outline: none;
1173
+ }
1174
+
1175
+ .generateForm select {
1176
+ height: 30px;
1177
+ border-radius: 8px;
1178
+ border: 1px solid var(--dsw-alias-border-l1);
1179
+ background: transparent;
1180
+ color: var(--dsw-alias-label-primary);
1181
+ font-size: 12.5px;
1182
+ padding: 0 8px;
1183
+ }
1184
+
1185
+ @keyframes canvasSpin {
1186
+ to { transform: rotate(360deg); }
1187
+ }