@almadar/ui 5.76.6 → 5.77.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.
@@ -5276,7 +5276,11 @@ var init_Switch = __esm({
5276
5276
  disabled,
5277
5277
  onClick: handleClick,
5278
5278
  className: cn(
5279
- "relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
5279
+ // Fixed rem sizes instead of spacing tokens: themes like atelier
5280
+ // redefine --space-11 to 68px, which makes w-11 enormous and leaves
5281
+ // the thumb stuck near the left edge. The switch geometry must stay
5282
+ // proportional regardless of a theme's density scale.
5283
+ "relative inline-flex h-[1.5rem] w-[2.75rem] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
5280
5284
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
5281
5285
  isChecked ? "bg-primary" : "bg-muted",
5282
5286
  disabled && "cursor-not-allowed opacity-50"
@@ -5285,8 +5289,8 @@ var init_Switch = __esm({
5285
5289
  "span",
5286
5290
  {
5287
5291
  className: cn(
5288
- "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform",
5289
- isChecked ? "translate-x-5" : "translate-x-0"
5292
+ "pointer-events-none block h-[1.25rem] w-[1.25rem] rounded-full bg-background shadow-lg ring-0 transition-transform",
5293
+ isChecked ? "translate-x-[1.25rem]" : "translate-x-0"
5290
5294
  )
5291
5295
  }
5292
5296
  )
@@ -12496,318 +12500,14 @@ var init_useCanvasEffects = __esm({
12496
12500
  "use client";
12497
12501
  }
12498
12502
  });
12499
- function pickPath(entry) {
12500
- if (Array.isArray(entry.path)) {
12501
- return entry.path[Math.floor(Math.random() * entry.path.length)];
12502
- }
12503
- return entry.path;
12504
- }
12505
- function useGameAudio({
12506
- manifest,
12507
- baseUrl = "",
12508
- initialMuted = false,
12509
- initialVolume = 1
12510
- }) {
12511
- const [muted, setMutedState] = React105.useState(initialMuted);
12512
- const [masterVolume, setMasterVolumeState] = React105.useState(initialVolume);
12513
- const mutedRef = React105.useRef(muted);
12514
- const volumeRef = React105.useRef(masterVolume);
12515
- const manifestRef = React105.useRef(manifest);
12516
- mutedRef.current = muted;
12517
- volumeRef.current = masterVolume;
12518
- manifestRef.current = manifest;
12519
- const poolsRef = React105.useRef(/* @__PURE__ */ new Map());
12520
- const getOrCreateElement = React105.useCallback((key) => {
12521
- const entry = manifestRef.current[key];
12522
- if (!entry) return null;
12523
- let pool = poolsRef.current.get(key);
12524
- if (!pool) {
12525
- pool = [];
12526
- poolsRef.current.set(key, pool);
12527
- }
12528
- const maxSize = entry.poolSize ?? 1;
12529
- for (const audio of pool) {
12530
- if (audio.paused && (audio.ended || audio.currentTime === 0)) {
12531
- return audio;
12532
- }
12533
- }
12534
- if (pool.length < maxSize) {
12535
- const src = baseUrl + pickPath(entry);
12536
- const audio = new Audio(src);
12537
- audio.loop = entry.loop ?? false;
12538
- pool.push(audio);
12539
- return audio;
12540
- }
12541
- if (!entry.loop) {
12542
- let oldest = pool[0];
12543
- for (const audio of pool) {
12544
- if (audio.currentTime > oldest.currentTime) {
12545
- oldest = audio;
12546
- }
12547
- }
12548
- oldest.pause();
12549
- oldest.currentTime = 0;
12550
- return oldest;
12551
- }
12552
- return null;
12553
- }, [baseUrl]);
12554
- const play = React105.useCallback((key) => {
12555
- if (mutedRef.current) return;
12556
- const entry = manifestRef.current[key];
12557
- if (!entry) return;
12558
- const audio = getOrCreateElement(key);
12559
- if (!audio) return;
12560
- audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
12561
- if (!entry.loop) {
12562
- audio.currentTime = 0;
12563
- }
12564
- const promise = audio.play();
12565
- if (promise) {
12566
- promise.catch(() => {
12567
- });
12568
- }
12569
- }, [getOrCreateElement]);
12570
- const stop = React105.useCallback((key) => {
12571
- const pool = poolsRef.current.get(key);
12572
- if (!pool) return;
12573
- for (const audio of pool) {
12574
- audio.pause();
12575
- audio.currentTime = 0;
12576
- }
12577
- }, []);
12578
- const currentMusicKeyRef = React105.useRef(null);
12579
- const currentMusicElRef = React105.useRef(null);
12580
- const musicFadeRef = React105.useRef(null);
12581
- const pendingMusicKeyRef = React105.useRef(null);
12582
- const clearMusicFade = React105.useCallback(() => {
12583
- if (musicFadeRef.current) {
12584
- clearInterval(musicFadeRef.current);
12585
- musicFadeRef.current = null;
12586
- }
12587
- }, []);
12588
- const playMusic = React105.useCallback((key) => {
12589
- if (key === currentMusicKeyRef.current) return;
12590
- pendingMusicKeyRef.current = key;
12591
- const entry = manifestRef.current[key];
12592
- if (!entry) return;
12593
- const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
12594
- const stepMs = 50;
12595
- const totalSteps = Math.max(1, fadeDurationMs / stepMs);
12596
- const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
12597
- clearMusicFade();
12598
- const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
12599
- const incoming = new Audio(src);
12600
- incoming.loop = true;
12601
- incoming.volume = 0;
12602
- const outgoing = currentMusicElRef.current;
12603
- const outgoingStartVol = outgoing?.volume ?? 0;
12604
- currentMusicKeyRef.current = key;
12605
- currentMusicElRef.current = incoming;
12606
- if (!mutedRef.current) {
12607
- incoming.play().catch(() => {
12608
- currentMusicKeyRef.current = null;
12609
- currentMusicElRef.current = outgoing;
12610
- });
12611
- }
12612
- let step = 0;
12613
- musicFadeRef.current = setInterval(() => {
12614
- step++;
12615
- const progress = Math.min(step / totalSteps, 1);
12616
- incoming.volume = Math.min(1, targetVolume * progress);
12617
- if (outgoing) {
12618
- outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
12619
- }
12620
- if (progress >= 1) {
12621
- clearMusicFade();
12622
- if (outgoing) {
12623
- outgoing.pause();
12624
- outgoing.src = "";
12625
- }
12626
- }
12627
- }, stepMs);
12628
- }, [baseUrl, clearMusicFade]);
12629
- const stopMusic = React105.useCallback((fadeDurationMs = 1e3) => {
12630
- const outgoing = currentMusicElRef.current;
12631
- if (!outgoing) return;
12632
- currentMusicKeyRef.current = null;
12633
- currentMusicElRef.current = null;
12634
- pendingMusicKeyRef.current = null;
12635
- clearMusicFade();
12636
- const startVolume = outgoing.volume;
12637
- const stepMs = 50;
12638
- const totalSteps = Math.max(1, fadeDurationMs / stepMs);
12639
- let step = 0;
12640
- musicFadeRef.current = setInterval(() => {
12641
- step++;
12642
- const progress = step / totalSteps;
12643
- outgoing.volume = Math.max(0, startVolume * (1 - progress));
12644
- if (progress >= 1) {
12645
- clearMusicFade();
12646
- outgoing.pause();
12647
- outgoing.src = "";
12648
- }
12649
- }, stepMs);
12650
- }, [clearMusicFade]);
12651
- const stopAll = React105.useCallback(() => {
12652
- for (const pool of poolsRef.current.values()) {
12653
- for (const audio of pool) {
12654
- audio.pause();
12655
- audio.currentTime = 0;
12656
- }
12657
- }
12658
- stopMusic(0);
12659
- }, [stopMusic]);
12660
- const setMuted = React105.useCallback((value) => {
12661
- setMutedState(value);
12662
- if (value) {
12663
- for (const [key, pool] of poolsRef.current.entries()) {
12664
- if (manifestRef.current[key]?.loop) {
12665
- for (const audio of pool) {
12666
- if (!audio.paused) audio.pause();
12667
- }
12668
- }
12669
- }
12670
- currentMusicElRef.current?.pause();
12671
- } else {
12672
- for (const [key, pool] of poolsRef.current.entries()) {
12673
- const entry = manifestRef.current[key];
12674
- if (entry?.loop && entry?.autostart) {
12675
- for (const audio of pool) {
12676
- if (audio.paused) audio.play().catch(() => {
12677
- });
12678
- }
12679
- }
12680
- }
12681
- const musicEl = currentMusicElRef.current;
12682
- if (musicEl) {
12683
- musicEl.play().catch(() => {
12684
- });
12685
- }
12686
- }
12687
- }, []);
12688
- const setMasterVolume = React105.useCallback((volume) => {
12689
- const clamped = Math.max(0, Math.min(1, volume));
12690
- setMasterVolumeState(clamped);
12691
- for (const [key, pool] of poolsRef.current.entries()) {
12692
- const entryVol = manifestRef.current[key]?.volume ?? 1;
12693
- for (const audio of pool) {
12694
- audio.volume = Math.min(1, entryVol * clamped);
12695
- }
12696
- }
12697
- if (!musicFadeRef.current && currentMusicElRef.current) {
12698
- const key = currentMusicKeyRef.current;
12699
- const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
12700
- currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
12701
- }
12702
- }, []);
12703
- const unlockedRef = React105.useRef(false);
12704
- React105.useEffect(() => {
12705
- const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
12706
- const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
12707
- const hasAutoStart = autoKeys.length > 0;
12708
- if (!hasAutoStart && !hasPendingMusic()) return;
12709
- const unlock = () => {
12710
- if (unlockedRef.current) return;
12711
- unlockedRef.current = true;
12712
- if (!mutedRef.current) {
12713
- for (const key of autoKeys) {
12714
- play(key);
12715
- }
12716
- const pending = pendingMusicKeyRef.current;
12717
- if (pending && pending !== currentMusicKeyRef.current) {
12718
- playMusic(pending);
12719
- }
12720
- }
12721
- };
12722
- document.addEventListener("click", unlock, { once: true });
12723
- document.addEventListener("keydown", unlock, { once: true });
12724
- document.addEventListener("touchstart", unlock, { once: true });
12725
- return () => {
12726
- document.removeEventListener("click", unlock);
12727
- document.removeEventListener("keydown", unlock);
12728
- document.removeEventListener("touchstart", unlock);
12729
- };
12730
- }, [manifest, play, playMusic]);
12731
- React105.useEffect(() => {
12732
- return () => {
12733
- clearMusicFade();
12734
- for (const pool of poolsRef.current.values()) {
12735
- for (const audio of pool) {
12736
- audio.pause();
12737
- audio.src = "";
12738
- }
12739
- }
12740
- poolsRef.current.clear();
12741
- if (currentMusicElRef.current) {
12742
- currentMusicElRef.current.pause();
12743
- currentMusicElRef.current.src = "";
12744
- currentMusicElRef.current = null;
12745
- }
12746
- };
12747
- }, [clearMusicFade]);
12748
- return {
12749
- play,
12750
- stop,
12751
- stopAll,
12752
- playMusic,
12753
- stopMusic,
12754
- muted,
12755
- setMuted,
12756
- masterVolume,
12757
- setMasterVolume
12758
- };
12759
- }
12760
12503
  var init_useGameAudio = __esm({
12761
12504
  "components/game/shared/hooks/useGameAudio.ts"() {
12762
12505
  "use client";
12763
- useGameAudio.displayName = "useGameAudio";
12764
12506
  }
12765
12507
  });
12766
12508
  function useGameAudioContextOptional() {
12767
12509
  return React105.useContext(GameAudioContext);
12768
12510
  }
12769
- function GameAudioProvider({
12770
- manifest,
12771
- baseUrl = "",
12772
- children,
12773
- initialMuted = false
12774
- }) {
12775
- const eventBus = useEventBus();
12776
- const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
12777
- React105.useEffect(() => {
12778
- const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
12779
- const key = event.payload?.key;
12780
- if (key) play(key);
12781
- });
12782
- const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
12783
- const key = event.payload?.key;
12784
- if (key) {
12785
- stop(key);
12786
- } else {
12787
- stopAll();
12788
- }
12789
- });
12790
- const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
12791
- const key = event.payload?.key;
12792
- if (key) {
12793
- playMusic(key);
12794
- } else {
12795
- stopMusic();
12796
- }
12797
- });
12798
- const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
12799
- stopMusic();
12800
- });
12801
- return () => {
12802
- unsubPlay();
12803
- unsubStop();
12804
- unsubChangeMusic();
12805
- unsubStopMusic();
12806
- };
12807
- }, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
12808
- const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
12809
- return /* @__PURE__ */ jsxRuntime.jsx(GameAudioContext.Provider, { value, children });
12810
- }
12811
12511
  var GameAudioContext;
12812
12512
  var init_GameAudioProvider = __esm({
12813
12513
  "components/game/shared/providers/GameAudioProvider.tsx"() {
@@ -12816,7 +12516,6 @@ var init_GameAudioProvider = __esm({
12816
12516
  init_useGameAudio();
12817
12517
  GameAudioContext = React105.createContext(null);
12818
12518
  GameAudioContext.displayName = "GameAudioContext";
12819
- GameAudioProvider.displayName = "GameAudioProvider";
12820
12519
  }
12821
12520
  });
12822
12521
  function GameAudioToggle({
@@ -27336,129 +27035,6 @@ var init_Breadcrumb = __esm({
27336
27035
  Breadcrumb.displayName = "Breadcrumb";
27337
27036
  }
27338
27037
  });
27339
- function useSafeEventBus2() {
27340
- try {
27341
- return useEventBus();
27342
- } catch {
27343
- return { emit: () => {
27344
- }, on: () => () => {
27345
- }, once: () => {
27346
- } };
27347
- }
27348
- }
27349
- var log6, lookStyles4, ButtonGroup;
27350
- var init_ButtonGroup = __esm({
27351
- "components/core/molecules/ButtonGroup.tsx"() {
27352
- "use client";
27353
- init_cn();
27354
- init_atoms();
27355
- init_useEventBus();
27356
- log6 = logger.createLogger("almadar:ui:button-group");
27357
- lookStyles4 = {
27358
- "right-aligned-buttons": "",
27359
- "floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
27360
- "inline-row": "gap-2 inline-flex",
27361
- "dropdown-menu": "[&>button:not(:first-child)]:hidden",
27362
- "command-palette-trigger": "[&>button:not(:first-child)]:hidden"
27363
- };
27364
- ButtonGroup = ({
27365
- children,
27366
- primary,
27367
- secondary,
27368
- variant = "default",
27369
- orientation = "horizontal",
27370
- className,
27371
- // Filter-group pattern props (entity and filters are used for schema-driven filtering)
27372
- entity: _entity,
27373
- filters,
27374
- look = "right-aligned-buttons"
27375
- }) => {
27376
- const eventBus = useSafeEventBus2();
27377
- const variantClasses2 = {
27378
- default: "gap-0",
27379
- segmented: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0",
27380
- toggle: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0"
27381
- };
27382
- const orientationClasses = {
27383
- horizontal: "flex-row",
27384
- vertical: "flex-col [&>button:first-child]:rounded-t-lg [&>button:last-child]:rounded-b-lg [&>button:not(:first-child)]:border-t-0 [&>button:not(:first-child)]:border-l"
27385
- };
27386
- const handleActionClick = (action) => {
27387
- if (action.event) {
27388
- eventBus.emit("UI:DISPATCH", { event: action.event });
27389
- }
27390
- if (action.navigatesTo) {
27391
- eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
27392
- }
27393
- };
27394
- const renderFormActions = () => {
27395
- const buttons = [];
27396
- if (secondary) {
27397
- secondary.forEach((action, index) => {
27398
- buttons.push(
27399
- /* @__PURE__ */ jsxRuntime.jsx(
27400
- Button,
27401
- {
27402
- type: action.actionType === "submit" ? "submit" : "button",
27403
- variant: action.variant || "ghost",
27404
- onClick: () => handleActionClick(action),
27405
- children: action.label
27406
- },
27407
- `secondary-${index}`
27408
- )
27409
- );
27410
- });
27411
- }
27412
- if (primary) {
27413
- const isSubmit = primary.actionType === "submit";
27414
- buttons.push(
27415
- /* @__PURE__ */ jsxRuntime.jsx(
27416
- Button,
27417
- {
27418
- type: isSubmit ? "submit" : "button",
27419
- variant: primary.variant || "primary",
27420
- onClick: () => handleActionClick(primary),
27421
- "data-testid": isSubmit ? "form-submit" : void 0,
27422
- children: primary.label
27423
- },
27424
- "primary"
27425
- )
27426
- );
27427
- }
27428
- return buttons;
27429
- };
27430
- const renderFilters = () => {
27431
- if (!filters || filters.length === 0) return null;
27432
- return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
27433
- Button,
27434
- {
27435
- variant: "ghost",
27436
- onClick: () => {
27437
- log6.debug("Filter clicked", { field: filter.field });
27438
- },
27439
- children: filter.label
27440
- },
27441
- `filter-${filter.field}-${index}`
27442
- ));
27443
- };
27444
- return /* @__PURE__ */ jsxRuntime.jsx(
27445
- "div",
27446
- {
27447
- className: cn(
27448
- "inline-flex gap-2",
27449
- variantClasses2[variant],
27450
- orientationClasses[orientation],
27451
- lookStyles4[look],
27452
- className
27453
- ),
27454
- role: "group",
27455
- children: children || renderFilters() || renderFormActions()
27456
- }
27457
- );
27458
- };
27459
- ButtonGroup.displayName = "ButtonGroup";
27460
- }
27461
- });
27462
27038
  function dayWindowForViewport(width) {
27463
27039
  if (width <= 640) return 1;
27464
27040
  if (width <= 1024) return 3;
@@ -28201,7 +27777,7 @@ var init_CardGrid = __esm({
28201
27777
  CardGrid.displayName = "CardGrid";
28202
27778
  }
28203
27779
  });
28204
- function useSafeEventBus3() {
27780
+ function useSafeEventBus2() {
28205
27781
  try {
28206
27782
  return useEventBus();
28207
27783
  } catch {
@@ -28237,7 +27813,7 @@ var init_Carousel = __esm({
28237
27813
  const [activeIndex, setActiveIndex] = React105.useState(0);
28238
27814
  const scrollRef = React105.useRef(null);
28239
27815
  const autoPlayRef = React105.useRef(null);
28240
- const eventBus = useSafeEventBus3();
27816
+ const eventBus = useSafeEventBus2();
28241
27817
  const { t } = hooks.useTranslate();
28242
27818
  const safeItems = items ?? [];
28243
27819
  const totalSlides = safeItems.length;
@@ -31559,7 +31135,7 @@ function DataGrid({
31559
31135
  /* @__PURE__ */ jsxRuntime.jsx(
31560
31136
  Box,
31561
31137
  {
31562
- className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles5[look], className),
31138
+ className: cn("grid", gapStyles5[gap], scrollX ? "grid-flow-col overflow-x-auto" : colsClass, lookStyles4[look], className),
31563
31139
  style: scrollX ? { gridAutoFlow: "column", gridAutoColumns: `minmax(${minCardWidth}px, 1fr)` } : gridTemplateColumns ? { gridTemplateColumns } : void 0,
31564
31140
  children: data.map((item, index) => {
31565
31141
  const itemData = item;
@@ -31754,7 +31330,7 @@ function DataGrid({
31754
31330
  ] })
31755
31331
  );
31756
31332
  }
31757
- var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles5;
31333
+ var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles4;
31758
31334
  var init_DataGrid = __esm({
31759
31335
  "components/core/molecules/DataGrid.tsx"() {
31760
31336
  "use client";
@@ -31789,7 +31365,7 @@ var init_DataGrid = __esm({
31789
31365
  lg: "gap-6",
31790
31366
  xl: "gap-8"
31791
31367
  };
31792
- lookStyles5 = {
31368
+ lookStyles4 = {
31793
31369
  dense: "gap-2 [&>*]:p-card-sm",
31794
31370
  spacious: "gap-8 [&>*]:p-card-lg",
31795
31371
  striped: "[&>*:nth-child(even)]:bg-muted/30",
@@ -32216,6 +31792,133 @@ var init_DataList = __esm({
32216
31792
  DataList.displayName = "DataList";
32217
31793
  }
32218
31794
  });
31795
+ var FormSection, FormLayout, FormActions;
31796
+ var init_FormSection = __esm({
31797
+ "components/core/molecules/FormSection.tsx"() {
31798
+ "use client";
31799
+ init_cn();
31800
+ init_atoms();
31801
+ init_Box();
31802
+ init_Typography();
31803
+ init_Button();
31804
+ init_Stack();
31805
+ init_Icon();
31806
+ init_useEventBus();
31807
+ FormSection = ({
31808
+ title,
31809
+ description,
31810
+ children,
31811
+ collapsible = false,
31812
+ defaultCollapsed = false,
31813
+ card = false,
31814
+ columns = 1,
31815
+ className
31816
+ }) => {
31817
+ const [collapsed, setCollapsed] = React105__namespace.default.useState(defaultCollapsed);
31818
+ const { t } = hooks.useTranslate();
31819
+ const eventBus = useEventBus();
31820
+ const gridClass = {
31821
+ 1: "grid-cols-1",
31822
+ 2: "grid-cols-1 md:grid-cols-2",
31823
+ 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
31824
+ }[columns];
31825
+ React105__namespace.default.useCallback(() => {
31826
+ if (collapsible) {
31827
+ setCollapsed((prev) => !prev);
31828
+ eventBus.emit("UI:TOGGLE_COLLAPSE", { collapsed: !collapsed });
31829
+ }
31830
+ }, [collapsible, collapsed, eventBus]);
31831
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31832
+ (title || description) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "mb-4", children: [
31833
+ title && /* @__PURE__ */ jsxRuntime.jsxs(
31834
+ HStack,
31835
+ {
31836
+ justify: "between",
31837
+ align: "center",
31838
+ className: cn(collapsible && "cursor-pointer"),
31839
+ action: collapsible ? "TOGGLE_COLLAPSE" : void 0,
31840
+ children: [
31841
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h3", weight: "semibold", children: title }),
31842
+ collapsible && /* @__PURE__ */ jsxRuntime.jsx(
31843
+ Button,
31844
+ {
31845
+ variant: "ghost",
31846
+ size: "sm",
31847
+ action: "TOGGLE_COLLAPSE",
31848
+ children: /* @__PURE__ */ jsxRuntime.jsx(
31849
+ Icon,
31850
+ {
31851
+ icon: LucideIcons2.ChevronDown,
31852
+ size: "sm",
31853
+ className: cn(
31854
+ "text-muted-foreground transition-transform",
31855
+ collapsed && "rotate-180"
31856
+ )
31857
+ }
31858
+ )
31859
+ }
31860
+ )
31861
+ ]
31862
+ }
31863
+ ),
31864
+ description && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: description })
31865
+ ] }),
31866
+ (!collapsible || !collapsed) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("grid gap-4", gridClass), children })
31867
+ ] });
31868
+ if (card) {
31869
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("p-6", className), children: content });
31870
+ }
31871
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
31872
+ };
31873
+ FormSection.displayName = "FormSection";
31874
+ FormLayout = ({
31875
+ children,
31876
+ dividers = true,
31877
+ className
31878
+ }) => {
31879
+ return /* @__PURE__ */ jsxRuntime.jsx(
31880
+ VStack,
31881
+ {
31882
+ gap: "lg",
31883
+ className: cn(
31884
+ dividers && "[&>*+*]:pt-8 [&>*+*]:border-t [&>*+*]:border-border",
31885
+ className
31886
+ ),
31887
+ children
31888
+ }
31889
+ );
31890
+ };
31891
+ FormLayout.displayName = "FormLayout";
31892
+ FormActions = ({
31893
+ children,
31894
+ sticky = false,
31895
+ align = "right",
31896
+ className
31897
+ }) => {
31898
+ const alignClass2 = {
31899
+ left: "justify-start",
31900
+ right: "justify-end",
31901
+ between: "justify-between",
31902
+ center: "justify-center"
31903
+ }[align];
31904
+ return /* @__PURE__ */ jsxRuntime.jsx(
31905
+ HStack,
31906
+ {
31907
+ gap: "sm",
31908
+ align: "center",
31909
+ className: cn(
31910
+ "pt-6 border-t border-border",
31911
+ alignClass2,
31912
+ sticky && "sticky bottom-0 bg-card py-4 -mx-6 px-6 shadow-[0_-4px_6px_-1px_rgb(0,0,0,0.05)]",
31913
+ className
31914
+ ),
31915
+ children
31916
+ }
31917
+ );
31918
+ };
31919
+ FormActions.displayName = "FormActions";
31920
+ }
31921
+ });
32219
31922
  function fileIcon(name) {
32220
31923
  const ext = name.split(".").pop()?.toLowerCase() ?? "";
32221
31924
  switch (ext) {
@@ -32377,6 +32080,129 @@ var init_FormField = __esm({
32377
32080
  FormField.displayName = "FormField";
32378
32081
  }
32379
32082
  });
32083
+ function useSafeEventBus3() {
32084
+ try {
32085
+ return useEventBus();
32086
+ } catch {
32087
+ return { emit: () => {
32088
+ }, on: () => () => {
32089
+ }, once: () => {
32090
+ } };
32091
+ }
32092
+ }
32093
+ var log6, lookStyles5, ButtonGroup;
32094
+ var init_ButtonGroup = __esm({
32095
+ "components/core/molecules/ButtonGroup.tsx"() {
32096
+ "use client";
32097
+ init_cn();
32098
+ init_atoms();
32099
+ init_useEventBus();
32100
+ log6 = logger.createLogger("almadar:ui:button-group");
32101
+ lookStyles5 = {
32102
+ "right-aligned-buttons": "",
32103
+ "floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
32104
+ "inline-row": "gap-2 inline-flex",
32105
+ "dropdown-menu": "[&>button:not(:first-child)]:hidden",
32106
+ "command-palette-trigger": "[&>button:not(:first-child)]:hidden"
32107
+ };
32108
+ ButtonGroup = ({
32109
+ children,
32110
+ primary,
32111
+ secondary,
32112
+ variant = "default",
32113
+ orientation = "horizontal",
32114
+ className,
32115
+ // Filter-group pattern props (entity and filters are used for schema-driven filtering)
32116
+ entity: _entity,
32117
+ filters,
32118
+ look = "right-aligned-buttons"
32119
+ }) => {
32120
+ const eventBus = useSafeEventBus3();
32121
+ const variantClasses2 = {
32122
+ default: "gap-0",
32123
+ segmented: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0",
32124
+ toggle: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0"
32125
+ };
32126
+ const orientationClasses = {
32127
+ horizontal: "flex-row",
32128
+ vertical: "flex-col [&>button:first-child]:rounded-t-lg [&>button:last-child]:rounded-b-lg [&>button:not(:first-child)]:border-t-0 [&>button:not(:first-child)]:border-l"
32129
+ };
32130
+ const handleActionClick = (action) => {
32131
+ if (action.event) {
32132
+ eventBus.emit("UI:DISPATCH", { event: action.event });
32133
+ }
32134
+ if (action.navigatesTo) {
32135
+ eventBus.emit("UI:NAVIGATE", { url: action.navigatesTo });
32136
+ }
32137
+ };
32138
+ const renderFormActions = () => {
32139
+ const buttons = [];
32140
+ if (secondary) {
32141
+ secondary.forEach((action, index) => {
32142
+ buttons.push(
32143
+ /* @__PURE__ */ jsxRuntime.jsx(
32144
+ Button,
32145
+ {
32146
+ type: action.actionType === "submit" ? "submit" : "button",
32147
+ variant: action.variant || "ghost",
32148
+ onClick: () => handleActionClick(action),
32149
+ children: action.label
32150
+ },
32151
+ `secondary-${index}`
32152
+ )
32153
+ );
32154
+ });
32155
+ }
32156
+ if (primary) {
32157
+ const isSubmit = primary.actionType === "submit";
32158
+ buttons.push(
32159
+ /* @__PURE__ */ jsxRuntime.jsx(
32160
+ Button,
32161
+ {
32162
+ type: isSubmit ? "submit" : "button",
32163
+ variant: primary.variant || "primary",
32164
+ onClick: () => handleActionClick(primary),
32165
+ "data-testid": isSubmit ? "form-submit" : void 0,
32166
+ children: primary.label
32167
+ },
32168
+ "primary"
32169
+ )
32170
+ );
32171
+ }
32172
+ return buttons;
32173
+ };
32174
+ const renderFilters = () => {
32175
+ if (!filters || filters.length === 0) return null;
32176
+ return filters.map((filter, index) => /* @__PURE__ */ jsxRuntime.jsx(
32177
+ Button,
32178
+ {
32179
+ variant: "ghost",
32180
+ onClick: () => {
32181
+ log6.debug("Filter clicked", { field: filter.field });
32182
+ },
32183
+ children: filter.label
32184
+ },
32185
+ `filter-${filter.field}-${index}`
32186
+ ));
32187
+ };
32188
+ return /* @__PURE__ */ jsxRuntime.jsx(
32189
+ "div",
32190
+ {
32191
+ className: cn(
32192
+ "inline-flex gap-2",
32193
+ variantClasses2[variant],
32194
+ orientationClasses[orientation],
32195
+ lookStyles5[look],
32196
+ className
32197
+ ),
32198
+ role: "group",
32199
+ children: children || renderFilters() || renderFormActions()
32200
+ }
32201
+ );
32202
+ };
32203
+ ButtonGroup.displayName = "ButtonGroup";
32204
+ }
32205
+ });
32380
32206
  var resolveFilterType, lookStyles6, FilterGroup;
32381
32207
  var init_FilterGroup = __esm({
32382
32208
  "components/core/molecules/FilterGroup.tsx"() {
@@ -50754,7 +50580,6 @@ var init_component_registry_generated = __esm({
50754
50580
  init_BranchingLogicBuilder();
50755
50581
  init_Breadcrumb();
50756
50582
  init_BuilderBoard();
50757
- init_ButtonGroup();
50758
50583
  init_CTABanner();
50759
50584
  init_CalendarGrid();
50760
50585
  init_Canvas2D();
@@ -50831,9 +50656,9 @@ var init_component_registry_generated = __esm({
50831
50656
  init_FlipContainer();
50832
50657
  init_FloatingActionButton();
50833
50658
  init_Form();
50659
+ init_FormSection();
50834
50660
  init_FormField();
50835
50661
  init_FormSectionHeader();
50836
- init_GameAudioProvider();
50837
50662
  init_GameAudioToggle();
50838
50663
  init_GameCard();
50839
50664
  init_GameHud();
@@ -51082,7 +50907,6 @@ var init_component_registry_generated = __esm({
51082
50907
  "Breadcrumb": Breadcrumb,
51083
50908
  "BuilderBoard": BuilderBoard,
51084
50909
  "Button": ButtonPattern,
51085
- "ButtonGroup": ButtonGroup,
51086
50910
  "ButtonPattern": ButtonPattern,
51087
50911
  "CTABanner": CTABanner,
51088
50912
  "CalendarGrid": CalendarGrid,
@@ -51162,9 +50986,10 @@ var init_component_registry_generated = __esm({
51162
50986
  "FlipContainer": FlipContainer,
51163
50987
  "FloatingActionButton": FloatingActionButton,
51164
50988
  "Form": Form,
50989
+ "FormActions": FormActions,
51165
50990
  "FormField": FormField,
50991
+ "FormLayout": FormLayout,
51166
50992
  "FormSectionHeader": FormSectionHeader,
51167
- "GameAudioProvider": GameAudioProvider,
51168
50993
  "GameAudioToggle": GameAudioToggle,
51169
50994
  "GameBoard3D": GameBoard3D,
51170
50995
  "GameCanvas3D": GameCanvas3D,