@runwisp/ui 0.4.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/LICENSE +215 -0
- package/README.md +20 -0
- package/package.json +128 -0
- package/src/app.d.ts +8 -0
- package/src/app.html +12 -0
- package/src/astro/CTABand.astro +32 -0
- package/src/astro/Hero.astro +58 -0
- package/src/astro/SiteFooter.astro +47 -0
- package/src/astro/SiteHeader.astro +46 -0
- package/src/lib/actions/portal.ts +16 -0
- package/src/lib/assets/runwisp-logo.svg +16 -0
- package/src/lib/components/Accordion.svelte +20 -0
- package/src/lib/components/AccordionItem.svelte +74 -0
- package/src/lib/components/Alert.svelte +107 -0
- package/src/lib/components/AlertDialog.svelte +63 -0
- package/src/lib/components/Avatar.svelte +88 -0
- package/src/lib/components/Badge.svelte +131 -0
- package/src/lib/components/Breadcrumb.svelte +58 -0
- package/src/lib/components/Button.svelte +69 -0
- package/src/lib/components/Card.svelte +70 -0
- package/src/lib/components/Checkbox.svelte +90 -0
- package/src/lib/components/CodeBlock.svelte +66 -0
- package/src/lib/components/CronInput.svelte +130 -0
- package/src/lib/components/DataGrid.svelte +368 -0
- package/src/lib/components/Drawer.svelte +156 -0
- package/src/lib/components/Dropdown.svelte +195 -0
- package/src/lib/components/DurationInput.svelte +170 -0
- package/src/lib/components/EmptyState.svelte +46 -0
- package/src/lib/components/ErrorState.svelte +73 -0
- package/src/lib/components/FeatureCard.svelte +35 -0
- package/src/lib/components/FeatureGrid.svelte +37 -0
- package/src/lib/components/FormField.svelte +53 -0
- package/src/lib/components/Heading.svelte +46 -0
- package/src/lib/components/Input.svelte +114 -0
- package/src/lib/components/Kbd.svelte +31 -0
- package/src/lib/components/Link.svelte +56 -0
- package/src/lib/components/LinkButton.svelte +55 -0
- package/src/lib/components/LogConsole.svelte +424 -0
- package/src/lib/components/Logo.svelte +34 -0
- package/src/lib/components/Modal.svelte +138 -0
- package/src/lib/components/PageContainer.svelte +38 -0
- package/src/lib/components/Pagination.svelte +96 -0
- package/src/lib/components/Popover.svelte +130 -0
- package/src/lib/components/Progress.svelte +68 -0
- package/src/lib/components/Prose.svelte +43 -0
- package/src/lib/components/Radio.svelte +82 -0
- package/src/lib/components/RadioGroup.svelte +53 -0
- package/src/lib/components/Select.svelte +454 -0
- package/src/lib/components/Separator.svelte +30 -0
- package/src/lib/components/Skeleton.svelte +75 -0
- package/src/lib/components/Sparkline.svelte +58 -0
- package/src/lib/components/Spinner.svelte +44 -0
- package/src/lib/components/StatusIndicator.svelte +203 -0
- package/src/lib/components/Table.svelte +19 -0
- package/src/lib/components/TableBody.svelte +17 -0
- package/src/lib/components/TableCell.svelte +34 -0
- package/src/lib/components/TableHead.svelte +19 -0
- package/src/lib/components/TableRow.svelte +22 -0
- package/src/lib/components/Tabs.svelte +148 -0
- package/src/lib/components/Text.svelte +57 -0
- package/src/lib/components/Textarea.svelte +81 -0
- package/src/lib/components/ThemeToggle.svelte +47 -0
- package/src/lib/components/TimezoneSelect.svelte +127 -0
- package/src/lib/components/ToastContainer.svelte +84 -0
- package/src/lib/components/Toggle.svelte +84 -0
- package/src/lib/components/Tooltip.svelte +56 -0
- package/src/lib/components/button-styles.ts +51 -0
- package/src/lib/components/dashboard/RunDetailPanel.svelte +252 -0
- package/src/lib/components/dashboard/RunsList.svelte +331 -0
- package/src/lib/components/dashboard/run-helpers.ts +12 -0
- package/src/lib/components/dashboard/status-config.ts +141 -0
- package/src/lib/components/dashboard/types.ts +35 -0
- package/src/lib/index.ts +122 -0
- package/src/lib/log-console/LogCache.svelte.ts +93 -0
- package/src/lib/log-console/LogFetcher.svelte.ts +179 -0
- package/src/lib/log-console/types.ts +24 -0
- package/src/lib/theme-tokens.css +235 -0
- package/src/lib/theme.css +276 -0
- package/src/lib/utils/error.ts +18 -0
- package/src/lib/utils/format.ts +76 -0
- package/src/lib/utils/id.ts +6 -0
- package/src/lib/utils/toast.svelte.ts +87 -0
- package/src/lib/utils/toast.ts +60 -0
- package/src/routes/+layout.svelte +10 -0
- package/src/routes/+page.svelte +5 -0
- package/src/routes/layout.css +3 -0
- package/src/stories/Alert.stories.svelte +64 -0
- package/src/stories/Avatar.stories.svelte +76 -0
- package/src/stories/Badge.stories.svelte +76 -0
- package/src/stories/Breadcrumb.stories.svelte +64 -0
- package/src/stories/Button.stories.svelte +101 -0
- package/src/stories/Card.stories.svelte +106 -0
- package/src/stories/Checkbox.stories.svelte +77 -0
- package/src/stories/DataGrid.stories.svelte +130 -0
- package/src/stories/Dropdown.stories.svelte +81 -0
- package/src/stories/EmptyState.stories.svelte +74 -0
- package/src/stories/Input.stories.svelte +88 -0
- package/src/stories/Logo.stories.svelte +82 -0
- package/src/stories/Modal.stories.svelte +101 -0
- package/src/stories/Progress.stories.svelte +65 -0
- package/src/stories/Select.stories.svelte +158 -0
- package/src/stories/Tabs.stories.svelte +103 -0
- package/src/stories/Textarea.stories.svelte +45 -0
- package/src/stories/Toggle.stories.svelte +74 -0
- package/src/stories/Tooltip.stories.svelte +77 -0
- package/src/stories/Welcome.mdx +241 -0
- package/src/stories/assets/runwisp_c.svg +16 -0
- package/src/svelte-shims.d.ts +18 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: PoppyCake, s.r.o.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import type { LogSlice, LogEvent } from "./types.js";
|
|
5
|
+
import { SvelteMap } from "svelte/reactivity";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* LogCache provides high-performance storage and pruning for log data.
|
|
9
|
+
*
|
|
10
|
+
* To handle up to 1 million lines without crashing the browser's memory,
|
|
11
|
+
* the cache must aggressively prune data that is far from the current viewport.
|
|
12
|
+
* It uses a SvelteMap for reactive, efficient line lookups and updates.
|
|
13
|
+
*/
|
|
14
|
+
export class LogCache {
|
|
15
|
+
lines = $state(new SvelteMap<number, string>());
|
|
16
|
+
totalLines = $state(0);
|
|
17
|
+
totalBytes = $state(0);
|
|
18
|
+
finished = $state(false);
|
|
19
|
+
firstAvailableLine = $state(0);
|
|
20
|
+
|
|
21
|
+
get size() {
|
|
22
|
+
return this.lines.size;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
readonly MAX_CACHE_SIZE = 50_000;
|
|
26
|
+
readonly PRUNE_THRESHOLD = 60_000;
|
|
27
|
+
|
|
28
|
+
mergeSlice(slice: LogSlice) {
|
|
29
|
+
let min = Number.POSITIVE_INFINITY;
|
|
30
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
31
|
+
let touched = false;
|
|
32
|
+
|
|
33
|
+
for (const [k, v] of Object.entries(slice)) {
|
|
34
|
+
const line = Number(k);
|
|
35
|
+
if (!Number.isFinite(line) || line < 0) continue;
|
|
36
|
+
min = Math.min(min, line);
|
|
37
|
+
max = Math.max(max, line);
|
|
38
|
+
const prev = this.lines.get(line);
|
|
39
|
+
if (prev !== v) {
|
|
40
|
+
this.lines.set(line, v);
|
|
41
|
+
touched = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { touched, min, max };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
applyEvent(event: LogEvent) {
|
|
49
|
+
this.totalLines = Math.max(this.totalLines, Math.floor(event.sizeLines));
|
|
50
|
+
this.totalBytes = Math.max(this.totalBytes, Math.floor(event.sizeBytes ?? 0));
|
|
51
|
+
this.finished = event.finished;
|
|
52
|
+
if (event.firstAvailableLine !== undefined && event.firstAvailableLine > 0) {
|
|
53
|
+
this.firstAvailableLine = Math.max(this.firstAvailableLine, event.firstAvailableLine);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this.mergeSlice(event.lines);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
prune(visibleStart: number, visibleEnd: number) {
|
|
60
|
+
if (this.lines.size < this.MAX_CACHE_SIZE) return;
|
|
61
|
+
|
|
62
|
+
const center = (visibleStart + visibleEnd) / 2;
|
|
63
|
+
const chunk = this.MAX_CACHE_SIZE / 2;
|
|
64
|
+
const low = center - chunk;
|
|
65
|
+
const high = center + chunk;
|
|
66
|
+
|
|
67
|
+
for (const key of this.lines.keys()) {
|
|
68
|
+
if (key < low || key > high) {
|
|
69
|
+
this.lines.delete(key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
reset() {
|
|
75
|
+
this.lines.clear();
|
|
76
|
+
this.totalLines = 0;
|
|
77
|
+
this.totalBytes = 0;
|
|
78
|
+
this.finished = false;
|
|
79
|
+
this.firstAvailableLine = 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
isRangeComplete(start: number, end: number) {
|
|
83
|
+
const max = Math.max(0, this.totalLines - 1);
|
|
84
|
+
const min = this.firstAvailableLine;
|
|
85
|
+
const s = Math.max(min, Math.min(max, start));
|
|
86
|
+
const e = Math.max(s, Math.min(max, end));
|
|
87
|
+
|
|
88
|
+
for (let line = s; line <= e; line++) {
|
|
89
|
+
if (!this.lines.has(line)) return false;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: PoppyCake, s.r.o.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { createLoggerFactory } from "@runwisp/common";
|
|
5
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
6
|
+
import type { LogCache } from "./LogCache.svelte.js";
|
|
7
|
+
import type { FetchLogsFn } from "./types.js";
|
|
8
|
+
import { isLogEvent } from "./types.js";
|
|
9
|
+
|
|
10
|
+
const createLogger = createLoggerFactory();
|
|
11
|
+
const logger = createLogger("LogFetcher");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* LogFetcher manages on-demand data loading for the LogConsole.
|
|
15
|
+
*
|
|
16
|
+
* It uses a chunk-based state machine to manage requests:
|
|
17
|
+
* - Maps lines to chunk indexes to naturally deduplicate overlapping ranges.
|
|
18
|
+
* - Schedules fetches using pending/in-flight chunk Sets.
|
|
19
|
+
* - Throttles and prioritizes requests to prevent viewport jitter.
|
|
20
|
+
*/
|
|
21
|
+
export class LogFetcher {
|
|
22
|
+
private cache: LogCache;
|
|
23
|
+
private fetchLogsFn: FetchLogsFn | undefined;
|
|
24
|
+
private chunkSize: number;
|
|
25
|
+
|
|
26
|
+
readonly MAX_INFLIGHT = 2;
|
|
27
|
+
readonly MIN_REQUEST_INTERVAL_MS = 140;
|
|
28
|
+
|
|
29
|
+
isFetching = $state(false);
|
|
30
|
+
|
|
31
|
+
private pendingChunks = new SvelteSet<number>();
|
|
32
|
+
private inFlightChunks = new SvelteSet<number>();
|
|
33
|
+
private fetchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
34
|
+
private lastRequestAt = 0;
|
|
35
|
+
|
|
36
|
+
private onDataLoaded?: ((min: number, max: number) => void) | undefined;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
cache: LogCache,
|
|
40
|
+
fetchLogsFn: FetchLogsFn | undefined,
|
|
41
|
+
chunkSize: number = 4096,
|
|
42
|
+
onDataLoaded?: (min: number, max: number) => void,
|
|
43
|
+
) {
|
|
44
|
+
this.cache = cache;
|
|
45
|
+
this.fetchLogsFn = fetchLogsFn;
|
|
46
|
+
this.chunkSize = Math.max(1, Math.floor(chunkSize));
|
|
47
|
+
this.onDataLoaded = onDataLoaded;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setFetchLogsFn(fn: FetchLogsFn | undefined) {
|
|
51
|
+
this.fetchLogsFn = fn;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setChunkSize(size: number) {
|
|
55
|
+
this.chunkSize = Math.max(1, Math.floor(size));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
enqueue(from: number, to: number) {
|
|
59
|
+
if (!this.fetchLogsFn) return;
|
|
60
|
+
|
|
61
|
+
const limit = this.cache.totalLines > 0 ? this.cache.totalLines : this.chunkSize;
|
|
62
|
+
const minLine = this.cache.firstAvailableLine;
|
|
63
|
+
const maxLine = limit - 1;
|
|
64
|
+
|
|
65
|
+
const clampedFrom = Math.max(from, minLine);
|
|
66
|
+
const clampedTo = Math.min(to, Math.max(clampedFrom, maxLine));
|
|
67
|
+
|
|
68
|
+
const startChunk = Math.floor(clampedFrom / this.chunkSize);
|
|
69
|
+
const endChunk = Math.floor(clampedTo / this.chunkSize);
|
|
70
|
+
|
|
71
|
+
let queuedAny = false;
|
|
72
|
+
for (let i = startChunk; i <= endChunk; i++) {
|
|
73
|
+
if (this.inFlightChunks.has(i) || this.pendingChunks.has(i)) continue;
|
|
74
|
+
|
|
75
|
+
const chunkFrom = i * this.chunkSize;
|
|
76
|
+
const chunkTo = Math.min((i + 1) * this.chunkSize - 1, maxLine);
|
|
77
|
+
if (this.cache.isRangeComplete(chunkFrom, chunkTo)) continue;
|
|
78
|
+
|
|
79
|
+
this.pendingChunks.add(i);
|
|
80
|
+
queuedAny = true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (queuedAny) {
|
|
84
|
+
this.scheduleFetch();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
maybeRequestMissing(visibleStart: number, visibleEnd: number, maxChunks = 12) {
|
|
89
|
+
if (!this.fetchLogsFn || this.cache.totalLines <= 0 || visibleEnd < visibleStart) return;
|
|
90
|
+
|
|
91
|
+
const startChunk = Math.floor(visibleStart / this.chunkSize);
|
|
92
|
+
const endChunk = Math.floor(visibleEnd / this.chunkSize);
|
|
93
|
+
|
|
94
|
+
let scanned = 0;
|
|
95
|
+
for (let i = startChunk; i <= endChunk; i++) {
|
|
96
|
+
if (scanned++ >= maxChunks) break;
|
|
97
|
+
|
|
98
|
+
const chunkFrom = i * this.chunkSize;
|
|
99
|
+
const chunkTo = Math.min((i + 1) * this.chunkSize - 1, this.cache.totalLines - 1);
|
|
100
|
+
if (this.cache.isRangeComplete(chunkFrom, chunkTo)) continue;
|
|
101
|
+
|
|
102
|
+
this.enqueue(chunkFrom, chunkTo);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private scheduleFetch() {
|
|
107
|
+
if (!this.fetchLogsFn || this.fetchTimer || this.pendingChunks.size === 0) return;
|
|
108
|
+
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
const wait = Math.max(0, this.MIN_REQUEST_INTERVAL_MS - (now - this.lastRequestAt));
|
|
111
|
+
this.fetchTimer = setTimeout(() => {
|
|
112
|
+
this.fetchTimer = null;
|
|
113
|
+
void this.runFetchPump();
|
|
114
|
+
}, wait);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
pruneQueue(renderedStart: number, renderedEnd: number) {
|
|
118
|
+
const centerLine = (renderedStart + renderedEnd) / 2;
|
|
119
|
+
const centerChunk = Math.floor(centerLine / this.chunkSize);
|
|
120
|
+
const radius = 2;
|
|
121
|
+
|
|
122
|
+
for (const chunk of this.pendingChunks) {
|
|
123
|
+
if (Math.abs(chunk - centerChunk) > radius) {
|
|
124
|
+
this.pendingChunks.delete(chunk);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private async runFetchPump() {
|
|
130
|
+
if (!this.fetchLogsFn || this.pendingChunks.size === 0) return;
|
|
131
|
+
|
|
132
|
+
if (this.inFlightChunks.size >= this.MAX_INFLIGHT) {
|
|
133
|
+
this.scheduleFetch();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Just pick the first pending chunk (pruneQueue guarantees relevance)
|
|
138
|
+
const nextChunk = this.pendingChunks.values().next().value;
|
|
139
|
+
if (nextChunk === undefined) return;
|
|
140
|
+
|
|
141
|
+
this.pendingChunks.delete(nextChunk);
|
|
142
|
+
this.inFlightChunks.add(nextChunk);
|
|
143
|
+
|
|
144
|
+
this.lastRequestAt = Date.now();
|
|
145
|
+
this.isFetching = true;
|
|
146
|
+
|
|
147
|
+
const limit = this.cache.totalLines > 0 ? this.cache.totalLines : this.chunkSize;
|
|
148
|
+
const maxLine = limit - 1;
|
|
149
|
+
const chunkFrom = nextChunk * this.chunkSize;
|
|
150
|
+
const chunkTo = Math.min((nextChunk + 1) * this.chunkSize - 1, maxLine);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const res = await this.fetchLogsFn(chunkFrom, chunkTo);
|
|
154
|
+
if (res) {
|
|
155
|
+
const merged = isLogEvent(res)
|
|
156
|
+
? this.cache.applyEvent(res)
|
|
157
|
+
: this.cache.mergeSlice(res);
|
|
158
|
+
|
|
159
|
+
if (merged.touched && this.onDataLoaded) {
|
|
160
|
+
this.onDataLoaded(merged.min, merged.max);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
logger.error(`Failed to fetch chunk ${String(nextChunk)}`, e);
|
|
165
|
+
} finally {
|
|
166
|
+
this.inFlightChunks.delete(nextChunk);
|
|
167
|
+
this.isFetching = this.inFlightChunks.size > 0;
|
|
168
|
+
this.scheduleFetch();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
destroy() {
|
|
173
|
+
if (this.fetchTimer) clearTimeout(this.fetchTimer);
|
|
174
|
+
this.fetchTimer = null;
|
|
175
|
+
this.pendingChunks.clear();
|
|
176
|
+
this.inFlightChunks.clear();
|
|
177
|
+
this.isFetching = false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: PoppyCake, s.r.o.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
export type LineNumber = number;
|
|
5
|
+
export type LogMessage = string;
|
|
6
|
+
|
|
7
|
+
export type LogSlice = Record<LineNumber, LogMessage>;
|
|
8
|
+
|
|
9
|
+
export type LogEvent = {
|
|
10
|
+
lines: LogSlice;
|
|
11
|
+
sizeLines: number;
|
|
12
|
+
sizeBytes?: number;
|
|
13
|
+
finished: boolean;
|
|
14
|
+
firstAvailableLine?: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function isLogEvent(value: unknown): value is LogEvent {
|
|
18
|
+
return value !== null && typeof value === "object" && "sizeLines" in value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type FetchLogsFn = (
|
|
22
|
+
from: number,
|
|
23
|
+
to: number,
|
|
24
|
+
) => Promise<LogSlice | LogEvent | undefined> | LogSlice | LogEvent | undefined;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/* SPDX-FileCopyrightText: PoppyCake, s.r.o. */
|
|
2
|
+
/* SPDX-License-Identifier: Apache-2.0 */
|
|
3
|
+
|
|
4
|
+
/* RunWisp Design Tokens — plain CSS, no Tailwind directives.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for the OKLch palette and the semantic `--rw-*`
|
|
7
|
+
* tokens that drive all apps:
|
|
8
|
+
* - apps/ui (SvelteKit dashboard): imported transitively via theme.css.
|
|
9
|
+
* - apps/docs (Astro + Starlight): imported directly through Starlight's
|
|
10
|
+
* customCss (no Tailwind pipeline).
|
|
11
|
+
*
|
|
12
|
+
* The raw palette is exposed under the --rw-color-* namespace; theme.css
|
|
13
|
+
* republishes those names under the --color-* namespace via Tailwind's
|
|
14
|
+
* @theme so utilities like `bg-wisp-500` work in the Svelte/Astro apps.
|
|
15
|
+
*
|
|
16
|
+
* Dark-mode contract:
|
|
17
|
+
* - apps/ui toggles the `.dark` class on <html>.
|
|
18
|
+
* - Starlight (apps/docs) writes data-theme="dark" on the documentElement.
|
|
19
|
+
* The union selector covers both. Light mode is the default.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
:root {
|
|
23
|
+
/* Primary — Wisp Violet */
|
|
24
|
+
--rw-color-wisp-50: oklch(0.97 0.02 280);
|
|
25
|
+
--rw-color-wisp-100: oklch(0.94 0.04 280);
|
|
26
|
+
--rw-color-wisp-200: oklch(0.88 0.08 280);
|
|
27
|
+
--rw-color-wisp-300: oklch(0.78 0.12 280);
|
|
28
|
+
--rw-color-wisp-400: oklch(0.68 0.16 275);
|
|
29
|
+
--rw-color-wisp-500: oklch(0.58 0.18 270);
|
|
30
|
+
--rw-color-wisp-600: oklch(0.5 0.18 268);
|
|
31
|
+
--rw-color-wisp-700: oklch(0.42 0.16 268);
|
|
32
|
+
--rw-color-wisp-800: oklch(0.35 0.12 268);
|
|
33
|
+
--rw-color-wisp-900: oklch(0.28 0.08 268);
|
|
34
|
+
--rw-color-wisp-950: oklch(0.18 0.06 268);
|
|
35
|
+
|
|
36
|
+
/* Accent — Aurora Teal */
|
|
37
|
+
--rw-color-aurora-50: oklch(0.97 0.02 180);
|
|
38
|
+
--rw-color-aurora-100: oklch(0.94 0.05 180);
|
|
39
|
+
--rw-color-aurora-200: oklch(0.88 0.1 178);
|
|
40
|
+
--rw-color-aurora-300: oklch(0.78 0.14 176);
|
|
41
|
+
--rw-color-aurora-400: oklch(0.68 0.16 174);
|
|
42
|
+
--rw-color-aurora-500: oklch(0.58 0.14 172);
|
|
43
|
+
--rw-color-aurora-600: oklch(0.5 0.12 172);
|
|
44
|
+
--rw-color-aurora-700: oklch(0.42 0.1 172);
|
|
45
|
+
--rw-color-aurora-800: oklch(0.35 0.08 172);
|
|
46
|
+
--rw-color-aurora-900: oklch(0.28 0.06 172);
|
|
47
|
+
|
|
48
|
+
/* Mist — neutral grays with a purple tint */
|
|
49
|
+
--rw-color-mist-50: oklch(0.98 0.005 280);
|
|
50
|
+
--rw-color-mist-100: oklch(0.96 0.008 280);
|
|
51
|
+
--rw-color-mist-200: oklch(0.92 0.01 280);
|
|
52
|
+
--rw-color-mist-300: oklch(0.86 0.012 280);
|
|
53
|
+
--rw-color-mist-400: oklch(0.7 0.012 280);
|
|
54
|
+
--rw-color-mist-500: oklch(0.55 0.012 280);
|
|
55
|
+
--rw-color-mist-600: oklch(0.45 0.012 280);
|
|
56
|
+
--rw-color-mist-700: oklch(0.35 0.012 280);
|
|
57
|
+
--rw-color-mist-800: oklch(0.25 0.012 280);
|
|
58
|
+
--rw-color-mist-900: oklch(0.18 0.012 280);
|
|
59
|
+
--rw-color-mist-950: oklch(0.12 0.012 280);
|
|
60
|
+
|
|
61
|
+
/* Success — Aurora Mint */
|
|
62
|
+
--rw-color-success-50: oklch(0.97 0.018 165);
|
|
63
|
+
--rw-color-success-100: oklch(0.94 0.035 165);
|
|
64
|
+
--rw-color-success-200: oklch(0.88 0.06 165);
|
|
65
|
+
--rw-color-success-300: oklch(0.78 0.09 165);
|
|
66
|
+
--rw-color-success-400: oklch(0.68 0.11 165);
|
|
67
|
+
--rw-color-success-500: oklch(0.58 0.115 165);
|
|
68
|
+
--rw-color-success-600: oklch(0.5 0.105 165);
|
|
69
|
+
--rw-color-success-700: oklch(0.42 0.095 165);
|
|
70
|
+
|
|
71
|
+
/* Danger — Rose Crimson */
|
|
72
|
+
--rw-color-danger-50: oklch(0.97 0.014 355);
|
|
73
|
+
--rw-color-danger-100: oklch(0.94 0.028 355);
|
|
74
|
+
--rw-color-danger-200: oklch(0.88 0.052 355);
|
|
75
|
+
--rw-color-danger-300: oklch(0.78 0.085 355);
|
|
76
|
+
--rw-color-danger-400: oklch(0.68 0.115 355);
|
|
77
|
+
--rw-color-danger-500: oklch(0.58 0.145 355);
|
|
78
|
+
--rw-color-danger-600: oklch(0.5 0.135 355);
|
|
79
|
+
--rw-color-danger-700: oklch(0.42 0.12 355);
|
|
80
|
+
|
|
81
|
+
/* Warning */
|
|
82
|
+
--rw-color-warning-50: oklch(0.98 0.03 85);
|
|
83
|
+
--rw-color-warning-100: oklch(0.95 0.08 85);
|
|
84
|
+
--rw-color-warning-200: oklch(0.9 0.12 80);
|
|
85
|
+
--rw-color-warning-300: oklch(0.85 0.16 75);
|
|
86
|
+
--rw-color-warning-400: oklch(0.8 0.18 70);
|
|
87
|
+
--rw-color-warning-500: oklch(0.75 0.16 65);
|
|
88
|
+
--rw-color-warning-600: oklch(0.65 0.14 60);
|
|
89
|
+
--rw-color-warning-700: oklch(0.55 0.12 55);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Light-mode semantic tokens (default) */
|
|
93
|
+
:root {
|
|
94
|
+
/* Surfaces */
|
|
95
|
+
--rw-surface: var(--rw-color-mist-50);
|
|
96
|
+
--rw-surface-raised: white;
|
|
97
|
+
--rw-surface-overlay: white;
|
|
98
|
+
--rw-surface-sunken: var(--rw-color-mist-100);
|
|
99
|
+
/* On-surface text */
|
|
100
|
+
--rw-on-surface: var(--rw-color-mist-900);
|
|
101
|
+
--rw-on-surface-muted: var(--rw-color-mist-600);
|
|
102
|
+
--rw-on-surface-faint: var(--rw-color-mist-400);
|
|
103
|
+
/* Borders / outlines */
|
|
104
|
+
--rw-outline: var(--rw-color-mist-200);
|
|
105
|
+
--rw-outline-hover: var(--rw-color-mist-300);
|
|
106
|
+
--rw-outline-faint: var(--rw-color-mist-100);
|
|
107
|
+
--rw-ring: var(--rw-color-wisp-500);
|
|
108
|
+
--rw-backdrop: oklch(0.12 0.012 280 / 0.5);
|
|
109
|
+
/* Primary */
|
|
110
|
+
--rw-primary: var(--rw-color-wisp-600);
|
|
111
|
+
--rw-primary-hover: var(--rw-color-wisp-700);
|
|
112
|
+
--rw-primary-active: var(--rw-color-wisp-800);
|
|
113
|
+
--rw-on-primary: white;
|
|
114
|
+
--rw-primary-soft: var(--rw-color-wisp-100);
|
|
115
|
+
--rw-primary-soft-text: var(--rw-color-wisp-700);
|
|
116
|
+
/* Danger */
|
|
117
|
+
--rw-danger: var(--rw-color-danger-600);
|
|
118
|
+
--rw-danger-hover: var(--rw-color-danger-700);
|
|
119
|
+
--rw-danger-active: var(--rw-color-danger-800);
|
|
120
|
+
--rw-on-danger: white;
|
|
121
|
+
--rw-danger-soft: var(--rw-color-danger-50);
|
|
122
|
+
--rw-danger-soft-text: var(--rw-color-danger-700);
|
|
123
|
+
/* Success */
|
|
124
|
+
--rw-success: var(--rw-color-success-600);
|
|
125
|
+
--rw-success-hover: var(--rw-color-success-700);
|
|
126
|
+
--rw-on-success: white;
|
|
127
|
+
--rw-success-soft: var(--rw-color-success-50);
|
|
128
|
+
--rw-success-soft-text: var(--rw-color-success-700);
|
|
129
|
+
/* Warning */
|
|
130
|
+
--rw-warning: var(--rw-color-warning-500);
|
|
131
|
+
--rw-warning-hover: var(--rw-color-warning-600);
|
|
132
|
+
--rw-on-warning: white;
|
|
133
|
+
--rw-warning-soft: var(--rw-color-warning-50);
|
|
134
|
+
--rw-warning-soft-text: var(--rw-color-warning-700);
|
|
135
|
+
/* Info */
|
|
136
|
+
--rw-info: var(--rw-color-aurora-600);
|
|
137
|
+
--rw-info-hover: var(--rw-color-aurora-700);
|
|
138
|
+
--rw-on-info: white;
|
|
139
|
+
--rw-info-soft: var(--rw-color-aurora-50);
|
|
140
|
+
--rw-info-soft-text: var(--rw-color-aurora-700);
|
|
141
|
+
/* Glass */
|
|
142
|
+
--rw-glass: oklch(1 0 0 / 0.8);
|
|
143
|
+
--rw-glass-blur: blur(12px);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Dark-mode semantic tokens — `.dark` class for Svelte apps, `[data-theme="dark"]`
|
|
147
|
+
* attribute for Starlight. Both selectors apply the same token set. */
|
|
148
|
+
:root.dark,
|
|
149
|
+
:root[data-theme="dark"] {
|
|
150
|
+
--rw-surface: var(--rw-color-mist-950);
|
|
151
|
+
--rw-surface-raised: var(--rw-color-mist-900);
|
|
152
|
+
--rw-surface-overlay: var(--rw-color-mist-900);
|
|
153
|
+
--rw-surface-sunken: oklch(0.08 0.012 280);
|
|
154
|
+
--rw-on-surface: var(--rw-color-mist-100);
|
|
155
|
+
--rw-on-surface-muted: var(--rw-color-mist-400);
|
|
156
|
+
--rw-on-surface-faint: var(--rw-color-mist-600);
|
|
157
|
+
--rw-outline: var(--rw-color-mist-800);
|
|
158
|
+
--rw-outline-hover: var(--rw-color-mist-700);
|
|
159
|
+
--rw-outline-faint: var(--rw-color-mist-800);
|
|
160
|
+
--rw-ring: var(--rw-color-wisp-400);
|
|
161
|
+
--rw-backdrop: oklch(0 0 0 / 0.7);
|
|
162
|
+
/* Primary */
|
|
163
|
+
--rw-primary: var(--rw-color-wisp-500);
|
|
164
|
+
--rw-primary-hover: var(--rw-color-wisp-400);
|
|
165
|
+
--rw-primary-active: var(--rw-color-wisp-300);
|
|
166
|
+
--rw-on-primary: white;
|
|
167
|
+
--rw-primary-soft: oklch(0.22 0.06 270);
|
|
168
|
+
--rw-primary-soft-text: var(--rw-color-wisp-300);
|
|
169
|
+
/* Danger */
|
|
170
|
+
--rw-danger: var(--rw-color-danger-500);
|
|
171
|
+
--rw-danger-hover: var(--rw-color-danger-400);
|
|
172
|
+
--rw-danger-active: var(--rw-color-danger-300);
|
|
173
|
+
--rw-on-danger: white;
|
|
174
|
+
--rw-danger-soft: oklch(0.2 0.04 355);
|
|
175
|
+
--rw-danger-soft-text: var(--rw-color-danger-300);
|
|
176
|
+
/* Success */
|
|
177
|
+
--rw-success: var(--rw-color-success-500);
|
|
178
|
+
--rw-success-hover: var(--rw-color-success-400);
|
|
179
|
+
--rw-on-success: white;
|
|
180
|
+
--rw-success-soft: oklch(0.2 0.04 165);
|
|
181
|
+
--rw-success-soft-text: var(--rw-color-success-300);
|
|
182
|
+
/* Warning */
|
|
183
|
+
--rw-warning: var(--rw-color-warning-400);
|
|
184
|
+
--rw-warning-hover: var(--rw-color-warning-300);
|
|
185
|
+
--rw-on-warning: var(--rw-color-mist-950);
|
|
186
|
+
--rw-warning-soft: oklch(0.2 0.04 85);
|
|
187
|
+
--rw-warning-soft-text: var(--rw-color-warning-300);
|
|
188
|
+
/* Info */
|
|
189
|
+
--rw-info: var(--rw-color-aurora-500);
|
|
190
|
+
--rw-info-hover: var(--rw-color-aurora-400);
|
|
191
|
+
--rw-on-info: white;
|
|
192
|
+
--rw-info-soft: oklch(0.2 0.04 180);
|
|
193
|
+
--rw-info-soft-text: var(--rw-color-aurora-300);
|
|
194
|
+
/* Glass */
|
|
195
|
+
--rw-glass: oklch(0.12 0.012 280 / 0.9);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Platform polish — focus ring, text selection, scrollbar. Lifted from
|
|
199
|
+
* theme.css so apps/docs (no Tailwind) gets the same treatment as the
|
|
200
|
+
* Svelte apps. */
|
|
201
|
+
|
|
202
|
+
:focus-visible {
|
|
203
|
+
outline: 2px solid var(--rw-ring);
|
|
204
|
+
outline-offset: 2px;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
::selection {
|
|
208
|
+
background-color: var(--rw-color-wisp-200);
|
|
209
|
+
color: var(--rw-color-wisp-900);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
:root.dark ::selection,
|
|
213
|
+
:root[data-theme="dark"] ::selection {
|
|
214
|
+
background-color: var(--rw-color-wisp-800);
|
|
215
|
+
color: var(--rw-color-wisp-100);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
::-webkit-scrollbar {
|
|
219
|
+
width: 8px;
|
|
220
|
+
height: 8px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
::-webkit-scrollbar-track {
|
|
224
|
+
background: var(--rw-surface-sunken);
|
|
225
|
+
border-radius: 9999px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
::-webkit-scrollbar-thumb {
|
|
229
|
+
background: var(--rw-outline-hover);
|
|
230
|
+
border-radius: 9999px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
::-webkit-scrollbar-thumb:hover {
|
|
234
|
+
background: var(--rw-on-surface-faint);
|
|
235
|
+
}
|