@aiaiai-pt/design-system 0.48.0 → 0.50.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/components/CaminhoStateChip.svelte +89 -0
- package/components/CaminhoStateChip.svelte.d.ts +38 -0
- package/components/KpiRegister.svelte +158 -0
- package/components/KpiRegister.svelte.d.ts +46 -0
- package/components/SealChip.svelte +133 -0
- package/components/SealChip.svelte.d.ts +41 -0
- package/components/index.d.ts +3 -0
- package/components/index.js +6 -0
- package/components/renderer/dispatch.d.ts +65 -28
- package/components/renderer/dispatch.ts +83 -44
- package/components/renderer/registry.d.ts +38 -9
- package/components/renderer/registry.ts +38 -9
- package/package.json +4 -1
- package/tokens/components.css +15 -0
- package/tokens/themes/ubp.css +41 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component CaminhoStateChip
|
|
3
|
+
|
|
4
|
+
A Badge-variant chip rendering the four verification states as WORDS.
|
|
5
|
+
|
|
6
|
+
The four-state machine (H1 Slice 6 / verification surface):
|
|
7
|
+
to-verify — horizon not yet reached; awaiting observation
|
|
8
|
+
verified — observed and confirmed at the horizon
|
|
9
|
+
not-verified — horizon passed; observation explicitly absent
|
|
10
|
+
not-done — path discarded; verification not applicable
|
|
11
|
+
|
|
12
|
+
States are rendered as English words, not icons or abbreviations.
|
|
13
|
+
Screen readers announce the full word; no icon-only encoding.
|
|
14
|
+
|
|
15
|
+
Built over --badge-* component tokens. English-only canonical state names
|
|
16
|
+
(shared-by-default doctrine — no urban vocabulary here).
|
|
17
|
+
|
|
18
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
19
|
+
|
|
20
|
+
@example All four states
|
|
21
|
+
<CaminhoStateChip state="to-verify" />
|
|
22
|
+
<CaminhoStateChip state="verified" />
|
|
23
|
+
<CaminhoStateChip state="not-verified" />
|
|
24
|
+
<CaminhoStateChip state="not-done" />
|
|
25
|
+
-->
|
|
26
|
+
<script>
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {'to-verify' | 'verified' | 'not-verified' | 'not-done'} CaminhoState
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
/** @type {CaminhoState} */
|
|
33
|
+
state,
|
|
34
|
+
/** @type {string} */
|
|
35
|
+
class: className = '',
|
|
36
|
+
...rest
|
|
37
|
+
} = $props();
|
|
38
|
+
|
|
39
|
+
/** @type {Record<CaminhoState, string>} */
|
|
40
|
+
const STATE_LABELS = {
|
|
41
|
+
'to-verify': 'To verify',
|
|
42
|
+
'verified': 'Verified',
|
|
43
|
+
'not-verified': 'Not verified',
|
|
44
|
+
'not-done': 'Not done',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const label = $derived(STATE_LABELS[state] ?? state);
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<span class="caminho-chip caminho-chip-{state} {className}" {...rest}>
|
|
51
|
+
{label}
|
|
52
|
+
</span>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.caminho-chip {
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
font-family: var(--badge-font);
|
|
59
|
+
font-size: var(--badge-size);
|
|
60
|
+
letter-spacing: var(--badge-tracking);
|
|
61
|
+
border-radius: var(--badge-radius);
|
|
62
|
+
padding: var(--badge-padding-y) var(--badge-padding-x);
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* to-verify: informational — horizon pending */
|
|
67
|
+
.caminho-chip-to-verify {
|
|
68
|
+
background: var(--color-info-subtle);
|
|
69
|
+
color: var(--color-info);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* verified: success — observation confirmed */
|
|
73
|
+
.caminho-chip-verified {
|
|
74
|
+
background: var(--color-success-subtle);
|
|
75
|
+
color: var(--color-success);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* not-verified: destructive — horizon passed, no observation */
|
|
79
|
+
.caminho-chip-not-verified {
|
|
80
|
+
background: var(--color-destructive-subtle);
|
|
81
|
+
color: var(--color-destructive);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* not-done: neutral — path discarded, verification not applicable */
|
|
85
|
+
.caminho-chip-not-done {
|
|
86
|
+
background: var(--badge-neutral-bg);
|
|
87
|
+
color: var(--badge-neutral-text);
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export default CaminhoStateChip;
|
|
2
|
+
type CaminhoStateChip = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* CaminhoStateChip
|
|
8
|
+
*
|
|
9
|
+
* A Badge-variant chip rendering the four verification states as WORDS.
|
|
10
|
+
*
|
|
11
|
+
* The four-state machine (H1 Slice 6 / verification surface):
|
|
12
|
+
* to-verify — horizon not yet reached; awaiting observation
|
|
13
|
+
* verified — observed and confirmed at the horizon
|
|
14
|
+
* not-verified — horizon passed; observation explicitly absent
|
|
15
|
+
* not-done — path discarded; verification not applicable
|
|
16
|
+
*
|
|
17
|
+
* States are rendered as English words, not icons or abbreviations.
|
|
18
|
+
* Screen readers announce the full word; no icon-only encoding.
|
|
19
|
+
*
|
|
20
|
+
* Built over --badge-* component tokens. English-only canonical state names
|
|
21
|
+
* (shared-by-default doctrine — no urban vocabulary here).
|
|
22
|
+
*
|
|
23
|
+
* H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
24
|
+
*
|
|
25
|
+
* @example All four states
|
|
26
|
+
* <CaminhoStateChip state="to-verify" />
|
|
27
|
+
* <CaminhoStateChip state="verified" />
|
|
28
|
+
* <CaminhoStateChip state="not-verified" />
|
|
29
|
+
* <CaminhoStateChip state="not-done" />
|
|
30
|
+
*/
|
|
31
|
+
declare const CaminhoStateChip: import("svelte").Component<{
|
|
32
|
+
state: any;
|
|
33
|
+
class?: string;
|
|
34
|
+
} & Record<string, any>, {}, "">;
|
|
35
|
+
type $$ComponentProps = {
|
|
36
|
+
state: any;
|
|
37
|
+
class?: string;
|
|
38
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component KpiRegister
|
|
3
|
+
|
|
4
|
+
A StatCard-variant register for KPI values where a measured reading
|
|
5
|
+
visually out-ranks a projected value (larger display text, higher position
|
|
6
|
+
in the visual hierarchy).
|
|
7
|
+
|
|
8
|
+
BOUNDARY ELEMENT: throws when a projected value is provided without a seal.
|
|
9
|
+
This is the enforcement point for the seal rule (prd.md §5.1): no unsealed
|
|
10
|
+
projected value may cross this boundary into the rendering surface. The throw
|
|
11
|
+
is unconditional — production code must never pass an unsealed projected value
|
|
12
|
+
here; the guard exists to catch contract violations early.
|
|
13
|
+
|
|
14
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
15
|
+
References sistema#67 for .type-overline (--type-overline-* tokens).
|
|
16
|
+
|
|
17
|
+
@example Measured only
|
|
18
|
+
<KpiRegister label="ENERGY OUTPUT" measuredValue="2.3 MW" />
|
|
19
|
+
|
|
20
|
+
@example With sealed projected value
|
|
21
|
+
<KpiRegister
|
|
22
|
+
label="ENERGY OUTPUT"
|
|
23
|
+
measuredValue="2.3 MW"
|
|
24
|
+
projectedValue="4.1 MW"
|
|
25
|
+
projectedSeal={{ evidence: 'projected', stale: false }}
|
|
26
|
+
/>
|
|
27
|
+
-->
|
|
28
|
+
<script>
|
|
29
|
+
import SealChip from './SealChip.svelte';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {'measured' | 'inferred' | 'projected'} EvidenceState
|
|
33
|
+
* @typedef {{ evidence: EvidenceState; stale: boolean }} SealRef
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
/** @type {string} — overline label (KPI name / dimension) */
|
|
38
|
+
label,
|
|
39
|
+
/** @type {string} — the measured (authoritative) value; always required */
|
|
40
|
+
measuredValue,
|
|
41
|
+
/** @type {string | undefined} — a projected value for the same KPI */
|
|
42
|
+
projectedValue = undefined,
|
|
43
|
+
/**
|
|
44
|
+
* @type {SealRef | undefined}
|
|
45
|
+
* Seal metadata for the projected value. REQUIRED when projectedValue
|
|
46
|
+
* is provided. The boundary guard throws when this is absent.
|
|
47
|
+
* Obtain from assignSeal() in @aiaiai-pt/widget-system/core.
|
|
48
|
+
*/
|
|
49
|
+
projectedSeal = undefined,
|
|
50
|
+
/** @type {string} */
|
|
51
|
+
class: className = '',
|
|
52
|
+
...rest
|
|
53
|
+
} = $props();
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* BOUNDARY GUARD — the seal rule enforcement point.
|
|
57
|
+
*
|
|
58
|
+
* Runs synchronously at component initialisation. Also runs reactively
|
|
59
|
+
* via $effect so prop changes after mount are also checked.
|
|
60
|
+
*
|
|
61
|
+
* Throws unconditionally when projectedValue is provided without a seal;
|
|
62
|
+
* production surfaces must never reach this state (prd.md §5.1 #3:
|
|
63
|
+
* "the product, deterministically" assigns the seal — a missing seal
|
|
64
|
+
* means the caller skipped assignSeal, which is the defect).
|
|
65
|
+
*/
|
|
66
|
+
if (projectedValue !== undefined && projectedSeal == null) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'[KpiRegister] A projected value must carry a seal. ' +
|
|
69
|
+
'Provide the `projectedSeal` prop (result of assignSeal()). ' +
|
|
70
|
+
'An unsealed projected value may not cross this boundary.',
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$effect(() => {
|
|
75
|
+
if (projectedValue !== undefined && projectedSeal == null) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
'[KpiRegister] A projected value must carry a seal. ' +
|
|
78
|
+
'Provide the `projectedSeal` prop (result of assignSeal()). ' +
|
|
79
|
+
'An unsealed projected value may not cross this boundary.',
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<div class="kpi-register {className}" {...rest}>
|
|
86
|
+
<!--
|
|
87
|
+
Overline label — micro-heading above the value.
|
|
88
|
+
Uses --type-overline-* tokens directly (sistema#67 .type-overline utility).
|
|
89
|
+
-->
|
|
90
|
+
<span class="kpi-register-label">{label}</span>
|
|
91
|
+
|
|
92
|
+
<!--
|
|
93
|
+
Measured value: the authoritative reading.
|
|
94
|
+
Display-scale type — visually out-ranks the projected row.
|
|
95
|
+
-->
|
|
96
|
+
<div class="kpi-register-measured">
|
|
97
|
+
<span class="kpi-register-measured-value">{measuredValue}</span>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!--
|
|
101
|
+
Projected row: subordinate to the measured value (smaller type, lower position).
|
|
102
|
+
Only rendered when a sealed projected value is provided.
|
|
103
|
+
projectedSeal is guaranteed non-null here (boundary guard above).
|
|
104
|
+
-->
|
|
105
|
+
{#if projectedValue !== undefined && projectedSeal != null}
|
|
106
|
+
<div class="kpi-register-projected">
|
|
107
|
+
<SealChip
|
|
108
|
+
value={projectedValue}
|
|
109
|
+
evidence={projectedSeal.evidence}
|
|
110
|
+
stale={projectedSeal.stale}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
{/if}
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<style>
|
|
117
|
+
.kpi-register {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
gap: var(--space-xs);
|
|
121
|
+
padding: var(--stat-padding);
|
|
122
|
+
border: var(--stat-border);
|
|
123
|
+
border-radius: var(--stat-radius);
|
|
124
|
+
background: var(--stat-bg);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* Overline label — consumes --type-overline-* tokens (sistema#67).
|
|
129
|
+
* Equivalent to the .type-overline utility class from utilities.css,
|
|
130
|
+
* declared here so the component is self-contained.
|
|
131
|
+
*/
|
|
132
|
+
.kpi-register-label {
|
|
133
|
+
font-family: var(--type-overline-font);
|
|
134
|
+
font-size: var(--type-overline-size);
|
|
135
|
+
font-weight: var(--type-overline-weight);
|
|
136
|
+
line-height: var(--type-overline-leading);
|
|
137
|
+
letter-spacing: var(--type-overline-tracking);
|
|
138
|
+
text-transform: uppercase;
|
|
139
|
+
color: var(--stat-label-color);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Measured value: display scale — the dominant visual element */
|
|
143
|
+
.kpi-register-measured-value {
|
|
144
|
+
font-family: var(--stat-value-font);
|
|
145
|
+
font-size: var(--stat-value-size);
|
|
146
|
+
font-weight: var(--stat-value-weight);
|
|
147
|
+
letter-spacing: var(--stat-value-tracking);
|
|
148
|
+
color: var(--color-text);
|
|
149
|
+
line-height: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Projected row: body-scale — subordinate to the measured value */
|
|
153
|
+
.kpi-register-projected {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
margin-top: var(--space-2xs);
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export default KpiRegister;
|
|
2
|
+
type KpiRegister = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* KpiRegister
|
|
8
|
+
*
|
|
9
|
+
* A StatCard-variant register for KPI values where a measured reading
|
|
10
|
+
* visually out-ranks a projected value (larger display text, higher position
|
|
11
|
+
* in the visual hierarchy).
|
|
12
|
+
*
|
|
13
|
+
* BOUNDARY ELEMENT: throws when a projected value is provided without a seal.
|
|
14
|
+
* This is the enforcement point for the seal rule (prd.md §5.1): no unsealed
|
|
15
|
+
* projected value may cross this boundary into the rendering surface. The throw
|
|
16
|
+
* is unconditional — production code must never pass an unsealed projected value
|
|
17
|
+
* here; the guard exists to catch contract violations early.
|
|
18
|
+
*
|
|
19
|
+
* H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
20
|
+
* References sistema#67 for .type-overline (--type-overline-* tokens).
|
|
21
|
+
*
|
|
22
|
+
* @example Measured only
|
|
23
|
+
* <KpiRegister label="ENERGY OUTPUT" measuredValue="2.3 MW" />
|
|
24
|
+
*
|
|
25
|
+
* @example With sealed projected value
|
|
26
|
+
* <KpiRegister
|
|
27
|
+
* label="ENERGY OUTPUT"
|
|
28
|
+
* measuredValue="2.3 MW"
|
|
29
|
+
* projectedValue="4.1 MW"
|
|
30
|
+
* projectedSeal={{ evidence: 'projected', stale: false }}
|
|
31
|
+
* />
|
|
32
|
+
*/
|
|
33
|
+
declare const KpiRegister: import("svelte").Component<{
|
|
34
|
+
label: any;
|
|
35
|
+
measuredValue: any;
|
|
36
|
+
projectedValue?: any;
|
|
37
|
+
projectedSeal?: any;
|
|
38
|
+
class?: string;
|
|
39
|
+
} & Record<string, any>, {}, "">;
|
|
40
|
+
type $$ComponentProps = {
|
|
41
|
+
label: any;
|
|
42
|
+
measuredValue: any;
|
|
43
|
+
projectedValue?: any;
|
|
44
|
+
projectedSeal?: any;
|
|
45
|
+
class?: string;
|
|
46
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component SealChip
|
|
3
|
+
|
|
4
|
+
Renders a value paired with its evidence seal as ONE phrase.
|
|
5
|
+
|
|
6
|
+
The seal must survive quotation: an sr-only span adjacent to the value
|
|
7
|
+
carries the evidence state word so copying or citing the value also
|
|
8
|
+
carries the seal (prd.md §5.1 #5: "a citation carries the seal of the
|
|
9
|
+
value it cites").
|
|
10
|
+
|
|
11
|
+
DS-owned evidence vocabulary: measured | inferred | projected.
|
|
12
|
+
Built over --badge-* + --seal-* component tokens.
|
|
13
|
+
|
|
14
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
15
|
+
|
|
16
|
+
@example Measured reading
|
|
17
|
+
<SealChip value="2.3 MW" evidence="measured" />
|
|
18
|
+
|
|
19
|
+
@example Projected (future window)
|
|
20
|
+
<SealChip value="4.1 MW" evidence="projected" />
|
|
21
|
+
|
|
22
|
+
@example Stale seal (horizon has passed)
|
|
23
|
+
<SealChip value="1.9 MW" evidence="projected" stale />
|
|
24
|
+
-->
|
|
25
|
+
<script>
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {'measured' | 'inferred' | 'projected'} EvidenceState
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
/** @type {string} — the formatted display value */
|
|
32
|
+
value,
|
|
33
|
+
/** @type {EvidenceState} — evidence state (from assignSeal) */
|
|
34
|
+
evidence,
|
|
35
|
+
/** @type {boolean} — true when the validity window has passed */
|
|
36
|
+
stale = false,
|
|
37
|
+
/** @type {string} */
|
|
38
|
+
class: className = '',
|
|
39
|
+
...rest
|
|
40
|
+
} = $props();
|
|
41
|
+
|
|
42
|
+
const EVIDENCE_LABELS = /** @type {Record<EvidenceState, string>} */ ({
|
|
43
|
+
measured: 'measured',
|
|
44
|
+
inferred: 'inferred',
|
|
45
|
+
projected: 'projected',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const evidenceLabel = $derived(EVIDENCE_LABELS[evidence] ?? evidence);
|
|
49
|
+
|
|
50
|
+
/** Label as spoken by AT and included in citations. */
|
|
51
|
+
const srLabel = $derived(stale ? `${evidenceLabel} (stale)` : evidenceLabel);
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<!--
|
|
55
|
+
The chip is one inline phrase. Structure:
|
|
56
|
+
[value text]
|
|
57
|
+
[visual badge — aria-hidden, not read by AT]
|
|
58
|
+
[sr-only span — read by AT; position:absolute but in DOM so text
|
|
59
|
+
travels with the value when the surrounding content is copied]
|
|
60
|
+
-->
|
|
61
|
+
<span
|
|
62
|
+
class="seal-chip seal-chip-{evidence} {stale ? 'seal-chip-stale' : ''} {className}"
|
|
63
|
+
{...rest}
|
|
64
|
+
>
|
|
65
|
+
<span class="seal-chip-value">{value}</span><!--
|
|
66
|
+
--><span class="seal-chip-badge" aria-hidden="true">{evidenceLabel}{stale ? ' (stale)' : ''}</span><!--
|
|
67
|
+
--><span class="seal-chip-sr-text"> {srLabel}</span>
|
|
68
|
+
</span>
|
|
69
|
+
|
|
70
|
+
<style>
|
|
71
|
+
.seal-chip {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: baseline;
|
|
74
|
+
gap: var(--space-xs);
|
|
75
|
+
position: relative;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Value text: no override — reads as its context's surrounding type */
|
|
79
|
+
.seal-chip-value {
|
|
80
|
+
/* intentionally empty */
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Visual evidence badge — seen by sighted users; hidden from AT */
|
|
84
|
+
.seal-chip-badge {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
font-family: var(--badge-font);
|
|
88
|
+
font-size: var(--badge-size);
|
|
89
|
+
letter-spacing: var(--badge-tracking);
|
|
90
|
+
border-radius: var(--badge-radius);
|
|
91
|
+
padding: var(--badge-padding-y) var(--badge-padding-x);
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
line-height: 1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Evidence-state colours via --seal-* component tokens */
|
|
97
|
+
.seal-chip-measured .seal-chip-badge {
|
|
98
|
+
color: var(--seal-measured-text);
|
|
99
|
+
background: var(--seal-measured-bg);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.seal-chip-inferred .seal-chip-badge {
|
|
103
|
+
color: var(--seal-inferred-text);
|
|
104
|
+
background: var(--seal-inferred-bg);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.seal-chip-projected .seal-chip-badge {
|
|
108
|
+
color: var(--seal-projected-text);
|
|
109
|
+
background: var(--seal-projected-bg);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Stale modifier: muted on all evidence states */
|
|
113
|
+
.seal-chip-stale .seal-chip-badge {
|
|
114
|
+
color: var(--seal-stale-text);
|
|
115
|
+
background: var(--seal-stale-bg);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* sr-only — visually hidden but present in the DOM so the text travels
|
|
120
|
+
* with the value when content is copied or cited (prd.md §5.1 #5).
|
|
121
|
+
* clip-path: inset(50%) is the modern replacement for deprecated clip: rect().
|
|
122
|
+
*/
|
|
123
|
+
.seal-chip-sr-text {
|
|
124
|
+
position: absolute;
|
|
125
|
+
width: 1px;
|
|
126
|
+
height: 1px;
|
|
127
|
+
padding: 0;
|
|
128
|
+
margin: -1px;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
clip-path: inset(50%);
|
|
131
|
+
white-space: nowrap;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export default SealChip;
|
|
2
|
+
type SealChip = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* SealChip
|
|
8
|
+
*
|
|
9
|
+
* Renders a value paired with its evidence seal as ONE phrase.
|
|
10
|
+
*
|
|
11
|
+
* The seal must survive quotation: an sr-only span adjacent to the value
|
|
12
|
+
* carries the evidence state word so copying or citing the value also
|
|
13
|
+
* carries the seal (prd.md §5.1 #5: "a citation carries the seal of the
|
|
14
|
+
* value it cites").
|
|
15
|
+
*
|
|
16
|
+
* DS-owned evidence vocabulary: measured | inferred | projected.
|
|
17
|
+
* Built over --badge-* + --seal-* component tokens.
|
|
18
|
+
*
|
|
19
|
+
* H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
20
|
+
*
|
|
21
|
+
* @example Measured reading
|
|
22
|
+
* <SealChip value="2.3 MW" evidence="measured" />
|
|
23
|
+
*
|
|
24
|
+
* @example Projected (future window)
|
|
25
|
+
* <SealChip value="4.1 MW" evidence="projected" />
|
|
26
|
+
*
|
|
27
|
+
* @example Stale seal (horizon has passed)
|
|
28
|
+
* <SealChip value="1.9 MW" evidence="projected" stale />
|
|
29
|
+
*/
|
|
30
|
+
declare const SealChip: import("svelte").Component<{
|
|
31
|
+
value: any;
|
|
32
|
+
evidence: any;
|
|
33
|
+
stale?: boolean;
|
|
34
|
+
class?: string;
|
|
35
|
+
} & Record<string, any>, {}, "">;
|
|
36
|
+
type $$ComponentProps = {
|
|
37
|
+
value: any;
|
|
38
|
+
evidence: any;
|
|
39
|
+
stale?: boolean;
|
|
40
|
+
class?: string;
|
|
41
|
+
} & Record<string, any>;
|
package/components/index.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export { default as Tag } from "./Tag.svelte";
|
|
|
3
3
|
export { default as Status } from "./Status.svelte";
|
|
4
4
|
export { default as Skeleton } from "./Skeleton.svelte";
|
|
5
5
|
export { default as KeyValue } from "./KeyValue.svelte";
|
|
6
|
+
export { default as SealChip } from "./SealChip.svelte";
|
|
7
|
+
export { default as KpiRegister } from "./KpiRegister.svelte";
|
|
8
|
+
export { default as CaminhoStateChip } from "./CaminhoStateChip.svelte";
|
|
6
9
|
export { default as Button } from "./Button.svelte";
|
|
7
10
|
export { default as Input } from "./Input.svelte";
|
|
8
11
|
export { default as Textarea } from "./Textarea.svelte";
|
package/components/index.js
CHANGED
|
@@ -15,6 +15,12 @@ export { default as Status } from "./Status.svelte";
|
|
|
15
15
|
export { default as Skeleton } from "./Skeleton.svelte";
|
|
16
16
|
export { default as KeyValue } from "./KeyValue.svelte";
|
|
17
17
|
|
|
18
|
+
// Evidence seal (H1 Slice 2 — westeuropeco/atelier-urban-workspace#57)
|
|
19
|
+
// DS-owned evidence vocabulary: measured | inferred | projected
|
|
20
|
+
export { default as SealChip } from "./SealChip.svelte";
|
|
21
|
+
export { default as KpiRegister } from "./KpiRegister.svelte";
|
|
22
|
+
export { default as CaminhoStateChip } from "./CaminhoStateChip.svelte";
|
|
23
|
+
|
|
18
24
|
// Form controls
|
|
19
25
|
export { default as Button } from "./Button.svelte";
|
|
20
26
|
export { default as Input } from "./Input.svelte";
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Widget dispatcher —
|
|
2
|
+
* Widget dispatcher — COMPATIBILITY ADAPTER over @aiaiai-pt/widget-system/core.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* (
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* @deprecated Import the generic dispatch from `@aiaiai-pt/widget-system/core`
|
|
5
|
+
* instead. This module is the S2 (#60) compatibility shim: it preserves the
|
|
6
|
+
* Atelier-shaped `@aiaiai-pt/design-system/renderer/dispatch` import surface for
|
|
7
|
+
* one migration window while the ranking ALGORITHM lives in exactly one place —
|
|
8
|
+
* `@aiaiai-pt/widget-system/core`. Scheduled for removal in the next MAJOR of
|
|
9
|
+
* `@aiaiai-pt/design-system` (S3). See `docs/migration/widget-system.md`.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Classification (#60 AC5): the tester/priority ranking loop is a GENERIC
|
|
12
|
+
* widget-system API — it is delegated below and NOT reimplemented here. The
|
|
13
|
+
* `(binding, schema, type)` tester SIGNATURE is ATELIER-COUPLED (it names
|
|
14
|
+
* `Binding`/`OntologySchema`/`Block` from ./types) and stays here as a thin
|
|
15
|
+
* adapter until consumers migrate to the ctx-shaped `WidgetMatchContext`.
|
|
14
16
|
*
|
|
15
|
-
* TH-08 (R-SEC-07) preserved:
|
|
16
|
-
*
|
|
17
|
-
* is
|
|
17
|
+
* TH-08 (R-SEC-07) preserved end-to-end: `core.selectEntry` returns the matched
|
|
18
|
+
* entry's known-good `key` literal — the operator's raw `binding.kind`/`type`
|
|
19
|
+
* is never returned as the key, so it never reaches `class`/`data-*`/`style`.
|
|
18
20
|
*/
|
|
21
|
+
import { type Match as CoreMatch, type RenderDecision as CoreRenderDecision } from "@aiaiai-pt/widget-system/core";
|
|
19
22
|
import type { Binding, Block, OntologySchema } from "./types";
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Tester sentinel — "this widget does not apply to this binding".
|
|
25
|
+
*
|
|
26
|
+
* @deprecated Re-exported from `@aiaiai-pt/widget-system/core`. Import it from
|
|
27
|
+
* there directly.
|
|
28
|
+
*/
|
|
21
29
|
export declare const NOT_APPLICABLE = -1;
|
|
22
30
|
/**
|
|
23
31
|
* A tester ranks a registry entry against a binding (+ optional schema and the
|
|
@@ -26,38 +34,67 @@ export declare const NOT_APPLICABLE = -1;
|
|
|
26
34
|
* generic kind widget (`entity-list`) for the SAME `binding.kind` without
|
|
27
35
|
* editing the kind entry. `type` is UNTRUSTED — it only influences ranking; the
|
|
28
36
|
* resolved `key` still comes from the matched entry (TH-08), never from `type`.
|
|
37
|
+
*
|
|
38
|
+
* @deprecated ATELIER-COUPLED signature. The generic tester in
|
|
39
|
+
* `@aiaiai-pt/widget-system/core` is `(ctx: WidgetMatchContext) => number`.
|
|
40
|
+
* Migrate custom testers to read `ctx.kind`/`ctx.type` from a match context.
|
|
29
41
|
*/
|
|
30
42
|
export type WidgetTester = (binding: Binding, schema: OntologySchema | null, type?: string) => number;
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated ATELIER-COUPLED entry shape (its `tester` names `Binding`). The
|
|
45
|
+
* generic entry lives in `@aiaiai-pt/widget-system/core` as
|
|
46
|
+
* `RegistryEntry<P, Ctx>`.
|
|
47
|
+
*/
|
|
31
48
|
export interface RegistryEntry<P> {
|
|
32
49
|
/** Known-good literal. Safe to render as `data-widget={key}` (TH-08). */
|
|
33
50
|
key: string;
|
|
34
51
|
payload: P;
|
|
35
52
|
tester: WidgetTester;
|
|
36
53
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The matched entry. Structurally identical to
|
|
56
|
+
* `@aiaiai-pt/widget-system/core`'s `Match<P>` — re-exported so consumers can
|
|
57
|
+
* migrate the import without a type change.
|
|
58
|
+
*
|
|
59
|
+
* @deprecated Import `Match` from `@aiaiai-pt/widget-system/core`.
|
|
60
|
+
*/
|
|
61
|
+
export type Match<P> = CoreMatch<P>;
|
|
62
|
+
/**
|
|
63
|
+
* The render decision. Structurally identical to
|
|
64
|
+
* `@aiaiai-pt/widget-system/core`'s `RenderDecision`.
|
|
65
|
+
*
|
|
66
|
+
* @deprecated Import `RenderDecision` from `@aiaiai-pt/widget-system/core`.
|
|
67
|
+
*/
|
|
68
|
+
export type RenderDecision = CoreRenderDecision;
|
|
41
69
|
/**
|
|
42
70
|
* Run every tester against the binding; the highest applicable score wins.
|
|
43
71
|
* Ties resolve to the first-registered entry (stable, deterministic). Returns
|
|
44
|
-
* `null` when no tester is applicable
|
|
45
|
-
*
|
|
72
|
+
* `null` when no tester is applicable.
|
|
73
|
+
*
|
|
74
|
+
* DELEGATION (#60): the ranking loop is NOT implemented here. Each
|
|
75
|
+
* Atelier-shaped entry is adapted into a `core.RegistryEntry` whose tester
|
|
76
|
+
* closes over `(binding, schema, type)` — so the full `(binding, schema, type)`
|
|
77
|
+
* tester contract (including `schema`, which the ctx model omits) is preserved
|
|
78
|
+
* — and the loop itself runs in `@aiaiai-pt/widget-system/core.selectEntry`.
|
|
79
|
+
* The `ctx` handed to core is a formality: core invokes `tester(ctx)` once per
|
|
80
|
+
* entry, and our adapted tester ignores it in favour of the closed-over args.
|
|
81
|
+
*
|
|
82
|
+
* @deprecated Use `selectEntry(entries, ctx)` from
|
|
83
|
+
* `@aiaiai-pt/widget-system/core` with `ctx: WidgetMatchContext`.
|
|
46
84
|
*/
|
|
47
85
|
export declare function selectEntry<P>(entries: ReadonlyArray<RegistryEntry<P>>, binding: Binding, schema: OntologySchema | null, type?: string): Match<P> | null;
|
|
48
|
-
export type RenderDecision = {
|
|
49
|
-
render: "widget";
|
|
50
|
-
} | {
|
|
51
|
-
render: "empty";
|
|
52
|
-
} | {
|
|
53
|
-
render: "error";
|
|
54
|
-
};
|
|
55
86
|
/**
|
|
56
87
|
* Fail-closed per blast radius (§14.8). Given whether a widget matched AND
|
|
57
88
|
* whether its data resolved, decide how the slot renders:
|
|
58
89
|
* - matched + data ok → render the widget
|
|
59
90
|
* - failed, optional slot → soft-empty (render nothing)
|
|
60
91
|
* - failed, structural slot → visible error (MUST NOT silently vanish)
|
|
61
|
-
*
|
|
92
|
+
*
|
|
93
|
+
* DELEGATION (#60): the decision is `@aiaiai-pt/widget-system/core.decideRender`;
|
|
94
|
+
* this adapter only projects the Atelier `Block.importance` field onto the
|
|
95
|
+
* generic `importance` argument.
|
|
96
|
+
*
|
|
97
|
+
* @deprecated Use `decideRender(importance, matched, dataOk)` from
|
|
98
|
+
* `@aiaiai-pt/widget-system/core`, passing `block.importance` directly.
|
|
62
99
|
*/
|
|
63
100
|
export declare function decideRender(block: Pick<Block, "importance">, matched: boolean, dataOk: boolean): RenderDecision;
|
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Widget dispatcher —
|
|
2
|
+
* Widget dispatcher — COMPATIBILITY ADAPTER over @aiaiai-pt/widget-system/core.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* (
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* @deprecated Import the generic dispatch from `@aiaiai-pt/widget-system/core`
|
|
5
|
+
* instead. This module is the S2 (#60) compatibility shim: it preserves the
|
|
6
|
+
* Atelier-shaped `@aiaiai-pt/design-system/renderer/dispatch` import surface for
|
|
7
|
+
* one migration window while the ranking ALGORITHM lives in exactly one place —
|
|
8
|
+
* `@aiaiai-pt/widget-system/core`. Scheduled for removal in the next MAJOR of
|
|
9
|
+
* `@aiaiai-pt/design-system` (S3). See `docs/migration/widget-system.md`.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Classification (#60 AC5): the tester/priority ranking loop is a GENERIC
|
|
12
|
+
* widget-system API — it is delegated below and NOT reimplemented here. The
|
|
13
|
+
* `(binding, schema, type)` tester SIGNATURE is ATELIER-COUPLED (it names
|
|
14
|
+
* `Binding`/`OntologySchema`/`Block` from ./types) and stays here as a thin
|
|
15
|
+
* adapter until consumers migrate to the ctx-shaped `WidgetMatchContext`.
|
|
14
16
|
*
|
|
15
|
-
* TH-08 (R-SEC-07) preserved:
|
|
16
|
-
*
|
|
17
|
-
* is
|
|
17
|
+
* TH-08 (R-SEC-07) preserved end-to-end: `core.selectEntry` returns the matched
|
|
18
|
+
* entry's known-good `key` literal — the operator's raw `binding.kind`/`type`
|
|
19
|
+
* is never returned as the key, so it never reaches `class`/`data-*`/`style`.
|
|
18
20
|
*/
|
|
21
|
+
import {
|
|
22
|
+
decideRender as coreDecideRender,
|
|
23
|
+
NOT_APPLICABLE as CORE_NOT_APPLICABLE,
|
|
24
|
+
selectEntry as coreSelectEntry,
|
|
25
|
+
type Match as CoreMatch,
|
|
26
|
+
type RenderDecision as CoreRenderDecision,
|
|
27
|
+
} from "@aiaiai-pt/widget-system/core";
|
|
19
28
|
import type { Binding, Block, OntologySchema } from "./types";
|
|
20
29
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Tester sentinel — "this widget does not apply to this binding".
|
|
32
|
+
*
|
|
33
|
+
* @deprecated Re-exported from `@aiaiai-pt/widget-system/core`. Import it from
|
|
34
|
+
* there directly.
|
|
35
|
+
*/
|
|
36
|
+
export const NOT_APPLICABLE = CORE_NOT_APPLICABLE;
|
|
23
37
|
|
|
24
38
|
/**
|
|
25
39
|
* A tester ranks a registry entry against a binding (+ optional schema and the
|
|
@@ -28,6 +42,10 @@ export const NOT_APPLICABLE = -1;
|
|
|
28
42
|
* generic kind widget (`entity-list`) for the SAME `binding.kind` without
|
|
29
43
|
* editing the kind entry. `type` is UNTRUSTED — it only influences ranking; the
|
|
30
44
|
* resolved `key` still comes from the matched entry (TH-08), never from `type`.
|
|
45
|
+
*
|
|
46
|
+
* @deprecated ATELIER-COUPLED signature. The generic tester in
|
|
47
|
+
* `@aiaiai-pt/widget-system/core` is `(ctx: WidgetMatchContext) => number`.
|
|
48
|
+
* Migrate custom testers to read `ctx.kind`/`ctx.type` from a match context.
|
|
31
49
|
*/
|
|
32
50
|
export type WidgetTester = (
|
|
33
51
|
binding: Binding,
|
|
@@ -35,6 +53,11 @@ export type WidgetTester = (
|
|
|
35
53
|
type?: string,
|
|
36
54
|
) => number;
|
|
37
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated ATELIER-COUPLED entry shape (its `tester` names `Binding`). The
|
|
58
|
+
* generic entry lives in `@aiaiai-pt/widget-system/core` as
|
|
59
|
+
* `RegistryEntry<P, Ctx>`.
|
|
60
|
+
*/
|
|
38
61
|
export interface RegistryEntry<P> {
|
|
39
62
|
/** Known-good literal. Safe to render as `data-widget={key}` (TH-08). */
|
|
40
63
|
key: string;
|
|
@@ -42,16 +65,38 @@ export interface RegistryEntry<P> {
|
|
|
42
65
|
tester: WidgetTester;
|
|
43
66
|
}
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
68
|
+
/**
|
|
69
|
+
* The matched entry. Structurally identical to
|
|
70
|
+
* `@aiaiai-pt/widget-system/core`'s `Match<P>` — re-exported so consumers can
|
|
71
|
+
* migrate the import without a type change.
|
|
72
|
+
*
|
|
73
|
+
* @deprecated Import `Match` from `@aiaiai-pt/widget-system/core`.
|
|
74
|
+
*/
|
|
75
|
+
export type Match<P> = CoreMatch<P>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The render decision. Structurally identical to
|
|
79
|
+
* `@aiaiai-pt/widget-system/core`'s `RenderDecision`.
|
|
80
|
+
*
|
|
81
|
+
* @deprecated Import `RenderDecision` from `@aiaiai-pt/widget-system/core`.
|
|
82
|
+
*/
|
|
83
|
+
export type RenderDecision = CoreRenderDecision;
|
|
49
84
|
|
|
50
85
|
/**
|
|
51
86
|
* Run every tester against the binding; the highest applicable score wins.
|
|
52
87
|
* Ties resolve to the first-registered entry (stable, deterministic). Returns
|
|
53
|
-
* `null` when no tester is applicable
|
|
54
|
-
*
|
|
88
|
+
* `null` when no tester is applicable.
|
|
89
|
+
*
|
|
90
|
+
* DELEGATION (#60): the ranking loop is NOT implemented here. Each
|
|
91
|
+
* Atelier-shaped entry is adapted into a `core.RegistryEntry` whose tester
|
|
92
|
+
* closes over `(binding, schema, type)` — so the full `(binding, schema, type)`
|
|
93
|
+
* tester contract (including `schema`, which the ctx model omits) is preserved
|
|
94
|
+
* — and the loop itself runs in `@aiaiai-pt/widget-system/core.selectEntry`.
|
|
95
|
+
* The `ctx` handed to core is a formality: core invokes `tester(ctx)` once per
|
|
96
|
+
* entry, and our adapted tester ignores it in favour of the closed-over args.
|
|
97
|
+
*
|
|
98
|
+
* @deprecated Use `selectEntry(entries, ctx)` from
|
|
99
|
+
* `@aiaiai-pt/widget-system/core` with `ctx: WidgetMatchContext`.
|
|
55
100
|
*/
|
|
56
101
|
export function selectEntry<P>(
|
|
57
102
|
entries: ReadonlyArray<RegistryEntry<P>>,
|
|
@@ -59,41 +104,35 @@ export function selectEntry<P>(
|
|
|
59
104
|
schema: OntologySchema | null,
|
|
60
105
|
type?: string,
|
|
61
106
|
): Match<P> | null {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
if (best === null) return null;
|
|
73
|
-
// TH-08: key from the matched entry, never from the binding/block.
|
|
74
|
-
return { key: best.key, payload: best.payload };
|
|
107
|
+
const ctx = { kind: binding.kind, type };
|
|
108
|
+
const adapted = entries.map((entry) => ({
|
|
109
|
+
key: entry.key,
|
|
110
|
+
payload: entry.payload,
|
|
111
|
+
// Close over the Atelier-shaped selection inputs; core supplies `ctx` but
|
|
112
|
+
// this adapter reads the richer (binding, schema, type) triple instead.
|
|
113
|
+
tester: () => entry.tester(binding, schema, type),
|
|
114
|
+
}));
|
|
115
|
+
return coreSelectEntry(adapted, ctx);
|
|
75
116
|
}
|
|
76
117
|
|
|
77
|
-
export type RenderDecision =
|
|
78
|
-
| { render: "widget" }
|
|
79
|
-
| { render: "empty" }
|
|
80
|
-
| { render: "error" };
|
|
81
|
-
|
|
82
118
|
/**
|
|
83
119
|
* Fail-closed per blast radius (§14.8). Given whether a widget matched AND
|
|
84
120
|
* whether its data resolved, decide how the slot renders:
|
|
85
121
|
* - matched + data ok → render the widget
|
|
86
122
|
* - failed, optional slot → soft-empty (render nothing)
|
|
87
123
|
* - failed, structural slot → visible error (MUST NOT silently vanish)
|
|
88
|
-
*
|
|
124
|
+
*
|
|
125
|
+
* DELEGATION (#60): the decision is `@aiaiai-pt/widget-system/core.decideRender`;
|
|
126
|
+
* this adapter only projects the Atelier `Block.importance` field onto the
|
|
127
|
+
* generic `importance` argument.
|
|
128
|
+
*
|
|
129
|
+
* @deprecated Use `decideRender(importance, matched, dataOk)` from
|
|
130
|
+
* `@aiaiai-pt/widget-system/core`, passing `block.importance` directly.
|
|
89
131
|
*/
|
|
90
132
|
export function decideRender(
|
|
91
133
|
block: Pick<Block, "importance">,
|
|
92
134
|
matched: boolean,
|
|
93
135
|
dataOk: boolean,
|
|
94
136
|
): RenderDecision {
|
|
95
|
-
|
|
96
|
-
return block.importance === "structural"
|
|
97
|
-
? { render: "error" }
|
|
98
|
-
: { render: "empty" };
|
|
137
|
+
return coreDecideRender(block.importance, matched, dataOk);
|
|
99
138
|
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DS renderer widget registry — the
|
|
2
|
+
* DS renderer widget registry — the Atelier-coupled BASE registry (#492).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* (
|
|
4
|
+
* @deprecated This is an S2 (#60) COMPATIBILITY surface. It is NOT a generic
|
|
5
|
+
* widget-system API — do not reclassify it as one (#60 AC5). It is an
|
|
6
|
+
* Atelier-coupled base registry: a module-global entry list preloaded with the
|
|
7
|
+
* DS widgets (stat-grid, results-chart, chart, metabase-embed) whose testers
|
|
8
|
+
* name `Binding`/`Block`/`WidgetKind` from ./types.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Migration destination: the generic isolated-registry machinery lives in
|
|
11
|
+
* `@aiaiai-pt/widget-system/core` (`createRegistry()` — an isolated, override-
|
|
12
|
+
* aware factory, not a module-global singleton) and its base-widget preload in
|
|
13
|
+
* `@aiaiai-pt/widget-system/widgets` (`registerBaseWidgets`). The Atelier host
|
|
14
|
+
* owns which widgets it preloads. This module is scheduled for removal in the
|
|
15
|
+
* next MAJOR of `@aiaiai-pt/design-system` (S3). See
|
|
16
|
+
* `docs/migration/widget-system.md`.
|
|
17
|
+
*
|
|
18
|
+
* NO DUPLICATE DISPATCH (#60 AC3): this module holds ZERO ranking logic. It
|
|
19
|
+
* only stores entries and delegates resolution to `./dispatch.selectEntry`,
|
|
20
|
+
* which itself delegates the ranking loop to `@aiaiai-pt/widget-system/core`.
|
|
12
21
|
*
|
|
13
22
|
* TH-08 preserved: `resolveWidget` returns the matched entry's known-good `key`
|
|
14
23
|
* literal — the operator's raw block `type`/`binding.kind` is never the key.
|
|
@@ -22,9 +31,18 @@ export type WidgetComponent = Component<WidgetProps>;
|
|
|
22
31
|
* `type` hint ranks it ABOVE the kind-generic widget (score 20 > 10) for the
|
|
23
32
|
* same binding. `type` is UNTRUSTED — it only influences ranking; the resolved
|
|
24
33
|
* key is the entry literal (TH-08).
|
|
34
|
+
*
|
|
35
|
+
* @deprecated ATELIER-COUPLED builder (its `kind` is a `WidgetKind` and its
|
|
36
|
+
* tester names `Binding`). Migrate to `byTypeOnKind` from
|
|
37
|
+
* `@aiaiai-pt/widget-system/core`, whose tester reads a ctx `WidgetMatchContext`.
|
|
25
38
|
*/
|
|
26
39
|
export declare function byTypeOnKind(key: string, kind: WidgetKind, component: unknown): RegistryEntry<WidgetComponent>;
|
|
27
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* A widget whose tester is the generic "binding.kind === K" check (score 10).
|
|
42
|
+
*
|
|
43
|
+
* @deprecated ATELIER-COUPLED builder. Migrate to `byKind` from
|
|
44
|
+
* `@aiaiai-pt/widget-system/core` (ctx-shaped tester).
|
|
45
|
+
*/
|
|
28
46
|
export declare function byKind(key: string, kind: WidgetKind, component: unknown): RegistryEntry<WidgetComponent>;
|
|
29
47
|
/**
|
|
30
48
|
* Register a widget in the host-extensible registry. Call at app startup
|
|
@@ -37,6 +55,11 @@ export declare function byKind(key: string, kind: WidgetKind, component: unknown
|
|
|
37
55
|
*
|
|
38
56
|
* @param entry - A registry entry built with `byKind`, `byTypeOnKind`, or a
|
|
39
57
|
* hand-crafted `{ key, payload, tester }` for a custom tester.
|
|
58
|
+
*
|
|
59
|
+
* @deprecated ATELIER-COUPLED module-global registration. Migrate to an
|
|
60
|
+
* isolated `createRegistry()` from `@aiaiai-pt/widget-system/core` plus
|
|
61
|
+
* `registerBaseWidgets` from `@aiaiai-pt/widget-system/widgets`, and call
|
|
62
|
+
* `registry.register(...)` on the host-owned instance.
|
|
40
63
|
*/
|
|
41
64
|
export declare function registerWidget(entry: RegistryEntry<WidgetComponent>): void;
|
|
42
65
|
/**
|
|
@@ -44,5 +67,11 @@ export declare function registerWidget(entry: RegistryEntry<WidgetComponent>): v
|
|
|
44
67
|
* the full registry (base DS entries + host-registered). Returns `null` when
|
|
45
68
|
* no widget applies — the caller renders per blast radius (`decideRender`),
|
|
46
69
|
* never interpolating the block's raw `type`/`kind`.
|
|
70
|
+
*
|
|
71
|
+
* Resolution delegates to `./dispatch.selectEntry` (→ widget-system/core); this
|
|
72
|
+
* module contributes only the entry list, never the ranking loop.
|
|
73
|
+
*
|
|
74
|
+
* @deprecated Migrate to `registry.resolve(ctx)` on an isolated
|
|
75
|
+
* `createRegistry()` from `@aiaiai-pt/widget-system/core`.
|
|
47
76
|
*/
|
|
48
77
|
export declare function resolveWidget(block: Block, schema?: OntologySchema | null): Match<WidgetComponent> | null;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DS renderer widget registry — the
|
|
2
|
+
* DS renderer widget registry — the Atelier-coupled BASE registry (#492).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* (
|
|
4
|
+
* @deprecated This is an S2 (#60) COMPATIBILITY surface. It is NOT a generic
|
|
5
|
+
* widget-system API — do not reclassify it as one (#60 AC5). It is an
|
|
6
|
+
* Atelier-coupled base registry: a module-global entry list preloaded with the
|
|
7
|
+
* DS widgets (stat-grid, results-chart, chart, metabase-embed) whose testers
|
|
8
|
+
* name `Binding`/`Block`/`WidgetKind` from ./types.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Migration destination: the generic isolated-registry machinery lives in
|
|
11
|
+
* `@aiaiai-pt/widget-system/core` (`createRegistry()` — an isolated, override-
|
|
12
|
+
* aware factory, not a module-global singleton) and its base-widget preload in
|
|
13
|
+
* `@aiaiai-pt/widget-system/widgets` (`registerBaseWidgets`). The Atelier host
|
|
14
|
+
* owns which widgets it preloads. This module is scheduled for removal in the
|
|
15
|
+
* next MAJOR of `@aiaiai-pt/design-system` (S3). See
|
|
16
|
+
* `docs/migration/widget-system.md`.
|
|
17
|
+
*
|
|
18
|
+
* NO DUPLICATE DISPATCH (#60 AC3): this module holds ZERO ranking logic. It
|
|
19
|
+
* only stores entries and delegates resolution to `./dispatch.selectEntry`,
|
|
20
|
+
* which itself delegates the ranking loop to `@aiaiai-pt/widget-system/core`.
|
|
12
21
|
*
|
|
13
22
|
* TH-08 preserved: `resolveWidget` returns the matched entry's known-good `key`
|
|
14
23
|
* literal — the operator's raw block `type`/`binding.kind` is never the key.
|
|
@@ -34,6 +43,10 @@ export type WidgetComponent = Component<WidgetProps>;
|
|
|
34
43
|
* `type` hint ranks it ABOVE the kind-generic widget (score 20 > 10) for the
|
|
35
44
|
* same binding. `type` is UNTRUSTED — it only influences ranking; the resolved
|
|
36
45
|
* key is the entry literal (TH-08).
|
|
46
|
+
*
|
|
47
|
+
* @deprecated ATELIER-COUPLED builder (its `kind` is a `WidgetKind` and its
|
|
48
|
+
* tester names `Binding`). Migrate to `byTypeOnKind` from
|
|
49
|
+
* `@aiaiai-pt/widget-system/core`, whose tester reads a ctx `WidgetMatchContext`.
|
|
37
50
|
*/
|
|
38
51
|
export function byTypeOnKind(
|
|
39
52
|
key: string,
|
|
@@ -48,7 +61,12 @@ export function byTypeOnKind(
|
|
|
48
61
|
};
|
|
49
62
|
}
|
|
50
63
|
|
|
51
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* A widget whose tester is the generic "binding.kind === K" check (score 10).
|
|
66
|
+
*
|
|
67
|
+
* @deprecated ATELIER-COUPLED builder. Migrate to `byKind` from
|
|
68
|
+
* `@aiaiai-pt/widget-system/core` (ctx-shaped tester).
|
|
69
|
+
*/
|
|
52
70
|
export function byKind(
|
|
53
71
|
key: string,
|
|
54
72
|
kind: WidgetKind,
|
|
@@ -92,6 +110,11 @@ const _entries: RegistryEntry<WidgetComponent>[] = [
|
|
|
92
110
|
*
|
|
93
111
|
* @param entry - A registry entry built with `byKind`, `byTypeOnKind`, or a
|
|
94
112
|
* hand-crafted `{ key, payload, tester }` for a custom tester.
|
|
113
|
+
*
|
|
114
|
+
* @deprecated ATELIER-COUPLED module-global registration. Migrate to an
|
|
115
|
+
* isolated `createRegistry()` from `@aiaiai-pt/widget-system/core` plus
|
|
116
|
+
* `registerBaseWidgets` from `@aiaiai-pt/widget-system/widgets`, and call
|
|
117
|
+
* `registry.register(...)` on the host-owned instance.
|
|
95
118
|
*/
|
|
96
119
|
export function registerWidget(entry: RegistryEntry<WidgetComponent>): void {
|
|
97
120
|
_entries.push(entry);
|
|
@@ -102,6 +125,12 @@ export function registerWidget(entry: RegistryEntry<WidgetComponent>): void {
|
|
|
102
125
|
* the full registry (base DS entries + host-registered). Returns `null` when
|
|
103
126
|
* no widget applies — the caller renders per blast radius (`decideRender`),
|
|
104
127
|
* never interpolating the block's raw `type`/`kind`.
|
|
128
|
+
*
|
|
129
|
+
* Resolution delegates to `./dispatch.selectEntry` (→ widget-system/core); this
|
|
130
|
+
* module contributes only the entry list, never the ranking loop.
|
|
131
|
+
*
|
|
132
|
+
* @deprecated Migrate to `registry.resolve(ctx)` on an isolated
|
|
133
|
+
* `createRegistry()` from `@aiaiai-pt/widget-system/core`.
|
|
105
134
|
*/
|
|
106
135
|
export function resolveWidget(
|
|
107
136
|
block: Block,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiaiai-pt/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Design system tokens and Svelte components for aiaiai products",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -74,6 +74,9 @@
|
|
|
74
74
|
"components",
|
|
75
75
|
"tokens"
|
|
76
76
|
],
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"@aiaiai-pt/widget-system": ">=0.1.0 <1"
|
|
79
|
+
},
|
|
77
80
|
"peerDependencies": {
|
|
78
81
|
"@codemirror/commands": "^6.10.3",
|
|
79
82
|
"@codemirror/lang-json": "^6.0.2",
|
package/tokens/components.css
CHANGED
|
@@ -248,6 +248,21 @@
|
|
|
248
248
|
--badge-neutral-bg: var(--color-surface-tertiary);
|
|
249
249
|
--badge-neutral-text: var(--color-text-secondary);
|
|
250
250
|
|
|
251
|
+
/* Evidence Seal — DS-owned vocabulary: measured | inferred | projected
|
|
252
|
+
H1 Slice 2 — westeuropeco/atelier-urban-workspace#57.
|
|
253
|
+
Defaults resolve via semantic color tokens so they work for every theme.
|
|
254
|
+
UBP dark scheme overrides below in tokens/themes/ubp.css to maintain
|
|
255
|
+
WCAG AA on the cool-steel dark surface (#0b182a). */
|
|
256
|
+
--seal-measured-text: var(--color-success);
|
|
257
|
+
--seal-measured-bg: var(--color-success-subtle);
|
|
258
|
+
--seal-inferred-text: var(--color-info);
|
|
259
|
+
--seal-inferred-bg: var(--color-info-subtle);
|
|
260
|
+
--seal-projected-text: var(--color-warning);
|
|
261
|
+
--seal-projected-bg: var(--color-warning-subtle);
|
|
262
|
+
/* stale modifier: muted regardless of base evidence state */
|
|
263
|
+
--seal-stale-text: var(--color-text-muted);
|
|
264
|
+
--seal-stale-bg: var(--color-surface-secondary);
|
|
265
|
+
|
|
251
266
|
/* Tag */
|
|
252
267
|
--tag-font: var(--type-label-font);
|
|
253
268
|
--tag-size: var(--type-label-size);
|
package/tokens/themes/ubp.css
CHANGED
|
@@ -138,6 +138,23 @@
|
|
|
138
138
|
/* Total color overrides: 22. Elevation: 2. Focus alias: 1.
|
|
139
139
|
Grand total: ~25 tokens. Typography/motion/spacing/grid unchanged.
|
|
140
140
|
--color-info and --color-info-subtle NOT overridden (inherit DS teal-blue). */
|
|
141
|
+
|
|
142
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
143
|
+
EVIDENCE SEAL — light-scheme text overrides (H1 Slice 2, uw#57)
|
|
144
|
+
--color-success (#16a34a) and --color-warning (#ca8a04) are
|
|
145
|
+
"large UI only" on the steel surface (3.15:1 and 2.83:1 resp.).
|
|
146
|
+
Seal badge text tokens use darker variants to clear WCAG AA (≥4.5:1)
|
|
147
|
+
on their respective subtle backgrounds.
|
|
148
|
+
|
|
149
|
+
Contrast on subtle backgrounds (AA = 4.5:1 for small badge text):
|
|
150
|
+
measured #256f20 (green-700) on #f0fdf4 (success-subtle): ~5.46:1 [AA]
|
|
151
|
+
projected #9e6308 (amber-700) on #fffbeb (warning-subtle): ~4.79:1 [AA]
|
|
152
|
+
--seal-inferred-text inherits --color-info (#2e63a3): 5.66:1 on
|
|
153
|
+
info-subtle (#f0f5fa) — no override needed.
|
|
154
|
+
--seal-stale-text inherits --color-text-muted (≥3.0:1 non-essential).
|
|
155
|
+
═══════════════════════════════════════════════════════════════ */
|
|
156
|
+
--seal-measured-text: #256f20; /* green-700 → 5.46:1 on success-subtle [AA] */
|
|
157
|
+
--seal-projected-text: #9e6308; /* amber-700 → 4.79:1 on warning-subtle [AA] */
|
|
141
158
|
}
|
|
142
159
|
|
|
143
160
|
/* Desktop density: compact button heights for toolbar/sidebar layouts.
|
|
@@ -222,3 +239,27 @@
|
|
|
222
239
|
/* Focus ring: accent blue, visible on all dark surfaces */
|
|
223
240
|
--focus-ring-color: var(--color-accent);
|
|
224
241
|
}
|
|
242
|
+
|
|
243
|
+
[data-theme="ubp"][data-scheme="dark"] {
|
|
244
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
245
|
+
EVIDENCE SEAL — dark-scheme overrides (H1 Slice 2, uw#57)
|
|
246
|
+
Text tokens lifted to 400-series for WCAG AA/AAA on steel-900
|
|
247
|
+
(#0b182a). Background tokens use color-mix() and are NOT
|
|
248
|
+
verified by the automated contrast suite (see ubp-theme-contrast
|
|
249
|
+
coverage-gap note) — visual QA is the complement.
|
|
250
|
+
|
|
251
|
+
Contrast on steel-900 (#0b182a, lum ≈ 0.0082):
|
|
252
|
+
measured #4ade80 (green-400) lum ≈ 0.403 → ~7.76:1 [AAA]
|
|
253
|
+
inferred #60a5fa (blue-400) lum ≈ 0.386 → ~7.49:1 [AAA]
|
|
254
|
+
projected #fbbf24 (amber-400) lum ≈ 0.598 → ~11.14:1 [AAA]
|
|
255
|
+
stale inherits --seal-stale-text: var(--color-text-muted) = #60758a (3.74:1)
|
|
256
|
+
═══════════════════════════════════════════════════════════════ */
|
|
257
|
+
--seal-measured-text: #4ade80;
|
|
258
|
+
--seal-measured-bg: color-mix(in srgb, #4ade80 12%, #0b182a);
|
|
259
|
+
--seal-inferred-text: #60a5fa;
|
|
260
|
+
--seal-inferred-bg: color-mix(in srgb, #60a5fa 12%, #0b182a);
|
|
261
|
+
--seal-projected-text: #fbbf24;
|
|
262
|
+
--seal-projected-bg: color-mix(in srgb, #fbbf24 12%, #0b182a);
|
|
263
|
+
/* stale: inherits --seal-stale-text (--color-text-muted = #60758a, 3.74:1 non-essential)
|
|
264
|
+
and --seal-stale-bg (--color-surface-secondary = #1a2a3a) */
|
|
265
|
+
}
|