@dumiflex/wp-kit 0.6.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.0](https://github.com/DumiFlex/wp-kit/compare/v0.6.0...v0.7.0) (2026-11-03)
4
+
5
+
6
+ ### Features
7
+
8
+ * IconPicker all (every PrimeIcons glyph, searchable), inline, color and allow-none ([#70](https://github.com/DumiFlex/wp-kit/issues/70)) ([a0dc837](https://github.com/DumiFlex/wp-kit/commit/a0dc83716441c8061428e69faf751ab7b52ed8ae))
9
+ * RichText, the module payload previews and the post loading shapes move into the kit ([#71](https://github.com/DumiFlex/wp-kit/issues/71)) ([b92d62f](https://github.com/DumiFlex/wp-kit/commit/b92d62f87ecd44cb205d2265d7d56c9018e29144))
10
+ * SelectionBar, the one bulk-action bar over a list of ticked rows ([#69](https://github.com/DumiFlex/wp-kit/issues/69)) ([13a51e2](https://github.com/DumiFlex/wp-kit/commit/13a51e22b0e27b404700dc6f3fb2f34fd912d10a))
11
+ * SettingGroup and SettingRow, the settings page rows both apps use ([#68](https://github.com/DumiFlex/wp-kit/issues/68)) ([20f4ff8](https://github.com/DumiFlex/wp-kit/commit/20f4ff8a8c28da63fcabfd9f2c0c2bf28a4f60dd))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * draw WorkingLine's marks on a canvas so they glide in Firefox ([#67](https://github.com/DumiFlex/wp-kit/issues/67)) ([6565f15](https://github.com/DumiFlex/wp-kit/commit/6565f1575ecb224ad9e94efdef611f5c31b12849))
17
+
3
18
  ## [0.6.0](https://github.com/DumiFlex/wp-kit/compare/v0.5.0...v0.6.0) (2026-11-02)
4
19
 
5
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dumiflex/wp-kit",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Vue 3 component kit shared by the Wildcard Pipeline manager, canvas and community site.",
5
5
  "keywords": [
6
6
  "vue",
@@ -0,0 +1,254 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Bundle: what the bundle needs from outside vs what it sets (folded from
4
+ * its children), then a child list beside the selected child's own full
5
+ * preview. Narrow hosts stack the list above the preview.
6
+ */
7
+ import { computed, ref } from "vue";
8
+ import PayloadPreview from "./PayloadPreview.vue";
9
+ import ListRow from "./ListRow.vue";
10
+ import Chip from "./Chip.vue";
11
+ import { itemType, kindColour, kindIcon, moduleSubtype } from "./kind-colour";
12
+ import {
13
+ derivationRules,
14
+ fixedValues,
15
+ innerOf,
16
+ readArray,
17
+ readGroups,
18
+ readObject,
19
+ readString,
20
+ varFlow,
21
+ wildcardOptions,
22
+ type Obj,
23
+ } from "./preview-readers";
24
+
25
+ const props = defineProps<{
26
+ payload: Obj;
27
+ refNames: Record<string, string>;
28
+ refGroups: Record<string, Record<string, string[]>>;
29
+ }>();
30
+
31
+ interface Child {
32
+ key: string;
33
+ id: string;
34
+ name: string;
35
+ type: string;
36
+ enabled: boolean;
37
+ raw: Obj;
38
+ }
39
+ function childName(c: unknown): string | null {
40
+ const meta = readObject(c, "meta");
41
+ return (
42
+ readString(meta, "name") ??
43
+ readString(meta, "library_name") ??
44
+ readString(c, "name") ??
45
+ readString(readObject(c, "payload"), "var_binding")
46
+ );
47
+ }
48
+ const children = computed<Child[]>(() =>
49
+ readArray(props.payload, "children").map((c, i) => ({
50
+ key: readString(c, "id") ?? String(i),
51
+ id: readString(c, "id") ?? "",
52
+ name: childName(c) ?? "—",
53
+ type: readString(c, "type") ?? "—",
54
+ enabled: (c as Obj | null)?.["enabled"] !== false,
55
+ raw: (c && typeof c === "object" ? c : {}) as Obj,
56
+ })),
57
+ );
58
+ /** Sibling names + wildcard axes so a child constraint resolves its
59
+ * wildcards the same way the list does. */
60
+ const childRefNames = computed(() => {
61
+ const out: Record<string, string> = { ...props.refNames };
62
+ for (const c of children.value) if (c.id) out[c.id] = c.name;
63
+ return out;
64
+ });
65
+ const childRefGroups = computed(() => {
66
+ const out: Record<string, Record<string, string[]>> = { ...props.refGroups };
67
+ for (const c of children.value) {
68
+ const g = readGroups(innerOf(c.raw), "tag_groups");
69
+ if (c.id && g) out[c.id] = g;
70
+ }
71
+ return out;
72
+ });
73
+ function summary(c: Child): string {
74
+ const inner = innerOf(c.raw);
75
+ const label = c.type.replace(/_/g, " ");
76
+ switch (c.type) {
77
+ case "wildcard": {
78
+ const n = wildcardOptions(inner).length;
79
+ const b = readString(inner, "var_binding");
80
+ return `${label} · ${n} option${n === 1 ? "" : "s"}${b ? ` · $${b}` : ""}`;
81
+ }
82
+ case "fixed_values": {
83
+ const n = fixedValues(inner).length;
84
+ return `${label} · ${n} value${n === 1 ? "" : "s"}`;
85
+ }
86
+ case "derivation": {
87
+ const n = derivationRules(inner).length;
88
+ return `${label} · ${n} rule${n === 1 ? "" : "s"}`;
89
+ }
90
+ case "combine": {
91
+ const out = readString(inner, "output_var");
92
+ return out ? `${label} · $${out}` : label;
93
+ }
94
+ case "constraint": {
95
+ const s = readString(inner, "source_wildcard_id");
96
+ const t = readString(inner, "target_wildcard_id");
97
+ const name = (id: string | null): string => (id ? (childRefNames.value[id] ?? id) : "?");
98
+ return `${label} · ${name(s)} → ${name(t)}`;
99
+ }
100
+ default:
101
+ return label;
102
+ }
103
+ }
104
+ const flow = computed(() => varFlow(props.payload));
105
+ const provide = computed(() => flow.value.reads.filter((v) => !flow.value.sets.includes(v)));
106
+
107
+ const selected = ref<string | null>(null);
108
+ const current = computed(
109
+ () => children.value.find((c) => c.key === selected.value) ?? children.value[0] ?? null,
110
+ );
111
+ const counts = computed(() => {
112
+ const out: Record<string, number> = {};
113
+ for (const c of children.value) out[c.type] = (out[c.type] ?? 0) + 1;
114
+ return out;
115
+ });
116
+ </script>
117
+
118
+ <template>
119
+ <div class="wp-pv wp-bundle">
120
+ <div class="wp-pv-stats">
121
+ <div class="wp-pv-stat">
122
+ <b>{{ children.length }}</b><span>module{{ children.length === 1 ? "" : "s" }}</span>
123
+ </div>
124
+ <div
125
+ v-for="(n, t) in counts"
126
+ :key="t"
127
+ class="wp-pv-stat wp-pv-hide-narrow"
128
+ >
129
+ <b>{{ n }}</b><span>{{ String(t).replace(/_/g, " ") }}</span>
130
+ </div>
131
+ </div>
132
+ <div
133
+ v-if="provide.length || flow.sets.length"
134
+ class="wp-bd-flow"
135
+ >
136
+ <template v-if="provide.length">
137
+ <span class="wp-pv-label">You provide</span>
138
+ <div>
139
+ <code
140
+ v-for="v in provide"
141
+ :key="`p-${v}`"
142
+ class="wp-bd-var"
143
+ >${{ v }}</code>
144
+ <span class="wp-bd-hint">read inside, not set by any module here</span>
145
+ </div>
146
+ </template>
147
+ <template v-if="flow.sets.length">
148
+ <span class="wp-pv-label">It sets</span>
149
+ <div>
150
+ <code
151
+ v-for="v in flow.sets"
152
+ :key="`s-${v}`"
153
+ class="wp-bd-var"
154
+ >${{ v }}</code>
155
+ </div>
156
+ </template>
157
+ </div>
158
+ <div class="wp-bd-split">
159
+ <nav
160
+ class="wp-bd-kids wp-bundle-children"
161
+ data-wp-rows
162
+ >
163
+ <ListRow
164
+ v-for="c in children"
165
+ :key="c.key"
166
+ class="wp-bd-kid wp-bundle-child"
167
+ :selected="current?.key === c.key"
168
+ :icon="kindIcon(c.type)"
169
+ :description="`${summary(c)}${c.enabled ? '' : ' · disabled'}`"
170
+ :muted="!c.enabled"
171
+ data-test="bundle-child"
172
+ @click="selected = c.key"
173
+ >
174
+ {{ c.name }}
175
+ </ListRow>
176
+ </nav>
177
+ <div
178
+ v-if="current"
179
+ class="wp-bd-pane"
180
+ data-test="bundle-child-body"
181
+ >
182
+ <div class="wp-bd-pane__head">
183
+ <Chip v-if="moduleSubtype(current.type)" :color="kindColour(itemType('module', moduleSubtype(current.type)))">{{ itemType('module', moduleSubtype(current.type)) }}</Chip>
184
+ <Chip v-else>{{ current.type }}</Chip>
185
+ <b>{{ current.name }}</b>
186
+ </div>
187
+ <PayloadPreview
188
+ :payload="current.raw"
189
+ :ref-names="childRefNames"
190
+ :ref-groups="childRefGroups"
191
+ nested
192
+ />
193
+ </div>
194
+ </div>
195
+ </div>
196
+ </template>
197
+
198
+ <style scoped src="./preview.css"></style>
199
+ <style scoped>
200
+ .wp-bd-flow {
201
+ display: grid;
202
+ grid-template-columns: auto minmax(0, 1fr);
203
+ gap: 6px 14px;
204
+ align-items: baseline;
205
+ padding: 12px 16px;
206
+ border-bottom: 1px solid var(--wp-border);
207
+ }
208
+ .wp-bd-flow > div {
209
+ display: flex;
210
+ flex-wrap: wrap;
211
+ gap: 6px;
212
+ align-items: baseline;
213
+ }
214
+ .wp-bd-var {
215
+ font-family: var(--wp-font-mono);
216
+ font-size: 12px;
217
+ color: var(--wp-success);
218
+ }
219
+ .wp-bd-hint {
220
+ font-size: 11.5px;
221
+ color: var(--wp-text-dim);
222
+ }
223
+ .wp-bd-split {
224
+ display: grid;
225
+ grid-template-columns: 240px minmax(0, 1fr);
226
+ }
227
+ @container (max-width: 700px) {
228
+ .wp-bd-split {
229
+ grid-template-columns: minmax(0, 1fr);
230
+ }
231
+ .wp-bd-kids {
232
+ border-right: 0;
233
+ border-bottom: 1px solid var(--wp-border);
234
+ }
235
+ }
236
+ .wp-bd-kids {
237
+ display: flex;
238
+ flex-direction: column;
239
+ gap: 2px;
240
+ padding: 8px;
241
+ border-right: 1px solid var(--wp-border);
242
+ }
243
+ .wp-bd-pane {
244
+ min-width: 0;
245
+ }
246
+ .wp-bd-pane__head {
247
+ display: flex;
248
+ align-items: center;
249
+ gap: 10px;
250
+ padding: 12px 16px;
251
+ border-bottom: 1px solid var(--wp-border);
252
+ font-size: 13px;
253
+ }
254
+ </style>
@@ -0,0 +1,150 @@
1
+ <script setup lang="ts">
2
+ /** Combine: the template in its own block, its negative under it, and a
3
+ * list of the variables it reads with where each one is used. */
4
+ import { computed, ref } from "vue";
5
+ import TokenizedValue from "./TokenizedValue.vue";
6
+ import Button from "./Button.vue";
7
+ import { readString, readStrings, scanText, type Obj } from "./preview-readers";
8
+
9
+ const props = defineProps<{
10
+ inner: Obj | null;
11
+ refMap: ReadonlyMap<string, string>;
12
+ }>();
13
+
14
+ const PAGE = 6;
15
+
16
+ const template = computed(() => readString(props.inner, "template") ?? "");
17
+ const negative = computed(() => readString(props.inner, "negative") ?? "");
18
+ const output = computed(() => readString(props.inner, "output_var"));
19
+ const tplScan = computed(() => scanText(template.value));
20
+ const negScan = computed(() => scanText(negative.value));
21
+
22
+ interface InputRow {
23
+ name: string;
24
+ where: string;
25
+ }
26
+ const inputs = computed<InputRow[]>(() => {
27
+ const names = [
28
+ ...new Set([
29
+ ...readStrings(props.inner, "input_vars"),
30
+ ...tplScan.value.vars,
31
+ ...negScan.value.vars,
32
+ ...negScan.value.negVars,
33
+ ]),
34
+ ];
35
+ return names.map((name) => {
36
+ const where: string[] = [];
37
+ if (tplScan.value.vars.includes(name)) where.push("template");
38
+ if (negScan.value.vars.includes(name)) where.push("negative");
39
+ if (negScan.value.negVars.includes(name) || tplScan.value.negVars.includes(name))
40
+ where.push("negative (.neg)");
41
+ return { name, where: where.join(", ") || "declared, not used" };
42
+ });
43
+ });
44
+ const showAll = ref(false);
45
+ const shown = computed(() => (showAll.value ? inputs.value : inputs.value.slice(0, PAGE)));
46
+ </script>
47
+
48
+ <template>
49
+ <div class="wp-pv">
50
+ <div class="wp-pv-stats">
51
+ <div class="wp-pv-stat">
52
+ <b>{{ inputs.length }}</b><span>input{{ inputs.length === 1 ? "" : "s" }}</span>
53
+ </div>
54
+ <div
55
+ v-if="tplScan.picks"
56
+ class="wp-pv-stat"
57
+ >
58
+ <b>{{ tplScan.picks }}</b><span>inline pick{{ tplScan.picks === 1 ? "" : "s" }}</span>
59
+ </div>
60
+ <div class="wp-pv-stat">
61
+ <b>{{ negative ? "yes" : "no" }}</b><span>negative</span>
62
+ </div>
63
+ </div>
64
+ <div class="wp-pv-sec">
65
+ Template<template v-if="output">
66
+ → <span class="wp-cb__out">${{ output }}</span>
67
+ </template>
68
+ </div>
69
+ <div
70
+ class="wp-pv-block wp-combine__template"
71
+ data-test="combine-template"
72
+ >
73
+ <TokenizedValue
74
+ :value="template"
75
+ :uuid-to-name="refMap"
76
+ />
77
+ </div>
78
+ <template v-if="negative">
79
+ <div class="wp-pv-sec wp-cb__neglabel">
80
+ Negative
81
+ </div>
82
+ <div
83
+ class="wp-pv-block wp-pv-block--neg"
84
+ data-test="combine-negative"
85
+ >
86
+ <TokenizedValue
87
+ :value="negative"
88
+ :uuid-to-name="refMap"
89
+ />
90
+ </div>
91
+ </template>
92
+ <template v-if="inputs.length">
93
+ <div class="wp-pv-sec">
94
+ Inputs
95
+ </div>
96
+ <table class="wp-pv-table">
97
+ <colgroup>
98
+ <col style="width: 180px">
99
+ <col>
100
+ </colgroup>
101
+ <tbody>
102
+ <tr
103
+ v-for="i in shown"
104
+ :key="i.name"
105
+ data-test="combine-input"
106
+ >
107
+ <td class="wp-pv-var">
108
+ ${{ i.name }}
109
+ </td>
110
+ <td :class="{ 'wp-cb__unused': i.where === 'declared, not used' }">
111
+ {{ i.where }}
112
+ </td>
113
+ </tr>
114
+ </tbody>
115
+ </table>
116
+ <div
117
+ v-if="inputs.length > PAGE"
118
+ class="wp-pv-more"
119
+ >
120
+ <Button
121
+ variant="link"
122
+ size="sm"
123
+ @click="showAll = !showAll"
124
+ >
125
+ {{ showAll ? "Show fewer" : `Show ${inputs.length - PAGE} more` }}
126
+ </Button>
127
+ </div>
128
+ </template>
129
+ <div class="wp-cb__pad" />
130
+ </div>
131
+ </template>
132
+
133
+ <style scoped src="./preview.css"></style>
134
+ <style scoped>
135
+ .wp-cb__out {
136
+ font-family: var(--wp-font-mono);
137
+ color: var(--wp-success);
138
+ text-transform: none;
139
+ letter-spacing: 0;
140
+ }
141
+ .wp-cb__neglabel {
142
+ color: var(--wp-danger-text, var(--wp-danger));
143
+ }
144
+ .wp-cb__unused {
145
+ color: var(--wp-text-dim);
146
+ }
147
+ .wp-cb__pad {
148
+ height: 6px;
149
+ }
150
+ </style>
@@ -29,8 +29,12 @@ import {
29
29
  groupHue as tagHue,
30
30
  groupLabel,
31
31
  isBucket,
32
+ MODE_GLYPH,
33
+ MODE_LABEL,
34
+ normalizeMode,
32
35
  orderByGroups,
33
36
  toGroups,
37
+ type CellMode,
34
38
  } from "./matrix-axis";
35
39
 
36
40
  const props = defineProps<{
@@ -53,27 +57,7 @@ const colGroups = computed(() => toGroups(orderedCols.value));
53
57
  const hasColBands = computed(() => colGroups.value.some((g) => !isBucket(g)));
54
58
  const hasRowGroups = computed(() => rowGroups.value.some((g) => !isBucket(g)));
55
59
 
56
- // `only` (schema v6, linked picks): when the source fires, targets without a
57
- // rule of their own in that row are excluded.
58
- type CnMode = "allow" | "boost" | "reduce" | "exclude" | "only";
59
- const MODE_GLYPH: Record<string, string> = {
60
- only: "✓",
61
- boost: "↑",
62
- reduce: "↓",
63
- exclude: "×",
64
- allow: "·",
65
- };
66
- const MODE_LABEL: Record<CnMode, string> = {
67
- boost: "Boost",
68
- reduce: "Reduce",
69
- exclude: "Exclude",
70
- allow: "Neutral",
71
- only: "Only",
72
- };
73
-
74
- function normalizeMode(raw: string | null | undefined): CnMode {
75
- return raw === "exclude" || raw === "boost" || raw === "reduce" || raw === "only" ? raw : "allow";
76
- }
60
+ type CnMode = CellMode;
77
61
 
78
62
  /** A row holding an `only` cell shuts out its neutral cells at runtime; the
79
63
  * preview marks them so the link reads the same as in the extension. */
@@ -296,7 +280,7 @@ function fmtFactor(f: number): string {
296
280
  class="wp-cn-exc__mode"
297
281
  :class="`wp-cn-exc__mode--${exc.mode}`"
298
282
  >
299
- <span class="wp-cn-exc__glyph">{{ MODE_GLYPH[exc.mode] }}</span>
283
+ <span class="wp-cn-exc__glyph">{{ MODE_GLYPH[normalizeMode(exc.mode)] }}</span>
300
284
  {{ MODE_LABEL[normalizeMode(exc.mode)] }}
301
285
  </span>
302
286
  <span