@almadar/ui 5.32.1 → 5.32.3

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.
@@ -2402,6 +2402,13 @@ var init_Textarea = __esm({
2402
2402
  exports.Textarea.displayName = "Textarea";
2403
2403
  }
2404
2404
  });
2405
+ function dispatchValueChange(onValueChange, eventBus, value) {
2406
+ if (typeof onValueChange === "string") {
2407
+ eventBus.emit(`UI:${onValueChange}`, { value });
2408
+ } else {
2409
+ onValueChange?.(value);
2410
+ }
2411
+ }
2405
2412
  function flatOptions(opts, groups) {
2406
2413
  const flat = opts ?? [];
2407
2414
  const grp = (groups ?? []).flatMap((g) => g.options);
@@ -2425,7 +2432,7 @@ function NativeSelect({
2425
2432
  } else {
2426
2433
  onChange?.(e);
2427
2434
  }
2428
- onValueChange?.(e.target.value);
2435
+ dispatchValueChange(onValueChange, eventBus, e.target.value);
2429
2436
  };
2430
2437
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2431
2438
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2485,7 +2492,7 @@ function RichSelect({
2485
2492
  if (typeof onChange === "string") {
2486
2493
  eventBus.emit(`UI:${onChange}`, { value: next });
2487
2494
  }
2488
- onValueChange?.(next);
2495
+ dispatchValueChange(onValueChange, eventBus, next);
2489
2496
  };
2490
2497
  const clear = (e) => {
2491
2498
  e.stopPropagation();
@@ -2493,7 +2500,7 @@ function RichSelect({
2493
2500
  if (typeof onChange === "string") {
2494
2501
  eventBus.emit(`UI:${onChange}`, { value: next });
2495
2502
  }
2496
- onValueChange?.(next);
2503
+ dispatchValueChange(onValueChange, eventBus, next);
2497
2504
  };
2498
2505
  React74.useEffect(() => {
2499
2506
  const handler = (e) => {
@@ -28812,7 +28819,7 @@ var init_MapView = __esm({
28812
28819
  shadowSize: [41, 41]
28813
28820
  });
28814
28821
  L.Marker.prototype.options.icon = defaultIcon;
28815
- const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback114, useState: useState101 } = React74__namespace.default;
28822
+ const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback115, useState: useState105 } = React74__namespace.default;
28816
28823
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28817
28824
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28818
28825
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -28857,8 +28864,8 @@ var init_MapView = __esm({
28857
28864
  showAttribution = true
28858
28865
  }) {
28859
28866
  const eventBus = useEventBus2();
28860
- const [clickedPosition, setClickedPosition] = useState101(null);
28861
- const handleMapClick = useCallback114((lat, lng) => {
28867
+ const [clickedPosition, setClickedPosition] = useState105(null);
28868
+ const handleMapClick = useCallback115((lat, lng) => {
28862
28869
  if (showClickedPin) {
28863
28870
  setClickedPosition({ lat, lng });
28864
28871
  }
@@ -28867,7 +28874,7 @@ var init_MapView = __esm({
28867
28874
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
28868
28875
  }
28869
28876
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
28870
- const handleMarkerClick = useCallback114((marker) => {
28877
+ const handleMarkerClick = useCallback115((marker) => {
28871
28878
  onMarkerClick?.(marker);
28872
28879
  if (markerClickEvent) {
28873
28880
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -37570,6 +37577,384 @@ var init_GraphCanvas = __esm({
37570
37577
  exports.GraphCanvas.displayName = "GraphCanvas";
37571
37578
  }
37572
37579
  });
37580
+ exports.ActivationBlock = void 0;
37581
+ var init_ActivationBlock = __esm({
37582
+ "components/core/molecules/ActivationBlock.tsx"() {
37583
+ "use client";
37584
+ init_useEventBus();
37585
+ init_cn();
37586
+ exports.ActivationBlock = ({
37587
+ question,
37588
+ savedResponse,
37589
+ saveEvent = "SAVE_ACTIVATION",
37590
+ className
37591
+ }) => {
37592
+ const [response, setResponse] = React74.useState(savedResponse ?? "");
37593
+ const [isExpanded, setIsExpanded] = React74.useState(!savedResponse);
37594
+ const { emit } = useEventBus();
37595
+ const handleSubmit = () => {
37596
+ emit(`UI:${saveEvent}`, { response });
37597
+ setIsExpanded(false);
37598
+ };
37599
+ return /* @__PURE__ */ jsxRuntime.jsx(
37600
+ "div",
37601
+ {
37602
+ className: cn(
37603
+ "bg-indigo-50 dark:bg-indigo-900/20 border-2 border-indigo-200 dark:border-indigo-800 rounded-lg p-5 mb-6",
37604
+ className
37605
+ ),
37606
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
37607
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.Lightbulb, { className: "text-indigo-600 dark:text-indigo-400 flex-shrink-0 mt-1", size: 24 }),
37608
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
37609
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-indigo-900 dark:text-indigo-100 mb-2", children: "Before You Begin..." }),
37610
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-700 dark:text-gray-300 mb-3 text-sm md:text-base", children: question }),
37611
+ isExpanded ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37612
+ /* @__PURE__ */ jsxRuntime.jsx(
37613
+ "textarea",
37614
+ {
37615
+ className: "w-full p-3 border border-indigo-300 dark:border-indigo-700 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-sm",
37616
+ placeholder: "Jot down your thoughts...",
37617
+ value: response,
37618
+ onChange: (e) => setResponse(e.target.value),
37619
+ rows: 3
37620
+ }
37621
+ ),
37622
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 mt-3", children: [
37623
+ /* @__PURE__ */ jsxRuntime.jsx(
37624
+ "button",
37625
+ {
37626
+ onClick: handleSubmit,
37627
+ className: "px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 text-sm font-medium transition-colors",
37628
+ children: "Continue to Lesson \u2192"
37629
+ }
37630
+ ),
37631
+ /* @__PURE__ */ jsxRuntime.jsx(
37632
+ "button",
37633
+ {
37634
+ onClick: () => {
37635
+ emit(`UI:${saveEvent}`, { response: "" });
37636
+ setIsExpanded(false);
37637
+ },
37638
+ className: "px-4 py-2 text-indigo-600 dark:text-indigo-400 hover:underline text-sm",
37639
+ children: "Skip for now"
37640
+ }
37641
+ )
37642
+ ] })
37643
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
37644
+ "button",
37645
+ {
37646
+ onClick: () => setIsExpanded(true),
37647
+ className: "text-sm text-indigo-600 dark:text-indigo-400 hover:underline font-medium",
37648
+ children: "\u2713 Answered \xB7 Edit response"
37649
+ }
37650
+ )
37651
+ ] })
37652
+ ] })
37653
+ }
37654
+ );
37655
+ };
37656
+ exports.ActivationBlock.displayName = "ActivationBlock";
37657
+ }
37658
+ });
37659
+ exports.ReflectionBlock = void 0;
37660
+ var init_ReflectionBlock = __esm({
37661
+ "components/core/molecules/ReflectionBlock.tsx"() {
37662
+ "use client";
37663
+ init_useEventBus();
37664
+ init_cn();
37665
+ exports.ReflectionBlock = ({
37666
+ prompt,
37667
+ index,
37668
+ savedNote,
37669
+ saveEvent = "SAVE_REFLECTION",
37670
+ className
37671
+ }) => {
37672
+ const [note, setNote] = React74.useState(savedNote ?? "");
37673
+ const [isExpanded, setIsExpanded] = React74.useState(false);
37674
+ const { emit } = useEventBus();
37675
+ const handleSave = () => {
37676
+ emit(`UI:${saveEvent}`, { index, note });
37677
+ setIsExpanded(false);
37678
+ };
37679
+ return /* @__PURE__ */ jsxRuntime.jsx(
37680
+ "div",
37681
+ {
37682
+ className: cn(
37683
+ "my-6 border-l-4 border-amber-400 bg-amber-50 dark:bg-amber-900/20 rounded-r-lg p-4",
37684
+ className
37685
+ ),
37686
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
37687
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.PauseCircle, { className: "text-amber-600 dark:text-amber-400 flex-shrink-0 mt-1", size: 20 }),
37688
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
37689
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-amber-900 dark:text-amber-100 mb-2", children: "Pause & Reflect" }),
37690
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-700 dark:text-gray-300 text-sm mb-3", children: prompt }),
37691
+ isExpanded ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37692
+ /* @__PURE__ */ jsxRuntime.jsx(
37693
+ "textarea",
37694
+ {
37695
+ className: "w-full p-2 border border-amber-300 dark:border-amber-700 rounded text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-amber-500 focus:border-transparent",
37696
+ placeholder: "Your thoughts...",
37697
+ value: note,
37698
+ onChange: (e) => setNote(e.target.value),
37699
+ rows: 2
37700
+ }
37701
+ ),
37702
+ /* @__PURE__ */ jsxRuntime.jsx(
37703
+ "button",
37704
+ {
37705
+ onClick: handleSave,
37706
+ className: "mt-2 text-sm px-3 py-1 bg-amber-600 text-white rounded hover:bg-amber-700 transition-colors",
37707
+ children: "Save & Continue"
37708
+ }
37709
+ )
37710
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
37711
+ "button",
37712
+ {
37713
+ onClick: () => setIsExpanded(true),
37714
+ className: "text-sm text-amber-600 dark:text-amber-400 hover:underline",
37715
+ children: savedNote ? "\u2713 Answered \xB7 Edit" : "Answer this question"
37716
+ }
37717
+ )
37718
+ ] })
37719
+ ] })
37720
+ }
37721
+ );
37722
+ };
37723
+ exports.ReflectionBlock.displayName = "ReflectionBlock";
37724
+ }
37725
+ });
37726
+ exports.ConnectionBlock = void 0;
37727
+ var init_ConnectionBlock = __esm({
37728
+ "components/core/molecules/ConnectionBlock.tsx"() {
37729
+ "use client";
37730
+ init_MarkdownContent();
37731
+ init_cn();
37732
+ exports.ConnectionBlock = ({ content, className }) => /* @__PURE__ */ jsxRuntime.jsx(
37733
+ "div",
37734
+ {
37735
+ className: cn(
37736
+ "bg-emerald-50 dark:bg-emerald-900/20 border-l-4 border-emerald-500 rounded-r-lg p-5 mb-6",
37737
+ className
37738
+ ),
37739
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
37740
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.Link2, { className: "text-emerald-600 dark:text-emerald-400 flex-shrink-0 mt-1", size: 20 }),
37741
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
37742
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-emerald-900 dark:text-emerald-100 mb-2", children: "Building On What You Know" }),
37743
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose dark:prose-invert prose-sm max-w-none text-gray-700 dark:text-gray-300", children: /* @__PURE__ */ jsxRuntime.jsx(exports.MarkdownContent, { content }) })
37744
+ ] })
37745
+ ] })
37746
+ }
37747
+ );
37748
+ exports.ConnectionBlock.displayName = "ConnectionBlock";
37749
+ }
37750
+ });
37751
+
37752
+ // components/core/molecules/lessonSegmentUtils.ts
37753
+ function parseMarkdownWithCodeBlocks2(content) {
37754
+ const segments = [];
37755
+ const codeBlockRegex = /```([\w-]+)?(?:\s+(run))?\n([\s\S]*?)```/g;
37756
+ let lastIndex = 0;
37757
+ let match;
37758
+ while ((match = codeBlockRegex.exec(content)) !== null) {
37759
+ const before = content.slice(lastIndex, match.index);
37760
+ if (before.trim()) {
37761
+ segments.push({ type: "markdown", content: before });
37762
+ }
37763
+ const rawLanguage = match[1] ?? "text";
37764
+ const runModifier = !!match[2];
37765
+ const suffixRunnable = rawLanguage.endsWith("-runnable");
37766
+ const runnable = runModifier || suffixRunnable;
37767
+ const baseLanguage = suffixRunnable ? rawLanguage.slice(0, -"-runnable".length) || "text" : rawLanguage;
37768
+ segments.push({ type: "code", language: baseLanguage, content: match[3].trim(), runnable });
37769
+ lastIndex = codeBlockRegex.lastIndex;
37770
+ }
37771
+ const remaining = content.slice(lastIndex);
37772
+ if (remaining.trim()) {
37773
+ segments.push({ type: "markdown", content: remaining });
37774
+ }
37775
+ return segments;
37776
+ }
37777
+ var init_lessonSegmentUtils = __esm({
37778
+ "components/core/molecules/lessonSegmentUtils.ts"() {
37779
+ }
37780
+ });
37781
+ var BLOOM_CONFIG; exports.BloomQuizBlock = void 0;
37782
+ var init_BloomQuizBlock = __esm({
37783
+ "components/core/molecules/BloomQuizBlock.tsx"() {
37784
+ "use client";
37785
+ init_MarkdownContent();
37786
+ init_CodeBlock();
37787
+ init_lessonSegmentUtils();
37788
+ init_useEventBus();
37789
+ init_cn();
37790
+ BLOOM_CONFIG = {
37791
+ remember: { color: "bg-gray-500", bgColor: "bg-gray-50 dark:bg-gray-900/30", label: "Remember" },
37792
+ understand: { color: "bg-blue-500", bgColor: "bg-blue-50 dark:bg-blue-900/30", label: "Understand" },
37793
+ apply: { color: "bg-green-500", bgColor: "bg-green-50 dark:bg-green-900/30", label: "Apply" },
37794
+ analyze: { color: "bg-yellow-500", bgColor: "bg-yellow-50 dark:bg-yellow-900/30", label: "Analyze" },
37795
+ evaluate: { color: "bg-orange-500", bgColor: "bg-orange-50 dark:bg-orange-900/30", label: "Evaluate" },
37796
+ create: { color: "bg-purple-500", bgColor: "bg-purple-50 dark:bg-purple-900/30", label: "Create" }
37797
+ };
37798
+ exports.BloomQuizBlock = ({
37799
+ level,
37800
+ question,
37801
+ answer,
37802
+ index,
37803
+ isAnswered,
37804
+ answerEvent = "ANSWER_BLOOM",
37805
+ className
37806
+ }) => {
37807
+ const [revealed, setRevealed] = React74.useState(false);
37808
+ const config = BLOOM_CONFIG[level];
37809
+ const { emit } = useEventBus();
37810
+ const questionSegments = React74.useMemo(() => parseMarkdownWithCodeBlocks2(question), [question]);
37811
+ const answerSegments = React74.useMemo(() => parseMarkdownWithCodeBlocks2(answer), [answer]);
37812
+ const handleReveal = () => {
37813
+ if (!revealed) {
37814
+ emit(`UI:${answerEvent}`, { index: index ?? 0, level });
37815
+ }
37816
+ setRevealed(!revealed);
37817
+ };
37818
+ return /* @__PURE__ */ jsxRuntime.jsxs(
37819
+ "div",
37820
+ {
37821
+ className: cn(
37822
+ "rounded-lg border border-indigo-100 dark:border-indigo-800 p-4 my-4 transition-all",
37823
+ config.bgColor,
37824
+ className
37825
+ ),
37826
+ children: [
37827
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
37828
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
37829
+ index !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-500 dark:text-gray-400 font-medium text-sm", children: [
37830
+ "Question ",
37831
+ index + 1
37832
+ ] }),
37833
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(config.color, "text-white text-xs px-2 py-1 rounded-full font-medium"), children: config.label })
37834
+ ] }),
37835
+ isAnswered && /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.CheckCircle, { className: "text-green-600 dark:text-green-400 flex-shrink-0", size: 20 })
37836
+ ] }),
37837
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-indigo-900 dark:text-indigo-200 mb-3 space-y-2", children: questionSegments.map(
37838
+ (segment, idx) => segment.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(exports.MarkdownContent, { content: segment.content }, `q-md-${idx}`) : /* @__PURE__ */ jsxRuntime.jsx(
37839
+ exports.CodeBlock,
37840
+ {
37841
+ language: segment.language ?? "text",
37842
+ code: segment.content
37843
+ },
37844
+ `q-code-${idx}`
37845
+ )
37846
+ ) }),
37847
+ /* @__PURE__ */ jsxRuntime.jsx(
37848
+ "button",
37849
+ {
37850
+ type: "button",
37851
+ className: "inline-flex items-center rounded-md bg-indigo-600 dark:bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white hover:bg-indigo-700 dark:hover:bg-indigo-600 transition-colors",
37852
+ onClick: handleReveal,
37853
+ children: revealed ? "Hide Answer" : "Reveal Answer"
37854
+ }
37855
+ ),
37856
+ revealed && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg bg-white/80 dark:bg-gray-800/80 p-3 text-sm text-slate-800 dark:text-gray-200 shadow-sm border border-indigo-100 dark:border-indigo-800 mt-3 space-y-2", children: [
37857
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-600 dark:text-gray-400 mb-1 font-medium uppercase tracking-wide", children: "Answer:" }),
37858
+ answerSegments.map(
37859
+ (segment, idx) => segment.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(exports.MarkdownContent, { content: segment.content }, `a-md-${idx}`) : /* @__PURE__ */ jsxRuntime.jsx(
37860
+ exports.CodeBlock,
37861
+ {
37862
+ language: segment.language ?? "text",
37863
+ code: segment.content
37864
+ },
37865
+ `a-code-${idx}`
37866
+ )
37867
+ )
37868
+ ] })
37869
+ ]
37870
+ }
37871
+ );
37872
+ };
37873
+ exports.BloomQuizBlock.displayName = "BloomQuizBlock";
37874
+ }
37875
+ });
37876
+
37877
+ // components/core/molecules/parseLessonSegments.ts
37878
+ function extractTagContent(content, tagName) {
37879
+ const closedTagRegex = new RegExp(`<${tagName}>([\\s\\S]*?)<\\/${tagName}>`, "i");
37880
+ const closedMatch = content.match(closedTagRegex);
37881
+ if (closedMatch) {
37882
+ return { content: closedMatch[1].trim(), fullMatch: closedMatch[0] };
37883
+ }
37884
+ const unclosedTagRegex = new RegExp(
37885
+ `<${tagName}>([\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$)`,
37886
+ "i"
37887
+ );
37888
+ const unclosedMatch = content.match(unclosedTagRegex);
37889
+ if (unclosedMatch) {
37890
+ return { content: unclosedMatch[1].trim(), fullMatch: unclosedMatch[0] };
37891
+ }
37892
+ return null;
37893
+ }
37894
+ function parseLessonSegments(lesson) {
37895
+ if (!lesson) return [];
37896
+ let content = lesson.replace(/<prq>[\s\S]*?<\/prq>/gi, "").trim();
37897
+ const segments = [];
37898
+ const activateResult = extractTagContent(content, "activate");
37899
+ if (activateResult) {
37900
+ segments.push({ type: "activate", question: activateResult.content });
37901
+ content = content.replace(activateResult.fullMatch, "").trim();
37902
+ }
37903
+ const connectResult = extractTagContent(content, "connect");
37904
+ if (connectResult) {
37905
+ segments.push({ type: "connect", content: connectResult.content });
37906
+ content = content.replace(connectResult.fullMatch, "").trim();
37907
+ }
37908
+ const tagRegex = new RegExp(
37909
+ '(?<reflect><reflect>(?<reflectClosed>[\\s\\S]*?)<\\/reflect>)|(?<reflectUnclosed><reflect>(?<reflectOpen>[\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$))|(?<bloom><bloom\\s+level="(?<bloomLevel>remember|understand|apply|analyze|evaluate|create)">(?<bloomClosed>[\\s\\S]*?)<\\/bloom>)|(?<bloomUnclosed><bloom\\s+level="(?<bloomLevelUn>remember|understand|apply|analyze|evaluate|create)">(?<bloomOpen>[\\s\\S]*?)(?=<(?:activate|connect|reflect|bloom|prq|question|answer|visualize)|\\n\\n#|$))|(?<quiz><question>(?<quizQuestion>[\\s\\S]*?)<\\/question>\\s*<answer>(?<quizAnswer>[\\s\\S]*?)<\\/answer>)|(?<visualize><visualize\\s+type="(?<vizType>chart|simulation)"\\s+description="(?<vizDesc>[^"]*?)"\\s*\\/?>)',
37910
+ "gi"
37911
+ );
37912
+ let lastIndex = 0;
37913
+ let match;
37914
+ while ((match = tagRegex.exec(content)) !== null) {
37915
+ const before = content.slice(lastIndex, match.index);
37916
+ if (before.trim()) {
37917
+ segments.push(...parseMarkdownWithCodeBlocks2(before));
37918
+ }
37919
+ const g = match.groups ?? {};
37920
+ if (g.reflect || g.reflectUnclosed) {
37921
+ const prompt = (g.reflectClosed ?? g.reflectOpen ?? "").trim();
37922
+ if (prompt) segments.push({ type: "reflect", prompt });
37923
+ } else if (g.bloom || g.bloomUnclosed) {
37924
+ const level = g.bloomLevel ?? g.bloomLevelUn;
37925
+ const bloomContent = g.bloomClosed ?? g.bloomOpen ?? "";
37926
+ if (level && bloomContent) {
37927
+ const qMatch = bloomContent.match(/<question>([\s\S]*?)<\/question>/i);
37928
+ const aMatch = bloomContent.match(/<answer>([\s\S]*?)<\/answer>/i);
37929
+ if (qMatch && aMatch) {
37930
+ segments.push({ type: "bloom", level, question: qMatch[1].trim(), answer: aMatch[1].trim() });
37931
+ } else if (qMatch) {
37932
+ segments.push({ type: "bloom", level, question: qMatch[1].trim(), answer: "(Answer not provided)" });
37933
+ } else {
37934
+ const clean = bloomContent.replace(/^\*\*Question\s*\d*:?\*\*\s*/i, "").replace(/^\*\*Q\d*:?\*\*\s*/i, "").trim();
37935
+ if (clean) segments.push({ type: "bloom", level, question: clean, answer: "(See answers section below)" });
37936
+ }
37937
+ }
37938
+ } else if (g.quiz) {
37939
+ segments.push({ type: "quiz", question: g.quizQuestion.trim(), answer: g.quizAnswer.trim() });
37940
+ } else if (g.visualize) {
37941
+ segments.push({
37942
+ type: "visualization",
37943
+ visualizationType: g.vizType,
37944
+ description: g.vizDesc ?? ""
37945
+ });
37946
+ }
37947
+ lastIndex = tagRegex.lastIndex;
37948
+ }
37949
+ const remaining = content.slice(lastIndex);
37950
+ if (remaining.trim()) segments.push(...parseMarkdownWithCodeBlocks2(remaining));
37951
+ return segments;
37952
+ }
37953
+ var init_parseLessonSegments = __esm({
37954
+ "components/core/molecules/parseLessonSegments.ts"() {
37955
+ init_lessonSegmentUtils();
37956
+ }
37957
+ });
37573
37958
 
37574
37959
  // components/core/molecules/index.ts
37575
37960
  var init_molecules2 = __esm({
@@ -37701,6 +38086,12 @@ var init_molecules2 = __esm({
37701
38086
  init_SignaturePad();
37702
38087
  init_DocumentViewer();
37703
38088
  init_GraphCanvas();
38089
+ init_ActivationBlock();
38090
+ init_ReflectionBlock();
38091
+ init_ConnectionBlock();
38092
+ init_BloomQuizBlock();
38093
+ init_parseLessonSegments();
38094
+ init_lessonSegmentUtils();
37704
38095
  }
37705
38096
  });
37706
38097
 
@@ -50084,6 +50475,271 @@ init_ShowcaseOrganism();
50084
50475
  init_TeamOrganism();
50085
50476
  init_CaseStudyOrganism();
50086
50477
 
50478
+ // components/core/organisms/CodeRunnerPanel.tsx
50479
+ init_Box();
50480
+ init_Button();
50481
+ init_Badge();
50482
+ init_Typography();
50483
+ init_Stack();
50484
+ init_CodeBlock();
50485
+ init_useEventBus();
50486
+ init_cn();
50487
+ var CodeRunnerPanel = ({
50488
+ code: initialCode,
50489
+ language,
50490
+ runnable = true,
50491
+ onRun,
50492
+ runEvent = "RUN_CODE",
50493
+ className
50494
+ }) => {
50495
+ const eventBus = useEventBus();
50496
+ const { t } = hooks.useTranslate();
50497
+ const [code, setCode] = React74.useState(initialCode);
50498
+ const [output, setOutput] = React74.useState(null);
50499
+ const [error, setError] = React74.useState(null);
50500
+ const [isRunning, setIsRunning] = React74.useState(false);
50501
+ const handleRun = React74.useCallback(async () => {
50502
+ if (!onRun) return;
50503
+ setIsRunning(true);
50504
+ setError(null);
50505
+ setOutput(null);
50506
+ try {
50507
+ const result = await onRun(code);
50508
+ setOutput(result);
50509
+ eventBus.emit(`UI:${runEvent}`, { language, exitCode: result.exitCode });
50510
+ } catch (err) {
50511
+ const message = err instanceof Error ? err.message : t("common.error");
50512
+ setError(message);
50513
+ eventBus.emit(`UI:${runEvent}`, { language, exitCode: 1, error: message });
50514
+ } finally {
50515
+ setIsRunning(false);
50516
+ }
50517
+ }, [code, language, onRun, runEvent, eventBus, t]);
50518
+ const handleReset = React74.useCallback(() => {
50519
+ setCode(initialCode);
50520
+ setOutput(null);
50521
+ setError(null);
50522
+ }, [initialCode]);
50523
+ if (!runnable || !onRun) {
50524
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className, children: /* @__PURE__ */ jsxRuntime.jsx(exports.CodeBlock, { language, code }) });
50525
+ }
50526
+ const hasOutput = output !== null || error !== null;
50527
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: cn("space-y-3", className), children: [
50528
+ /* @__PURE__ */ jsxRuntime.jsx(
50529
+ exports.CodeBlock,
50530
+ {
50531
+ language,
50532
+ code,
50533
+ editable: true,
50534
+ onChange: setCode,
50535
+ showLanguageBadge: true,
50536
+ showCopyButton: true
50537
+ }
50538
+ ),
50539
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "between", children: [
50540
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
50541
+ /* @__PURE__ */ jsxRuntime.jsx(
50542
+ exports.Button,
50543
+ {
50544
+ variant: "primary",
50545
+ size: "sm",
50546
+ onClick: handleRun,
50547
+ disabled: isRunning,
50548
+ className: "min-w-[5rem]",
50549
+ children: isRunning ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", children: [
50550
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.RotateCcw, { size: 16, className: "animate-spin" }),
50551
+ t("common.loading")
50552
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", children: [
50553
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.Play, { size: 16 }),
50554
+ "Run"
50555
+ ] })
50556
+ }
50557
+ ),
50558
+ /* @__PURE__ */ jsxRuntime.jsx(
50559
+ exports.Button,
50560
+ {
50561
+ variant: "secondary",
50562
+ size: "sm",
50563
+ onClick: handleReset,
50564
+ disabled: isRunning,
50565
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2", children: [
50566
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.RotateCcw, { size: 16 }),
50567
+ "Reset"
50568
+ ] })
50569
+ }
50570
+ )
50571
+ ] }),
50572
+ output && /* @__PURE__ */ jsxRuntime.jsxs(
50573
+ exports.Badge,
50574
+ {
50575
+ variant: output.exitCode === 0 ? "success" : "danger",
50576
+ size: "sm",
50577
+ children: [
50578
+ "Exit ",
50579
+ output.exitCode
50580
+ ]
50581
+ }
50582
+ )
50583
+ ] }),
50584
+ hasOutput && /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "rounded-lg border border-gray-700 bg-[#0d0d0d] overflow-hidden", children: [
50585
+ /* @__PURE__ */ jsxRuntime.jsxs(
50586
+ exports.HStack,
50587
+ {
50588
+ gap: "sm",
50589
+ align: "center",
50590
+ className: "px-3 py-2 bg-gray-800 border-b border-gray-700",
50591
+ children: [
50592
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.Terminal, { size: 16, className: "text-gray-400" }),
50593
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-gray-300 font-medium", children: "Output" })
50594
+ ]
50595
+ }
50596
+ ),
50597
+ /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", className: "p-3 font-mono text-sm", children: error ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-red-400 whitespace-pre-wrap", children: error }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
50598
+ output?.stdout ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-gray-200 whitespace-pre-wrap", children: output.stdout }) : null,
50599
+ output?.stderr ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-red-400 whitespace-pre-wrap", children: output.stderr }) : null,
50600
+ !output?.stdout && !output?.stderr ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-gray-500 italic", children: "No output" }) : null,
50601
+ output && output.testResults.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mt-3 pt-3 border-t border-gray-700 space-y-2", children: output.testResults.map((test, index) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "start", className: "text-xs", children: [
50602
+ test.passed ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.CheckCircle, { size: 14, className: "text-green-400 mt-0.5" }) : /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.XCircle, { size: 14, className: "text-red-400 mt-0.5" }),
50603
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "flex-1", children: [
50604
+ /* @__PURE__ */ jsxRuntime.jsxs(
50605
+ exports.Typography,
50606
+ {
50607
+ variant: "small",
50608
+ className: test.passed ? "text-green-400" : "text-red-400",
50609
+ children: [
50610
+ "Test ",
50611
+ index + 1,
50612
+ ": ",
50613
+ test.passed ? "passed" : "failed"
50614
+ ]
50615
+ }
50616
+ ),
50617
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "text-gray-400", children: [
50618
+ "Input: ",
50619
+ test.input
50620
+ ] }),
50621
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "text-gray-400", children: [
50622
+ "Expected: ",
50623
+ test.expectedOutput
50624
+ ] }),
50625
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "text-gray-400", children: [
50626
+ "Actual: ",
50627
+ test.actualOutput
50628
+ ] })
50629
+ ] })
50630
+ ] }, index)) })
50631
+ ] }) })
50632
+ ] })
50633
+ ] });
50634
+ };
50635
+ CodeRunnerPanel.displayName = "CodeRunnerPanel";
50636
+
50637
+ // components/core/organisms/SegmentRenderer.tsx
50638
+ init_MarkdownContent();
50639
+ init_CodeBlock();
50640
+ init_QuizBlock();
50641
+ init_ActivationBlock();
50642
+ init_ConnectionBlock();
50643
+ init_ReflectionBlock();
50644
+ init_BloomQuizBlock();
50645
+ init_cn();
50646
+ var SegmentRenderer = ({
50647
+ segments,
50648
+ className,
50649
+ containerClassName,
50650
+ userProgress,
50651
+ onRunCodeSimulation,
50652
+ onRenderVisualization
50653
+ }) => {
50654
+ if (segments.length === 0) return null;
50655
+ let reflectIndex = 0;
50656
+ let bloomIndex = 0;
50657
+ return /* @__PURE__ */ jsxRuntime.jsx(
50658
+ "div",
50659
+ {
50660
+ className: cn(
50661
+ "border border-gray-200 dark:border-gray-700 rounded-lg p-2 md:p-4 overflow-x-auto space-y-6",
50662
+ containerClassName,
50663
+ className
50664
+ ),
50665
+ children: segments.map((segment, index) => {
50666
+ if (segment.type === "markdown") {
50667
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.MarkdownContent, { content: segment.content }, `md-${index}`);
50668
+ }
50669
+ if (segment.type === "code") {
50670
+ if (segment.runnable && onRunCodeSimulation) {
50671
+ return /* @__PURE__ */ jsxRuntime.jsx(
50672
+ CodeRunnerPanel,
50673
+ {
50674
+ language: segment.language,
50675
+ code: segment.content,
50676
+ runnable: true,
50677
+ onRun: (code) => onRunCodeSimulation(code, segment.language)
50678
+ },
50679
+ `code-${index}`
50680
+ );
50681
+ }
50682
+ return /* @__PURE__ */ jsxRuntime.jsx(
50683
+ exports.CodeBlock,
50684
+ {
50685
+ language: segment.language ?? "text",
50686
+ code: segment.content
50687
+ },
50688
+ `code-${index}`
50689
+ );
50690
+ }
50691
+ if (segment.type === "quiz") {
50692
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.QuizBlock, { question: segment.question, answer: segment.answer }, `quiz-${index}`);
50693
+ }
50694
+ if (segment.type === "activate") {
50695
+ return /* @__PURE__ */ jsxRuntime.jsx(
50696
+ exports.ActivationBlock,
50697
+ {
50698
+ question: segment.question,
50699
+ savedResponse: userProgress?.activationResponse
50700
+ },
50701
+ `activate-${index}`
50702
+ );
50703
+ }
50704
+ if (segment.type === "connect") {
50705
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.ConnectionBlock, { content: segment.content }, `connect-${index}`);
50706
+ }
50707
+ if (segment.type === "reflect") {
50708
+ const ri = reflectIndex++;
50709
+ return /* @__PURE__ */ jsxRuntime.jsx(
50710
+ exports.ReflectionBlock,
50711
+ {
50712
+ prompt: segment.prompt,
50713
+ index: ri,
50714
+ savedNote: userProgress?.reflectionNotes?.[ri]
50715
+ },
50716
+ `reflect-${index}`
50717
+ );
50718
+ }
50719
+ if (segment.type === "bloom") {
50720
+ const bi = bloomIndex++;
50721
+ return /* @__PURE__ */ jsxRuntime.jsx(
50722
+ exports.BloomQuizBlock,
50723
+ {
50724
+ level: segment.level,
50725
+ question: segment.question,
50726
+ answer: segment.answer,
50727
+ index: bi,
50728
+ isAnswered: userProgress?.bloomAnswered?.[bi]
50729
+ },
50730
+ `bloom-${index}`
50731
+ );
50732
+ }
50733
+ if (segment.type === "visualization") {
50734
+ return onRenderVisualization ? onRenderVisualization(segment.visualizationType, segment.description, index) ?? null : null;
50735
+ }
50736
+ return null;
50737
+ })
50738
+ }
50739
+ );
50740
+ };
50741
+ SegmentRenderer.displayName = "SegmentRenderer";
50742
+
50087
50743
  // components/core/templates/index.ts
50088
50744
  init_DashboardLayout();
50089
50745
  init_AuthLayout();
@@ -50116,6 +50772,7 @@ exports.CastleBoard = CastleBoard;
50116
50772
  exports.CastleTemplate = CastleTemplate;
50117
50773
  exports.ChoiceButton = ChoiceButton;
50118
50774
  exports.ClassifierBoard = ClassifierBoard;
50775
+ exports.CodeRunnerPanel = CodeRunnerPanel;
50119
50776
  exports.CollapsibleSection = CollapsibleSection;
50120
50777
  exports.CombatLog = CombatLog;
50121
50778
  exports.ComboCounter = ComboCounter;
@@ -50173,6 +50830,7 @@ exports.SHEET_COLUMNS = SHEET_COLUMNS;
50173
50830
  exports.SPRITE_SHEET_LAYOUT = SPRITE_SHEET_LAYOUT;
50174
50831
  exports.ScoreBoard = ScoreBoard;
50175
50832
  exports.ScoreDisplay = ScoreDisplay;
50833
+ exports.SegmentRenderer = SegmentRenderer;
50176
50834
  exports.SequenceBar = SequenceBar;
50177
50835
  exports.SequencerBoard = SequencerBoard;
50178
50836
  exports.SimulationCanvas = SimulationCanvas;
@@ -50226,6 +50884,8 @@ exports.getTileDimensions = getTileDimensions;
50226
50884
  exports.inferDirection = inferDirection;
50227
50885
  exports.isoToScreen = isoToScreen;
50228
50886
  exports.mapBookData = mapBookData;
50887
+ exports.parseLessonSegments = parseLessonSegments;
50888
+ exports.parseMarkdownWithCodeBlocks = parseMarkdownWithCodeBlocks2;
50229
50889
  exports.resolveFieldMap = resolveFieldMap;
50230
50890
  exports.resolveFrame = resolveFrame;
50231
50891
  exports.resolveSheetDirection = resolveSheetDirection;