@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 +15 -0
- package/package.json +1 -1
- package/src/BundlePreview.vue +254 -0
- package/src/CombinePreview.vue +150 -0
- package/src/ConstraintMatrixPreview.vue +6 -22
- package/src/ConstraintPreview.vue +259 -0
- package/src/DerivationCondition.vue +68 -0
- package/src/DerivationPreview.vue +345 -0
- package/src/FixedValuesPreview.vue +184 -0
- package/src/IconPicker.vue +31 -102
- package/src/IconPickerPanel.vue +132 -0
- package/src/LoadingMark.vue +6 -61
- package/src/PayloadPreview.vue +154 -0
- package/src/PostDetailSkeleton.vue +65 -0
- package/src/PostGridSkeleton.vue +61 -0
- package/src/PreviewGroup.vue +75 -0
- package/src/ProfileSkeleton.vue +73 -0
- package/src/RichText.vue +61 -0
- package/src/SelectionBar.vue +90 -0
- package/src/SettingGroup.vue +57 -0
- package/src/SettingRow.vue +127 -0
- package/src/WildcardPreview.vue +607 -0
- package/src/WorkingLine.vue +38 -124
- package/src/canvas-motion.ts +251 -0
- package/src/kind-colour.ts +24 -1
- package/src/linkify.ts +142 -0
- package/src/loading-mark-motion.ts +12 -155
- package/src/matrix-axis.ts +28 -0
- package/src/pi-icons.ts +51 -0
- package/src/preview-readers.ts +401 -0
- package/src/preview.css +194 -0
- package/src/tokens.ts +8 -2
- package/src/working-line-motion.ts +135 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Constraint: counts up top, then the grouped matrix (ConstraintMatrixPreview)
|
|
4
|
+
* or, for a big matrix, a searchable list of only the cells that do
|
|
5
|
+
* something. The search narrows rows / columns in both views.
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from "vue";
|
|
8
|
+
import ConstraintMatrixPreview from "./ConstraintMatrixPreview.vue";
|
|
9
|
+
import { MODE_GLYPH as GLYPH, MODE_LABEL as LABEL, normalizeMode, type CellMode as Mode } from "./matrix-axis";
|
|
10
|
+
import {
|
|
11
|
+
readArray,
|
|
12
|
+
readGroups,
|
|
13
|
+
readNumber,
|
|
14
|
+
readObject,
|
|
15
|
+
readString,
|
|
16
|
+
type Obj,
|
|
17
|
+
} from "./preview-readers";
|
|
18
|
+
import WpSegmented, { type SegmentedOption } from "./WpSegmented.vue";
|
|
19
|
+
import SearchInput from "./SearchInput.vue";
|
|
20
|
+
|
|
21
|
+
const props = defineProps<{
|
|
22
|
+
inner: Obj | null;
|
|
23
|
+
refMap: ReadonlyMap<string, string>;
|
|
24
|
+
refNames: Record<string, string>;
|
|
25
|
+
refGroups: Record<string, Record<string, string[]>>;
|
|
26
|
+
}>();
|
|
27
|
+
|
|
28
|
+
interface Cell {
|
|
29
|
+
mode: Mode;
|
|
30
|
+
factor: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const matrix = computed<Record<string, Record<string, Cell>>>(() => {
|
|
34
|
+
const m = readObject(props.inner, "matrix");
|
|
35
|
+
if (!m) return {};
|
|
36
|
+
const out: Record<string, Record<string, Cell>> = {};
|
|
37
|
+
for (const row of Object.keys(m)) {
|
|
38
|
+
const byCol = readObject(m, row);
|
|
39
|
+
if (!byCol) continue;
|
|
40
|
+
out[row] = {};
|
|
41
|
+
for (const col of Object.keys(byCol)) {
|
|
42
|
+
const e = readObject(byCol, col);
|
|
43
|
+
if (!e) continue;
|
|
44
|
+
out[row][col] = { mode: normalizeMode(readString(e, "mode")), factor: readNumber(e, "factor") ?? 1 };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
});
|
|
49
|
+
const rows = computed(() => Object.keys(matrix.value));
|
|
50
|
+
/** Union of every row's columns, first-seen order. */
|
|
51
|
+
const cols = computed(() => {
|
|
52
|
+
const seen = new Set<string>();
|
|
53
|
+
for (const r of rows.value) for (const c of Object.keys(matrix.value[r] ?? {})) seen.add(c);
|
|
54
|
+
return [...seen];
|
|
55
|
+
});
|
|
56
|
+
/** Axis names: cached name → dependency name → raw id → role word. */
|
|
57
|
+
function axisName(idKey: string, nameKey: string, role: string): string {
|
|
58
|
+
const id = readString(props.inner, idKey);
|
|
59
|
+
return readString(props.inner, nameKey) ?? (id ? props.refNames[id] : undefined) ?? id ?? role;
|
|
60
|
+
}
|
|
61
|
+
const sourceName = computed(() => axisName("source_wildcard_id", "source_wildcard_name", "source"));
|
|
62
|
+
const targetName = computed(() => axisName("target_wildcard_id", "target_wildcard_name", "target"));
|
|
63
|
+
/** Embedded axis snapshot first, then the dependency's tag_groups. */
|
|
64
|
+
function axisGroups(embeddedKey: string, idKey: string): Record<string, string[]> {
|
|
65
|
+
const embedded = readGroups(props.inner, embeddedKey);
|
|
66
|
+
if (embedded) return embedded;
|
|
67
|
+
const id = readString(props.inner, idKey);
|
|
68
|
+
return (id ? props.refGroups[id] : undefined) ?? {};
|
|
69
|
+
}
|
|
70
|
+
const sourceGroups = computed(() => axisGroups("source_groups", "source_wildcard_id"));
|
|
71
|
+
const targetGroups = computed(() => axisGroups("target_groups", "target_wildcard_id"));
|
|
72
|
+
/** Library exceptions only; instance `extra_exceptions` never publish. */
|
|
73
|
+
const exceptions = computed(() =>
|
|
74
|
+
readArray(props.inner, "exceptions").map((e) => ({
|
|
75
|
+
source: readString(e, "source_value") ?? readString(e, "source") ?? "",
|
|
76
|
+
target: readString(e, "target_value") ?? readString(e, "target") ?? "",
|
|
77
|
+
mode: normalizeMode(readString(e, "mode")),
|
|
78
|
+
factor: readNumber(e, "factor") ?? 1,
|
|
79
|
+
})),
|
|
80
|
+
);
|
|
81
|
+
const targetSelect = computed(() => {
|
|
82
|
+
const sel = readObject(props.inner, "target_select");
|
|
83
|
+
const mode = readString(sel, "mode");
|
|
84
|
+
if (!mode) return null;
|
|
85
|
+
const count = readNumber(sel, "count");
|
|
86
|
+
return count !== null ? `${mode} ${count}` : mode;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const ruleCells = computed(() => {
|
|
90
|
+
const out: { row: string; col: string; cell: Cell }[] = [];
|
|
91
|
+
for (const r of rows.value)
|
|
92
|
+
for (const c of cols.value) {
|
|
93
|
+
const cell = matrix.value[r]?.[c];
|
|
94
|
+
if (cell && cell.mode !== "allow") out.push({ row: r, col: c, cell });
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
});
|
|
98
|
+
const excludeCount = computed(() => ruleCells.value.filter((x) => x.cell.mode === "exclude").length);
|
|
99
|
+
|
|
100
|
+
// ─── search + view ───
|
|
101
|
+
const search = ref("");
|
|
102
|
+
const q = computed(() => search.value.trim().toLowerCase());
|
|
103
|
+
const shownRows = computed(() => {
|
|
104
|
+
if (!q.value) return rows.value;
|
|
105
|
+
const hit = rows.value.filter((r) => r.toLowerCase().includes(q.value));
|
|
106
|
+
return hit.length ? hit : rows.value;
|
|
107
|
+
});
|
|
108
|
+
const shownCols = computed(() => {
|
|
109
|
+
if (!q.value) return cols.value;
|
|
110
|
+
const hit = cols.value.filter((c) => c.toLowerCase().includes(q.value));
|
|
111
|
+
return hit.length ? hit : cols.value;
|
|
112
|
+
});
|
|
113
|
+
const big = computed(() => rows.value.length * cols.value.length > 60);
|
|
114
|
+
const view = ref<"matrix" | "list">("matrix");
|
|
115
|
+
const VIEW_OPTIONS: SegmentedOption<"matrix" | "list">[] = [
|
|
116
|
+
{ value: "matrix", label: "Matrix" },
|
|
117
|
+
{ value: "list", label: "Rules list" },
|
|
118
|
+
];
|
|
119
|
+
const listRows = computed(() =>
|
|
120
|
+
ruleCells.value.filter(
|
|
121
|
+
(x) => !q.value || x.row.toLowerCase().includes(q.value) || x.col.toLowerCase().includes(q.value),
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
function fmtFactor(f: number): string {
|
|
125
|
+
return Number.isInteger(f) ? String(f) : f.toFixed(2).replace(/\.?0+$/, "");
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<template>
|
|
130
|
+
<div class="wp-pv">
|
|
131
|
+
<div class="wp-pv-stats">
|
|
132
|
+
<div class="wp-pv-stat">
|
|
133
|
+
<b>{{ rows.length }} × {{ cols.length }}</b><span>sources × targets</span>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="wp-pv-stat">
|
|
136
|
+
<b>{{ ruleCells.length }}</b><span>rules set</span>
|
|
137
|
+
</div>
|
|
138
|
+
<div
|
|
139
|
+
v-if="excludeCount"
|
|
140
|
+
class="wp-pv-stat"
|
|
141
|
+
>
|
|
142
|
+
<b>{{ excludeCount }}</b><span>excludes</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div
|
|
145
|
+
v-if="exceptions.length"
|
|
146
|
+
class="wp-pv-stat"
|
|
147
|
+
>
|
|
148
|
+
<b>{{ exceptions.length }}</b><span>exception{{ exceptions.length === 1 ? "" : "s" }}</span>
|
|
149
|
+
</div>
|
|
150
|
+
<div
|
|
151
|
+
v-if="targetSelect"
|
|
152
|
+
class="wp-pv-stat"
|
|
153
|
+
>
|
|
154
|
+
<b>{{ targetSelect }}</b><span>reach</span>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
<div
|
|
158
|
+
v-if="big"
|
|
159
|
+
class="wp-pv-tool"
|
|
160
|
+
>
|
|
161
|
+
<SearchInput
|
|
162
|
+
v-model="search"
|
|
163
|
+
placeholder="Filter rows or columns…"
|
|
164
|
+
data-test="constraint-search"
|
|
165
|
+
class="wp-pv-search"
|
|
166
|
+
/>
|
|
167
|
+
<span class="wp-pv-label">Show</span>
|
|
168
|
+
<WpSegmented
|
|
169
|
+
v-model="view"
|
|
170
|
+
:options="VIEW_OPTIONS"
|
|
171
|
+
size="sm"
|
|
172
|
+
aria-label="Show"
|
|
173
|
+
test-prefix="constraint"
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
<div
|
|
177
|
+
v-if="view === 'matrix'"
|
|
178
|
+
class="wp-cn-wrap"
|
|
179
|
+
>
|
|
180
|
+
<ConstraintMatrixPreview
|
|
181
|
+
:rows="shownRows"
|
|
182
|
+
:cols="shownCols"
|
|
183
|
+
:matrix="matrix"
|
|
184
|
+
:source-name="sourceName"
|
|
185
|
+
:target-name="targetName"
|
|
186
|
+
:source-groups="sourceGroups"
|
|
187
|
+
:target-groups="targetGroups"
|
|
188
|
+
:exceptions="exceptions"
|
|
189
|
+
:target-select="targetSelect"
|
|
190
|
+
:ref-map="refMap"
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
<template v-else>
|
|
194
|
+
<table class="wp-pv-table">
|
|
195
|
+
<thead>
|
|
196
|
+
<tr>
|
|
197
|
+
<th>{{ sourceName }}</th>
|
|
198
|
+
<th>{{ targetName }}</th>
|
|
199
|
+
<th>Rule</th>
|
|
200
|
+
</tr>
|
|
201
|
+
</thead>
|
|
202
|
+
<tbody>
|
|
203
|
+
<tr
|
|
204
|
+
v-for="x in listRows"
|
|
205
|
+
:key="`${x.row}|${x.col}`"
|
|
206
|
+
data-test="constraint-rule"
|
|
207
|
+
>
|
|
208
|
+
<td>{{ x.row }}</td>
|
|
209
|
+
<td>{{ x.col }}</td>
|
|
210
|
+
<td>
|
|
211
|
+
<span
|
|
212
|
+
class="wp-cn-rule"
|
|
213
|
+
:data-mode="x.cell.mode"
|
|
214
|
+
>{{ GLYPH[x.cell.mode] }} {{ LABEL[x.cell.mode] }}<template v-if="x.cell.mode === 'boost' || x.cell.mode === 'reduce'"> ×{{ fmtFactor(x.cell.factor) }}</template></span>
|
|
215
|
+
</td>
|
|
216
|
+
</tr>
|
|
217
|
+
</tbody>
|
|
218
|
+
</table>
|
|
219
|
+
<p
|
|
220
|
+
v-if="!listRows.length"
|
|
221
|
+
class="wp-pv-empty"
|
|
222
|
+
>
|
|
223
|
+
No rules match.
|
|
224
|
+
</p>
|
|
225
|
+
</template>
|
|
226
|
+
</div>
|
|
227
|
+
</template>
|
|
228
|
+
|
|
229
|
+
<style scoped src="./preview.css"></style>
|
|
230
|
+
<style scoped>
|
|
231
|
+
.wp-cn-wrap {
|
|
232
|
+
padding: 14px 16px 16px;
|
|
233
|
+
min-width: 0;
|
|
234
|
+
}
|
|
235
|
+
.wp-cn-rule {
|
|
236
|
+
font-family: var(--wp-font-mono);
|
|
237
|
+
font-size: 11.5px;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
padding: 2px 7px;
|
|
240
|
+
border-radius: min(5px, var(--wp-radius-cap, 999px));
|
|
241
|
+
background: var(--wp-bg-3);
|
|
242
|
+
}
|
|
243
|
+
.wp-cn-rule[data-mode="boost"] {
|
|
244
|
+
color: var(--wp-success);
|
|
245
|
+
background: color-mix(in oklab, var(--wp-success) 14%, transparent);
|
|
246
|
+
}
|
|
247
|
+
.wp-cn-rule[data-mode="reduce"] {
|
|
248
|
+
color: var(--wp-warn);
|
|
249
|
+
background: color-mix(in oklab, var(--wp-warn) 14%, transparent);
|
|
250
|
+
}
|
|
251
|
+
.wp-cn-rule[data-mode="exclude"] {
|
|
252
|
+
color: var(--wp-danger-text, var(--wp-danger));
|
|
253
|
+
background: color-mix(in oklab, var(--wp-danger) 15%, transparent);
|
|
254
|
+
}
|
|
255
|
+
.wp-cn-rule[data-mode="only"] {
|
|
256
|
+
color: color-mix(in oklab, var(--wp-info) 55%, white);
|
|
257
|
+
background: color-mix(in oklab, var(--wp-info) 16%, transparent);
|
|
258
|
+
}
|
|
259
|
+
</style>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** One derivation condition: a single test, or an ALL OF / ANY OF group
|
|
3
|
+
* drawn as a nested box (schema v7 AND / OR). */
|
|
4
|
+
import type { Condition } from "./preview-readers";
|
|
5
|
+
|
|
6
|
+
defineOptions({ name: "DerivationCondition" });
|
|
7
|
+
defineProps<{ cond: Condition }>();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<span
|
|
12
|
+
v-if="cond.kind === 'test'"
|
|
13
|
+
class="wp-dv-test"
|
|
14
|
+
data-test="derivation-test"
|
|
15
|
+
>
|
|
16
|
+
{{ cond.var }} <em>{{ cond.op.replace(/_/g, " ") }}</em>{{ cond.value !== null ? ` ${cond.value}` : "" }}
|
|
17
|
+
</span>
|
|
18
|
+
<div
|
|
19
|
+
v-else
|
|
20
|
+
class="wp-dv-group"
|
|
21
|
+
:data-match="cond.match"
|
|
22
|
+
data-test="derivation-group"
|
|
23
|
+
>
|
|
24
|
+
<span class="wp-dv-group__head">{{ cond.match === "any" ? "ANY OF" : "ALL OF" }}</span>
|
|
25
|
+
<DerivationCondition
|
|
26
|
+
v-for="(c, i) in cond.conditions"
|
|
27
|
+
:key="i"
|
|
28
|
+
:cond="c"
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<style scoped>
|
|
34
|
+
.wp-dv-test {
|
|
35
|
+
display: inline-block;
|
|
36
|
+
align-self: flex-start;
|
|
37
|
+
font-family: var(--wp-font-mono);
|
|
38
|
+
font-size: 12px;
|
|
39
|
+
padding: 2px 8px;
|
|
40
|
+
border-radius: min(5px, var(--wp-radius-cap, 999px));
|
|
41
|
+
background: var(--wp-bg-3);
|
|
42
|
+
color: var(--wp-text);
|
|
43
|
+
overflow-wrap: anywhere;
|
|
44
|
+
}
|
|
45
|
+
.wp-dv-test em {
|
|
46
|
+
font-style: normal;
|
|
47
|
+
color: var(--wp-text-muted);
|
|
48
|
+
}
|
|
49
|
+
.wp-dv-group {
|
|
50
|
+
display: inline-flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
gap: 5px;
|
|
53
|
+
max-width: 100%;
|
|
54
|
+
padding: 6px 8px;
|
|
55
|
+
border: 1px dashed var(--wp-border-strong);
|
|
56
|
+
border-radius: min(8px, var(--wp-radius-cap, 999px));
|
|
57
|
+
}
|
|
58
|
+
.wp-dv-group__head {
|
|
59
|
+
font-family: var(--wp-font-mono);
|
|
60
|
+
font-size: 9.5px;
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
letter-spacing: 0.08em;
|
|
63
|
+
color: var(--wp-constraint-source-text);
|
|
64
|
+
}
|
|
65
|
+
.wp-dv-group[data-match="any"] > .wp-dv-group__head {
|
|
66
|
+
color: var(--wp-constraint-target-text);
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Derivation as rule cards: what it reads / writes / sends to negatives,
|
|
4
|
+
* then per rule IF / ELSE IF / ELSE with nested AND / OR groups and every
|
|
5
|
+
* action numbered (the action plus its schema v8 `extra_actions`).
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from "vue";
|
|
8
|
+
import TokenizedValue from "./TokenizedValue.vue";
|
|
9
|
+
import DerivationCondition from "./DerivationCondition.vue";
|
|
10
|
+
import Disclosure from "./Disclosure.vue";
|
|
11
|
+
import {
|
|
12
|
+
derivationRules,
|
|
13
|
+
varFlow,
|
|
14
|
+
type ActionOp,
|
|
15
|
+
type DerivationRule,
|
|
16
|
+
type Obj,
|
|
17
|
+
} from "./preview-readers";
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
/** The whole module row (the var summary reads it). */
|
|
21
|
+
payload: Obj | null;
|
|
22
|
+
inner: Obj | null;
|
|
23
|
+
refMap: ReadonlyMap<string, string>;
|
|
24
|
+
}>();
|
|
25
|
+
|
|
26
|
+
const OPEN_RULES = 3;
|
|
27
|
+
|
|
28
|
+
const rules = computed(() => derivationRules(props.inner));
|
|
29
|
+
const flow = computed(() => varFlow(props.payload));
|
|
30
|
+
const branchCount = computed(() =>
|
|
31
|
+
rules.value.reduce((n, r) => n + r.branches.length + (r.fallback ? 1 : 0), 0),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const OP_LABEL: Record<ActionOp, string> = {
|
|
35
|
+
set: "SET",
|
|
36
|
+
append: "APPEND",
|
|
37
|
+
prepend: "PREPEND",
|
|
38
|
+
neg_add: "+ NEGATIVE",
|
|
39
|
+
neg_replace: "REPLACE NEG",
|
|
40
|
+
other: "",
|
|
41
|
+
};
|
|
42
|
+
function opLabel(op: ActionOp, mode: string): string {
|
|
43
|
+
return OP_LABEL[op] || mode.toUpperCase();
|
|
44
|
+
}
|
|
45
|
+
function ruleSummary(r: DerivationRule): string {
|
|
46
|
+
const n = r.branches.length;
|
|
47
|
+
const parts = [`${n} branch${n === 1 ? "" : "es"}${r.fallback ? " + else" : ""}`];
|
|
48
|
+
const targets = [
|
|
49
|
+
...new Set(
|
|
50
|
+
[...r.branches.flatMap((b) => b.actions), ...(r.fallback ?? [])]
|
|
51
|
+
.filter((a) => a.op !== "neg_add" && a.op !== "neg_replace")
|
|
52
|
+
.map((a) => a.target),
|
|
53
|
+
),
|
|
54
|
+
];
|
|
55
|
+
if (targets.length) parts.push(`sets ${targets.join(", ")}`);
|
|
56
|
+
return parts.join(" · ");
|
|
57
|
+
}
|
|
58
|
+
const toggled = ref(new Set<number>());
|
|
59
|
+
function isOpen(i: number): boolean {
|
|
60
|
+
const byDefault = i < OPEN_RULES;
|
|
61
|
+
return toggled.value.has(i) ? !byDefault : byDefault;
|
|
62
|
+
}
|
|
63
|
+
function toggle(i: number): void {
|
|
64
|
+
const next = new Set(toggled.value);
|
|
65
|
+
if (next.has(i)) next.delete(i);
|
|
66
|
+
else next.add(i);
|
|
67
|
+
toggled.value = next;
|
|
68
|
+
}
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<div class="wp-pv wp-derivation">
|
|
73
|
+
<div class="wp-pv-stats">
|
|
74
|
+
<div class="wp-pv-stat">
|
|
75
|
+
<b>{{ rules.length }}</b><span>rule{{ rules.length === 1 ? "" : "s" }}</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="wp-pv-stat">
|
|
78
|
+
<b>{{ branchCount }}</b><span>branches</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="wp-dv-io">
|
|
82
|
+
<div v-if="flow.reads.length">
|
|
83
|
+
<span class="wp-pv-label">Reads</span>
|
|
84
|
+
<code
|
|
85
|
+
v-for="v in flow.reads"
|
|
86
|
+
:key="`r-${v}`"
|
|
87
|
+
class="wp-dv-var"
|
|
88
|
+
>${{ v }}</code>
|
|
89
|
+
</div>
|
|
90
|
+
<div v-if="flow.sets.length">
|
|
91
|
+
<span class="wp-pv-label">Writes</span>
|
|
92
|
+
<code
|
|
93
|
+
v-for="v in flow.sets"
|
|
94
|
+
:key="`w-${v}`"
|
|
95
|
+
class="wp-dv-var"
|
|
96
|
+
>${{ v }}</code>
|
|
97
|
+
</div>
|
|
98
|
+
<div v-if="flow.negatives.length">
|
|
99
|
+
<span class="wp-pv-label">Negatives</span>
|
|
100
|
+
<code
|
|
101
|
+
v-for="v in flow.negatives"
|
|
102
|
+
:key="`n-${v}`"
|
|
103
|
+
class="wp-dv-var wp-dv-var--neg"
|
|
104
|
+
>{{ v }}</code>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div
|
|
109
|
+
v-for="(r, ri) in rules"
|
|
110
|
+
:key="ri"
|
|
111
|
+
class="wp-dv-rule wp-derivation__rule"
|
|
112
|
+
>
|
|
113
|
+
<Disclosure
|
|
114
|
+
flush
|
|
115
|
+
lazy
|
|
116
|
+
class="wp-dv-rule__fold"
|
|
117
|
+
:open="isOpen(ri)"
|
|
118
|
+
@update:open="toggle(ri)"
|
|
119
|
+
>
|
|
120
|
+
<template #summary>
|
|
121
|
+
<b class="wp-dv-rule__name">Rule {{ ri + 1 }}</b><span class="wp-dv-rule__sum">{{ ruleSummary(r) }}</span>
|
|
122
|
+
</template>
|
|
123
|
+
<div
|
|
124
|
+
v-for="(b, bi) in r.branches"
|
|
125
|
+
:key="bi"
|
|
126
|
+
class="wp-dv-branch wp-derivation__branch"
|
|
127
|
+
>
|
|
128
|
+
<span class="wp-dv-kw">{{ bi === 0 ? "IF" : "ELSE IF" }}</span>
|
|
129
|
+
<div class="wp-dv-body">
|
|
130
|
+
<DerivationCondition
|
|
131
|
+
v-if="b.condition"
|
|
132
|
+
:cond="b.condition"
|
|
133
|
+
/>
|
|
134
|
+
<span
|
|
135
|
+
v-else
|
|
136
|
+
class="wp-dv-none"
|
|
137
|
+
>(no condition)</span>
|
|
138
|
+
<ol class="wp-dv-steps">
|
|
139
|
+
<li
|
|
140
|
+
v-for="(a, ai) in b.actions"
|
|
141
|
+
:key="ai"
|
|
142
|
+
data-test="derivation-action"
|
|
143
|
+
>
|
|
144
|
+
<span class="wp-dv-i">{{ ai + 1 }}</span>
|
|
145
|
+
<span class="wp-dv-op-cell">
|
|
146
|
+
<span
|
|
147
|
+
class="wp-dv-op"
|
|
148
|
+
:data-op="a.op"
|
|
149
|
+
>{{ opLabel(a.op, a.mode) }}</span>
|
|
150
|
+
<code class="wp-dv-target wp-derivation__target">{{ a.target }}</code>
|
|
151
|
+
</span>
|
|
152
|
+
<span class="wp-dv-val"><TokenizedValue
|
|
153
|
+
:value="a.value"
|
|
154
|
+
:uuid-to-name="refMap"
|
|
155
|
+
/></span>
|
|
156
|
+
</li>
|
|
157
|
+
</ol>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div
|
|
161
|
+
v-if="r.fallback"
|
|
162
|
+
class="wp-dv-branch wp-derivation__branch is-else"
|
|
163
|
+
>
|
|
164
|
+
<span class="wp-dv-kw wp-dv-kw--else">ELSE</span>
|
|
165
|
+
<div class="wp-dv-body">
|
|
166
|
+
<ol class="wp-dv-steps">
|
|
167
|
+
<li
|
|
168
|
+
v-for="(a, ai) in r.fallback"
|
|
169
|
+
:key="ai"
|
|
170
|
+
data-test="derivation-action"
|
|
171
|
+
>
|
|
172
|
+
<span class="wp-dv-i">{{ ai + 1 }}</span>
|
|
173
|
+
<span class="wp-dv-op-cell">
|
|
174
|
+
<span
|
|
175
|
+
class="wp-dv-op"
|
|
176
|
+
:data-op="a.op"
|
|
177
|
+
>{{ opLabel(a.op, a.mode) }}</span>
|
|
178
|
+
<code class="wp-dv-target wp-derivation__target">{{ a.target }}</code>
|
|
179
|
+
</span>
|
|
180
|
+
<span class="wp-dv-val"><TokenizedValue
|
|
181
|
+
:value="a.value"
|
|
182
|
+
:uuid-to-name="refMap"
|
|
183
|
+
/></span>
|
|
184
|
+
</li>
|
|
185
|
+
</ol>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</Disclosure>
|
|
189
|
+
</div>
|
|
190
|
+
<div class="wp-dv-pad" />
|
|
191
|
+
</div>
|
|
192
|
+
</template>
|
|
193
|
+
|
|
194
|
+
<style scoped src="./preview.css"></style>
|
|
195
|
+
<style scoped>
|
|
196
|
+
.wp-dv-io {
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-wrap: wrap;
|
|
199
|
+
gap: 10px 24px;
|
|
200
|
+
padding: 12px 16px;
|
|
201
|
+
border-bottom: 1px solid var(--wp-border);
|
|
202
|
+
}
|
|
203
|
+
.wp-dv-io > div {
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-wrap: wrap;
|
|
206
|
+
align-items: center;
|
|
207
|
+
gap: 6px;
|
|
208
|
+
}
|
|
209
|
+
.wp-dv-var {
|
|
210
|
+
font-family: var(--wp-font-mono);
|
|
211
|
+
font-size: 12px;
|
|
212
|
+
color: var(--wp-success);
|
|
213
|
+
}
|
|
214
|
+
.wp-dv-var--neg {
|
|
215
|
+
color: var(--wp-danger-text, var(--wp-danger));
|
|
216
|
+
background: color-mix(in oklab, var(--wp-danger) 10%, transparent);
|
|
217
|
+
border-radius: min(3px, var(--wp-radius-cap, 999px));
|
|
218
|
+
padding: 0 4px;
|
|
219
|
+
}
|
|
220
|
+
.wp-dv-rule {
|
|
221
|
+
margin: 14px 16px 0;
|
|
222
|
+
border: 1px solid var(--wp-border);
|
|
223
|
+
border-radius: min(10px, var(--wp-radius-cap, 999px));
|
|
224
|
+
overflow: hidden;
|
|
225
|
+
}
|
|
226
|
+
.wp-dv-rule__name {
|
|
227
|
+
font-size: 12.5px;
|
|
228
|
+
}
|
|
229
|
+
.wp-dv-rule__sum {
|
|
230
|
+
min-width: 0;
|
|
231
|
+
overflow: hidden;
|
|
232
|
+
color: var(--wp-text-dim);
|
|
233
|
+
font-size: 11.5px;
|
|
234
|
+
font-weight: 400;
|
|
235
|
+
text-overflow: ellipsis;
|
|
236
|
+
white-space: nowrap;
|
|
237
|
+
}
|
|
238
|
+
.wp-dv-branch {
|
|
239
|
+
display: grid;
|
|
240
|
+
grid-template-columns: 64px minmax(0, 1fr);
|
|
241
|
+
}
|
|
242
|
+
.wp-dv-branch + .wp-dv-branch {
|
|
243
|
+
border-top: 1px solid var(--wp-border);
|
|
244
|
+
}
|
|
245
|
+
.wp-dv-kw {
|
|
246
|
+
padding: 12px 0 0 14px;
|
|
247
|
+
font-family: var(--wp-font-mono);
|
|
248
|
+
font-size: 10.5px;
|
|
249
|
+
font-weight: 700;
|
|
250
|
+
letter-spacing: 0.06em;
|
|
251
|
+
color: var(--wp-accent-text);
|
|
252
|
+
}
|
|
253
|
+
.wp-dv-kw--else {
|
|
254
|
+
color: var(--wp-text-dim);
|
|
255
|
+
}
|
|
256
|
+
.wp-dv-body {
|
|
257
|
+
display: flex;
|
|
258
|
+
flex-direction: column;
|
|
259
|
+
align-items: flex-start;
|
|
260
|
+
gap: 10px;
|
|
261
|
+
padding: 10px 14px 12px 0;
|
|
262
|
+
min-width: 0;
|
|
263
|
+
}
|
|
264
|
+
.wp-dv-none {
|
|
265
|
+
font-size: 12px;
|
|
266
|
+
color: var(--wp-text-dim);
|
|
267
|
+
}
|
|
268
|
+
.wp-dv-steps {
|
|
269
|
+
list-style: none;
|
|
270
|
+
margin: 0;
|
|
271
|
+
padding: 0;
|
|
272
|
+
width: 100%;
|
|
273
|
+
display: flex;
|
|
274
|
+
flex-direction: column;
|
|
275
|
+
gap: 6px;
|
|
276
|
+
}
|
|
277
|
+
.wp-dv-steps li {
|
|
278
|
+
display: grid;
|
|
279
|
+
grid-template-columns: 18px minmax(120px, max-content) minmax(0, 1fr);
|
|
280
|
+
align-items: baseline;
|
|
281
|
+
gap: 8px;
|
|
282
|
+
}
|
|
283
|
+
.wp-dv-i {
|
|
284
|
+
font-family: var(--wp-font-mono);
|
|
285
|
+
font-size: 11px;
|
|
286
|
+
color: var(--wp-text-dim);
|
|
287
|
+
text-align: right;
|
|
288
|
+
}
|
|
289
|
+
.wp-dv-op-cell {
|
|
290
|
+
display: inline-flex;
|
|
291
|
+
align-items: baseline;
|
|
292
|
+
gap: 6px;
|
|
293
|
+
white-space: nowrap;
|
|
294
|
+
}
|
|
295
|
+
.wp-dv-op {
|
|
296
|
+
font-family: var(--wp-font-mono);
|
|
297
|
+
font-size: 10px;
|
|
298
|
+
font-weight: 600;
|
|
299
|
+
padding: 2px 6px;
|
|
300
|
+
border-radius: min(5px, var(--wp-radius-cap, 999px));
|
|
301
|
+
background: var(--wp-bg-3);
|
|
302
|
+
color: var(--wp-text-muted);
|
|
303
|
+
}
|
|
304
|
+
.wp-dv-op[data-op="set"] {
|
|
305
|
+
background: color-mix(in oklab, var(--wp-success) 14%, transparent);
|
|
306
|
+
color: var(--wp-success);
|
|
307
|
+
}
|
|
308
|
+
.wp-dv-op[data-op="append"],
|
|
309
|
+
.wp-dv-op[data-op="prepend"] {
|
|
310
|
+
background: color-mix(in oklab, var(--wp-info) 16%, transparent);
|
|
311
|
+
color: color-mix(in oklab, var(--wp-info) 55%, white);
|
|
312
|
+
}
|
|
313
|
+
.wp-dv-op[data-op="neg_add"],
|
|
314
|
+
.wp-dv-op[data-op="neg_replace"] {
|
|
315
|
+
background: color-mix(in oklab, var(--wp-danger) 16%, transparent);
|
|
316
|
+
color: var(--wp-danger-text, var(--wp-danger));
|
|
317
|
+
}
|
|
318
|
+
.wp-dv-target {
|
|
319
|
+
font-family: var(--wp-font-mono);
|
|
320
|
+
font-size: 12px;
|
|
321
|
+
color: var(--wp-success);
|
|
322
|
+
}
|
|
323
|
+
.wp-dv-val {
|
|
324
|
+
font-family: var(--wp-font-mono);
|
|
325
|
+
font-size: 12px;
|
|
326
|
+
line-height: 1.6;
|
|
327
|
+
padding: 3px 9px;
|
|
328
|
+
border-radius: min(5px, var(--wp-radius-cap, 999px));
|
|
329
|
+
background: var(--wp-bg-3);
|
|
330
|
+
color: var(--wp-text);
|
|
331
|
+
overflow-wrap: anywhere;
|
|
332
|
+
min-width: 0;
|
|
333
|
+
}
|
|
334
|
+
.wp-dv-pad {
|
|
335
|
+
height: 14px;
|
|
336
|
+
}
|
|
337
|
+
@container (max-width: 640px) {
|
|
338
|
+
.wp-dv-steps li {
|
|
339
|
+
grid-template-columns: 18px minmax(0, 1fr);
|
|
340
|
+
}
|
|
341
|
+
.wp-dv-val {
|
|
342
|
+
grid-column: 2;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
</style>
|