@dumiflex/wp-kit 0.3.0 → 0.4.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +77 -51
  2. package/LICENSE +674 -674
  3. package/README.md +73 -76
  4. package/package.json +78 -77
  5. package/src/Avatar.vue +83 -83
  6. package/src/Badge.vue +97 -97
  7. package/src/Banner.vue +118 -118
  8. package/src/BookmarkButton.vue +27 -27
  9. package/src/BundleMeta.vue +51 -51
  10. package/src/Button.vue +103 -103
  11. package/src/Card.vue +68 -68
  12. package/src/Checkbox.vue +109 -107
  13. package/src/Chip.vue +163 -97
  14. package/src/ChoiceCards.vue +133 -133
  15. package/src/CloseButton.vue +69 -69
  16. package/src/CodeBlock.vue +119 -119
  17. package/src/ColorField.vue +188 -206
  18. package/src/ColorPicker.vue +201 -201
  19. package/src/Combobox.vue +331 -334
  20. package/src/ConfirmDialog.vue +165 -163
  21. package/src/ConfirmModal.vue +147 -147
  22. package/src/ConstraintMatrixPreview.vue +708 -708
  23. package/src/ContextMenu.vue +386 -379
  24. package/src/CopyButton.vue +156 -156
  25. package/src/DataTable.vue +292 -292
  26. package/src/DatePicker.vue +234 -234
  27. package/src/DescriptionList.vue +73 -73
  28. package/src/DiffView.vue +68 -68
  29. package/src/Disclosure.vue +166 -166
  30. package/src/DragHandle.vue +49 -49
  31. package/src/Drawer.vue +131 -131
  32. package/src/DropZone.vue +196 -196
  33. package/src/DropdownMenu.vue +72 -72
  34. package/src/EmptyState.vue +111 -111
  35. package/src/Field.vue +28 -28
  36. package/src/HsvPicker.vue +208 -208
  37. package/src/Icon.vue +39 -39
  38. package/src/IconPicker.vue +209 -209
  39. package/src/ImageGrid.vue +125 -125
  40. package/src/InlineEdit.vue +183 -183
  41. package/src/Input.vue +99 -99
  42. package/src/Kbd.vue +56 -56
  43. package/src/Lightbox.vue +173 -173
  44. package/src/ListFilter.vue +85 -85
  45. package/src/ListRow.vue +204 -204
  46. package/src/LoadingMark.vue +175 -161
  47. package/src/Logo.vue +60 -60
  48. package/src/Modal.vue +78 -76
  49. package/src/ModalShell.vue +109 -107
  50. package/src/Pagination.vue +106 -106
  51. package/src/Popover.vue +180 -171
  52. package/src/PostCard.vue +210 -211
  53. package/src/ProgressBar.vue +90 -90
  54. package/src/RadioGroup.vue +177 -177
  55. package/src/RangeSlider.vue +183 -183
  56. package/src/RelativeDate.vue +35 -35
  57. package/src/ResizeHandle.vue +143 -143
  58. package/src/SearchInput.vue +192 -199
  59. package/src/Select.vue +644 -641
  60. package/src/Separator.vue +54 -54
  61. package/src/Skeleton.vue +82 -82
  62. package/src/Slider.vue +198 -198
  63. package/src/SortableList.vue +260 -260
  64. package/src/SplitPane.vue +68 -68
  65. package/src/StarButton.vue +40 -40
  66. package/src/Stepper.vue +141 -141
  67. package/src/SupportLinks.vue +93 -93
  68. package/src/TagInput.vue +297 -300
  69. package/src/Textarea.vue +57 -57
  70. package/src/Timeline.vue +92 -92
  71. package/src/ToastCard.vue +119 -119
  72. package/src/ToastHost.vue +40 -40
  73. package/src/Toggle.vue +36 -36
  74. package/src/TokenizedValue.vue +310 -310
  75. package/src/Toolbar.vue +110 -110
  76. package/src/VirtualList.vue +87 -87
  77. package/src/VisuallyHidden.vue +29 -29
  78. package/src/WpButton.vue +75 -75
  79. package/src/WpCheck.vue +104 -102
  80. package/src/WpIconButton.vue +107 -107
  81. package/src/WpImageZoom.vue +544 -544
  82. package/src/WpNumber.vue +178 -183
  83. package/src/WpSegmented.vue +175 -175
  84. package/src/WpSelect.vue +93 -89
  85. package/src/WpSwitch.vue +163 -163
  86. package/src/WpTabs.vue +188 -188
  87. package/src/_modal-motion.css +33 -33
  88. package/src/_skeleton.css +50 -50
  89. package/src/axis-color.ts +42 -42
  90. package/src/button.css +169 -169
  91. package/src/calendar.ts +59 -59
  92. package/src/clipboard.ts +50 -50
  93. package/src/colour-presets.ts +12 -12
  94. package/src/diff.ts +51 -51
  95. package/src/drop-zone.ts +41 -41
  96. package/src/field.css +50 -0
  97. package/src/flip.ts +237 -237
  98. package/src/floating-layer.ts +48 -0
  99. package/src/floating.ts +99 -99
  100. package/src/hsv.ts +100 -100
  101. package/src/icon-catalog.ts +52 -52
  102. package/src/image-zoom.ts +93 -93
  103. package/src/kind-colour.ts +28 -0
  104. package/src/logo-paths.ts +21 -21
  105. package/src/matrix-axis.ts +91 -91
  106. package/src/pagination.ts +27 -27
  107. package/src/platform-keys.ts +29 -29
  108. package/src/rich-tokenize.ts +357 -357
  109. package/src/select-types.ts +32 -32
  110. package/src/slider-math.ts +45 -45
  111. package/src/sortable.ts +22 -22
  112. package/src/theme/fonts/OFL-Fraunces.txt +93 -93
  113. package/src/theme/fonts/OFL-JetBrainsMono.txt +93 -93
  114. package/src/theme/fonts/OFL-Manrope.txt +93 -93
  115. package/src/theme/looks.css +428 -428
  116. package/src/theme/palettes.css +254 -254
  117. package/src/theme/tokens.css +2896 -2894
  118. package/src/tokens.ts +56 -56
  119. package/src/tooltip.css +43 -43
  120. package/src/tooltip.ts +146 -146
  121. package/src/useDialogFocus.ts +73 -73
  122. package/src/useGrowableField.ts +357 -357
  123. package/src/useModalKeyShield.ts +116 -116
  124. package/src/useToast.ts +48 -48
  125. package/src/wp-logo.svg +14 -14
  126. package/src/ChipToggle.vue +0 -92
  127. package/src/KindChip.vue +0 -53
  128. package/src/RatingBadge.vue +0 -42
package/src/floating.ts CHANGED
@@ -1,99 +1,99 @@
1
- /**
2
- * Where a floating panel (popover, menu, tooltip) goes next to its anchor:
3
- * on the preferred side when it fits, on the other side when it doesn't,
4
- * and always kept inside the viewport. Pure maths, no DOM, so it is the
5
- * same on the canvas and in the manager. `Popover`, `ContextMenu` and the
6
- * tooltip directive all place themselves through it.
7
- */
8
-
9
- export type Side = "top" | "bottom" | "left" | "right";
10
- export type Align = "start" | "center" | "end";
11
- export type Placement = Side | `${Side}-${Exclude<Align, "center">}`;
12
-
13
- export interface Rect {
14
- left: number;
15
- top: number;
16
- width: number;
17
- height: number;
18
- }
19
-
20
- export interface Size {
21
- width: number;
22
- height: number;
23
- }
24
-
25
- export interface PlaceOptions {
26
- placement?: Placement;
27
- /** Space between the anchor and the panel. */
28
- gap?: number;
29
- /** Space the panel keeps from the viewport edge. */
30
- margin?: number;
31
- }
32
-
33
- export interface Placed {
34
- x: number;
35
- y: number;
36
- /** The side it ended up on, after any flip. */
37
- side: Side;
38
- }
39
-
40
- const OPPOSITE: Record<Side, Side> = { top: "bottom", bottom: "top", left: "right", right: "left" };
41
-
42
- export function splitPlacement(p: Placement): { side: Side; align: Align } {
43
- const [side, align] = p.split("-") as [Side, Exclude<Align, "center"> | undefined];
44
- return { side, align: align ?? "center" };
45
- }
46
-
47
- function room(side: Side, a: Rect, view: Size, gap: number, margin: number): number {
48
- switch (side) {
49
- case "top": return a.top - gap - margin;
50
- case "bottom": return view.height - (a.top + a.height) - gap - margin;
51
- case "left": return a.left - gap - margin;
52
- case "right": return view.width - (a.left + a.width) - gap - margin;
53
- }
54
- }
55
-
56
- function clamp(v: number, min: number, max: number): number {
57
- return max < min ? min : Math.min(Math.max(v, min), max);
58
- }
59
-
60
- export function placeFloating(anchor: Rect, panel: Size, view: Size, opts: PlaceOptions = {}): Placed {
61
- const gap = opts.gap ?? 6;
62
- const margin = opts.margin ?? 8;
63
- const { side: want, align } = splitPlacement(opts.placement ?? "bottom-start");
64
- const vertical = want === "top" || want === "bottom";
65
- const need = vertical ? panel.height : panel.width;
66
-
67
- // Flip only when the other side has more room; a panel too big for both
68
- // stays on the side with more space and gets clamped.
69
- let side = want;
70
- if (room(want, anchor, view, gap, margin) < need) {
71
- const other = OPPOSITE[want];
72
- if (room(other, anchor, view, gap, margin) > room(want, anchor, view, gap, margin)) side = other;
73
- }
74
-
75
- let x: number;
76
- let y: number;
77
- if (vertical) {
78
- y = side === "bottom" ? anchor.top + anchor.height + gap : anchor.top - gap - panel.height;
79
- x = align === "start" ? anchor.left
80
- : align === "end" ? anchor.left + anchor.width - panel.width
81
- : anchor.left + (anchor.width - panel.width) / 2;
82
- } else {
83
- x = side === "right" ? anchor.left + anchor.width + gap : anchor.left - gap - panel.width;
84
- y = align === "start" ? anchor.top
85
- : align === "end" ? anchor.top + anchor.height - panel.height
86
- : anchor.top + (anchor.height - panel.height) / 2;
87
- }
88
-
89
- return {
90
- x: Math.round(clamp(x, margin, view.width - margin - panel.width)),
91
- y: Math.round(clamp(y, margin, view.height - margin - panel.height)),
92
- side,
93
- };
94
- }
95
-
96
- /** The viewport, for callers that have a window. */
97
- export function viewportSize(): Size {
98
- return { width: window.innerWidth, height: window.innerHeight };
99
- }
1
+ /**
2
+ * Where a floating panel (popover, menu, tooltip) goes next to its anchor:
3
+ * on the preferred side when it fits, on the other side when it doesn't,
4
+ * and always kept inside the viewport. Pure maths, no DOM, so it is the
5
+ * same on the canvas and in the manager. `Popover`, `ContextMenu` and the
6
+ * tooltip directive all place themselves through it.
7
+ */
8
+
9
+ export type Side = "top" | "bottom" | "left" | "right";
10
+ export type Align = "start" | "center" | "end";
11
+ export type Placement = Side | `${Side}-${Exclude<Align, "center">}`;
12
+
13
+ export interface Rect {
14
+ left: number;
15
+ top: number;
16
+ width: number;
17
+ height: number;
18
+ }
19
+
20
+ export interface Size {
21
+ width: number;
22
+ height: number;
23
+ }
24
+
25
+ export interface PlaceOptions {
26
+ placement?: Placement;
27
+ /** Space between the anchor and the panel. */
28
+ gap?: number;
29
+ /** Space the panel keeps from the viewport edge. */
30
+ margin?: number;
31
+ }
32
+
33
+ export interface Placed {
34
+ x: number;
35
+ y: number;
36
+ /** The side it ended up on, after any flip. */
37
+ side: Side;
38
+ }
39
+
40
+ const OPPOSITE: Record<Side, Side> = { top: "bottom", bottom: "top", left: "right", right: "left" };
41
+
42
+ export function splitPlacement(p: Placement): { side: Side; align: Align } {
43
+ const [side, align] = p.split("-") as [Side, Exclude<Align, "center"> | undefined];
44
+ return { side, align: align ?? "center" };
45
+ }
46
+
47
+ function room(side: Side, a: Rect, view: Size, gap: number, margin: number): number {
48
+ switch (side) {
49
+ case "top": return a.top - gap - margin;
50
+ case "bottom": return view.height - (a.top + a.height) - gap - margin;
51
+ case "left": return a.left - gap - margin;
52
+ case "right": return view.width - (a.left + a.width) - gap - margin;
53
+ }
54
+ }
55
+
56
+ function clamp(v: number, min: number, max: number): number {
57
+ return max < min ? min : Math.min(Math.max(v, min), max);
58
+ }
59
+
60
+ export function placeFloating(anchor: Rect, panel: Size, view: Size, opts: PlaceOptions = {}): Placed {
61
+ const gap = opts.gap ?? 6;
62
+ const margin = opts.margin ?? 8;
63
+ const { side: want, align } = splitPlacement(opts.placement ?? "bottom-start");
64
+ const vertical = want === "top" || want === "bottom";
65
+ const need = vertical ? panel.height : panel.width;
66
+
67
+ // Flip only when the other side has more room; a panel too big for both
68
+ // stays on the side with more space and gets clamped.
69
+ let side = want;
70
+ if (room(want, anchor, view, gap, margin) < need) {
71
+ const other = OPPOSITE[want];
72
+ if (room(other, anchor, view, gap, margin) > room(want, anchor, view, gap, margin)) side = other;
73
+ }
74
+
75
+ let x: number;
76
+ let y: number;
77
+ if (vertical) {
78
+ y = side === "bottom" ? anchor.top + anchor.height + gap : anchor.top - gap - panel.height;
79
+ x = align === "start" ? anchor.left
80
+ : align === "end" ? anchor.left + anchor.width - panel.width
81
+ : anchor.left + (anchor.width - panel.width) / 2;
82
+ } else {
83
+ x = side === "right" ? anchor.left + anchor.width + gap : anchor.left - gap - panel.width;
84
+ y = align === "start" ? anchor.top
85
+ : align === "end" ? anchor.top + anchor.height - panel.height
86
+ : anchor.top + (anchor.height - panel.height) / 2;
87
+ }
88
+
89
+ return {
90
+ x: Math.round(clamp(x, margin, view.width - margin - panel.width)),
91
+ y: Math.round(clamp(y, margin, view.height - margin - panel.height)),
92
+ side,
93
+ };
94
+ }
95
+
96
+ /** The viewport, for callers that have a window. */
97
+ export function viewportSize(): Size {
98
+ return { width: window.innerWidth, height: window.innerHeight };
99
+ }
package/src/hsv.ts CHANGED
@@ -1,100 +1,100 @@
1
- /**
2
- * HSV / RGB / hex color conversion utilities.
3
- *
4
- * `h` in `[0, 360)`, `s` and `v` in `[0, 1]`. Hex strings are `#rrggbb`
5
- * lowercase. Functions are tolerant of out-of-range input — they clamp
6
- * rather than throw, which keeps the picker UI robust under fast drags.
7
- *
8
- * Lives in shared/ so both the manager-side ColorPicker / TweaksPanel
9
- * AND the canvas-side bundle modal can drive the same HsvPicker
10
- * without a cross-package import.
11
- */
12
-
13
- export interface Hsv {
14
- h: number;
15
- s: number;
16
- v: number;
17
- }
18
-
19
- export interface Rgb {
20
- r: number;
21
- g: number;
22
- b: number;
23
- }
24
-
25
- const HEX_PATTERN = /^#?([0-9a-fA-F]{6})$/;
26
-
27
- function clamp(n: number, lo: number, hi: number): number {
28
- if (Number.isNaN(n)) return lo;
29
- return Math.min(hi, Math.max(lo, n));
30
- }
31
-
32
- export function hsvToRgb(hsv: Hsv): Rgb {
33
- const H = (((hsv.h % 360) + 360) % 360);
34
- const S = clamp(hsv.s, 0, 1);
35
- const V = clamp(hsv.v, 0, 1);
36
-
37
- const C = V * S;
38
- const X = C * (1 - Math.abs(((H / 60) % 2) - 1));
39
- const M = V - C;
40
- let r1 = 0;
41
- let g1 = 0;
42
- let b1 = 0;
43
- if (H < 60) { r1 = C; g1 = X; b1 = 0; }
44
- else if (H < 120) { r1 = X; g1 = C; b1 = 0; }
45
- else if (H < 180) { r1 = 0; g1 = C; b1 = X; }
46
- else if (H < 240) { r1 = 0; g1 = X; b1 = C; }
47
- else if (H < 300) { r1 = X; g1 = 0; b1 = C; }
48
- else { r1 = C; g1 = 0; b1 = X; }
49
- return {
50
- r: Math.round((r1 + M) * 255),
51
- g: Math.round((g1 + M) * 255),
52
- b: Math.round((b1 + M) * 255),
53
- };
54
- }
55
-
56
- export function rgbToHsv(rgb: Rgb): Hsv {
57
- const R = clamp(rgb.r, 0, 255) / 255;
58
- const G = clamp(rgb.g, 0, 255) / 255;
59
- const B = clamp(rgb.b, 0, 255) / 255;
60
- const max = Math.max(R, G, B);
61
- const min = Math.min(R, G, B);
62
- const delta = max - min;
63
-
64
- let h = 0;
65
- if (delta > 0) {
66
- if (max === R) h = ((G - B) / delta) % 6;
67
- else if (max === G) h = (B - R) / delta + 2;
68
- else h = (R - G) / delta + 4;
69
- h *= 60;
70
- if (h < 0) h += 360;
71
- }
72
- const s = max === 0 ? 0 : delta / max;
73
- const v = max;
74
- return { h, s, v };
75
- }
76
-
77
- export function rgbToHex(rgb: Rgb): string {
78
- const part = (n: number): string => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0");
79
- return `#${part(rgb.r)}${part(rgb.g)}${part(rgb.b)}`;
80
- }
81
-
82
- export function hexToRgb(hex: string): Rgb | null {
83
- const matches = hex.trim().match(HEX_PATTERN);
84
- if (!matches) return null;
85
- const v = matches[1];
86
- return {
87
- r: parseInt(v.slice(0, 2), 16),
88
- g: parseInt(v.slice(2, 4), 16),
89
- b: parseInt(v.slice(4, 6), 16),
90
- };
91
- }
92
-
93
- export function hexToHsv(hex: string): Hsv | null {
94
- const rgb = hexToRgb(hex);
95
- return rgb ? rgbToHsv(rgb) : null;
96
- }
97
-
98
- export function hsvToHex(hsv: Hsv): string {
99
- return rgbToHex(hsvToRgb(hsv));
100
- }
1
+ /**
2
+ * HSV / RGB / hex color conversion utilities.
3
+ *
4
+ * `h` in `[0, 360)`, `s` and `v` in `[0, 1]`. Hex strings are `#rrggbb`
5
+ * lowercase. Functions are tolerant of out-of-range input — they clamp
6
+ * rather than throw, which keeps the picker UI robust under fast drags.
7
+ *
8
+ * Lives in shared/ so both the manager-side ColorPicker / TweaksPanel
9
+ * AND the canvas-side bundle modal can drive the same HsvPicker
10
+ * without a cross-package import.
11
+ */
12
+
13
+ export interface Hsv {
14
+ h: number;
15
+ s: number;
16
+ v: number;
17
+ }
18
+
19
+ export interface Rgb {
20
+ r: number;
21
+ g: number;
22
+ b: number;
23
+ }
24
+
25
+ const HEX_PATTERN = /^#?([0-9a-fA-F]{6})$/;
26
+
27
+ function clamp(n: number, lo: number, hi: number): number {
28
+ if (Number.isNaN(n)) return lo;
29
+ return Math.min(hi, Math.max(lo, n));
30
+ }
31
+
32
+ export function hsvToRgb(hsv: Hsv): Rgb {
33
+ const H = (((hsv.h % 360) + 360) % 360);
34
+ const S = clamp(hsv.s, 0, 1);
35
+ const V = clamp(hsv.v, 0, 1);
36
+
37
+ const C = V * S;
38
+ const X = C * (1 - Math.abs(((H / 60) % 2) - 1));
39
+ const M = V - C;
40
+ let r1 = 0;
41
+ let g1 = 0;
42
+ let b1 = 0;
43
+ if (H < 60) { r1 = C; g1 = X; b1 = 0; }
44
+ else if (H < 120) { r1 = X; g1 = C; b1 = 0; }
45
+ else if (H < 180) { r1 = 0; g1 = C; b1 = X; }
46
+ else if (H < 240) { r1 = 0; g1 = X; b1 = C; }
47
+ else if (H < 300) { r1 = X; g1 = 0; b1 = C; }
48
+ else { r1 = C; g1 = 0; b1 = X; }
49
+ return {
50
+ r: Math.round((r1 + M) * 255),
51
+ g: Math.round((g1 + M) * 255),
52
+ b: Math.round((b1 + M) * 255),
53
+ };
54
+ }
55
+
56
+ export function rgbToHsv(rgb: Rgb): Hsv {
57
+ const R = clamp(rgb.r, 0, 255) / 255;
58
+ const G = clamp(rgb.g, 0, 255) / 255;
59
+ const B = clamp(rgb.b, 0, 255) / 255;
60
+ const max = Math.max(R, G, B);
61
+ const min = Math.min(R, G, B);
62
+ const delta = max - min;
63
+
64
+ let h = 0;
65
+ if (delta > 0) {
66
+ if (max === R) h = ((G - B) / delta) % 6;
67
+ else if (max === G) h = (B - R) / delta + 2;
68
+ else h = (R - G) / delta + 4;
69
+ h *= 60;
70
+ if (h < 0) h += 360;
71
+ }
72
+ const s = max === 0 ? 0 : delta / max;
73
+ const v = max;
74
+ return { h, s, v };
75
+ }
76
+
77
+ export function rgbToHex(rgb: Rgb): string {
78
+ const part = (n: number): string => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0");
79
+ return `#${part(rgb.r)}${part(rgb.g)}${part(rgb.b)}`;
80
+ }
81
+
82
+ export function hexToRgb(hex: string): Rgb | null {
83
+ const matches = hex.trim().match(HEX_PATTERN);
84
+ if (!matches) return null;
85
+ const v = matches[1];
86
+ return {
87
+ r: parseInt(v.slice(0, 2), 16),
88
+ g: parseInt(v.slice(2, 4), 16),
89
+ b: parseInt(v.slice(4, 6), 16),
90
+ };
91
+ }
92
+
93
+ export function hexToHsv(hex: string): Hsv | null {
94
+ const rgb = hexToRgb(hex);
95
+ return rgb ? rgbToHsv(rgb) : null;
96
+ }
97
+
98
+ export function hsvToHex(hsv: Hsv): string {
99
+ return rgbToHex(hsvToRgb(hsv));
100
+ }
@@ -1,52 +1,52 @@
1
- /**
2
- * Curated PrimeIcons glyphs offered by `IconPicker.vue`.
3
- *
4
- * PrimeIcons ships ~314 glyphs; this is a subset. The full set would need its
5
- * own search UI, and most of it (chevrons, spinners, media transport, brand
6
- * logos) means nothing as a library label. The groups below follow the kinds
7
- * of things people actually categorise prompt modules by.
8
- *
9
- * Names are PrimeIcons ids MINUS the `pi-` prefix (`"user"`, not `"pi-user"`)
10
- * — that is what the engine's `icon` column holds and what `Icon.vue` expects.
11
- *
12
- * In a plain module rather than inside the SFC so `icon-catalog.test.ts` can
13
- * check every name against the installed `primeicons.css` without mounting a
14
- * component. That test is the point: a name with no matching glyph renders as
15
- * an EMPTY CELL, which looks like a layout bug rather than a typo, and `brush`
16
- * shipped that way because nothing verified the list.
17
- */
18
- export interface IconGroup {
19
- label: string;
20
- icons: string[];
21
- }
22
-
23
- export const ICON_GROUPS: IconGroup[] = [
24
- {
25
- label: "Subject",
26
- icons: ["user", "users", "heart", "star", "eye", "face-smile", "crown", "id-card"],
27
- },
28
- {
29
- // `sliders-h` stands in for the art-supply glyph PrimeIcons doesn't have —
30
- // there is no brush/paint icon in the set, and `palette` already carries
31
- // "art style". Sliders read as "tune the look".
32
- label: "Style & look",
33
- icons: ["palette", "pencil", "sliders-h", "camera", "image", "sparkles", "sun", "moon"],
34
- },
35
- {
36
- label: "Scene",
37
- icons: ["home", "building", "map", "map-marker", "globe", "compass", "car", "cloud"],
38
- },
39
- {
40
- label: "Structure",
41
- icons: ["box", "folder", "book", "tag", "tags", "list", "sitemap", "objects-column"],
42
- },
43
- {
44
- label: "Signals",
45
- icons: ["bolt", "flag", "bell", "shield", "lock", "wrench", "filter", "info-circle"],
46
- },
47
- ];
48
-
49
- /** Every offered glyph, flattened — for tests and any future search box. */
50
- export function allCuratedIcons(): string[] {
51
- return ICON_GROUPS.flatMap((g) => g.icons);
52
- }
1
+ /**
2
+ * Curated PrimeIcons glyphs offered by `IconPicker.vue`.
3
+ *
4
+ * PrimeIcons ships ~314 glyphs; this is a subset. The full set would need its
5
+ * own search UI, and most of it (chevrons, spinners, media transport, brand
6
+ * logos) means nothing as a library label. The groups below follow the kinds
7
+ * of things people actually categorise prompt modules by.
8
+ *
9
+ * Names are PrimeIcons ids MINUS the `pi-` prefix (`"user"`, not `"pi-user"`)
10
+ * — that is what the engine's `icon` column holds and what `Icon.vue` expects.
11
+ *
12
+ * In a plain module rather than inside the SFC so `icon-catalog.test.ts` can
13
+ * check every name against the installed `primeicons.css` without mounting a
14
+ * component. That test is the point: a name with no matching glyph renders as
15
+ * an EMPTY CELL, which looks like a layout bug rather than a typo, and `brush`
16
+ * shipped that way because nothing verified the list.
17
+ */
18
+ export interface IconGroup {
19
+ label: string;
20
+ icons: string[];
21
+ }
22
+
23
+ export const ICON_GROUPS: IconGroup[] = [
24
+ {
25
+ label: "Subject",
26
+ icons: ["user", "users", "heart", "star", "eye", "face-smile", "crown", "id-card"],
27
+ },
28
+ {
29
+ // `sliders-h` stands in for the art-supply glyph PrimeIcons doesn't have —
30
+ // there is no brush/paint icon in the set, and `palette` already carries
31
+ // "art style". Sliders read as "tune the look".
32
+ label: "Style & look",
33
+ icons: ["palette", "pencil", "sliders-h", "camera", "image", "sparkles", "sun", "moon"],
34
+ },
35
+ {
36
+ label: "Scene",
37
+ icons: ["home", "building", "map", "map-marker", "globe", "compass", "car", "cloud"],
38
+ },
39
+ {
40
+ label: "Structure",
41
+ icons: ["box", "folder", "book", "tag", "tags", "list", "sitemap", "objects-column"],
42
+ },
43
+ {
44
+ label: "Signals",
45
+ icons: ["bolt", "flag", "bell", "shield", "lock", "wrench", "filter", "info-circle"],
46
+ },
47
+ ];
48
+
49
+ /** Every offered glyph, flattened — for tests and any future search box. */
50
+ export function allCuratedIcons(): string[] {
51
+ return ICON_GROUPS.flatMap((g) => g.icons);
52
+ }