@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.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,