@signal9/era-ui 4.14.1 → 4.15.1
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/apps/notes/note-editor.svelte +20 -0
- package/dist/apps/notes/notes.svelte +132 -78
- package/dist/apps/notes/notes.svelte.d.ts +31 -0
- package/dist/dev/audit/audits/prose-leading.js +24 -11
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/bar.md +1 -0
- package/dist/generated-docs/llms-full.txt +2 -0
- package/dist/generated-docs/manifest.json +2 -2
- package/dist/generated-docs/pane.md +1 -0
- package/dist/os/index.d.ts +1 -1
- package/dist/os/index.js +1 -1
- package/dist/os/pane-manager.svelte.d.ts +16 -0
- package/dist/os/pane-manager.svelte.js +10 -0
- package/dist/os/pane.svelte +16 -9
- package/dist/styles/themes.css +88 -100
- package/dist/ui/bar/bar.svelte +27 -2
- package/dist/ui/bar/bar.svelte.d.ts +68 -0
- package/dist/ui/dialog/dialog-content.svelte +2 -1
- package/dist/ui/dialog/dialog-header.svelte +10 -25
- package/dist/ui/pane/pane-handle.svelte +42 -14
- package/dist/ui/pane/pane-handle.svelte.d.ts +7 -0
- package/dist/ui/pane/pane.svelte +12 -2
- package/dist/ui/pane/pane.svelte.d.ts +2 -0
- package/dist/ui/sheet/sheet-content.svelte +14 -16
- package/dist/ui/sheet/sheet-content.svelte.d.ts +18 -18
- package/package.json +19 -19
|
@@ -915,6 +915,7 @@ Inherits all props from `HTMLAttributes<HTMLDivElement>`.
|
|
|
915
915
|
| `content?` | `'tier' \| 'xxs'` | `'tier'` | Tier of the controls inside — drives the bar's padding and gap so they
|
|
916
916
|
stay concentric to them. `tier` (default) is one tier smaller; `xxs` is
|
|
917
917
|
the compact pairing (xxs pills/fields in an sm or md bar). |
|
|
918
|
+
| `divider?` | `boolean` | `false` | The flush cap-a-panel chrome: outset divider below, bottom rd-md. |
|
|
918
919
|
| `children?` | `Snippet` | — | — |
|
|
919
920
|
|
|
920
921
|
<!-- end: bar -->
|
|
@@ -1028,6 +1029,7 @@ after the pane's own. Pass Compartments for reactive plugin sets. |
|
|
|
1028
1029
|
| `onDragEnd?` | `(data: DragEventData) => void` | — | — |
|
|
1029
1030
|
| `onClose?` | `() => void` | — | When set, a close button renders at the right of the handle bar. |
|
|
1030
1031
|
| `header?` | `Snippet` | — | Extra handle-bar content, rendered after the title. |
|
|
1032
|
+
| `center?` | `Snippet` | — | Content centred on the bar itself — Pane.Handle's three-track grid. |
|
|
1031
1033
|
| `children?` | `Snippet` | — | — |
|
|
1032
1034
|
|
|
1033
1035
|
<!-- end: pane -->
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
"slug": "bar",
|
|
160
160
|
"title": "Bar",
|
|
161
161
|
"summary": "A flex container for toolbars and headers, in three heights.",
|
|
162
|
-
"tokenEstimate":
|
|
162
|
+
"tokenEstimate": 207,
|
|
163
163
|
"sections": [
|
|
164
164
|
"Import",
|
|
165
165
|
"Props"
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"slug": "pane",
|
|
195
195
|
"title": "Pane",
|
|
196
196
|
"summary": "A draggable floating window with a handle bar.",
|
|
197
|
-
"tokenEstimate":
|
|
197
|
+
"tokenEstimate": 441,
|
|
198
198
|
"sections": [
|
|
199
199
|
"Import",
|
|
200
200
|
"Props"
|
|
@@ -29,4 +29,5 @@ after the pane's own. Pass Compartments for reactive plugin sets. |
|
|
|
29
29
|
| `onDragEnd?` | `(data: DragEventData) => void` | — | — |
|
|
30
30
|
| `onClose?` | `() => void` | — | When set, a close button renders at the right of the handle bar. |
|
|
31
31
|
| `header?` | `Snippet` | — | Extra handle-bar content, rendered after the title. |
|
|
32
|
+
| `center?` | `Snippet` | — | Content centred on the bar itself — Pane.Handle's three-track grid. |
|
|
32
33
|
| `children?` | `Snippet` | — | — |
|
package/dist/os/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { PaneManager, getPaneManager, setPaneManagerContext, type Point, type Size, type Rect, type Insets, type Frame, type AppDefinition, type AppPane, type AppComponent, type AppProps, type PaneState, type JsonValue, type PaneSnapshot, type WorkspaceSnapshot, SNAPSHOT_VERSION, readSnapshot } from './pane-manager.svelte.js';
|
|
1
|
+
export { PaneManager, getPaneManager, tryGetPaneManager, setPaneManagerContext, type Point, type Size, type Rect, type Insets, type Frame, type AppDefinition, type AppPane, type AppComponent, type AppProps, type PaneState, type JsonValue, type PaneSnapshot, type WorkspaceSnapshot, SNAPSHOT_VERSION, readSnapshot } from './pane-manager.svelte.js';
|
|
2
2
|
export { persistWorkspace, loadWorkspace, saveWorkspace, clearWorkspace, WORKSPACE_STORAGE_KEY } from './workspace-storage.svelte.js';
|
|
3
3
|
export { type SnapZone, type Workspace } from './pane-manager.svelte.js';
|
|
4
4
|
export { NotificationService, getNotifications, setNotificationContext, type AppNotification, type NotifyOptions, type NotificationAction, type NotificationTone } from './notifications.svelte.js';
|
package/dist/os/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Two stores (pane manager + notifications) shared via context, and the
|
|
3
3
|
// components that render them: Desktop host, Pane, Taskbar (with its
|
|
4
4
|
// built-in command bar), Toaster, NotificationCenter.
|
|
5
|
-
export { PaneManager, getPaneManager, setPaneManagerContext, SNAPSHOT_VERSION, readSnapshot } from './pane-manager.svelte.js';
|
|
5
|
+
export { PaneManager, getPaneManager, tryGetPaneManager, setPaneManagerContext, SNAPSHOT_VERSION, readSnapshot } from './pane-manager.svelte.js';
|
|
6
6
|
export { persistWorkspace, loadWorkspace, saveWorkspace, clearWorkspace, WORKSPACE_STORAGE_KEY } from './workspace-storage.svelte.js';
|
|
7
7
|
export {} from './pane-manager.svelte.js';
|
|
8
8
|
export { NotificationService, getNotifications, setNotificationContext } from './notifications.svelte.js';
|
|
@@ -150,6 +150,14 @@ export interface AppDefinition {
|
|
|
150
150
|
* live AppPane too, so a host can swap it as the pane's content
|
|
151
151
|
* changes. */
|
|
152
152
|
actions?: Snippet;
|
|
153
|
+
/** Replaces the default icon+title cluster on the LEFT of the title bar.
|
|
154
|
+
* Assignable live, like `actions`. */
|
|
155
|
+
lead?: Snippet;
|
|
156
|
+
/** Content centred on the title bar itself (a document title, a breadcrumb) —
|
|
157
|
+
* rendered in Pane.Handle's three-track grid, so it stays pinned to the true
|
|
158
|
+
* centre of the bar however the sides grow. Assignable live. Interactive
|
|
159
|
+
* content in it must carry data-pane-control (the bar is the drag zone). */
|
|
160
|
+
center?: Snippet;
|
|
153
161
|
}
|
|
154
162
|
/** A live pane instance in the registry. */
|
|
155
163
|
export interface AppPane {
|
|
@@ -173,6 +181,10 @@ export interface AppPane {
|
|
|
173
181
|
minimizable: boolean;
|
|
174
182
|
bodyClass?: string;
|
|
175
183
|
actions?: Snippet;
|
|
184
|
+
/** See AppDefinition — both assignable on the live pane, which is how an
|
|
185
|
+
* app claims its own chrome: `pane.center = mySnippet` from the body. */
|
|
186
|
+
lead?: Snippet;
|
|
187
|
+
center?: Snippet;
|
|
176
188
|
/** App-specific state, carried verbatim through a snapshot. */
|
|
177
189
|
props?: JsonValue;
|
|
178
190
|
}
|
|
@@ -284,3 +296,7 @@ export declare class PaneManager {
|
|
|
284
296
|
}
|
|
285
297
|
export declare function setPaneManagerContext(pm: PaneManager): PaneManager;
|
|
286
298
|
export declare function getPaneManager(): PaneManager;
|
|
299
|
+
/** The optional form, for content that renders both inside and outside a
|
|
300
|
+
* Desktop (a docs route under ?bare, a component demoed standalone):
|
|
301
|
+
* undefined instead of a throw when no shell is up. */
|
|
302
|
+
export declare function tryGetPaneManager(): PaneManager | undefined;
|
|
@@ -183,6 +183,8 @@ export class PaneManager {
|
|
|
183
183
|
minimizable: app.minimizable ?? true,
|
|
184
184
|
bodyClass: app.bodyClass,
|
|
185
185
|
actions: app.actions,
|
|
186
|
+
lead: app.lead,
|
|
187
|
+
center: app.center,
|
|
186
188
|
props: app.props
|
|
187
189
|
};
|
|
188
190
|
this.panes.push(win);
|
|
@@ -364,6 +366,8 @@ export class PaneManager {
|
|
|
364
366
|
minimizable: app.minimizable ?? true,
|
|
365
367
|
bodyClass: app.bodyClass,
|
|
366
368
|
actions: app.actions,
|
|
369
|
+
lead: app.lead,
|
|
370
|
+
center: app.center,
|
|
367
371
|
props: s.props ?? app.props
|
|
368
372
|
});
|
|
369
373
|
}
|
|
@@ -444,3 +448,9 @@ export function getPaneManager() {
|
|
|
444
448
|
throw new Error('getPaneManager() must be used inside a <Desktop>.');
|
|
445
449
|
return pm;
|
|
446
450
|
}
|
|
451
|
+
/** The optional form, for content that renders both inside and outside a
|
|
452
|
+
* Desktop (a docs route under ?bare, a component demoed standalone):
|
|
453
|
+
* undefined instead of a throw when no shell is up. */
|
|
454
|
+
export function tryGetPaneManager() {
|
|
455
|
+
return getContext(PM_KEY);
|
|
456
|
+
}
|
package/dist/os/pane.svelte
CHANGED
|
@@ -92,14 +92,21 @@
|
|
|
92
92
|
from the edge against 5px above and below it — read as crowded because it
|
|
93
93
|
was. --era-inset-md is (h-md − text)/2, so the label now starts the same
|
|
94
94
|
distance in from the edge as it sits from the top. -->
|
|
95
|
-
<Pane.Handle class="gap-(--era-gap) pl-(--era-inset-md)">
|
|
96
|
-
{#if pane.
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
<Pane.Handle class="gap-(--era-gap) pl-(--era-inset-md)" center={pane.center}>
|
|
96
|
+
{#if pane.lead}
|
|
97
|
+
<!-- The app replaced the whole icon+title cluster. Its content lives in
|
|
98
|
+
the drag zone like the default one; anything interactive in it
|
|
99
|
+
carries data-pane-control itself. -->
|
|
100
|
+
{@render pane.lead()}
|
|
101
|
+
{:else}
|
|
102
|
+
{#if pane.icon}
|
|
103
|
+
{@const Icon = pane.icon}
|
|
104
|
+
<Icon class="size-(--era-h-xs) shrink-0 text-muted" />
|
|
105
|
+
{/if}
|
|
106
|
+
<span class={cn('truncate text-body', focused ? 'text-bright' : 'text-muted')}
|
|
107
|
+
>{pane.title}</span
|
|
108
|
+
>
|
|
99
109
|
{/if}
|
|
100
|
-
<span class={cn('truncate text-body', focused ? 'text-bright' : 'text-muted')}
|
|
101
|
-
>{pane.title}</span
|
|
102
|
-
>
|
|
103
110
|
<!-- Close / minimize / maximize. Deliberately NOT data-pane-control (same as
|
|
104
111
|
the raw Pane.Close): a left press-and-HOLD drags the pane — neodrag only
|
|
105
112
|
drags the primary button and suppresses the trailing click after a real
|
|
@@ -109,7 +116,7 @@
|
|
|
109
116
|
rhythm as the rest of the handle. Like the close button they carry no
|
|
110
117
|
data-pane-control, so a press-and-hold anywhere on the bar still drags
|
|
111
118
|
the pane and only a clean click activates them. -->
|
|
112
|
-
|
|
119
|
+
{#snippet trail()}
|
|
113
120
|
{@render pane.actions?.()}
|
|
114
121
|
<!-- The macOS traffic lights, one button: red = close at rest, and the
|
|
115
122
|
held preview recolours to the action it will commit — yellow for
|
|
@@ -158,7 +165,7 @@
|
|
|
158
165
|
<X />
|
|
159
166
|
{/if}
|
|
160
167
|
</Button>
|
|
161
|
-
|
|
168
|
+
{/snippet}
|
|
162
169
|
</Pane.Handle>
|
|
163
170
|
|
|
164
171
|
<Pane.Content class={pane.bodyClass}>
|
package/dist/styles/themes.css
CHANGED
|
@@ -49,54 +49,8 @@
|
|
|
49
49
|
--color-surface: var(--color-2);
|
|
50
50
|
--color-hover: oklch(0.25 0 none);
|
|
51
51
|
--color-fg: var(--color-11);
|
|
52
|
-
/*
|
|
53
|
-
*
|
|
54
|
-
* Step 12 is the ramp's extreme, and at the extreme a neutral ramp has run out
|
|
55
|
-
* of colour — so `bright` came out pure white on the dark themes and pure
|
|
56
|
-
* black on signalnine light. That is the one place a themed UI should NOT go
|
|
57
|
-
* achromatic: bright is the emphasis ink (headings, titles, the sentence you
|
|
58
|
-
* are meant to read first), so it is the text carrying the most identity and
|
|
59
|
-
* it was carrying none.
|
|
60
|
-
*
|
|
61
|
-
* It now takes 12% of --color-primary — the theme's own accent, which is also
|
|
62
|
-
* what the accent picker writes, so choosing an accent tints the emphasis text
|
|
63
|
-
* with it live. 12% is a cast, not a colour: it moves lightness by ~0.02 (the
|
|
64
|
-
* default dark page keeps 11.6:1) and lands chroma near 0.013, which reads as
|
|
65
|
-
* "warm white" or "blue-black" rather than as a coloured heading.
|
|
66
|
-
*
|
|
67
|
-
* A theme whose accent IS neutral (the default) is unchanged by construction —
|
|
68
|
-
* there is no hue to lend — which is the correct behaviour rather than an
|
|
69
|
-
* exception.
|
|
70
|
-
*
|
|
71
|
-
* The achromatic step 12s are written with hue `none` rather than hue 0, and
|
|
72
|
-
* that is load-bearing: hue 0 is a REAL hue (red), so oklch interpolation
|
|
73
|
-
* walks it round to the accent's the short way and a black-plus-blue mix came
|
|
74
|
-
* out magenta (signalnine light's #000 landed on hue 349). `none` marks the
|
|
75
|
-
* hue as powerless — which it is at chroma 0 — so the mix adopts the accent's
|
|
76
|
-
* hue instead of travelling to it. The colours themselves are unchanged
|
|
77
|
-
* everywhere else: at chroma 0 the hue paints nothing. */
|
|
78
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
79
|
-
/* Muted is body copy, not a de-emphasis wash — the /text page sets whole
|
|
80
|
-
* paragraphs in it — so it does NOT sit flat on step 10: it leans 40% of the
|
|
81
|
-
* way to --color-bright. Step 10 alone read as "greyed out" at paragraph
|
|
82
|
-
* length (7.80:1 on the default dark page); the lean lands it at 10.43:1, and
|
|
83
|
-
* the ordering muted < fg < bright still holds by a clear margin (0.80 / 0.86
|
|
84
|
-
* / 0.93 in dark), so the mixed-tone case — muted prose beside a bright
|
|
85
|
-
* literal — keeps all three of its levels.
|
|
86
|
-
*
|
|
87
|
-
* It aims at BRIGHT rather than fg on purpose: fg is the ink of controls and
|
|
88
|
-
* labels, bright is the ink of the sentence you are meant to read, and body
|
|
89
|
-
* copy belongs with the second one. It also means muted inherits the theme's
|
|
90
|
-
* tint through bright, so prose is never a neutral grey on a themed page.
|
|
91
|
-
*
|
|
92
|
-
* The lean is toward a TOKEN rather than a fixed lightness so it mirrors
|
|
93
|
-
* correctly in light mode (muted DARKENS toward bright) and keeps each theme's
|
|
94
|
-
* own hue family — the tinted scales interpolate between two steps of their
|
|
95
|
-
* own ramp, never toward neutral.
|
|
96
|
-
*
|
|
97
|
-
* signalnine sets its muted by hand from measured source colours and is
|
|
98
|
-
* deliberately exempt. */
|
|
99
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
52
|
+
/* --color-bright / --color-muted: declared once for every theme — see the
|
|
53
|
+
* shared rule right after this block. */
|
|
100
54
|
/* Inline link ink. Declared once here and inherited by every theme —
|
|
101
55
|
* var(--color-bright) resolves in the element's own context, so a theme only
|
|
102
56
|
* names this if it wants links to differ from emphasis text (signalnine
|
|
@@ -142,6 +96,54 @@
|
|
|
142
96
|
--color-warning-fg: oklch(0.162 0 none);
|
|
143
97
|
}
|
|
144
98
|
|
|
99
|
+
/* -------------------------------------------------- */
|
|
100
|
+
/* Emphasis + body ink — one law for every theme */
|
|
101
|
+
/* -------------------------------------------------- */
|
|
102
|
+
/* Declared ONCE and inherited, exactly like --color-link below in the base
|
|
103
|
+
* block: every theme selector targets the same elements (:root / a .dark or
|
|
104
|
+
* .light subtree), and the mixes reference tokens that resolve against THAT
|
|
105
|
+
* element's own ramp and accent — so each theme gets its own result from one
|
|
106
|
+
* formula, and a new theme cannot forget the law by not copying it.
|
|
107
|
+
*
|
|
108
|
+
* EMPHASIS INK IS TINTED BY THE THEME, not pinned to the top of the ramp.
|
|
109
|
+
* Step 12 is the ramp's extreme, and at the extreme a neutral ramp has run
|
|
110
|
+
* out of colour — bright used to come out pure white on the dark themes and
|
|
111
|
+
* literally #000 on signalnine light. That is the one place a themed UI
|
|
112
|
+
* should NOT go achromatic: bright is the emphasis ink (headings, titles,
|
|
113
|
+
* the sentence you are meant to read first), so it is the text carrying the
|
|
114
|
+
* most identity and it was carrying none. It takes 12% of --color-primary —
|
|
115
|
+
* the theme's own accent, and the property the accent picker writes, so
|
|
116
|
+
* choosing an accent tints the emphasis text live. 12% is a cast, not a
|
|
117
|
+
* colour: lightness moves ~0.02 (the default dark page keeps 11.6:1) and
|
|
118
|
+
* chroma lands near 0.013 — "warm white" / "blue-black", never a coloured
|
|
119
|
+
* heading. A theme whose accent IS neutral is unchanged by construction.
|
|
120
|
+
*
|
|
121
|
+
* The achromatic ramp steps are written with hue `none` rather than hue 0,
|
|
122
|
+
* and that is load-bearing here: hue 0 is a REAL hue (red), so oklch
|
|
123
|
+
* interpolation walks it toward the accent's hue the short way — #000 mixed
|
|
124
|
+
* with a CTA blue came out magenta. `none` marks the hue powerless (which it
|
|
125
|
+
* is at chroma 0), so the mix ADOPTS the accent's hue instead of travelling
|
|
126
|
+
* to it.
|
|
127
|
+
*
|
|
128
|
+
* MUTED IS BODY COPY, not a de-emphasis wash — the /text page sets whole
|
|
129
|
+
* paragraphs in it — so it does not sit flat on step 10: it leans 40% of the
|
|
130
|
+
* way to bright (10.43:1 on the default dark page; step 10 alone read as
|
|
131
|
+
* "greyed out" at paragraph length). It aims at BRIGHT rather than fg on
|
|
132
|
+
* purpose: fg is the ink of controls and labels, bright is the ink of the
|
|
133
|
+
* sentence you are meant to read, and body copy belongs with the second one
|
|
134
|
+
* — which also hands muted the theme's tint. The ordering muted < fg <
|
|
135
|
+
* bright holds by a clear margin (0.80 / 0.86 / 0.93 in dark).
|
|
136
|
+
*
|
|
137
|
+
* signalnine overrides muted only (measured source colours — Tomorrow's
|
|
138
|
+
* comment grey, yahoo's #5c7eb8): the single deliberate exception, and it
|
|
139
|
+
* wins on specificity, not on order. */
|
|
140
|
+
:root,
|
|
141
|
+
.dark,
|
|
142
|
+
.light {
|
|
143
|
+
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
144
|
+
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
145
|
+
}
|
|
146
|
+
|
|
145
147
|
/* -------------------------------------------------- */
|
|
146
148
|
/* Light theme (reversed scale) */
|
|
147
149
|
/* -------------------------------------------------- */
|
|
@@ -166,8 +168,6 @@
|
|
|
166
168
|
--color-surface: var(--color-2);
|
|
167
169
|
--color-hover: oklch(0.8 0 none);
|
|
168
170
|
--color-fg: var(--color-11);
|
|
169
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
170
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
171
171
|
|
|
172
172
|
/* Accent tokens */
|
|
173
173
|
--color-primary: var(--color-fg);
|
|
@@ -205,8 +205,6 @@
|
|
|
205
205
|
--color-surface: var(--color-2);
|
|
206
206
|
--color-hover: oklch(0.8 0 none);
|
|
207
207
|
--color-fg: var(--color-11);
|
|
208
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
209
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
210
208
|
|
|
211
209
|
/* Accent tokens */
|
|
212
210
|
--color-primary: var(--color-fg);
|
|
@@ -249,8 +247,6 @@
|
|
|
249
247
|
--color-surface: var(--color-2);
|
|
250
248
|
--color-hover: oklch(0.252 0.024 56);
|
|
251
249
|
--color-fg: var(--color-11);
|
|
252
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
253
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
254
250
|
|
|
255
251
|
/* Warm sandstone primary + unexpected turquoise support. */
|
|
256
252
|
--color-primary: oklch(0.74 0.108 68);
|
|
@@ -293,8 +289,6 @@
|
|
|
293
289
|
--color-surface: var(--color-2);
|
|
294
290
|
--color-hover: oklch(0.81 0.022 73);
|
|
295
291
|
--color-fg: var(--color-11);
|
|
296
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
297
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
298
292
|
|
|
299
293
|
--color-primary: oklch(0.515 0.16 38);
|
|
300
294
|
--color-primary-fg: oklch(0.96 0.014 78);
|
|
@@ -328,8 +322,6 @@
|
|
|
328
322
|
--color-surface: var(--color-2);
|
|
329
323
|
--color-hover: oklch(0.81 0.022 73);
|
|
330
324
|
--color-fg: var(--color-11);
|
|
331
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
332
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
333
325
|
|
|
334
326
|
--color-primary: oklch(0.515 0.16 38);
|
|
335
327
|
--color-primary-fg: oklch(0.96 0.014 78);
|
|
@@ -366,8 +358,6 @@
|
|
|
366
358
|
--color-surface: var(--color-2);
|
|
367
359
|
--color-hover: color-mix(in oklch, var(--color-3) 80%, var(--color-2));
|
|
368
360
|
--color-fg: var(--color-11);
|
|
369
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
370
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
371
361
|
|
|
372
362
|
--color-primary: oklch(0.8 0.034 54);
|
|
373
363
|
--color-primary-fg: var(--color-1);
|
|
@@ -404,8 +394,6 @@
|
|
|
404
394
|
--color-surface: var(--color-2);
|
|
405
395
|
--color-hover: oklch(0.88 0.016 72);
|
|
406
396
|
--color-fg: var(--color-11);
|
|
407
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
408
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
409
397
|
|
|
410
398
|
--color-primary: oklch(0.55 0.12 58);
|
|
411
399
|
--color-primary-fg: oklch(0.97 0.008 80);
|
|
@@ -439,8 +427,6 @@
|
|
|
439
427
|
--color-surface: var(--color-2);
|
|
440
428
|
--color-hover: oklch(0.88 0.016 72);
|
|
441
429
|
--color-fg: var(--color-11);
|
|
442
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
443
|
-
--color-muted: color-mix(in oklch, var(--color-10) 60%, var(--color-bright));
|
|
444
430
|
|
|
445
431
|
--color-primary: oklch(0.55 0.12 58);
|
|
446
432
|
--color-primary-fg: oklch(0.97 0.008 80);
|
|
@@ -454,12 +440,16 @@
|
|
|
454
440
|
}
|
|
455
441
|
|
|
456
442
|
/* -------------------------------------------------- */
|
|
457
|
-
/* Signalnine — dark (Tomorrow,
|
|
443
|
+
/* Signalnine — dark (Tomorrow bones, amber ink) */
|
|
458
444
|
/* */
|
|
459
|
-
/* The classic imageboard dark theme. The
|
|
460
|
-
/*
|
|
461
|
-
/*
|
|
462
|
-
/*
|
|
445
|
+
/* The classic imageboard dark theme. The scale keeps */
|
|
446
|
+
/* Tomorrow Night's lightnesses — comment and */
|
|
447
|
+
/* foreground still land at steps 8 and 11 — but the */
|
|
448
|
+
/* BLUE IS GONE: the ramp's cast and the accent are */
|
|
449
|
+
/* both a cool amber (hue 84, the yellow side of */
|
|
450
|
+
/* amber), so chrome, links and emphasis share one */
|
|
451
|
+
/* temperature instead of a blue ramp under yellow */
|
|
452
|
+
/* accents. */
|
|
463
453
|
/* */
|
|
464
454
|
/* The DARK END is deliberately deeper than Tomorrow's. */
|
|
465
455
|
/* Its #1d1f21 page read light next to era's other dark */
|
|
@@ -474,11 +464,11 @@
|
|
|
474
464
|
/* the link blue had been placed IN the ramp, which */
|
|
475
465
|
/* forced the steps under it to swerve to reach it. */
|
|
476
466
|
/* Tomorrow does not work that way and neither does */
|
|
477
|
-
/* this now — the ramp is one
|
|
478
|
-
/*
|
|
479
|
-
/*
|
|
480
|
-
/*
|
|
481
|
-
/* carried by the text.
|
|
467
|
+
/* this now — the ramp is one gray from end to end */
|
|
468
|
+
/* (cast faintly amber, never placed IN the ramp), and */
|
|
469
|
+
/* the amber, red and green are ACCENT tokens sitting */
|
|
470
|
+
/* on top of it. That is what an imageboard actually */
|
|
471
|
+
/* looks like: gray chrome, colour carried by the text. */
|
|
482
472
|
/* -------------------------------------------------- */
|
|
483
473
|
:root[data-theme='signalnine'],
|
|
484
474
|
[data-theme='signalnine'].dark {
|
|
@@ -488,18 +478,18 @@
|
|
|
488
478
|
* own #1d1f21 — that read too light beside the other dark themes. Everything
|
|
489
479
|
* from step 2 up re-spaces evenly from the new floor to the comment anchor,
|
|
490
480
|
* so lowering the page did not leave a cliff between it and the panels. */
|
|
491
|
-
--color-1: oklch(0.168 0.
|
|
492
|
-
--color-2: oklch(0.225 0.
|
|
493
|
-
--color-3: oklch(0.29 0.
|
|
494
|
-
--color-4: oklch(0.355 0.
|
|
495
|
-
--color-5: oklch(0.435 0.
|
|
496
|
-
--color-6: oklch(0.515 0.
|
|
497
|
-
--color-7: oklch(0.596 0.
|
|
498
|
-
--color-8: oklch(0.677 0.006
|
|
499
|
-
--color-9: oklch(0.73 0.005
|
|
500
|
-
--color-10: oklch(0.78 0.005
|
|
501
|
-
--color-11: oklch(0.83 0.
|
|
502
|
-
--color-12: oklch(0.9 0.
|
|
481
|
+
--color-1: oklch(0.168 0.004 84); /* graphite floor, amber-cast */
|
|
482
|
+
--color-2: oklch(0.225 0.005 84);
|
|
483
|
+
--color-3: oklch(0.29 0.007 84);
|
|
484
|
+
--color-4: oklch(0.355 0.008 84);
|
|
485
|
+
--color-5: oklch(0.435 0.008 84);
|
|
486
|
+
--color-6: oklch(0.515 0.008 84);
|
|
487
|
+
--color-7: oklch(0.596 0.007 84);
|
|
488
|
+
--color-8: oklch(0.677 0.006 84); /* comment grey, warmed */
|
|
489
|
+
--color-9: oklch(0.73 0.005 84);
|
|
490
|
+
--color-10: oklch(0.78 0.005 84);
|
|
491
|
+
--color-11: oklch(0.83 0.005 84); /* foreground, warmed */
|
|
492
|
+
--color-12: oklch(0.9 0.005 84);
|
|
503
493
|
|
|
504
494
|
/* Border sits a step LOWER than the usual 5. Tomorrow's own rules are
|
|
505
495
|
* quiet — its border colour is the current-line grey — and at step 5 the
|
|
@@ -507,25 +497,25 @@
|
|
|
507
497
|
* page, inside the 1.7–3.2 the other dark themes hold. */
|
|
508
498
|
--color-border: var(--color-4);
|
|
509
499
|
--color-surface: var(--color-2);
|
|
510
|
-
--color-hover: oklch(0.255 0.
|
|
500
|
+
--color-hover: oklch(0.255 0.006 84);
|
|
511
501
|
--color-fg: var(--color-11);
|
|
512
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
513
502
|
/* Tomorrow's comment grey, which is what it uses for de-emphasised text. */
|
|
514
503
|
--color-muted: var(--color-8);
|
|
515
|
-
/* The link
|
|
516
|
-
--color-link:
|
|
517
|
-
|
|
518
|
-
/*
|
|
519
|
-
*
|
|
520
|
-
|
|
521
|
-
--color-primary
|
|
504
|
+
/* The link follows the accent — one ink for "this is interactive". */
|
|
505
|
+
--color-link: var(--color-primary);
|
|
506
|
+
|
|
507
|
+
/* A COOL amber: hue 84 — the yellow side of amber, not orange — with the
|
|
508
|
+
* pastel restraint of the Tomorrow accents it replaces, so it still takes
|
|
509
|
+
* DARK text (the page colour) rather than white. */
|
|
510
|
+
--color-primary: oklch(0.76 0.115 84); /* cool amber */
|
|
511
|
+
--color-primary-fg: oklch(0.168 0.004 84);
|
|
522
512
|
--color-primary-hover: color-mix(in oklch, var(--color-primary) 85%, oklch(0 0 none));
|
|
523
513
|
--color-destructive: oklch(0.6308 0.1298 21.4); /* #cc6666 red */
|
|
524
|
-
--color-destructive-fg: oklch(0.168 0.
|
|
514
|
+
--color-destructive-fg: oklch(0.168 0.004 84);
|
|
525
515
|
--color-success: oklch(0.7733 0.1095 113.4); /* #b5bd68 green */
|
|
526
|
-
--color-success-fg: oklch(0.168 0.
|
|
516
|
+
--color-success-fg: oklch(0.168 0.004 84);
|
|
527
517
|
--color-warning: oklch(0.8462 0.1115 82.9); /* #f0c674 yellow */
|
|
528
|
-
--color-warning-fg: oklch(0.168 0.
|
|
518
|
+
--color-warning-fg: oklch(0.168 0.004 84);
|
|
529
519
|
}
|
|
530
520
|
|
|
531
521
|
/* Signalnine — light (yahoo.co.jp, measured) */
|
|
@@ -603,7 +593,6 @@
|
|
|
603
593
|
--color-surface: var(--color-2);
|
|
604
594
|
--color-hover: oklch(0.97 0.014 268.5); /* #f1f5ff */
|
|
605
595
|
--color-fg: var(--color-11);
|
|
606
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
607
596
|
--color-muted: var(--color-7);
|
|
608
597
|
--color-link: var(--color-10);
|
|
609
598
|
/* Cards read --era-surface-bg, which the surface contract defines as
|
|
@@ -646,7 +635,6 @@
|
|
|
646
635
|
--color-surface: var(--color-2);
|
|
647
636
|
--color-hover: oklch(0.97 0.014 268.5); /* #f1f5ff */
|
|
648
637
|
--color-fg: var(--color-11);
|
|
649
|
-
--color-bright: color-mix(in oklch, var(--color-12) 88%, var(--color-primary));
|
|
650
638
|
--color-muted: var(--color-7);
|
|
651
639
|
--color-link: var(--color-10);
|
|
652
640
|
/* Cards read --era-surface-bg, which the surface contract defines as
|
package/dist/ui/bar/bar.svelte
CHANGED
|
@@ -34,6 +34,28 @@
|
|
|
34
34
|
content: {
|
|
35
35
|
tier: 'gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)',
|
|
36
36
|
xxs: ''
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
40
|
+
* an app's own section bars. One variant because the law kept getting
|
|
41
|
+
* restated by hand at every site:
|
|
42
|
+
*
|
|
43
|
+
* - shrink-0: the bar holds its tier height in a flex column.
|
|
44
|
+
* - The divider is drawn OUTSIDE the box (0 1px 0, never inset). An
|
|
45
|
+
* inset shadow paints over the bar's last pixel row, which belongs to
|
|
46
|
+
* the content's own bottom gap — a centred 18px control in a 24px bar
|
|
47
|
+
* has 3px above and 3 below, and the inset line ate one of the three.
|
|
48
|
+
* Outset costs no layout and puts the line on the boundary between
|
|
49
|
+
* the bar and what's beneath it.
|
|
50
|
+
* - Corners: the TOP defers to the panel's own radius (transparent bar,
|
|
51
|
+
* rounded surface behind), the BOTTOM rounds to rd-md so the divider
|
|
52
|
+
* curves up at the ends and the bar reads as a long pill. A square
|
|
53
|
+
* in-app bar (Notes) passes `rounded-none` and tailwind-merge drops
|
|
54
|
+
* both. Collapses to flat in square mode like every radius.
|
|
55
|
+
*/
|
|
56
|
+
divider: {
|
|
57
|
+
true: 'shrink-0 rounded-t-none rounded-b-(--era-rd-md) [box-shadow:0_1px_0_var(--color-divider-faded)]',
|
|
58
|
+
false: ''
|
|
37
59
|
}
|
|
38
60
|
},
|
|
39
61
|
compoundVariants: [
|
|
@@ -45,7 +67,7 @@
|
|
|
45
67
|
// token because nothing pairs a page header with pill-tier controls.)
|
|
46
68
|
{ size: 'sm', content: 'xxs', class: 'gap-(--era-xxs-inset-sm) px-(--era-xxs-inset-sm)' }
|
|
47
69
|
],
|
|
48
|
-
defaultVariants: { size: 'lg', content: 'tier' }
|
|
70
|
+
defaultVariants: { size: 'lg', content: 'tier', divider: false }
|
|
49
71
|
});
|
|
50
72
|
</script>
|
|
51
73
|
|
|
@@ -57,6 +79,7 @@
|
|
|
57
79
|
ref = $bindable(null),
|
|
58
80
|
size = 'lg',
|
|
59
81
|
content = 'tier',
|
|
82
|
+
divider = false,
|
|
60
83
|
children,
|
|
61
84
|
class: className,
|
|
62
85
|
...restProps
|
|
@@ -70,10 +93,12 @@
|
|
|
70
93
|
* the compact pairing (xxs pills/fields in an sm or md bar).
|
|
71
94
|
*/
|
|
72
95
|
content?: 'tier' | 'xxs';
|
|
96
|
+
/** The flush cap-a-panel chrome: outset divider below, bottom rd-md. */
|
|
97
|
+
divider?: boolean;
|
|
73
98
|
children?: Snippet;
|
|
74
99
|
} = $props();
|
|
75
100
|
</script>
|
|
76
101
|
|
|
77
|
-
<div bind:this={ref} class={cn(barVariants({ size, content }), className)} {...restProps}>
|
|
102
|
+
<div bind:this={ref} class={cn(barVariants({ size, content, divider }), className)} {...restProps}>
|
|
78
103
|
{@render children?.()}
|
|
79
104
|
</div>
|
|
@@ -22,6 +22,28 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
22
22
|
tier: "gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)";
|
|
23
23
|
xxs: "";
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
27
|
+
* an app's own section bars. One variant because the law kept getting
|
|
28
|
+
* restated by hand at every site:
|
|
29
|
+
*
|
|
30
|
+
* - shrink-0: the bar holds its tier height in a flex column.
|
|
31
|
+
* - The divider is drawn OUTSIDE the box (0 1px 0, never inset). An
|
|
32
|
+
* inset shadow paints over the bar's last pixel row, which belongs to
|
|
33
|
+
* the content's own bottom gap — a centred 18px control in a 24px bar
|
|
34
|
+
* has 3px above and 3 below, and the inset line ate one of the three.
|
|
35
|
+
* Outset costs no layout and puts the line on the boundary between
|
|
36
|
+
* the bar and what's beneath it.
|
|
37
|
+
* - Corners: the TOP defers to the panel's own radius (transparent bar,
|
|
38
|
+
* rounded surface behind), the BOTTOM rounds to rd-md so the divider
|
|
39
|
+
* curves up at the ends and the bar reads as a long pill. A square
|
|
40
|
+
* in-app bar (Notes) passes `rounded-none` and tailwind-merge drops
|
|
41
|
+
* both. Collapses to flat in square mode like every radius.
|
|
42
|
+
*/
|
|
43
|
+
divider: {
|
|
44
|
+
true: "shrink-0 rounded-t-none rounded-b-(--era-rd-md) [box-shadow:0_1px_0_var(--color-divider-faded)]";
|
|
45
|
+
false: "";
|
|
46
|
+
};
|
|
25
47
|
}, undefined, "flex items-center era-text-trim", {
|
|
26
48
|
size: {
|
|
27
49
|
sm: "h-(--era-h-sm) rounded-(--era-rd-sm)";
|
|
@@ -46,6 +68,28 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
46
68
|
tier: "gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)";
|
|
47
69
|
xxs: "";
|
|
48
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
73
|
+
* an app's own section bars. One variant because the law kept getting
|
|
74
|
+
* restated by hand at every site:
|
|
75
|
+
*
|
|
76
|
+
* - shrink-0: the bar holds its tier height in a flex column.
|
|
77
|
+
* - The divider is drawn OUTSIDE the box (0 1px 0, never inset). An
|
|
78
|
+
* inset shadow paints over the bar's last pixel row, which belongs to
|
|
79
|
+
* the content's own bottom gap — a centred 18px control in a 24px bar
|
|
80
|
+
* has 3px above and 3 below, and the inset line ate one of the three.
|
|
81
|
+
* Outset costs no layout and puts the line on the boundary between
|
|
82
|
+
* the bar and what's beneath it.
|
|
83
|
+
* - Corners: the TOP defers to the panel's own radius (transparent bar,
|
|
84
|
+
* rounded surface behind), the BOTTOM rounds to rd-md so the divider
|
|
85
|
+
* curves up at the ends and the bar reads as a long pill. A square
|
|
86
|
+
* in-app bar (Notes) passes `rounded-none` and tailwind-merge drops
|
|
87
|
+
* both. Collapses to flat in square mode like every radius.
|
|
88
|
+
*/
|
|
89
|
+
divider: {
|
|
90
|
+
true: "shrink-0 rounded-t-none rounded-b-(--era-rd-md) [box-shadow:0_1px_0_var(--color-divider-faded)]";
|
|
91
|
+
false: "";
|
|
92
|
+
};
|
|
49
93
|
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
50
94
|
size: {
|
|
51
95
|
sm: "h-(--era-h-sm) rounded-(--era-rd-sm)";
|
|
@@ -70,6 +114,28 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
70
114
|
tier: "gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)";
|
|
71
115
|
xxs: "";
|
|
72
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
119
|
+
* an app's own section bars. One variant because the law kept getting
|
|
120
|
+
* restated by hand at every site:
|
|
121
|
+
*
|
|
122
|
+
* - shrink-0: the bar holds its tier height in a flex column.
|
|
123
|
+
* - The divider is drawn OUTSIDE the box (0 1px 0, never inset). An
|
|
124
|
+
* inset shadow paints over the bar's last pixel row, which belongs to
|
|
125
|
+
* the content's own bottom gap — a centred 18px control in a 24px bar
|
|
126
|
+
* has 3px above and 3 below, and the inset line ate one of the three.
|
|
127
|
+
* Outset costs no layout and puts the line on the boundary between
|
|
128
|
+
* the bar and what's beneath it.
|
|
129
|
+
* - Corners: the TOP defers to the panel's own radius (transparent bar,
|
|
130
|
+
* rounded surface behind), the BOTTOM rounds to rd-md so the divider
|
|
131
|
+
* curves up at the ends and the bar reads as a long pill. A square
|
|
132
|
+
* in-app bar (Notes) passes `rounded-none` and tailwind-merge drops
|
|
133
|
+
* both. Collapses to flat in square mode like every radius.
|
|
134
|
+
*/
|
|
135
|
+
divider: {
|
|
136
|
+
true: "shrink-0 rounded-t-none rounded-b-(--era-rd-md) [box-shadow:0_1px_0_var(--color-divider-faded)]";
|
|
137
|
+
false: "";
|
|
138
|
+
};
|
|
73
139
|
}, undefined>>;
|
|
74
140
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
75
141
|
import type { Snippet } from 'svelte';
|
|
@@ -83,6 +149,8 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
83
149
|
* the compact pairing (xxs pills/fields in an sm or md bar).
|
|
84
150
|
*/
|
|
85
151
|
content?: 'tier' | 'xxs';
|
|
152
|
+
/** The flush cap-a-panel chrome: outset divider below, bottom rd-md. */
|
|
153
|
+
divider?: boolean;
|
|
86
154
|
children?: Snippet;
|
|
87
155
|
};
|
|
88
156
|
declare const Bar: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { Dialog } from 'bits-ui';
|
|
4
|
+
import Overlay from './dialog-overlay.svelte';
|
|
4
5
|
import { cn, type PartProps } from '../../utils/index.js';
|
|
5
6
|
|
|
6
7
|
let {
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
</script>
|
|
23
24
|
|
|
24
25
|
<Dialog.Portal>
|
|
25
|
-
<
|
|
26
|
+
<Overlay />
|
|
26
27
|
<!-- border-divider: the pane's hairline-edge law. A floating panel must be
|
|
27
28
|
separable from what's behind it, and on the flat surface shadow-lg
|
|
28
29
|
collapses to none — without the hairline the whole edge was a tone step. -->
|