@signal9/era-ui 4.12.1 → 4.14.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/dist/ai/conversation/conversation-empty-state.svelte +1 -1
- package/dist/ai/error-panel/error-panel.svelte +1 -1
- package/dist/ai/message/message-content.svelte +1 -1
- package/dist/ai/prompt-input/prompt-input-textarea.svelte +1 -1
- package/dist/ai/response/response.svelte +1 -1
- package/dist/ai/swarm/swarm-artifact.svelte +1 -1
- package/dist/ai/tool/tool-output.svelte +1 -1
- package/dist/apps/llm-shell/llm-shell.svelte +1 -1
- package/dist/dev/audit/audits/index.d.ts +2 -1
- package/dist/dev/audit/audits/index.js +3 -1
- package/dist/dev/audit/audits/prose-leading.d.ts +2 -0
- package/dist/dev/audit/audits/prose-leading.js +91 -0
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/dialog.md +14 -0
- package/dist/generated-docs/llms-full.txt +14 -0
- package/dist/generated-docs/manifest.json +3 -2
- package/dist/os/notification-center.svelte +3 -1
- package/dist/os/toast.svelte +6 -3
- package/dist/styles/density.css +19 -0
- package/dist/styles/index.css +4 -0
- package/dist/styles/themes.css +21 -9
- package/dist/ui/accordion/accordion-content.svelte +1 -1
- package/dist/ui/alert-dialog/alert-dialog-content.svelte +1 -1
- package/dist/ui/alert-dialog/alert-dialog-description.svelte +1 -1
- package/dist/ui/code-block/code-block.svelte +1 -1
- package/dist/ui/dialog/dialog-content.svelte +25 -3
- package/dist/ui/dialog/dialog-content.svelte.d.ts +12 -1
- package/dist/ui/dialog/dialog-description.svelte +1 -1
- package/dist/ui/dialog/dialog-header.svelte +56 -0
- package/dist/ui/dialog/dialog-header.svelte.d.ts +10 -0
- package/dist/ui/dialog/index.d.ts +1 -0
- package/dist/ui/dialog/index.js +1 -0
- package/dist/ui/sheet/index.d.ts +1 -1
- package/dist/ui/sheet/index.js +1 -1
- package/dist/ui/sheet/sheet-content.svelte +44 -9
- package/dist/ui/sheet/sheet-content.svelte.d.ts +49 -13
- package/package.json +1 -1
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
tier → rd-md (the step-content / menu-panel precedent), not rd-lg. -->
|
|
17
17
|
<div
|
|
18
18
|
class={cn(
|
|
19
|
-
'min-w-0 text-body leading-
|
|
19
|
+
'min-w-0 text-body leading-body text-fg',
|
|
20
20
|
'group-data-[role=user]/message:max-w-[80%] group-data-[role=user]/message:rounded-(--era-rd-md) group-data-[role=user]/message:bg-(--era-surface-bg-elevated) group-data-[role=user]/message:p-(--era-pad-sm) group-data-[role=user]/message:whitespace-pre-wrap group-data-[role=user]/message:shadow-(--era-shadow)',
|
|
21
21
|
className
|
|
22
22
|
)}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
onkeydown={onKeydown}
|
|
59
59
|
onpaste={onPaste}
|
|
60
60
|
class={cn(
|
|
61
|
-
'field-sizing-content max-h-48 min-h-(--era-h-md) w-full resize-none bg-transparent px-(--era-inset-md) py-[calc((var(--era-h-md)-1lh)/2)] text-body leading-
|
|
61
|
+
'field-sizing-content max-h-48 min-h-(--era-h-md) w-full resize-none bg-transparent px-(--era-inset-md) py-[calc((var(--era-h-md)-1lh)/2)] text-body leading-body text-fg outline-none placeholder:text-muted',
|
|
62
62
|
className
|
|
63
63
|
)}
|
|
64
64
|
{...restProps}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<!-- eslint-disable better-tailwindcss/no-unknown-classes -- era-response* are defined in this component's <style> -->
|
|
35
35
|
<div
|
|
36
36
|
class={cn(
|
|
37
|
-
'era-response flex min-w-0 flex-col gap-(--era-gap) text-body leading-
|
|
37
|
+
'era-response flex min-w-0 flex-col gap-(--era-gap) text-body leading-body text-fg',
|
|
38
38
|
streaming && 'era-response-streaming',
|
|
39
39
|
className
|
|
40
40
|
)}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
{:else if output !== undefined}
|
|
13
13
|
<Step.Section label="output">
|
|
14
14
|
{#if typeof output === 'string'}
|
|
15
|
-
<p class="text-body whitespace-pre-wrap text-muted">{output}</p>
|
|
15
|
+
<p class="text-body leading-body whitespace-pre-wrap text-muted">{output}</p>
|
|
16
16
|
{:else}
|
|
17
17
|
<CodeBlock code={JSON.stringify(output, null, 2)} />
|
|
18
18
|
{/if}
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
<AI.Reasoning.Root streaming={block.streaming} bind:duration={block.duration}>
|
|
224
224
|
<AI.Reasoning.Trigger streaming={block.streaming} duration={block.duration} />
|
|
225
225
|
<AI.Reasoning.Content>
|
|
226
|
-
<p class="text-body leading-
|
|
226
|
+
<p class="text-body leading-body text-muted">{block.text}</p>
|
|
227
227
|
</AI.Reasoning.Content>
|
|
228
228
|
</AI.Reasoning.Root>
|
|
229
229
|
{:else if block.kind === 'tool'}
|
|
@@ -6,9 +6,10 @@ import { iconFlush } from './icon-flush.js';
|
|
|
6
6
|
import { offAxisMotion } from './off-axis-motion.js';
|
|
7
7
|
import { offTierHeight } from './off-tier-height.js';
|
|
8
8
|
import { pillHoldsItsInk } from './pill-holds-its-ink.js';
|
|
9
|
+
import { proseLeading } from './prose-leading.js';
|
|
9
10
|
import { radiusConcentricity } from './radius-concentricity.js';
|
|
10
11
|
import { restingGap } from './resting-gap.js';
|
|
11
12
|
import { singleGlyphSquare } from './single-glyph-square.js';
|
|
12
13
|
import { uniformSiblingGaps } from './uniform-sibling-gaps.js';
|
|
13
14
|
import { wallMatchesGap } from './wall-matches-gap.js';
|
|
14
|
-
export { clippedGlyphs, collapsedTextTrim, concentricInset, flatChrome, iconFlush, offAxisMotion, offTierHeight, pillHoldsItsInk, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
15
|
+
export { clippedGlyphs, collapsedTextTrim, concentricInset, flatChrome, iconFlush, offAxisMotion, offTierHeight, pillHoldsItsInk, proseLeading, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
@@ -7,12 +7,13 @@ import { iconFlush } from './icon-flush.js';
|
|
|
7
7
|
import { offAxisMotion } from './off-axis-motion.js';
|
|
8
8
|
import { offTierHeight } from './off-tier-height.js';
|
|
9
9
|
import { pillHoldsItsInk } from './pill-holds-its-ink.js';
|
|
10
|
+
import { proseLeading } from './prose-leading.js';
|
|
10
11
|
import { radiusConcentricity } from './radius-concentricity.js';
|
|
11
12
|
import { restingGap } from './resting-gap.js';
|
|
12
13
|
import { singleGlyphSquare } from './single-glyph-square.js';
|
|
13
14
|
import { uniformSiblingGaps } from './uniform-sibling-gaps.js';
|
|
14
15
|
import { wallMatchesGap } from './wall-matches-gap.js';
|
|
15
|
-
export { clippedGlyphs, collapsedTextTrim, concentricInset, flatChrome, iconFlush, offAxisMotion, offTierHeight, pillHoldsItsInk, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
16
|
+
export { clippedGlyphs, collapsedTextTrim, concentricInset, flatChrome, iconFlush, offAxisMotion, offTierHeight, pillHoldsItsInk, proseLeading, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
16
17
|
// Auto-register built-ins. Consumers can still unregister or add their own.
|
|
17
18
|
//
|
|
18
19
|
// Two of these only tell the truth on a particular axis setting, because the bug
|
|
@@ -29,6 +30,7 @@ registry.register(iconFlush);
|
|
|
29
30
|
registry.register(offAxisMotion);
|
|
30
31
|
registry.register(offTierHeight);
|
|
31
32
|
registry.register(pillHoldsItsInk);
|
|
33
|
+
registry.register(proseLeading);
|
|
32
34
|
registry.register(radiusConcentricity);
|
|
33
35
|
registry.register(restingGap);
|
|
34
36
|
registry.register(singleGlyphSquare);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Wrapping text set at a control's line-height.
|
|
3
|
+
*
|
|
4
|
+
* Era's type has two regimes and they want opposite things:
|
|
5
|
+
*
|
|
6
|
+
* SINGLE-LINE control text wants NO leading. `era-text-trim` collapses the
|
|
7
|
+
* line box to 1 and centres the ink inside a tier height — the space above
|
|
8
|
+
* and below the line is the container's job, not the type's.
|
|
9
|
+
*
|
|
10
|
+
* WRAPPING text wants leading and nothing else provides it. The space between
|
|
11
|
+
* line 1 and line 2 is the only thing holding a paragraph apart, so a
|
|
12
|
+
* paragraph set at line-height 1 is not "tight", it is two lines touching.
|
|
13
|
+
*
|
|
14
|
+
* The failure is silent and it is INHERITED, which is why it needs a runtime
|
|
15
|
+
* check rather than a lint rule: a wrapping <p> that names no leading of its own
|
|
16
|
+
* takes whatever its ancestors set, and in this library ancestors are very often
|
|
17
|
+
* fixed-height rows that legitimately set `leading-none` or the trim. The <p>
|
|
18
|
+
* looks correct in the markup — it just renders wrong the moment its text is
|
|
19
|
+
* long enough to wrap, which is exactly the case nobody screenshots.
|
|
20
|
+
*
|
|
21
|
+
* So the check is geometric, on live text: find elements that ACTUALLY wrapped
|
|
22
|
+
* (≥2 line boxes measured off a Range over their own text), then assert their
|
|
23
|
+
* line-height is a prose ratio and not a control's. Single-line text is never
|
|
24
|
+
* flagged — a trimmed button label is correct and must stay correct.
|
|
25
|
+
*
|
|
26
|
+
* The fix is always the same: add `leading-body` (--era-leading, 1.6).
|
|
27
|
+
*/
|
|
28
|
+
/** Below this ratio, the leading belongs to a control, not to a paragraph. */
|
|
29
|
+
const PROSE_RATIO = 1.35;
|
|
30
|
+
/**
|
|
31
|
+
* How many line boxes this element's OWN text occupies.
|
|
32
|
+
*
|
|
33
|
+
* Measured with a Range over the element's direct text nodes rather than by
|
|
34
|
+
* dividing height by line-height: the element may also contain inline children,
|
|
35
|
+
* floats or a wrapped icon, any of which make the height lie. Rect tops are
|
|
36
|
+
* bucketed to the nearest 0.5px because a fractional line box can report
|
|
37
|
+
* neighbouring rects a hair apart.
|
|
38
|
+
*/
|
|
39
|
+
function ownTextLines(el) {
|
|
40
|
+
const range = document.createRange();
|
|
41
|
+
const tops = new Set();
|
|
42
|
+
for (const node of el.childNodes) {
|
|
43
|
+
if (node.nodeType !== Node.TEXT_NODE || !node.textContent?.trim())
|
|
44
|
+
continue;
|
|
45
|
+
range.selectNodeContents(node);
|
|
46
|
+
for (const rect of range.getClientRects()) {
|
|
47
|
+
if (rect.width === 0 && rect.height === 0)
|
|
48
|
+
continue;
|
|
49
|
+
tops.add(Math.round(rect.top * 2) / 2);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
range.detach();
|
|
53
|
+
return tops.size;
|
|
54
|
+
}
|
|
55
|
+
export const proseLeading = {
|
|
56
|
+
id: 'typography/prose-leading',
|
|
57
|
+
name: 'Wrapping text has prose leading',
|
|
58
|
+
description: 'Text that wraps to a second line must set its own leading (leading-body) — otherwise it inherits a fixed-height row’s control leading and the lines collide.',
|
|
59
|
+
category: 'layout',
|
|
60
|
+
severity: 'error',
|
|
61
|
+
// Only elements that can hold text directly; a <div> wrapper with no text of
|
|
62
|
+
// its own has nothing to measure and is skipped by ownTextLines anyway.
|
|
63
|
+
selector: '*',
|
|
64
|
+
check(el) {
|
|
65
|
+
if (!el.textContent?.trim())
|
|
66
|
+
return null;
|
|
67
|
+
const s = getComputedStyle(el);
|
|
68
|
+
// Text that cannot wrap cannot have this bug — the whole defect is the
|
|
69
|
+
// gap between line 1 and line 2.
|
|
70
|
+
if (s.whiteSpace === 'nowrap' || s.whiteSpace === 'pre')
|
|
71
|
+
return null;
|
|
72
|
+
const fontSize = parseFloat(s.fontSize);
|
|
73
|
+
const lineHeight = parseFloat(s.lineHeight); // `normal` resolves to px here
|
|
74
|
+
if (!fontSize || !lineHeight)
|
|
75
|
+
return null;
|
|
76
|
+
const ratio = lineHeight / fontSize;
|
|
77
|
+
if (ratio >= PROSE_RATIO)
|
|
78
|
+
return null;
|
|
79
|
+
// The expensive half, deliberately last: only measure once the leading is
|
|
80
|
+
// already known to be too tight.
|
|
81
|
+
if (ownTextLines(el) < 2)
|
|
82
|
+
return null;
|
|
83
|
+
const issue = {
|
|
84
|
+
auditId: 'typography/prose-leading',
|
|
85
|
+
element: el,
|
|
86
|
+
message: `wrapped text at line-height ${ratio.toFixed(2)}× (${lineHeight.toFixed(1)}px on ${fontSize}px type) — this is control leading, inherited from a fixed-height row. Add leading-body.`,
|
|
87
|
+
details: { ratio: +ratio.toFixed(2), lineHeight, fontSize, lines: ownTextLines(el) }
|
|
88
|
+
};
|
|
89
|
+
return issue;
|
|
90
|
+
}
|
|
91
|
+
};
|