@almadar/ui 2.16.0 → 2.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.
@@ -1055,6 +1055,16 @@ AvlBindingRef.displayName = "AvlBindingRef";
1055
1055
 
1056
1056
  // components/molecules/avl/avl-layout.ts
1057
1057
  function ringPositions(cx, cy, r, count, startAngle = -Math.PI / 2) {
1058
+ if (count === 0) return [];
1059
+ if (count === 1) {
1060
+ return [{ x: cx, y: cy, angle: 0 }];
1061
+ }
1062
+ if (count === 2) {
1063
+ return [
1064
+ { x: cx - r * 0.7, y: cy, angle: Math.PI },
1065
+ { x: cx + r * 0.7, y: cy, angle: 0 }
1066
+ ];
1067
+ }
1058
1068
  return Array.from({ length: count }, (_, i) => {
1059
1069
  const angle = startAngle + Math.PI * 2 * i / count;
1060
1070
  return {
@@ -1152,6 +1162,43 @@ var AvlStateMachine = ({
1152
1162
  ` }),
1153
1163
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx, cy, r: r + 30, fill: `url(#${ids.grad})` }),
1154
1164
  transitions.map((tr, i) => {
1165
+ if (tr.from !== tr.to) return null;
1166
+ const idx = stateIndex.get(tr.from);
1167
+ if (idx === void 0) return null;
1168
+ const pos = positions[idx];
1169
+ const loopR = 20;
1170
+ const loopY = pos.y - stateHeight / 2 - 4;
1171
+ const d = `M${pos.x - 14},${loopY} C${pos.x - 14},${loopY - loopR * 2} ${pos.x + 14},${loopY - loopR * 2} ${pos.x + 14},${loopY}`;
1172
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
1173
+ /* @__PURE__ */ jsxRuntime.jsx(
1174
+ "path",
1175
+ {
1176
+ d,
1177
+ fill: "none",
1178
+ stroke: color,
1179
+ strokeWidth: 1.5,
1180
+ opacity: 0.7,
1181
+ markerEnd: `url(#${ids.grad})`
1182
+ }
1183
+ ),
1184
+ tr.event && /* @__PURE__ */ jsxRuntime.jsx(
1185
+ "text",
1186
+ {
1187
+ x: pos.x,
1188
+ y: loopY - loopR * 2 + 4,
1189
+ textAnchor: "middle",
1190
+ fill: color,
1191
+ fontSize: 9,
1192
+ fontFamily: "inherit",
1193
+ fontWeight: "bold",
1194
+ opacity: 0.8,
1195
+ children: tr.event
1196
+ }
1197
+ )
1198
+ ] }, `self-${i}`);
1199
+ }),
1200
+ transitions.map((tr, i) => {
1201
+ if (tr.from === tr.to) return null;
1155
1202
  const fromIdx = stateIndex.get(tr.from);
1156
1203
  const toIdx = stateIndex.get(tr.to);
1157
1204
  if (fromIdx === void 0 || toIdx === void 0) return null;
@@ -1611,6 +1658,42 @@ var AvlEmitListen = ({
1611
1658
  ] });
1612
1659
  };
1613
1660
  AvlEmitListen.displayName = "AvlEmitListen";
1661
+ var SLOT_PRESETS = {
1662
+ header: { x: 10, y: 5, width: 340, height: 35 },
1663
+ main: { x: 120, y: 50, width: 230, height: 195 },
1664
+ sidebar: { x: 10, y: 50, width: 100, height: 195 },
1665
+ modal: { x: 80, y: 60, width: 200, height: 140 },
1666
+ drawer: { x: 220, y: 50, width: 130, height: 195 },
1667
+ toast: { x: 220, y: 210, width: 130, height: 35 },
1668
+ footer: { x: 10, y: 220, width: 340, height: 30 },
1669
+ center: { x: 60, y: 50, width: 240, height: 195 },
1670
+ "hud-top": { x: 10, y: 5, width: 340, height: 30 },
1671
+ "hud-bottom": { x: 10, y: 220, width: 340, height: 30 }
1672
+ };
1673
+ function resolveSlot(slot, fallbackIdx) {
1674
+ if (slot.x !== void 0 && slot.y !== void 0 && slot.width !== void 0 && slot.height !== void 0) {
1675
+ return slot;
1676
+ }
1677
+ const preset = SLOT_PRESETS[slot.name];
1678
+ if (preset) {
1679
+ return {
1680
+ name: slot.name,
1681
+ x: slot.x ?? preset.x,
1682
+ y: slot.y ?? preset.y,
1683
+ width: slot.width ?? preset.width,
1684
+ height: slot.height ?? preset.height
1685
+ };
1686
+ }
1687
+ const col = fallbackIdx % 2;
1688
+ const row = Math.floor(fallbackIdx / 2);
1689
+ return {
1690
+ name: slot.name,
1691
+ x: 10 + col * 175,
1692
+ y: 50 + row * 100,
1693
+ width: 165,
1694
+ height: 90
1695
+ };
1696
+ }
1614
1697
  var AvlSlotMap = ({
1615
1698
  slots,
1616
1699
  pageWidth = 360,
@@ -1621,6 +1704,13 @@ var AvlSlotMap = ({
1621
1704
  }) => {
1622
1705
  const ox = (600 - pageWidth) / 2;
1623
1706
  const oy = (400 - pageHeight) / 2;
1707
+ let unknownIdx = 0;
1708
+ const resolvedSlots = slots.map((slot) => {
1709
+ const isUnknown = !SLOT_PRESETS[slot.name] && (slot.x === void 0 || slot.y === void 0);
1710
+ const resolved = resolveSlot(slot, isUnknown ? unknownIdx : 0);
1711
+ if (isUnknown) unknownIdx++;
1712
+ return resolved;
1713
+ });
1624
1714
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 600 400", xmlns: "http://www.w3.org/2000/svg", className, children: [
1625
1715
  animated && /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1626
1716
  @keyframes avl-slot-pulse { 0%, 100% { opacity: 0.15; } 50% { opacity: 0.25; } }
@@ -1665,7 +1755,7 @@ var AvlSlotMap = ({
1665
1755
  children: "Page Layout"
1666
1756
  }
1667
1757
  ),
1668
- slots.map((slot) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
1758
+ resolvedSlots.map((slot) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
1669
1759
  /* @__PURE__ */ jsxRuntime.jsx(
1670
1760
  "rect",
1671
1761
  {
@@ -249,10 +249,11 @@ declare const AvlEmitListen: React.FC<AvlEmitListenProps>;
249
249
 
250
250
  interface AvlSlotMapSlot {
251
251
  name: string;
252
- x: number;
253
- y: number;
254
- width: number;
255
- height: number;
252
+ /** Manual position overrides. Omit for auto-layout. */
253
+ x?: number;
254
+ y?: number;
255
+ width?: number;
256
+ height?: number;
256
257
  }
257
258
  interface AvlSlotMapProps {
258
259
  slots: AvlSlotMapSlot[];
@@ -280,7 +281,8 @@ declare const AvlExprTree: React.FC<AvlExprTreeProps>;
280
281
  /**
281
282
  * AVL layout utilities for positioning atoms in composed diagrams.
282
283
  */
283
- /** Distribute N points evenly around a ring. */
284
+ /** Distribute N points evenly around a ring.
285
+ * Handles edge cases: 1 state (centered), 2 states (horizontal). */
284
286
  declare function ringPositions(cx: number, cy: number, r: number, count: number, startAngle?: number): Array<{
285
287
  x: number;
286
288
  y: number;
package/dist/avl/index.js CHANGED
@@ -1049,6 +1049,16 @@ AvlBindingRef.displayName = "AvlBindingRef";
1049
1049
 
1050
1050
  // components/molecules/avl/avl-layout.ts
1051
1051
  function ringPositions(cx, cy, r, count, startAngle = -Math.PI / 2) {
1052
+ if (count === 0) return [];
1053
+ if (count === 1) {
1054
+ return [{ x: cx, y: cy, angle: 0 }];
1055
+ }
1056
+ if (count === 2) {
1057
+ return [
1058
+ { x: cx - r * 0.7, y: cy, angle: Math.PI },
1059
+ { x: cx + r * 0.7, y: cy, angle: 0 }
1060
+ ];
1061
+ }
1052
1062
  return Array.from({ length: count }, (_, i) => {
1053
1063
  const angle = startAngle + Math.PI * 2 * i / count;
1054
1064
  return {
@@ -1146,6 +1156,43 @@ var AvlStateMachine = ({
1146
1156
  ` }),
1147
1157
  /* @__PURE__ */ jsx("circle", { cx, cy, r: r + 30, fill: `url(#${ids.grad})` }),
1148
1158
  transitions.map((tr, i) => {
1159
+ if (tr.from !== tr.to) return null;
1160
+ const idx = stateIndex.get(tr.from);
1161
+ if (idx === void 0) return null;
1162
+ const pos = positions[idx];
1163
+ const loopR = 20;
1164
+ const loopY = pos.y - stateHeight / 2 - 4;
1165
+ const d = `M${pos.x - 14},${loopY} C${pos.x - 14},${loopY - loopR * 2} ${pos.x + 14},${loopY - loopR * 2} ${pos.x + 14},${loopY}`;
1166
+ return /* @__PURE__ */ jsxs("g", { children: [
1167
+ /* @__PURE__ */ jsx(
1168
+ "path",
1169
+ {
1170
+ d,
1171
+ fill: "none",
1172
+ stroke: color,
1173
+ strokeWidth: 1.5,
1174
+ opacity: 0.7,
1175
+ markerEnd: `url(#${ids.grad})`
1176
+ }
1177
+ ),
1178
+ tr.event && /* @__PURE__ */ jsx(
1179
+ "text",
1180
+ {
1181
+ x: pos.x,
1182
+ y: loopY - loopR * 2 + 4,
1183
+ textAnchor: "middle",
1184
+ fill: color,
1185
+ fontSize: 9,
1186
+ fontFamily: "inherit",
1187
+ fontWeight: "bold",
1188
+ opacity: 0.8,
1189
+ children: tr.event
1190
+ }
1191
+ )
1192
+ ] }, `self-${i}`);
1193
+ }),
1194
+ transitions.map((tr, i) => {
1195
+ if (tr.from === tr.to) return null;
1149
1196
  const fromIdx = stateIndex.get(tr.from);
1150
1197
  const toIdx = stateIndex.get(tr.to);
1151
1198
  if (fromIdx === void 0 || toIdx === void 0) return null;
@@ -1605,6 +1652,42 @@ var AvlEmitListen = ({
1605
1652
  ] });
1606
1653
  };
1607
1654
  AvlEmitListen.displayName = "AvlEmitListen";
1655
+ var SLOT_PRESETS = {
1656
+ header: { x: 10, y: 5, width: 340, height: 35 },
1657
+ main: { x: 120, y: 50, width: 230, height: 195 },
1658
+ sidebar: { x: 10, y: 50, width: 100, height: 195 },
1659
+ modal: { x: 80, y: 60, width: 200, height: 140 },
1660
+ drawer: { x: 220, y: 50, width: 130, height: 195 },
1661
+ toast: { x: 220, y: 210, width: 130, height: 35 },
1662
+ footer: { x: 10, y: 220, width: 340, height: 30 },
1663
+ center: { x: 60, y: 50, width: 240, height: 195 },
1664
+ "hud-top": { x: 10, y: 5, width: 340, height: 30 },
1665
+ "hud-bottom": { x: 10, y: 220, width: 340, height: 30 }
1666
+ };
1667
+ function resolveSlot(slot, fallbackIdx) {
1668
+ if (slot.x !== void 0 && slot.y !== void 0 && slot.width !== void 0 && slot.height !== void 0) {
1669
+ return slot;
1670
+ }
1671
+ const preset = SLOT_PRESETS[slot.name];
1672
+ if (preset) {
1673
+ return {
1674
+ name: slot.name,
1675
+ x: slot.x ?? preset.x,
1676
+ y: slot.y ?? preset.y,
1677
+ width: slot.width ?? preset.width,
1678
+ height: slot.height ?? preset.height
1679
+ };
1680
+ }
1681
+ const col = fallbackIdx % 2;
1682
+ const row = Math.floor(fallbackIdx / 2);
1683
+ return {
1684
+ name: slot.name,
1685
+ x: 10 + col * 175,
1686
+ y: 50 + row * 100,
1687
+ width: 165,
1688
+ height: 90
1689
+ };
1690
+ }
1608
1691
  var AvlSlotMap = ({
1609
1692
  slots,
1610
1693
  pageWidth = 360,
@@ -1615,6 +1698,13 @@ var AvlSlotMap = ({
1615
1698
  }) => {
1616
1699
  const ox = (600 - pageWidth) / 2;
1617
1700
  const oy = (400 - pageHeight) / 2;
1701
+ let unknownIdx = 0;
1702
+ const resolvedSlots = slots.map((slot) => {
1703
+ const isUnknown = !SLOT_PRESETS[slot.name] && (slot.x === void 0 || slot.y === void 0);
1704
+ const resolved = resolveSlot(slot, isUnknown ? unknownIdx : 0);
1705
+ if (isUnknown) unknownIdx++;
1706
+ return resolved;
1707
+ });
1618
1708
  return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 600 400", xmlns: "http://www.w3.org/2000/svg", className, children: [
1619
1709
  animated && /* @__PURE__ */ jsx("style", { children: `
1620
1710
  @keyframes avl-slot-pulse { 0%, 100% { opacity: 0.15; } 50% { opacity: 0.25; } }
@@ -1659,7 +1749,7 @@ var AvlSlotMap = ({
1659
1749
  children: "Page Layout"
1660
1750
  }
1661
1751
  ),
1662
- slots.map((slot) => /* @__PURE__ */ jsxs("g", { children: [
1752
+ resolvedSlots.map((slot) => /* @__PURE__ */ jsxs("g", { children: [
1663
1753
  /* @__PURE__ */ jsx(
1664
1754
  "rect",
1665
1755
  {