@opengeni/react 6.1.0-canary.3 → 6.1.0-canary.4
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/{chunk-URNTAVGX.js → chunk-AWEBVNVX.js} +201 -59
- package/dist/chunk-AWEBVNVX.js.map +1 -0
- package/dist/{chunk-NYESIADM.js → chunk-B7TTSUEW.js} +3 -1
- package/dist/chunk-B7TTSUEW.js.map +1 -0
- package/dist/{chunk-EQEIQSRJ.js → chunk-BC7BFCMN.js} +2 -2
- package/dist/{chunk-LVHCZABQ.js → chunk-GHSSI2BK.js} +4 -4
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/composer.js +2 -2
- package/dist/connect-setup.d.ts +2 -0
- package/dist/connect.js +16 -6
- package/dist/connect.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/realtime.js +20 -20
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.js +4 -4
- package/dist/session.js +1 -1
- package/package.json +3 -3
- package/src/components/markdown.tsx +12 -7
- package/src/components/preview-loading.tsx +154 -0
- package/src/connect-setup.tsx +17 -5
- package/src/hooks/use-composer.ts +6 -0
- package/src/realtime/realtime-control.tsx +6 -8
- package/styles/compiled.css +48 -0
- package/styles/index.css +45 -0
- package/dist/chunk-NYESIADM.js.map +0 -1
- package/dist/chunk-URNTAVGX.js.map +0 -1
- /package/dist/{chunk-EQEIQSRJ.js.map → chunk-BC7BFCMN.js.map} +0 -0
- /package/dist/{chunk-LVHCZABQ.js.map → chunk-GHSSI2BK.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "6.1.0-canary.
|
|
3
|
+
"version": "6.1.0-canary.4",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -133,8 +133,8 @@
|
|
|
133
133
|
"@dnd-kit/utilities": "3.2.2",
|
|
134
134
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
135
135
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
136
|
-
"@opengeni/connect": "^0.2.0-canary.
|
|
137
|
-
"@opengeni/sdk": "^6.1.0-canary.
|
|
136
|
+
"@opengeni/connect": "^0.2.0-canary.15",
|
|
137
|
+
"@opengeni/sdk": "^6.1.0-canary.4",
|
|
138
138
|
"clsx": "^2.1.1",
|
|
139
139
|
"dequal": "2.0.3",
|
|
140
140
|
"lucide-react": "^1.8.0",
|
|
@@ -26,6 +26,7 @@ import { tableElementToTsv } from "../lib/clipboard";
|
|
|
26
26
|
import { prefersReducedMotion } from "../lib/motion";
|
|
27
27
|
import { MOTION_INSPECT_SCALE } from "../lib/motion-inspect";
|
|
28
28
|
import { CopyButton } from "./copy-button";
|
|
29
|
+
import { PreviewLoading } from "./preview-loading";
|
|
29
30
|
import type { observeMarkdownTableLayout } from "./markdown-table-layout";
|
|
30
31
|
import { softenStreamingMarkdown } from "./soften-streaming-markdown";
|
|
31
32
|
import { createStreamReveal, rehypeStreamReveal, type StreamReveal } from "./stream-reveal";
|
|
@@ -434,13 +435,17 @@ function InteractiveCodeBlock({ children, node }: ComponentPropsWithoutRef<"pre"
|
|
|
434
435
|
if (!complete) {
|
|
435
436
|
return (
|
|
436
437
|
<div role="status" aria-live="polite" aria-busy={streaming === true} className="my-3">
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
438
|
+
{streaming ? (
|
|
439
|
+
<PreviewLoading />
|
|
440
|
+
) : (
|
|
441
|
+
<ActivityDisclosure
|
|
442
|
+
icon={<PanelsTopLeftIcon aria-hidden className="size-3.5" />}
|
|
443
|
+
title="Preview incomplete"
|
|
444
|
+
running={false}
|
|
445
|
+
expandable={false}
|
|
446
|
+
preview="Generation stopped before the preview was ready."
|
|
447
|
+
/>
|
|
448
|
+
)}
|
|
444
449
|
</div>
|
|
445
450
|
);
|
|
446
451
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/** Private loading surface: never receives or executes the unfinished fence. */
|
|
4
|
+
export function PreviewLoading() {
|
|
5
|
+
const ref = useRef<HTMLCanvasElement>(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const canvas = ref.current;
|
|
8
|
+
if (!canvas) return;
|
|
9
|
+
let ctx: CanvasRenderingContext2D | null;
|
|
10
|
+
try {
|
|
11
|
+
ctx = canvas.getContext("2d");
|
|
12
|
+
} catch {
|
|
13
|
+
return; // The CSS grid and status remain when canvas is unavailable.
|
|
14
|
+
}
|
|
15
|
+
if (!ctx) return;
|
|
16
|
+
const context = ctx;
|
|
17
|
+
const motion = window.matchMedia?.("(prefers-reduced-motion: reduce)");
|
|
18
|
+
let frame: number | undefined;
|
|
19
|
+
let time = 2;
|
|
20
|
+
let last: number | undefined;
|
|
21
|
+
let disposed = false;
|
|
22
|
+
// Without visibility observation, keep a static surface rather than doing
|
|
23
|
+
// unbounded work for previews that may be outside the viewport.
|
|
24
|
+
let visible = false;
|
|
25
|
+
const canAnimate = () => !disposed && visible && !document.hidden && motion?.matches === false;
|
|
26
|
+
|
|
27
|
+
function draw() {
|
|
28
|
+
const w = canvas!.clientWidth;
|
|
29
|
+
const h = canvas!.clientHeight;
|
|
30
|
+
if (!w || !h || document.hidden || disposed) return;
|
|
31
|
+
const d = Math.min(window.devicePixelRatio || 1, 2);
|
|
32
|
+
if (canvas!.width !== Math.round(w * d) || canvas!.height !== Math.round(h * d)) {
|
|
33
|
+
canvas!.width = Math.round(w * d);
|
|
34
|
+
canvas!.height = Math.round(h * d);
|
|
35
|
+
}
|
|
36
|
+
const style = getComputedStyle(canvas!);
|
|
37
|
+
const ink = style.getPropertyValue("--_og-preview-ink").trim() || "#bca3ef";
|
|
38
|
+
const glowColor = style.getPropertyValue("--_og-preview-glow").trim() || "#8f6ccc";
|
|
39
|
+
context.setTransform(d, 0, 0, d, 0, 0);
|
|
40
|
+
context.clearRect(0, 0, w, h);
|
|
41
|
+
const cx = w * (0.5 + 0.13 * Math.sin(time * 0.21));
|
|
42
|
+
const cy = h * (0.5 + 0.12 * Math.cos(time * 0.27));
|
|
43
|
+
const glow = context.createRadialGradient(cx, cy, 0, cx, cy, Math.max(w, h) * 0.65);
|
|
44
|
+
glow.addColorStop(0, glowColor);
|
|
45
|
+
glow.addColorStop(1, "transparent");
|
|
46
|
+
context.globalAlpha = 0.09;
|
|
47
|
+
context.fillStyle = glow;
|
|
48
|
+
context.fillRect(0, 0, w, h);
|
|
49
|
+
const rows = Math.ceil(h / 18) + 7;
|
|
50
|
+
const cols = Math.ceil(w / 18) + 7;
|
|
51
|
+
const points = Array.from({ length: rows }, (_row, r) =>
|
|
52
|
+
Array.from({ length: cols }, (_col, c) => {
|
|
53
|
+
const x = (c - 3) * 18;
|
|
54
|
+
const y = (r - 3) * 18;
|
|
55
|
+
const dist = Math.hypot((x - cx) * 0.8, y - cy);
|
|
56
|
+
const wave = Math.sin(dist * 0.032 - time * 0.95);
|
|
57
|
+
return {
|
|
58
|
+
x: x + ((x - cx) / Math.max(dist, 1)) * wave * 4,
|
|
59
|
+
y: y + ((y - cy) / Math.max(dist, 1)) * wave * 4,
|
|
60
|
+
l: Math.pow((wave + 1) / 2, 5),
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
context.strokeStyle = ink;
|
|
65
|
+
context.fillStyle = ink;
|
|
66
|
+
context.lineWidth = 0.65;
|
|
67
|
+
points.forEach((row, r) =>
|
|
68
|
+
row.forEach((p, c) => {
|
|
69
|
+
context.globalAlpha = 0.035 + p.l * 0.055;
|
|
70
|
+
context.beginPath();
|
|
71
|
+
if (c + 1 < cols) {
|
|
72
|
+
context.moveTo(p.x, p.y);
|
|
73
|
+
context.lineTo(row[c + 1]!.x, row[c + 1]!.y);
|
|
74
|
+
}
|
|
75
|
+
if (r + 1 < rows) {
|
|
76
|
+
context.moveTo(p.x, p.y);
|
|
77
|
+
context.lineTo(points[r + 1]![c]!.x, points[r + 1]![c]!.y);
|
|
78
|
+
}
|
|
79
|
+
context.stroke();
|
|
80
|
+
context.globalAlpha = 0.15 + p.l * 0.55;
|
|
81
|
+
context.beginPath();
|
|
82
|
+
context.arc(p.x, p.y, 0.85 + p.l * 0.65, 0, Math.PI * 2);
|
|
83
|
+
context.fill();
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
context.globalAlpha = 1;
|
|
87
|
+
canvas!.dataset.painted = "true";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stop() {
|
|
91
|
+
if (frame !== undefined) window.cancelAnimationFrame(frame);
|
|
92
|
+
frame = undefined;
|
|
93
|
+
last = undefined;
|
|
94
|
+
}
|
|
95
|
+
function tick(stamp: number) {
|
|
96
|
+
frame = undefined;
|
|
97
|
+
if (!canAnimate()) return;
|
|
98
|
+
if (last !== undefined) time += Math.min((stamp - last) / 1000, 0.05);
|
|
99
|
+
last = stamp;
|
|
100
|
+
draw();
|
|
101
|
+
frame = window.requestAnimationFrame(tick);
|
|
102
|
+
}
|
|
103
|
+
function refresh() {
|
|
104
|
+
stop();
|
|
105
|
+
if (disposed || document.hidden || (!visible && intersection)) return;
|
|
106
|
+
draw();
|
|
107
|
+
if (canAnimate() && typeof window.requestAnimationFrame === "function") {
|
|
108
|
+
frame = window.requestAnimationFrame(tick);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const intersection =
|
|
112
|
+
typeof IntersectionObserver === "function"
|
|
113
|
+
? new IntersectionObserver(([entry]) => {
|
|
114
|
+
visible = entry?.isIntersecting === true;
|
|
115
|
+
refresh();
|
|
116
|
+
})
|
|
117
|
+
: undefined;
|
|
118
|
+
intersection?.observe(canvas);
|
|
119
|
+
const resize = typeof ResizeObserver === "function" ? new ResizeObserver(refresh) : undefined;
|
|
120
|
+
resize?.observe(canvas);
|
|
121
|
+
// Observe only ancestors, not the document subtree. This also updates the
|
|
122
|
+
// static reduced-motion canvas when a host changes its local theme.
|
|
123
|
+
const theme =
|
|
124
|
+
typeof MutationObserver === "function" ? new MutationObserver(refresh) : undefined;
|
|
125
|
+
for (let ancestor = canvas.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
126
|
+
theme?.observe(ancestor, {
|
|
127
|
+
attributes: true,
|
|
128
|
+
attributeFilter: ["class", "style", "data-og-theme"],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
motion?.addEventListener?.("change", refresh);
|
|
132
|
+
document.addEventListener("visibilitychange", refresh);
|
|
133
|
+
window.addEventListener("resize", refresh);
|
|
134
|
+
// Old hosts get one still frame; observed surfaces wait until onscreen.
|
|
135
|
+
if (!intersection) draw();
|
|
136
|
+
return () => {
|
|
137
|
+
disposed = true;
|
|
138
|
+
stop();
|
|
139
|
+
intersection?.disconnect();
|
|
140
|
+
resize?.disconnect();
|
|
141
|
+
theme?.disconnect();
|
|
142
|
+
motion?.removeEventListener?.("change", refresh);
|
|
143
|
+
document.removeEventListener("visibilitychange", refresh);
|
|
144
|
+
window.removeEventListener("resize", refresh);
|
|
145
|
+
};
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className="og-preview-loading">
|
|
150
|
+
<canvas ref={ref} aria-hidden="true" />
|
|
151
|
+
<span className="og-preview-loading-label">Preparing preview…</span>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
package/src/connect-setup.tsx
CHANGED
|
@@ -35,6 +35,8 @@ export type ConnectSetupProps = {
|
|
|
35
35
|
controller: ConnectController;
|
|
36
36
|
/** Called synchronously in the click handler so a host can open a popup. */
|
|
37
37
|
onAuthorize: (attempt: ConnectAttempt) => void | Promise<unknown>;
|
|
38
|
+
/** Called synchronously on Continue; hosts can reserve a popup before advancing. */
|
|
39
|
+
onSelectAccount?: (accountId: string) => void | Promise<unknown>;
|
|
38
40
|
/** Host pagination must retain selections across pages and submit the final set. */
|
|
39
41
|
onBrowseResources?: (attempt: ConnectAttempt) => void | Promise<unknown>;
|
|
40
42
|
className?: string;
|
|
@@ -61,11 +63,13 @@ function ScopedSetup({
|
|
|
61
63
|
controller,
|
|
62
64
|
onAuthorize,
|
|
63
65
|
onBrowseResources,
|
|
66
|
+
onSelectAccount,
|
|
64
67
|
className,
|
|
65
68
|
authorizeLabel = "Authorize connection",
|
|
66
69
|
}: ConnectSetupProps) {
|
|
67
70
|
const view = useConnect(controller);
|
|
68
71
|
const [localError, setLocalError] = useState(false);
|
|
72
|
+
const [navigating, setNavigating] = useState(false);
|
|
69
73
|
const invoke = (operation: () => unknown | Promise<unknown>) => {
|
|
70
74
|
setLocalError(false);
|
|
71
75
|
try {
|
|
@@ -91,7 +95,7 @@ function ScopedSetup({
|
|
|
91
95
|
event.preventDefault();
|
|
92
96
|
const form = event.currentTarget;
|
|
93
97
|
const data = new FormData(form);
|
|
94
|
-
if (view.busy) return;
|
|
98
|
+
if (view.busy || navigating) return;
|
|
95
99
|
const key = crypto.randomUUID();
|
|
96
100
|
if (action.type === "credentials") {
|
|
97
101
|
const values = Object.fromEntries(
|
|
@@ -105,7 +109,15 @@ function ScopedSetup({
|
|
|
105
109
|
if (
|
|
106
110
|
action.accounts.some((account) => account.id === accountId && account.status !== "disabled")
|
|
107
111
|
)
|
|
108
|
-
invoke(() =>
|
|
112
|
+
invoke(async () => {
|
|
113
|
+
if (!onSelectAccount) return view.advance({ type: "account", accountId }, key);
|
|
114
|
+
setNavigating(true);
|
|
115
|
+
try {
|
|
116
|
+
await onSelectAccount(accountId);
|
|
117
|
+
} finally {
|
|
118
|
+
setNavigating(false);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
109
121
|
} else if (action.type === "select_resources") {
|
|
110
122
|
if (action.cursor) return;
|
|
111
123
|
const resourceIds = data.getAll("resource").map(String);
|
|
@@ -129,7 +141,7 @@ function ScopedSetup({
|
|
|
129
141
|
<section
|
|
130
142
|
className={["og-connect-setup", className].filter(Boolean).join(" ")}
|
|
131
143
|
aria-label="Connection setup"
|
|
132
|
-
aria-busy={view.busy}
|
|
144
|
+
aria-busy={view.busy || navigating}
|
|
133
145
|
>
|
|
134
146
|
<div className="og-connect-setup-summary">
|
|
135
147
|
<p className="og-connect-setup-scope">
|
|
@@ -154,7 +166,7 @@ function ScopedSetup({
|
|
|
154
166
|
)}
|
|
155
167
|
{!terminal && (
|
|
156
168
|
<form key={`${attempt.id}:${attempt.revision}`} onSubmit={submit} autoComplete="off">
|
|
157
|
-
<fieldset disabled={view.busy}>
|
|
169
|
+
<fieldset disabled={view.busy || navigating}>
|
|
158
170
|
{["credentials", "select_account", "select_resources", "preview"].includes(
|
|
159
171
|
action.type,
|
|
160
172
|
) ? (
|
|
@@ -261,7 +273,7 @@ function ScopedSetup({
|
|
|
261
273
|
className="og-connect-setup-primary"
|
|
262
274
|
onClick={() => invoke(() => onAuthorize(structuredClone(attempt)))}
|
|
263
275
|
>
|
|
264
|
-
{authorizeLabel}
|
|
276
|
+
{navigating ? "Waiting for authorization…" : authorizeLabel}
|
|
265
277
|
</button>
|
|
266
278
|
)}
|
|
267
279
|
{action.type === "wait" && (
|
|
@@ -1478,6 +1478,9 @@ export function useComposer(
|
|
|
1478
1478
|
...(wireInput.connectionAuthorities
|
|
1479
1479
|
? { connectionAuthorities: wireInput.connectionAuthorities }
|
|
1480
1480
|
: {}),
|
|
1481
|
+
...(wireInput.selectedHostMcpDelegations
|
|
1482
|
+
? { selectedHostMcpDelegations: wireInput.selectedHostMcpDelegations }
|
|
1483
|
+
: {}),
|
|
1481
1484
|
...(wireInput.personalResourceAttachment
|
|
1482
1485
|
? { personalResourceAttachment: wireInput.personalResourceAttachment }
|
|
1483
1486
|
: {}),
|
|
@@ -1754,6 +1757,9 @@ export function useComposer(
|
|
|
1754
1757
|
...(input.connectionAuthorities
|
|
1755
1758
|
? { connectionAuthorities: input.connectionAuthorities }
|
|
1756
1759
|
: {}),
|
|
1760
|
+
...(input.selectedHostMcpDelegations
|
|
1761
|
+
? { selectedHostMcpDelegations: input.selectedHostMcpDelegations }
|
|
1762
|
+
: {}),
|
|
1757
1763
|
...(input.personalResourceAttachment
|
|
1758
1764
|
? { personalResourceAttachment: input.personalResourceAttachment }
|
|
1759
1765
|
: {}),
|
|
@@ -1105,16 +1105,14 @@ export function RealtimeVoiceModelPanel(props: {
|
|
|
1105
1105
|
|
|
1106
1106
|
return (
|
|
1107
1107
|
<div className="flex min-h-0 flex-1 flex-col" data-testid="realtime-voice-model-panel">
|
|
1108
|
-
<div className="flex shrink-0 items-
|
|
1108
|
+
<div className="flex min-h-10 shrink-0 items-center gap-2 border-b border-og-border px-2 pb-2">
|
|
1109
1109
|
{props.leading}
|
|
1110
|
-
<div className="min-w-0">
|
|
1111
|
-
<div className="text-og-menu font-medium text-og-fg">Voice model</div>
|
|
1112
|
-
<p className="mt-0.5 text-og-control text-og-fg-subtle">
|
|
1113
|
-
Used when you start a voice conversation.
|
|
1114
|
-
</p>
|
|
1115
|
-
</div>
|
|
1110
|
+
<div className="min-w-0 flex-1 text-og-menu font-medium text-og-fg">Voice model</div>
|
|
1116
1111
|
</div>
|
|
1117
|
-
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain
|
|
1112
|
+
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain p-2">
|
|
1113
|
+
<p className="mb-3 text-og-control text-og-fg-muted">
|
|
1114
|
+
Used when you start a voice conversation.
|
|
1115
|
+
</p>
|
|
1118
1116
|
<RealtimeModelPickerMenu
|
|
1119
1117
|
models={props.models}
|
|
1120
1118
|
selectedModel={props.selectedModel}
|
package/styles/compiled.css
CHANGED
|
@@ -608,6 +608,54 @@
|
|
|
608
608
|
--_og-session-chrome-ease: var(--og-session-chrome-ease, var(--og-ease-out));
|
|
609
609
|
--_og-session-chrome-row-hover: var(--og-session-chrome-row-hover, var(--og-color-surface-3));
|
|
610
610
|
}
|
|
611
|
+
:where(.og-root).og-preview-loading,:where(.og-root) .og-preview-loading {
|
|
612
|
+
--_og-preview-ink: #bca3ef;
|
|
613
|
+
--_og-preview-glow: #8f6ccc;
|
|
614
|
+
position: relative;
|
|
615
|
+
width: 100%;
|
|
616
|
+
overflow: hidden;
|
|
617
|
+
border-radius: 10px;
|
|
618
|
+
background: #17161d;
|
|
619
|
+
container: og-preview / inline-size;
|
|
620
|
+
}
|
|
621
|
+
:where(.og-root):is([data-og-theme="light"], .og-light) .og-preview-loading, :is([data-og-theme="light"], .og-light) :where(.og-root) .og-preview-loading,:where(.og-root) :is([data-og-theme="light"], .og-light) .og-preview-loading {
|
|
622
|
+
--_og-preview-ink: #76629c;
|
|
623
|
+
--_og-preview-glow: #ba8ee0;
|
|
624
|
+
background: #faf9fd;
|
|
625
|
+
}
|
|
626
|
+
:where(.og-root).og-preview-loading canvas,:where(.og-root) .og-preview-loading canvas {
|
|
627
|
+
display: block;
|
|
628
|
+
width: 100%;
|
|
629
|
+
height: 320px;
|
|
630
|
+
background-image: radial-gradient(circle, var(--_og-preview-ink) 1px, transparent 1px);
|
|
631
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
632
|
+
background-image: radial-gradient(circle, color-mix(in srgb, var(--_og-preview-ink) 30%, transparent) 1px, transparent 1px);
|
|
633
|
+
}
|
|
634
|
+
background-size: 18px 18px;
|
|
635
|
+
}
|
|
636
|
+
:where(.og-root).og-preview-loading canvas[data-painted="true"],:where(.og-root) .og-preview-loading canvas[data-painted="true"] {
|
|
637
|
+
background-image: none;
|
|
638
|
+
}
|
|
639
|
+
:where(.og-root).og-preview-loading-label,:where(.og-root) .og-preview-loading-label {
|
|
640
|
+
position: absolute;
|
|
641
|
+
left: 16px;
|
|
642
|
+
bottom: 14px;
|
|
643
|
+
color: var(--og-color-fg-muted);
|
|
644
|
+
font-family: var(--og-font-sans);
|
|
645
|
+
font-size: var(--og-font-size-xs);
|
|
646
|
+
line-height: var(--og-line-height-xs);
|
|
647
|
+
font-weight: 400;
|
|
648
|
+
}
|
|
649
|
+
@container og-preview (max-width: 500px) {
|
|
650
|
+
:where(.og-root).og-preview-loading canvas,:where(.og-root) .og-preview-loading canvas {
|
|
651
|
+
height: 260px;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
@media (max-width: 500px) {
|
|
655
|
+
:where(.og-root).og-preview-loading canvas,:where(.og-root) .og-preview-loading canvas {
|
|
656
|
+
height: 260px;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
611
659
|
.og-root :where([hidden]:not([hidden="until-found"])) {
|
|
612
660
|
display: none !important;
|
|
613
661
|
}
|
package/styles/index.css
CHANGED
|
@@ -17,6 +17,51 @@
|
|
|
17
17
|
@import "./tokens.css";
|
|
18
18
|
@import "./effective-tokens.css";
|
|
19
19
|
|
|
20
|
+
/* Full-surface preview preparation. Private palette preserves the accepted
|
|
21
|
+
ripple study; the steady label uses the host's typography and text tokens. */
|
|
22
|
+
.og-preview-loading {
|
|
23
|
+
--_og-preview-ink: #bca3ef;
|
|
24
|
+
--_og-preview-glow: #8f6ccc;
|
|
25
|
+
position: relative;
|
|
26
|
+
width: 100%;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
border-radius: 10px;
|
|
29
|
+
background: #17161d;
|
|
30
|
+
container: og-preview / inline-size;
|
|
31
|
+
}
|
|
32
|
+
.og-root:is([data-og-theme="light"], .og-light) .og-preview-loading,
|
|
33
|
+
:is([data-og-theme="light"], .og-light) .og-root .og-preview-loading,
|
|
34
|
+
.og-root :is([data-og-theme="light"], .og-light) .og-preview-loading {
|
|
35
|
+
--_og-preview-ink: #76629c;
|
|
36
|
+
--_og-preview-glow: #ba8ee0;
|
|
37
|
+
background: #faf9fd;
|
|
38
|
+
}
|
|
39
|
+
.og-preview-loading canvas {
|
|
40
|
+
display: block;
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 320px;
|
|
43
|
+
/* Non-canvas / pre-effect fallback, removed only after a successful paint. */
|
|
44
|
+
background-image: radial-gradient(circle, color-mix(in srgb, var(--_og-preview-ink) 30%, transparent) 1px, transparent 1px);
|
|
45
|
+
background-size: 18px 18px;
|
|
46
|
+
}
|
|
47
|
+
.og-preview-loading canvas[data-painted="true"] { background-image: none; }
|
|
48
|
+
.og-preview-loading-label {
|
|
49
|
+
position: absolute;
|
|
50
|
+
left: 16px;
|
|
51
|
+
bottom: 14px;
|
|
52
|
+
color: var(--og-color-fg-muted);
|
|
53
|
+
font-family: var(--og-font-sans);
|
|
54
|
+
font-size: var(--og-font-size-xs);
|
|
55
|
+
line-height: var(--og-line-height-xs);
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
}
|
|
58
|
+
@container og-preview (max-width: 500px) {
|
|
59
|
+
.og-preview-loading canvas { height: 260px; }
|
|
60
|
+
}
|
|
61
|
+
@media (max-width: 500px) {
|
|
62
|
+
.og-preview-loading canvas { height: 260px; }
|
|
63
|
+
}
|
|
64
|
+
|
|
20
65
|
/* Standalone embeds do not necessarily load Tailwind Preflight. Keep the
|
|
21
66
|
control reset inside SDK roots, before utilities, without resetting the host.
|
|
22
67
|
:where keeps controls at the same specificity as component utilities. */
|