@cloudvoyant/helix-react 0.16.0 → 0.17.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/dist/index.cjs +1159 -0
- package/dist/index.d.cts +133 -2
- package/dist/index.d.ts +133 -2
- package/dist/index.js +1226 -0
- package/package.json +17 -4
package/dist/index.js
CHANGED
|
@@ -2176,6 +2176,1206 @@ function Mermaid({ code, svg, className, ...props }) {
|
|
|
2176
2176
|
}
|
|
2177
2177
|
);
|
|
2178
2178
|
}
|
|
2179
|
+
|
|
2180
|
+
// src/clipboard.tsx
|
|
2181
|
+
import { ClipboardRoot, ClipboardTrigger, ClipboardIndicator } from "@ark-ui/react/clipboard";
|
|
2182
|
+
import { clipboardTriggerBase, cn as cn59 } from "@cloudvoyant/helix";
|
|
2183
|
+
import { jsx as jsx59, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2184
|
+
function CopyIcon() {
|
|
2185
|
+
return /* @__PURE__ */ jsxs18(
|
|
2186
|
+
"svg",
|
|
2187
|
+
{
|
|
2188
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2189
|
+
viewBox: "0 0 24 24",
|
|
2190
|
+
fill: "none",
|
|
2191
|
+
stroke: "currentColor",
|
|
2192
|
+
strokeWidth: "2",
|
|
2193
|
+
strokeLinecap: "round",
|
|
2194
|
+
strokeLinejoin: "round",
|
|
2195
|
+
"aria-hidden": "true",
|
|
2196
|
+
children: [
|
|
2197
|
+
/* @__PURE__ */ jsx59("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
|
|
2198
|
+
/* @__PURE__ */ jsx59("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
|
|
2199
|
+
]
|
|
2200
|
+
}
|
|
2201
|
+
);
|
|
2202
|
+
}
|
|
2203
|
+
function CheckIcon() {
|
|
2204
|
+
return /* @__PURE__ */ jsx59(
|
|
2205
|
+
"svg",
|
|
2206
|
+
{
|
|
2207
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2208
|
+
viewBox: "0 0 24 24",
|
|
2209
|
+
fill: "none",
|
|
2210
|
+
stroke: "currentColor",
|
|
2211
|
+
strokeWidth: "2",
|
|
2212
|
+
strokeLinecap: "round",
|
|
2213
|
+
strokeLinejoin: "round",
|
|
2214
|
+
"aria-hidden": "true",
|
|
2215
|
+
children: /* @__PURE__ */ jsx59("path", { d: "M20 6 9 17l-5-5" })
|
|
2216
|
+
}
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
function CopyButton({ value, label = "Copy", className, ...props }) {
|
|
2220
|
+
return /* @__PURE__ */ jsx59(ClipboardRoot, { value, ...props, children: /* @__PURE__ */ jsx59(ClipboardTrigger, { className: cn59(clipboardTriggerBase, className), "aria-label": label, children: /* @__PURE__ */ jsx59(ClipboardIndicator, { copied: /* @__PURE__ */ jsx59(CheckIcon, {}), children: /* @__PURE__ */ jsx59(CopyIcon, {}) }) }) });
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// src/radio-group.tsx
|
|
2224
|
+
import {
|
|
2225
|
+
RadioGroupRoot,
|
|
2226
|
+
RadioGroupItem as ArkRadioGroupItem,
|
|
2227
|
+
RadioGroupItemControl,
|
|
2228
|
+
RadioGroupItemText,
|
|
2229
|
+
RadioGroupItemHiddenInput,
|
|
2230
|
+
useRadioGroupContext,
|
|
2231
|
+
useRadioGroupItemContext
|
|
2232
|
+
} from "@ark-ui/react/radio-group";
|
|
2233
|
+
import {
|
|
2234
|
+
radioGroupRootBase,
|
|
2235
|
+
radioGroupItemControlBase,
|
|
2236
|
+
radioGroupItemTextBase,
|
|
2237
|
+
cn as cn60
|
|
2238
|
+
} from "@cloudvoyant/helix";
|
|
2239
|
+
import { jsx as jsx60, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2240
|
+
function RadioGroup({ className, ...props }) {
|
|
2241
|
+
return /* @__PURE__ */ jsx60(RadioGroupRoot, { className: cn60(radioGroupRootBase, className), ...props });
|
|
2242
|
+
}
|
|
2243
|
+
function RadioGroupItem({ className, children, ...props }) {
|
|
2244
|
+
return /* @__PURE__ */ jsxs19(ArkRadioGroupItem, { className: cn60("flex cursor-pointer items-center gap-2.5", className), ...props, children: [
|
|
2245
|
+
/* @__PURE__ */ jsx60(RadioGroupItemControl, { className: radioGroupItemControlBase, children: /* @__PURE__ */ jsx60("span", { className: "size-1.5 rounded-full bg-current hidden [[data-state=checked]>&]:block" }) }),
|
|
2246
|
+
/* @__PURE__ */ jsx60(RadioGroupItemText, { className: radioGroupItemTextBase, children }),
|
|
2247
|
+
/* @__PURE__ */ jsx60(RadioGroupItemHiddenInput, {})
|
|
2248
|
+
] });
|
|
2249
|
+
}
|
|
2250
|
+
var useRadioGroup = useRadioGroupContext;
|
|
2251
|
+
|
|
2252
|
+
// src/notice.tsx
|
|
2253
|
+
import { ark as ark36 } from "@ark-ui/react/factory";
|
|
2254
|
+
import { noticeVariants, noticeTitleBase, noticeDescriptionBase, cn as cn61 } from "@cloudvoyant/helix";
|
|
2255
|
+
import { jsx as jsx61, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2256
|
+
function InfoIcon() {
|
|
2257
|
+
return /* @__PURE__ */ jsxs20(
|
|
2258
|
+
"svg",
|
|
2259
|
+
{
|
|
2260
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2261
|
+
viewBox: "0 0 24 24",
|
|
2262
|
+
fill: "none",
|
|
2263
|
+
stroke: "currentColor",
|
|
2264
|
+
strokeWidth: "2",
|
|
2265
|
+
strokeLinecap: "round",
|
|
2266
|
+
strokeLinejoin: "round",
|
|
2267
|
+
"aria-hidden": "true",
|
|
2268
|
+
children: [
|
|
2269
|
+
/* @__PURE__ */ jsx61("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2270
|
+
/* @__PURE__ */ jsx61("path", { d: "M12 16v-4" }),
|
|
2271
|
+
/* @__PURE__ */ jsx61("path", { d: "M12 8h.01" })
|
|
2272
|
+
]
|
|
2273
|
+
}
|
|
2274
|
+
);
|
|
2275
|
+
}
|
|
2276
|
+
function CheckCircleIcon() {
|
|
2277
|
+
return /* @__PURE__ */ jsxs20(
|
|
2278
|
+
"svg",
|
|
2279
|
+
{
|
|
2280
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2281
|
+
viewBox: "0 0 24 24",
|
|
2282
|
+
fill: "none",
|
|
2283
|
+
stroke: "currentColor",
|
|
2284
|
+
strokeWidth: "2",
|
|
2285
|
+
strokeLinecap: "round",
|
|
2286
|
+
strokeLinejoin: "round",
|
|
2287
|
+
"aria-hidden": "true",
|
|
2288
|
+
children: [
|
|
2289
|
+
/* @__PURE__ */ jsx61("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2290
|
+
/* @__PURE__ */ jsx61("path", { d: "m9 12 2 2 4-4" })
|
|
2291
|
+
]
|
|
2292
|
+
}
|
|
2293
|
+
);
|
|
2294
|
+
}
|
|
2295
|
+
function TriangleAlertIcon() {
|
|
2296
|
+
return /* @__PURE__ */ jsxs20(
|
|
2297
|
+
"svg",
|
|
2298
|
+
{
|
|
2299
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2300
|
+
viewBox: "0 0 24 24",
|
|
2301
|
+
fill: "none",
|
|
2302
|
+
stroke: "currentColor",
|
|
2303
|
+
strokeWidth: "2",
|
|
2304
|
+
strokeLinecap: "round",
|
|
2305
|
+
strokeLinejoin: "round",
|
|
2306
|
+
"aria-hidden": "true",
|
|
2307
|
+
children: [
|
|
2308
|
+
/* @__PURE__ */ jsx61("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
|
|
2309
|
+
/* @__PURE__ */ jsx61("path", { d: "M12 9v4" }),
|
|
2310
|
+
/* @__PURE__ */ jsx61("path", { d: "M12 17h.01" })
|
|
2311
|
+
]
|
|
2312
|
+
}
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
function CircleXIcon() {
|
|
2316
|
+
return /* @__PURE__ */ jsxs20(
|
|
2317
|
+
"svg",
|
|
2318
|
+
{
|
|
2319
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2320
|
+
viewBox: "0 0 24 24",
|
|
2321
|
+
fill: "none",
|
|
2322
|
+
stroke: "currentColor",
|
|
2323
|
+
strokeWidth: "2",
|
|
2324
|
+
strokeLinecap: "round",
|
|
2325
|
+
strokeLinejoin: "round",
|
|
2326
|
+
"aria-hidden": "true",
|
|
2327
|
+
children: [
|
|
2328
|
+
/* @__PURE__ */ jsx61("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2329
|
+
/* @__PURE__ */ jsx61("path", { d: "m15 9-6 6" }),
|
|
2330
|
+
/* @__PURE__ */ jsx61("path", { d: "m9 9 6 6" })
|
|
2331
|
+
]
|
|
2332
|
+
}
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
var noticeIcons = {
|
|
2336
|
+
info: InfoIcon,
|
|
2337
|
+
success: CheckCircleIcon,
|
|
2338
|
+
warning: TriangleAlertIcon,
|
|
2339
|
+
error: CircleXIcon
|
|
2340
|
+
};
|
|
2341
|
+
function Notice({ variant = "info", title, children, className, ...props }) {
|
|
2342
|
+
const Icon = variant !== null && variant !== void 0 && variant !== "none" ? noticeIcons[variant] : null;
|
|
2343
|
+
const role = variant === "error" ? "alert" : variant === "none" ? void 0 : "status";
|
|
2344
|
+
return /* @__PURE__ */ jsxs20(ark36.div, { role, className: cn61(noticeVariants({ variant }), className), ...props, children: [
|
|
2345
|
+
Icon ? /* @__PURE__ */ jsx61(Icon, {}) : null,
|
|
2346
|
+
title ? /* @__PURE__ */ jsx61(ark36.div, { className: noticeTitleBase, children: title }) : null,
|
|
2347
|
+
children ? /* @__PURE__ */ jsx61(ark36.div, { className: noticeDescriptionBase, children }) : null
|
|
2348
|
+
] });
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
// src/figure.tsx
|
|
2352
|
+
import { ark as ark37 } from "@ark-ui/react/factory";
|
|
2353
|
+
import { figureRootBase, figureImageBase, figureCaptionBase, cn as cn62 } from "@cloudvoyant/helix";
|
|
2354
|
+
import { jsx as jsx62, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2355
|
+
function Figure({ src, alt, img, caption, className, ...props }) {
|
|
2356
|
+
return /* @__PURE__ */ jsxs21(ark37.figure, { className: cn62(figureRootBase, className), ...props, children: [
|
|
2357
|
+
img ?? (src ? /* @__PURE__ */ jsx62(ark37.img, { src, alt, loading: "lazy", className: figureImageBase }) : null),
|
|
2358
|
+
caption ? /* @__PURE__ */ jsx62(ark37.figcaption, { className: figureCaptionBase, children: caption }) : null
|
|
2359
|
+
] });
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
// src/reveal.tsx
|
|
2363
|
+
import { CollapsibleRoot as CollapsibleRoot2, CollapsibleTrigger as CollapsibleTrigger2, CollapsibleContent as CollapsibleContent2 } from "@ark-ui/react/collapsible";
|
|
2364
|
+
import { revealRootBase, revealTriggerBase, revealChevronBase, revealContentBase, cn as cn63 } from "@cloudvoyant/helix";
|
|
2365
|
+
import { jsx as jsx63, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2366
|
+
function ChevronRightIcon({ className }) {
|
|
2367
|
+
return /* @__PURE__ */ jsx63(
|
|
2368
|
+
"svg",
|
|
2369
|
+
{
|
|
2370
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2371
|
+
viewBox: "0 0 24 24",
|
|
2372
|
+
fill: "none",
|
|
2373
|
+
stroke: "currentColor",
|
|
2374
|
+
strokeWidth: "2",
|
|
2375
|
+
strokeLinecap: "round",
|
|
2376
|
+
strokeLinejoin: "round",
|
|
2377
|
+
"aria-hidden": "true",
|
|
2378
|
+
className,
|
|
2379
|
+
children: /* @__PURE__ */ jsx63("path", { d: "m9 18 6-6-6-6" })
|
|
2380
|
+
}
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
function Reveal({ question, children, className, ...props }) {
|
|
2384
|
+
return /* @__PURE__ */ jsxs22(CollapsibleRoot2, { className: cn63(revealRootBase, className), ...props, children: [
|
|
2385
|
+
/* @__PURE__ */ jsxs22(CollapsibleTrigger2, { className: revealTriggerBase, children: [
|
|
2386
|
+
/* @__PURE__ */ jsx63(ChevronRightIcon, { className: revealChevronBase }),
|
|
2387
|
+
/* @__PURE__ */ jsx63("span", { children: question })
|
|
2388
|
+
] }),
|
|
2389
|
+
/* @__PURE__ */ jsx63(CollapsibleContent2, { className: revealContentBase, children })
|
|
2390
|
+
] });
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
// src/prevnext.tsx
|
|
2394
|
+
import { ark as ark38 } from "@ark-ui/react/factory";
|
|
2395
|
+
import {
|
|
2396
|
+
prevNextRootBase,
|
|
2397
|
+
prevNextLinkBase,
|
|
2398
|
+
prevNextLinkNextBase,
|
|
2399
|
+
prevNextDirectionBase,
|
|
2400
|
+
prevNextTitleBase,
|
|
2401
|
+
prevNextSpacerBase,
|
|
2402
|
+
cn as cn64
|
|
2403
|
+
} from "@cloudvoyant/helix";
|
|
2404
|
+
import { jsx as jsx64, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2405
|
+
function PrevNext({ prev, next, className, ...props }) {
|
|
2406
|
+
if (!prev && !next) return null;
|
|
2407
|
+
return /* @__PURE__ */ jsxs23(ark38.nav, { className: cn64(prevNextRootBase, className), "aria-label": "Previous and next pages", ...props, children: [
|
|
2408
|
+
prev ? /* @__PURE__ */ jsxs23(ark38.a, { href: prev.href, className: prevNextLinkBase, children: [
|
|
2409
|
+
/* @__PURE__ */ jsx64(ark38.span, { className: prevNextDirectionBase, children: "Previous" }),
|
|
2410
|
+
/* @__PURE__ */ jsxs23(ark38.span, { className: prevNextTitleBase, children: [
|
|
2411
|
+
"\u2190 ",
|
|
2412
|
+
prev.title
|
|
2413
|
+
] })
|
|
2414
|
+
] }) : /* @__PURE__ */ jsx64(ark38.span, { className: prevNextSpacerBase }),
|
|
2415
|
+
next ? /* @__PURE__ */ jsxs23(ark38.a, { href: next.href, className: cn64(prevNextLinkBase, prevNextLinkNextBase), children: [
|
|
2416
|
+
/* @__PURE__ */ jsx64(ark38.span, { className: prevNextDirectionBase, children: "Next" }),
|
|
2417
|
+
/* @__PURE__ */ jsxs23(ark38.span, { className: prevNextTitleBase, children: [
|
|
2418
|
+
next.title,
|
|
2419
|
+
" \u2192"
|
|
2420
|
+
] })
|
|
2421
|
+
] }) : /* @__PURE__ */ jsx64(ark38.span, { className: prevNextSpacerBase })
|
|
2422
|
+
] });
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// src/latex.tsx
|
|
2426
|
+
import { ark as ark39 } from "@ark-ui/react/factory";
|
|
2427
|
+
import {
|
|
2428
|
+
latexRootBase,
|
|
2429
|
+
latexDisplayBase,
|
|
2430
|
+
latexInlineBase,
|
|
2431
|
+
latexErrorBase,
|
|
2432
|
+
toLaTeX,
|
|
2433
|
+
cn as cn65
|
|
2434
|
+
} from "@cloudvoyant/helix";
|
|
2435
|
+
import { jsx as jsx65 } from "react/jsx-runtime";
|
|
2436
|
+
function LaTeX({ latex, html, className, ...props }) {
|
|
2437
|
+
let markup;
|
|
2438
|
+
let error = false;
|
|
2439
|
+
if (html !== void 0) {
|
|
2440
|
+
markup = html;
|
|
2441
|
+
} else {
|
|
2442
|
+
try {
|
|
2443
|
+
markup = toLaTeX(latex, { displayMode: true });
|
|
2444
|
+
} catch {
|
|
2445
|
+
markup = latex;
|
|
2446
|
+
error = true;
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
return /* @__PURE__ */ jsx65(
|
|
2450
|
+
ark39.div,
|
|
2451
|
+
{
|
|
2452
|
+
...props,
|
|
2453
|
+
"data-latex-state": error ? "error" : "done",
|
|
2454
|
+
className: cn65(latexRootBase, latexDisplayBase, className),
|
|
2455
|
+
children: error ? /* @__PURE__ */ jsx65("code", { className: latexErrorBase, children: latex }) : /* @__PURE__ */ jsx65("span", { dangerouslySetInnerHTML: { __html: markup } })
|
|
2456
|
+
}
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
function InlineLaTeX({ latex, html, className, ...props }) {
|
|
2460
|
+
let markup;
|
|
2461
|
+
let error = false;
|
|
2462
|
+
if (html !== void 0) {
|
|
2463
|
+
markup = html;
|
|
2464
|
+
} else {
|
|
2465
|
+
try {
|
|
2466
|
+
markup = toLaTeX(latex);
|
|
2467
|
+
} catch {
|
|
2468
|
+
markup = latex;
|
|
2469
|
+
error = true;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
return /* @__PURE__ */ jsx65(
|
|
2473
|
+
ark39.span,
|
|
2474
|
+
{
|
|
2475
|
+
...props,
|
|
2476
|
+
"data-latex-state": error ? "error" : "done",
|
|
2477
|
+
className: cn65(latexRootBase, latexInlineBase, className),
|
|
2478
|
+
children: error ? /* @__PURE__ */ jsx65("code", { className: latexErrorBase, children: latex }) : /* @__PURE__ */ jsx65("span", { dangerouslySetInnerHTML: { __html: markup } })
|
|
2479
|
+
}
|
|
2480
|
+
);
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
// src/code-block.tsx
|
|
2484
|
+
import { createContext as createContext6, useContext as useContext6, useEffect as useEffect4, useState as useState4 } from "react";
|
|
2485
|
+
import { ark as ark40 } from "@ark-ui/react/factory";
|
|
2486
|
+
import {
|
|
2487
|
+
codeBlockRootBase,
|
|
2488
|
+
codeBlockHeaderBase,
|
|
2489
|
+
codeBlockTitleBase,
|
|
2490
|
+
codeBlockBodyBase,
|
|
2491
|
+
codeBlockBodyScrollableBase,
|
|
2492
|
+
codeBlockContentBase,
|
|
2493
|
+
codeBlockLineNumbersBase,
|
|
2494
|
+
codeBlockHighlightBase,
|
|
2495
|
+
codeBlockLoadingBase,
|
|
2496
|
+
codeBlockSpinnerBase,
|
|
2497
|
+
codeBlockCopyFloatBase,
|
|
2498
|
+
injectCodeBlockStyles,
|
|
2499
|
+
renderCodeToHtml,
|
|
2500
|
+
cn as cn66
|
|
2501
|
+
} from "@cloudvoyant/helix";
|
|
2502
|
+
import { Fragment as Fragment5, jsx as jsx66, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2503
|
+
var CodeBlockContext = createContext6(null);
|
|
2504
|
+
function useCodeBlock() {
|
|
2505
|
+
const ctx = useContext6(CodeBlockContext);
|
|
2506
|
+
if (!ctx) throw new Error("CodeBlock parts must be rendered inside a <CodeBlock>.");
|
|
2507
|
+
return ctx;
|
|
2508
|
+
}
|
|
2509
|
+
function CodeRenderer({ code, language, html, showLineNumbers, scrollable, maxHeight, highlightLines }) {
|
|
2510
|
+
const [rendered, setRendered] = useState4(html ?? null);
|
|
2511
|
+
useEffect4(() => {
|
|
2512
|
+
injectCodeBlockStyles();
|
|
2513
|
+
}, []);
|
|
2514
|
+
const highlightKey = highlightLines?.join(",");
|
|
2515
|
+
useEffect4(() => {
|
|
2516
|
+
if (html !== void 0) return;
|
|
2517
|
+
let cancelled = false;
|
|
2518
|
+
setRendered(null);
|
|
2519
|
+
renderCodeToHtml(code, language, highlightLines).then((h) => {
|
|
2520
|
+
if (!cancelled) setRendered(h);
|
|
2521
|
+
});
|
|
2522
|
+
return () => {
|
|
2523
|
+
cancelled = true;
|
|
2524
|
+
};
|
|
2525
|
+
}, [code, language, html, highlightKey]);
|
|
2526
|
+
return /* @__PURE__ */ jsx66(
|
|
2527
|
+
"div",
|
|
2528
|
+
{
|
|
2529
|
+
className: cn66(codeBlockBodyBase, scrollable && codeBlockBodyScrollableBase),
|
|
2530
|
+
style: scrollable ? { maxHeight: `${maxHeight}px` } : void 0,
|
|
2531
|
+
children: rendered !== null ? /* @__PURE__ */ jsx66(
|
|
2532
|
+
"div",
|
|
2533
|
+
{
|
|
2534
|
+
dangerouslySetInnerHTML: { __html: rendered },
|
|
2535
|
+
className: cn66(
|
|
2536
|
+
codeBlockContentBase,
|
|
2537
|
+
showLineNumbers && codeBlockLineNumbersBase,
|
|
2538
|
+
highlightLines?.length && codeBlockHighlightBase
|
|
2539
|
+
)
|
|
2540
|
+
}
|
|
2541
|
+
) : /* @__PURE__ */ jsx66("div", { className: codeBlockLoadingBase, children: /* @__PURE__ */ jsx66("div", { className: codeBlockSpinnerBase }) })
|
|
2542
|
+
}
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
function CodeBlock({
|
|
2546
|
+
code,
|
|
2547
|
+
language = "tsx",
|
|
2548
|
+
filename,
|
|
2549
|
+
html,
|
|
2550
|
+
showLineNumbers = false,
|
|
2551
|
+
scrollable = false,
|
|
2552
|
+
maxHeight = 400,
|
|
2553
|
+
highlightLines,
|
|
2554
|
+
className,
|
|
2555
|
+
children,
|
|
2556
|
+
...props
|
|
2557
|
+
}) {
|
|
2558
|
+
const ctx = { code, language, html, showLineNumbers, scrollable, maxHeight, highlightLines };
|
|
2559
|
+
return /* @__PURE__ */ jsx66(CodeBlockContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx66(ark40.div, { "data-code-block": true, className: cn66(codeBlockRootBase, className), ...props, children: children ?? /* @__PURE__ */ jsxs24(Fragment5, { children: [
|
|
2560
|
+
/* @__PURE__ */ jsxs24(CodeBlockHeader, { children: [
|
|
2561
|
+
/* @__PURE__ */ jsx66(CodeBlockTitle, { children: filename ?? language }),
|
|
2562
|
+
/* @__PURE__ */ jsx66("div", { className: "ml-auto", children: /* @__PURE__ */ jsx66(CodeBlockCopyButton, {}) })
|
|
2563
|
+
] }),
|
|
2564
|
+
/* @__PURE__ */ jsx66(CodeBlockContent, {})
|
|
2565
|
+
] }) }) });
|
|
2566
|
+
}
|
|
2567
|
+
function CodeBlockHeader({ className, children, ...props }) {
|
|
2568
|
+
return /* @__PURE__ */ jsx66(ark40.div, { className: cn66(codeBlockHeaderBase, className), ...props, children });
|
|
2569
|
+
}
|
|
2570
|
+
function CodeBlockTitle({ className, children, ...props }) {
|
|
2571
|
+
return /* @__PURE__ */ jsx66(ark40.span, { className: cn66(codeBlockTitleBase, className), ...props, children });
|
|
2572
|
+
}
|
|
2573
|
+
function CodeBlockCopyButton({ label = "Copy code", className, ...props }) {
|
|
2574
|
+
const { code } = useCodeBlock();
|
|
2575
|
+
return /* @__PURE__ */ jsx66(ark40.div, { className, ...props, children: /* @__PURE__ */ jsx66(CopyButton, { value: code, label }) });
|
|
2576
|
+
}
|
|
2577
|
+
function CodeBlockContent({ className, html, ...props }) {
|
|
2578
|
+
const ctx = useCodeBlock();
|
|
2579
|
+
return /* @__PURE__ */ jsx66(ark40.div, { className, ...props, children: /* @__PURE__ */ jsx66(CodeRenderer, { ...ctx, html: html ?? ctx.html }) });
|
|
2580
|
+
}
|
|
2581
|
+
function MultiFileCodeBlock({
|
|
2582
|
+
files,
|
|
2583
|
+
showLineNumbers = false,
|
|
2584
|
+
scrollable = false,
|
|
2585
|
+
maxHeight = 400,
|
|
2586
|
+
className,
|
|
2587
|
+
...props
|
|
2588
|
+
}) {
|
|
2589
|
+
const [active, setActive] = useState4(files[0]?.filename ?? "");
|
|
2590
|
+
const file = files.find((f) => f.filename === active) ?? files[0];
|
|
2591
|
+
useEffect4(() => {
|
|
2592
|
+
if (!files.some((f) => f.filename === active)) {
|
|
2593
|
+
setActive(files[0]?.filename ?? "");
|
|
2594
|
+
}
|
|
2595
|
+
}, [files, active]);
|
|
2596
|
+
return /* @__PURE__ */ jsxs24(ark40.div, { "data-code-block": true, className: cn66(codeBlockRootBase, className), ...props, children: [
|
|
2597
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex items-center justify-between border-b bg-muted/50", children: [
|
|
2598
|
+
/* @__PURE__ */ jsx66("div", { role: "tablist", "aria-label": "Files", className: "flex overflow-x-auto no-scrollbar pl-1", children: files.map((f) => /* @__PURE__ */ jsx66(
|
|
2599
|
+
"button",
|
|
2600
|
+
{
|
|
2601
|
+
role: "tab",
|
|
2602
|
+
"aria-selected": active === f.filename,
|
|
2603
|
+
onClick: () => setActive(f.filename),
|
|
2604
|
+
className: cn66(
|
|
2605
|
+
"h-10 shrink-0 border-b-2 px-3 font-mono text-xs transition-colors",
|
|
2606
|
+
active === f.filename ? "border-foreground text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
|
|
2607
|
+
),
|
|
2608
|
+
children: f.filename
|
|
2609
|
+
},
|
|
2610
|
+
f.filename
|
|
2611
|
+
)) }),
|
|
2612
|
+
file ? /* @__PURE__ */ jsx66("div", { className: "shrink-0 pr-3", children: /* @__PURE__ */ jsx66(CopyButton, { value: file.code, label: "Copy code" }) }) : null
|
|
2613
|
+
] }),
|
|
2614
|
+
file ? /* @__PURE__ */ jsx66(
|
|
2615
|
+
CodeRenderer,
|
|
2616
|
+
{
|
|
2617
|
+
code: file.code,
|
|
2618
|
+
language: file.language ?? "tsx",
|
|
2619
|
+
html: file.html,
|
|
2620
|
+
showLineNumbers,
|
|
2621
|
+
scrollable,
|
|
2622
|
+
maxHeight
|
|
2623
|
+
}
|
|
2624
|
+
) : null
|
|
2625
|
+
] });
|
|
2626
|
+
}
|
|
2627
|
+
function LanguageTabsCodeBlock({
|
|
2628
|
+
tabs,
|
|
2629
|
+
showLineNumbers = false,
|
|
2630
|
+
scrollable = false,
|
|
2631
|
+
maxHeight = 400,
|
|
2632
|
+
className,
|
|
2633
|
+
...props
|
|
2634
|
+
}) {
|
|
2635
|
+
const [active, setActive] = useState4(tabs[0]?.label ?? "");
|
|
2636
|
+
const tab = tabs.find((t) => t.label === active) ?? tabs[0];
|
|
2637
|
+
useEffect4(() => {
|
|
2638
|
+
if (!tabs.some((t) => t.label === active)) {
|
|
2639
|
+
setActive(tabs[0]?.label ?? "");
|
|
2640
|
+
}
|
|
2641
|
+
}, [tabs, active]);
|
|
2642
|
+
return /* @__PURE__ */ jsxs24(ark40.div, { "data-code-block": true, className: cn66(codeBlockRootBase, className), ...props, children: [
|
|
2643
|
+
/* @__PURE__ */ jsxs24(CodeBlockHeader, { children: [
|
|
2644
|
+
/* @__PURE__ */ jsx66(CodeBlockTitle, { children: tab?.filename }),
|
|
2645
|
+
/* @__PURE__ */ jsx66("div", { className: "ml-auto", children: /* @__PURE__ */ jsxs24(
|
|
2646
|
+
Select,
|
|
2647
|
+
{
|
|
2648
|
+
items: tabs.map((t) => ({ value: t.label, label: t.label })),
|
|
2649
|
+
value: [active],
|
|
2650
|
+
onValueChange: (details) => setActive(details.value[0]),
|
|
2651
|
+
"aria-label": "Language",
|
|
2652
|
+
children: [
|
|
2653
|
+
/* @__PURE__ */ jsxs24(SelectTrigger, { size: "sm", className: "min-w-28 justify-between font-mono text-xs", children: [
|
|
2654
|
+
/* @__PURE__ */ jsx66(SelectValue, {}),
|
|
2655
|
+
/* @__PURE__ */ jsx66(SelectIndicator, { children: /* @__PURE__ */ jsx66(
|
|
2656
|
+
"svg",
|
|
2657
|
+
{
|
|
2658
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2659
|
+
viewBox: "0 0 24 24",
|
|
2660
|
+
fill: "none",
|
|
2661
|
+
stroke: "currentColor",
|
|
2662
|
+
strokeWidth: "2",
|
|
2663
|
+
strokeLinecap: "round",
|
|
2664
|
+
strokeLinejoin: "round",
|
|
2665
|
+
"aria-hidden": "true",
|
|
2666
|
+
children: /* @__PURE__ */ jsx66("path", { d: "m6 9 6 6 6-6" })
|
|
2667
|
+
}
|
|
2668
|
+
) })
|
|
2669
|
+
] }),
|
|
2670
|
+
/* @__PURE__ */ jsx66(SelectContent, { children: tabs.map((t) => /* @__PURE__ */ jsxs24(SelectItem, { item: { value: t.label, label: t.label }, children: [
|
|
2671
|
+
/* @__PURE__ */ jsx66(SelectItemText, { children: t.label }),
|
|
2672
|
+
/* @__PURE__ */ jsx66(SelectItemIndicator, { children: /* @__PURE__ */ jsx66(
|
|
2673
|
+
"svg",
|
|
2674
|
+
{
|
|
2675
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2676
|
+
viewBox: "0 0 24 24",
|
|
2677
|
+
fill: "none",
|
|
2678
|
+
stroke: "currentColor",
|
|
2679
|
+
strokeWidth: "2",
|
|
2680
|
+
strokeLinecap: "round",
|
|
2681
|
+
strokeLinejoin: "round",
|
|
2682
|
+
"aria-hidden": "true",
|
|
2683
|
+
children: /* @__PURE__ */ jsx66("path", { d: "M20 6 9 17l-5-5" })
|
|
2684
|
+
}
|
|
2685
|
+
) })
|
|
2686
|
+
] }, t.label)) })
|
|
2687
|
+
]
|
|
2688
|
+
}
|
|
2689
|
+
) })
|
|
2690
|
+
] }),
|
|
2691
|
+
tab ? /* @__PURE__ */ jsxs24("div", { className: "relative", children: [
|
|
2692
|
+
/* @__PURE__ */ jsx66(
|
|
2693
|
+
CodeRenderer,
|
|
2694
|
+
{
|
|
2695
|
+
code: tab.code,
|
|
2696
|
+
language: tab.language ?? "tsx",
|
|
2697
|
+
html: tab.html,
|
|
2698
|
+
showLineNumbers,
|
|
2699
|
+
scrollable,
|
|
2700
|
+
maxHeight
|
|
2701
|
+
}
|
|
2702
|
+
),
|
|
2703
|
+
/* @__PURE__ */ jsx66("div", { className: codeBlockCopyFloatBase, children: /* @__PURE__ */ jsx66(CopyButton, { value: tab.code, label: "Copy code" }) })
|
|
2704
|
+
] }) : null
|
|
2705
|
+
] });
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// src/manim.tsx
|
|
2709
|
+
import { useEffect as useEffect5, useRef as useRef2, useState as useState5 } from "react";
|
|
2710
|
+
import { ark as ark41 } from "@ark-ui/react/factory";
|
|
2711
|
+
import {
|
|
2712
|
+
manimRootBase,
|
|
2713
|
+
manimContainerBase,
|
|
2714
|
+
manimPlaceholderBase,
|
|
2715
|
+
manimPlaceholderLabelBase,
|
|
2716
|
+
manimErrorBase,
|
|
2717
|
+
manimCaptionBase,
|
|
2718
|
+
cn as cn67
|
|
2719
|
+
} from "@cloudvoyant/helix";
|
|
2720
|
+
import { jsx as jsx67, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2721
|
+
function Manim({ build, width = 800, height = 450, caption, className, ...props }) {
|
|
2722
|
+
const wrapperRef = useRef2(null);
|
|
2723
|
+
const containerRef = useRef2(null);
|
|
2724
|
+
const [state, setState] = useState5("loading");
|
|
2725
|
+
const [errorMsg, setErrorMsg] = useState5("");
|
|
2726
|
+
useEffect5(() => {
|
|
2727
|
+
const wrapper = wrapperRef.current;
|
|
2728
|
+
if (!wrapper) return;
|
|
2729
|
+
let disposed = false;
|
|
2730
|
+
let player = null;
|
|
2731
|
+
let resizeTimer;
|
|
2732
|
+
let mountedWidth = 0;
|
|
2733
|
+
let generation = 0;
|
|
2734
|
+
function fail(msg) {
|
|
2735
|
+
if (!disposed) {
|
|
2736
|
+
setErrorMsg(msg);
|
|
2737
|
+
setState("error");
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
async function mount(pixelWidth) {
|
|
2741
|
+
const gen = ++generation;
|
|
2742
|
+
try {
|
|
2743
|
+
const manim = await import("manim-web");
|
|
2744
|
+
if (disposed || gen !== generation || !containerRef.current) return;
|
|
2745
|
+
try {
|
|
2746
|
+
player?.dispose?.();
|
|
2747
|
+
} catch {
|
|
2748
|
+
}
|
|
2749
|
+
containerRef.current.replaceChildren();
|
|
2750
|
+
mountedWidth = pixelWidth;
|
|
2751
|
+
player = new manim.Player(containerRef.current, {
|
|
2752
|
+
width: pixelWidth,
|
|
2753
|
+
height: Math.round(pixelWidth * height / width),
|
|
2754
|
+
backgroundOpacity: 0,
|
|
2755
|
+
autoPlay: true,
|
|
2756
|
+
loop: false
|
|
2757
|
+
});
|
|
2758
|
+
setState("ready");
|
|
2759
|
+
await player.sequence(async (scene) => {
|
|
2760
|
+
await build(manim, scene);
|
|
2761
|
+
});
|
|
2762
|
+
} catch (err) {
|
|
2763
|
+
if (gen === generation) {
|
|
2764
|
+
fail(`Failed to load animation: ${err instanceof Error ? err.message : String(err)}`);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
const initialWidth = Math.floor(wrapper.getBoundingClientRect().width);
|
|
2769
|
+
if (initialWidth > 0) void mount(initialWidth);
|
|
2770
|
+
const observer = new ResizeObserver((entries) => {
|
|
2771
|
+
const measured = Math.floor(entries[0]?.contentRect.width ?? 0);
|
|
2772
|
+
if (measured <= 0 || measured === mountedWidth) return;
|
|
2773
|
+
clearTimeout(resizeTimer);
|
|
2774
|
+
resizeTimer = setTimeout(() => {
|
|
2775
|
+
if (!disposed) void mount(measured);
|
|
2776
|
+
}, 200);
|
|
2777
|
+
});
|
|
2778
|
+
observer.observe(wrapper);
|
|
2779
|
+
return () => {
|
|
2780
|
+
disposed = true;
|
|
2781
|
+
clearTimeout(resizeTimer);
|
|
2782
|
+
observer.disconnect();
|
|
2783
|
+
try {
|
|
2784
|
+
player?.dispose?.();
|
|
2785
|
+
} catch {
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
}, [build, width, height]);
|
|
2789
|
+
return /* @__PURE__ */ jsxs25(ark41.figure, { "data-manim-state": state, className: cn67(manimRootBase, className), ...props, children: [
|
|
2790
|
+
/* @__PURE__ */ jsxs25("div", { ref: wrapperRef, className: manimContainerBase, children: [
|
|
2791
|
+
/* @__PURE__ */ jsx67("div", { ref: containerRef, className: "w-full" }),
|
|
2792
|
+
state === "loading" && /* @__PURE__ */ jsx67("div", { className: manimPlaceholderBase, style: { aspectRatio: `${width} / ${height}` }, children: /* @__PURE__ */ jsx67("span", { className: manimPlaceholderLabelBase, children: "Loading animation\u2026" }) }),
|
|
2793
|
+
state === "error" && /* @__PURE__ */ jsx67("div", { className: manimErrorBase, children: errorMsg })
|
|
2794
|
+
] }),
|
|
2795
|
+
caption ? /* @__PURE__ */ jsx67(ark41.figcaption, { className: manimCaptionBase, children: caption }) : null
|
|
2796
|
+
] });
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
// src/youtube.tsx
|
|
2800
|
+
import { createContext as createContext7, useContext as useContext7, useEffect as useEffect6, useState as useState6 } from "react";
|
|
2801
|
+
import { ark as ark42 } from "@ark-ui/react/factory";
|
|
2802
|
+
import {
|
|
2803
|
+
youtubeRootBase,
|
|
2804
|
+
youtubeTitleBase,
|
|
2805
|
+
youtubeFacadeBase,
|
|
2806
|
+
youtubePlayButtonBase,
|
|
2807
|
+
youtubePlayButtonInnerBase,
|
|
2808
|
+
youtubeDescriptionBase,
|
|
2809
|
+
youtubeChaptersBase,
|
|
2810
|
+
youtubeChaptersSummaryBase,
|
|
2811
|
+
youtubeChaptersChevronBase,
|
|
2812
|
+
youtubeChaptersListBase,
|
|
2813
|
+
youtubeChapterButtonBase,
|
|
2814
|
+
youtubeChapterTimeBase,
|
|
2815
|
+
youtubeChapterLabelBase,
|
|
2816
|
+
extractYouTubeId,
|
|
2817
|
+
formatTime,
|
|
2818
|
+
parseYouTubeChapters,
|
|
2819
|
+
fetchYouTubeMetadata,
|
|
2820
|
+
cn as cn68
|
|
2821
|
+
} from "@cloudvoyant/helix";
|
|
2822
|
+
import { jsx as jsx68, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2823
|
+
function PlayIcon() {
|
|
2824
|
+
return /* @__PURE__ */ jsx68(
|
|
2825
|
+
"svg",
|
|
2826
|
+
{
|
|
2827
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2828
|
+
viewBox: "0 0 24 24",
|
|
2829
|
+
fill: "currentColor",
|
|
2830
|
+
className: "h-8 w-8",
|
|
2831
|
+
"aria-hidden": "true",
|
|
2832
|
+
children: /* @__PURE__ */ jsx68("path", { d: "M8 5v14l11-7z" })
|
|
2833
|
+
}
|
|
2834
|
+
);
|
|
2835
|
+
}
|
|
2836
|
+
function ChevronRightIcon2({ className }) {
|
|
2837
|
+
return /* @__PURE__ */ jsx68(
|
|
2838
|
+
"svg",
|
|
2839
|
+
{
|
|
2840
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2841
|
+
viewBox: "0 0 24 24",
|
|
2842
|
+
fill: "none",
|
|
2843
|
+
stroke: "currentColor",
|
|
2844
|
+
strokeWidth: "2",
|
|
2845
|
+
strokeLinecap: "round",
|
|
2846
|
+
strokeLinejoin: "round",
|
|
2847
|
+
"aria-hidden": "true",
|
|
2848
|
+
className,
|
|
2849
|
+
children: /* @__PURE__ */ jsx68("path", { d: "m9 18 6-6-6-6" })
|
|
2850
|
+
}
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
var YouTubeContext = createContext7(null);
|
|
2854
|
+
function useYouTube() {
|
|
2855
|
+
const ctx = useContext7(YouTubeContext);
|
|
2856
|
+
if (!ctx) throw new Error("YoutubeTimestamps must be rendered inside a <YouTube>.");
|
|
2857
|
+
return ctx;
|
|
2858
|
+
}
|
|
2859
|
+
function YouTube({ url, title, description, poster, className, children, ...props }) {
|
|
2860
|
+
const videoId = extractYouTubeId(url);
|
|
2861
|
+
const posterUrl = poster ?? `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
|
|
2862
|
+
const [playing, setPlaying] = useState6(false);
|
|
2863
|
+
const [startAt, setStartAt] = useState6(null);
|
|
2864
|
+
const seek = (t) => {
|
|
2865
|
+
setStartAt(t);
|
|
2866
|
+
setPlaying(true);
|
|
2867
|
+
};
|
|
2868
|
+
const src = `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1${startAt != null ? `&start=${startAt}` : ""}`;
|
|
2869
|
+
return /* @__PURE__ */ jsx68(YouTubeContext.Provider, { value: { url, videoId, seek }, children: /* @__PURE__ */ jsxs26(ark42.div, { "data-youtube-id": videoId, className: cn68(youtubeRootBase, className), ...props, children: [
|
|
2870
|
+
title ? /* @__PURE__ */ jsx68(ark42.h3, { className: youtubeTitleBase, children: title }) : null,
|
|
2871
|
+
playing ? /* @__PURE__ */ jsx68(
|
|
2872
|
+
"iframe",
|
|
2873
|
+
{
|
|
2874
|
+
src,
|
|
2875
|
+
title: title ?? `YouTube video ${videoId}`,
|
|
2876
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
2877
|
+
allowFullScreen: true,
|
|
2878
|
+
className: "aspect-video w-full rounded-md border border-border"
|
|
2879
|
+
}
|
|
2880
|
+
) : /* @__PURE__ */ jsxs26(
|
|
2881
|
+
ark42.button,
|
|
2882
|
+
{
|
|
2883
|
+
type: "button",
|
|
2884
|
+
className: youtubeFacadeBase,
|
|
2885
|
+
onClick: () => setPlaying(true),
|
|
2886
|
+
"aria-label": title ? `Play video: ${title}` : "Play video",
|
|
2887
|
+
children: [
|
|
2888
|
+
/* @__PURE__ */ jsx68(ark42.img, { src: posterUrl, alt: title ?? `YouTube video ${videoId}`, loading: "lazy", className: "h-full w-full object-cover" }),
|
|
2889
|
+
/* @__PURE__ */ jsx68(ark42.span, { className: youtubePlayButtonBase, "aria-hidden": "true", children: /* @__PURE__ */ jsx68(ark42.span, { className: youtubePlayButtonInnerBase, children: /* @__PURE__ */ jsx68(PlayIcon, {}) }) })
|
|
2890
|
+
]
|
|
2891
|
+
}
|
|
2892
|
+
),
|
|
2893
|
+
description ? /* @__PURE__ */ jsx68(ark42.p, { className: youtubeDescriptionBase, children: description }) : null,
|
|
2894
|
+
children
|
|
2895
|
+
] }) });
|
|
2896
|
+
}
|
|
2897
|
+
function YoutubeTimestampAt({ t, children, className, ...props }) {
|
|
2898
|
+
const { seek } = useYouTube();
|
|
2899
|
+
return /* @__PURE__ */ jsx68(ark42.li, { className, ...props, children: /* @__PURE__ */ jsxs26(ark42.button, { type: "button", onClick: () => seek(t), className: youtubeChapterButtonBase, children: [
|
|
2900
|
+
/* @__PURE__ */ jsx68(ark42.span, { className: youtubeChapterTimeBase, children: formatTime(t) }),
|
|
2901
|
+
/* @__PURE__ */ jsx68(ark42.span, { className: youtubeChapterLabelBase, children })
|
|
2902
|
+
] }) });
|
|
2903
|
+
}
|
|
2904
|
+
function YoutubeTimestamps({ chapters, children, className, ...props }) {
|
|
2905
|
+
const { url } = useYouTube();
|
|
2906
|
+
const [inferred, setInferred] = useState6([]);
|
|
2907
|
+
const [loading, setLoading] = useState6(chapters === void 0);
|
|
2908
|
+
useEffect6(() => {
|
|
2909
|
+
if (chapters !== void 0) return;
|
|
2910
|
+
let cancelled = false;
|
|
2911
|
+
setLoading(true);
|
|
2912
|
+
fetchYouTubeMetadata(url).then((meta) => {
|
|
2913
|
+
if (!cancelled) {
|
|
2914
|
+
setInferred(meta.description ? parseYouTubeChapters(meta.description) : []);
|
|
2915
|
+
setLoading(false);
|
|
2916
|
+
}
|
|
2917
|
+
}).catch(() => {
|
|
2918
|
+
if (!cancelled) setLoading(false);
|
|
2919
|
+
});
|
|
2920
|
+
return () => {
|
|
2921
|
+
cancelled = true;
|
|
2922
|
+
};
|
|
2923
|
+
}, [url, chapters]);
|
|
2924
|
+
const resolved = chapters ?? inferred;
|
|
2925
|
+
const hasChildren = children !== void 0 && children !== null;
|
|
2926
|
+
if (!loading && resolved.length === 0 && !hasChildren) return null;
|
|
2927
|
+
return /* @__PURE__ */ jsx68(ark42.div, { className: cn68(youtubeChaptersBase, className), ...props, children: /* @__PURE__ */ jsxs26("details", { className: "group", open: true, children: [
|
|
2928
|
+
/* @__PURE__ */ jsxs26(ark42.summary, { className: youtubeChaptersSummaryBase, children: [
|
|
2929
|
+
/* @__PURE__ */ jsx68(ChevronRightIcon2, { className: youtubeChaptersChevronBase }),
|
|
2930
|
+
/* @__PURE__ */ jsx68("span", { children: "Timestamps" })
|
|
2931
|
+
] }),
|
|
2932
|
+
/* @__PURE__ */ jsx68(ark42.nav, { "aria-label": "Video timestamps", className: youtubeChaptersListBase, children: /* @__PURE__ */ jsxs26(ark42.ul, { className: "space-y-1", children: [
|
|
2933
|
+
resolved.map((ch) => /* @__PURE__ */ jsx68(YoutubeTimestampAt, { t: ch.t, children: ch.label }, ch.t)),
|
|
2934
|
+
children
|
|
2935
|
+
] }) })
|
|
2936
|
+
] }) });
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
// src/chart.tsx
|
|
2940
|
+
import { ark as ark43 } from "@ark-ui/react/factory";
|
|
2941
|
+
import { Chart as TanStackChart } from "@tanstack/charts/react";
|
|
2942
|
+
import { chartRootBase, buildChartDefinition, cn as cn69 } from "@cloudvoyant/helix";
|
|
2943
|
+
import { jsx as jsx69 } from "react/jsx-runtime";
|
|
2944
|
+
function Chart({ svg, className, ...props }) {
|
|
2945
|
+
const { type, height = 320 } = props;
|
|
2946
|
+
if (svg !== void 0) {
|
|
2947
|
+
return /* @__PURE__ */ jsx69(ark43.div, { "data-chart-state": "prerendered", "data-chart-type": type, className: cn69(chartRootBase, className), children: /* @__PURE__ */ jsx69("div", { className: "flex w-full justify-center [&>svg]:max-w-full", dangerouslySetInnerHTML: { __html: svg } }) });
|
|
2948
|
+
}
|
|
2949
|
+
const definition = buildChartDefinition(props);
|
|
2950
|
+
return /* @__PURE__ */ jsx69(ark43.div, { "data-chart-state": "ready", "data-chart-type": type, className: cn69(chartRootBase, className), children: /* @__PURE__ */ jsx69(TanStackChart, { definition, height, ariaLabel: type === "pie" ? "Pie chart" : "Chart" }) });
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/questions.tsx
|
|
2954
|
+
import { useEffect as useEffect7, useState as useState7 } from "react";
|
|
2955
|
+
import { ark as ark44 } from "@ark-ui/react/factory";
|
|
2956
|
+
import {
|
|
2957
|
+
questionCardBase,
|
|
2958
|
+
questionPromptBase,
|
|
2959
|
+
questionGroupBase,
|
|
2960
|
+
questionOptionBase,
|
|
2961
|
+
questionOptionCorrectBase,
|
|
2962
|
+
questionOptionIncorrectBase,
|
|
2963
|
+
questionCheckButtonBase,
|
|
2964
|
+
questionInputBase,
|
|
2965
|
+
questionUnitBase,
|
|
2966
|
+
feedbackCorrectBase,
|
|
2967
|
+
feedbackIncorrectBase,
|
|
2968
|
+
feedbackExplanationBase,
|
|
2969
|
+
quizRootBase,
|
|
2970
|
+
quizHeaderBase,
|
|
2971
|
+
quizTitleBase,
|
|
2972
|
+
quizScoreBase,
|
|
2973
|
+
quizResultBase,
|
|
2974
|
+
quizResultTitleBase,
|
|
2975
|
+
quizResultTextBase,
|
|
2976
|
+
quizSubmitButtonBase,
|
|
2977
|
+
quizResetButtonBase,
|
|
2978
|
+
cn as cn70
|
|
2979
|
+
} from "@cloudvoyant/helix";
|
|
2980
|
+
import { Fragment as Fragment6, jsx as jsx70, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2981
|
+
function CheckCircleIcon2() {
|
|
2982
|
+
return /* @__PURE__ */ jsxs27(
|
|
2983
|
+
"svg",
|
|
2984
|
+
{
|
|
2985
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2986
|
+
viewBox: "0 0 24 24",
|
|
2987
|
+
fill: "none",
|
|
2988
|
+
stroke: "currentColor",
|
|
2989
|
+
strokeWidth: "2",
|
|
2990
|
+
strokeLinecap: "round",
|
|
2991
|
+
strokeLinejoin: "round",
|
|
2992
|
+
"aria-hidden": "true",
|
|
2993
|
+
className: "size-4 shrink-0",
|
|
2994
|
+
children: [
|
|
2995
|
+
/* @__PURE__ */ jsx70("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2996
|
+
/* @__PURE__ */ jsx70("path", { d: "m9 12 2 2 4-4" })
|
|
2997
|
+
]
|
|
2998
|
+
}
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
function CircleXIcon2() {
|
|
3002
|
+
return /* @__PURE__ */ jsxs27(
|
|
3003
|
+
"svg",
|
|
3004
|
+
{
|
|
3005
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3006
|
+
viewBox: "0 0 24 24",
|
|
3007
|
+
fill: "none",
|
|
3008
|
+
stroke: "currentColor",
|
|
3009
|
+
strokeWidth: "2",
|
|
3010
|
+
strokeLinecap: "round",
|
|
3011
|
+
strokeLinejoin: "round",
|
|
3012
|
+
"aria-hidden": "true",
|
|
3013
|
+
className: "size-4 shrink-0",
|
|
3014
|
+
children: [
|
|
3015
|
+
/* @__PURE__ */ jsx70("circle", { cx: "12", cy: "12", r: "10" }),
|
|
3016
|
+
/* @__PURE__ */ jsx70("path", { d: "m15 9-6 6" }),
|
|
3017
|
+
/* @__PURE__ */ jsx70("path", { d: "m9 9 6 6" })
|
|
3018
|
+
]
|
|
3019
|
+
}
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
function CheckIcon2() {
|
|
3023
|
+
return /* @__PURE__ */ jsx70(
|
|
3024
|
+
"svg",
|
|
3025
|
+
{
|
|
3026
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3027
|
+
viewBox: "0 0 24 24",
|
|
3028
|
+
fill: "none",
|
|
3029
|
+
stroke: "currentColor",
|
|
3030
|
+
strokeWidth: "2",
|
|
3031
|
+
strokeLinecap: "round",
|
|
3032
|
+
strokeLinejoin: "round",
|
|
3033
|
+
"aria-hidden": "true",
|
|
3034
|
+
children: /* @__PURE__ */ jsx70("path", { d: "M20 6 9 17l-5-5" })
|
|
3035
|
+
}
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
function Feedback({ correct, explanation }) {
|
|
3039
|
+
if (correct === null) return null;
|
|
3040
|
+
return /* @__PURE__ */ jsxs27("div", { "aria-live": "polite", role: "status", className: "mt-3", children: [
|
|
3041
|
+
/* @__PURE__ */ jsx70(
|
|
3042
|
+
"div",
|
|
3043
|
+
{
|
|
3044
|
+
className: cn70(
|
|
3045
|
+
"flex items-center gap-2 text-sm font-medium",
|
|
3046
|
+
correct ? feedbackCorrectBase : feedbackIncorrectBase
|
|
3047
|
+
),
|
|
3048
|
+
children: correct ? /* @__PURE__ */ jsxs27(Fragment6, { children: [
|
|
3049
|
+
/* @__PURE__ */ jsx70(CheckCircleIcon2, {}),
|
|
3050
|
+
/* @__PURE__ */ jsx70("span", { children: "Correct" })
|
|
3051
|
+
] }) : /* @__PURE__ */ jsxs27(Fragment6, { children: [
|
|
3052
|
+
/* @__PURE__ */ jsx70(CircleXIcon2, {}),
|
|
3053
|
+
/* @__PURE__ */ jsx70("span", { children: "Incorrect" })
|
|
3054
|
+
] })
|
|
3055
|
+
}
|
|
3056
|
+
),
|
|
3057
|
+
explanation ? /* @__PURE__ */ jsx70("p", { className: feedbackExplanationBase, children: explanation }) : null
|
|
3058
|
+
] });
|
|
3059
|
+
}
|
|
3060
|
+
function SingleChoiceRenderer({
|
|
3061
|
+
prompt,
|
|
3062
|
+
choices,
|
|
3063
|
+
correct,
|
|
3064
|
+
explanation,
|
|
3065
|
+
quiz = false,
|
|
3066
|
+
submitted = false,
|
|
3067
|
+
onAnswerChange,
|
|
3068
|
+
onGraded,
|
|
3069
|
+
className,
|
|
3070
|
+
...rest
|
|
3071
|
+
}) {
|
|
3072
|
+
const [selected, setSelected] = useState7(null);
|
|
3073
|
+
const [result, setResult] = useState7(null);
|
|
3074
|
+
useEffect7(() => {
|
|
3075
|
+
if (quiz) {
|
|
3076
|
+
const isAnswered = selected !== null;
|
|
3077
|
+
onAnswerChange?.({ answered: isAnswered, correct: isAnswered && selected === correct });
|
|
3078
|
+
}
|
|
3079
|
+
}, [quiz, selected, correct]);
|
|
3080
|
+
const displayResult = quiz ? submitted ? selected === correct : null : result;
|
|
3081
|
+
const handleCheck = () => {
|
|
3082
|
+
if (selected === null) return;
|
|
3083
|
+
const isCorrect = selected === correct;
|
|
3084
|
+
setResult(isCorrect);
|
|
3085
|
+
onGraded?.(isCorrect);
|
|
3086
|
+
};
|
|
3087
|
+
return /* @__PURE__ */ jsxs27("div", { className: cn70(questionCardBase, className), ...rest, children: [
|
|
3088
|
+
/* @__PURE__ */ jsx70("p", { className: questionPromptBase, children: prompt }),
|
|
3089
|
+
/* @__PURE__ */ jsx70(
|
|
3090
|
+
RadioGroup,
|
|
3091
|
+
{
|
|
3092
|
+
orientation: "vertical",
|
|
3093
|
+
value: selected !== null ? String(selected) : null,
|
|
3094
|
+
onValueChange: (details) => {
|
|
3095
|
+
if (quiz ? submitted : result !== null) return;
|
|
3096
|
+
setSelected(details.value != null ? Number(details.value) : null);
|
|
3097
|
+
},
|
|
3098
|
+
disabled: quiz ? submitted : result !== null,
|
|
3099
|
+
className: questionGroupBase,
|
|
3100
|
+
children: choices.map((choice, i) => /* @__PURE__ */ jsx70(
|
|
3101
|
+
RadioGroupItem,
|
|
3102
|
+
{
|
|
3103
|
+
value: String(i),
|
|
3104
|
+
className: cn70(
|
|
3105
|
+
questionOptionBase,
|
|
3106
|
+
displayResult !== null && i === correct && questionOptionCorrectBase,
|
|
3107
|
+
displayResult !== null && i === selected && i !== correct && questionOptionIncorrectBase
|
|
3108
|
+
),
|
|
3109
|
+
children: choice
|
|
3110
|
+
},
|
|
3111
|
+
i
|
|
3112
|
+
))
|
|
3113
|
+
}
|
|
3114
|
+
),
|
|
3115
|
+
!quiz && result === null && /* @__PURE__ */ jsx70(
|
|
3116
|
+
ark44.button,
|
|
3117
|
+
{
|
|
3118
|
+
type: "button",
|
|
3119
|
+
onClick: handleCheck,
|
|
3120
|
+
disabled: selected === null,
|
|
3121
|
+
className: questionCheckButtonBase,
|
|
3122
|
+
children: "Check"
|
|
3123
|
+
}
|
|
3124
|
+
),
|
|
3125
|
+
/* @__PURE__ */ jsx70(Feedback, { correct: displayResult, explanation: displayResult !== null ? explanation : void 0 })
|
|
3126
|
+
] });
|
|
3127
|
+
}
|
|
3128
|
+
function MultipleChoiceRenderer({
|
|
3129
|
+
prompt,
|
|
3130
|
+
choices,
|
|
3131
|
+
correct,
|
|
3132
|
+
explanation,
|
|
3133
|
+
quiz = false,
|
|
3134
|
+
submitted = false,
|
|
3135
|
+
onAnswerChange,
|
|
3136
|
+
onGraded,
|
|
3137
|
+
className,
|
|
3138
|
+
...rest
|
|
3139
|
+
}) {
|
|
3140
|
+
const [selected, setSelected] = useState7(/* @__PURE__ */ new Set());
|
|
3141
|
+
const [result, setResult] = useState7(null);
|
|
3142
|
+
const computeCorrect = () => {
|
|
3143
|
+
const correctSet = new Set(correct);
|
|
3144
|
+
return selected.size === correctSet.size && [...selected].every((v) => correctSet.has(v));
|
|
3145
|
+
};
|
|
3146
|
+
useEffect7(() => {
|
|
3147
|
+
if (quiz) {
|
|
3148
|
+
const isAnswered = selected.size > 0;
|
|
3149
|
+
onAnswerChange?.({ answered: isAnswered, correct: isAnswered && computeCorrect() });
|
|
3150
|
+
}
|
|
3151
|
+
}, [quiz, selected, correct]);
|
|
3152
|
+
const toggleOption = (index) => {
|
|
3153
|
+
if (quiz ? submitted : result !== null) return;
|
|
3154
|
+
setSelected((prev) => {
|
|
3155
|
+
const next = new Set(prev);
|
|
3156
|
+
if (next.has(index)) next.delete(index);
|
|
3157
|
+
else next.add(index);
|
|
3158
|
+
return next;
|
|
3159
|
+
});
|
|
3160
|
+
};
|
|
3161
|
+
const displayResult = quiz ? submitted ? computeCorrect() : null : result;
|
|
3162
|
+
const handleCheck = () => {
|
|
3163
|
+
if (selected.size === 0) return;
|
|
3164
|
+
const isCorrect = computeCorrect();
|
|
3165
|
+
setResult(isCorrect);
|
|
3166
|
+
onGraded?.(isCorrect);
|
|
3167
|
+
};
|
|
3168
|
+
return /* @__PURE__ */ jsxs27("div", { className: cn70(questionCardBase, className), ...rest, children: [
|
|
3169
|
+
/* @__PURE__ */ jsx70("p", { className: questionPromptBase, children: prompt }),
|
|
3170
|
+
/* @__PURE__ */ jsx70("div", { role: "group", className: questionGroupBase, children: choices.map((choice, i) => {
|
|
3171
|
+
const isCorrectOption = correct.includes(i);
|
|
3172
|
+
return /* @__PURE__ */ jsxs27(
|
|
3173
|
+
Checkbox,
|
|
3174
|
+
{
|
|
3175
|
+
checked: selected.has(i),
|
|
3176
|
+
onCheckedChange: () => toggleOption(i),
|
|
3177
|
+
disabled: quiz ? submitted : result !== null,
|
|
3178
|
+
className: cn70(
|
|
3179
|
+
questionOptionBase,
|
|
3180
|
+
displayResult !== null && isCorrectOption && questionOptionCorrectBase,
|
|
3181
|
+
displayResult !== null && selected.has(i) && !isCorrectOption && questionOptionIncorrectBase
|
|
3182
|
+
),
|
|
3183
|
+
children: [
|
|
3184
|
+
/* @__PURE__ */ jsx70(CheckboxControl, { children: /* @__PURE__ */ jsx70(CheckboxIndicator, { children: /* @__PURE__ */ jsx70(CheckIcon2, {}) }) }),
|
|
3185
|
+
/* @__PURE__ */ jsx70(CheckboxLabel, { children: choice })
|
|
3186
|
+
]
|
|
3187
|
+
},
|
|
3188
|
+
i
|
|
3189
|
+
);
|
|
3190
|
+
}) }),
|
|
3191
|
+
!quiz && result === null && /* @__PURE__ */ jsx70(
|
|
3192
|
+
ark44.button,
|
|
3193
|
+
{
|
|
3194
|
+
type: "button",
|
|
3195
|
+
onClick: handleCheck,
|
|
3196
|
+
disabled: selected.size === 0,
|
|
3197
|
+
className: questionCheckButtonBase,
|
|
3198
|
+
children: "Check"
|
|
3199
|
+
}
|
|
3200
|
+
),
|
|
3201
|
+
/* @__PURE__ */ jsx70(Feedback, { correct: displayResult, explanation: displayResult !== null ? explanation : void 0 })
|
|
3202
|
+
] });
|
|
3203
|
+
}
|
|
3204
|
+
function NumericRenderer({
|
|
3205
|
+
id,
|
|
3206
|
+
prompt,
|
|
3207
|
+
answer,
|
|
3208
|
+
tolerance = 0,
|
|
3209
|
+
unit,
|
|
3210
|
+
explanation,
|
|
3211
|
+
quiz = false,
|
|
3212
|
+
submitted = false,
|
|
3213
|
+
onAnswerChange,
|
|
3214
|
+
onGraded,
|
|
3215
|
+
className,
|
|
3216
|
+
...rest
|
|
3217
|
+
}) {
|
|
3218
|
+
const inputId = `q-${id}-input`;
|
|
3219
|
+
const [value, setValue] = useState7("");
|
|
3220
|
+
const [result, setResult] = useState7(null);
|
|
3221
|
+
const computeCorrect = () => {
|
|
3222
|
+
const parsed = parseFloat(value);
|
|
3223
|
+
return !Number.isNaN(parsed) && Math.abs(parsed - answer) <= tolerance;
|
|
3224
|
+
};
|
|
3225
|
+
useEffect7(() => {
|
|
3226
|
+
if (quiz) {
|
|
3227
|
+
const parsed = parseFloat(value);
|
|
3228
|
+
const isAnswered = value.trim() !== "" && !Number.isNaN(parsed);
|
|
3229
|
+
onAnswerChange?.({ answered: isAnswered, correct: isAnswered && computeCorrect() });
|
|
3230
|
+
}
|
|
3231
|
+
}, [quiz, value, answer, tolerance]);
|
|
3232
|
+
const displayResult = quiz ? submitted ? computeCorrect() : null : result;
|
|
3233
|
+
const handleCheck = () => {
|
|
3234
|
+
const parsed = parseFloat(value);
|
|
3235
|
+
if (Number.isNaN(parsed)) return;
|
|
3236
|
+
const isCorrect = Math.abs(parsed - answer) <= tolerance;
|
|
3237
|
+
setResult(isCorrect);
|
|
3238
|
+
onGraded?.(isCorrect);
|
|
3239
|
+
};
|
|
3240
|
+
return /* @__PURE__ */ jsxs27("div", { className: cn70(questionCardBase, className), ...rest, children: [
|
|
3241
|
+
/* @__PURE__ */ jsx70(ark44.label, { htmlFor: inputId, className: questionPromptBase, children: prompt }),
|
|
3242
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
|
|
3243
|
+
/* @__PURE__ */ jsx70(
|
|
3244
|
+
ark44.input,
|
|
3245
|
+
{
|
|
3246
|
+
id: inputId,
|
|
3247
|
+
type: "text",
|
|
3248
|
+
inputMode: "decimal",
|
|
3249
|
+
value,
|
|
3250
|
+
onChange: (e) => {
|
|
3251
|
+
if (quiz ? submitted : result !== null) return;
|
|
3252
|
+
setValue(e.currentTarget.value);
|
|
3253
|
+
},
|
|
3254
|
+
disabled: quiz ? submitted : result !== null,
|
|
3255
|
+
className: questionInputBase
|
|
3256
|
+
}
|
|
3257
|
+
),
|
|
3258
|
+
unit ? /* @__PURE__ */ jsx70(ark44.span, { className: questionUnitBase, children: unit }) : null
|
|
3259
|
+
] }),
|
|
3260
|
+
!quiz && result === null && /* @__PURE__ */ jsx70(
|
|
3261
|
+
ark44.button,
|
|
3262
|
+
{
|
|
3263
|
+
type: "button",
|
|
3264
|
+
onClick: handleCheck,
|
|
3265
|
+
disabled: value.trim() === "" || Number.isNaN(parseFloat(value)),
|
|
3266
|
+
className: questionCheckButtonBase,
|
|
3267
|
+
children: "Check"
|
|
3268
|
+
}
|
|
3269
|
+
),
|
|
3270
|
+
/* @__PURE__ */ jsx70(Feedback, { correct: displayResult, explanation: displayResult !== null ? explanation : void 0 })
|
|
3271
|
+
] });
|
|
3272
|
+
}
|
|
3273
|
+
function SingleChoiceQuestion(props) {
|
|
3274
|
+
return /* @__PURE__ */ jsx70(SingleChoiceRenderer, { ...props });
|
|
3275
|
+
}
|
|
3276
|
+
function MultipleChoiceQuestion(props) {
|
|
3277
|
+
return /* @__PURE__ */ jsx70(MultipleChoiceRenderer, { ...props });
|
|
3278
|
+
}
|
|
3279
|
+
function NumericQuestion(props) {
|
|
3280
|
+
return /* @__PURE__ */ jsx70(NumericRenderer, { ...props });
|
|
3281
|
+
}
|
|
3282
|
+
function Quiz({ id, title, questions, className, ...props }) {
|
|
3283
|
+
const [answers, setAnswers] = useState7({});
|
|
3284
|
+
const [submitted, setSubmitted] = useState7(false);
|
|
3285
|
+
const [resetKey, setResetKey] = useState7(0);
|
|
3286
|
+
const total = questions.length;
|
|
3287
|
+
const allAnswered = questions.length > 0 && questions.every((q) => answers[q.id]?.answered === true);
|
|
3288
|
+
const score = questions.filter((q) => answers[q.id]?.correct === true).length;
|
|
3289
|
+
const pct = total > 0 ? Math.round(score / total * 100) : 0;
|
|
3290
|
+
const handleAnswerChange = (questionId) => (state) => {
|
|
3291
|
+
setAnswers((prev) => ({ ...prev, [questionId]: state }));
|
|
3292
|
+
};
|
|
3293
|
+
const handleReset = () => {
|
|
3294
|
+
setAnswers({});
|
|
3295
|
+
setSubmitted(false);
|
|
3296
|
+
setResetKey((k) => k + 1);
|
|
3297
|
+
};
|
|
3298
|
+
const renderQuestion = (q) => {
|
|
3299
|
+
const key = `${q.id}-${String(resetKey)}`;
|
|
3300
|
+
const quizProps = {
|
|
3301
|
+
quiz: true,
|
|
3302
|
+
submitted,
|
|
3303
|
+
onAnswerChange: handleAnswerChange(q.id)
|
|
3304
|
+
};
|
|
3305
|
+
switch (q.type) {
|
|
3306
|
+
case "single":
|
|
3307
|
+
return /* @__PURE__ */ jsx70(
|
|
3308
|
+
SingleChoiceRenderer,
|
|
3309
|
+
{
|
|
3310
|
+
id: q.id,
|
|
3311
|
+
prompt: q.prompt,
|
|
3312
|
+
choices: q.choices,
|
|
3313
|
+
correct: q.correct,
|
|
3314
|
+
explanation: q.explanation,
|
|
3315
|
+
...quizProps
|
|
3316
|
+
},
|
|
3317
|
+
key
|
|
3318
|
+
);
|
|
3319
|
+
case "multiple":
|
|
3320
|
+
return /* @__PURE__ */ jsx70(
|
|
3321
|
+
MultipleChoiceRenderer,
|
|
3322
|
+
{
|
|
3323
|
+
id: q.id,
|
|
3324
|
+
prompt: q.prompt,
|
|
3325
|
+
choices: q.choices,
|
|
3326
|
+
correct: q.correct,
|
|
3327
|
+
explanation: q.explanation,
|
|
3328
|
+
...quizProps
|
|
3329
|
+
},
|
|
3330
|
+
key
|
|
3331
|
+
);
|
|
3332
|
+
case "numeric":
|
|
3333
|
+
return /* @__PURE__ */ jsx70(
|
|
3334
|
+
NumericRenderer,
|
|
3335
|
+
{
|
|
3336
|
+
id: q.id,
|
|
3337
|
+
prompt: q.prompt,
|
|
3338
|
+
answer: q.answer,
|
|
3339
|
+
tolerance: q.tolerance,
|
|
3340
|
+
unit: q.unit,
|
|
3341
|
+
explanation: q.explanation,
|
|
3342
|
+
...quizProps
|
|
3343
|
+
},
|
|
3344
|
+
key
|
|
3345
|
+
);
|
|
3346
|
+
default:
|
|
3347
|
+
return null;
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
3350
|
+
return /* @__PURE__ */ jsxs27("div", { "data-quiz-id": id, className: cn70(quizRootBase, className), ...props, children: [
|
|
3351
|
+
/* @__PURE__ */ jsx70("div", { className: quizHeaderBase, children: title ? /* @__PURE__ */ jsx70(ark44.h3, { className: quizTitleBase, children: title }) : null }),
|
|
3352
|
+
questions.map(renderQuestion),
|
|
3353
|
+
/* @__PURE__ */ jsxs27("div", { className: "mt-4 flex items-center gap-3", children: [
|
|
3354
|
+
!submitted ? /* @__PURE__ */ jsx70(
|
|
3355
|
+
ark44.button,
|
|
3356
|
+
{
|
|
3357
|
+
type: "button",
|
|
3358
|
+
onClick: () => setSubmitted(true),
|
|
3359
|
+
disabled: !allAnswered,
|
|
3360
|
+
className: quizSubmitButtonBase,
|
|
3361
|
+
children: "Submit"
|
|
3362
|
+
}
|
|
3363
|
+
) : /* @__PURE__ */ jsx70(ark44.button, { type: "button", onClick: handleReset, className: quizResetButtonBase, children: "Reset" }),
|
|
3364
|
+
/* @__PURE__ */ jsx70(ark44.span, { role: "status", className: quizScoreBase, children: submitted ? `${score}/${total} (${pct}%)` : `${total} question${total === 1 ? "" : "s"}` })
|
|
3365
|
+
] }),
|
|
3366
|
+
submitted && /* @__PURE__ */ jsx70("div", { className: quizResultBase, children: /* @__PURE__ */ jsxs27("div", { "aria-live": "polite", role: "status", className: "text-center", children: [
|
|
3367
|
+
/* @__PURE__ */ jsxs27("p", { className: quizResultTitleBase, children: [
|
|
3368
|
+
score,
|
|
3369
|
+
"/",
|
|
3370
|
+
total,
|
|
3371
|
+
" (",
|
|
3372
|
+
pct,
|
|
3373
|
+
"%)"
|
|
3374
|
+
] }),
|
|
3375
|
+
/* @__PURE__ */ jsx70("p", { className: quizResultTextBase, children: pct >= 70 ? "Well done!" : "Keep studying and try again." })
|
|
3376
|
+
] }) })
|
|
3377
|
+
] });
|
|
3378
|
+
}
|
|
2179
3379
|
export {
|
|
2180
3380
|
Badge,
|
|
2181
3381
|
Button,
|
|
@@ -2187,11 +3387,17 @@ export {
|
|
|
2187
3387
|
CardHeader,
|
|
2188
3388
|
CardTitle,
|
|
2189
3389
|
Center,
|
|
3390
|
+
Chart,
|
|
2190
3391
|
Checkbox,
|
|
2191
3392
|
CheckboxControl,
|
|
2192
3393
|
CheckboxGroup,
|
|
2193
3394
|
CheckboxIndicator,
|
|
2194
3395
|
CheckboxLabel,
|
|
3396
|
+
CodeBlock,
|
|
3397
|
+
CodeBlockContent,
|
|
3398
|
+
CodeBlockCopyButton,
|
|
3399
|
+
CodeBlockHeader,
|
|
3400
|
+
CodeBlockTitle,
|
|
2195
3401
|
Col,
|
|
2196
3402
|
Combobox,
|
|
2197
3403
|
ComboboxClear,
|
|
@@ -2207,6 +3413,7 @@ export {
|
|
|
2207
3413
|
ComboboxList,
|
|
2208
3414
|
ComboboxTrigger,
|
|
2209
3415
|
Container,
|
|
3416
|
+
CopyButton,
|
|
2210
3417
|
Dialog,
|
|
2211
3418
|
DialogBackdrop,
|
|
2212
3419
|
DialogContent,
|
|
@@ -2226,10 +3433,17 @@ export {
|
|
|
2226
3433
|
FieldSetError,
|
|
2227
3434
|
FieldSetHelper,
|
|
2228
3435
|
FieldSetLegend,
|
|
3436
|
+
Figure,
|
|
2229
3437
|
HStack,
|
|
3438
|
+
InlineLaTeX,
|
|
2230
3439
|
Input2 as Input,
|
|
2231
3440
|
Item,
|
|
3441
|
+
LaTeX,
|
|
3442
|
+
LanguageTabsCodeBlock,
|
|
3443
|
+
Manim,
|
|
2232
3444
|
Mermaid,
|
|
3445
|
+
MultiFileCodeBlock,
|
|
3446
|
+
MultipleChoiceQuestion,
|
|
2233
3447
|
Navbar,
|
|
2234
3448
|
NavbarActions,
|
|
2235
3449
|
NavbarActivationArea,
|
|
@@ -2249,11 +3463,13 @@ export {
|
|
|
2249
3463
|
NavbarMobileOverlay,
|
|
2250
3464
|
NavbarProvider,
|
|
2251
3465
|
NavbarTrigger,
|
|
3466
|
+
Notice,
|
|
2252
3467
|
NumberInput,
|
|
2253
3468
|
NumberInputControl,
|
|
2254
3469
|
NumberInputDecrement,
|
|
2255
3470
|
NumberInputIncrement,
|
|
2256
3471
|
NumberInputInput,
|
|
3472
|
+
NumericQuestion,
|
|
2257
3473
|
Page,
|
|
2258
3474
|
PageContent,
|
|
2259
3475
|
PageFooter,
|
|
@@ -2275,6 +3491,11 @@ export {
|
|
|
2275
3491
|
PopoverTitle,
|
|
2276
3492
|
PopoverTrigger,
|
|
2277
3493
|
Portal5 as Portal,
|
|
3494
|
+
PrevNext,
|
|
3495
|
+
Quiz,
|
|
3496
|
+
RadioGroup,
|
|
3497
|
+
RadioGroupItem,
|
|
3498
|
+
Reveal,
|
|
2278
3499
|
Row,
|
|
2279
3500
|
Scroll,
|
|
2280
3501
|
Select,
|
|
@@ -2311,6 +3532,7 @@ export {
|
|
|
2311
3532
|
Rail as SidebarRail,
|
|
2312
3533
|
Separator as SidebarSeparator,
|
|
2313
3534
|
Trigger as SidebarTrigger,
|
|
3535
|
+
SingleChoiceQuestion,
|
|
2314
3536
|
Splitter,
|
|
2315
3537
|
SplitterPanel,
|
|
2316
3538
|
SplitterResizeTrigger,
|
|
@@ -2352,6 +3574,9 @@ export {
|
|
|
2352
3574
|
WindowResizeTrigger,
|
|
2353
3575
|
WindowStageTrigger,
|
|
2354
3576
|
WindowTitle,
|
|
3577
|
+
YouTube,
|
|
3578
|
+
YoutubeTimestampAt,
|
|
3579
|
+
YoutubeTimestamps,
|
|
2355
3580
|
navbarMenuTriggerStyle2 as navbarMenuTriggerStyle,
|
|
2356
3581
|
useCheckbox,
|
|
2357
3582
|
useCombobox,
|
|
@@ -2363,6 +3588,7 @@ export {
|
|
|
2363
3588
|
useNumberInput,
|
|
2364
3589
|
usePopover,
|
|
2365
3590
|
usePopoverContext,
|
|
3591
|
+
useRadioGroup,
|
|
2366
3592
|
useSelect,
|
|
2367
3593
|
useSidebar,
|
|
2368
3594
|
useSwitch,
|