@robr0/design-system 0.9.0 → 0.11.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.
Files changed (99) hide show
  1. package/README.md +16 -10
  2. package/components/AgentPlan/AgentPlan.css +14 -3
  3. package/components/AiButton/AiButton.css +1 -1
  4. package/components/Alert/Alert.css +9 -0
  5. package/components/AlertDialog/AlertDialog.css +3 -1
  6. package/components/AlertDialog/AlertDialog.js +2 -3
  7. package/components/AppSidebar/AppSidebar.css +22 -3
  8. package/components/AppSidebar/AppSidebar.d.ts +6 -2
  9. package/components/AppSidebar/AppSidebar.js +67 -36
  10. package/components/Card/Card.css +7 -2
  11. package/components/CardStack/CardStack.css +106 -0
  12. package/components/CardStack/CardStack.d.ts +44 -0
  13. package/components/CardStack/CardStack.js +140 -0
  14. package/components/Carousel/Carousel.css +9 -0
  15. package/components/Carousel/Carousel.js +37 -4
  16. package/components/Chart/AreaChart.js +6 -5
  17. package/components/Chart/BarChart.js +1 -1
  18. package/components/Chart/LineChart.js +4 -3
  19. package/components/Chart/PieChart.js +2 -15
  20. package/components/Chart/RadarChart.js +5 -4
  21. package/components/Chart/RadialChart.js +2 -15
  22. package/components/Chart/ScatterChart.js +3 -13
  23. package/components/Chart/StackedBarChart.js +2 -15
  24. package/components/Chart/Treemap.js +3 -17
  25. package/components/Chart/palette.d.ts +1 -0
  26. package/components/Chart/palette.js +19 -0
  27. package/components/ChatMarker/ChatMarker.css +9 -0
  28. package/components/ChatMessage/ChatMessage.css +16 -0
  29. package/components/ChatThread/ChatThread.js +2 -2
  30. package/components/Chip/Chip.css +9 -0
  31. package/components/CodeBlock/CodeBlock.css +10 -1
  32. package/components/CodeBlock/CodeBlock.js +2 -1
  33. package/components/ColorPicker/ColorPicker.js +10 -0
  34. package/components/Combobox/Combobox.css +9 -0
  35. package/components/CommandPalette/CommandPalette.css +9 -0
  36. package/components/CommandPalette/CommandPalette.js +31 -5
  37. package/components/Composer/Composer.css +118 -0
  38. package/components/Composer/Composer.d.ts +7 -0
  39. package/components/Composer/Composer.js +7 -1
  40. package/components/ContactCard/ContactCard.css +9 -0
  41. package/components/ContextMenu/ContextMenu.js +39 -3
  42. package/components/DatePicker/DatePicker.js +16 -0
  43. package/components/Dialog/Dialog.css +3 -1
  44. package/components/Dialog/Dialog.js +5 -4
  45. package/components/DocumentChip/DocumentChip.css +9 -0
  46. package/components/Dropdown/Dropdown.js +22 -3
  47. package/components/DropdownMenu/DropdownMenu.js +43 -6
  48. package/components/EmptyState/EmptyState.css +9 -0
  49. package/components/FileInput/FileInput.css +9 -0
  50. package/components/Globe/Globe.css +155 -0
  51. package/components/Globe/Globe.d.ts +96 -0
  52. package/components/Globe/Globe.js +415 -0
  53. package/components/InterruptCard/InterruptCard.css +27 -17
  54. package/components/InterruptCard/InterruptCard.d.ts +1 -1
  55. package/components/InterruptCard/InterruptCard.js +1 -1
  56. package/components/MapCallout/MapCallout.css +46 -0
  57. package/components/MapCallout/MapCallout.d.ts +29 -0
  58. package/components/MapCallout/MapCallout.js +17 -0
  59. package/components/MapLegend/MapLegend.css +88 -0
  60. package/components/MapLegend/MapLegend.d.ts +39 -0
  61. package/components/MapLegend/MapLegend.js +68 -0
  62. package/components/MessageActions/MessageActions.css +9 -0
  63. package/components/MessageCard/MessageCard.css +68 -16
  64. package/components/MessageCard/MessageCard.d.ts +1 -1
  65. package/components/MessageCard/MessageCard.js +6 -4
  66. package/components/ModelPicker/ModelPicker.css +4 -1
  67. package/components/ModelPicker/ModelPicker.js +43 -1
  68. package/components/NotificationCenter/NotificationCenter.css +9 -0
  69. package/components/NotificationCenter/NotificationCenter.js +26 -10
  70. package/components/NumberInput/NumberInput.css +9 -0
  71. package/components/Popover/Popover.css +3 -1
  72. package/components/Popover/Popover.js +2 -1
  73. package/components/SectionTitle/SectionTitle.css +8 -0
  74. package/components/SectionTitle/SectionTitle.d.ts +3 -1
  75. package/components/SectionTitle/SectionTitle.js +6 -1
  76. package/components/SourceChip/SourceChip.css +9 -0
  77. package/components/Stat/Stat.css +9 -0
  78. package/components/Stepper/Stepper.css +9 -0
  79. package/components/TagInput/TagInput.css +9 -0
  80. package/components/TimePicker/TimePicker.js +4 -1
  81. package/components/Toast/Toast.css +32 -11
  82. package/components/Toast/Toast.js +5 -4
  83. package/components/ToggleGroup/ToggleGroup.css +9 -0
  84. package/components/ToolCall/ToolCall.css +18 -6
  85. package/components/Tooltip/Tooltip.js +20 -16
  86. package/components/registry.json +37 -0
  87. package/components/registry.json.d.ts +37 -0
  88. package/components/registry.json.js +2 -2
  89. package/index.d.ts +4 -0
  90. package/index.js +8 -0
  91. package/package.json +5 -1
  92. package/tokens/motion.d.ts +23 -0
  93. package/tokens/motion.js +14 -0
  94. package/tokens/registry.json +17 -0
  95. package/tokens/registry.json.d.ts +17 -0
  96. package/tokens/registry.json.js +1 -1
  97. package/tokens/tokens-dark.css +28 -13
  98. package/tokens/tokens-light.css +26 -12
  99. package/tokens/tokens-typography.css +20 -0
@@ -0,0 +1,415 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import React, { useId, useState, useRef, useCallback, useSyncExternalStore, useEffect } from "react";
4
+ import "./Globe.css";
5
+ const VIEW = 480;
6
+ const CENTER = VIEW / 2;
7
+ const RADIUS = 180;
8
+ const GRATICULE_SAMPLES = 60;
9
+ const ARC_SAMPLES = 48;
10
+ const DRAG_DEGREES_PER_RADIUS = 90;
11
+ const KEY_STEP = 10;
12
+ const MAX_LAT = 80;
13
+ const MARKER = 5;
14
+ const DEG = Math.PI / 180;
15
+ const toVec = (lat, lng) => {
16
+ const φ = lat * DEG;
17
+ const λ = lng * DEG;
18
+ return [Math.cos(φ) * Math.cos(λ), Math.cos(φ) * Math.sin(λ), Math.sin(φ)];
19
+ };
20
+ const makeProjector = ([lng0, lat0]) => {
21
+ const λ0 = lng0 * DEG;
22
+ const φ0 = lat0 * DEG;
23
+ const cosλ = Math.cos(λ0);
24
+ const sinλ = Math.sin(λ0);
25
+ const cosφ = Math.cos(φ0);
26
+ const sinφ = Math.sin(φ0);
27
+ return ([x, y, z], altitude = 0) => {
28
+ const x1 = x * cosλ + y * sinλ;
29
+ const y1 = -x * sinλ + y * cosλ;
30
+ const depth = x1 * cosφ + z * sinφ;
31
+ const up = -x1 * sinφ + z * cosφ;
32
+ const s = 1 + altitude;
33
+ const sx = y1 * s;
34
+ const sy = up * s;
35
+ return {
36
+ x: CENTER + RADIUS * sx,
37
+ y: CENTER - RADIUS * sy,
38
+ visible: depth >= 0 || sx * sx + sy * sy > 1
39
+ };
40
+ };
41
+ };
42
+ const round = (n) => Math.round(n * 10) / 10;
43
+ const splitRuns = (pts) => {
44
+ let front = "";
45
+ let back = "";
46
+ let prev;
47
+ for (const p of pts) {
48
+ const cmd = `${prev && prev.visible === p.visible ? "L" : "M"}${round(p.x)} ${round(p.y)}`;
49
+ if (p.visible) front += cmd;
50
+ else back += cmd;
51
+ if (prev && prev.visible !== p.visible) {
52
+ const start = `M${round(prev.x)} ${round(prev.y)}L${round(p.x)} ${round(p.y)}`;
53
+ if (p.visible) front += start;
54
+ else back += start;
55
+ }
56
+ prev = p;
57
+ }
58
+ return { front, back };
59
+ };
60
+ const arcVectors = (a, b, altitude) => {
61
+ const dot = Math.max(-1, Math.min(1, a[0] * b[0] + a[1] * b[1] + a[2] * b[2]));
62
+ const ω = Math.acos(dot);
63
+ const sinω = Math.sin(ω);
64
+ const out = [];
65
+ for (let i = 0; i <= ARC_SAMPLES; i++) {
66
+ const t = i / ARC_SAMPLES;
67
+ let v;
68
+ if (sinω < 1e-6) {
69
+ v = a;
70
+ } else {
71
+ const wa = Math.sin((1 - t) * ω) / sinω;
72
+ const wb = Math.sin(t * ω) / sinω;
73
+ v = [wa * a[0] + wb * b[0], wa * a[1] + wb * b[1], wa * a[2] + wb * b[2]];
74
+ }
75
+ out.push([v, altitude * Math.sin(t * Math.PI)]);
76
+ }
77
+ return out;
78
+ };
79
+ const normalizeLng = (lng) => ((lng + 180) % 360 + 360) % 360 - 180;
80
+ const clampLat = (lat) => Math.max(-MAX_LAT, Math.min(MAX_LAT, lat));
81
+ const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
82
+ const subscribeReducedMotion = (callback) => {
83
+ const query = window.matchMedia(REDUCED_MOTION_QUERY);
84
+ query.addEventListener("change", callback);
85
+ return () => query.removeEventListener("change", callback);
86
+ };
87
+ const getReducedMotion = () => window.matchMedia(REDUCED_MOTION_QUERY).matches;
88
+ const getServerReducedMotion = () => false;
89
+ const KEY_DELTAS = {
90
+ ArrowLeft: [KEY_STEP, 0],
91
+ ArrowRight: [-KEY_STEP, 0],
92
+ ArrowUp: [0, -KEY_STEP],
93
+ ArrowDown: [0, KEY_STEP],
94
+ a: [KEY_STEP, 0],
95
+ d: [-KEY_STEP, 0],
96
+ w: [0, -KEY_STEP],
97
+ s: [0, KEY_STEP]
98
+ };
99
+ const Globe = React.forwardRef(
100
+ ({
101
+ points = [],
102
+ arcs = [],
103
+ rotation: rotationProp,
104
+ defaultRotation = [-20, 20],
105
+ onRotationChange,
106
+ autoRotate = 3,
107
+ interactive = true,
108
+ graticuleStep = 30,
109
+ showLabels = true,
110
+ activePointId,
111
+ onPointHover,
112
+ onPointClick,
113
+ renderCallout,
114
+ label = "Globe",
115
+ className = "",
116
+ onPointerDown,
117
+ onPointerMove,
118
+ onPointerUp,
119
+ onPointerCancel,
120
+ onPointerEnter,
121
+ onPointerLeave,
122
+ onKeyDown,
123
+ onFocus,
124
+ onBlur,
125
+ ...rest
126
+ }, ref) => {
127
+ const baseClass = "ds-globe";
128
+ const gradientId = useId();
129
+ const isControlled = rotationProp !== void 0;
130
+ const [innerRotation, setInnerRotation] = useState(defaultRotation);
131
+ const rotation = isControlled ? rotationProp : innerRotation;
132
+ const rotationRef = useRef(rotation);
133
+ rotationRef.current = rotation;
134
+ const onRotationChangeRef = useRef(onRotationChange);
135
+ onRotationChangeRef.current = onRotationChange;
136
+ const commit = useCallback(
137
+ (next) => {
138
+ const clean = [normalizeLng(next[0]), clampLat(next[1])];
139
+ if (!isControlled) setInnerRotation(clean);
140
+ onRotationChangeRef.current?.(clean);
141
+ },
142
+ [isControlled]
143
+ );
144
+ const [hoveredId, setHoveredId] = useState(null);
145
+ const [paused, setPaused] = useState(false);
146
+ const reducedMotion = useSyncExternalStore(
147
+ subscribeReducedMotion,
148
+ getReducedMotion,
149
+ getServerReducedMotion
150
+ );
151
+ const spinning = autoRotate !== 0 && !paused && !reducedMotion;
152
+ useEffect(() => {
153
+ if (!spinning) return;
154
+ let frame = 0;
155
+ let last = performance.now();
156
+ const tick = (now) => {
157
+ const dt = (now - last) / 1e3;
158
+ last = now;
159
+ const [lng, lat] = rotationRef.current;
160
+ commit([lng - autoRotate * dt, lat]);
161
+ frame = requestAnimationFrame(tick);
162
+ };
163
+ frame = requestAnimationFrame(tick);
164
+ return () => cancelAnimationFrame(frame);
165
+ }, [spinning, autoRotate, commit]);
166
+ const rootRef = useRef(null);
167
+ const drag = useRef(null);
168
+ const [dragging, setDragging] = useState(false);
169
+ const setRef = (node) => {
170
+ rootRef.current = node;
171
+ if (typeof ref === "function") ref(node);
172
+ else if (ref) ref.current = node;
173
+ };
174
+ const handlePointerDown = (e) => {
175
+ onPointerDown?.(e);
176
+ if (!interactive || e.button !== 0) return;
177
+ drag.current = { x: e.clientX, y: e.clientY, rotation: rotationRef.current };
178
+ setDragging(true);
179
+ setPaused(true);
180
+ e.currentTarget.setPointerCapture(e.pointerId);
181
+ };
182
+ const handlePointerMove = (e) => {
183
+ onPointerMove?.(e);
184
+ if (!drag.current || !rootRef.current) return;
185
+ const radiusPx = rootRef.current.clientWidth * (RADIUS / VIEW) || 1;
186
+ const perPx = DRAG_DEGREES_PER_RADIUS / radiusPx;
187
+ const [lng, lat] = drag.current.rotation;
188
+ commit([
189
+ lng - (e.clientX - drag.current.x) * perPx,
190
+ lat + (e.clientY - drag.current.y) * perPx
191
+ ]);
192
+ };
193
+ const endDrag = (e) => {
194
+ if (!drag.current) return;
195
+ drag.current = null;
196
+ setDragging(false);
197
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
198
+ e.currentTarget.releasePointerCapture(e.pointerId);
199
+ }
200
+ };
201
+ const handlePointerUp = (e) => {
202
+ onPointerUp?.(e);
203
+ endDrag(e);
204
+ };
205
+ const handlePointerCancel = (e) => {
206
+ onPointerCancel?.(e);
207
+ endDrag(e);
208
+ };
209
+ const handlePointerEnter = (e) => {
210
+ onPointerEnter?.(e);
211
+ setPaused(true);
212
+ };
213
+ const handlePointerLeave = (e) => {
214
+ onPointerLeave?.(e);
215
+ if (document.activeElement !== e.currentTarget) setPaused(false);
216
+ };
217
+ const handleKeyDown = (e) => {
218
+ onKeyDown?.(e);
219
+ if (!interactive || e.defaultPrevented) return;
220
+ const delta = KEY_DELTAS[e.key] ?? KEY_DELTAS[e.key.toLowerCase()];
221
+ if (!delta) return;
222
+ e.preventDefault();
223
+ const [lng, lat] = rotationRef.current;
224
+ commit([lng + delta[0], lat + delta[1]]);
225
+ };
226
+ const handleFocus = (e) => {
227
+ onFocus?.(e);
228
+ setPaused(true);
229
+ };
230
+ const handleBlur = (e) => {
231
+ onBlur?.(e);
232
+ if (!e.currentTarget.matches(":hover")) setPaused(false);
233
+ };
234
+ const project = makeProjector(rotation);
235
+ let graticuleFront = "";
236
+ let graticuleBack = "";
237
+ if (graticuleStep > 0) {
238
+ for (let lng = -180; lng < 180; lng += graticuleStep) {
239
+ const pts = [];
240
+ for (let i = 0; i <= GRATICULE_SAMPLES; i++) {
241
+ pts.push(project(toVec(-90 + 180 * i / GRATICULE_SAMPLES, lng)));
242
+ }
243
+ const runs = splitRuns(pts);
244
+ graticuleFront += runs.front;
245
+ graticuleBack += runs.back;
246
+ }
247
+ for (let lat = -90 + graticuleStep; lat < 90; lat += graticuleStep) {
248
+ const pts = [];
249
+ for (let i = 0; i <= GRATICULE_SAMPLES; i++) {
250
+ pts.push(project(toVec(lat, -180 + 360 * i / GRATICULE_SAMPLES)));
251
+ }
252
+ const runs = splitRuns(pts);
253
+ graticuleFront += runs.front;
254
+ graticuleBack += runs.back;
255
+ }
256
+ }
257
+ const byId = new Map(points.map((p) => [p.id, p]));
258
+ const projectedPoints = points.map((p) => ({ point: p, at: project(toVec(p.lat, p.lng)) }));
259
+ const drawnArcs = arcs.flatMap((arc, i) => {
260
+ const from = byId.get(arc.from);
261
+ const to = byId.get(arc.to);
262
+ if (!from || !to) return [];
263
+ const altitude = arc.altitude ?? 0.25;
264
+ const samples = arcVectors(toVec(from.lat, from.lng), toVec(to.lat, to.lng), altitude);
265
+ const pts = samples.map(([v, h]) => project(v, h));
266
+ const runs = splitRuns(pts);
267
+ return [
268
+ {
269
+ key: `${arc.from}-${arc.to}-${i}`,
270
+ color: arc.color,
271
+ start: pts[0],
272
+ end: pts[pts.length - 1],
273
+ ...runs
274
+ }
275
+ ];
276
+ });
277
+ const calloutId = activePointId ?? hoveredId;
278
+ const callout = calloutId ? projectedPoints.find((p) => p.point.id === calloutId) : void 0;
279
+ const calloutSide = callout && callout.at.x < CENTER ? "left" : "right";
280
+ const classes = [
281
+ baseClass,
282
+ interactive ? `${baseClass}--interactive` : "",
283
+ dragging ? `${baseClass}--dragging` : "",
284
+ className
285
+ ].filter(Boolean).join(" ");
286
+ const description = `${points.length} ${points.length === 1 ? "point" : "points"}, ${arcs.length} ${arcs.length === 1 ? "arc" : "arcs"}.${interactive ? " Drag to rotate, or use the arrow keys when focused." : ""}`;
287
+ return /* @__PURE__ */ jsxs(
288
+ "div",
289
+ {
290
+ ...rest,
291
+ ref: setRef,
292
+ className: classes,
293
+ role: interactive ? "application" : "img",
294
+ "aria-roledescription": "globe",
295
+ "aria-label": `${label}. ${description}`,
296
+ tabIndex: interactive ? 0 : void 0,
297
+ onPointerDown: handlePointerDown,
298
+ onPointerMove: handlePointerMove,
299
+ onPointerUp: handlePointerUp,
300
+ onPointerCancel: handlePointerCancel,
301
+ onPointerEnter: handlePointerEnter,
302
+ onPointerLeave: handlePointerLeave,
303
+ onKeyDown: handleKeyDown,
304
+ onFocus: handleFocus,
305
+ onBlur: handleBlur,
306
+ children: [
307
+ /* @__PURE__ */ jsxs(
308
+ "svg",
309
+ {
310
+ className: `${baseClass}__svg`,
311
+ viewBox: `0 0 ${VIEW} ${VIEW}`,
312
+ xmlns: "http://www.w3.org/2000/svg",
313
+ "aria-hidden": "true",
314
+ focusable: "false",
315
+ children: [
316
+ /* @__PURE__ */ jsx("defs", { children: drawnArcs.map(
317
+ (arc) => arc.color ? null : /* @__PURE__ */ jsxs(
318
+ "linearGradient",
319
+ {
320
+ id: `${gradientId}-${arc.key}`,
321
+ gradientUnits: "userSpaceOnUse",
322
+ x1: round(arc.start.x),
323
+ y1: round(arc.start.y),
324
+ x2: round(arc.end.x),
325
+ y2: round(arc.end.y),
326
+ children: [
327
+ /* @__PURE__ */ jsx("stop", { offset: "0", className: `${baseClass}__arc-stop-start` }),
328
+ /* @__PURE__ */ jsx("stop", { offset: "1", className: `${baseClass}__arc-stop-end` })
329
+ ]
330
+ },
331
+ arc.key
332
+ )
333
+ ) }),
334
+ /* @__PURE__ */ jsx("circle", { className: `${baseClass}__limb`, cx: CENTER, cy: CENTER, r: RADIUS }),
335
+ graticuleBack && /* @__PURE__ */ jsx("path", { className: `${baseClass}__graticule-back`, d: graticuleBack }),
336
+ graticuleFront && /* @__PURE__ */ jsx("path", { className: `${baseClass}__graticule`, d: graticuleFront }),
337
+ /* @__PURE__ */ jsx("g", { className: `${baseClass}__arcs`, children: drawnArcs.map((arc) => {
338
+ const stroke = arc.color ?? `url(#${gradientId}-${arc.key})`;
339
+ return /* @__PURE__ */ jsxs("g", { children: [
340
+ arc.back && /* @__PURE__ */ jsx("path", { className: `${baseClass}__arc-back`, d: arc.back, stroke }),
341
+ arc.front && /* @__PURE__ */ jsx("path", { className: `${baseClass}__arc`, d: arc.front, stroke })
342
+ ] }, arc.key);
343
+ }) }),
344
+ /* @__PURE__ */ jsx("g", { className: `${baseClass}__points`, children: projectedPoints.map(({ point, at }) => {
345
+ const x = round(at.x);
346
+ const y = round(at.y);
347
+ const active = point.id === calloutId;
348
+ const markerClasses = [
349
+ `${baseClass}__point`,
350
+ `${baseClass}__point--${point.kind ?? "point"}`,
351
+ at.visible ? "" : `${baseClass}__point--back`,
352
+ active ? `${baseClass}__point--active` : ""
353
+ ].filter(Boolean).join(" ");
354
+ return /* @__PURE__ */ jsxs(
355
+ "g",
356
+ {
357
+ className: markerClasses,
358
+ transform: `translate(${x} ${y})`,
359
+ onPointerEnter: () => {
360
+ setHoveredId(point.id);
361
+ onPointHover?.(point);
362
+ },
363
+ onPointerLeave: () => {
364
+ setHoveredId((id) => id === point.id ? null : id);
365
+ onPointHover?.(null);
366
+ },
367
+ onClick: onPointClick ? () => onPointClick(point) : void 0,
368
+ children: [
369
+ /* @__PURE__ */ jsx("circle", { className: `${baseClass}__hit`, r: MARKER * 2.4 }),
370
+ point.kind === "anchor" ? /* @__PURE__ */ jsx(
371
+ "rect",
372
+ {
373
+ className: `${baseClass}__glyph`,
374
+ x: -MARKER,
375
+ y: -MARKER,
376
+ width: MARKER * 2,
377
+ height: MARKER * 2
378
+ }
379
+ ) : /* @__PURE__ */ jsx(
380
+ "path",
381
+ {
382
+ className: `${baseClass}__glyph`,
383
+ d: `M${-MARKER} 0H${MARKER}M0 ${-MARKER}V${MARKER}`
384
+ }
385
+ ),
386
+ showLabels && point.label && /* @__PURE__ */ jsx("text", { className: `${baseClass}__label`, x: MARKER * 1.8, y: -MARKER * 1.2, children: point.label })
387
+ ]
388
+ },
389
+ point.id
390
+ );
391
+ }) })
392
+ ]
393
+ }
394
+ ),
395
+ callout && renderCallout && /* @__PURE__ */ jsx(
396
+ "div",
397
+ {
398
+ className: `${baseClass}__callout`,
399
+ "data-side": calloutSide,
400
+ style: {
401
+ left: `${callout.at.x / VIEW * 100}%`,
402
+ top: `${callout.at.y / VIEW * 100}%`
403
+ },
404
+ children: renderCallout(callout.point)
405
+ }
406
+ )
407
+ ]
408
+ }
409
+ );
410
+ }
411
+ );
412
+ Globe.displayName = "Globe";
413
+ export {
414
+ Globe
415
+ };
@@ -19,7 +19,8 @@
19
19
  width: 100%;
20
20
  background-color: var(--color-bg-container-primary);
21
21
  border: var(--border-xs) solid var(--color-bg-container-border);
22
- border-radius: var(--radius-md);
22
+ /* The chat furniture's shared 24px shell — see ToolCall. */
23
+ border-radius: var(--radius-xl);
23
24
  overflow: hidden;
24
25
  }
25
26
 
@@ -31,13 +32,15 @@
31
32
  display: flex;
32
33
  align-items: flex-start;
33
34
  gap: var(--gap-sm);
34
- padding: var(--padding-sm-md) var(--padding-md);
35
+ padding: var(--padding-md) var(--padding-lg);
35
36
  }
36
37
 
37
38
  .ds-interrupt-card__icon {
38
39
  display: inline-flex;
39
40
  align-items: center;
40
41
  flex: none;
42
+ /* Centre the icon on the title's first line. */
43
+ margin-top: calc((var(--font-heading-3-line-height) - var(--icon-size-sm)) / 2);
41
44
  color: var(--ds-interrupt-card-color);
42
45
  }
43
46
 
@@ -48,18 +51,18 @@
48
51
  .ds-interrupt-card__heading {
49
52
  display: flex;
50
53
  flex-direction: column;
51
- gap: var(--gap-xxs);
54
+ gap: var(--gap-xs);
52
55
  min-width: 0;
53
56
  }
54
57
 
55
- /* The question is the card's one heading, so it takes the title tier
56
- rather than an emphasised paragraph */
58
+ /* The question is the card's one heading, so it takes the same heading
59
+ tier as MessageCard's title the chat cards share one title scale. */
57
60
  .ds-interrupt-card__title {
58
- font-family: var(--font-title-body-family);
59
- font-size: var(--font-title-body-size);
60
- font-weight: var(--font-title-body-weight);
61
- line-height: var(--font-title-body-line-height);
62
- letter-spacing: var(--font-title-body-letter-spacing);
61
+ font-family: var(--font-heading-3-family);
62
+ font-size: var(--font-heading-3-size);
63
+ font-weight: var(--font-heading-3-weight);
64
+ line-height: var(--font-heading-3-line-height);
65
+ letter-spacing: var(--font-heading-3-letter-spacing);
63
66
  color: var(--color-text-primary);
64
67
  }
65
68
 
@@ -80,7 +83,7 @@
80
83
  ============================================ */
81
84
 
82
85
  .ds-interrupt-card__detail {
83
- padding: 0 var(--padding-md) var(--padding-sm-md);
86
+ padding: 0 var(--padding-lg) var(--padding-md);
84
87
  }
85
88
 
86
89
  .ds-interrupt-card__detail > :first-child {
@@ -101,10 +104,9 @@
101
104
  .ds-interrupt-card__options {
102
105
  display: flex;
103
106
  align-items: center;
104
- justify-content: flex-end;
107
+ justify-content: flex-start;
105
108
  gap: var(--gap-sm);
106
- padding: var(--padding-sm-md) var(--padding-md);
107
- border-top: var(--border-xs) solid var(--color-divider);
109
+ padding: var(--padding-sm) var(--padding-lg) var(--padding-md);
108
110
  }
109
111
 
110
112
  /* ============================================
@@ -116,10 +118,9 @@
116
118
  .ds-interrupt-card__answer {
117
119
  display: flex;
118
120
  align-items: center;
119
- justify-content: flex-end;
121
+ justify-content: flex-start;
120
122
  gap: var(--gap-xs);
121
- padding: var(--padding-sm-md) var(--padding-md);
122
- border-top: var(--border-xs) solid var(--color-divider);
123
+ padding: var(--padding-sm) var(--padding-lg) var(--padding-md);
123
124
  font-family: var(--font-paragraph-sm-family);
124
125
  font-size: var(--font-paragraph-sm-size);
125
126
  font-weight: var(--font-paragraph-sm-weight);
@@ -152,3 +153,12 @@
152
153
  white-space: nowrap;
153
154
  border: 0;
154
155
  }
156
+
157
+ /* Icons take the colour of the text around them. Explicit, not just
158
+ inherited by default: a consumer stylesheet that sets a colour on
159
+ .material-symbols-rounded itself (a common global default) would
160
+ otherwise override inheritance and detach the icon from the state
161
+ colours this component sets on its parents. */
162
+ .ds-interrupt-card .material-symbols-rounded {
163
+ color: inherit;
164
+ }
@@ -14,7 +14,7 @@ type InterruptCardOwnProps = {
14
14
  title: string;
15
15
  /** One line of context under the title. */
16
16
  description?: string;
17
- /** Leading Material Symbol name, or any custom element. */
17
+ /** Leading Material Symbol name, or any custom element. No icon by default. */
18
18
  icon?: string | React.ReactNode;
19
19
  /** The choices, rendered as buttons left to right. */
20
20
  options?: InterruptCardOption[];
@@ -12,7 +12,7 @@ const InterruptCard = React.forwardRef(
12
12
  ({
13
13
  title,
14
14
  description,
15
- icon = "pause_circle",
15
+ icon,
16
16
  options,
17
17
  onValueChange,
18
18
  value,
@@ -0,0 +1,46 @@
1
+ /* ============================================
2
+ MAP CALLOUT COMPONENT
3
+ The annotation beside a marker: a name in
4
+ capitals over readout lines in the code face.
5
+ ============================================ */
6
+
7
+ /* Base */
8
+
9
+ .ds-map-callout {
10
+ display: inline-flex;
11
+ flex-direction: column;
12
+ gap: var(--gap-xxs);
13
+ font-family: var(--font-family-code);
14
+ color: var(--color-text-primary);
15
+ }
16
+
17
+ .ds-map-callout--start {
18
+ align-items: flex-start;
19
+ text-align: left;
20
+ }
21
+
22
+ .ds-map-callout--end {
23
+ align-items: flex-end;
24
+ text-align: right;
25
+ }
26
+
27
+ /* Parts */
28
+
29
+ /* The overline face on the code family: the metrics and tracking are the
30
+ uppercase-label tokens, the family stays monospace with the readouts. */
31
+ .ds-map-callout__title {
32
+ font-size: var(--font-overline-size);
33
+ font-weight: var(--font-overline-weight);
34
+ line-height: var(--font-overline-line-height);
35
+ letter-spacing: var(--font-overline-letter-spacing);
36
+ text-transform: uppercase;
37
+ color: var(--color-text-primary);
38
+ }
39
+
40
+ .ds-map-callout__line {
41
+ font-size: var(--font-caption-size);
42
+ font-weight: var(--font-caption-weight);
43
+ line-height: var(--font-caption-line-height);
44
+ color: var(--color-text-tertiary);
45
+ font-variant-numeric: tabular-nums;
46
+ }
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by MapCallout itself — everything else falls through to the root `<div>`. */
3
+ type MapCalloutOwnProps = {
4
+ /**
5
+ * The place's name, set in capitals: "Santiago, CL". Intentionally shadows
6
+ * the native `title` tooltip attribute, which MapCallout does not expose.
7
+ */
8
+ title: string;
9
+ /** Readout lines under the title, in order: a status, a reading, a route. */
10
+ lines?: React.ReactNode[];
11
+ /**
12
+ * Which way the text ranges. A callout sitting left of its marker ranges
13
+ * right so it hangs off the point; `start` is the ordinary reading order.
14
+ */
15
+ align?: 'start' | 'end';
16
+ /** Additional CSS classes */
17
+ className?: string;
18
+ };
19
+ export interface MapCalloutProps extends MapCalloutOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof MapCalloutOwnProps | 'children'> {
20
+ }
21
+ /**
22
+ * MapCallout — the annotation beside a point on a map or globe: the name in
23
+ * capitals, then the readouts beneath it in the code face, so a coordinate
24
+ * and a reading line up digit for digit. Pure markup, no hooks, so it
25
+ * renders from a Server Component; Globe's `renderCallout` is its intended
26
+ * home, where `align` follows the overlay's `data-side`.
27
+ */
28
+ export declare const MapCallout: React.ForwardRefExoticComponent<MapCalloutProps & React.RefAttributes<HTMLDivElement>>;
29
+ export {};
@@ -0,0 +1,17 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./MapCallout.css";
4
+ const MapCallout = React.forwardRef(
5
+ ({ title, lines = [], align = "start", className = "", ...rest }, ref) => {
6
+ const baseClass = "ds-map-callout";
7
+ const classes = [baseClass, `${baseClass}--${align}`, className].filter(Boolean).join(" ");
8
+ return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, children: [
9
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__title`, children: title }),
10
+ lines.map((line, i) => /* @__PURE__ */ jsx("span", { className: `${baseClass}__line`, children: line }, i))
11
+ ] });
12
+ }
13
+ );
14
+ MapCallout.displayName = "MapCallout";
15
+ export {
16
+ MapCallout
17
+ };