@almadar/ui 5.146.3 → 5.148.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/avl/index.js CHANGED
@@ -15252,7 +15252,10 @@ var init_LearningCanvas = __esm({
15252
15252
  ctx.fillRect(0, 0, width, height);
15253
15253
  }
15254
15254
  for (const shape of shapes) {
15255
- drawShape(ctx, shape, width, height);
15255
+ if (shape.type !== "text") drawShape(ctx, shape, width, height);
15256
+ }
15257
+ for (const shape of shapes) {
15258
+ if (shape.type === "text") drawShape(ctx, shape, width, height);
15256
15259
  }
15257
15260
  }, [width, height, backgroundColor, shapes]);
15258
15261
  useEffect(() => {
@@ -16815,7 +16818,7 @@ var init_AlgorithmCanvas = __esm({
16815
16818
  DEFAULT_CELL_COLOR = "#e5e7eb";
16816
16819
  DEFAULT_POINTER_COLOR = "#dc2626";
16817
16820
  POINTER_BAND = 34;
16818
- TOP_PAD = 12;
16821
+ TOP_PAD = 26;
16819
16822
  AlgorithmCanvas = ({
16820
16823
  className,
16821
16824
  width = 600,
@@ -27594,6 +27597,7 @@ var init_FilterGroup = __esm({
27594
27597
  init_Badge();
27595
27598
  init_Stack();
27596
27599
  init_Icon();
27600
+ init_RangeSlider();
27597
27601
  init_useEventBus();
27598
27602
  init_useQuerySingleton();
27599
27603
  resolveFilterType = (filter) => filter.filterType ?? filter.type;
@@ -27788,6 +27792,35 @@ var init_FilterGroup = __esm({
27788
27792
  onClear: () => handleFilterSelect(`${filter.field}_to`, null)
27789
27793
  }
27790
27794
  )
27795
+ ] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
27796
+ /* @__PURE__ */ jsx(
27797
+ RangeSlider,
27798
+ {
27799
+ min: filter.min ?? 0,
27800
+ max: filter.max ?? 100,
27801
+ step: filter.step ?? 1,
27802
+ value: Number(
27803
+ selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
27804
+ ),
27805
+ onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
27806
+ showTooltip: true,
27807
+ "aria-label": t("filterGroup.from")
27808
+ }
27809
+ ),
27810
+ /* @__PURE__ */ jsx(
27811
+ RangeSlider,
27812
+ {
27813
+ min: filter.min ?? 0,
27814
+ max: filter.max ?? 100,
27815
+ step: filter.step ?? 1,
27816
+ value: Number(
27817
+ selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
27818
+ ),
27819
+ onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
27820
+ showTooltip: true,
27821
+ "aria-label": t("filterGroup.to")
27822
+ }
27823
+ )
27791
27824
  ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
27792
27825
  Input,
27793
27826
  {
@@ -27870,6 +27903,36 @@ var init_FilterGroup = __esm({
27870
27903
  className: "text-sm min-w-[100px]"
27871
27904
  }
27872
27905
  )
27906
+ ] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
27907
+ /* @__PURE__ */ jsx(
27908
+ RangeSlider,
27909
+ {
27910
+ min: filter.min ?? 0,
27911
+ max: filter.max ?? 100,
27912
+ step: filter.step ?? 1,
27913
+ value: Number(
27914
+ selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
27915
+ ),
27916
+ onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
27917
+ className: "min-w-[100px]",
27918
+ "aria-label": t("filterGroup.from")
27919
+ }
27920
+ ),
27921
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" }),
27922
+ /* @__PURE__ */ jsx(
27923
+ RangeSlider,
27924
+ {
27925
+ min: filter.min ?? 0,
27926
+ max: filter.max ?? 100,
27927
+ step: filter.step ?? 1,
27928
+ value: Number(
27929
+ selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
27930
+ ),
27931
+ onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
27932
+ className: "min-w-[100px]",
27933
+ "aria-label": t("filterGroup.to")
27934
+ }
27935
+ )
27873
27936
  ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
27874
27937
  Input,
27875
27938
  {
@@ -27988,6 +28051,38 @@ var init_FilterGroup = __esm({
27988
28051
  className: "min-w-[130px]"
27989
28052
  }
27990
28053
  )
28054
+ ] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
28055
+ /* @__PURE__ */ jsx(
28056
+ RangeSlider,
28057
+ {
28058
+ min: filter.min ?? 0,
28059
+ max: filter.max ?? 100,
28060
+ step: filter.step ?? 1,
28061
+ value: Number(
28062
+ selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
28063
+ ),
28064
+ onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
28065
+ showTooltip: true,
28066
+ className: "min-w-[130px]",
28067
+ "aria-label": t("filterGroup.from")
28068
+ }
28069
+ ),
28070
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" }),
28071
+ /* @__PURE__ */ jsx(
28072
+ RangeSlider,
28073
+ {
28074
+ min: filter.min ?? 0,
28075
+ max: filter.max ?? 100,
28076
+ step: filter.step ?? 1,
28077
+ value: Number(
28078
+ selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
28079
+ ),
28080
+ onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
28081
+ showTooltip: true,
28082
+ className: "min-w-[130px]",
28083
+ "aria-label": t("filterGroup.to")
28084
+ }
28085
+ )
27991
28086
  ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
27992
28087
  Input,
27993
28088
  {
@@ -29887,7 +29982,12 @@ function daysAgo(n) {
29887
29982
  d.setDate(d.getDate() - n);
29888
29983
  return d;
29889
29984
  }
29890
- var DEFAULT_PRESETS, DateRangePicker;
29985
+ function resolvePresetRange(preset) {
29986
+ if (typeof preset.range === "function") return preset.range();
29987
+ if (preset.range) return preset.range;
29988
+ return TOKEN_RANGES[preset.value]?.() ?? null;
29989
+ }
29990
+ var TOKEN_RANGES, DEFAULT_PRESETS, DateRangePicker;
29891
29991
  var init_DateRangePicker = __esm({
29892
29992
  "components/core/molecules/DateRangePicker.tsx"() {
29893
29993
  "use client";
@@ -29897,32 +29997,19 @@ var init_DateRangePicker = __esm({
29897
29997
  init_Stack();
29898
29998
  init_Typography();
29899
29999
  init_useEventBus();
30000
+ TOKEN_RANGES = {
30001
+ "7d": () => ({ from: toISODate(daysAgo(7)), to: toISODate(/* @__PURE__ */ new Date()) }),
30002
+ "30d": () => ({ from: toISODate(daysAgo(30)), to: toISODate(/* @__PURE__ */ new Date()) }),
30003
+ month: () => ({ from: toISODate(startOfMonth(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) }),
30004
+ quarter: () => ({ from: toISODate(startOfQuarter(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) }),
30005
+ ytd: () => ({ from: toISODate(startOfYear(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
30006
+ };
29900
30007
  DEFAULT_PRESETS = [
29901
- {
29902
- label: "Last 7 days",
29903
- value: "7d",
29904
- range: () => ({ from: toISODate(daysAgo(7)), to: toISODate(/* @__PURE__ */ new Date()) })
29905
- },
29906
- {
29907
- label: "Last 30 days",
29908
- value: "30d",
29909
- range: () => ({ from: toISODate(daysAgo(30)), to: toISODate(/* @__PURE__ */ new Date()) })
29910
- },
29911
- {
29912
- label: "This Month",
29913
- value: "month",
29914
- range: () => ({ from: toISODate(startOfMonth(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
29915
- },
29916
- {
29917
- label: "This Quarter",
29918
- value: "quarter",
29919
- range: () => ({ from: toISODate(startOfQuarter(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
29920
- },
29921
- {
29922
- label: "YTD",
29923
- value: "ytd",
29924
- range: () => ({ from: toISODate(startOfYear(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
29925
- }
30008
+ { label: "Last 7 days", value: "7d" },
30009
+ { label: "Last 30 days", value: "30d" },
30010
+ { label: "This Month", value: "month" },
30011
+ { label: "This Quarter", value: "quarter" },
30012
+ { label: "YTD", value: "ytd" }
29926
30013
  ];
29927
30014
  DateRangePicker = ({
29928
30015
  from: fromProp,
@@ -29963,7 +30050,8 @@ var init_DateRangePicker = __esm({
29963
30050
  );
29964
30051
  const handlePreset = useCallback(
29965
30052
  (preset) => {
29966
- const range = preset.range();
30053
+ const range = resolvePresetRange(preset);
30054
+ if (range === null) return;
29967
30055
  setFrom(range.from);
29968
30056
  setTo(range.to);
29969
30057
  setActivePreset(preset.value);
@@ -29971,8 +30059,12 @@ var init_DateRangePicker = __esm({
29971
30059
  },
29972
30060
  [emit]
29973
30061
  );
30062
+ const renderablePresets = useMemo(
30063
+ () => presets.filter((p) => p.range !== void 0 || TOKEN_RANGES[p.value] !== void 0),
30064
+ [presets]
30065
+ );
29974
30066
  const presetButtons = useMemo(
29975
- () => presets.map((preset) => /* @__PURE__ */ jsx(
30067
+ () => renderablePresets.map((preset) => /* @__PURE__ */ jsx(
29976
30068
  Button,
29977
30069
  {
29978
30070
  variant: activePreset === preset.value ? "primary" : "ghost",
@@ -29982,7 +30074,7 @@ var init_DateRangePicker = __esm({
29982
30074
  },
29983
30075
  preset.value
29984
30076
  )),
29985
- [presets, activePreset, handlePreset]
30077
+ [renderablePresets, activePreset, handlePreset]
29986
30078
  );
29987
30079
  return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn(className), children: [
29988
30080
  /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "end", children: [
@@ -30009,7 +30101,7 @@ var init_DateRangePicker = __esm({
30009
30101
  )
30010
30102
  ] })
30011
30103
  ] }),
30012
- presets.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: presetButtons })
30104
+ renderablePresets.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: presetButtons })
30013
30105
  ] });
30014
30106
  };
30015
30107
  DateRangePicker.displayName = "DateRangePicker";
@@ -30451,6 +30543,208 @@ var init_PhysicsCanvas = __esm({
30451
30543
  };
30452
30544
  }
30453
30545
  });
30546
+
30547
+ // lib/graphViewLayouts.ts
30548
+ function buildAdjacency(nodeIds, edges) {
30549
+ const known = new Set(nodeIds);
30550
+ const out = /* @__PURE__ */ new Map();
30551
+ const inMap = /* @__PURE__ */ new Map();
30552
+ for (const id of nodeIds) {
30553
+ out.set(id, []);
30554
+ inMap.set(id, []);
30555
+ }
30556
+ for (const edge of edges) {
30557
+ if (!known.has(edge.source) || !known.has(edge.target)) continue;
30558
+ out.get(edge.source)?.push(edge.target);
30559
+ inMap.get(edge.target)?.push(edge.source);
30560
+ }
30561
+ return { out, in: inMap };
30562
+ }
30563
+ function findRoots(nodeIds, adjacency) {
30564
+ return nodeIds.filter((id) => (adjacency.in.get(id)?.length ?? 0) === 0);
30565
+ }
30566
+ function assignLayers(nodeIds, adjacency, roots) {
30567
+ const layer = new Map(nodeIds.map((id) => [id, 0]));
30568
+ const onStack = /* @__PURE__ */ new Set();
30569
+ const visit = (id) => {
30570
+ onStack.add(id);
30571
+ const currentLayer = layer.get(id) ?? 0;
30572
+ for (const next of adjacency.out.get(id) ?? []) {
30573
+ if (onStack.has(next)) continue;
30574
+ const candidate = currentLayer + 1;
30575
+ if (candidate > (layer.get(next) ?? 0)) {
30576
+ layer.set(next, candidate);
30577
+ }
30578
+ visit(next);
30579
+ }
30580
+ onStack.delete(id);
30581
+ };
30582
+ for (const root of roots) visit(root);
30583
+ return layer;
30584
+ }
30585
+ function computeVisitOrder(nodeIds, adjacency, roots) {
30586
+ const order = /* @__PURE__ */ new Map();
30587
+ const visited = /* @__PURE__ */ new Set();
30588
+ let counter = 0;
30589
+ const visit = (id) => {
30590
+ if (visited.has(id)) return;
30591
+ visited.add(id);
30592
+ order.set(id, counter++);
30593
+ for (const next of adjacency.out.get(id) ?? []) visit(next);
30594
+ };
30595
+ for (const root of roots) visit(root);
30596
+ for (const id of nodeIds) {
30597
+ if (!visited.has(id)) order.set(id, counter++);
30598
+ }
30599
+ return order;
30600
+ }
30601
+ function bfsDepthAndOrder(nodeIds, adjacency, roots) {
30602
+ const depth = new Map(nodeIds.map((id) => [id, 0]));
30603
+ const visitOrder = /* @__PURE__ */ new Map();
30604
+ const visited = /* @__PURE__ */ new Set();
30605
+ const queue = [];
30606
+ let counter = 0;
30607
+ for (const root of roots) {
30608
+ if (visited.has(root)) continue;
30609
+ visited.add(root);
30610
+ depth.set(root, 0);
30611
+ visitOrder.set(root, counter++);
30612
+ queue.push(root);
30613
+ }
30614
+ let head = 0;
30615
+ while (head < queue.length) {
30616
+ const id = queue[head++];
30617
+ const d = depth.get(id) ?? 0;
30618
+ for (const next of adjacency.out.get(id) ?? []) {
30619
+ if (visited.has(next)) continue;
30620
+ visited.add(next);
30621
+ depth.set(next, d + 1);
30622
+ visitOrder.set(next, counter++);
30623
+ queue.push(next);
30624
+ }
30625
+ }
30626
+ for (const id of nodeIds) {
30627
+ if (!visited.has(id)) visitOrder.set(id, counter++);
30628
+ }
30629
+ return { depth, visitOrder };
30630
+ }
30631
+ function edgeWalkOrder(nodeIds, adjacency) {
30632
+ const visited = /* @__PURE__ */ new Set();
30633
+ const result = [];
30634
+ for (const start of nodeIds) {
30635
+ if (visited.has(start)) continue;
30636
+ let current = start;
30637
+ while (current !== void 0 && !visited.has(current)) {
30638
+ visited.add(current);
30639
+ result.push(current);
30640
+ const outs = adjacency.out.get(current) ?? [];
30641
+ current = outs.find((next) => !visited.has(next));
30642
+ }
30643
+ }
30644
+ return result;
30645
+ }
30646
+ function groupByTier(nodeIds, tierOf, maxTier) {
30647
+ const groups = Array.from({ length: maxTier + 1 }, () => []);
30648
+ for (const id of nodeIds) {
30649
+ groups[tierOf.get(id) ?? 0].push(id);
30650
+ }
30651
+ return groups;
30652
+ }
30653
+ function distributeAxis(count, start, end) {
30654
+ if (count <= 0) return [];
30655
+ const slot = (end - start) / count;
30656
+ return Array.from({ length: count }, (_, i) => start + slot * (i + 0.5));
30657
+ }
30658
+ function orderByParentPositionThenInput(group, adjacency, positioned, inputIndex) {
30659
+ const withKey = group.map((id) => {
30660
+ const parentValues = (adjacency.in.get(id) ?? []).map((p) => positioned.get(p)).filter((v) => v !== void 0);
30661
+ const avg = parentValues.length > 0 ? parentValues.reduce((a, b) => a + b, 0) / parentValues.length : Number.POSITIVE_INFINITY;
30662
+ return { id, avg, idx: inputIndex.get(id) ?? 0 };
30663
+ });
30664
+ withKey.sort((a, b) => a.avg !== b.avg ? a.avg - b.avg : a.idx - b.idx);
30665
+ return withKey.map((k) => k.id);
30666
+ }
30667
+ function layoutFlow(nodeIds, adjacency, roots, width, height, margin) {
30668
+ const inputIndex = new Map(nodeIds.map((id, i) => [id, i]));
30669
+ const layers = assignLayers(nodeIds, adjacency, roots);
30670
+ const maxLayer = Math.max(...Array.from(layers.values()));
30671
+ const layerGroups = groupByTier(nodeIds, layers, maxLayer);
30672
+ const positions = /* @__PURE__ */ new Map();
30673
+ const yById = /* @__PURE__ */ new Map();
30674
+ for (let l = 0; l <= maxLayer; l++) {
30675
+ const x = margin + l * (width - 2 * margin) / Math.max(1, maxLayer);
30676
+ const ordered2 = orderByParentPositionThenInput(layerGroups[l], adjacency, yById, inputIndex);
30677
+ const ys = distributeAxis(ordered2.length, margin, height - margin);
30678
+ ordered2.forEach((id, i) => {
30679
+ positions.set(id, { id, x, y: ys[i] });
30680
+ yById.set(id, ys[i]);
30681
+ });
30682
+ }
30683
+ return nodeIds.map((id) => positions.get(id));
30684
+ }
30685
+ function layoutTree2(nodeIds, adjacency, roots, width, height, margin) {
30686
+ const effectiveRoots = roots.length > 0 ? roots : [nodeIds[0]];
30687
+ const layers = assignLayers(nodeIds, adjacency, effectiveRoots);
30688
+ const maxLayer = Math.max(...Array.from(layers.values()));
30689
+ const visitOrder = computeVisitOrder(nodeIds, adjacency, effectiveRoots);
30690
+ const layerGroups = groupByTier(nodeIds, layers, maxLayer);
30691
+ const positions = /* @__PURE__ */ new Map();
30692
+ for (let l = 0; l <= maxLayer; l++) {
30693
+ const ordered2 = [...layerGroups[l]].sort(
30694
+ (a, b) => (visitOrder.get(a) ?? 0) - (visitOrder.get(b) ?? 0)
30695
+ );
30696
+ const y = margin + l * (height - 2 * margin) / Math.max(1, maxLayer);
30697
+ const xs = distributeAxis(ordered2.length, margin, width - margin);
30698
+ ordered2.forEach((id, i) => positions.set(id, { id, x: xs[i], y }));
30699
+ }
30700
+ return nodeIds.map((id) => positions.get(id));
30701
+ }
30702
+ function layoutRadial(nodeIds, adjacency, roots, width, height, margin) {
30703
+ const cx = width / 2;
30704
+ const cy = height / 2;
30705
+ const maxRadius = Math.min(width, height) / 2 - margin;
30706
+ if (roots.length === 0) {
30707
+ const order = edgeWalkOrder(nodeIds, adjacency);
30708
+ const k = order.length;
30709
+ const positions2 = /* @__PURE__ */ new Map();
30710
+ order.forEach((id, i) => {
30711
+ const angle = i / k * 2 * Math.PI;
30712
+ positions2.set(id, { id, x: cx + maxRadius * Math.cos(angle), y: cy + maxRadius * Math.sin(angle) });
30713
+ });
30714
+ return nodeIds.map((id) => positions2.get(id));
30715
+ }
30716
+ const { depth, visitOrder } = bfsDepthAndOrder(nodeIds, adjacency, roots);
30717
+ const maxDepth = Math.max(...Array.from(depth.values()));
30718
+ const ringGroups = groupByTier(nodeIds, depth, maxDepth);
30719
+ const positions = /* @__PURE__ */ new Map();
30720
+ for (let d = 0; d <= maxDepth; d++) {
30721
+ const ordered2 = [...ringGroups[d]].sort(
30722
+ (a, b) => (visitOrder.get(a) ?? 0) - (visitOrder.get(b) ?? 0)
30723
+ );
30724
+ const radius = d * maxRadius / Math.max(1, maxDepth);
30725
+ const k = ordered2.length;
30726
+ ordered2.forEach((id, i) => {
30727
+ const angle = i / k * 2 * Math.PI;
30728
+ positions.set(id, { id, x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) });
30729
+ });
30730
+ }
30731
+ return nodeIds.map((id) => positions.get(id));
30732
+ }
30733
+ function computeStaticLayout(mode, input) {
30734
+ const { nodeIds, edges, width, height } = input;
30735
+ const margin = input.margin ?? 40;
30736
+ if (nodeIds.length === 0) return [];
30737
+ if (nodeIds.length === 1) return [{ id: nodeIds[0], x: width / 2, y: height / 2 }];
30738
+ const adjacency = buildAdjacency(nodeIds, edges);
30739
+ const roots = findRoots(nodeIds, adjacency);
30740
+ if (mode === "flow") return layoutFlow(nodeIds, adjacency, roots, width, height, margin);
30741
+ if (mode === "tree") return layoutTree2(nodeIds, adjacency, roots, width, height, margin);
30742
+ return layoutRadial(nodeIds, adjacency, roots, width, height, margin);
30743
+ }
30744
+ var init_graphViewLayouts = __esm({
30745
+ "lib/graphViewLayouts.ts"() {
30746
+ }
30747
+ });
30454
30748
  function resolveNodeColor(node, groups) {
30455
30749
  if (node.color) return node.color;
30456
30750
  if (node.group) {
@@ -30465,6 +30759,7 @@ var init_GraphView = __esm({
30465
30759
  "use client";
30466
30760
  init_cn();
30467
30761
  init_atoms();
30762
+ init_graphViewLayouts();
30468
30763
  GROUP_COLORS = [
30469
30764
  "#3b82f6",
30470
30765
  // blue-500
@@ -30495,11 +30790,13 @@ var init_GraphView = __esm({
30495
30790
  height: propHeight,
30496
30791
  className,
30497
30792
  showLabels = true,
30498
- zoomToFit = true
30793
+ zoomToFit = true,
30794
+ layout = "force"
30499
30795
  }) => {
30500
30796
  const { t } = useTranslate();
30501
30797
  const containerRef = useRef(null);
30502
30798
  const animRef = useRef(0);
30799
+ const arrowMarkerId = useId();
30503
30800
  const [simNodes, setSimNodes] = useState([]);
30504
30801
  const [settled, setSettled] = useState(false);
30505
30802
  const [hoveredId, setHoveredId] = useState(null);
@@ -30556,6 +30853,22 @@ var init_GraphView = __esm({
30556
30853
  fy: 0
30557
30854
  };
30558
30855
  });
30856
+ if (layout !== "force") {
30857
+ const points = computeStaticLayout(layout, {
30858
+ nodeIds: nodes.map((n) => n.id),
30859
+ edges,
30860
+ width: w,
30861
+ height: h
30862
+ });
30863
+ const pointById = new Map(points.map((p) => [p.id, p]));
30864
+ const laidOut = initialNodes.map((node) => {
30865
+ const point = pointById.get(node.id);
30866
+ return point ? { ...node, x: point.x, y: point.y } : node;
30867
+ });
30868
+ setSimNodes(laidOut);
30869
+ setSettled(true);
30870
+ return;
30871
+ }
30559
30872
  let iterations = 0;
30560
30873
  const maxIterations = 120;
30561
30874
  let currentNodes = initialNodes;
@@ -30624,7 +30937,7 @@ var init_GraphView = __esm({
30624
30937
  return () => {
30625
30938
  cancelAnimationFrame(animRef.current);
30626
30939
  };
30627
- }, [nodes, edges, w, h, groups]);
30940
+ }, [nodes, edges, w, h, groups, layout]);
30628
30941
  const viewBox = useMemo(() => {
30629
30942
  if (!zoomToFit || !settled || simNodes.length === 0) {
30630
30943
  return `0 0 ${w} ${h}`;
@@ -30699,6 +31012,19 @@ var init_GraphView = __esm({
30699
31012
  viewBox,
30700
31013
  preserveAspectRatio: "xMidYMid meet",
30701
31014
  children: [
31015
+ layout !== "force" && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
31016
+ "marker",
31017
+ {
31018
+ id: arrowMarkerId,
31019
+ viewBox: "0 0 10 10",
31020
+ refX: "9",
31021
+ refY: "5",
31022
+ markerWidth: "6",
31023
+ markerHeight: "6",
31024
+ orient: "auto-start-reverse",
31025
+ children: /* @__PURE__ */ jsx("path", { d: "M0,0 L10,5 L0,10 z", fill: "currentColor" })
31026
+ }
31027
+ ) }),
30702
31028
  edges.map((edge, idx) => {
30703
31029
  const source = nodeMap.get(edge.source);
30704
31030
  const target = nodeMap.get(edge.target);
@@ -30713,8 +31039,10 @@ var init_GraphView = __esm({
30713
31039
  x2: target.x,
30714
31040
  y2: target.y,
30715
31041
  stroke: edge.color ?? DEFAULT_EDGE_COLOR,
31042
+ color: edge.color ?? DEFAULT_EDGE_COLOR,
30716
31043
  strokeWidth: 1.5,
30717
- opacity: isHighlighted ? 0.8 : 0.15
31044
+ opacity: isHighlighted ? 0.8 : 0.15,
31045
+ markerEnd: layout !== "force" ? `url(#${arrowMarkerId})` : void 0
30718
31046
  }
30719
31047
  ),
30720
31048
  showLabels && edge.label && /* @__PURE__ */ jsx(
@@ -1,4 +1,5 @@
1
1
  import { OrbitalSchema, SExpr, Effect, OrbitalVerificationAPI, TraitStateSnapshot, EventPayload, BusEvent, VerificationCheck, BridgeHealth, VerificationSnapshot, VerificationSummary, TransitionTrace, ServerResponseTrace, CheckStatus, AssetLoadStatus } from '@almadar/core';
2
+ import React__default from 'react';
2
3
  import { D as DrawableNode } from './paintDispatch-Cb_hQj4Y.cjs';
3
4
  import { ClassValue } from 'clsx';
4
5
 
@@ -34,7 +35,7 @@ type ContentSegment = {
34
35
  * Splits markdown into segments of plain markdown and fenced code blocks.
35
36
  * JSON/orb code blocks containing orbital schemas are tagged as 'orbital'.
36
37
  */
37
- declare function parseMarkdownWithCodeBlocks(content: string | undefined | null): ContentSegment[];
38
+ declare function parseMarkdownWithCodeBlocks$1(content: string | undefined | null): ContentSegment[];
38
39
  /**
39
40
  * Parse content to extract all segments including quiz tags and code blocks.
40
41
  *
@@ -45,6 +46,95 @@ declare function parseMarkdownWithCodeBlocks(content: string | undefined | null)
45
46
  */
46
47
  declare function parseContentSegments(content: string | undefined | null): ContentSegment[];
47
48
 
49
+ /**
50
+ * BloomQuizBlock Molecule Component
51
+ *
52
+ * Practice Q&A with Bloom's Taxonomy level badge. Emits `UI:ANSWER_BLOOM { index, level }`.
53
+ *
54
+ * Event Contract:
55
+ * - Emits: UI:ANSWER_BLOOM { index, level }
56
+ * - entityAware: false
57
+ */
58
+
59
+ type BloomLevel = 'remember' | 'understand' | 'apply' | 'analyze' | 'evaluate' | 'create';
60
+ interface BloomQuizBlockProps {
61
+ level: BloomLevel;
62
+ question: string;
63
+ answer: string;
64
+ /** Zero-based index (used in the emitted event payload) */
65
+ index?: number;
66
+ /** Whether the learner has already answered */
67
+ isAnswered?: boolean;
68
+ /** Event name emitted on first reveal (as `UI:<answerEvent>`). Defaults to 'ANSWER_BLOOM'. */
69
+ answerEvent?: string;
70
+ /** Additional CSS classes */
71
+ className?: string;
72
+ }
73
+ declare const BloomQuizBlock: React__default.FC<BloomQuizBlockProps>;
74
+
75
+ /**
76
+ * Utility for lesson-segment parsing: splits a markdown string into alternating
77
+ * markdown and fenced-code-block segments. Exported so SegmentRenderer and
78
+ * BloomQuizBlock can reuse without circular imports.
79
+ */
80
+ type MarkdownSegment = {
81
+ type: 'markdown';
82
+ content: string;
83
+ };
84
+ type CodeSegment = {
85
+ type: 'code';
86
+ language: string;
87
+ content: string;
88
+ runnable?: boolean;
89
+ };
90
+ type MixedSegment = MarkdownSegment | CodeSegment;
91
+ /** Splits markdown content into markdown and fenced-code-block segments. */
92
+ declare function parseMarkdownWithCodeBlocks(content: string): MixedSegment[];
93
+
94
+ /**
95
+ * parseLessonSegments — parses lesson markdown with embedded learning-science
96
+ * XML-style tags into a typed Segment array.
97
+ *
98
+ * Supported tags: <activate>, <connect>, <reflect>, <bloom level="...">,
99
+ * <question>/<answer>, <visualize type="..." description="..." />.
100
+ *
101
+ * Tags may be open (unclosed) — the parser falls back to consuming content
102
+ * until the next recognised tag, a section heading (`\n\n#`), or end-of-input.
103
+ */
104
+
105
+ type InteractiveOrbitalType = 'algorithms' | 'math' | 'physics' | 'biology' | 'chemistry' | 'probability';
106
+ type LessonSegment = MixedSegment | {
107
+ type: 'quiz';
108
+ question: string;
109
+ answer: string;
110
+ } | {
111
+ type: 'activate';
112
+ question: string;
113
+ } | {
114
+ type: 'connect';
115
+ content: string;
116
+ } | {
117
+ type: 'reflect';
118
+ prompt: string;
119
+ } | {
120
+ type: 'bloom';
121
+ level: BloomLevel;
122
+ question: string;
123
+ answer: string;
124
+ } | {
125
+ type: 'visualization';
126
+ visualizationType: InteractiveOrbitalType;
127
+ description: string;
128
+ };
129
+ /** User progress state passed into SegmentRenderer. */
130
+ interface LessonUserProgress {
131
+ activationResponse?: string;
132
+ reflectionNotes?: string[];
133
+ bloomAnswered?: Record<number, boolean>;
134
+ }
135
+ /** Parse a lesson string into typed segments. Returns `[]` when input is empty. */
136
+ declare function parseLessonSegments(lesson: string | undefined): LessonSegment[];
137
+
48
138
  /**
49
139
  * Orbital State Machine Visualizer
50
140
  *
@@ -292,4 +382,4 @@ declare function clearVerification(): void;
292
382
  */
293
383
  declare function cn(...inputs: ClassValue[]): string;
294
384
 
295
- export { parseMarkdownWithCodeBlocks as A, recordServerResponse as B, type ContentSegment as C, DEFAULT_CONFIG as D, type EntityDefinition as E, recordTransition as F, registerCheck as G, registerTraitSnapshot as H, renderStateMachineToDomData as I, renderStateMachineToSvg as J, subscribeToVerification as K, updateAssetStatus as L, updateBridgeHealth as M, updateCheck as N, waitForTransition as O, type RenderOptions as R, type StateDefinition as S, type TraitSnapshotGetter as T, type VisualizerConfig as V, type DomEntityBox as a, type DomLayoutData as b, type DomOutputsBox as c, type DomStateNode as d, type DomTransitionLabel as e, type DomTransitionPath as f, type StateMachineDefinition as g, type TransitionDefinition as h, bindCanvasCapture as i, bindEventBus as j, bindLastDrawables as k, bindTraitStateGetter as l, clearVerification as m, cn as n, extractOutputsFromTransitions as o, extractStateMachine as p, formatGuard as q, getAllChecks as r, getBridgeHealth as s, getEffectSummary as t, getSnapshot as u, getSummary as v, getTraitSnapshots as w, getTransitions as x, getTransitionsForTrait as y, parseContentSegments as z };
385
+ export { parseMarkdownWithCodeBlocks as $, parseLessonSegments as A, parseMarkdownWithCodeBlocks$1 as B, type ContentSegment as C, DEFAULT_CONFIG as D, type EntityDefinition as E, recordServerResponse as F, recordTransition as G, registerCheck as H, registerTraitSnapshot as I, renderStateMachineToDomData as J, renderStateMachineToSvg as K, type LessonSegment as L, subscribeToVerification as M, updateAssetStatus as N, updateBridgeHealth as O, updateCheck as P, waitForTransition as Q, type RenderOptions as R, type StateDefinition as S, type TraitSnapshotGetter as T, type LessonUserProgress as U, type VisualizerConfig as V, type InteractiveOrbitalType as W, type BloomLevel as X, BloomQuizBlock as Y, type BloomQuizBlockProps as Z, type MixedSegment as _, type DomEntityBox as a, type DomLayoutData as b, type DomOutputsBox as c, type DomStateNode as d, type DomTransitionLabel as e, type DomTransitionPath as f, type StateMachineDefinition as g, type TransitionDefinition as h, bindCanvasCapture as i, bindEventBus as j, bindLastDrawables as k, bindTraitStateGetter as l, clearVerification as m, cn as n, extractOutputsFromTransitions as o, extractStateMachine as p, formatGuard as q, getAllChecks as r, getBridgeHealth as s, getEffectSummary as t, getSnapshot as u, getSummary as v, getTraitSnapshots as w, getTransitions as x, getTransitionsForTrait as y, parseContentSegments as z };