@robr0/design-system 0.6.0 → 0.8.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 +53 -2
- package/README.md +36 -5
- package/components/AgentPlan/AgentPlan.css +231 -0
- package/components/AgentPlan/AgentPlan.d.ts +41 -0
- package/components/AgentPlan/AgentPlan.js +99 -0
- package/components/AgentStatus/AgentStatus.css +26 -11
- package/components/Card/Card.css +9 -0
- package/components/Card/Card.d.ts +6 -0
- package/components/Card/Card.js +2 -1
- package/components/Composer/Composer.css +18 -0
- package/components/Composer/Composer.d.ts +3 -0
- package/components/Composer/Composer.js +12 -1
- package/components/DataTable/DataTable.css +105 -0
- package/components/DataTable/DataTable.d.ts +81 -0
- package/components/DataTable/DataTable.js +235 -0
- package/components/EventCalendar/EventCalendar.css +328 -0
- package/components/EventCalendar/EventCalendar.d.ts +51 -0
- package/components/EventCalendar/EventCalendar.js +213 -0
- package/components/ModelPicker/ModelPicker.css +238 -0
- package/components/ModelPicker/ModelPicker.d.ts +60 -0
- package/components/ModelPicker/ModelPicker.js +217 -0
- package/components/NotificationCenter/NotificationCenter.css +262 -0
- package/components/NotificationCenter/NotificationCenter.d.ts +72 -0
- package/components/NotificationCenter/NotificationCenter.js +128 -0
- package/components/Prose/Prose.css +18 -1
- package/components/Reasoning/Reasoning.css +19 -4
- package/components/ShaderField/ShaderField.css +24 -0
- package/components/ShaderField/ShaderField.d.ts +68 -0
- package/components/ShaderField/ShaderField.js +54 -0
- package/components/ShaderField/field.glsl.d.ts +39 -0
- package/components/ShaderField/field.glsl.js +178 -0
- package/components/ShaderField/useShaderField.d.ts +103 -0
- package/components/ShaderField/useShaderField.js +409 -0
- package/components/Timeline/Timeline.css +17 -0
- package/components/Timeline/Timeline.d.ts +2 -0
- package/components/Timeline/Timeline.js +1 -0
- package/components/registry.json +48 -0
- package/components/registry.json.d.ts +48 -0
- package/components/registry.json.js +1 -1
- package/index.d.ts +6 -0
- package/index.js +19 -0
- package/package.json +5 -1
- package/tokens/registry.json +1 -0
- package/tokens/registry.json.d.ts +1 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +1 -1
- package/tokens/tokens-light.css +2 -2
- package/tokens/tokens-motion.css +2 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from "react";
|
|
2
|
+
import { BLOB_COUNT, vertexSource, fragmentSource } from "./field.glsl.js";
|
|
3
|
+
const DEFAULT_SHADER_PARAMS = {
|
|
4
|
+
intensity: 0.5,
|
|
5
|
+
warp: 0.14,
|
|
6
|
+
scale: 2.7,
|
|
7
|
+
speed: 2,
|
|
8
|
+
grain: 0.1,
|
|
9
|
+
streak: 0.4,
|
|
10
|
+
react: 0,
|
|
11
|
+
crop: 0.5
|
|
12
|
+
};
|
|
13
|
+
const DEFAULT_SHADER_BLOBS = [
|
|
14
|
+
{ token: "--color-core-accent-gold", size: 0.45, cx: 0.175, cy: 0.125, period: 18, phase: 0, weight: 1 },
|
|
15
|
+
{ token: "--color-core-accent-mint", size: 0.55, cx: 0.125, cy: 0.125, period: 16, phase: 1, weight: 1 },
|
|
16
|
+
{ token: "--color-core-accent-violet", size: 0.9, cx: 0.35, cy: 0.25, period: 22, phase: 0.5, weight: 1 },
|
|
17
|
+
{ token: "--color-bg-container-secondary", size: 0.55, cx: 0.025, cy: 0.075, period: 14, phase: 1.5, weight: 1 },
|
|
18
|
+
{ token: "--color-core-accent-cobalt", size: 0.55, cx: -0.125, cy: 0.475, period: 17, phase: 0.8, weight: 1 },
|
|
19
|
+
{ token: "--color-core-accent-coral", size: 0.48, cx: 0.39, cy: -0.26, period: 19, phase: 0.3, weight: 1 },
|
|
20
|
+
{ token: "--color-core-accent-amber", size: 0.3, cx: 0.35, cy: 0.3, period: 15, phase: 1.2, weight: 1 },
|
|
21
|
+
{ token: "--color-core-ui-secondary", size: 0.75, cx: -0.125, cy: -0.025, period: 20, phase: 0.7, weight: 1 }
|
|
22
|
+
];
|
|
23
|
+
const RENDER_SCALE = 0.5;
|
|
24
|
+
const REFERENCE_WIDTH = 1440;
|
|
25
|
+
function fieldUnit(cssWidth, crop) {
|
|
26
|
+
if (cssWidth <= 0) return 1;
|
|
27
|
+
return cssWidth * Math.pow(Math.max(REFERENCE_WIDTH / cssWidth, 1), crop);
|
|
28
|
+
}
|
|
29
|
+
const FRAME_MS = 1e3 / 30;
|
|
30
|
+
const FRAME_MS_ACTIVE = 1e3 / 60;
|
|
31
|
+
const WAKE_IDLE = 0.015;
|
|
32
|
+
const COLOR_FADE_MS = 300;
|
|
33
|
+
function parseColor(value) {
|
|
34
|
+
const v = value.trim();
|
|
35
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(v);
|
|
36
|
+
if (hex) {
|
|
37
|
+
const h = hex[1];
|
|
38
|
+
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
39
|
+
return [
|
|
40
|
+
parseInt(full.slice(0, 2), 16) / 255,
|
|
41
|
+
parseInt(full.slice(2, 4), 16) / 255,
|
|
42
|
+
parseInt(full.slice(4, 6), 16) / 255
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
const rgb = /^rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/.exec(v);
|
|
46
|
+
if (rgb) {
|
|
47
|
+
return [Number(rgb[1]) / 255, Number(rgb[2]) / 255, Number(rgb[3]) / 255];
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function srgbToLinear(c) {
|
|
52
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
53
|
+
}
|
|
54
|
+
function compile(gl, type, source) {
|
|
55
|
+
const shader = gl.createShader(type);
|
|
56
|
+
if (!shader) return null;
|
|
57
|
+
gl.shaderSource(shader, source);
|
|
58
|
+
gl.compileShader(shader);
|
|
59
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
60
|
+
console.error("ShaderField compile error:", gl.getShaderInfoLog(shader));
|
|
61
|
+
gl.deleteShader(shader);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return shader;
|
|
65
|
+
}
|
|
66
|
+
function useShaderField(canvasRef, params, enabled = true) {
|
|
67
|
+
const [supported, setSupported] = useState(true);
|
|
68
|
+
const [active, setActive] = useState(false);
|
|
69
|
+
const paramsRef = useRef(params);
|
|
70
|
+
const redrawRef = useRef(() => {
|
|
71
|
+
});
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
paramsRef.current = params;
|
|
74
|
+
redrawRef.current();
|
|
75
|
+
}, [params]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const canvas = canvasRef.current;
|
|
78
|
+
if (!canvas || !enabled) return;
|
|
79
|
+
const gl = canvas.getContext("webgl2", {
|
|
80
|
+
alpha: true,
|
|
81
|
+
antialias: false,
|
|
82
|
+
depth: false,
|
|
83
|
+
stencil: false,
|
|
84
|
+
premultipliedAlpha: true,
|
|
85
|
+
powerPreference: "low-power"
|
|
86
|
+
});
|
|
87
|
+
if (!gl) {
|
|
88
|
+
setSupported(false);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!gl.isContextLost()) {
|
|
92
|
+
canvas.__dsLoseExt = gl.getExtension("WEBGL_lose_context");
|
|
93
|
+
}
|
|
94
|
+
const loseExt = () => gl.getExtension("WEBGL_lose_context") ?? canvas.__dsLoseExt ?? null;
|
|
95
|
+
let program = null;
|
|
96
|
+
let vao = null;
|
|
97
|
+
let loc = {};
|
|
98
|
+
function initGL() {
|
|
99
|
+
if (!gl) return false;
|
|
100
|
+
const vs = compile(gl, gl.VERTEX_SHADER, vertexSource);
|
|
101
|
+
const fs = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
102
|
+
if (!vs || !fs) return false;
|
|
103
|
+
program = gl.createProgram();
|
|
104
|
+
if (!program) return false;
|
|
105
|
+
gl.attachShader(program, vs);
|
|
106
|
+
gl.attachShader(program, fs);
|
|
107
|
+
gl.linkProgram(program);
|
|
108
|
+
gl.deleteShader(vs);
|
|
109
|
+
gl.deleteShader(fs);
|
|
110
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
111
|
+
console.error("ShaderField link error:", gl.getProgramInfoLog(program));
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
gl.useProgram(program);
|
|
115
|
+
vao = gl.createVertexArray();
|
|
116
|
+
gl.bindVertexArray(vao);
|
|
117
|
+
gl.disable(gl.BLEND);
|
|
118
|
+
gl.disable(gl.DEPTH_TEST);
|
|
119
|
+
loc = {};
|
|
120
|
+
for (const name of [
|
|
121
|
+
"u_resolution",
|
|
122
|
+
"u_unit",
|
|
123
|
+
"u_time",
|
|
124
|
+
"u_color[0]",
|
|
125
|
+
"u_blob[0]",
|
|
126
|
+
"u_motion[0]",
|
|
127
|
+
"u_intensity",
|
|
128
|
+
"u_warp",
|
|
129
|
+
"u_scale",
|
|
130
|
+
"u_grain",
|
|
131
|
+
"u_streak",
|
|
132
|
+
"u_mouse",
|
|
133
|
+
"u_mvel",
|
|
134
|
+
"u_react"
|
|
135
|
+
]) {
|
|
136
|
+
loc[name] = gl.getUniformLocation(program, name);
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
let uploadedBlobs = null;
|
|
141
|
+
function uploadBlobs(blobs) {
|
|
142
|
+
if (!gl) return;
|
|
143
|
+
const blob = new Float32Array(BLOB_COUNT * 4);
|
|
144
|
+
const motion = new Float32Array(BLOB_COUNT * 4);
|
|
145
|
+
for (let i = 0; i < BLOB_COUNT; i++) {
|
|
146
|
+
const b = blobs[i];
|
|
147
|
+
if (!b) {
|
|
148
|
+
blob[i * 4 + 0] = 99;
|
|
149
|
+
blob[i * 4 + 1] = 99;
|
|
150
|
+
blob[i * 4 + 2] = 0.01;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
blob[i * 4 + 0] = b.cx;
|
|
154
|
+
blob[i * 4 + 1] = b.cy;
|
|
155
|
+
blob[i * 4 + 2] = b.size * 0.42;
|
|
156
|
+
blob[i * 4 + 3] = b.weight;
|
|
157
|
+
const wx = 2 * Math.PI / b.period;
|
|
158
|
+
motion[i * 4 + 0] = wx;
|
|
159
|
+
motion[i * 4 + 1] = wx * 0.83;
|
|
160
|
+
motion[i * 4 + 2] = b.phase * 2.1;
|
|
161
|
+
motion[i * 4 + 3] = b.phase * 3.7;
|
|
162
|
+
}
|
|
163
|
+
gl.uniform4fv(loc["u_blob[0]"], blob);
|
|
164
|
+
gl.uniform4fv(loc["u_motion[0]"], motion);
|
|
165
|
+
uploadedBlobs = blobs;
|
|
166
|
+
}
|
|
167
|
+
const colorCurrent = new Float32Array(BLOB_COUNT * 3);
|
|
168
|
+
const colorTarget = new Float32Array(BLOB_COUNT * 3);
|
|
169
|
+
let colorFadeT = COLOR_FADE_MS;
|
|
170
|
+
function readColors(into) {
|
|
171
|
+
const style = getComputedStyle(canvas);
|
|
172
|
+
into.fill(0);
|
|
173
|
+
paramsRef.current.blobs.slice(0, BLOB_COUNT).forEach((b, i) => {
|
|
174
|
+
const parsed = parseColor(style.getPropertyValue(b.token));
|
|
175
|
+
if (parsed) {
|
|
176
|
+
into[i * 3 + 0] = srgbToLinear(parsed[0]);
|
|
177
|
+
into[i * 3 + 1] = srgbToLinear(parsed[1]);
|
|
178
|
+
into[i * 3 + 2] = srgbToLinear(parsed[2]);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
readColors(colorCurrent);
|
|
183
|
+
colorTarget.set(colorCurrent);
|
|
184
|
+
let bufWidth = 0;
|
|
185
|
+
let bufHeight = 0;
|
|
186
|
+
let cssWidth = 0;
|
|
187
|
+
function resize() {
|
|
188
|
+
if (!gl) return;
|
|
189
|
+
const c = canvas;
|
|
190
|
+
const dpr = window.devicePixelRatio || 1;
|
|
191
|
+
cssWidth = c.clientWidth;
|
|
192
|
+
const w = Math.max(1, Math.round(c.clientWidth * dpr * RENDER_SCALE));
|
|
193
|
+
const h = Math.max(1, Math.round(c.clientHeight * dpr * RENDER_SCALE));
|
|
194
|
+
if (w === bufWidth && h === bufHeight) return;
|
|
195
|
+
bufWidth = w;
|
|
196
|
+
bufHeight = h;
|
|
197
|
+
c.width = w;
|
|
198
|
+
c.height = h;
|
|
199
|
+
gl.viewport(0, 0, w, h);
|
|
200
|
+
gl.uniform2f(loc["u_resolution"], w, h);
|
|
201
|
+
}
|
|
202
|
+
let fieldTime = 0;
|
|
203
|
+
let colorsDirty = false;
|
|
204
|
+
const mouseTarget = { x: 9, y: 9 };
|
|
205
|
+
const mouse = { x: 9, y: 9 };
|
|
206
|
+
let mouseVel = 0;
|
|
207
|
+
let moveAccum = 0;
|
|
208
|
+
const onPointerMove = (e) => {
|
|
209
|
+
const c = canvas;
|
|
210
|
+
const rect = c.getBoundingClientRect();
|
|
211
|
+
if (rect.width === 0) return;
|
|
212
|
+
const unit = fieldUnit(rect.width, paramsRef.current.crop);
|
|
213
|
+
const x = (e.clientX - rect.left - rect.width / 2) / unit;
|
|
214
|
+
const y = (e.clientY - rect.top - rect.height / 2) / unit;
|
|
215
|
+
if (mouseTarget.x < 5) {
|
|
216
|
+
moveAccum += Math.hypot(x - mouseTarget.x, y - mouseTarget.y);
|
|
217
|
+
}
|
|
218
|
+
mouseTarget.x = x;
|
|
219
|
+
mouseTarget.y = y;
|
|
220
|
+
if (mouse.x > 5) {
|
|
221
|
+
mouse.x = x;
|
|
222
|
+
mouse.y = y;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
window.addEventListener("pointermove", onPointerMove, { passive: true });
|
|
226
|
+
function draw(dtMs) {
|
|
227
|
+
if (!gl || !program) return;
|
|
228
|
+
resize();
|
|
229
|
+
if (paramsRef.current.blobs !== uploadedBlobs) {
|
|
230
|
+
uploadBlobs(paramsRef.current.blobs);
|
|
231
|
+
colorsDirty = true;
|
|
232
|
+
}
|
|
233
|
+
if (colorsDirty) {
|
|
234
|
+
readColors(colorTarget);
|
|
235
|
+
colorFadeT = 0;
|
|
236
|
+
colorsDirty = false;
|
|
237
|
+
}
|
|
238
|
+
if (colorFadeT < COLOR_FADE_MS) {
|
|
239
|
+
colorFadeT = Math.min(colorFadeT + dtMs, COLOR_FADE_MS);
|
|
240
|
+
const t = colorFadeT / COLOR_FADE_MS;
|
|
241
|
+
for (let i = 0; i < colorCurrent.length; i++) {
|
|
242
|
+
colorCurrent[i] += (colorTarget[i] - colorCurrent[i]) * t;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const p = paramsRef.current;
|
|
246
|
+
gl.uniform3fv(loc["u_color[0]"], colorCurrent);
|
|
247
|
+
gl.uniform1f(
|
|
248
|
+
loc["u_unit"],
|
|
249
|
+
bufWidth * (fieldUnit(cssWidth, p.crop) / Math.max(cssWidth, 1))
|
|
250
|
+
);
|
|
251
|
+
gl.uniform1f(loc["u_time"], fieldTime);
|
|
252
|
+
gl.uniform1f(loc["u_intensity"], p.intensity);
|
|
253
|
+
gl.uniform1f(loc["u_warp"], p.warp);
|
|
254
|
+
gl.uniform1f(loc["u_scale"], p.scale);
|
|
255
|
+
gl.uniform1f(loc["u_grain"], p.grain);
|
|
256
|
+
gl.uniform1f(loc["u_streak"], p.streak);
|
|
257
|
+
const dtSec = Math.max(dtMs / 1e3, 1e-3);
|
|
258
|
+
const k = 1 - Math.exp(-dtSec * 14);
|
|
259
|
+
mouse.x += (mouseTarget.x - mouse.x) * k;
|
|
260
|
+
mouse.y += (mouseTarget.y - mouse.y) * k;
|
|
261
|
+
const instant = moveAccum / dtSec;
|
|
262
|
+
moveAccum = 0;
|
|
263
|
+
const blend = 1 - Math.exp(-dtSec * (instant > mouseVel ? 14 : 2.2));
|
|
264
|
+
mouseVel += (instant - mouseVel) * blend;
|
|
265
|
+
gl.uniform2f(loc["u_mouse"], mouse.x, mouse.y);
|
|
266
|
+
gl.uniform1f(loc["u_mvel"], mouseVel);
|
|
267
|
+
gl.uniform1f(loc["u_react"], p.react);
|
|
268
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
269
|
+
frameBudget = mouseVel > WAKE_IDLE && p.react > 0 ? FRAME_MS_ACTIVE : FRAME_MS;
|
|
270
|
+
}
|
|
271
|
+
let raf = 0;
|
|
272
|
+
let lastDraw = 0;
|
|
273
|
+
let running = false;
|
|
274
|
+
let disposed = false;
|
|
275
|
+
let frameBudget = FRAME_MS;
|
|
276
|
+
const reduceQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
277
|
+
function frame(now) {
|
|
278
|
+
raf = requestAnimationFrame(frame);
|
|
279
|
+
const elapsed = now - lastDraw;
|
|
280
|
+
if (elapsed < frameBudget - 1) return;
|
|
281
|
+
const dt = Math.min(elapsed, 100);
|
|
282
|
+
lastDraw = now;
|
|
283
|
+
fieldTime += dt / 1e3 * paramsRef.current.speed;
|
|
284
|
+
draw(dt);
|
|
285
|
+
}
|
|
286
|
+
function start() {
|
|
287
|
+
if (running || disposed || reduceQuery.matches || document.hidden) return;
|
|
288
|
+
running = true;
|
|
289
|
+
lastDraw = performance.now();
|
|
290
|
+
raf = requestAnimationFrame(frame);
|
|
291
|
+
}
|
|
292
|
+
function stop() {
|
|
293
|
+
running = false;
|
|
294
|
+
cancelAnimationFrame(raf);
|
|
295
|
+
}
|
|
296
|
+
function renderOnce() {
|
|
297
|
+
if (disposed || running) return;
|
|
298
|
+
draw(COLOR_FADE_MS);
|
|
299
|
+
}
|
|
300
|
+
redrawRef.current = renderOnce;
|
|
301
|
+
const onReduceChange = () => {
|
|
302
|
+
if (reduceQuery.matches) {
|
|
303
|
+
stop();
|
|
304
|
+
renderOnce();
|
|
305
|
+
} else {
|
|
306
|
+
start();
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
reduceQuery.addEventListener("change", onReduceChange);
|
|
310
|
+
const onVisibility = () => {
|
|
311
|
+
if (document.hidden) stop();
|
|
312
|
+
else start();
|
|
313
|
+
};
|
|
314
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
315
|
+
const themeObserver = new MutationObserver(() => {
|
|
316
|
+
colorsDirty = true;
|
|
317
|
+
if (!running) {
|
|
318
|
+
readColors(colorTarget);
|
|
319
|
+
colorCurrent.set(colorTarget);
|
|
320
|
+
colorsDirty = false;
|
|
321
|
+
renderOnce();
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
themeObserver.observe(document.documentElement, {
|
|
325
|
+
attributes: true,
|
|
326
|
+
attributeFilter: ["data-theme", "style"]
|
|
327
|
+
});
|
|
328
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
329
|
+
if (!running) {
|
|
330
|
+
renderOnce();
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
resizeObserver.observe(canvas);
|
|
334
|
+
let restoreTimer = 0;
|
|
335
|
+
const onContextLost = (e) => {
|
|
336
|
+
e.preventDefault();
|
|
337
|
+
stop();
|
|
338
|
+
setActive(false);
|
|
339
|
+
window.clearTimeout(restoreTimer);
|
|
340
|
+
restoreTimer = window.setTimeout(() => {
|
|
341
|
+
if (disposed || !gl.isContextLost()) return;
|
|
342
|
+
try {
|
|
343
|
+
loseExt()?.restoreContext();
|
|
344
|
+
} catch {
|
|
345
|
+
setSupported(false);
|
|
346
|
+
}
|
|
347
|
+
}, 700);
|
|
348
|
+
};
|
|
349
|
+
const onContextRestored = () => {
|
|
350
|
+
if (initGL()) {
|
|
351
|
+
bufWidth = 0;
|
|
352
|
+
bufHeight = 0;
|
|
353
|
+
uploadedBlobs = null;
|
|
354
|
+
colorsDirty = true;
|
|
355
|
+
setActive(true);
|
|
356
|
+
if (reduceQuery.matches) renderOnce();
|
|
357
|
+
else start();
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
canvas.addEventListener("webglcontextlost", onContextLost);
|
|
361
|
+
canvas.addEventListener("webglcontextrestored", onContextRestored);
|
|
362
|
+
if (gl.isContextLost()) {
|
|
363
|
+
const ext = loseExt();
|
|
364
|
+
if (!ext) {
|
|
365
|
+
queueMicrotask(() => setSupported(false));
|
|
366
|
+
} else {
|
|
367
|
+
try {
|
|
368
|
+
ext.restoreContext();
|
|
369
|
+
} catch {
|
|
370
|
+
queueMicrotask(() => setSupported(false));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} else if (initGL()) {
|
|
374
|
+
draw(COLOR_FADE_MS);
|
|
375
|
+
setActive(true);
|
|
376
|
+
start();
|
|
377
|
+
} else {
|
|
378
|
+
setSupported(false);
|
|
379
|
+
}
|
|
380
|
+
return () => {
|
|
381
|
+
disposed = true;
|
|
382
|
+
stop();
|
|
383
|
+
window.clearTimeout(restoreTimer);
|
|
384
|
+
redrawRef.current = () => {
|
|
385
|
+
};
|
|
386
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
387
|
+
reduceQuery.removeEventListener("change", onReduceChange);
|
|
388
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
389
|
+
themeObserver.disconnect();
|
|
390
|
+
resizeObserver.disconnect();
|
|
391
|
+
canvas.removeEventListener("webglcontextlost", onContextLost);
|
|
392
|
+
canvas.removeEventListener("webglcontextrestored", onContextRestored);
|
|
393
|
+
if (program) gl.deleteProgram(program);
|
|
394
|
+
if (vao) gl.deleteVertexArray(vao);
|
|
395
|
+
try {
|
|
396
|
+
if (!gl.isContextLost()) loseExt()?.loseContext();
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}, [canvasRef, enabled]);
|
|
401
|
+
return { supported, active };
|
|
402
|
+
}
|
|
403
|
+
export {
|
|
404
|
+
DEFAULT_SHADER_BLOBS,
|
|
405
|
+
DEFAULT_SHADER_PARAMS,
|
|
406
|
+
parseColor,
|
|
407
|
+
srgbToLinear,
|
|
408
|
+
useShaderField
|
|
409
|
+
};
|
|
@@ -286,6 +286,23 @@
|
|
|
286
286
|
color: var(--color-status-positive-text);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
.ds-timeline__role-subtitle {
|
|
290
|
+
margin: 0;
|
|
291
|
+
font-family: var(--font-paragraph-family);
|
|
292
|
+
font-size: var(--font-paragraph-size);
|
|
293
|
+
font-weight: var(--font-paragraph-weight);
|
|
294
|
+
line-height: var(--font-paragraph-line-height);
|
|
295
|
+
letter-spacing: var(--font-paragraph-letter-spacing);
|
|
296
|
+
color: var(--color-text-secondary);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* The title and its subtitle read as one header block, so whatever follows
|
|
300
|
+
needs a wider break than the role's own 8px rhythm to separate from it. */
|
|
301
|
+
.ds-timeline__role-subtitle + .ds-timeline__role-description,
|
|
302
|
+
.ds-timeline__role-subtitle + .ds-timeline__role-bullets {
|
|
303
|
+
margin-top: var(--gap-sm);
|
|
304
|
+
}
|
|
305
|
+
|
|
289
306
|
.ds-timeline__role-description {
|
|
290
307
|
margin: 0;
|
|
291
308
|
font-family: var(--font-paragraph-family);
|
|
@@ -13,6 +13,8 @@ export interface TimelineItem {
|
|
|
13
13
|
export interface TimelineRole {
|
|
14
14
|
/** Role or position title */
|
|
15
15
|
title: string;
|
|
16
|
+
/** Secondary line under the title, e.g. the team, org, or product the role sat in */
|
|
17
|
+
subtitle?: React.ReactNode;
|
|
16
18
|
/** Start of the date range, e.g. "May 2024" (omit for entries with no dates) */
|
|
17
19
|
start?: string;
|
|
18
20
|
/** End of the date range, e.g. "Jan 2026" (ignored when `present` is set) */
|
|
@@ -23,6 +23,7 @@ const CompanyTimeline = ({ items, className = "" }) => {
|
|
|
23
23
|
/* @__PURE__ */ jsx("h3", { className: `${BASE_CLASS}__role-title`, children: role.title }),
|
|
24
24
|
renderRoleDates(role)
|
|
25
25
|
] }),
|
|
26
|
+
role.subtitle && /* @__PURE__ */ jsx("p", { className: `${BASE_CLASS}__role-subtitle`, children: role.subtitle }),
|
|
26
27
|
role.description && /* @__PURE__ */ jsx("p", { className: `${BASE_CLASS}__role-description`, children: role.description }),
|
|
27
28
|
role.bullets && role.bullets.length > 0 && /* @__PURE__ */ jsx("ul", { className: `${BASE_CLASS}__role-bullets`, children: role.bullets.map((bullet, bulletIndex) => /* @__PURE__ */ jsx("li", { children: bullet }, bulletIndex)) })
|
|
28
29
|
] }, `${role.title}-${roleIndex}`)) })
|
package/components/registry.json
CHANGED
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
"category": "data-display",
|
|
21
21
|
"client": true
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
"name": "AgentPlan",
|
|
25
|
+
"label": "Agent plan",
|
|
26
|
+
"slug": "agent-plan",
|
|
27
|
+
"description": "A collapsible checklist of an agent's task, with live step states and a progress readout.",
|
|
28
|
+
"category": "ai",
|
|
29
|
+
"client": true
|
|
30
|
+
},
|
|
23
31
|
{
|
|
24
32
|
"name": "AgentStatus",
|
|
25
33
|
"label": "Agent status",
|
|
@@ -252,6 +260,14 @@
|
|
|
252
260
|
"category": "charts",
|
|
253
261
|
"client": false
|
|
254
262
|
},
|
|
263
|
+
{
|
|
264
|
+
"name": "DataTable",
|
|
265
|
+
"label": "Data table",
|
|
266
|
+
"slug": "data-table",
|
|
267
|
+
"description": "The wired table: sorting, search, row selection, and pagination assembled around Table.",
|
|
268
|
+
"category": "data-display",
|
|
269
|
+
"client": true
|
|
270
|
+
},
|
|
255
271
|
{
|
|
256
272
|
"name": "DateInput",
|
|
257
273
|
"label": "Date input",
|
|
@@ -332,6 +348,14 @@
|
|
|
332
348
|
"category": "data-display",
|
|
333
349
|
"client": false
|
|
334
350
|
},
|
|
351
|
+
{
|
|
352
|
+
"name": "EventCalendar",
|
|
353
|
+
"label": "Event calendar",
|
|
354
|
+
"slug": "event-calendar",
|
|
355
|
+
"description": "A month grid with event pills, overflow counts, and month navigation.",
|
|
356
|
+
"category": "data-display",
|
|
357
|
+
"client": true
|
|
358
|
+
},
|
|
335
359
|
{
|
|
336
360
|
"name": "Field",
|
|
337
361
|
"label": "Field",
|
|
@@ -412,6 +436,14 @@
|
|
|
412
436
|
"category": "ai",
|
|
413
437
|
"client": false
|
|
414
438
|
},
|
|
439
|
+
{
|
|
440
|
+
"name": "ModelPicker",
|
|
441
|
+
"label": "Model picker",
|
|
442
|
+
"slug": "model-picker",
|
|
443
|
+
"description": "A model selector for chat surfaces, with per-model descriptions and an optional effort row.",
|
|
444
|
+
"category": "ai",
|
|
445
|
+
"client": true
|
|
446
|
+
},
|
|
415
447
|
{
|
|
416
448
|
"name": "Nav",
|
|
417
449
|
"label": "Navigation",
|
|
@@ -428,6 +460,14 @@
|
|
|
428
460
|
"category": "navigation",
|
|
429
461
|
"client": true
|
|
430
462
|
},
|
|
463
|
+
{
|
|
464
|
+
"name": "NotificationCenter",
|
|
465
|
+
"label": "Notification centre",
|
|
466
|
+
"slug": "notification-center",
|
|
467
|
+
"description": "A persistent notification inbox with unread count, filter tabs, and per-item actions.",
|
|
468
|
+
"category": "feedback",
|
|
469
|
+
"client": true
|
|
470
|
+
},
|
|
431
471
|
{
|
|
432
472
|
"name": "Pagination",
|
|
433
473
|
"label": "Pagination",
|
|
@@ -516,6 +556,14 @@
|
|
|
516
556
|
"category": "data-display",
|
|
517
557
|
"client": true
|
|
518
558
|
},
|
|
559
|
+
{
|
|
560
|
+
"name": "ShaderField",
|
|
561
|
+
"label": "Shader field",
|
|
562
|
+
"slug": "shader-field",
|
|
563
|
+
"description": "An ambient WebGL2 field of soft light sources that sample colour tokens, with a reported fallback status.",
|
|
564
|
+
"category": "layout",
|
|
565
|
+
"client": true
|
|
566
|
+
},
|
|
519
567
|
{
|
|
520
568
|
"name": "Skeleton",
|
|
521
569
|
"label": "Skeleton",
|
|
@@ -20,6 +20,14 @@ declare const _default: {
|
|
|
20
20
|
"category": "data-display",
|
|
21
21
|
"client": true
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
"name": "AgentPlan",
|
|
25
|
+
"label": "Agent plan",
|
|
26
|
+
"slug": "agent-plan",
|
|
27
|
+
"description": "A collapsible checklist of an agent's task, with live step states and a progress readout.",
|
|
28
|
+
"category": "ai",
|
|
29
|
+
"client": true
|
|
30
|
+
},
|
|
23
31
|
{
|
|
24
32
|
"name": "AgentStatus",
|
|
25
33
|
"label": "Agent status",
|
|
@@ -252,6 +260,14 @@ declare const _default: {
|
|
|
252
260
|
"category": "charts",
|
|
253
261
|
"client": false
|
|
254
262
|
},
|
|
263
|
+
{
|
|
264
|
+
"name": "DataTable",
|
|
265
|
+
"label": "Data table",
|
|
266
|
+
"slug": "data-table",
|
|
267
|
+
"description": "The wired table: sorting, search, row selection, and pagination assembled around Table.",
|
|
268
|
+
"category": "data-display",
|
|
269
|
+
"client": true
|
|
270
|
+
},
|
|
255
271
|
{
|
|
256
272
|
"name": "DateInput",
|
|
257
273
|
"label": "Date input",
|
|
@@ -332,6 +348,14 @@ declare const _default: {
|
|
|
332
348
|
"category": "data-display",
|
|
333
349
|
"client": false
|
|
334
350
|
},
|
|
351
|
+
{
|
|
352
|
+
"name": "EventCalendar",
|
|
353
|
+
"label": "Event calendar",
|
|
354
|
+
"slug": "event-calendar",
|
|
355
|
+
"description": "A month grid with event pills, overflow counts, and month navigation.",
|
|
356
|
+
"category": "data-display",
|
|
357
|
+
"client": true
|
|
358
|
+
},
|
|
335
359
|
{
|
|
336
360
|
"name": "Field",
|
|
337
361
|
"label": "Field",
|
|
@@ -412,6 +436,14 @@ declare const _default: {
|
|
|
412
436
|
"category": "ai",
|
|
413
437
|
"client": false
|
|
414
438
|
},
|
|
439
|
+
{
|
|
440
|
+
"name": "ModelPicker",
|
|
441
|
+
"label": "Model picker",
|
|
442
|
+
"slug": "model-picker",
|
|
443
|
+
"description": "A model selector for chat surfaces, with per-model descriptions and an optional effort row.",
|
|
444
|
+
"category": "ai",
|
|
445
|
+
"client": true
|
|
446
|
+
},
|
|
415
447
|
{
|
|
416
448
|
"name": "Nav",
|
|
417
449
|
"label": "Navigation",
|
|
@@ -428,6 +460,14 @@ declare const _default: {
|
|
|
428
460
|
"category": "navigation",
|
|
429
461
|
"client": true
|
|
430
462
|
},
|
|
463
|
+
{
|
|
464
|
+
"name": "NotificationCenter",
|
|
465
|
+
"label": "Notification centre",
|
|
466
|
+
"slug": "notification-center",
|
|
467
|
+
"description": "A persistent notification inbox with unread count, filter tabs, and per-item actions.",
|
|
468
|
+
"category": "feedback",
|
|
469
|
+
"client": true
|
|
470
|
+
},
|
|
431
471
|
{
|
|
432
472
|
"name": "Pagination",
|
|
433
473
|
"label": "Pagination",
|
|
@@ -516,6 +556,14 @@ declare const _default: {
|
|
|
516
556
|
"category": "data-display",
|
|
517
557
|
"client": true
|
|
518
558
|
},
|
|
559
|
+
{
|
|
560
|
+
"name": "ShaderField",
|
|
561
|
+
"label": "Shader field",
|
|
562
|
+
"slug": "shader-field",
|
|
563
|
+
"description": "An ambient WebGL2 field of soft light sources that sample colour tokens, with a reported fallback status.",
|
|
564
|
+
"category": "layout",
|
|
565
|
+
"client": true
|
|
566
|
+
},
|
|
519
567
|
{
|
|
520
568
|
"name": "Skeleton",
|
|
521
569
|
"label": "Skeleton",
|