@signal9/era-ui 9.0.0 → 10.1.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/artifact/artifact-close.svelte +2 -2
- package/dist/ai/checkpoint.svelte +1 -1
- package/dist/ai/context/context.svelte +1 -1
- package/dist/ai/conversation/conversation-scroll-button.svelte +1 -1
- package/dist/ai/conversations/conversations-item.svelte +3 -3
- package/dist/ai/index.js +1 -1
- package/dist/ai/message/message-branch-selector.svelte +2 -2
- package/dist/ai/message/message-usage.svelte +1 -1
- package/dist/ai/prompt-input/prompt-input-add-attachments.svelte +1 -1
- package/dist/ai/prompt-input/prompt-input-attachments.svelte +1 -1
- package/dist/ai/prompt-input/prompt-input-submit.svelte +1 -1
- package/dist/ai/run-status/run-status.svelte +4 -3
- package/dist/ai/suggestion/suggestion.svelte +1 -1
- package/dist/ai/swarm/swarm-actions.svelte +2 -2
- package/dist/ai/swarm/swarm-agent.svelte +2 -2
- package/dist/ai/swarm/swarm-artifact.svelte +1 -1
- package/dist/ai/swarm/swarm-barrier.svelte +1 -1
- package/dist/ai/swarm/swarm-footer.svelte +3 -2
- package/dist/ai/swarm/swarm-header.svelte +1 -1
- package/dist/ai/swarm/swarm-node.svelte +1 -1
- package/dist/ai/swarm/swarm-role.svelte +5 -2
- package/dist/ai/tool/tool-chip.svelte +1 -1
- package/dist/ai/web-preview/web-preview.svelte +2 -2
- package/dist/apps/index.d.ts +3 -3
- package/dist/apps/index.js +3 -3
- package/dist/apps/notes/editor/bubble-toolbar.svelte +3 -3
- package/dist/apps/notes/editor/slash-menu.svelte +2 -4
- package/dist/apps/notes/notes.svelte +12 -12
- package/dist/dev/audit/audits/anonymous-text.d.ts +2 -0
- package/dist/dev/audit/audits/anonymous-text.js +73 -0
- package/dist/dev/audit/audits/index.d.ts +2 -1
- package/dist/dev/audit/audits/index.js +3 -1
- package/dist/dev/audit/types.d.ts +1 -1
- package/dist/docs/css-utilities.js +1 -1
- package/dist/docs/llms-txt.js +2 -2
- package/dist/docs/notes.md +3 -3
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/bar.md +2 -2
- package/dist/generated-docs/button.md +1 -1
- package/dist/generated-docs/chip.md +1 -1
- package/dist/generated-docs/input.md +1 -1
- package/dist/generated-docs/llms-full.txt +101 -21
- package/dist/generated-docs/llms.txt +11 -7
- package/dist/generated-docs/manifest.json +32 -11
- package/dist/generated-docs/notes.md +3 -3
- package/dist/generated-docs/utilities.json +23 -4
- package/dist/generated-docs/utilities.md +82 -6
- package/dist/os/notification-center.svelte +1 -1
- package/dist/os/pane.svelte +2 -2
- package/dist/os/taskbar.svelte +4 -4
- package/dist/os/toast.svelte +2 -2
- package/dist/styles/index.css +43 -5
- package/dist/ui/bar/bar.svelte +13 -13
- package/dist/ui/bar/bar.svelte.d.ts +15 -15
- package/dist/ui/button/button.svelte +15 -4
- package/dist/ui/button/button.svelte.d.ts +1 -1
- package/dist/ui/button/index.d.ts +1 -1
- package/dist/ui/button/variants.d.ts +10 -13
- package/dist/ui/button/variants.js +13 -15
- package/dist/ui/chip/chip.svelte +25 -15
- package/dist/ui/chip/chip.svelte.d.ts +8 -8
- package/dist/ui/command/command-item.svelte +1 -1
- package/dist/ui/command/command-link-item.svelte +1 -1
- package/dist/ui/command-bar/command-bar.svelte +1 -1
- package/dist/ui/copy-button/copy-button.svelte +1 -1
- package/dist/ui/dialog/dialog-header.svelte +7 -2
- package/dist/ui/index.js +1 -1
- package/dist/ui/input/input.svelte +3 -3
- package/dist/ui/input/input.svelte.d.ts +1 -1
- package/dist/ui/input/variants.d.ts +6 -6
- package/dist/ui/input/variants.js +3 -3
- package/dist/ui/kv/kv.svelte +1 -1
- package/dist/ui/pane/pane-close.svelte +2 -2
- package/dist/ui/pane/pane-handle.svelte +3 -3
- package/dist/ui/select/select-trigger.svelte +1 -1
- package/dist/ui/select/select-value.svelte +1 -1
- package/dist/ui/step/step-summary.svelte +1 -1
- package/package.json +1 -1
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
let { onclick }: { onclick?: (e: MouseEvent) => void } = $props();
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
|
-
<!-- size="
|
|
7
|
+
<!-- size="pill", the close-affordance tier (Pane.Close, the chip dismiss): an
|
|
8
8
|
18px hit target whose tier draws the 12px dismiss ink by itself. The <X>
|
|
9
9
|
used to carry size-(--era-h-icon) — a class the Button variant's [&_svg]
|
|
10
10
|
selector outranks, so it claimed 14px while 10 shipped. -->
|
|
11
|
-
<Button icon size="
|
|
11
|
+
<Button icon size="pill" aria-label="Close" {onclick}>
|
|
12
12
|
<X />
|
|
13
13
|
</Button>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
<div class={cn('flex w-full items-center gap-(--era-gap) text-muted', className)}>
|
|
22
22
|
<Bookmark class="size-(--era-h-icon) shrink-0" aria-hidden="true" />
|
|
23
|
-
<Button variant="link" size="
|
|
23
|
+
<Button variant="link" size="chip" onclick={onRestore}>
|
|
24
24
|
{#if children}{@render children()}{:else}{label}{/if}
|
|
25
25
|
</Button>
|
|
26
26
|
<Separator class="flex-1" />
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
25
|
<Popover.Root>
|
|
26
|
-
<Popover.Trigger size="
|
|
26
|
+
<Popover.Trigger size="chip" class={cn('gap-(--era-gap) tabular-nums', className)}>
|
|
27
27
|
{Math.round(percent * 100)}%
|
|
28
28
|
<svg viewBox="0 0 24 24" class="size-(--era-h-icon) -rotate-90" aria-hidden="true">
|
|
29
29
|
<circle cx="12" cy="12" r="10" fill="none" stroke="var(--color-3)" stroke-width="3" />
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
>
|
|
204
204
|
<Button
|
|
205
205
|
icon
|
|
206
|
-
size="
|
|
206
|
+
size="pill"
|
|
207
207
|
aria-label={starred ? 'Unstar conversation' : 'Star conversation'}
|
|
208
208
|
aria-pressed={starred}
|
|
209
209
|
onclick={toggleStar}
|
|
@@ -212,14 +212,14 @@
|
|
|
212
212
|
</Button>
|
|
213
213
|
|
|
214
214
|
{#if showCancel}
|
|
215
|
-
<Button icon size="
|
|
215
|
+
<Button icon size="pill" aria-label="Cancel generation" onclick={() => onCancel?.()}>
|
|
216
216
|
<X class="size-(--era-h-icon)" />
|
|
217
217
|
</Button>
|
|
218
218
|
{/if}
|
|
219
219
|
|
|
220
220
|
<Button
|
|
221
221
|
icon
|
|
222
|
-
size="
|
|
222
|
+
size="pill"
|
|
223
223
|
tone="destructive"
|
|
224
224
|
active={armed}
|
|
225
225
|
aria-label={armed ? 'Confirm delete conversation' : 'Delete conversation'}
|
package/dist/ai/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// era-ui take on Vercel's AI Elements (via the Svelte port). Namespaced parts
|
|
3
3
|
// for the families, flat exports for the singles. `workflow/` is deliberately
|
|
4
4
|
// NOT exported here: it depends on @xyflow/svelte and lives on its own
|
|
5
|
-
// subpath (`@
|
|
5
|
+
// subpath (`@signal9/era-ui/ai/workflow`) so the dependency stays opt-in.
|
|
6
6
|
export * as Conversation from './conversation/index.js';
|
|
7
7
|
export * as Message from './message/index.js';
|
|
8
8
|
export * as PromptInput from './prompt-input/index.js';
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
{#if ctrl.total > 1}
|
|
14
14
|
<ButtonGroup class={cn(className)}>
|
|
15
|
-
<Button icon size="
|
|
15
|
+
<Button icon size="chip" aria-label="Previous version" onclick={() => ctrl.previous()}>
|
|
16
16
|
<ChevronLeft class="size-(--era-h-icon)" />
|
|
17
17
|
</Button>
|
|
18
18
|
<span class="flex items-center px-(--era-inset-chip) text-body text-muted tabular-nums">
|
|
19
19
|
{ctrl.current + 1} of {ctrl.total}
|
|
20
20
|
</span>
|
|
21
|
-
<Button icon size="
|
|
21
|
+
<Button icon size="chip" aria-label="Next version" onclick={() => ctrl.next()}>
|
|
22
22
|
<ChevronRight class="size-(--era-h-icon)" />
|
|
23
23
|
</Button>
|
|
24
24
|
</ButtonGroup>
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
|
|
85
85
|
<div
|
|
86
86
|
class={cn(
|
|
87
|
-
'flex items-center gap-(--era-inset-chip) font-mono text-body
|
|
87
|
+
'flex items-center gap-(--era-inset-chip) font-mono text-body leading-none text-muted tabular-nums',
|
|
88
88
|
className
|
|
89
89
|
)}
|
|
90
90
|
{...restProps}
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
const attachments = getAttachmentsContext();
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<Button icon size="
|
|
10
|
+
<Button icon size="control" aria-label={label} onclick={() => attachments.openFileDialog()}>
|
|
11
11
|
<Plus class="size-(--era-h-icon)" />
|
|
12
12
|
</Button>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<!-- md control geometry, like a Button with an icon: wall inset and
|
|
23
23
|
icon gap both --era-inset-control, ink centered by era-text-trim. -->
|
|
24
24
|
<div
|
|
25
|
-
class="flex h-(--era-h-control) items-center gap-(--era-inset-control) rounded-(--era-rd-control) bg-(--era-surface-bg) px-(--era-inset-control) text-body
|
|
25
|
+
class="flex h-(--era-h-control) items-center gap-(--era-inset-control) rounded-(--era-rd-control) bg-(--era-surface-bg) px-(--era-inset-control) text-body leading-none text-muted shadow-(--era-shadow)"
|
|
26
26
|
>
|
|
27
27
|
<Paperclip class="size-(--era-h-icon) shrink-0" />
|
|
28
28
|
<span class="max-w-32 truncate">{a.filename}</span>
|
|
@@ -91,11 +91,12 @@
|
|
|
91
91
|
<div class="flex h-(--era-h-control) items-center gap-(--era-gap) px-(--era-inset-control)">
|
|
92
92
|
<!-- leading status dot + label -->
|
|
93
93
|
<span
|
|
94
|
-
class="flex items-center gap-(--era-inset-pill) font-mono text-body {style.text}
|
|
94
|
+
class="flex items-center gap-(--era-inset-pill) font-mono text-body {style.text} leading-none"
|
|
95
95
|
>
|
|
96
96
|
<span class={cn('size-1.5 shrink-0 rounded-full', style.dot, style.pulse && 'animate-pulse')}
|
|
97
97
|
></span>
|
|
98
|
-
|
|
98
|
+
<!-- Own box, so the trim applies — see Role for the anonymous-item rule. -->
|
|
99
|
+
<span class="era-text-trim">{style.label}</span>
|
|
99
100
|
</span>
|
|
100
101
|
|
|
101
102
|
{#if tools}
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
<div class="flex-1"></div>
|
|
116
117
|
|
|
117
118
|
{#if style.active && oncancel}
|
|
118
|
-
<Button size="
|
|
119
|
+
<Button size="icon" tone="destructive" onclick={() => oncancel?.()}>
|
|
119
120
|
<X class="size-(--era-h-icon)" aria-hidden="true" />
|
|
120
121
|
Cancel
|
|
121
122
|
</Button>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
vertical inset) so the words breathe. Era pills are rd-sm, not
|
|
22
22
|
full-round — touch mode alone pills the ladder. -->
|
|
23
23
|
<Button
|
|
24
|
-
size="
|
|
24
|
+
size="chip"
|
|
25
25
|
class={cn('shrink-0 px-(--era-chip-px)', className)}
|
|
26
26
|
onclick={() => onSelect?.(suggestion)}
|
|
27
27
|
>
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
{#if cancel}
|
|
61
61
|
<Button
|
|
62
62
|
icon
|
|
63
|
-
size="
|
|
63
|
+
size="pill"
|
|
64
64
|
tone="destructive"
|
|
65
65
|
aria-label="Cancel {agent.task}"
|
|
66
66
|
onclick={(event: MouseEvent) => {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
{#if open}
|
|
75
75
|
<Button
|
|
76
76
|
icon
|
|
77
|
-
size="
|
|
77
|
+
size="pill"
|
|
78
78
|
aria-label="Open {agent.task}"
|
|
79
79
|
onclick={(event: MouseEvent) => {
|
|
80
80
|
event.stopPropagation();
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
)}
|
|
62
62
|
{...restProps}
|
|
63
63
|
>
|
|
64
|
-
<div class="flex min-w-0 items-center gap-(--era-gap) text-body
|
|
64
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body leading-none">
|
|
65
65
|
<Dot status={agent.status} />
|
|
66
66
|
<Role role={agent.role ?? 'worker'} label={agent.label} class="min-w-0" />
|
|
67
67
|
{#if agent.key}
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
{@render children?.(agent)}
|
|
86
86
|
|
|
87
87
|
{#if agent.sandbox || elapsed != null}
|
|
88
|
-
<div class="flex min-w-0 items-center gap-(--era-gap) text-body
|
|
88
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body leading-none text-muted">
|
|
89
89
|
{#if agent.sandbox}
|
|
90
90
|
<Sandbox sandbox={agent.sandbox} label />
|
|
91
91
|
{/if}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
class={cn('flex min-w-0 flex-col gap-(--era-gap) p-(--era-pad-panel)', className)}
|
|
44
44
|
{...restProps}
|
|
45
45
|
>
|
|
46
|
-
<div class="flex min-w-0 items-center gap-(--era-gap) text-body
|
|
46
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body leading-none">
|
|
47
47
|
<span class="shrink-0 font-mono text-[0.625rem] tracking-[0.15em] text-muted uppercase">
|
|
48
48
|
{artifact.kind}
|
|
49
49
|
</span>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
class={cn('flex min-w-0 flex-col gap-(--era-gap)', className)}
|
|
41
41
|
{...restProps}
|
|
42
42
|
>
|
|
43
|
-
<div class="flex min-w-0 items-center gap-(--era-gap) text-body
|
|
43
|
+
<div class="flex min-w-0 items-center gap-(--era-gap) text-body leading-none">
|
|
44
44
|
<GitMerge class="size-(--era-h-icon) shrink-0 text-muted" aria-hidden="true" />
|
|
45
45
|
<span class="min-w-0 flex-1 truncate text-fg">{barrier.title}</span>
|
|
46
46
|
{#if barrier.key}
|
|
@@ -50,14 +50,15 @@
|
|
|
50
50
|
|
|
51
51
|
<div
|
|
52
52
|
class={cn(
|
|
53
|
-
'flex h-(--era-h-control) shrink-0 items-center gap-(--era-gap) border-t border-divider-faded px-(--era-inset-control) font-mono text-body
|
|
53
|
+
'flex h-(--era-h-control) shrink-0 items-center gap-(--era-gap) border-t border-divider-faded px-(--era-inset-control) font-mono text-body leading-none',
|
|
54
54
|
className
|
|
55
55
|
)}
|
|
56
56
|
{...restProps}
|
|
57
57
|
>
|
|
58
58
|
<span class={cn('flex shrink-0 items-center gap-(--era-gap)', tone.class)}>
|
|
59
59
|
<Dot status={tone.status} label={tone.label} class={tone.pulse ? 'animate-pulse' : undefined} />
|
|
60
|
-
|
|
60
|
+
<!-- Own box, so the trim applies — see Role for the anonymous-item rule. -->
|
|
61
|
+
<span class="era-text-trim">{tone.label}</span>
|
|
61
62
|
</span>
|
|
62
63
|
<!-- Always rendered, empty when there is nothing to say: the span is also the
|
|
63
64
|
spacer that pushes the watermark to the trailing edge. -->
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
the end of a sentence you can hover for, never the count of what failed. -->
|
|
44
44
|
<div
|
|
45
45
|
class={cn(
|
|
46
|
-
'flex h-(--era-h-control) shrink-0 items-center gap-(--era-gap) px-(--era-inset-control) text-body
|
|
46
|
+
'flex h-(--era-h-control) shrink-0 items-center gap-(--era-gap) px-(--era-inset-control) text-body leading-none',
|
|
47
47
|
className
|
|
48
48
|
)}
|
|
49
49
|
{...restProps}
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
highlight instantly so a fast pass down the tree doesn't smear. -->
|
|
161
161
|
<div
|
|
162
162
|
class={cn(
|
|
163
|
-
'flex h-(--era-h-control) items-center gap-(--era-gap) rounded-(--era-rd-control) pl-(--era-inset-control) text-body
|
|
163
|
+
'flex h-(--era-h-control) items-center gap-(--era-gap) rounded-(--era-rd-control) pl-(--era-inset-control) text-body leading-none',
|
|
164
164
|
hasActions ? 'pr-(--era-pill-inset-control)' : 'pr-(--era-inset-control)',
|
|
165
165
|
(hasChildren || hasDetail) && 'cursor-pointer',
|
|
166
166
|
active
|
|
@@ -53,12 +53,15 @@
|
|
|
53
53
|
data-role={role}
|
|
54
54
|
title={name}
|
|
55
55
|
class={cn(
|
|
56
|
-
'inline-flex shrink-0 items-center gap-(--era-gap) font-mono text-[0.625rem]
|
|
56
|
+
'inline-flex shrink-0 items-center gap-(--era-gap) font-mono text-[0.625rem] leading-none tracking-[0.15em] uppercase',
|
|
57
57
|
role === 'orchestrator' ? 'text-fg' : 'text-muted',
|
|
58
58
|
className
|
|
59
59
|
)}
|
|
60
60
|
{...restProps}
|
|
61
61
|
>
|
|
62
62
|
<Icon class="size-(--era-h-icon) shrink-0" aria-hidden="true" />
|
|
63
|
-
|
|
63
|
+
<!-- The name gets its OWN box so the caps trim has something to trim. Left on
|
|
64
|
+
the flex row it was inert: bare text in a flex container is an anonymous
|
|
65
|
+
item, which no non-inherited property reaches. -->
|
|
66
|
+
<span class="era-text-trim-caps">{name}</span>
|
|
64
67
|
</span>
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
data-status={status}
|
|
48
48
|
{onclick}
|
|
49
49
|
class={cn(
|
|
50
|
-
'inline-flex h-(--era-h-pill) max-w-full min-w-0 shrink-0 era-interactive cursor-pointer items-center gap-(--era-gap) rounded-(--era-rd-pill) px-(--era-pill-px) text-body
|
|
50
|
+
'inline-flex h-(--era-h-pill) max-w-full min-w-0 shrink-0 era-interactive cursor-pointer items-center gap-(--era-gap) rounded-(--era-rd-pill) px-(--era-pill-px) text-body leading-none text-muted transition-colors duration-(--era-duration) ease-(--era-ease) hover:bg-(--era-highlight) hover:text-fg',
|
|
51
51
|
active && 'bg-fill text-fg',
|
|
52
52
|
className
|
|
53
53
|
)}
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
className
|
|
22
22
|
)}
|
|
23
23
|
>
|
|
24
|
-
<Bar size="
|
|
25
|
-
<Button icon size="
|
|
24
|
+
<Bar size="control" class="shrink-0 gap-(--era-gap) rounded-none">
|
|
25
|
+
<Button icon size="chip" aria-label="Reload" onclick={() => (nonce += 1)}>
|
|
26
26
|
<RotateCw class="size-(--era-h-icon)" />
|
|
27
27
|
</Button>
|
|
28
28
|
<Input
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* makes product decisions a primitive never would, which is exactly why these
|
|
8
8
|
* live behind their own subpath instead of the root barrel.
|
|
9
9
|
*
|
|
10
|
-
* Apps are NOT re-exported from `@
|
|
10
|
+
* Apps are NOT re-exported from `@signal9/era-ui`. Each carries heavy,
|
|
11
11
|
* app-specific dependencies (Notes brings TipTap), and the same reasoning that
|
|
12
12
|
* isolates `ai/workflow` on its own subpath applies here: a consumer who never
|
|
13
|
-
* imports an app never pays for it. Import from `@
|
|
14
|
-
* single app, e.g. `@
|
|
13
|
+
* imports an app never pays for it. Import from `@signal9/era-ui/apps` (or a
|
|
14
|
+
* single app, e.g. `@signal9/era-ui/apps/notes`) and install that app's peers.
|
|
15
15
|
*
|
|
16
16
|
* Exports are flat and prefixed by app (`Notes`, `NotesStore`, `NoteEditor`) so
|
|
17
17
|
* a second app can land without a namespace churn.
|
package/dist/apps/index.js
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* makes product decisions a primitive never would, which is exactly why these
|
|
8
8
|
* live behind their own subpath instead of the root barrel.
|
|
9
9
|
*
|
|
10
|
-
* Apps are NOT re-exported from `@
|
|
10
|
+
* Apps are NOT re-exported from `@signal9/era-ui`. Each carries heavy,
|
|
11
11
|
* app-specific dependencies (Notes brings TipTap), and the same reasoning that
|
|
12
12
|
* isolates `ai/workflow` on its own subpath applies here: a consumer who never
|
|
13
|
-
* imports an app never pays for it. Import from `@
|
|
14
|
-
* single app, e.g. `@
|
|
13
|
+
* imports an app never pays for it. Import from `@signal9/era-ui/apps` (or a
|
|
14
|
+
* single app, e.g. `@signal9/era-ui/apps/notes`) and install that app's peers.
|
|
15
15
|
*
|
|
16
16
|
* Exports are flat and prefixed by app (`Notes`, `NotesStore`, `NoteEditor`) so
|
|
17
17
|
* a second app can land without a namespace churn.
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
leaves no discernible edge. border-divider-faded is the token'd hairline, so
|
|
32
32
|
it still tracks the surface rather than being a drawn-on line. -->
|
|
33
33
|
<Bar
|
|
34
|
-
size="
|
|
35
|
-
content="
|
|
34
|
+
size="control"
|
|
35
|
+
content="pill"
|
|
36
36
|
class="w-max border border-divider-faded bg-elevated shadow-lg glass-blur"
|
|
37
37
|
>
|
|
38
38
|
{#each items as item (item.mark)}
|
|
39
39
|
{@const Icon = item.icon}
|
|
40
40
|
<Button
|
|
41
41
|
icon
|
|
42
|
-
size="
|
|
42
|
+
size="pill"
|
|
43
43
|
active={active[item.mark] ?? false}
|
|
44
44
|
aria-label={item.label}
|
|
45
45
|
title={item.label}
|
|
@@ -53,9 +53,7 @@
|
|
|
53
53
|
class="scrollbar-none flex max-h-[calc(var(--era-h-control)*8)] w-max min-w-48 flex-col overflow-y-auto rounded-control bg-elevated p-menu shadow-lg glass-blur"
|
|
54
54
|
>
|
|
55
55
|
{#if items.length === 0}
|
|
56
|
-
<div
|
|
57
|
-
class="flex h-control shrink-0 items-center px-inset-control text-body era-text-trim text-muted"
|
|
58
|
-
>
|
|
56
|
+
<div class="era-label-ink h-control shrink-0 px-inset-control text-body text-muted">
|
|
59
57
|
No commands
|
|
60
58
|
</div>
|
|
61
59
|
{:else}
|
|
@@ -63,7 +61,7 @@
|
|
|
63
61
|
{@const Icon = item.icon}
|
|
64
62
|
<button
|
|
65
63
|
type="button"
|
|
66
|
-
class="flex h-control w-full shrink-0 cursor-pointer items-center gap-gutter rounded-menu-item px-inset-control text-left text-body
|
|
64
|
+
class="flex h-control w-full shrink-0 cursor-pointer items-center gap-gutter rounded-menu-item px-inset-control text-left text-body leading-none text-fg {i ===
|
|
67
65
|
selected
|
|
68
66
|
? 'bg-highlight shadow-highlight'
|
|
69
67
|
: ''}"
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
|
|
166
166
|
{#snippet iconPicker(note: Note)}
|
|
167
167
|
<Popover.Root bind:open={iconPickerOpen}>
|
|
168
|
-
<Popover.Trigger icon size="
|
|
168
|
+
<Popover.Trigger icon size="pill" aria-label="Change icon" title="Change icon">
|
|
169
169
|
{#if note.icon}
|
|
170
170
|
<span aria-hidden="true">{note.icon}</span>
|
|
171
171
|
{:else}
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
{#each row as glyph (glyph)}
|
|
180
180
|
<Button
|
|
181
181
|
icon
|
|
182
|
-
size="
|
|
182
|
+
size="control"
|
|
183
183
|
active={note.icon === glyph}
|
|
184
184
|
aria-label={glyph}
|
|
185
185
|
onclick={() => {
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
{/each}
|
|
195
195
|
{#if note.icon}
|
|
196
196
|
<Button
|
|
197
|
-
size="
|
|
197
|
+
size="chip"
|
|
198
198
|
class="w-full"
|
|
199
199
|
onclick={() => {
|
|
200
200
|
store.setIcon(note.id, null);
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
{#snippet titleField(note: Note, cls: string)}
|
|
220
220
|
<Input
|
|
221
221
|
reveal
|
|
222
|
-
size="
|
|
222
|
+
size="pill"
|
|
223
223
|
class={cls}
|
|
224
224
|
aria-label="Note title"
|
|
225
225
|
placeholder="Untitled"
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
>
|
|
266
266
|
<!-- Sidebar: filter + note list -->
|
|
267
267
|
<div class="flex w-56 shrink-0 flex-col border-r border-divider-faded">
|
|
268
|
-
<!-- content="
|
|
268
|
+
<!-- content="pill": 24/30/36/48 of chrome around an 18/20/22/26 field and
|
|
269
269
|
button, with Bar deriving the two-tier concentric inset. A notes sidebar
|
|
270
270
|
is chrome around a list; at the lg/md pairing it was a 32px bar over
|
|
271
271
|
24px rows and the chrome outweighed the content. This is the tier pair
|
|
@@ -277,17 +277,17 @@
|
|
|
277
277
|
centred in 23px and sat 2.5px from the top against a 3px wall. Outset
|
|
278
278
|
costs no layout, so the bar keeps its full tier height and every child
|
|
279
279
|
sits the wall's distance from all four edges. -->
|
|
280
|
-
<Bar size="
|
|
280
|
+
<Bar size="control" content="pill" divider class="rounded-none">
|
|
281
281
|
<Input
|
|
282
282
|
icon={Search}
|
|
283
|
-
size="
|
|
283
|
+
size="pill"
|
|
284
284
|
class="min-w-0 flex-1"
|
|
285
285
|
type="text"
|
|
286
286
|
placeholder="Filter…"
|
|
287
287
|
aria-label="Filter notes"
|
|
288
288
|
bind:value={query}
|
|
289
289
|
/>
|
|
290
|
-
<Button icon size="
|
|
290
|
+
<Button icon size="pill" aria-label="New note" title="New note" onclick={createNote}>
|
|
291
291
|
<Plus />
|
|
292
292
|
</Button>
|
|
293
293
|
</Bar>
|
|
@@ -331,7 +331,7 @@
|
|
|
331
331
|
{#snippet child({ props })}
|
|
332
332
|
<div
|
|
333
333
|
{...props}
|
|
334
|
-
class="group/row flex h-control cursor-pointer items-center gap-(--era-pill-inset-control) rounded-control px-(--era-pill-inset-control)
|
|
334
|
+
class="group/row flex h-control cursor-pointer items-center gap-(--era-pill-inset-control) rounded-control px-(--era-pill-inset-control) leading-none {current
|
|
335
335
|
? 'bg-hover text-bright shadow-(--era-shadow-pressed)'
|
|
336
336
|
: 'hover:bg-highlight hover:shadow-highlight'}"
|
|
337
337
|
role="option"
|
|
@@ -374,7 +374,7 @@
|
|
|
374
374
|
>
|
|
375
375
|
<Button
|
|
376
376
|
icon
|
|
377
|
-
size="
|
|
377
|
+
size="pill"
|
|
378
378
|
aria-label={note.pinned ? 'Unpin note' : 'Pin note'}
|
|
379
379
|
title={note.pinned ? 'Unpin note' : 'Pin note'}
|
|
380
380
|
active={note.pinned}
|
|
@@ -387,7 +387,7 @@
|
|
|
387
387
|
</Button>
|
|
388
388
|
<Button
|
|
389
389
|
icon
|
|
390
|
-
size="
|
|
390
|
+
size="pill"
|
|
391
391
|
tone="destructive"
|
|
392
392
|
aria-label="Delete note"
|
|
393
393
|
title="Delete note"
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
header (see the snippets below) lives in the pane bar's centre and
|
|
445
445
|
drawing it twice would title the document twice. -->
|
|
446
446
|
{#if !pane}
|
|
447
|
-
<Bar size="
|
|
447
|
+
<Bar size="control" content="pill" divider class="rounded-none">
|
|
448
448
|
{@render iconPicker(selected)}
|
|
449
449
|
{@render titleField(selected, 'min-w-0 flex-1')}
|
|
450
450
|
<!-- Fixed-width so the badge appearing and clearing never nudges the
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A flex row that centres its OWN bare text — the one place ink centring
|
|
3
|
+
* cannot be applied, and so the one place it silently isn't.
|
|
4
|
+
*
|
|
5
|
+
* `text-box-trim` is era's mechanism for centring INK rather than the font box:
|
|
6
|
+
* it collapses the line box to a typographic band so what you see is centred
|
|
7
|
+
* rather than what the metrics measure. It applies to a BLOCK box. It cannot
|
|
8
|
+
* apply to bare text inside a flex container, because that text is wrapped in
|
|
9
|
+
* an ANONYMOUS flex item — a box no selector reaches and no non-inherited
|
|
10
|
+
* property touches. `text-box-trim` is not inherited, so it never arrives.
|
|
11
|
+
*
|
|
12
|
+
* Proven rather than reasoned: the same pill rendered `flex + trim` and `flex,
|
|
13
|
+
* no trim` produces IDENTICAL ink, to the device pixel. Centring then falls
|
|
14
|
+
* back to `align-items` on the LINE BOX — precisely the font-metric-dependent
|
|
15
|
+
* centring the trim exists to eliminate. On the shipped mono stack that put a
|
|
16
|
+
* badge's caps 2px high in an 18px pill.
|
|
17
|
+
*
|
|
18
|
+
* This audit deliberately does NOT look for the `era-text-trim` class. That was
|
|
19
|
+
* the first version, and it was the wrong instrument: deleting an inert class
|
|
20
|
+
* silences it while leaving the text exactly as mis-centred as before. What
|
|
21
|
+
* matters is the SHAPE — a centring flex row with a text node of its own — so
|
|
22
|
+
* that is what it measures. It stays true after the cleanup.
|
|
23
|
+
*
|
|
24
|
+
* Two kinds of hit, and only one of them is the author's to fix:
|
|
25
|
+
*
|
|
26
|
+
* FIXABLE — the row holds nothing but text. It has no reason to be flex; as a
|
|
27
|
+
* block with `era-label-ink` (or `era-token-ink` for a centred token) the trim
|
|
28
|
+
* works and the centring stops depending on the font.
|
|
29
|
+
*
|
|
30
|
+
* STRUCTURAL — the row holds an icon AND text. The box must stay flex to lay
|
|
31
|
+
* the two out, which re-creates the anonymous item, and no CSS reaches it.
|
|
32
|
+
* Components take the `not-has-[svg]:era-label-ink` guard so the text-only
|
|
33
|
+
* case is centred properly and only the icon case falls back. Closing it for
|
|
34
|
+
* good means the label arriving in its own slot instead of inside an opaque
|
|
35
|
+
* `children` snippet — an API change, tracked in KNOWN_ISSUES.
|
|
36
|
+
*/
|
|
37
|
+
/** Cheap pre-filter: only a row that CENTRES is relying on ink centring. */
|
|
38
|
+
const CENTRING = new Set(['center', 'safe center', 'anchor-center']);
|
|
39
|
+
export const anonymousText = {
|
|
40
|
+
id: 'typography/anonymous-text',
|
|
41
|
+
name: 'A flex row centres its own bare text',
|
|
42
|
+
description: 'Bare text in a flex container is an anonymous flex item, which text-box-trim cannot reach — the ink is centred by font metrics instead. Make a text-only row a block with era-label-ink; an icon+text row needs the label in its own element.',
|
|
43
|
+
category: 'typography',
|
|
44
|
+
severity: 'warn',
|
|
45
|
+
// Every flex box is a candidate; the checks below are the real gate and are
|
|
46
|
+
// far cheaper than reading styles for the whole tree.
|
|
47
|
+
selector: 'div,span,button,a,label,li,header,footer,summary',
|
|
48
|
+
check(el) {
|
|
49
|
+
const s = getComputedStyle(el);
|
|
50
|
+
if (s.display !== 'flex' && s.display !== 'inline-flex')
|
|
51
|
+
return null;
|
|
52
|
+
if (!CENTRING.has(s.alignItems))
|
|
53
|
+
return null;
|
|
54
|
+
// The anonymous item itself: a text node directly under the flex box.
|
|
55
|
+
const ownText = [...el.childNodes].some((n) => n.nodeType === Node.TEXT_NODE && n.textContent?.trim());
|
|
56
|
+
if (!ownText)
|
|
57
|
+
return null;
|
|
58
|
+
// Multi-line text is not centred by ink in the first place — this law is
|
|
59
|
+
// about single-line control chrome, which is what `nowrap` marks.
|
|
60
|
+
if (s.whiteSpace !== 'nowrap' && s.flexWrap !== 'nowrap')
|
|
61
|
+
return null;
|
|
62
|
+
const hasElementChild = el.children.length > 0;
|
|
63
|
+
const issue = {
|
|
64
|
+
auditId: 'typography/anonymous-text',
|
|
65
|
+
element: el,
|
|
66
|
+
message: hasElementChild
|
|
67
|
+
? 'flex row holding an icon AND its own bare text — the text is an anonymous flex item, so its ink is centred by font metrics. The label needs its own element (or its own slot) before a trim can reach it.'
|
|
68
|
+
: 'flex row holding nothing but text — it has no reason to be flex. Make it a block with era-label-ink (or era-token-ink for a centred token) and the trim applies.',
|
|
69
|
+
details: { display: s.display, hasElementChild }
|
|
70
|
+
};
|
|
71
|
+
return issue;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -4,6 +4,7 @@ import { concentricInset } from './concentric-inset.js';
|
|
|
4
4
|
import { cornersAxis } from './corners-axis.js';
|
|
5
5
|
import { evenContainerPadding } from './even-container-padding.js';
|
|
6
6
|
import { flatChrome } from './flat-chrome.js';
|
|
7
|
+
import { anonymousText } from './anonymous-text.js';
|
|
7
8
|
import { iconFlush } from './icon-flush.js';
|
|
8
9
|
import { offAxisMotion } from './off-axis-motion.js';
|
|
9
10
|
import { offTierHeight } from './off-tier-height.js';
|
|
@@ -14,4 +15,4 @@ import { restingGap } from './resting-gap.js';
|
|
|
14
15
|
import { singleGlyphSquare } from './single-glyph-square.js';
|
|
15
16
|
import { uniformSiblingGaps } from './uniform-sibling-gaps.js';
|
|
16
17
|
import { wallMatchesGap } from './wall-matches-gap.js';
|
|
17
|
-
export { clippedGlyphs, collapsedTextTrim, concentricInset, cornersAxis, evenContainerPadding, flatChrome, iconFlush, offAxisMotion, offTierHeight, pillHoldsItsInk, proseLeading, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
18
|
+
export { clippedGlyphs, collapsedTextTrim, concentricInset, cornersAxis, evenContainerPadding, flatChrome, iconFlush, anonymousText, offAxisMotion, offTierHeight, pillHoldsItsInk, proseLeading, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
@@ -5,6 +5,7 @@ import { concentricInset } from './concentric-inset.js';
|
|
|
5
5
|
import { cornersAxis } from './corners-axis.js';
|
|
6
6
|
import { evenContainerPadding } from './even-container-padding.js';
|
|
7
7
|
import { flatChrome } from './flat-chrome.js';
|
|
8
|
+
import { anonymousText } from './anonymous-text.js';
|
|
8
9
|
import { iconFlush } from './icon-flush.js';
|
|
9
10
|
import { offAxisMotion } from './off-axis-motion.js';
|
|
10
11
|
import { offTierHeight } from './off-tier-height.js';
|
|
@@ -15,7 +16,7 @@ import { restingGap } from './resting-gap.js';
|
|
|
15
16
|
import { singleGlyphSquare } from './single-glyph-square.js';
|
|
16
17
|
import { uniformSiblingGaps } from './uniform-sibling-gaps.js';
|
|
17
18
|
import { wallMatchesGap } from './wall-matches-gap.js';
|
|
18
|
-
export { clippedGlyphs, collapsedTextTrim, concentricInset, cornersAxis, evenContainerPadding, flatChrome, iconFlush, offAxisMotion, offTierHeight, pillHoldsItsInk, proseLeading, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
19
|
+
export { clippedGlyphs, collapsedTextTrim, concentricInset, cornersAxis, evenContainerPadding, flatChrome, iconFlush, anonymousText, offAxisMotion, offTierHeight, pillHoldsItsInk, proseLeading, radiusConcentricity, restingGap, singleGlyphSquare, uniformSiblingGaps, wallMatchesGap };
|
|
19
20
|
// Auto-register built-ins. Consumers can still unregister or add their own.
|
|
20
21
|
//
|
|
21
22
|
// Two of these only tell the truth on a particular axis setting, because the bug
|
|
@@ -31,6 +32,7 @@ registry.register(cornersAxis);
|
|
|
31
32
|
registry.register(evenContainerPadding);
|
|
32
33
|
registry.register(flatChrome);
|
|
33
34
|
registry.register(iconFlush);
|
|
35
|
+
registry.register(anonymousText);
|
|
34
36
|
registry.register(offAxisMotion);
|
|
35
37
|
registry.register(offTierHeight);
|
|
36
38
|
registry.register(pillHoldsItsInk);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type AuditSeverity = 'info' | 'warn' | 'error';
|
|
2
|
-
export type AuditCategory = 'layout' | 'tokens' | 'a11y' | 'semantics';
|
|
2
|
+
export type AuditCategory = 'layout' | 'tokens' | 'typography' | 'a11y' | 'semantics';
|
|
3
3
|
export interface Issue {
|
|
4
4
|
readonly auditId: string;
|
|
5
5
|
readonly element: HTMLElement;
|
package/dist/docs/llms-txt.js
CHANGED
|
@@ -10,8 +10,8 @@ function orientation(origin) {
|
|
|
10
10
|
'## Using the library',
|
|
11
11
|
'',
|
|
12
12
|
'```ts',
|
|
13
|
-
'import { Button, Table, Select } from "@
|
|
14
|
-
'import "@
|
|
13
|
+
'import { Button, Table, Select } from "@signal9/era-ui";',
|
|
14
|
+
'import "@signal9/era-ui/css";',
|
|
15
15
|
'```',
|
|
16
16
|
'',
|
|
17
17
|
'Named exports wrap a single primitive (`Button`, `Badge`, `Chip`, `Input`, …). Namespace exports compose primitives via sub-components (`Table.Root`, `Table.Row`, `Select.Trigger`, `Dialog.Content`, …). Every wrapper forwards unknown props to its underlying element or bits-ui primitive, so standard HTML attributes (`class`, `style`, event handlers, `aria-*`, `data-*`) work without ceremony.',
|