wings-plugin-sdk 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ui-css.js ADDED
@@ -0,0 +1,695 @@
1
+ // The wings-plugin-sdk/ui styles, for Wings versions whose plugin-ui.css doesn't have them yet. Wings' own copy
2
+ // in plugin-ui.css wins whenever it's there, so a Wings update restyles every plugin. Keep both the same.
3
+
4
+ /** The version of the classes this package renders. Wings' plugin-ui.css sets it as `--wings-ui`. */
5
+ export const UI_VERSION = 2;
6
+
7
+ export const fallbackCss = `/* wings-ui 2 start
8
+ The classes wings-plugin-sdk/ui renders, a public contract: the names and data attributes stay, and Wings may
9
+ restyle them. wings-plugin-sdk carries a copy of this block for older Wings without --wings-ui, so keep the
10
+ two the same (apps/desktop/src/lib/plugin-ui.test.ts checks). Bump --wings-ui when a class is added, and only add
11
+ rules: on an older Wings the SDK's copy loads before Wings' own, so a changed rule would lose to the old one. */
12
+
13
+ /* Raised layers, like a list of suggestions or a menu, as Wings' own menus draw them. */
14
+ :root {
15
+ --wings-menu-bg: rgb(32 29 35);
16
+ --wings-menu-ring: rgb(236 233 228 / 0.1);
17
+ --wings-menu-active: rgb(255 255 255 / 0.08);
18
+ --wings-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
19
+ }
20
+
21
+ /* The page's place: a sidebar, popover, view (modal, window or tab) or settings page. */
22
+ .w-shell {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 10px;
26
+ padding: 14px;
27
+ }
28
+ .w-shell[data-place="popover"] {
29
+ gap: 12px;
30
+ padding: 12px 14px 14px;
31
+ }
32
+ .w-shell[data-place="settings"] {
33
+ gap: 12px;
34
+ }
35
+ /* A page that lays itself out edge to edge, like a canvas, fills the frame and scrolls on its own. */
36
+ .w-shell[data-fill] {
37
+ height: 100vh;
38
+ gap: 0;
39
+ padding: 0;
40
+ overflow: hidden;
41
+ }
42
+
43
+ /* Layout. */
44
+ .w-row {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 8px;
48
+ min-width: 0;
49
+ }
50
+ .w-stack {
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: 8px;
54
+ min-width: 0;
55
+ }
56
+ :is(ul, ol):is(.w-row, .w-stack, .w-card) {
57
+ margin: 0;
58
+ list-style: none;
59
+ }
60
+ :is(ul, ol):is(.w-row, .w-stack) {
61
+ padding: 0;
62
+ }
63
+ :is(.w-row, .w-stack)[data-gap="none"] {
64
+ gap: 0;
65
+ }
66
+ :is(.w-row, .w-stack)[data-gap="xs"] {
67
+ gap: 2px;
68
+ }
69
+ :is(.w-row, .w-stack)[data-gap="s"] {
70
+ gap: 4px;
71
+ }
72
+ :is(.w-row, .w-stack)[data-gap="l"] {
73
+ gap: 12px;
74
+ }
75
+ .w-row[data-align="start"] {
76
+ align-items: flex-start;
77
+ }
78
+ .w-row[data-align="baseline"] {
79
+ align-items: baseline;
80
+ }
81
+ .w-row[data-justify="end"] {
82
+ justify-content: flex-end;
83
+ }
84
+ .w-row[data-justify="between"] {
85
+ justify-content: space-between;
86
+ }
87
+ .w-row[data-wrap] {
88
+ flex-wrap: wrap;
89
+ }
90
+ :is(.w-row, .w-stack, .w-text)[data-grow] {
91
+ flex: 1;
92
+ min-width: 0;
93
+ }
94
+
95
+ /* Text. */
96
+ .w-text[data-tone="muted"] {
97
+ color: var(--wings-muted);
98
+ }
99
+ .w-text[data-tone="info"] {
100
+ color: var(--wings-info);
101
+ }
102
+ .w-text[data-tone="success"] {
103
+ color: var(--wings-success);
104
+ }
105
+ .w-text[data-tone="warn"] {
106
+ color: var(--wings-warning);
107
+ }
108
+ .w-text[data-tone="danger"] {
109
+ color: var(--wings-danger);
110
+ }
111
+ .w-text[data-size="small"] {
112
+ font-size: 12px;
113
+ }
114
+ .w-text[data-size="tiny"] {
115
+ font-size: 11px;
116
+ }
117
+ .w-text[data-weight="medium"] {
118
+ font-weight: 500;
119
+ }
120
+ .w-text[data-weight="strong"] {
121
+ font-weight: 600;
122
+ }
123
+ .w-text[data-mono] {
124
+ font-family: var(--wings-mono);
125
+ }
126
+ .w-text[data-numeric] {
127
+ font-variant-numeric: tabular-nums;
128
+ }
129
+ .w-text[data-truncate] {
130
+ display: block;
131
+ overflow: hidden;
132
+ text-overflow: ellipsis;
133
+ white-space: nowrap;
134
+ }
135
+ .w-text[data-clamp] {
136
+ display: -webkit-box;
137
+ -webkit-line-clamp: 3;
138
+ -webkit-box-orient: vertical;
139
+ overflow: hidden;
140
+ }
141
+ :is(.w-row, .w-list-item, .w-list-button) > .w-text[data-truncate] {
142
+ min-width: 0;
143
+ }
144
+
145
+ /* A page's or a card's title. */
146
+ .w-title {
147
+ font-size: 14px;
148
+ font-weight: 600;
149
+ line-height: 1.35;
150
+ overflow-wrap: anywhere;
151
+ }
152
+ .w-title[data-size="large"] {
153
+ font-size: 15px;
154
+ }
155
+
156
+ /* A section heading, with its controls on the right. */
157
+ .w-heading {
158
+ margin-top: 6px;
159
+ font-size: 12px;
160
+ font-weight: 500;
161
+ color: var(--wings-muted);
162
+ }
163
+ .w-heading-row {
164
+ display: flex;
165
+ align-items: center;
166
+ gap: 8px;
167
+ min-width: 0;
168
+ }
169
+ .w-heading-row > .w-heading {
170
+ flex: 1;
171
+ min-width: 0;
172
+ }
173
+
174
+ /* A card, and the quieter one for a summary line. */
175
+ .w-card {
176
+ display: flex;
177
+ flex-direction: column;
178
+ gap: 10px;
179
+ min-width: 0;
180
+ padding: 10px 12px;
181
+ border: 1px solid var(--wings-hairline);
182
+ border-radius: var(--wings-radius);
183
+ background: var(--wings-surface);
184
+ }
185
+ .w-card[data-size="compact"] {
186
+ gap: 6px;
187
+ padding: 8px 10px;
188
+ font-size: 12px;
189
+ }
190
+
191
+ /* A labelled bar, and the bar on its own. --w-value is how full it is. */
192
+ .w-gauge {
193
+ display: grid;
194
+ grid-template-columns: 1fr auto;
195
+ gap: 4px 8px;
196
+ font-size: 12px;
197
+ }
198
+ .w-gauge-value {
199
+ font-variant-numeric: tabular-nums;
200
+ font-weight: 500;
201
+ }
202
+ .w-gauge-note {
203
+ grid-column: 1 / -1;
204
+ color: var(--wings-muted);
205
+ }
206
+ .w-meter {
207
+ grid-column: 1 / -1;
208
+ height: 4px;
209
+ border-radius: 2px;
210
+ background: var(--wings-hairline-strong);
211
+ overflow: hidden;
212
+ }
213
+ .w-meter-fill {
214
+ display: block;
215
+ width: var(--w-value, 0%);
216
+ height: 100%;
217
+ border-radius: 2px;
218
+ background: var(--wings-info);
219
+ }
220
+ .w-meter[data-tone="warn"] > .w-meter-fill {
221
+ background: var(--wings-warning);
222
+ }
223
+ .w-meter[data-tone="danger"] > .w-meter-fill {
224
+ background: var(--wings-danger);
225
+ }
226
+
227
+ /* A row of small figures, label over value. */
228
+ .w-stats {
229
+ display: grid;
230
+ grid-auto-flow: column;
231
+ grid-auto-columns: minmax(0, 1fr);
232
+ gap: 8px;
233
+ margin: 0;
234
+ padding: 10px 12px;
235
+ border: 1px solid var(--wings-hairline);
236
+ border-radius: var(--wings-radius);
237
+ background: var(--wings-surface);
238
+ }
239
+ .w-stat-label {
240
+ font-size: 11px;
241
+ color: var(--wings-muted);
242
+ }
243
+ .w-stat-value {
244
+ margin: 2px 0 0;
245
+ font-weight: 500;
246
+ overflow: hidden;
247
+ text-overflow: ellipsis;
248
+ white-space: nowrap;
249
+ }
250
+ .w-stat-value[data-tone="warn"] {
251
+ color: var(--wings-warning);
252
+ }
253
+ .w-stat-value[data-tone="danger"] {
254
+ color: var(--wings-danger);
255
+ }
256
+
257
+ /* Label and value pairs, like a work item's fields. */
258
+ .w-props {
259
+ display: flex;
260
+ flex-direction: column;
261
+ gap: 6px;
262
+ margin: 0;
263
+ }
264
+ .w-prop {
265
+ display: grid;
266
+ grid-template-columns: 92px minmax(0, 1fr);
267
+ gap: 8px;
268
+ align-items: start;
269
+ font-size: 12px;
270
+ line-height: 22px;
271
+ }
272
+ .w-prop-label {
273
+ color: var(--wings-muted);
274
+ }
275
+ .w-prop-value {
276
+ min-width: 0;
277
+ margin: 0;
278
+ overflow-wrap: anywhere;
279
+ }
280
+
281
+ /* Lists: rows that light up under the pointer, or rows that are a button. */
282
+ .w-list {
283
+ display: flex;
284
+ flex-direction: column;
285
+ margin: 0;
286
+ padding: 0;
287
+ list-style: none;
288
+ font-size: 12px;
289
+ }
290
+ .w-list-item {
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 8px;
294
+ min-width: 0;
295
+ padding: 4px 6px;
296
+ border-radius: 6px;
297
+ }
298
+ .w-list[data-density="compact"] > .w-list-item {
299
+ padding: 0 6px;
300
+ }
301
+ .w-list-item[data-align="start"] {
302
+ align-items: flex-start;
303
+ }
304
+ .w-list-item:hover {
305
+ background: var(--wings-hover);
306
+ }
307
+ .w-list-item:has(> .w-list-button) {
308
+ display: block;
309
+ padding: 0;
310
+ }
311
+ .w-list-item:has(> .w-list-button):hover {
312
+ background: none;
313
+ }
314
+ .w-list-button {
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: flex-start;
318
+ gap: 8px;
319
+ width: 100%;
320
+ min-width: 0;
321
+ height: auto;
322
+ padding: 5px 6px;
323
+ border: 0;
324
+ border-radius: 6px;
325
+ background: none;
326
+ font-size: inherit;
327
+ font-weight: inherit;
328
+ text-align: left;
329
+ }
330
+ .w-list-button[data-align="baseline"] {
331
+ align-items: baseline;
332
+ }
333
+ .w-list-button:hover {
334
+ background: var(--wings-hover);
335
+ }
336
+ .w-list-button:active {
337
+ transform: none;
338
+ }
339
+
340
+ /* Status: a dot, a spinner, a filled pill and an outlined tag. */
341
+ .w-dot {
342
+ flex: none;
343
+ width: 6px;
344
+ height: 6px;
345
+ border-radius: 50%;
346
+ background: var(--wings-success);
347
+ }
348
+ .w-dot[data-tone="muted"] {
349
+ background: var(--wings-muted);
350
+ }
351
+ .w-dot[data-tone="info"] {
352
+ background: var(--wings-info);
353
+ }
354
+ .w-dot[data-tone="warn"] {
355
+ background: var(--wings-warning);
356
+ }
357
+ .w-dot[data-tone="danger"] {
358
+ background: var(--wings-danger);
359
+ }
360
+ /* A colour from the plugin's data, like a work item type's, in --w-color. */
361
+ .w-dot[data-color] {
362
+ background: var(--w-color);
363
+ }
364
+ .w-spinner {
365
+ flex: none;
366
+ width: 10px;
367
+ height: 10px;
368
+ border: 2px solid var(--wings-hairline-strong);
369
+ border-top-color: var(--wings-fg);
370
+ border-radius: 50%;
371
+ animation: w-spin 0.8s linear infinite;
372
+ }
373
+ .w-spinner[data-size="large"] {
374
+ width: 14px;
375
+ height: 14px;
376
+ }
377
+ @keyframes w-spin {
378
+ to {
379
+ transform: rotate(360deg);
380
+ }
381
+ }
382
+ .w-pill {
383
+ flex: none;
384
+ padding: 0 6px;
385
+ border-radius: 999px;
386
+ background: var(--wings-hover);
387
+ font-size: 11px;
388
+ font-weight: 600;
389
+ line-height: 18px;
390
+ white-space: nowrap;
391
+ }
392
+ .w-pill[data-tone="info"] {
393
+ color: var(--wings-info);
394
+ background: color-mix(in srgb, var(--wings-info) 14%, transparent);
395
+ }
396
+ .w-pill[data-tone="success"] {
397
+ color: var(--wings-success);
398
+ background: color-mix(in srgb, var(--wings-success) 14%, transparent);
399
+ }
400
+ .w-pill[data-tone="warn"] {
401
+ color: var(--wings-warning);
402
+ background: color-mix(in srgb, var(--wings-warning) 14%, transparent);
403
+ }
404
+ .w-pill[data-tone="danger"] {
405
+ color: var(--wings-danger);
406
+ background: color-mix(in srgb, var(--wings-danger) 14%, transparent);
407
+ }
408
+ .w-tag {
409
+ flex: none;
410
+ max-width: 100%;
411
+ overflow: hidden;
412
+ text-overflow: ellipsis;
413
+ white-space: nowrap;
414
+ padding: 0 5px;
415
+ border: 1px solid var(--wings-hairline-strong);
416
+ border-radius: 5px;
417
+ font-size: 10.5px;
418
+ line-height: 15px;
419
+ color: var(--wings-muted);
420
+ }
421
+ .w-tag[data-tone="info"] {
422
+ border-color: color-mix(in srgb, var(--wings-info) 45%, transparent);
423
+ color: var(--wings-info);
424
+ }
425
+ .w-tag[data-tone="success"] {
426
+ border-color: color-mix(in srgb, var(--wings-success) 45%, transparent);
427
+ color: var(--wings-success);
428
+ }
429
+ .w-tag[data-tone="warn"] {
430
+ border-color: color-mix(in srgb, var(--wings-warning) 45%, transparent);
431
+ color: var(--wings-warning);
432
+ }
433
+ .w-tag[data-tone="danger"] {
434
+ border-color: color-mix(in srgb, var(--wings-danger) 45%, transparent);
435
+ color: var(--wings-danger);
436
+ }
437
+
438
+ /* Nothing to show yet, or nothing at all. */
439
+ .w-loading {
440
+ color: var(--wings-muted);
441
+ }
442
+ .w-empty {
443
+ display: flex;
444
+ flex-direction: column;
445
+ gap: 4px;
446
+ padding: 24px 4px;
447
+ text-align: center;
448
+ }
449
+ .w-empty-detail {
450
+ color: var(--wings-muted);
451
+ }
452
+
453
+ /* Buttons. Plain buttons already look like Wings; these add the variants and sizes. */
454
+ .w-button[data-variant="primary"] {
455
+ border-color: transparent;
456
+ background: var(--wings-primary);
457
+ color: var(--wings-primary-fg);
458
+ }
459
+ .w-button[data-variant="primary"]:hover {
460
+ background: color-mix(in srgb, var(--wings-primary) 85%, transparent);
461
+ }
462
+ .w-button[data-variant="danger"] {
463
+ border-color: transparent;
464
+ background: color-mix(in srgb, var(--wings-danger) 85%, transparent);
465
+ color: #1b0f0d;
466
+ }
467
+ .w-button[data-variant="ghost"] {
468
+ border-color: transparent;
469
+ background: transparent;
470
+ }
471
+ .w-button[data-variant="ghost"]:hover {
472
+ background: var(--wings-hover);
473
+ }
474
+ /* Busy: it keeps focus, unlike a disabled button, and looks like one. */
475
+ .w-button[aria-disabled="true"] {
476
+ opacity: 0.5;
477
+ cursor: default;
478
+ }
479
+ .w-button[aria-disabled="true"]:active {
480
+ transform: none;
481
+ }
482
+ .w-button[data-size="small"] {
483
+ height: 24px;
484
+ padding: 0 8px;
485
+ font-size: 12px;
486
+ }
487
+
488
+ /* Chips: small quiet buttons, and a group of them that filters what shows. */
489
+ .w-chips {
490
+ display: flex;
491
+ align-items: center;
492
+ gap: 2px;
493
+ min-width: 0;
494
+ }
495
+ .w-chip {
496
+ height: 22px;
497
+ padding: 0 8px;
498
+ border: 0;
499
+ border-radius: 6px;
500
+ background: none;
501
+ font-size: 11px;
502
+ color: var(--wings-muted);
503
+ }
504
+ .w-chip:hover,
505
+ .w-chip[aria-pressed="true"] {
506
+ background: var(--wings-pressed);
507
+ }
508
+ .w-chip[aria-pressed="true"] {
509
+ color: var(--wings-fg);
510
+ }
511
+
512
+ /* Tabs over a panel. */
513
+ .w-tabs {
514
+ display: flex;
515
+ gap: 2px;
516
+ border-bottom: 1px solid var(--wings-hairline);
517
+ }
518
+ .w-tab {
519
+ height: 28px;
520
+ padding: 0 10px;
521
+ border: 0;
522
+ border-bottom: 2px solid transparent;
523
+ border-radius: 0;
524
+ background: none;
525
+ font-size: 12px;
526
+ color: var(--wings-muted);
527
+ }
528
+ .w-tab:hover {
529
+ background: none;
530
+ color: var(--wings-fg);
531
+ }
532
+ .w-tab:active {
533
+ transform: none;
534
+ }
535
+ .w-tab[aria-selected="true"] {
536
+ border-bottom-color: var(--wings-fg);
537
+ color: var(--wings-fg);
538
+ }
539
+
540
+ /* Form fields. Inputs, selects and text areas already look like Wings. */
541
+ .w-field {
542
+ display: flex;
543
+ flex-direction: column;
544
+ gap: 4px;
545
+ }
546
+ .w-field-label {
547
+ font-size: 12px;
548
+ font-weight: 500;
549
+ }
550
+ .w-field[data-layout="inline"] > .w-field-row {
551
+ display: flex;
552
+ align-items: center;
553
+ gap: 8px;
554
+ min-width: 0;
555
+ }
556
+ .w-field-row > .w-field-label {
557
+ flex: none;
558
+ }
559
+ .w-field-group {
560
+ display: flex;
561
+ flex-direction: column;
562
+ gap: 4px;
563
+ min-width: 0;
564
+ margin: 0;
565
+ padding: 0;
566
+ border: 0;
567
+ }
568
+ .w-field-group > legend {
569
+ margin-bottom: 4px;
570
+ padding: 0;
571
+ }
572
+ .w-field-hint {
573
+ font-size: 12px;
574
+ color: var(--wings-muted);
575
+ }
576
+ .w-field-hint[data-tone="danger"] {
577
+ color: var(--wings-danger);
578
+ }
579
+ .w-check {
580
+ display: flex;
581
+ align-items: center;
582
+ gap: 6px;
583
+ min-width: 0;
584
+ font-size: 12px;
585
+ cursor: pointer;
586
+ }
587
+ .w-check > input {
588
+ flex: none;
589
+ width: auto;
590
+ height: auto;
591
+ margin: 0;
592
+ }
593
+
594
+ /* A field with suggestions under it, and a menu under a button: raised lists of options. */
595
+ .w-combobox,
596
+ .w-menu-root {
597
+ position: relative;
598
+ min-width: 0;
599
+ }
600
+ .w-menu-root {
601
+ display: inline-block;
602
+ }
603
+ .w-listbox,
604
+ .w-menu {
605
+ position: absolute;
606
+ z-index: 10;
607
+ top: calc(100% + 4px);
608
+ left: 0;
609
+ max-height: 240px;
610
+ margin: 0;
611
+ padding: 4px;
612
+ overflow: auto;
613
+ list-style: none;
614
+ border-radius: var(--wings-radius);
615
+ background: var(--wings-menu-bg);
616
+ box-shadow:
617
+ 0 0 0 1px var(--wings-menu-ring),
618
+ var(--wings-shadow);
619
+ }
620
+ .w-listbox {
621
+ right: 0;
622
+ }
623
+ .w-listbox > ul {
624
+ margin: 0;
625
+ padding: 0;
626
+ list-style: none;
627
+ }
628
+ .w-menu {
629
+ min-width: 100%;
630
+ width: max-content;
631
+ }
632
+ .w-menu[data-align="end"] {
633
+ left: auto;
634
+ right: 0;
635
+ }
636
+ .w-listbox[hidden],
637
+ .w-menu[hidden] {
638
+ display: none;
639
+ }
640
+ .w-option,
641
+ .w-menu-item {
642
+ display: flex;
643
+ align-items: baseline;
644
+ gap: 8px;
645
+ padding: 4px 6px;
646
+ border-radius: 8px;
647
+ cursor: default;
648
+ }
649
+ .w-option[aria-selected="true"],
650
+ .w-menu-item:focus {
651
+ background: var(--wings-menu-active);
652
+ }
653
+ /* Like Wings' own menus, the highlight shows where focus is. */
654
+ .w-menu-item:focus-visible {
655
+ outline: none;
656
+ }
657
+ .w-menu-item[data-tone="danger"] {
658
+ color: var(--wings-danger);
659
+ }
660
+ .w-option-label {
661
+ flex: 1;
662
+ min-width: 0;
663
+ overflow: hidden;
664
+ text-overflow: ellipsis;
665
+ white-space: nowrap;
666
+ }
667
+ .w-option-detail {
668
+ flex: none;
669
+ max-width: 45%;
670
+ overflow: hidden;
671
+ text-overflow: ellipsis;
672
+ white-space: nowrap;
673
+ font-size: 12px;
674
+ color: var(--wings-muted);
675
+ }
676
+ .w-option-empty {
677
+ padding: 4px 6px;
678
+ color: var(--wings-muted);
679
+ }
680
+
681
+ /* More behind a summary line. */
682
+ .w-disclosure > summary {
683
+ padding: 4px 6px;
684
+ font-size: 12px;
685
+ color: var(--wings-muted);
686
+ cursor: pointer;
687
+ }
688
+
689
+ @media (prefers-reduced-motion: reduce) {
690
+ .w-spinner {
691
+ animation: none;
692
+ }
693
+ }
694
+ /* wings-ui 2 end */
695
+ `;