@omerdlw/base-framework 1.0.1 → 1.0.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.
Files changed (141) hide show
  1. package/dist/atoms/index.d.ts +0 -1
  2. package/dist/atoms/index.d.ts.map +1 -1
  3. package/dist/atoms/index.js +1 -64
  4. package/dist/atoms/index.js.map +1 -1
  5. package/dist/error/index.js +22 -25
  6. package/dist/error/index.js.map +1 -1
  7. package/dist/hooks/index.d.ts +2 -0
  8. package/dist/hooks/index.d.ts.map +1 -1
  9. package/dist/hooks/index.js +97 -4
  10. package/dist/hooks/index.js.map +1 -1
  11. package/dist/hooks/types.d.ts +11 -0
  12. package/dist/hooks/types.d.ts.map +1 -1
  13. package/dist/hooks/use-controllable-state.d.ts +1 -1
  14. package/dist/hooks/use-controllable-state.d.ts.map +1 -1
  15. package/dist/hooks/use-hotkey.d.ts +1 -1
  16. package/dist/hooks/use-hotkey.d.ts.map +1 -1
  17. package/dist/index.js +10811 -10696
  18. package/dist/index.js.map +1 -1
  19. package/dist/kernel/adapters.d.ts.map +1 -1
  20. package/dist/kernel/index.d.ts +1 -0
  21. package/dist/kernel/index.d.ts.map +1 -1
  22. package/dist/kernel/index.js +13 -84
  23. package/dist/kernel/index.js.map +1 -1
  24. package/dist/kernel/installed.d.ts.map +1 -1
  25. package/dist/kernel/page-controller.d.ts.map +1 -1
  26. package/dist/kernel/provider.d.ts.map +1 -1
  27. package/dist/modules/ambient/constants.d.ts +1 -1
  28. package/dist/modules/ambient/constants.d.ts.map +1 -1
  29. package/dist/modules/ambient/index.js +180 -326
  30. package/dist/modules/ambient/index.js.map +1 -1
  31. package/dist/modules/ambient/module.d.ts.map +1 -1
  32. package/dist/modules/background/constants.d.ts +4 -2
  33. package/dist/modules/background/constants.d.ts.map +1 -1
  34. package/dist/modules/background/context.d.ts +3 -0
  35. package/dist/modules/background/context.d.ts.map +1 -1
  36. package/dist/modules/background/hooks.d.ts +1 -1
  37. package/dist/modules/background/index.d.ts +2 -2
  38. package/dist/modules/background/index.d.ts.map +1 -1
  39. package/dist/modules/background/index.js +1172 -1270
  40. package/dist/modules/background/index.js.map +1 -1
  41. package/dist/modules/background/overlay.d.ts +1 -1
  42. package/dist/modules/background/overlay.d.ts.map +1 -1
  43. package/dist/modules/background/types.d.ts +1 -1
  44. package/dist/modules/background/types.d.ts.map +1 -1
  45. package/dist/modules/context-menu/constants.d.ts +1 -1
  46. package/dist/modules/context-menu/constants.d.ts.map +1 -1
  47. package/dist/modules/context-menu/context.d.ts.map +1 -1
  48. package/dist/modules/context-menu/hooks.d.ts.map +1 -1
  49. package/dist/modules/context-menu/index.js +135 -240
  50. package/dist/modules/context-menu/index.js.map +1 -1
  51. package/dist/modules/controls/constants.d.ts +1 -1
  52. package/dist/modules/controls/constants.d.ts.map +1 -1
  53. package/dist/modules/controls/hooks.d.ts +1 -1
  54. package/dist/modules/controls/index.js +31 -165
  55. package/dist/modules/controls/index.js.map +1 -1
  56. package/dist/modules/controls/types.d.ts +1 -1
  57. package/dist/modules/controls/types.d.ts.map +1 -1
  58. package/dist/modules/dock/cards.d.ts +1 -1
  59. package/dist/modules/dock/constants.d.ts +1 -1
  60. package/dist/modules/dock/constants.d.ts.map +1 -1
  61. package/dist/modules/dock/context.d.ts +10 -6
  62. package/dist/modules/dock/context.d.ts.map +1 -1
  63. package/dist/modules/dock/hooks.d.ts +4 -4
  64. package/dist/modules/dock/hooks.d.ts.map +1 -1
  65. package/dist/modules/dock/index.d.ts +1 -1
  66. package/dist/modules/dock/index.d.ts.map +1 -1
  67. package/dist/modules/dock/index.js +2063 -2083
  68. package/dist/modules/dock/index.js.map +1 -1
  69. package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
  70. package/dist/modules/dock/types.d.ts +1 -1
  71. package/dist/modules/dock/types.d.ts.map +1 -1
  72. package/dist/modules/index.js +10193 -10350
  73. package/dist/modules/index.js.map +1 -1
  74. package/dist/modules/loading/constants.d.ts +4 -2
  75. package/dist/modules/loading/constants.d.ts.map +1 -1
  76. package/dist/modules/loading/context.d.ts +3 -0
  77. package/dist/modules/loading/context.d.ts.map +1 -1
  78. package/dist/modules/loading/hooks.d.ts +1 -1
  79. package/dist/modules/loading/hooks.d.ts.map +1 -1
  80. package/dist/modules/loading/index.d.ts +2 -2
  81. package/dist/modules/loading/index.d.ts.map +1 -1
  82. package/dist/modules/loading/index.js +121 -225
  83. package/dist/modules/loading/index.js.map +1 -1
  84. package/dist/modules/loading/types.d.ts +1 -1
  85. package/dist/modules/loading/types.d.ts.map +1 -1
  86. package/dist/modules/media/constants.d.ts +2 -1
  87. package/dist/modules/media/constants.d.ts.map +1 -1
  88. package/dist/modules/media/context.d.ts +3 -0
  89. package/dist/modules/media/context.d.ts.map +1 -1
  90. package/dist/modules/media/index.d.ts +2 -2
  91. package/dist/modules/media/index.d.ts.map +1 -1
  92. package/dist/modules/media/index.js +61 -218
  93. package/dist/modules/media/index.js.map +1 -1
  94. package/dist/modules/modal/constants.d.ts +1 -1
  95. package/dist/modules/modal/constants.d.ts.map +1 -1
  96. package/dist/modules/modal/context.d.ts.map +1 -1
  97. package/dist/modules/modal/hooks.d.ts +2 -2
  98. package/dist/modules/modal/index.js +31 -293
  99. package/dist/modules/modal/index.js.map +1 -1
  100. package/dist/modules/notification/constants.d.ts +4 -2
  101. package/dist/modules/notification/constants.d.ts.map +1 -1
  102. package/dist/modules/notification/context.d.ts +4 -0
  103. package/dist/modules/notification/context.d.ts.map +1 -1
  104. package/dist/modules/notification/hooks.d.ts +1 -1
  105. package/dist/modules/notification/index.d.ts +2 -3
  106. package/dist/modules/notification/index.d.ts.map +1 -1
  107. package/dist/modules/notification/index.js +65 -151
  108. package/dist/modules/notification/index.js.map +1 -1
  109. package/dist/modules/notification/module.d.ts.map +1 -1
  110. package/dist/provider.js +255 -257
  111. package/dist/provider.js.map +1 -1
  112. package/dist/theme.d.ts +1 -0
  113. package/dist/theme.d.ts.map +1 -1
  114. package/dist/theme.js +2 -2
  115. package/dist/theme.js.map +1 -1
  116. package/dist/utils/dom.d.ts +2 -0
  117. package/dist/utils/dom.d.ts.map +1 -1
  118. package/dist/utils/index.d.ts +1 -1
  119. package/dist/utils/index.d.ts.map +1 -1
  120. package/dist/utils/index.js +28 -19
  121. package/dist/utils/index.js.map +1 -1
  122. package/docs/modules/loading.md +1 -1
  123. package/package.json +48 -2
  124. package/dist/hooks/use-async-action.d.ts +0 -3
  125. package/dist/hooks/use-async-action.d.ts.map +0 -1
  126. package/dist/hooks/use-debounce.d.ts +0 -2
  127. package/dist/hooks/use-debounce.d.ts.map +0 -1
  128. package/dist/hooks/use-intersection-observer.d.ts +0 -4
  129. package/dist/hooks/use-intersection-observer.d.ts.map +0 -1
  130. package/dist/hooks/use-media-query.d.ts +0 -2
  131. package/dist/hooks/use-media-query.d.ts.map +0 -1
  132. package/dist/hooks/use-mounted.d.ts +0 -2
  133. package/dist/hooks/use-mounted.d.ts.map +0 -1
  134. package/dist/hooks/use-server-action.d.ts +0 -4
  135. package/dist/hooks/use-server-action.d.ts.map +0 -1
  136. package/dist/hooks/use-storage-state.d.ts +0 -5
  137. package/dist/hooks/use-storage-state.d.ts.map +0 -1
  138. package/dist/modules/theme.d.ts +0 -3
  139. package/dist/modules/theme.d.ts.map +0 -1
  140. package/dist/utils/json.d.ts +0 -3
  141. package/dist/utils/json.d.ts.map +0 -1
@@ -14,9 +14,6 @@ function cn(...inputs) {
14
14
  return twMerge(clsx(inputs));
15
15
  }
16
16
 
17
- // src/utils/dom.ts
18
- var isBrowser = typeof window !== "undefined";
19
-
20
17
  // src/utils/string.ts
21
18
  function trimToNull(value) {
22
19
  if (value === null || value === void 0) return null;
@@ -37,6 +34,9 @@ function isImageIconSource(icon) {
37
34
  return typeof icon === "string" && (icon.startsWith("http://") || icon.startsWith("https://") || icon.startsWith("/") || icon.startsWith("data:image/"));
38
35
  }
39
36
 
37
+ // src/utils/dom.ts
38
+ var isBrowser = typeof window !== "undefined";
39
+
40
40
  // src/utils/number.ts
41
41
  function clamp(value, min, max) {
42
42
  const num = Number(value);
@@ -423,68 +423,58 @@ function Button({
423
423
  );
424
424
  }
425
425
  Button.displayName = "Button";
426
- var listeners = /* @__PURE__ */ new Set();
427
- var activeFullscreenStateIds = /* @__PURE__ */ new Set();
428
- var ACTIVE_FULLSCREEN_ROOT_SELECTOR = '[data-fullscreen-state-root="true"][data-affect-global-state="true"]';
429
- var domObserver = null;
430
- var lastSnapshot = false;
431
- function getDomSnapshot() {
432
- if (typeof document === "undefined") {
433
- return activeFullscreenStateIds.size > 0;
434
- }
435
- return document.querySelector(ACTIVE_FULLSCREEN_ROOT_SELECTOR) !== null;
436
- }
437
- function emitChange() {
438
- listeners.forEach((listener) => listener());
439
- }
440
- function emitIfSnapshotChanged() {
441
- const nextSnapshot = getDomSnapshot();
442
- if (nextSnapshot === lastSnapshot) {
443
- return;
444
- }
445
- lastSnapshot = nextSnapshot;
446
- emitChange();
447
- }
448
- function ensureDomObserver() {
449
- if (typeof document === "undefined" || domObserver || listeners.size === 0) {
450
- return;
451
- }
452
- domObserver = new MutationObserver(() => {
453
- emitIfSnapshotChanged();
454
- });
455
- domObserver.observe(document.documentElement, {
456
- attributes: true,
457
- childList: true,
458
- subtree: true,
459
- attributeFilter: [
460
- "data-fullscreen-state",
461
- "data-fullscreen-state-root",
462
- "data-affect-global-state"
463
- ]
464
- });
465
- }
466
- function releaseDomObserver() {
467
- if (listeners.size > 0 || !domObserver) {
468
- return;
469
- }
470
- domObserver.disconnect();
471
- domObserver = null;
472
- }
473
- function subscribe(listener) {
474
- listeners.add(listener);
475
- ensureDomObserver();
476
- emitIfSnapshotChanged();
477
- return () => {
478
- listeners.delete(listener);
479
- releaseDomObserver();
480
- };
481
- }
482
- function getSnapshot() {
483
- return getDomSnapshot();
484
- }
485
- function useIsFullscreenStateActive() {
486
- return useSyncExternalStore(subscribe, getSnapshot, () => false);
487
- }
426
+
427
+ // src/kernel/constants.ts
428
+ var DEFAULT_SOURCE = "dynamic";
429
+ var REGISTRY_SOURCES = Object.freeze({
430
+ STATIC: "static",
431
+ DYNAMIC: "dynamic",
432
+ USER: "user"
433
+ });
434
+ var REGISTRY_LIFECYCLES = Object.freeze({
435
+ IMMEDIATE: "immediate",
436
+ GRACEFUL: "graceful",
437
+ PERSISTENT: "persistent",
438
+ ROUTE: "route"
439
+ });
440
+ var REGISTRY_VALIDATION_MODES = Object.freeze({
441
+ WARN: "warn",
442
+ STRICT: "strict"
443
+ });
444
+ var REGISTRY_METADATA_KEYS = Object.freeze([
445
+ "cleanup",
446
+ "cleanupDelayMs",
447
+ "instanceId",
448
+ "lifecycle",
449
+ "priority",
450
+ "source",
451
+ "scope",
452
+ "validation"
453
+ ]);
454
+ Object.freeze({
455
+ [REGISTRY_SOURCES.STATIC]: 100,
456
+ [REGISTRY_SOURCES.DYNAMIC]: 200,
457
+ [REGISTRY_SOURCES.USER]: 300
458
+ });
459
+ Object.freeze({
460
+ [REGISTRY_SOURCES.STATIC]: 10,
461
+ [REGISTRY_SOURCES.DYNAMIC]: 20,
462
+ [REGISTRY_SOURCES.USER]: 30
463
+ });
464
+ var REGISTRY_LIFECYCLE_VALUES = new Set(
465
+ Object.values(REGISTRY_LIFECYCLES)
466
+ );
467
+ var REGISTRY_METADATA_KEY_SET = new Set(
468
+ REGISTRY_METADATA_KEYS
469
+ );
470
+ var REGISTRY_SCOPE_KINDS = Object.freeze({
471
+ APP: "app",
472
+ SESSION: "session",
473
+ ROUTE: "route",
474
+ INSTANCE: "instance",
475
+ WORKSPACE: "workspace"
476
+ });
477
+ REGISTRY_SCOPE_KINDS.APP;
488
478
  function getOrCreateGlobalContext(name, defaultValue) {
489
479
  const sym = /* @__PURE__ */ Symbol.for(`@omerdlw/base-framework/context/${name}`);
490
480
  const store = globalThis;
@@ -494,723 +484,104 @@ function getOrCreateGlobalContext(name, defaultValue) {
494
484
  return store[sym];
495
485
  }
496
486
 
497
- // src/theme.tsx
498
- function defineThemeSpec(id) {
499
- return Object.freeze({ id });
487
+ // src/kernel/utils.ts
488
+ function parseFiniteNumber(value) {
489
+ const parsed = Number(value);
490
+ return Number.isFinite(parsed) ? parsed : null;
500
491
  }
501
- var EMPTY = /* @__PURE__ */ new Map();
502
- var ThemeContext = getOrCreateGlobalContext("ThemeContext", EMPTY);
503
- function useTheme(spec) {
504
- const config = useContext(ThemeContext).get(spec.id);
505
- if (!config) {
506
- throw new Error(
507
- `Theme "${spec.id}" is missing. Add its theme file to src/config/ and list it in src/config/index.ts.`
492
+ function getValidString(value, fallback = null) {
493
+ return trimToNull(value) ?? fallback;
494
+ }
495
+
496
+ // src/kernel/schema.ts
497
+ function resolveLifecycle(metadata, fallback) {
498
+ const lifecycle = metadata?.lifecycle ?? metadata?.cleanup;
499
+ return lifecycle && REGISTRY_LIFECYCLE_VALUES.has(lifecycle) ? lifecycle : fallback;
500
+ }
501
+ function validateRegistryMetadata(metadata) {
502
+ if (metadata === void 0 || metadata === null)
503
+ return { issues: [], valid: true };
504
+ if (!isObject(metadata))
505
+ return { issues: ["registry metadata must be an object"], valid: false };
506
+ const issues = [];
507
+ Object.keys(metadata).forEach((key) => {
508
+ if (!REGISTRY_METADATA_KEY_SET.has(key))
509
+ issues.push(`unknown registry metadata field: ${key}`);
510
+ });
511
+ if (metadata.source !== void 0 && !getValidString(metadata.source))
512
+ issues.push("source must be a non-empty string");
513
+ if (metadata.instanceId !== void 0 && !getValidString(metadata.instanceId))
514
+ issues.push("instanceId must be a non-empty string");
515
+ if (metadata.scope !== void 0 && !getValidString(metadata.scope))
516
+ issues.push("scope must be a non-empty string");
517
+ if (metadata.priority !== void 0 && parseFiniteNumber(metadata.priority) === null)
518
+ issues.push("priority must be a finite number");
519
+ if (metadata.cleanupDelayMs !== void 0) {
520
+ const delay = parseFiniteNumber(metadata.cleanupDelayMs);
521
+ if (delay === null || delay < 0)
522
+ issues.push("cleanupDelayMs must be a non-negative finite number");
523
+ }
524
+ const lifecycle = metadata.lifecycle ?? metadata.cleanup;
525
+ if (lifecycle !== void 0 && !REGISTRY_LIFECYCLE_VALUES.has(String(lifecycle))) {
526
+ issues.push(
527
+ `lifecycle must be one of: ${[...REGISTRY_LIFECYCLE_VALUES].join(", ")}`
528
+ );
529
+ }
530
+ if (metadata.validation !== void 0 && !Object.values(REGISTRY_VALIDATION_MODES).includes(
531
+ metadata.validation
532
+ )) {
533
+ issues.push(
534
+ `validation must be one of: ${Object.values(REGISTRY_VALIDATION_MODES).join(", ")}`
508
535
  );
509
536
  }
537
+ return { issues, valid: issues.length === 0 };
538
+ }
539
+ function normalizeRegistryMetadata(metadata, {
540
+ defaultCleanupDelayMs = null,
541
+ defaultLifecycle = null,
542
+ defaultSource = DEFAULT_SOURCE
543
+ } = {}) {
544
+ const registryMeta = isObject(metadata) ? metadata : {};
545
+ const normalizedDefaultDelay = parseFiniteNumber(defaultCleanupDelayMs);
546
+ const fallbackCleanupDelayMs = normalizedDefaultDelay !== null && normalizedDefaultDelay >= 0 ? normalizedDefaultDelay : null;
547
+ const fallbackLifecycle = defaultLifecycle && REGISTRY_LIFECYCLE_VALUES.has(defaultLifecycle) ? defaultLifecycle : null;
548
+ const source = getValidString(registryMeta.source) || getValidString(defaultSource, DEFAULT_SOURCE) || DEFAULT_SOURCE;
549
+ const scope = getValidString(registryMeta.scope);
550
+ const priority = parseFiniteNumber(registryMeta.priority);
551
+ const lifecycle = resolveLifecycle(registryMeta, fallbackLifecycle);
552
+ const validation = isValidationMode(registryMeta.validation) ? registryMeta.validation : null;
553
+ const cleanupDelayCandidate = parseFiniteNumber(registryMeta.cleanupDelayMs);
554
+ let cleanupDelayMs = cleanupDelayCandidate !== null && cleanupDelayCandidate >= 0 ? cleanupDelayCandidate : fallbackCleanupDelayMs;
555
+ if (lifecycle === REGISTRY_LIFECYCLES.IMMEDIATE) {
556
+ cleanupDelayMs = 0;
557
+ } else if ((lifecycle === REGISTRY_LIFECYCLES.GRACEFUL || lifecycle === REGISTRY_LIFECYCLES.ROUTE) && cleanupDelayMs === null) {
558
+ cleanupDelayMs = fallbackCleanupDelayMs;
559
+ }
510
560
  return {
511
- slots: config.slots,
512
- styles: config.styles ?? {}
561
+ cleanupDelayMs,
562
+ lifecycle,
563
+ priority,
564
+ registerOptions: {
565
+ ...priority !== null && { priority },
566
+ ...validation === REGISTRY_VALIDATION_MODES.STRICT && { validation },
567
+ ...scope && { scope }
568
+ },
569
+ source,
570
+ ...scope && { scope },
571
+ ...validation && { validation }
513
572
  };
514
573
  }
515
-
516
- // src/modules/dock/constants.ts
517
- var DOCK_STYLE_SECTIONS = Object.freeze([
518
- "card",
519
- "icon",
520
- "title",
521
- "description"
522
- ]);
523
- var DOCK_EVENTS = Object.freeze({
524
- NAVIGATE_START: "DOCK_NAVIGATE_START",
525
- NAVIGATE_END: "DOCK_NAVIGATE_END",
526
- UPDATE_BADGE: "DOCK_UPDATE_BADGE",
527
- NAVIGATE: "DOCK_NAVIGATE",
528
- GUARD: "DOCK_GUARD",
529
- NOT_FOUND: "DOCK_NOT_FOUND",
530
- STATUS_SET: "DOCK_STATUS_SET",
531
- STATUS_CLEAR: "DOCK_STATUS_CLEAR"
532
- });
533
- var DOCK_HUD_RENDER_MODE = Object.freeze({
534
- COMPONENT: "component",
535
- NODE: "node"
536
- });
537
- var DOCK_HUD_VARIANT = Object.freeze({
538
- EXPANDED: "expanded",
539
- PROGRESS: "progress",
540
- CUSTOM: "custom"
541
- });
542
- var DOCK_HUD_PRIORITY = Object.freeze({
543
- TASK_PROGRESS: 30,
544
- CONTEXTUAL: 10,
545
- SELECTION: 20,
546
- CRITICAL: 50,
547
- DEFAULT: 0,
548
- MEDIA: 15
549
- });
550
- var DOCK_ATTENTION_KIND = Object.freeze({
551
- SURFACE: "surface",
552
- OPERATION: "operation",
553
- LOADING: "loading",
554
- STATUS: "status",
555
- ROUTE: "route",
556
- HUD: "hud"
557
- });
558
- var DOCK_ATTENTION_PRIORITY = Object.freeze({
559
- STATUS_OVERLAY: 300,
560
- SURFACE: 400,
561
- OPERATION: 250,
562
- LOADING: 100,
563
- STATUS: 75,
564
- ROUTE: 0,
565
- HUD: 200
566
- });
567
- var DOCK_ATTENTION_PRIORITY_OFFSET_MAX = 99;
568
- var DOCK_SURFACE_RENDER_MODE = Object.freeze({
569
- COMPONENT: "component",
570
- NODE: "node"
571
- });
572
- var DOCK_SURFACE_FLOW_STATUS = Object.freeze({
573
- OPEN: "open",
574
- COMPLETED: "completed",
575
- CANCELLED: "cancelled"
576
- });
577
- var DOCK_SURFACE_PHASE = Object.freeze({
578
- IDLE: "idle",
579
- DISMISSING_ACTION: "dismissing_action",
580
- EXPANDING_BODY: "expanding_body",
581
- OPEN: "open",
582
- CLOSING_ANTICIPATION: "closing_anticipation",
583
- COLLAPSING_BODY: "collapsing_body",
584
- RESTORING_HEADER: "restoring_header"
585
- });
586
- var VIEWPORT_MARGIN = 24;
587
- var DOCK_CARD_DIMENSIONS = Object.freeze({
588
- expandedY: -72,
589
- chromeHeight: 20,
590
- collapsedY: -10,
591
- extensionShelfY: -42,
592
- extensionShelfScale: 0.94,
593
- extensionShelfHeight: 45,
594
- hudHeight: 52,
595
- actionGap: 10,
596
- height: 68
597
- });
598
- var DOCK_CARD_LAYOUT = Object.freeze({
599
- collapsed: Object.freeze({
600
- offsetY: DOCK_CARD_DIMENSIONS.collapsedY,
601
- scale: 0.88
602
- }),
603
- expanded: Object.freeze({
604
- offsetY: DOCK_CARD_DIMENSIONS.expandedY,
605
- scale: 1
606
- }),
607
- extensionShelfY: DOCK_CARD_DIMENSIONS.extensionShelfY,
608
- extensionShelfScale: DOCK_CARD_DIMENSIONS.extensionShelfScale,
609
- extensionShelfHeight: DOCK_CARD_DIMENSIONS.extensionShelfHeight,
610
- chromeHeight: DOCK_CARD_DIMENSIONS.chromeHeight,
611
- hudHeight: DOCK_CARD_DIMENSIONS.hudHeight,
612
- actionGap: DOCK_CARD_DIMENSIONS.actionGap,
613
- baseHeight: DOCK_CARD_DIMENSIONS.height
614
- });
615
- var DOCK_TRANSACTION_EVENTS = Object.freeze({
616
- START: "START",
617
- COMPLETE: "COMPLETE",
618
- CANCEL: "CANCEL",
619
- FAIL: "FAIL",
620
- TIME_OUT: "TIME_OUT"
621
- });
622
- var DOCK_TRANSACTION_STATUS = Object.freeze({
623
- PENDING: "pending",
624
- COMPLETED: "completed",
625
- CANCELLED: "cancelled",
626
- FAILED: "failed",
627
- TIMED_OUT: "timed-out"
628
- });
629
- var DOCK_TRANSACTION_REASON = Object.freeze({
630
- GUARD: "guard",
631
- SUPERSEDED: "superseded",
632
- TIME_OUT: "time-out"
633
- });
634
- var DOCK_TRANSACTION_TIMEOUT_MS = 15e3;
635
- var DOCK_PREFETCH_INTENT_DELAY_MS = 90;
636
- var DOCK_CONTINUITY_EVENTS = Object.freeze({
637
- CLEAR: "CLEAR",
638
- CONSUME_RETURN: "CONSUME_RETURN",
639
- DELIVER_RETURN: "DELIVER_RETURN",
640
- RECORD: "RECORD",
641
- REMOVE: "REMOVE"
642
- });
643
- var DOCK_CONTINUITY_MAX_ENTRIES = 32;
644
- var DOCK_SURFACE_RETURN_MAX_ENTRIES = 16;
645
- var DOCK_OPERATION_EVENTS = Object.freeze({
646
- CANCEL: "CANCEL",
647
- CLEAR: "CLEAR",
648
- COMPLETE: "COMPLETE",
649
- START: "START",
650
- UPDATE: "UPDATE"
651
- });
652
- var DOCK_OPERATION_STATUS = Object.freeze({
653
- CANCELLED: "cancelled",
654
- COMPLETED: "completed",
655
- PENDING: "pending"
656
- });
657
- var DOCK_OPERATION_MAX_ENTRIES = 24;
658
- var DOCK_FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
659
- var DOCK_FOCUS_RESTORE_BLOCKED_REASONS = Object.freeze([
660
- "browser-back",
661
- "dock",
662
- "unmount"
663
- ]);
664
- var DOCK_LIFECYCLE = Object.freeze({
665
- CLOSING: "closing",
666
- OPENING: "opening",
667
- IDLE: "idle",
668
- OPEN: "open"
669
- });
670
- var PLAYBACK_RATES = Object.freeze([1, 1.25, 1.5, 2]);
671
- var STATUS_PRIORITY = Object.freeze({
672
- GUARD: 120,
673
- ACCOUNT_DELETE: 115,
674
- APP_ERROR: 100,
675
- API_ERROR: 95,
676
- NOT_FOUND: 97,
677
- OFFLINE: 90,
678
- LOGOUT: 110,
679
- SIGNUP: 110,
680
- LOGIN: 110,
681
- ONLINE: 10
682
- });
683
- var ERROR_STATUS_TYPES = /* @__PURE__ */ new Set([
684
- "GUARD",
685
- "ACCOUNT_DELETE",
686
- "APP_ERROR",
687
- "API_ERROR",
688
- "NOT_FOUND"
689
- ]);
690
- var OVERLAY_STATUS_STORAGE_KEY = "dock_overlay_status";
691
- var OVERLAY_STATUS_CLEAR_DURATION = 3e3;
692
- var STATUS_CLEAR_DURATION = 4500;
693
- var API_ERROR_BATCH_DELAY = 300;
694
- var DOCK_HEIGHT_BUFFER = 16;
695
- var HEIGHT_EPSILON = 0.5;
696
- var MAX_VISIBLE_STACKED_CARDS = 3;
697
- var dockTheme = defineThemeSpec("dock");
698
-
699
- // src/modules/dock/utils.ts
700
- function isSamePath(left, right) {
701
- const normalizedLeft = normalizePath(left);
702
- const normalizedRight = normalizePath(right);
703
- return Boolean(
704
- normalizedLeft && normalizedRight && normalizedLeft === normalizedRight
705
- );
706
- }
707
- function isPathPrefix(candidatePath, pathname) {
708
- const normalizedCandidate = normalizePath(candidatePath);
709
- const normalizedPathname = normalizePath(pathname);
710
- if (!normalizedCandidate || !normalizedPathname) return false;
711
- if (normalizedCandidate === normalizedPathname) return true;
712
- if (normalizedCandidate === "/") return normalizedPathname.startsWith("/");
713
- return normalizedPathname.startsWith(`${normalizedCandidate}/`);
714
- }
715
- function isInlineActionPathMatch(path, pathname) {
716
- return isSamePath(path, pathname) || path !== "/" && isPathPrefix(path, pathname);
717
- }
718
- function isSafeInternalHref(value) {
719
- const href = typeof value === "string" ? value.trim() : "";
720
- return href.startsWith("/") && !href.startsWith("//");
721
- }
722
- function getDockLocationKey({
723
- hash = "",
724
- pathname = "/",
725
- search = ""
726
- } = {}) {
727
- const normalizedPathname = String(pathname || "/").trim() || "/";
728
- const normalizedSearch = String(search || "").trim();
729
- const normalizedHash = String(hash || "").trim();
730
- const query = normalizedSearch ? normalizedSearch.startsWith("?") ? normalizedSearch : `?${normalizedSearch}` : "";
731
- const fragment = normalizedHash ? normalizedHash.startsWith("#") ? normalizedHash : `#${normalizedHash}` : "";
732
- return `${normalizedPathname}${query}${fragment}`;
733
- }
734
- function formatMediaTime(seconds = 0) {
735
- const sec = Math.max(0, Math.floor(Number(seconds) || 0));
736
- const m = Math.floor(sec / 60);
737
- const s = sec % 60;
738
- return `${m}:${s < 10 ? "0" : ""}${s}`;
739
- }
740
- function isValidBannerUrl(banner) {
741
- const value = trimToNull(banner);
742
- if (!value) return false;
743
- return /^(https?:\/\/|\/|data:image\/)/.test(value);
744
- }
745
- function toObject(value) {
746
- return isObject(value) ? value : {};
747
- }
748
- function isValidComponentType(type) {
749
- if (typeof type === "function") return true;
750
- return Boolean(
751
- type != null && typeof type === "object" && !isValidElement(type) && "$$typeof" in type
752
- );
753
- }
754
- function resolveComponentType(...candidates) {
755
- return candidates.find(isValidComponentType) ?? null;
756
- }
757
- function resolveRenderableContent(...candidates) {
758
- return candidates.find(
759
- (candidate) => candidate !== null && candidate !== void 0
760
- ) ?? null;
761
- }
762
- function collectSearchableText(value, visitedObjects) {
763
- if (typeof value === "string") return value;
764
- if (typeof value === "number" || typeof value === "boolean")
765
- return String(value);
766
- if (Array.isArray(value)) {
767
- if (visitedObjects.has(value)) return "";
768
- visitedObjects.add(value);
769
- const text = value.map((entry) => collectSearchableText(entry, visitedObjects)).join(" ");
770
- visitedObjects.delete(value);
771
- return text;
772
- }
773
- if (isValidElement(value)) {
774
- if (visitedObjects.has(value)) return "";
775
- visitedObjects.add(value);
776
- const text = collectSearchableText(
777
- value.props?.children,
778
- visitedObjects
779
- );
780
- visitedObjects.delete(value);
781
- return text;
782
- }
783
- if (value && typeof value === "object") {
784
- if (visitedObjects.has(value)) return "";
785
- visitedObjects.add(value);
786
- return Object.values(value).map((entry) => collectSearchableText(entry, visitedObjects)).join(" ");
787
- }
788
- return "";
789
- }
790
- function toSearchableText(value) {
791
- return collectSearchableText(value, /* @__PURE__ */ new WeakSet());
792
- }
793
- function normalizeUpper(value) {
794
- return String(value || "").trim().toUpperCase();
795
- }
796
- function splitStyle(style = {}) {
797
- const { className, ...inlineStyle } = style;
798
- return {
799
- className,
800
- inlineStyle
801
- };
802
- }
803
- function getLineClampStyle(maxLines, style) {
804
- if (Number(maxLines) <= 1) return style;
805
- return {
806
- WebkitBoxOrient: "vertical",
807
- WebkitLineClamp: maxLines,
808
- display: "-webkit-box",
809
- overflow: "hidden",
810
- ...style
811
- };
812
- }
813
- function getImageIconStyle(style, icon) {
814
- const nextStyle = {
815
- ...style
816
- };
817
- delete nextStyle.background;
818
- delete nextStyle.backgroundImage;
819
- return {
820
- ...nextStyle,
821
- backgroundImage: `url(${icon})`
822
- };
823
- }
824
- function resolveDockActionClass(slots, {
825
- className = "",
826
- button = "",
827
- isActive = false,
828
- variant = "",
829
- tone = "",
830
- base,
831
- cn: classNamesFn
832
- } = {}) {
833
- const resolve = classNamesFn || cn;
834
- const toneClasses = {
835
- muted: slots.actionMuted,
836
- active: slots.actionActive
837
- };
838
- if (button && !className && base === void 0) {
839
- if (tone) {
840
- const toneClass = toneClasses[tone] || slots.actionSurface || slots.actionMuted;
841
- return resolve(button, toneClass);
842
- }
843
- const stateToken = isActive ? slots.actionActive : slots.actionMuted;
844
- return resolve(button, stateToken);
845
- }
846
- const elementClass = className || button;
847
- const resolvedBase = base !== void 0 ? base : slots.action;
848
- const stateClass = variant || tone && (toneClasses[tone] || slots.actionSurface) || (isActive ? slots.actionActive : slots.actionMuted);
849
- return resolve(stateClass, resolvedBase, elementClass);
850
- }
851
- function blurActiveElement() {
852
- if (typeof document === "undefined") return;
853
- const activeElement = document.activeElement;
854
- if (activeElement && typeof activeElement.blur === "function") {
855
- activeElement.blur();
856
- }
857
- }
858
- function closestMatches(target, selector) {
859
- const element = target;
860
- return Boolean(
861
- element && typeof element.closest === "function" && element.closest(selector)
862
- );
863
- }
864
- function isInteractiveTarget(target) {
865
- return closestMatches(
866
- target,
867
- 'button, a, input, textarea, select, [role="button"], [tabindex]:not([tabindex="-1"])'
868
- );
869
- }
870
- function isEditableDockTarget(target) {
871
- return closestMatches(
872
- target,
873
- 'input, textarea, select, [contenteditable="true"]'
874
- );
875
- }
876
- function getDockFocusableElements(container) {
877
- if (!container || typeof container.querySelectorAll !== "function") return [];
878
- return Array.from(
879
- container.querySelectorAll(DOCK_FOCUSABLE_SELECTOR)
880
- ).filter((element) => {
881
- if (!element || element.getAttribute("aria-hidden") === "true")
882
- return false;
883
- const style = typeof window !== "undefined" ? window.getComputedStyle(element) : null;
884
- return style?.display !== "none" && style?.visibility !== "hidden" && style?.pointerEvents !== "none";
885
- });
886
- }
887
- function focusDockElement(element) {
888
- if (!element || typeof element.focus !== "function") return false;
889
- try {
890
- element.focus({ preventScroll: true });
891
- return document.activeElement === element;
892
- } catch {
893
- return false;
894
- }
895
- }
896
- function shouldRestoreDockFocus(result) {
897
- const blockedReason = result?.blockedReason;
898
- return !result || !DOCK_FOCUS_RESTORE_BLOCKED_REASONS.includes(
899
- blockedReason
900
- );
901
- }
902
- function isSameDockItem(item, candidate) {
903
- return Boolean(
904
- item?.path && item.path === candidate?.path || item?.name && item.name === candidate?.name
905
- );
906
- }
907
- var isSameItem = isSameDockItem;
908
- function getItemKey(link, index = 0) {
909
- const identity = link?.id ?? link?.path ?? link?.name ?? link?.type;
910
- return `dock-card:${identity == null ? `slot-${index}` : String(identity)}`;
911
- }
912
- function getItemMeasurementKey({
913
- link,
914
- expanded,
915
- isHud = false
916
- }) {
917
- const state = isHud ? "hud" : link.isLoading ? "loading" : link.isSurface ? `surface-${link.surfacePhase ?? "open"}` : "standard";
918
- return `${link.path || link.name || "item"}:${state}:${expanded ? "expanded" : "collapsed"}`;
919
- }
920
- function getRouteMeasurementKey(pathname, key) {
921
- return `${pathname || ""}:${key}`;
922
- }
923
- function resolveDockHeaderKey({
924
- link,
925
- description = "",
926
- showVideoIcon = false
927
- }) {
928
- const statusPart = link?.isStatus ? `status:${link.statusType || link.type || "status"}` : "standard";
929
- const identityPart = link?.path || link?.name || link?.id || "item";
930
- const titlePart = link?.title || link?.name || "";
931
- const descPart = description || "";
932
- const iconPart = showVideoIcon ? "video" : link?.icon || "no-icon";
933
- return `${statusPart}:${identityPart}:${iconPart}:${titlePart}:${descPart}`;
934
- }
935
- function findDockItemIndex(dockItems, activeItem, pathname) {
936
- const normalizedPathname = normalizePath(pathname);
937
- const selectedDataSourceIndex = dockItems.findIndex(
938
- (item) => item.isDataSource && item.isSelected
939
- );
940
- if (selectedDataSourceIndex !== -1) return selectedDataSourceIndex;
941
- if (activeItem) {
942
- const matchedActiveIndex = dockItems.findIndex(
943
- (item) => item.path && isSamePath(item.path, activeItem.path) || item.name && item.name === activeItem.name
944
- );
945
- if (matchedActiveIndex !== -1) return matchedActiveIndex;
946
- }
947
- return dockItems.findIndex(
948
- (item) => isSamePath(item.path, normalizedPathname) || item.targetPath && isSamePath(item.targetPath, normalizedPathname)
949
- );
950
- }
951
- function resolveActiveIndex({
952
- dockItems,
953
- activeItem,
954
- pathname
955
- }) {
956
- return Math.max(0, findDockItemIndex(dockItems, activeItem, pathname));
957
- }
958
- function shouldKeepAncestorItem(item, activePath) {
959
- const policy = item?.keepWhenDescendant;
960
- if (typeof policy === "function") {
961
- try {
962
- return Boolean(policy(activePath, item));
963
- } catch {
964
- return false;
965
- }
966
- }
967
- return policy === true;
968
- }
969
- function isAncestorPath(item, activePath) {
970
- const candidatePath = item?.path;
971
- if (!candidatePath || candidatePath === "/" || candidatePath === activePath)
972
- return false;
973
- return isPathPrefix(candidatePath, activePath) && !shouldKeepAncestorItem(item, activePath);
974
- }
975
- function removeAncestorDuplicates(items = []) {
976
- if (!Array.isArray(items) || items.length <= 1) return items;
977
- const activePath = items[0]?.path;
978
- if (!activePath) return items;
979
- return items.filter(
980
- (item, index) => index === 0 || !isAncestorPath(item, activePath)
981
- );
982
- }
983
- function replaceActiveItem(items, activeIndex, activeItem) {
984
- if (activeIndex === -1 || !activeItem) return items;
985
- const nextItems = [...items];
986
- nextItems[activeIndex] = activeItem;
987
- return nextItems;
988
- }
989
- function removeInactiveLoadingItems(items = [], activeItem = null) {
990
- if (!Array.isArray(items) || items.length === 0) return items;
991
- return items.filter(
992
- (item) => !item?.isLoading || isSameItem(item, activeItem)
993
- );
994
- }
995
- function reorderItemsWithActiveFirst(items, activeIndex) {
996
- if (activeIndex === -1) return items;
997
- const active = items[activeIndex];
998
- const rest = [
999
- ...items.slice(0, activeIndex),
1000
- ...items.slice(activeIndex + 1)
1001
- ];
1002
- rest.sort((a, b) => a.path === "/" ? 1 : b.path === "/" ? -1 : 0);
1003
- return [active, ...rest];
1004
- }
1005
- function isHudDescriptor(value) {
1006
- return isObject(value) && !isValidElement(value) && ("component" in value || "content" in value || "node" in value || "element" in value || "isActive" in value || "id" in value);
1007
- }
1008
- function isSurfaceDescriptor(value) {
1009
- return value != null && typeof value === "object" && !Array.isArray(value) && !isValidElement(value);
1010
- }
1011
- var generatedExtensionId = 0;
1012
- function normalizeSurfaceExtension(input) {
1013
- if (!input) return null;
1014
- if (isValidElement(input)) {
1015
- return {
1016
- align: "left",
1017
- className: "",
1018
- component: null,
1019
- content: input,
1020
- id: `ext-${++generatedExtensionId}`,
1021
- order: 0,
1022
- props: {},
1023
- unstyled: false
1024
- };
1025
- }
1026
- if (typeof input !== "object") return null;
1027
- const extension = input;
1028
- const component = isValidComponentType(extension.component) ? extension.component : null;
1029
- const content = isValidElement(extension.content) || typeof extension.content === "string" || typeof extension.content === "number" ? extension.content : null;
1030
- if (!component && content == null) return null;
1031
- const align = extension.align === "right" || extension.align === "end" ? "right" : extension.align === "center" ? "center" : "left";
1032
- return {
1033
- align,
1034
- className: typeof extension.className === "string" ? extension.className : "",
1035
- component,
1036
- content,
1037
- id: String(
1038
- extension.id || extension.key || `ext-${++generatedExtensionId}`
1039
- ),
1040
- order: Number.isFinite(Number(extension.order)) ? Number(extension.order) : 0,
1041
- props: isPlainObject(extension.props) ? extension.props : {},
1042
- unstyled: Boolean(extension.unstyled)
1043
- };
1044
- }
1045
- function normalizeSurfaceFlowSnapshot(value) {
1046
- if (!isPlainObject(value)) return null;
1047
- return { ...value };
1048
- }
1049
- function createSurfaceReturnHandshake(input) {
1050
- const source = typeof input === "string" ? { pathname: input } : input;
1051
- const pathname = typeof source?.pathname === "string" ? source.pathname.trim() : "";
1052
- if (!isSafeInternalHref(pathname)) return null;
1053
- return {
1054
- focusKey: typeof source?.focusKey === "string" && source.focusKey.trim() ? source.focusKey.trim() : null,
1055
- pathname,
1056
- restoreScroll: source?.restoreScroll !== false,
1057
- returnOnCancel: source?.returnOnCancel === true
1058
- };
1059
- }
1060
- function resolveSurfaceFlowReturnHandshake(definition, input) {
1061
- const flowInput = input && typeof input === "object" ? input : null;
1062
- const inputHandshake = flowInput?.returnHandshake ?? (flowInput?.returnTo ? {
1063
- focusKey: flowInput.returnFocusKey,
1064
- pathname: flowInput.returnTo,
1065
- restoreScroll: flowInput.restoreReturnScroll,
1066
- returnOnCancel: flowInput.returnOnCancel
1067
- } : null);
1068
- const baseHandshake = definition?.returnHandshake;
1069
- if (!baseHandshake && !inputHandshake) return null;
1070
- return createSurfaceReturnHandshake({ ...baseHandshake, ...inputHandshake });
1071
- }
1072
- function createDockScheduler(options = {}) {
1073
- return createScheduler({
1074
- defaultLabel: "dock-task",
1075
- ...options
1076
- });
1077
- }
1078
-
1079
- // src/modules/dock/surface/context.tsx
1080
- var SurfaceExtensionsContext = createContext(null);
1081
- var SurfaceItemContext = createContext({
1082
- id: null,
1083
- width: null
1084
- });
1085
- function useSurfaceDimensions() {
1086
- const { width } = use(SurfaceItemContext);
1087
- return useMemo(() => ({ width }), [width]);
1088
- }
1089
- function useSurfaceId() {
1090
- return use(SurfaceItemContext).id;
1091
- }
1092
- function useSurfaceHeader() {
1093
- const store = use(SurfaceExtensionsContext)?.headerActions ?? null;
1094
- const surfaceId = useSurfaceId();
1095
- return useCallback(
1096
- (patch) => {
1097
- if (!store) return;
1098
- const data = typeof patch === "function" ? patch({}) : patch;
1099
- if (data?.headerAction !== void 0)
1100
- store.setAction(surfaceId, data.headerAction);
1101
- },
1102
- [store, surfaceId]
1103
- );
1104
- }
1105
- function useSurfaceAction(action) {
1106
- const store = use(SurfaceExtensionsContext)?.headerActions ?? null;
1107
- const surfaceId = useSurfaceId();
1108
- useEffect(() => {
1109
- if (!store || action === void 0) return;
1110
- store.setAction(surfaceId, action);
1111
- return () => store.removeAction(surfaceId);
1112
- }, [store, surfaceId, action]);
1113
- }
1114
- var SurfaceHeaderActionStore = class {
1115
- store = createStore({
1116
- actionsBySurface: {},
1117
- version: 0
1118
- });
1119
- get version() {
1120
- return this.store.getSnapshot().version;
1121
- }
1122
- subscribe = (listener) => this.store.subscribe(listener);
1123
- getAction = (surfaceId) => {
1124
- const sId = surfaceId != null ? String(surfaceId) : "global";
1125
- const { actionsBySurface } = this.store.getSnapshot();
1126
- return actionsBySurface[sId] ?? actionsBySurface.global ?? null;
1127
- };
1128
- setAction = (surfaceId, action) => {
1129
- const sId = surfaceId != null ? String(surfaceId) : "global";
1130
- const current = this.store.getSnapshot();
1131
- if (current.actionsBySurface[sId] === action) return;
1132
- this.store.setState({
1133
- actionsBySurface: {
1134
- ...current.actionsBySurface,
1135
- [sId]: action
1136
- },
1137
- version: current.version + 1
1138
- });
1139
- };
1140
- removeAction = (surfaceId) => {
1141
- const sId = surfaceId != null ? String(surfaceId) : "global";
1142
- const current = this.store.getSnapshot();
1143
- if (!(sId in current.actionsBySurface)) return;
1144
- const { [sId]: _, ...nextActions } = current.actionsBySurface;
1145
- this.store.setState({
1146
- actionsBySurface: nextActions,
1147
- version: current.version + 1
1148
- });
1149
- };
1150
- clearSurface = (surfaceId) => this.removeAction(surfaceId);
1151
- };
1152
- var SurfaceExtensionsStore = class {
1153
- extensionsBySurface = /* @__PURE__ */ new Map();
1154
- cachedListBySurface = /* @__PURE__ */ new Map();
1155
- store = createStore({ version: 0 });
1156
- get version() {
1157
- return this.store.getSnapshot().version;
1158
- }
1159
- subscribe = (listener) => this.store.subscribe(listener);
1160
- notify = () => {
1161
- this.cachedListBySurface.clear();
1162
- this.store.setState((prev) => ({ version: prev.version + 1 }));
1163
- };
1164
- getExtensionsForSurface = (surfaceId) => {
1165
- const sId = surfaceId != null ? String(surfaceId) : "global";
1166
- if (this.cachedListBySurface.has(sId))
1167
- return this.cachedListBySurface.get(sId);
1168
- const globalExts = this.extensionsBySurface.get("global");
1169
- const surfaceExts = this.extensionsBySurface.get(sId);
1170
- if (!globalExts && !surfaceExts) {
1171
- const empty = [];
1172
- this.cachedListBySurface.set(sId, empty);
1173
- return empty;
1174
- }
1175
- const merged = new Map([...globalExts || [], ...surfaceExts || []]);
1176
- const result = Array.from(merged.values()).sort(
1177
- (a, b) => a.order - b.order
1178
- );
1179
- this.cachedListBySurface.set(sId, result);
1180
- return result;
1181
- };
1182
- setExtension = (surfaceId, extension) => {
1183
- if (!extension) return;
1184
- const normalized = normalizeSurfaceExtension(extension);
1185
- if (!normalized) return;
1186
- const sId = surfaceId != null ? String(surfaceId) : "global";
1187
- let surfaceMap = this.extensionsBySurface.get(sId);
1188
- if (!surfaceMap) {
1189
- surfaceMap = /* @__PURE__ */ new Map();
1190
- this.extensionsBySurface.set(sId, surfaceMap);
1191
- }
1192
- const prev = surfaceMap.get(normalized.id);
1193
- if (prev && prev.content === normalized.content && prev.align === normalized.align && prev.order === normalized.order && prev.className === normalized.className && prev.unstyled === normalized.unstyled && prev.component === normalized.component && prev.props === normalized.props)
1194
- return;
1195
- surfaceMap.set(normalized.id, normalized);
1196
- this.notify();
1197
- };
1198
- removeExtension = (surfaceId, extensionId) => {
1199
- if (!extensionId) return;
1200
- const sId = surfaceId != null ? String(surfaceId) : "global";
1201
- const surfaceMap = this.extensionsBySurface.get(sId);
1202
- if (!surfaceMap || !surfaceMap.has(extensionId)) return;
1203
- surfaceMap.delete(extensionId);
1204
- if (surfaceMap.size === 0) this.extensionsBySurface.delete(sId);
1205
- this.notify();
1206
- };
1207
- clearSurface = (surfaceId) => {
1208
- const sId = surfaceId != null ? String(surfaceId) : "global";
1209
- if (!this.extensionsBySurface.has(sId)) return;
1210
- this.extensionsBySurface.delete(sId);
1211
- this.notify();
1212
- };
1213
- };
574
+ function isValidationMode(value) {
575
+ return Object.values(REGISTRY_VALIDATION_MODES).includes(value);
576
+ }
577
+ function pickRegistryMetadata(options) {
578
+ if (!isObject(options)) return {};
579
+ return Object.fromEntries(
580
+ REGISTRY_METADATA_KEYS.filter((key) => options[key] !== void 0).map(
581
+ (key) => [key, options[key]]
582
+ )
583
+ );
584
+ }
1214
585
  function useRequiredContext(context, hookName, providerName) {
1215
586
  const value = use(context);
1216
587
  if (value === null) {
@@ -1301,1233 +672,1439 @@ var EventEmitter = class {
1301
672
  report(`Event listener (${String(event)})`, error);
1302
673
  }
1303
674
  }
1304
- }
1305
- emitDebounced(event, payload, waitMs = 100) {
1306
- const existing = this.debounceTimers.get(event);
1307
- if (existing) clearTimeout(existing);
1308
- const timer = setTimeout(() => {
1309
- this.debounceTimers.delete(event);
1310
- this.emit(event, payload);
1311
- }, waitMs);
1312
- this.debounceTimers.set(event, timer);
1313
- }
1314
- cancelDebounced(event) {
1315
- if (event) {
1316
- const timer = this.debounceTimers.get(event);
1317
- if (timer) {
1318
- clearTimeout(timer);
1319
- this.debounceTimers.delete(event);
1320
- }
1321
- } else {
1322
- for (const timer of this.debounceTimers.values()) {
1323
- clearTimeout(timer);
675
+ }
676
+ emitDebounced(event, payload, waitMs = 100) {
677
+ const existing = this.debounceTimers.get(event);
678
+ if (existing) clearTimeout(existing);
679
+ const timer = setTimeout(() => {
680
+ this.debounceTimers.delete(event);
681
+ this.emit(event, payload);
682
+ }, waitMs);
683
+ this.debounceTimers.set(event, timer);
684
+ }
685
+ cancelDebounced(event) {
686
+ if (event) {
687
+ const timer = this.debounceTimers.get(event);
688
+ if (timer) {
689
+ clearTimeout(timer);
690
+ this.debounceTimers.delete(event);
691
+ }
692
+ } else {
693
+ for (const timer of this.debounceTimers.values()) {
694
+ clearTimeout(timer);
695
+ }
696
+ this.debounceTimers.clear();
697
+ }
698
+ }
699
+ unsubscribeAll(event) {
700
+ this.cancelDebounced(event);
701
+ if (event) this.events.delete(event);
702
+ else this.events.clear();
703
+ }
704
+ hasListeners(event) {
705
+ return Boolean(this.events.get(event)?.size);
706
+ }
707
+ getListenerCount(event) {
708
+ return this.events.get(event)?.size || 0;
709
+ }
710
+ getAllEvents() {
711
+ return [...this.events.keys()];
712
+ }
713
+ };
714
+ var GLOBAL_EVENTS_KEY = /* @__PURE__ */ Symbol.for("__base_framework_global_events__");
715
+ var _global = globalThis;
716
+ var globalEvents = _global[GLOBAL_EVENTS_KEY] ?? (_global[GLOBAL_EVENTS_KEY] = new EventEmitter());
717
+
718
+ // src/hooks/use-global-event.ts
719
+ function useGlobalEvent(event, callback, options) {
720
+ const callbackRef = useRef(callback);
721
+ const debounceTimerRef = useRef(null);
722
+ useRef(0);
723
+ const throttleTimerRef = useRef(null);
724
+ useIsomorphicLayoutEffect(() => {
725
+ callbackRef.current = callback;
726
+ });
727
+ const debounceMs = options?.debounceMs;
728
+ const throttleMs = options?.throttleMs;
729
+ useEffect(() => {
730
+ if (!event) return;
731
+ const events = Array.isArray(event) ? event.filter(Boolean) : [event];
732
+ if (events.length === 0) return;
733
+ const handler = (payload) => {
734
+ callbackRef.current?.(payload);
735
+ };
736
+ const unsubs = events.map((ev) => globalEvents.subscribe(ev, handler));
737
+ return () => {
738
+ unsubs.forEach((unsub) => unsub());
739
+ if (debounceTimerRef.current !== null) {
740
+ clearTimeout(debounceTimerRef.current);
741
+ debounceTimerRef.current = null;
742
+ }
743
+ if (throttleTimerRef.current !== null) {
744
+ clearTimeout(throttleTimerRef.current);
745
+ throttleTimerRef.current = null;
746
+ }
747
+ };
748
+ }, [event, debounceMs, throttleMs]);
749
+ }
750
+ var listeners = /* @__PURE__ */ new Set();
751
+ var activeFullscreenStateIds = /* @__PURE__ */ new Set();
752
+ var ACTIVE_FULLSCREEN_ROOT_SELECTOR = '[data-fullscreen-state-root="true"][data-affect-global-state="true"]';
753
+ var domObserver = null;
754
+ var lastSnapshot = false;
755
+ function getDomSnapshot() {
756
+ if (typeof document === "undefined") {
757
+ return activeFullscreenStateIds.size > 0;
758
+ }
759
+ return document.querySelector(ACTIVE_FULLSCREEN_ROOT_SELECTOR) !== null;
760
+ }
761
+ function emitChange() {
762
+ listeners.forEach((listener) => listener());
763
+ }
764
+ function emitIfSnapshotChanged() {
765
+ const nextSnapshot = getDomSnapshot();
766
+ if (nextSnapshot === lastSnapshot) {
767
+ return;
768
+ }
769
+ lastSnapshot = nextSnapshot;
770
+ emitChange();
771
+ }
772
+ function ensureDomObserver() {
773
+ if (typeof document === "undefined" || domObserver || listeners.size === 0) {
774
+ return;
775
+ }
776
+ domObserver = new MutationObserver(() => {
777
+ emitIfSnapshotChanged();
778
+ });
779
+ domObserver.observe(document.documentElement, {
780
+ attributes: true,
781
+ childList: true,
782
+ subtree: true,
783
+ attributeFilter: [
784
+ "data-fullscreen-state",
785
+ "data-fullscreen-state-root",
786
+ "data-affect-global-state"
787
+ ]
788
+ });
789
+ }
790
+ function releaseDomObserver() {
791
+ if (listeners.size > 0 || !domObserver) {
792
+ return;
793
+ }
794
+ domObserver.disconnect();
795
+ domObserver = null;
796
+ }
797
+ function subscribe(listener) {
798
+ listeners.add(listener);
799
+ ensureDomObserver();
800
+ emitIfSnapshotChanged();
801
+ return () => {
802
+ listeners.delete(listener);
803
+ releaseDomObserver();
804
+ };
805
+ }
806
+ function getSnapshot() {
807
+ return getDomSnapshot();
808
+ }
809
+ function useIsFullscreenStateActive() {
810
+ return useSyncExternalStore(subscribe, getSnapshot, () => false);
811
+ }
812
+ var RegistryContext = getOrCreateGlobalContext(
813
+ "RegistryContext",
814
+ null
815
+ );
816
+ function useRegistryActions() {
817
+ return useRequiredContext(
818
+ RegistryContext,
819
+ "useRegistryActions",
820
+ "RegistryProvider"
821
+ ).actions;
822
+ }
823
+ function useRegistrySubscription() {
824
+ return useRequiredContext(
825
+ RegistryContext,
826
+ "useRegistrySubscription",
827
+ "RegistryProvider"
828
+ ).subscription;
829
+ }
830
+ function useRegistryEntries(type) {
831
+ const { getEntriesSnapshot, subscribe: subscribe2 } = useRegistrySubscription();
832
+ const subscribeToType = useCallback(
833
+ (listener) => subscribe2(type, null, listener),
834
+ [subscribe2, type]
835
+ );
836
+ const getEntries = useCallback(
837
+ () => getEntriesSnapshot(type),
838
+ [getEntriesSnapshot, type]
839
+ );
840
+ return useSyncExternalStore(subscribeToType, getEntries, getEntries);
841
+ }
842
+ var EMPTY_MODULES = Object.freeze([]);
843
+ var ModuleHostContext = getOrCreateGlobalContext("ModuleHostContext", null);
844
+ function selectModuleSlice(coreModule, config) {
845
+ const slice = coreModule.page?.select ? coreModule.page.select(config) : config[coreModule.id];
846
+ return slice ?? null;
847
+ }
848
+ function createModulePayload(modules, config, metadata = {}) {
849
+ const payload = {};
850
+ let hasSlices = false;
851
+ for (const coreModule of modules) {
852
+ if (!coreModule.page?.entries) continue;
853
+ const slice = selectModuleSlice(coreModule, config);
854
+ if (slice === null) continue;
855
+ payload[coreModule.id] = slice;
856
+ hasSlices = true;
857
+ }
858
+ if (!hasSlices) return null;
859
+ return {
860
+ ...payload,
861
+ registry: {
862
+ ...metadata,
863
+ ...isObject(config.registry) ? config.registry : {}
864
+ }
865
+ };
866
+ }
867
+ function useInstalledModules() {
868
+ return use(ModuleHostContext)?.modules ?? EMPTY_MODULES;
869
+ }
870
+
871
+ // src/kernel/handlers.ts
872
+ function isPersistentLifecycle(lifecycle) {
873
+ return lifecycle === REGISTRY_LIFECYCLES.PERSISTENT;
874
+ }
875
+ function runRegistrationBatch(context, executor) {
876
+ return typeof context.batch === "function" ? context.batch(executor) : executor(context);
877
+ }
878
+ function createEntryCleanupKey(type, key, source, instanceId, scope) {
879
+ return JSON.stringify([
880
+ "entry",
881
+ type,
882
+ key,
883
+ scope ?? null,
884
+ source,
885
+ instanceId
886
+ ]);
887
+ }
888
+ function getCleanupScope(context) {
889
+ return context?.cleanupScope || /* @__PURE__ */ new Map();
890
+ }
891
+ function clearCleanupTimer(scope, cleanupKey) {
892
+ const lifecycle = scope.get(cleanupKey);
893
+ if (!lifecycle) return;
894
+ lifecycle.cancelled = true;
895
+ if (lifecycle.timerId !== null) clearTimeout(lifecycle.timerId);
896
+ scope.delete(cleanupKey);
897
+ }
898
+ function scheduleCleanup(scope, cleanupKey, callback, delayMs) {
899
+ clearCleanupTimer(scope, cleanupKey);
900
+ const lifecycle = {
901
+ cancelled: false,
902
+ timerId: null
903
+ };
904
+ lifecycle.timerId = setTimeout(() => {
905
+ if (lifecycle.cancelled || scope.get(cleanupKey) !== lifecycle) return;
906
+ scope.delete(cleanupKey);
907
+ callback();
908
+ }, delayMs);
909
+ scope.set(cleanupKey, lifecycle);
910
+ }
911
+ function collectModuleRegistrations(coreModule, slice, pageMetadata, context) {
912
+ const definition = coreModule.registry;
913
+ const toEntries = coreModule.page?.entries;
914
+ if (!definition || !toEntries || slice == null || slice === false) return [];
915
+ const type = coreModule.id;
916
+ const items = Array.isArray(slice) ? slice : [slice];
917
+ const registrations = [];
918
+ for (const item of items) {
919
+ if (item == null || item === false) continue;
920
+ const itemMetadata = isObject(item) && isObject(item.registry) ? item.registry : {};
921
+ const metadata = { ...pageMetadata, ...itemMetadata };
922
+ if (metadata.validation === "strict" && !validateRegistryMetadata(metadata).valid) {
923
+ continue;
924
+ }
925
+ const payload = isObject(item) ? Object.fromEntries(
926
+ Object.entries(item).filter(([key]) => key !== "registry")
927
+ ) : item;
928
+ const { cleanupDelayMs, lifecycle, registerOptions, scope, source } = normalizeRegistryMetadata(metadata, {
929
+ defaultCleanupDelayMs: definition.cleanupDelayMs ?? null,
930
+ defaultLifecycle: definition.lifecycle,
931
+ defaultSource: DEFAULT_SOURCE
932
+ });
933
+ for (const { key, value } of toEntries(payload, context) ?? []) {
934
+ const validation = definition.validate?.(value);
935
+ if (validation && !validation.valid) {
936
+ report(`Registry ${type} config is invalid`, validation.issues, "warn");
937
+ continue;
1324
938
  }
1325
- this.debounceTimers.clear();
939
+ registrations.push({
940
+ cleanupDelayMs,
941
+ cleanupKey: createEntryCleanupKey(
942
+ type,
943
+ key,
944
+ source,
945
+ context.instanceId,
946
+ scope
947
+ ),
948
+ key,
949
+ lifecycle,
950
+ registerOptions,
951
+ scope,
952
+ source,
953
+ type,
954
+ value
955
+ });
1326
956
  }
1327
957
  }
1328
- unsubscribeAll(event) {
1329
- this.cancelDebounced(event);
1330
- if (event) this.events.delete(event);
1331
- else this.events.clear();
1332
- }
1333
- hasListeners(event) {
1334
- return Boolean(this.events.get(event)?.size);
1335
- }
1336
- getListenerCount(event) {
1337
- return this.events.get(event)?.size || 0;
1338
- }
1339
- getAllEvents() {
1340
- return [...this.events.keys()];
1341
- }
958
+ return registrations;
959
+ }
960
+ var NO_CLEANUP = () => {
1342
961
  };
1343
- var GLOBAL_EVENTS_KEY = /* @__PURE__ */ Symbol.for("__base_framework_global_events__");
1344
- var _global = globalThis;
1345
- var globalEvents = _global[GLOBAL_EVENTS_KEY] ?? (_global[GLOBAL_EVENTS_KEY] = new EventEmitter());
1346
-
1347
- // src/hooks/use-global-event.ts
1348
- function useGlobalEvent(event, callback, options) {
1349
- const callbackRef = useRef(callback);
1350
- const debounceTimerRef = useRef(null);
1351
- useRef(0);
1352
- const throttleTimerRef = useRef(null);
1353
- useIsomorphicLayoutEffect(() => {
1354
- callbackRef.current = callback;
962
+ function applyRegistryConfig(config, context) {
963
+ const modules = context.modules ?? [];
964
+ if (!config || modules.length === 0) return NO_CLEANUP;
965
+ const pageMetadata = isObject(config.registry) ? config.registry : {};
966
+ const slices = config;
967
+ const registrations = modules.flatMap((coreModule) => {
968
+ try {
969
+ return collectModuleRegistrations(
970
+ coreModule,
971
+ slices[coreModule.id],
972
+ pageMetadata,
973
+ context
974
+ );
975
+ } catch (error) {
976
+ report(`Registry apply (${coreModule.id})`, error);
977
+ return [];
978
+ }
1355
979
  });
1356
- const debounceMs = options?.debounceMs;
1357
- const throttleMs = options?.throttleMs;
1358
- useEffect(() => {
1359
- if (!event) return;
1360
- const events = Array.isArray(event) ? event.filter(Boolean) : [event];
1361
- if (events.length === 0) return;
1362
- const handler = (payload) => {
1363
- callbackRef.current?.(payload);
1364
- };
1365
- const unsubs = events.map((ev) => globalEvents.subscribe(ev, handler));
1366
- return () => {
1367
- unsubs.forEach((unsub) => unsub());
1368
- if (debounceTimerRef.current !== null) {
1369
- clearTimeout(debounceTimerRef.current);
1370
- debounceTimerRef.current = null;
980
+ if (registrations.length === 0) return NO_CLEANUP;
981
+ const cleanupScope = getCleanupScope(context);
982
+ const owner = (registration) => ({
983
+ instanceId: context.instanceId,
984
+ scope: registration.scope,
985
+ source: registration.source
986
+ });
987
+ runRegistrationBatch(context, (queue) => {
988
+ registrations.forEach(
989
+ ({ cleanupKey, key, registerOptions, source, type, value }) => {
990
+ clearCleanupTimer(cleanupScope, cleanupKey);
991
+ queue.register(type, key, value, source, registerOptions);
1371
992
  }
1372
- if (throttleTimerRef.current !== null) {
1373
- clearTimeout(throttleTimerRef.current);
1374
- throttleTimerRef.current = null;
993
+ );
994
+ });
995
+ return () => {
996
+ const immediate = [];
997
+ registrations.forEach((registration) => {
998
+ if (isPersistentLifecycle(registration.lifecycle)) return;
999
+ const { cleanupDelayMs, cleanupKey, key, type } = registration;
1000
+ if (typeof cleanupDelayMs === "number" && cleanupDelayMs > 0) {
1001
+ scheduleCleanup(
1002
+ cleanupScope,
1003
+ cleanupKey,
1004
+ () => context.unregister(type, key, owner(registration)),
1005
+ cleanupDelayMs
1006
+ );
1007
+ } else {
1008
+ immediate.push(registration);
1375
1009
  }
1376
- };
1377
- }, [event, debounceMs, throttleMs]);
1010
+ });
1011
+ if (immediate.length === 0) return;
1012
+ runRegistrationBatch(context, (queue) => {
1013
+ immediate.forEach(
1014
+ (registration) => queue.unregister(
1015
+ registration.type,
1016
+ registration.key,
1017
+ owner(registration)
1018
+ )
1019
+ );
1020
+ });
1021
+ };
1378
1022
  }
1379
1023
 
1380
- // src/kernel/constants.ts
1381
- var DEFAULT_SOURCE = "dynamic";
1382
- var REGISTRY_SOURCES = Object.freeze({
1383
- STATIC: "static",
1384
- DYNAMIC: "dynamic",
1385
- USER: "user"
1386
- });
1387
- var REGISTRY_LIFECYCLES = Object.freeze({
1388
- IMMEDIATE: "immediate",
1389
- GRACEFUL: "graceful",
1390
- PERSISTENT: "persistent",
1391
- ROUTE: "route"
1392
- });
1393
- var REGISTRY_VALIDATION_MODES = Object.freeze({
1394
- WARN: "warn",
1395
- STRICT: "strict"
1396
- });
1397
- var REGISTRY_METADATA_KEYS = Object.freeze([
1398
- "cleanup",
1399
- "cleanupDelayMs",
1400
- "instanceId",
1401
- "lifecycle",
1402
- "priority",
1403
- "source",
1404
- "scope",
1405
- "validation"
1406
- ]);
1407
- Object.freeze({
1408
- [REGISTRY_SOURCES.STATIC]: 100,
1409
- [REGISTRY_SOURCES.DYNAMIC]: 200,
1410
- [REGISTRY_SOURCES.USER]: 300
1411
- });
1412
- Object.freeze({
1413
- [REGISTRY_SOURCES.STATIC]: 10,
1414
- [REGISTRY_SOURCES.DYNAMIC]: 20,
1415
- [REGISTRY_SOURCES.USER]: 30
1416
- });
1417
- var REGISTRY_LIFECYCLE_VALUES = new Set(
1418
- Object.values(REGISTRY_LIFECYCLES)
1419
- );
1420
- var REGISTRY_METADATA_KEY_SET = new Set(
1421
- REGISTRY_METADATA_KEYS
1422
- );
1423
- var REGISTRY_SCOPE_KINDS = Object.freeze({
1424
- APP: "app",
1425
- SESSION: "session",
1426
- ROUTE: "route",
1427
- INSTANCE: "instance",
1428
- WORKSPACE: "workspace"
1429
- });
1430
- REGISTRY_SCOPE_KINDS.APP;
1431
-
1432
- // src/kernel/utils.ts
1433
- function parseFiniteNumber(value) {
1434
- const parsed = Number(value);
1435
- return Number.isFinite(parsed) ? parsed : null;
1024
+ // src/kernel/hooks.ts
1025
+ function withInstanceId(instanceId, sourceOrOptions, optionsArg) {
1026
+ if (typeof sourceOrOptions === "string") {
1027
+ return {
1028
+ optionsArg: { ...isObject(optionsArg) ? optionsArg : {}, instanceId },
1029
+ sourceOrOptions
1030
+ };
1031
+ }
1032
+ if (isObject(sourceOrOptions)) {
1033
+ return {
1034
+ optionsArg,
1035
+ sourceOrOptions: { ...sourceOrOptions, instanceId }
1036
+ };
1037
+ }
1038
+ return {
1039
+ optionsArg: void 0,
1040
+ sourceOrOptions: {
1041
+ ...isObject(optionsArg) ? optionsArg : {},
1042
+ instanceId
1043
+ }
1044
+ };
1436
1045
  }
1437
- function getValidString(value, fallback = null) {
1438
- return trimToNull(value) ?? fallback;
1046
+ function withInstanceIdForUnregister(instanceId, sourceOrOptions) {
1047
+ if (typeof sourceOrOptions === "string") {
1048
+ return { instanceId, source: sourceOrOptions };
1049
+ }
1050
+ if (isObject(sourceOrOptions)) {
1051
+ return { ...sourceOrOptions, instanceId };
1052
+ }
1053
+ return { instanceId };
1439
1054
  }
1440
-
1441
- // src/kernel/schema.ts
1442
- function resolveLifecycle(metadata, fallback) {
1443
- const lifecycle = metadata?.lifecycle ?? metadata?.cleanup;
1444
- return lifecycle && REGISTRY_LIFECYCLE_VALUES.has(lifecycle) ? lifecycle : fallback;
1055
+ function resolveRegisterArgsWithInstance(instanceId, sourceOrOptions, optionsArg) {
1056
+ const input = withInstanceId(instanceId, sourceOrOptions, optionsArg);
1057
+ return [input.sourceOrOptions, input.optionsArg];
1445
1058
  }
1446
- function validateRegistryMetadata(metadata) {
1447
- if (metadata === void 0 || metadata === null)
1448
- return { issues: [], valid: true };
1449
- if (!isObject(metadata))
1450
- return { issues: ["registry metadata must be an object"], valid: false };
1451
- const issues = [];
1452
- Object.keys(metadata).forEach((key) => {
1453
- if (!REGISTRY_METADATA_KEY_SET.has(key))
1454
- issues.push(`unknown registry metadata field: ${key}`);
1455
- });
1456
- if (metadata.source !== void 0 && !getValidString(metadata.source))
1457
- issues.push("source must be a non-empty string");
1458
- if (metadata.instanceId !== void 0 && !getValidString(metadata.instanceId))
1459
- issues.push("instanceId must be a non-empty string");
1460
- if (metadata.scope !== void 0 && !getValidString(metadata.scope))
1461
- issues.push("scope must be a non-empty string");
1462
- if (metadata.priority !== void 0 && parseFiniteNumber(metadata.priority) === null)
1463
- issues.push("priority must be a finite number");
1464
- if (metadata.cleanupDelayMs !== void 0) {
1465
- const delay = parseFiniteNumber(metadata.cleanupDelayMs);
1466
- if (delay === null || delay < 0)
1467
- issues.push("cleanupDelayMs must be a non-negative finite number");
1059
+ var MAX_STABILIZATION_DEPTH = 64;
1060
+ function createStableFunctionEntry(fn) {
1061
+ const entry = {
1062
+ current: fn,
1063
+ stable(...args) {
1064
+ return entry.current?.apply(this, args);
1065
+ }
1066
+ };
1067
+ return entry;
1068
+ }
1069
+ function isDirectRegistryComponentPath(path, componentSlices) {
1070
+ const match = /^config\.([^.[]+)\.[^.[]+$/.exec(path);
1071
+ return Boolean(match && componentSlices.has(match[1]));
1072
+ }
1073
+ function isReactNodeLike(value) {
1074
+ return value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" || typeof value === "number" || isValidElement(value);
1075
+ }
1076
+ function isStabilizableObject(value) {
1077
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isValidElement(value);
1078
+ }
1079
+ function hasSameObjectKeys(previousValue, nextValue) {
1080
+ if (!isStabilizableObject(previousValue) || !isStabilizableObject(nextValue)) {
1081
+ return false;
1468
1082
  }
1469
- const lifecycle = metadata.lifecycle ?? metadata.cleanup;
1470
- if (lifecycle !== void 0 && !REGISTRY_LIFECYCLE_VALUES.has(String(lifecycle))) {
1471
- issues.push(
1472
- `lifecycle must be one of: ${[...REGISTRY_LIFECYCLE_VALUES].join(", ")}`
1473
- );
1083
+ const previousKeys = Object.keys(previousValue);
1084
+ const nextKeys = Object.keys(nextValue);
1085
+ if (previousKeys.length !== nextKeys.length) return false;
1086
+ return nextKeys.every(
1087
+ (key) => Object.prototype.hasOwnProperty.call(previousValue, key)
1088
+ );
1089
+ }
1090
+ function stabilizeRegistryValue(value, path, scope, previousValue, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
1091
+ if (depth > MAX_STABILIZATION_DEPTH) return value;
1092
+ if (typeof value === "function") {
1093
+ const isComponent = value.name && /^[A-Z]/.test(value.name);
1094
+ if (isComponent || isDirectRegistryComponentPath(path, scope.componentSlices)) {
1095
+ return value;
1096
+ }
1097
+ scope.usedPaths.add(path);
1098
+ let entry = scope.functionEntries.get(path);
1099
+ if (!entry) {
1100
+ entry = createStableFunctionEntry(value);
1101
+ scope.functionEntries.set(path, entry);
1102
+ } else {
1103
+ entry.current = value;
1104
+ }
1105
+ return entry.stable;
1474
1106
  }
1475
- if (metadata.validation !== void 0 && !Object.values(REGISTRY_VALIDATION_MODES).includes(
1476
- metadata.validation
1477
- )) {
1478
- issues.push(
1479
- `validation must be one of: ${Object.values(REGISTRY_VALIDATION_MODES).join(", ")}`
1107
+ if (value && typeof value === "object") {
1108
+ if (seen.has(value)) return value;
1109
+ seen.add(value);
1110
+ }
1111
+ if (isValidElement(value)) {
1112
+ const nextProps = stabilizeRegistryValue(
1113
+ value.props,
1114
+ `${path}.props`,
1115
+ scope,
1116
+ isValidElement(previousValue) ? previousValue.props : void 0,
1117
+ seen,
1118
+ depth + 1
1480
1119
  );
1120
+ if (isValidElement(previousValue) && previousValue.type === value.type && previousValue.key === value.key && previousValue.props === nextProps) {
1121
+ return previousValue;
1122
+ }
1123
+ return cloneElement(value, nextProps);
1481
1124
  }
1482
- return { issues, valid: issues.length === 0 };
1483
- }
1484
- function normalizeRegistryMetadata(metadata, {
1485
- defaultCleanupDelayMs = null,
1486
- defaultLifecycle = null,
1487
- defaultSource = DEFAULT_SOURCE
1488
- } = {}) {
1489
- const registryMeta = isObject(metadata) ? metadata : {};
1490
- const normalizedDefaultDelay = parseFiniteNumber(defaultCleanupDelayMs);
1491
- const fallbackCleanupDelayMs = normalizedDefaultDelay !== null && normalizedDefaultDelay >= 0 ? normalizedDefaultDelay : null;
1492
- const fallbackLifecycle = defaultLifecycle && REGISTRY_LIFECYCLE_VALUES.has(defaultLifecycle) ? defaultLifecycle : null;
1493
- const source = getValidString(registryMeta.source) || getValidString(defaultSource, DEFAULT_SOURCE) || DEFAULT_SOURCE;
1494
- const scope = getValidString(registryMeta.scope);
1495
- const priority = parseFiniteNumber(registryMeta.priority);
1496
- const lifecycle = resolveLifecycle(registryMeta, fallbackLifecycle);
1497
- const validation = isValidationMode(registryMeta.validation) ? registryMeta.validation : null;
1498
- const cleanupDelayCandidate = parseFiniteNumber(registryMeta.cleanupDelayMs);
1499
- let cleanupDelayMs = cleanupDelayCandidate !== null && cleanupDelayCandidate >= 0 ? cleanupDelayCandidate : fallbackCleanupDelayMs;
1500
- if (lifecycle === REGISTRY_LIFECYCLES.IMMEDIATE) {
1501
- cleanupDelayMs = 0;
1502
- } else if ((lifecycle === REGISTRY_LIFECYCLES.GRACEFUL || lifecycle === REGISTRY_LIFECYCLES.ROUTE) && cleanupDelayMs === null) {
1503
- cleanupDelayMs = fallbackCleanupDelayMs;
1125
+ if (Array.isArray(value)) {
1126
+ const nextValue = value.every(isReactNodeLike) ? Children.toArray(value) : value;
1127
+ const previousArray = Array.isArray(previousValue) ? previousValue : null;
1128
+ let hasChanged2 = !previousArray || previousArray.length !== nextValue.length;
1129
+ const stabilizedValue2 = nextValue.map((item, index) => {
1130
+ const nextItem = stabilizeRegistryValue(
1131
+ item,
1132
+ `${path}[${index}]`,
1133
+ scope,
1134
+ previousArray?.[index],
1135
+ seen,
1136
+ depth + 1
1137
+ );
1138
+ if (!previousArray || nextItem !== previousArray[index]) {
1139
+ hasChanged2 = true;
1140
+ }
1141
+ return nextItem;
1142
+ });
1143
+ return !hasChanged2 ? previousArray : stabilizedValue2;
1504
1144
  }
1505
- return {
1506
- cleanupDelayMs,
1507
- lifecycle,
1508
- priority,
1509
- registerOptions: {
1510
- ...priority !== null && { priority },
1511
- ...validation === REGISTRY_VALIDATION_MODES.STRICT && { validation },
1512
- ...scope && { scope }
1513
- },
1514
- source,
1515
- ...scope && { scope },
1516
- ...validation && { validation }
1517
- };
1145
+ if (typeof value !== "object" || value === null) {
1146
+ return value;
1147
+ }
1148
+ const source = value;
1149
+ const previousRecord = previousValue;
1150
+ const stabilizedValue = {};
1151
+ const canReusePrevious = hasSameObjectKeys(previousValue, value);
1152
+ let hasChanged = !canReusePrevious;
1153
+ Object.keys(source).forEach((key) => {
1154
+ const nextValue = stabilizeRegistryValue(
1155
+ source[key],
1156
+ `${path}.${key}`,
1157
+ scope,
1158
+ previousRecord?.[key],
1159
+ seen,
1160
+ depth + 1
1161
+ );
1162
+ stabilizedValue[key] = nextValue;
1163
+ if (!canReusePrevious || nextValue !== previousRecord?.[key]) {
1164
+ hasChanged = true;
1165
+ }
1166
+ });
1167
+ return !hasChanged ? previousValue : stabilizedValue;
1518
1168
  }
1519
- function isValidationMode(value) {
1520
- return Object.values(REGISTRY_VALIDATION_MODES).includes(value);
1169
+ function useAppliedRegistryConfig(payload, context, componentSlices) {
1170
+ const functionEntriesRef = useRef(/* @__PURE__ */ new Map());
1171
+ const appliedRef = useRef(null);
1172
+ useIsomorphicLayoutEffect(() => {
1173
+ const previous = appliedRef.current;
1174
+ if (previous && previous.payload === payload && previous.context === context) {
1175
+ return;
1176
+ }
1177
+ const functionEntries = functionEntriesRef.current;
1178
+ const usedPaths = /* @__PURE__ */ new Set();
1179
+ const config = stabilizeRegistryValue(
1180
+ payload,
1181
+ "config",
1182
+ { componentSlices, functionEntries, usedPaths },
1183
+ previous?.config
1184
+ );
1185
+ functionEntries.forEach((_entry, path) => {
1186
+ if (!usedPaths.has(path)) functionEntries.delete(path);
1187
+ });
1188
+ if (previous && previous.config === config && previous.context === context) {
1189
+ previous.payload = payload;
1190
+ return;
1191
+ }
1192
+ previous?.cleanup();
1193
+ appliedRef.current = {
1194
+ cleanup: applyRegistryConfig(config, context),
1195
+ config,
1196
+ context,
1197
+ payload
1198
+ };
1199
+ });
1200
+ useIsomorphicLayoutEffect(
1201
+ () => () => {
1202
+ appliedRef.current?.cleanup();
1203
+ appliedRef.current = null;
1204
+ },
1205
+ []
1206
+ );
1521
1207
  }
1522
- function pickRegistryMetadata(options) {
1523
- if (!isObject(options)) return {};
1524
- return Object.fromEntries(
1525
- REGISTRY_METADATA_KEYS.filter((key) => options[key] !== void 0).map(
1526
- (key) => [key, options[key]]
1208
+ function createRegistryApplyContext(actions, {
1209
+ cleanupScope = /* @__PURE__ */ new Map(),
1210
+ instanceId,
1211
+ modules = [],
1212
+ pathname
1213
+ }) {
1214
+ const stamp = (queue) => ({
1215
+ register: (type, key, item, sourceOrOptions, optionsArg) => queue.register(
1216
+ type,
1217
+ key,
1218
+ item,
1219
+ ...resolveRegisterArgsWithInstance(
1220
+ instanceId,
1221
+ sourceOrOptions,
1222
+ optionsArg
1223
+ )
1224
+ ),
1225
+ unregister: (type, key, sourceOrOptions) => queue.unregister(
1226
+ type,
1227
+ key,
1228
+ withInstanceIdForUnregister(instanceId, sourceOrOptions)
1527
1229
  )
1528
- );
1529
- }
1530
- function createNoopHandle(status = "rejected", reason = "unknown") {
1531
- const handle = Object.assign(() => false, {
1532
- active: false,
1533
- dispose: () => false,
1534
- instanceId: null,
1535
- key: "",
1536
- priority: 0,
1537
- reason,
1538
- source: "",
1539
- status,
1540
- type: "",
1541
- update: () => handle,
1542
- updatedAt: 0,
1543
- validation: "warn"
1544
1230
  });
1545
- return handle;
1231
+ return {
1232
+ ...stamp(actions),
1233
+ batch: (executor) => typeof executor === "function" ? actions.batch((queue) => executor(stamp(queue))) : 0,
1234
+ cleanupScope,
1235
+ instanceId,
1236
+ modules,
1237
+ pathname
1238
+ };
1546
1239
  }
1547
- createNoopHandle();
1548
- var RegistryContext = getOrCreateGlobalContext(
1549
- "RegistryContext",
1550
- null
1551
- );
1552
- function useRegistryActions() {
1553
- return useRequiredContext(
1554
- RegistryContext,
1555
- "useRegistryActions",
1556
- "RegistryProvider"
1557
- ).actions;
1240
+ function useRegistry(config, metadata) {
1241
+ const { batch, register, unregister } = useRegistryActions();
1242
+ const pathname = usePathname();
1243
+ const modules = useInstalledModules();
1244
+ const instanceId = `registry-instance-${useId()}`;
1245
+ const [cleanupScope] = useState(
1246
+ () => /* @__PURE__ */ new Map()
1247
+ );
1248
+ const context = useMemo(
1249
+ () => createRegistryApplyContext(
1250
+ { batch, register, unregister },
1251
+ {
1252
+ cleanupScope,
1253
+ instanceId,
1254
+ modules,
1255
+ pathname
1256
+ }
1257
+ ),
1258
+ [batch, cleanupScope, instanceId, modules, pathname, register, unregister]
1259
+ );
1260
+ const payload = useMemo(
1261
+ () => config ? createModulePayload(modules, config, metadata) : null,
1262
+ [config, metadata, modules]
1263
+ );
1264
+ const componentSlices = useMemo(
1265
+ () => new Set(
1266
+ modules.filter(
1267
+ (coreModule) => coreModule.registry?.valueKind === "component"
1268
+ ).map((coreModule) => coreModule.id)
1269
+ ),
1270
+ [modules]
1271
+ );
1272
+ useAppliedRegistryConfig(payload, context, componentSlices);
1558
1273
  }
1559
- function useRegistrySubscription() {
1560
- return useRequiredContext(
1561
- RegistryContext,
1562
- "useRegistrySubscription",
1563
- "RegistryProvider"
1564
- ).subscription;
1274
+ function defineModule(coreModule) {
1275
+ return Object.freeze({ ...coreModule });
1565
1276
  }
1566
- function useRegistryEntries(type) {
1567
- const { getEntriesSnapshot, subscribe: subscribe2 } = useRegistrySubscription();
1568
- const subscribeToType = useCallback(
1569
- (listener) => subscribe2(type, null, listener),
1570
- [subscribe2, type]
1571
- );
1572
- const getEntries = useCallback(
1573
- () => getEntriesSnapshot(type),
1574
- [getEntriesSnapshot, type]
1277
+ createStore(void 0);
1278
+
1279
+ // src/kernel/adapters.ts
1280
+ function useModuleRegistration(id, value, options) {
1281
+ const coreModule = useInstalledModules().find(
1282
+ (candidate) => candidate.id === id
1575
1283
  );
1576
- return useSyncExternalStore(subscribeToType, getEntries, getEntries);
1284
+ const config = coreModule && options?.enabled !== false && value != null ? { [id]: value } : null;
1285
+ useRegistry(config, pickRegistryMetadata(options));
1577
1286
  }
1578
- var EMPTY_MODULES = Object.freeze([]);
1579
- var ModuleHostContext = getOrCreateGlobalContext("ModuleHostContext", null);
1580
- function selectModuleSlice(coreModule, config) {
1581
- const slice = coreModule.page?.select ? coreModule.page.select(config) : config[coreModule.id];
1582
- return slice ?? null;
1287
+
1288
+ // src/theme.tsx
1289
+ function defineThemeSpec(id) {
1290
+ return Object.freeze({ id });
1583
1291
  }
1584
- function createModulePayload(modules, config, metadata = {}) {
1585
- const payload = {};
1586
- let hasSlices = false;
1587
- for (const coreModule of modules) {
1588
- if (!coreModule.page?.entries) continue;
1589
- const slice = selectModuleSlice(coreModule, config);
1590
- if (slice === null) continue;
1591
- payload[coreModule.id] = slice;
1592
- hasSlices = true;
1292
+ var EMPTY = /* @__PURE__ */ new Map();
1293
+ var ThemeContext = getOrCreateGlobalContext("ThemeContext", EMPTY);
1294
+ function useTheme(spec) {
1295
+ const config = useContext(ThemeContext).get(spec.id);
1296
+ if (!config) {
1297
+ throw new Error(
1298
+ `Theme "${spec.id}" is missing. Ensure its theme configuration is provided to <ThemeProvider themes={[...]}> (e.g. in src/config/ or your theme setup).`
1299
+ );
1593
1300
  }
1594
- if (!hasSlices) return null;
1595
1301
  return {
1596
- ...payload,
1597
- registry: {
1598
- ...metadata,
1599
- ...isObject(config.registry) ? config.registry : {}
1600
- }
1302
+ slots: config.slots,
1303
+ styles: config.styles ?? {}
1601
1304
  };
1602
1305
  }
1603
- function useInstalledModules() {
1604
- return use(ModuleHostContext)?.modules ?? EMPTY_MODULES;
1605
- }
1606
1306
 
1607
- // src/kernel/handlers.ts
1608
- function isPersistentLifecycle(lifecycle) {
1609
- return lifecycle === REGISTRY_LIFECYCLES.PERSISTENT;
1610
- }
1611
- function runRegistrationBatch(context, executor) {
1612
- return typeof context.batch === "function" ? context.batch(executor) : executor(context);
1613
- }
1614
- function createEntryCleanupKey(type, key, source, instanceId, scope) {
1615
- return JSON.stringify([
1616
- "entry",
1617
- type,
1618
- key,
1619
- scope ?? null,
1620
- source,
1621
- instanceId
1622
- ]);
1623
- }
1624
- function getCleanupScope(context) {
1625
- return context?.cleanupScope || /* @__PURE__ */ new Map();
1626
- }
1627
- function clearCleanupTimer(scope, cleanupKey) {
1628
- const lifecycle = scope.get(cleanupKey);
1629
- if (!lifecycle) return;
1630
- lifecycle.cancelled = true;
1631
- if (lifecycle.timerId !== null) clearTimeout(lifecycle.timerId);
1632
- scope.delete(cleanupKey);
1307
+ // src/modules/dock/constants.ts
1308
+ var DOCK_STYLE_SECTIONS = Object.freeze([
1309
+ "card",
1310
+ "icon",
1311
+ "title",
1312
+ "description"
1313
+ ]);
1314
+ var DOCK_EVENTS = Object.freeze({
1315
+ NAVIGATE_START: "DOCK_NAVIGATE_START",
1316
+ NAVIGATE_END: "DOCK_NAVIGATE_END",
1317
+ UPDATE_BADGE: "DOCK_UPDATE_BADGE",
1318
+ NAVIGATE: "DOCK_NAVIGATE",
1319
+ GUARD: "DOCK_GUARD",
1320
+ NOT_FOUND: "DOCK_NOT_FOUND",
1321
+ STATUS_SET: "DOCK_STATUS_SET",
1322
+ STATUS_CLEAR: "DOCK_STATUS_CLEAR"
1323
+ });
1324
+ var DOCK_HUD_RENDER_MODE = Object.freeze({
1325
+ COMPONENT: "component",
1326
+ NODE: "node"
1327
+ });
1328
+ var DOCK_HUD_VARIANT = Object.freeze({
1329
+ EXPANDED: "expanded",
1330
+ PROGRESS: "progress",
1331
+ CUSTOM: "custom"
1332
+ });
1333
+ var DOCK_HUD_PRIORITY = Object.freeze({
1334
+ TASK_PROGRESS: 30,
1335
+ CONTEXTUAL: 10,
1336
+ SELECTION: 20,
1337
+ CRITICAL: 50,
1338
+ DEFAULT: 0,
1339
+ MEDIA: 15
1340
+ });
1341
+ var DOCK_ATTENTION_KIND = Object.freeze({
1342
+ SURFACE: "surface",
1343
+ OPERATION: "operation",
1344
+ LOADING: "loading",
1345
+ STATUS: "status",
1346
+ ROUTE: "route",
1347
+ HUD: "hud"
1348
+ });
1349
+ var DOCK_ATTENTION_PRIORITY = Object.freeze({
1350
+ STATUS_OVERLAY: 300,
1351
+ SURFACE: 400,
1352
+ OPERATION: 250,
1353
+ LOADING: 100,
1354
+ STATUS: 75,
1355
+ ROUTE: 0,
1356
+ HUD: 200
1357
+ });
1358
+ var DOCK_ATTENTION_PRIORITY_OFFSET_MAX = 99;
1359
+ var DOCK_SURFACE_RENDER_MODE = Object.freeze({
1360
+ COMPONENT: "component",
1361
+ NODE: "node"
1362
+ });
1363
+ var DOCK_SURFACE_FLOW_STATUS = Object.freeze({
1364
+ OPEN: "open",
1365
+ COMPLETED: "completed",
1366
+ CANCELLED: "cancelled"
1367
+ });
1368
+ var DOCK_SURFACE_PHASE = Object.freeze({
1369
+ IDLE: "idle",
1370
+ DISMISSING_ACTION: "dismissing_action",
1371
+ EXPANDING_BODY: "expanding_body",
1372
+ OPEN: "open",
1373
+ CLOSING_ANTICIPATION: "closing_anticipation",
1374
+ COLLAPSING_BODY: "collapsing_body",
1375
+ RESTORING_HEADER: "restoring_header"
1376
+ });
1377
+ var VIEWPORT_MARGIN = 24;
1378
+ var DOCK_CARD_DIMENSIONS = Object.freeze({
1379
+ expandedY: -72,
1380
+ chromeHeight: 20,
1381
+ collapsedY: -10,
1382
+ extensionShelfY: -42,
1383
+ extensionShelfScale: 0.94,
1384
+ extensionShelfHeight: 45,
1385
+ hudHeight: 52,
1386
+ actionGap: 10,
1387
+ height: 68
1388
+ });
1389
+ var DOCK_CARD_LAYOUT = Object.freeze({
1390
+ collapsed: Object.freeze({
1391
+ offsetY: DOCK_CARD_DIMENSIONS.collapsedY,
1392
+ scale: 0.88
1393
+ }),
1394
+ expanded: Object.freeze({
1395
+ offsetY: DOCK_CARD_DIMENSIONS.expandedY,
1396
+ scale: 1
1397
+ }),
1398
+ extensionShelfY: DOCK_CARD_DIMENSIONS.extensionShelfY,
1399
+ extensionShelfScale: DOCK_CARD_DIMENSIONS.extensionShelfScale,
1400
+ extensionShelfHeight: DOCK_CARD_DIMENSIONS.extensionShelfHeight,
1401
+ chromeHeight: DOCK_CARD_DIMENSIONS.chromeHeight,
1402
+ hudHeight: DOCK_CARD_DIMENSIONS.hudHeight,
1403
+ actionGap: DOCK_CARD_DIMENSIONS.actionGap,
1404
+ baseHeight: DOCK_CARD_DIMENSIONS.height
1405
+ });
1406
+ var DOCK_TRANSACTION_EVENTS = Object.freeze({
1407
+ START: "START",
1408
+ COMPLETE: "COMPLETE",
1409
+ CANCEL: "CANCEL",
1410
+ FAIL: "FAIL",
1411
+ TIME_OUT: "TIME_OUT"
1412
+ });
1413
+ var DOCK_TRANSACTION_STATUS = Object.freeze({
1414
+ PENDING: "pending",
1415
+ COMPLETED: "completed",
1416
+ CANCELLED: "cancelled",
1417
+ FAILED: "failed",
1418
+ TIMED_OUT: "timed-out"
1419
+ });
1420
+ var DOCK_TRANSACTION_REASON = Object.freeze({
1421
+ GUARD: "guard",
1422
+ SUPERSEDED: "superseded",
1423
+ TIME_OUT: "time-out"
1424
+ });
1425
+ var DOCK_TRANSACTION_TIMEOUT_MS = 15e3;
1426
+ var DOCK_PREFETCH_INTENT_DELAY_MS = 90;
1427
+ var DOCK_CONTINUITY_EVENTS = Object.freeze({
1428
+ CLEAR: "CLEAR",
1429
+ CONSUME_RETURN: "CONSUME_RETURN",
1430
+ DELIVER_RETURN: "DELIVER_RETURN",
1431
+ RECORD: "RECORD",
1432
+ REMOVE: "REMOVE"
1433
+ });
1434
+ var DOCK_CONTINUITY_MAX_ENTRIES = 32;
1435
+ var DOCK_SURFACE_RETURN_MAX_ENTRIES = 16;
1436
+ var DOCK_OPERATION_EVENTS = Object.freeze({
1437
+ CANCEL: "CANCEL",
1438
+ CLEAR: "CLEAR",
1439
+ COMPLETE: "COMPLETE",
1440
+ START: "START",
1441
+ UPDATE: "UPDATE"
1442
+ });
1443
+ var DOCK_OPERATION_STATUS = Object.freeze({
1444
+ CANCELLED: "cancelled",
1445
+ COMPLETED: "completed",
1446
+ PENDING: "pending"
1447
+ });
1448
+ var DOCK_OPERATION_MAX_ENTRIES = 24;
1449
+ var DOCK_FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
1450
+ var DOCK_FOCUS_RESTORE_BLOCKED_REASONS = Object.freeze([
1451
+ "browser-back",
1452
+ "dock",
1453
+ "unmount"
1454
+ ]);
1455
+ var DOCK_LIFECYCLE = Object.freeze({
1456
+ CLOSING: "closing",
1457
+ OPENING: "opening",
1458
+ IDLE: "idle",
1459
+ OPEN: "open"
1460
+ });
1461
+ var PLAYBACK_RATES = Object.freeze([1, 1.25, 1.5, 2]);
1462
+ var STATUS_PRIORITY = Object.freeze({
1463
+ GUARD: 120,
1464
+ ACCOUNT_DELETE: 115,
1465
+ APP_ERROR: 100,
1466
+ API_ERROR: 95,
1467
+ NOT_FOUND: 97,
1468
+ OFFLINE: 90,
1469
+ LOGOUT: 110,
1470
+ SIGNUP: 110,
1471
+ LOGIN: 110,
1472
+ ONLINE: 10
1473
+ });
1474
+ var ERROR_STATUS_TYPES = /* @__PURE__ */ new Set([
1475
+ "GUARD",
1476
+ "ACCOUNT_DELETE",
1477
+ "APP_ERROR",
1478
+ "API_ERROR",
1479
+ "NOT_FOUND"
1480
+ ]);
1481
+ var OVERLAY_STATUS_STORAGE_KEY = "dock_overlay_status";
1482
+ var OVERLAY_STATUS_CLEAR_DURATION = 3e3;
1483
+ var STATUS_CLEAR_DURATION = 4500;
1484
+ var API_ERROR_BATCH_DELAY = 300;
1485
+ var DOCK_HEIGHT_BUFFER = 16;
1486
+ var HEIGHT_EPSILON = 0.5;
1487
+ var MAX_VISIBLE_STACKED_CARDS = 3;
1488
+ var dockTheme = defineThemeSpec("dock");
1489
+
1490
+ // src/modules/dock/utils.ts
1491
+ function isSamePath(left, right) {
1492
+ const normalizedLeft = normalizePath(left);
1493
+ const normalizedRight = normalizePath(right);
1494
+ return Boolean(
1495
+ normalizedLeft && normalizedRight && normalizedLeft === normalizedRight
1496
+ );
1633
1497
  }
1634
- function scheduleCleanup(scope, cleanupKey, callback, delayMs) {
1635
- clearCleanupTimer(scope, cleanupKey);
1636
- const lifecycle = {
1637
- cancelled: false,
1638
- timerId: null
1639
- };
1640
- lifecycle.timerId = setTimeout(() => {
1641
- if (lifecycle.cancelled || scope.get(cleanupKey) !== lifecycle) return;
1642
- scope.delete(cleanupKey);
1643
- callback();
1644
- }, delayMs);
1645
- scope.set(cleanupKey, lifecycle);
1498
+ function isPathPrefix(candidatePath, pathname) {
1499
+ const normalizedCandidate = normalizePath(candidatePath);
1500
+ const normalizedPathname = normalizePath(pathname);
1501
+ if (!normalizedCandidate || !normalizedPathname) return false;
1502
+ if (normalizedCandidate === normalizedPathname) return true;
1503
+ if (normalizedCandidate === "/") return normalizedPathname.startsWith("/");
1504
+ return normalizedPathname.startsWith(`${normalizedCandidate}/`);
1646
1505
  }
1647
- function collectModuleRegistrations(coreModule, slice, pageMetadata, context) {
1648
- const definition = coreModule.registry;
1649
- const toEntries = coreModule.page?.entries;
1650
- if (!definition || !toEntries || slice == null || slice === false) return [];
1651
- const type = coreModule.id;
1652
- const items = Array.isArray(slice) ? slice : [slice];
1653
- const registrations = [];
1654
- for (const item of items) {
1655
- if (item == null || item === false) continue;
1656
- const itemMetadata = isObject(item) && isObject(item.registry) ? item.registry : {};
1657
- const metadata = { ...pageMetadata, ...itemMetadata };
1658
- if (metadata.validation === "strict" && !validateRegistryMetadata(metadata).valid) {
1659
- continue;
1660
- }
1661
- const payload = isObject(item) ? Object.fromEntries(
1662
- Object.entries(item).filter(([key]) => key !== "registry")
1663
- ) : item;
1664
- const { cleanupDelayMs, lifecycle, registerOptions, scope, source } = normalizeRegistryMetadata(metadata, {
1665
- defaultCleanupDelayMs: definition.cleanupDelayMs ?? null,
1666
- defaultLifecycle: definition.lifecycle,
1667
- defaultSource: DEFAULT_SOURCE
1668
- });
1669
- for (const { key, value } of toEntries(payload, context) ?? []) {
1670
- const validation = definition.validate?.(value);
1671
- if (validation && !validation.valid) {
1672
- report(`Registry ${type} config is invalid`, validation.issues, "warn");
1673
- continue;
1674
- }
1675
- registrations.push({
1676
- cleanupDelayMs,
1677
- cleanupKey: createEntryCleanupKey(
1678
- type,
1679
- key,
1680
- source,
1681
- context.instanceId,
1682
- scope
1683
- ),
1684
- key,
1685
- lifecycle,
1686
- registerOptions,
1687
- scope,
1688
- source,
1689
- type,
1690
- value
1691
- });
1692
- }
1693
- }
1694
- return registrations;
1506
+ function isInlineActionPathMatch(path, pathname) {
1507
+ return isSamePath(path, pathname) || path !== "/" && isPathPrefix(path, pathname);
1695
1508
  }
1696
- var NO_CLEANUP = () => {
1697
- };
1698
- function applyRegistryConfig(config, context) {
1699
- const modules = context.modules ?? [];
1700
- if (!config || modules.length === 0) return NO_CLEANUP;
1701
- const pageMetadata = isObject(config.registry) ? config.registry : {};
1702
- const slices = config;
1703
- const registrations = modules.flatMap((coreModule) => {
1704
- try {
1705
- return collectModuleRegistrations(
1706
- coreModule,
1707
- slices[coreModule.id],
1708
- pageMetadata,
1709
- context
1710
- );
1711
- } catch (error) {
1712
- report(`Registry apply (${coreModule.id})`, error);
1713
- return [];
1714
- }
1715
- });
1716
- if (registrations.length === 0) return NO_CLEANUP;
1717
- const cleanupScope = getCleanupScope(context);
1718
- const owner = (registration) => ({
1719
- instanceId: context.instanceId,
1720
- scope: registration.scope,
1721
- source: registration.source
1722
- });
1723
- runRegistrationBatch(context, (queue) => {
1724
- registrations.forEach(
1725
- ({ cleanupKey, key, registerOptions, source, type, value }) => {
1726
- clearCleanupTimer(cleanupScope, cleanupKey);
1727
- queue.register(type, key, value, source, registerOptions);
1728
- }
1729
- );
1730
- });
1731
- return () => {
1732
- const immediate = [];
1733
- registrations.forEach((registration) => {
1734
- if (isPersistentLifecycle(registration.lifecycle)) return;
1735
- const { cleanupDelayMs, cleanupKey, key, type } = registration;
1736
- if (typeof cleanupDelayMs === "number" && cleanupDelayMs > 0) {
1737
- scheduleCleanup(
1738
- cleanupScope,
1739
- cleanupKey,
1740
- () => context.unregister(type, key, owner(registration)),
1741
- cleanupDelayMs
1742
- );
1743
- } else {
1744
- immediate.push(registration);
1745
- }
1746
- });
1747
- if (immediate.length === 0) return;
1748
- runRegistrationBatch(context, (queue) => {
1749
- immediate.forEach(
1750
- (registration) => queue.unregister(
1751
- registration.type,
1752
- registration.key,
1753
- owner(registration)
1754
- )
1755
- );
1756
- });
1757
- };
1509
+ function isSafeInternalHref(value) {
1510
+ const href = typeof value === "string" ? value.trim() : "";
1511
+ return href.startsWith("/") && !href.startsWith("//");
1758
1512
  }
1759
-
1760
- // src/kernel/hooks.ts
1761
- function withInstanceId(instanceId, sourceOrOptions, optionsArg) {
1762
- if (typeof sourceOrOptions === "string") {
1763
- return {
1764
- optionsArg: { ...isObject(optionsArg) ? optionsArg : {}, instanceId },
1765
- sourceOrOptions
1766
- };
1767
- }
1768
- if (isObject(sourceOrOptions)) {
1769
- return {
1770
- optionsArg,
1771
- sourceOrOptions: { ...sourceOrOptions, instanceId }
1772
- };
1773
- }
1774
- return {
1775
- optionsArg: void 0,
1776
- sourceOrOptions: {
1777
- ...isObject(optionsArg) ? optionsArg : {},
1778
- instanceId
1779
- }
1780
- };
1513
+ function getDockLocationKey({
1514
+ hash = "",
1515
+ pathname = "/",
1516
+ search = ""
1517
+ } = {}) {
1518
+ const normalizedPathname = String(pathname || "/").trim() || "/";
1519
+ const normalizedSearch = String(search || "").trim();
1520
+ const normalizedHash = String(hash || "").trim();
1521
+ const query = normalizedSearch ? normalizedSearch.startsWith("?") ? normalizedSearch : `?${normalizedSearch}` : "";
1522
+ const fragment = normalizedHash ? normalizedHash.startsWith("#") ? normalizedHash : `#${normalizedHash}` : "";
1523
+ return `${normalizedPathname}${query}${fragment}`;
1781
1524
  }
1782
- function withInstanceIdForUnregister(instanceId, sourceOrOptions) {
1783
- if (typeof sourceOrOptions === "string") {
1784
- return { instanceId, source: sourceOrOptions };
1785
- }
1786
- if (isObject(sourceOrOptions)) {
1787
- return { ...sourceOrOptions, instanceId };
1788
- }
1789
- return { instanceId };
1525
+ function formatMediaTime(seconds = 0) {
1526
+ const sec = Math.max(0, Math.floor(Number(seconds) || 0));
1527
+ const m = Math.floor(sec / 60);
1528
+ const s = sec % 60;
1529
+ return `${m}:${s < 10 ? "0" : ""}${s}`;
1790
1530
  }
1791
- function resolveRegisterArgsWithInstance(instanceId, sourceOrOptions, optionsArg) {
1792
- const input = withInstanceId(instanceId, sourceOrOptions, optionsArg);
1793
- return [input.sourceOrOptions, input.optionsArg];
1531
+ function isValidBannerUrl(banner) {
1532
+ const value = trimToNull(banner);
1533
+ if (!value) return false;
1534
+ return /^(https?:\/\/|\/|data:image\/)/.test(value);
1794
1535
  }
1795
- var MAX_STABILIZATION_DEPTH = 64;
1796
- function createStableFunctionEntry(fn) {
1797
- const entry = {
1798
- current: fn,
1799
- stable(...args) {
1800
- return entry.current?.apply(this, args);
1801
- }
1802
- };
1803
- return entry;
1536
+ function toObject(value) {
1537
+ return isObject(value) ? value : {};
1804
1538
  }
1805
- function isDirectRegistryComponentPath(path, componentSlices) {
1806
- const match = /^config\.([^.[]+)\.[^.[]+$/.exec(path);
1807
- return Boolean(match && componentSlices.has(match[1]));
1539
+ function isValidComponentType(type) {
1540
+ if (typeof type === "function") return true;
1541
+ return Boolean(
1542
+ type != null && typeof type === "object" && !isValidElement(type) && "$$typeof" in type
1543
+ );
1808
1544
  }
1809
- function isReactNodeLike(value) {
1810
- return value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" || typeof value === "number" || isValidElement(value);
1545
+ function resolveComponentType(...candidates) {
1546
+ return candidates.find(isValidComponentType) ?? null;
1811
1547
  }
1812
- function isStabilizableObject(value) {
1813
- return typeof value === "object" && value !== null && !Array.isArray(value) && !isValidElement(value);
1548
+ function resolveRenderableContent(...candidates) {
1549
+ return candidates.find(
1550
+ (candidate) => candidate !== null && candidate !== void 0
1551
+ ) ?? null;
1814
1552
  }
1815
- function hasSameObjectKeys(previousValue, nextValue) {
1816
- if (!isStabilizableObject(previousValue) || !isStabilizableObject(nextValue)) {
1817
- return false;
1553
+ function collectSearchableText(value, visitedObjects) {
1554
+ if (typeof value === "string") return value;
1555
+ if (typeof value === "number" || typeof value === "boolean")
1556
+ return String(value);
1557
+ if (Array.isArray(value)) {
1558
+ if (visitedObjects.has(value)) return "";
1559
+ visitedObjects.add(value);
1560
+ const text = value.map((entry) => collectSearchableText(entry, visitedObjects)).join(" ");
1561
+ visitedObjects.delete(value);
1562
+ return text;
1818
1563
  }
1819
- const previousKeys = Object.keys(previousValue);
1820
- const nextKeys = Object.keys(nextValue);
1821
- if (previousKeys.length !== nextKeys.length) return false;
1822
- return nextKeys.every(
1823
- (key) => Object.prototype.hasOwnProperty.call(previousValue, key)
1824
- );
1825
- }
1826
- function stabilizeRegistryValue(value, path, scope, previousValue, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
1827
- if (depth > MAX_STABILIZATION_DEPTH) return value;
1828
- if (typeof value === "function") {
1829
- const isComponent = value.name && /^[A-Z]/.test(value.name);
1830
- if (isComponent || isDirectRegistryComponentPath(path, scope.componentSlices)) {
1831
- return value;
1832
- }
1833
- scope.usedPaths.add(path);
1834
- let entry = scope.functionEntries.get(path);
1835
- if (!entry) {
1836
- entry = createStableFunctionEntry(value);
1837
- scope.functionEntries.set(path, entry);
1838
- } else {
1839
- entry.current = value;
1840
- }
1841
- return entry.stable;
1564
+ if (isValidElement(value)) {
1565
+ if (visitedObjects.has(value)) return "";
1566
+ visitedObjects.add(value);
1567
+ const text = collectSearchableText(
1568
+ value.props?.children,
1569
+ visitedObjects
1570
+ );
1571
+ visitedObjects.delete(value);
1572
+ return text;
1842
1573
  }
1843
1574
  if (value && typeof value === "object") {
1844
- if (seen.has(value)) return value;
1845
- seen.add(value);
1575
+ if (visitedObjects.has(value)) return "";
1576
+ visitedObjects.add(value);
1577
+ return Object.values(value).map((entry) => collectSearchableText(entry, visitedObjects)).join(" ");
1846
1578
  }
1847
- if (isValidElement(value)) {
1848
- const nextProps = stabilizeRegistryValue(
1849
- value.props,
1850
- `${path}.props`,
1851
- scope,
1852
- isValidElement(previousValue) ? previousValue.props : void 0,
1853
- seen,
1854
- depth + 1
1855
- );
1856
- if (isValidElement(previousValue) && previousValue.type === value.type && previousValue.key === value.key && previousValue.props === nextProps) {
1857
- return previousValue;
1579
+ return "";
1580
+ }
1581
+ function toSearchableText(value) {
1582
+ return collectSearchableText(value, /* @__PURE__ */ new WeakSet());
1583
+ }
1584
+ function normalizeUpper(value) {
1585
+ return String(value || "").trim().toUpperCase();
1586
+ }
1587
+ function splitStyle(style = {}) {
1588
+ const { className, ...inlineStyle } = style;
1589
+ return {
1590
+ className,
1591
+ inlineStyle
1592
+ };
1593
+ }
1594
+ function getLineClampStyle(maxLines, style) {
1595
+ if (Number(maxLines) <= 1) return style;
1596
+ return {
1597
+ WebkitBoxOrient: "vertical",
1598
+ WebkitLineClamp: maxLines,
1599
+ display: "-webkit-box",
1600
+ overflow: "hidden",
1601
+ ...style
1602
+ };
1603
+ }
1604
+ function getImageIconStyle(style, icon) {
1605
+ const nextStyle = {
1606
+ ...style
1607
+ };
1608
+ delete nextStyle.background;
1609
+ delete nextStyle.backgroundImage;
1610
+ return {
1611
+ ...nextStyle,
1612
+ backgroundImage: `url(${icon})`
1613
+ };
1614
+ }
1615
+ function resolveDockActionClass(slots, {
1616
+ className = "",
1617
+ button = "",
1618
+ isActive = false,
1619
+ variant = "",
1620
+ tone = "",
1621
+ base,
1622
+ cn: classNamesFn
1623
+ } = {}) {
1624
+ const resolve = classNamesFn || cn;
1625
+ const toneClasses = {
1626
+ muted: slots.actionMuted,
1627
+ active: slots.actionActive
1628
+ };
1629
+ if (button && !className && base === void 0) {
1630
+ if (tone) {
1631
+ const toneClass = toneClasses[tone] || slots.actionSurface || slots.actionMuted;
1632
+ return resolve(button, toneClass);
1858
1633
  }
1859
- return cloneElement(value, nextProps);
1860
- }
1861
- if (Array.isArray(value)) {
1862
- const nextValue = value.every(isReactNodeLike) ? Children.toArray(value) : value;
1863
- const previousArray = Array.isArray(previousValue) ? previousValue : null;
1864
- let hasChanged2 = !previousArray || previousArray.length !== nextValue.length;
1865
- const stabilizedValue2 = nextValue.map((item, index) => {
1866
- const nextItem = stabilizeRegistryValue(
1867
- item,
1868
- `${path}[${index}]`,
1869
- scope,
1870
- previousArray?.[index],
1871
- seen,
1872
- depth + 1
1873
- );
1874
- if (!previousArray || nextItem !== previousArray[index]) {
1875
- hasChanged2 = true;
1876
- }
1877
- return nextItem;
1878
- });
1879
- return !hasChanged2 ? previousArray : stabilizedValue2;
1634
+ const stateToken = isActive ? slots.actionActive : slots.actionMuted;
1635
+ return resolve(button, stateToken);
1880
1636
  }
1881
- if (typeof value !== "object" || value === null) {
1882
- return value;
1637
+ const elementClass = className || button;
1638
+ const resolvedBase = base !== void 0 ? base : slots.action;
1639
+ const stateClass = variant || tone && (toneClasses[tone] || slots.actionSurface) || (isActive ? slots.actionActive : slots.actionMuted);
1640
+ return resolve(stateClass, resolvedBase, elementClass);
1641
+ }
1642
+ function blurActiveElement() {
1643
+ if (typeof document === "undefined") return;
1644
+ const activeElement = document.activeElement;
1645
+ if (activeElement && typeof activeElement.blur === "function") {
1646
+ activeElement.blur();
1883
1647
  }
1884
- const source = value;
1885
- const previousRecord = previousValue;
1886
- const stabilizedValue = {};
1887
- const canReusePrevious = hasSameObjectKeys(previousValue, value);
1888
- let hasChanged = !canReusePrevious;
1889
- Object.keys(source).forEach((key) => {
1890
- const nextValue = stabilizeRegistryValue(
1891
- source[key],
1892
- `${path}.${key}`,
1893
- scope,
1894
- previousRecord?.[key],
1895
- seen,
1896
- depth + 1
1897
- );
1898
- stabilizedValue[key] = nextValue;
1899
- if (!canReusePrevious || nextValue !== previousRecord?.[key]) {
1900
- hasChanged = true;
1901
- }
1902
- });
1903
- return !hasChanged ? previousValue : stabilizedValue;
1904
1648
  }
1905
- function useAppliedRegistryConfig(payload, context, componentSlices) {
1906
- const functionEntriesRef = useRef(/* @__PURE__ */ new Map());
1907
- const appliedRef = useRef(null);
1908
- useIsomorphicLayoutEffect(() => {
1909
- const previous = appliedRef.current;
1910
- if (previous && previous.payload === payload && previous.context === context) {
1911
- return;
1912
- }
1913
- const functionEntries = functionEntriesRef.current;
1914
- const usedPaths = /* @__PURE__ */ new Set();
1915
- const config = stabilizeRegistryValue(
1916
- payload,
1917
- "config",
1918
- { componentSlices, functionEntries, usedPaths },
1919
- previous?.config
1920
- );
1921
- functionEntries.forEach((_entry, path) => {
1922
- if (!usedPaths.has(path)) functionEntries.delete(path);
1923
- });
1924
- if (previous && previous.config === config && previous.context === context) {
1925
- previous.payload = payload;
1926
- return;
1927
- }
1928
- previous?.cleanup();
1929
- appliedRef.current = {
1930
- cleanup: applyRegistryConfig(config, context),
1931
- config,
1932
- context,
1933
- payload
1934
- };
1935
- });
1936
- useIsomorphicLayoutEffect(
1937
- () => () => {
1938
- appliedRef.current?.cleanup();
1939
- appliedRef.current = null;
1940
- },
1941
- []
1649
+ function closestMatches(target, selector) {
1650
+ const element = target;
1651
+ return Boolean(
1652
+ element && typeof element.closest === "function" && element.closest(selector)
1653
+ );
1654
+ }
1655
+ function isInteractiveTarget(target) {
1656
+ return closestMatches(
1657
+ target,
1658
+ 'button, a, input, textarea, select, [role="button"], [tabindex]:not([tabindex="-1"])'
1659
+ );
1660
+ }
1661
+ function isEditableDockTarget(target) {
1662
+ return closestMatches(
1663
+ target,
1664
+ 'input, textarea, select, [contenteditable="true"]'
1942
1665
  );
1943
1666
  }
1944
- function createRegistryApplyContext(actions, {
1945
- cleanupScope = /* @__PURE__ */ new Map(),
1946
- instanceId,
1947
- modules = [],
1948
- pathname
1949
- }) {
1950
- const stamp = (queue) => ({
1951
- register: (type, key, item, sourceOrOptions, optionsArg) => queue.register(
1952
- type,
1953
- key,
1954
- item,
1955
- ...resolveRegisterArgsWithInstance(
1956
- instanceId,
1957
- sourceOrOptions,
1958
- optionsArg
1959
- )
1960
- ),
1961
- unregister: (type, key, sourceOrOptions) => queue.unregister(
1962
- type,
1963
- key,
1964
- withInstanceIdForUnregister(instanceId, sourceOrOptions)
1965
- )
1667
+ function getDockFocusableElements(container) {
1668
+ if (!container || typeof container.querySelectorAll !== "function") return [];
1669
+ return Array.from(
1670
+ container.querySelectorAll(DOCK_FOCUSABLE_SELECTOR)
1671
+ ).filter((element) => {
1672
+ if (!element || element.getAttribute("aria-hidden") === "true")
1673
+ return false;
1674
+ const style = typeof window !== "undefined" ? window.getComputedStyle(element) : null;
1675
+ return style?.display !== "none" && style?.visibility !== "hidden" && style?.pointerEvents !== "none";
1966
1676
  });
1967
- return {
1968
- ...stamp(actions),
1969
- batch: (executor) => typeof executor === "function" ? actions.batch((queue) => executor(stamp(queue))) : 0,
1970
- cleanupScope,
1971
- instanceId,
1972
- modules,
1973
- pathname
1974
- };
1975
1677
  }
1976
- function useRegistry(config, metadata) {
1977
- const { batch, register, unregister } = useRegistryActions();
1978
- const pathname = usePathname();
1979
- const modules = useInstalledModules();
1980
- const instanceId = `registry-instance-${useId()}`;
1981
- const [cleanupScope] = useState(
1982
- () => /* @__PURE__ */ new Map()
1678
+ function focusDockElement(element) {
1679
+ if (!element || typeof element.focus !== "function") return false;
1680
+ try {
1681
+ element.focus({ preventScroll: true });
1682
+ return document.activeElement === element;
1683
+ } catch {
1684
+ return false;
1685
+ }
1686
+ }
1687
+ function shouldRestoreDockFocus(result) {
1688
+ const blockedReason = result?.blockedReason;
1689
+ return !result || !DOCK_FOCUS_RESTORE_BLOCKED_REASONS.includes(
1690
+ blockedReason
1983
1691
  );
1984
- const context = useMemo(
1985
- () => createRegistryApplyContext(
1986
- { batch, register, unregister },
1987
- {
1988
- cleanupScope,
1989
- instanceId,
1990
- modules,
1991
- pathname
1992
- }
1993
- ),
1994
- [batch, cleanupScope, instanceId, modules, pathname, register, unregister]
1692
+ }
1693
+ function isSameDockItem(item, candidate) {
1694
+ return Boolean(
1695
+ item?.path && item.path === candidate?.path || item?.name && item.name === candidate?.name
1995
1696
  );
1996
- const payload = useMemo(
1997
- () => config ? createModulePayload(modules, config, metadata) : null,
1998
- [config, metadata, modules]
1697
+ }
1698
+ var isSameItem = isSameDockItem;
1699
+ function getItemKey(link, index = 0) {
1700
+ const identity = link?.id ?? link?.path ?? link?.name ?? link?.type;
1701
+ return `dock-card:${identity == null ? `slot-${index}` : String(identity)}`;
1702
+ }
1703
+ function getItemMeasurementKey({
1704
+ link,
1705
+ expanded,
1706
+ isHud = false
1707
+ }) {
1708
+ const state = isHud ? "hud" : link.isLoading ? "loading" : link.isSurface ? `surface-${link.surfacePhase ?? "open"}` : "standard";
1709
+ return `${link.path || link.name || "item"}:${state}:${expanded ? "expanded" : "collapsed"}`;
1710
+ }
1711
+ function getRouteMeasurementKey(pathname, key) {
1712
+ return `${pathname || ""}:${key}`;
1713
+ }
1714
+ function resolveDockHeaderKey({
1715
+ link,
1716
+ description = "",
1717
+ showVideoIcon = false
1718
+ }) {
1719
+ const statusPart = link?.isStatus ? `status:${link.statusType || link.type || "status"}` : "standard";
1720
+ const identityPart = link?.path || link?.name || link?.id || "item";
1721
+ const titlePart = link?.title || link?.name || "";
1722
+ const descPart = description || "";
1723
+ const iconPart = showVideoIcon ? "video" : link?.icon || "no-icon";
1724
+ return `${statusPart}:${identityPart}:${iconPart}:${titlePart}:${descPart}`;
1725
+ }
1726
+ function findDockItemIndex(dockItems, activeItem, pathname) {
1727
+ const normalizedPathname = normalizePath(pathname);
1728
+ const selectedDataSourceIndex = dockItems.findIndex(
1729
+ (item) => item.isDataSource && item.isSelected
1999
1730
  );
2000
- const componentSlices = useMemo(
2001
- () => new Set(
2002
- modules.filter(
2003
- (coreModule) => coreModule.registry?.valueKind === "component"
2004
- ).map((coreModule) => coreModule.id)
2005
- ),
2006
- [modules]
1731
+ if (selectedDataSourceIndex !== -1) return selectedDataSourceIndex;
1732
+ if (activeItem) {
1733
+ const matchedActiveIndex = dockItems.findIndex(
1734
+ (item) => item.path && isSamePath(item.path, activeItem.path) || item.name && item.name === activeItem.name
1735
+ );
1736
+ if (matchedActiveIndex !== -1) return matchedActiveIndex;
1737
+ }
1738
+ return dockItems.findIndex(
1739
+ (item) => isSamePath(item.path, normalizedPathname) || item.targetPath && isSamePath(item.targetPath, normalizedPathname)
2007
1740
  );
2008
- useAppliedRegistryConfig(payload, context, componentSlices);
2009
1741
  }
2010
- function createPageControllerStore() {
2011
- const listeners2 = /* @__PURE__ */ new Set();
2012
- let stack = [];
2013
- const notify = () => {
2014
- listeners2.forEach((listener) => {
2015
- try {
2016
- listener();
2017
- } catch (error) {
2018
- report("Page controller listener", error);
2019
- }
2020
- });
2021
- };
2022
- return {
2023
- getSnapshot() {
2024
- const top = stack[stack.length - 1];
2025
- return top?.controllerRef?.current || null;
2026
- },
2027
- notify,
2028
- register(id, controllerRef) {
2029
- stack = stack.filter((item) => item.id !== id);
2030
- stack.push({ controllerRef, id });
2031
- notify();
2032
- },
2033
- subscribe(listener) {
2034
- listeners2.add(listener);
2035
- return () => {
2036
- listeners2.delete(listener);
2037
- };
2038
- },
2039
- unregister(id) {
2040
- stack = stack.filter((item) => item.id !== id);
2041
- notify();
1742
+ function resolveActiveIndex({
1743
+ dockItems,
1744
+ activeItem,
1745
+ pathname
1746
+ }) {
1747
+ return Math.max(0, findDockItemIndex(dockItems, activeItem, pathname));
1748
+ }
1749
+ function shouldKeepAncestorItem(item, activePath) {
1750
+ const policy = item?.keepWhenDescendant;
1751
+ if (typeof policy === "function") {
1752
+ try {
1753
+ return Boolean(policy(activePath, item));
1754
+ } catch {
1755
+ return false;
2042
1756
  }
2043
- };
1757
+ }
1758
+ return policy === true;
2044
1759
  }
2045
- var fallbackStandaloneStore = createPageControllerStore();
2046
- getOrCreateGlobalContext(
2047
- "PageControllerContext",
2048
- null
2049
- );
2050
- getOrCreateGlobalContext(
2051
- "PageControllerStoreContext",
2052
- fallbackStandaloneStore
2053
- );
2054
- function defineModule(coreModule) {
2055
- return Object.freeze({ ...coreModule });
1760
+ function isAncestorPath(item, activePath) {
1761
+ const candidatePath = item?.path;
1762
+ if (!candidatePath || candidatePath === "/" || candidatePath === activePath)
1763
+ return false;
1764
+ return isPathPrefix(candidatePath, activePath) && !shouldKeepAncestorItem(item, activePath);
2056
1765
  }
2057
- function useModule(id) {
2058
- const coreModule = use(ModuleHostContext)?.byId.get(id);
2059
- return coreModule?.context ? use(coreModule.context) ?? null : null;
1766
+ function removeAncestorDuplicates(items = []) {
1767
+ if (!Array.isArray(items) || items.length <= 1) return items;
1768
+ const activePath = items[0]?.path;
1769
+ if (!activePath) return items;
1770
+ return items.filter(
1771
+ (item, index) => index === 0 || !isAncestorPath(item, activePath)
1772
+ );
2060
1773
  }
2061
- function definePeer(id, inert) {
2062
- const useRuntime = () => useModule(id) ?? inert;
2063
- return {
2064
- useRuntime,
2065
- useActions: () => useRuntime().actions,
2066
- useState: (selector, isEqual) => useStore(
2067
- useRuntime().store,
2068
- selector,
2069
- isEqual
2070
- )
2071
- };
1774
+ function replaceActiveItem(items, activeIndex, activeItem) {
1775
+ if (activeIndex === -1 || !activeItem) return items;
1776
+ const nextItems = [...items];
1777
+ nextItems[activeIndex] = activeItem;
1778
+ return nextItems;
2072
1779
  }
2073
- createStore(void 0);
2074
-
2075
- // src/kernel/adapters.tsx
2076
- function useModuleRegistration(id, value, options) {
2077
- const coreModule = useInstalledModules().find(
2078
- (candidate) => candidate.id === id
1780
+ function removeInactiveLoadingItems(items = [], activeItem = null) {
1781
+ if (!Array.isArray(items) || items.length === 0) return items;
1782
+ return items.filter(
1783
+ (item) => !item?.isLoading || isSameItem(item, activeItem)
2079
1784
  );
2080
- const config = coreModule && options?.enabled !== false && value != null ? { [id]: value } : null;
2081
- useRegistry(config, pickRegistryMetadata(options));
2082
1785
  }
2083
-
2084
- // src/modules/dock/context.ts
2085
- var DockContext = getOrCreateGlobalContext(
2086
- "DockContext",
2087
- null
2088
- );
2089
- function useDockState() {
2090
- const { store } = useRequiredContext(
2091
- DockContext,
2092
- "useDockState",
2093
- "DockProvider"
2094
- );
2095
- return useStore(store);
1786
+ function reorderItemsWithActiveFirst(items, activeIndex) {
1787
+ if (activeIndex === -1) return items;
1788
+ const active = items[activeIndex];
1789
+ const rest = [
1790
+ ...items.slice(0, activeIndex),
1791
+ ...items.slice(activeIndex + 1)
1792
+ ];
1793
+ rest.sort((a, b) => a.path === "/" ? 1 : b.path === "/" ? -1 : 0);
1794
+ return [active, ...rest];
2096
1795
  }
2097
- function useDockSelector(selector, isEqual = Object.is) {
2098
- const { store } = useRequiredContext(
2099
- DockContext,
2100
- "useDockSelector",
2101
- "DockProvider"
2102
- );
2103
- return useStore(store, selector, isEqual);
1796
+ function isHudDescriptor(value) {
1797
+ return isObject(value) && !isValidElement(value) && ("component" in value || "content" in value || "node" in value || "element" in value || "isActive" in value || "id" in value);
1798
+ }
1799
+ function isSurfaceDescriptor(value) {
1800
+ return value != null && typeof value === "object" && !Array.isArray(value) && !isValidElement(value);
1801
+ }
1802
+ var generatedExtensionId = 0;
1803
+ function normalizeSurfaceExtension(input) {
1804
+ if (!input) return null;
1805
+ if (isValidElement(input)) {
1806
+ return {
1807
+ align: "left",
1808
+ className: "",
1809
+ component: null,
1810
+ content: input,
1811
+ id: `ext-${++generatedExtensionId}`,
1812
+ order: 0,
1813
+ props: {},
1814
+ unstyled: false
1815
+ };
1816
+ }
1817
+ if (typeof input !== "object") return null;
1818
+ const extension = input;
1819
+ const component = isValidComponentType(extension.component) ? extension.component : null;
1820
+ const content = isValidElement(extension.content) || typeof extension.content === "string" || typeof extension.content === "number" ? extension.content : null;
1821
+ if (!component && content == null) return null;
1822
+ const align = extension.align === "right" || extension.align === "end" ? "right" : extension.align === "center" ? "center" : "left";
1823
+ return {
1824
+ align,
1825
+ className: typeof extension.className === "string" ? extension.className : "",
1826
+ component,
1827
+ content,
1828
+ id: String(
1829
+ extension.id || extension.key || `ext-${++generatedExtensionId}`
1830
+ ),
1831
+ order: Number.isFinite(Number(extension.order)) ? Number(extension.order) : 0,
1832
+ props: isPlainObject(extension.props) ? extension.props : {},
1833
+ unstyled: Boolean(extension.unstyled)
1834
+ };
2104
1835
  }
2105
- function useDockActions() {
2106
- return useRequiredContext(DockContext, "useDockActions", "DockProvider").actions;
1836
+ function normalizeSurfaceFlowSnapshot(value) {
1837
+ if (!isPlainObject(value)) return null;
1838
+ return { ...value };
2107
1839
  }
2108
- function useDockHeight() {
2109
- const dockHeight = useDockSelector((state) => state.dockHeight);
2110
- return { dockHeight, padding: { paddingBottom: `${dockHeight}px` } };
1840
+ function createSurfaceReturnHandshake(input) {
1841
+ const source = typeof input === "string" ? { pathname: input } : input;
1842
+ const pathname = typeof source?.pathname === "string" ? source.pathname.trim() : "";
1843
+ if (!isSafeInternalHref(pathname)) return null;
1844
+ return {
1845
+ focusKey: typeof source?.focusKey === "string" && source.focusKey.trim() ? source.focusKey.trim() : null,
1846
+ pathname,
1847
+ restoreScroll: source?.restoreScroll !== false,
1848
+ returnOnCancel: source?.returnOnCancel === true
1849
+ };
2111
1850
  }
2112
- var EMPTY_DOCK_STATE = Object.freeze({
2113
- activeItem: null,
2114
- activeOperation: null,
2115
- activeSurfaceEntry: null,
2116
- activeSurfaceId: null,
2117
- contextActions: [],
2118
- dockContinuity: [],
2119
- dockHeight: 0,
2120
- dockReturnHandoffs: [],
2121
- expanded: false,
2122
- hud: null,
2123
- hudEntries: [],
2124
- isHudActive: false,
2125
- isSurfaceOpen: false,
2126
- locationKey: "",
2127
- operations: [],
2128
- pathname: "",
2129
- searchQuery: "",
2130
- selectionMode: null,
2131
- surfaceLifecycle: "",
2132
- surfacePhase: "",
2133
- surfaceStack: []
2134
- });
2135
- var mediaPeer = definePeer("media", {
2136
- actions: Object.freeze({
2137
- removeSource: () => {
2138
- },
2139
- setMuted: () => {
2140
- },
2141
- toggle: () => {
2142
- },
2143
- toggleLoop: () => {
2144
- },
2145
- upsertSource: () => {
2146
- }
2147
- }),
2148
- store: createStore({
2149
- audibleElement: null,
2150
- element: null,
2151
- hasMedia: false,
2152
- isPlaying: false,
2153
- kind: null,
2154
- loop: false,
2155
- sourceId: null
2156
- })
2157
- });
2158
- function useDockMedia() {
2159
- return mediaPeer.useState((state) => state, shallowEqual);
1851
+ function resolveSurfaceFlowReturnHandshake(definition, input) {
1852
+ const flowInput = input && typeof input === "object" ? input : null;
1853
+ const inputHandshake = flowInput?.returnHandshake ?? (flowInput?.returnTo ? {
1854
+ focusKey: flowInput.returnFocusKey,
1855
+ pathname: flowInput.returnTo,
1856
+ restoreScroll: flowInput.restoreReturnScroll,
1857
+ returnOnCancel: flowInput.returnOnCancel
1858
+ } : null);
1859
+ const baseHandshake = definition?.returnHandshake;
1860
+ if (!baseHandshake && !inputHandshake) return null;
1861
+ return createSurfaceReturnHandshake({ ...baseHandshake, ...inputHandshake });
2160
1862
  }
2161
- var selectHasMedia = (state) => state.hasMedia;
2162
- function useDockHasMedia() {
2163
- return mediaPeer.useState(selectHasMedia);
1863
+ function createDockScheduler(options = {}) {
1864
+ return createScheduler({
1865
+ defaultLabel: "dock-task",
1866
+ ...options
1867
+ });
2164
1868
  }
2165
- var useDockMediaActions = mediaPeer.useActions;
2166
- var loadingPeer = definePeer("loading", {
2167
- actions: {
2168
- setLoading: () => {
2169
- },
2170
- setSkeleton: () => {
2171
- },
2172
- startLoading: () => {
2173
- },
2174
- stopLoading: () => {
2175
- },
2176
- withLoading: async (task) => typeof task === "function" ? task() : task
2177
- },
2178
- store: createStore({
2179
- isLoading: false,
2180
- isPageLoading: false,
2181
- message: null,
2182
- minDuration: 0,
2183
- showOverlay: false,
2184
- skeleton: null
2185
- })
1869
+
1870
+ // src/modules/dock/surface/context.tsx
1871
+ var SurfaceExtensionsContext = createContext(null);
1872
+ var SurfaceItemContext = createContext({
1873
+ id: null,
1874
+ width: null
2186
1875
  });
2187
- function useDockLoadingState() {
2188
- return loadingPeer.useState((state) => state);
1876
+ function useSurfaceDimensions() {
1877
+ const { width } = use(SurfaceItemContext);
1878
+ return useMemo(() => ({ width }), [width]);
2189
1879
  }
2190
- var useDockLoadingActions = loadingPeer.useActions;
2191
- var notificationPeer = definePeer("notification", {
2192
- actions: {
2193
- dismissAllNotifications: () => {
2194
- },
2195
- dismissNotification: () => {
1880
+ function useSurfaceId() {
1881
+ return use(SurfaceItemContext).id;
1882
+ }
1883
+ function useSurfaceHeader() {
1884
+ const store = use(SurfaceExtensionsContext)?.headerActions ?? null;
1885
+ const surfaceId = useSurfaceId();
1886
+ return useCallback(
1887
+ (patch) => {
1888
+ if (!store) return;
1889
+ const data = typeof patch === "function" ? patch({}) : patch;
1890
+ if (data?.headerAction !== void 0)
1891
+ store.setAction(surfaceId, data.headerAction);
2196
1892
  },
2197
- showNotification: () => null
2198
- },
2199
- store: createStore({ notifications: {} })
2200
- });
2201
- function useDockNotificationVisible() {
2202
- return notificationPeer.useState(
2203
- ({ notifications }) => Object.keys(notifications).length > 0
1893
+ [store, surfaceId]
2204
1894
  );
2205
1895
  }
2206
- var statusActionDefaults = { error: null, guard: null };
2207
-
2208
- // src/modules/dock/surface/definition.ts
2209
- var isNormalizedExtension = (extension) => extension !== null;
2210
- function normalizeExtensions(raw) {
2211
- if (!raw) return [];
2212
- return (Array.isArray(raw) ? raw : [raw]).map(normalizeSurfaceExtension).filter(isNormalizedExtension);
1896
+ function useSurfaceAction(action) {
1897
+ const store = use(SurfaceExtensionsContext)?.headerActions ?? null;
1898
+ const surfaceId = useSurfaceId();
1899
+ useEffect(() => {
1900
+ if (!store || action === void 0) return;
1901
+ store.setAction(surfaceId, action);
1902
+ return () => store.removeAction(surfaceId);
1903
+ }, [store, surfaceId, action]);
2213
1904
  }
2214
- function normalizeSurfaceDefinition(input, config = {}, {
2215
- allowPrimitiveContent = false,
2216
- defaultShowAction = false
2217
- } = {}) {
2218
- const descriptor = isSurfaceDescriptor(input) && (isValidComponentType(input.component) || typeof input.loader === "function" || "content" in input || "node" in input || "element" in input || Array.isArray(input.steps) && input.steps.length > 0) ? input : null;
2219
- const configuredSteps = descriptor?.steps ?? config?.steps;
2220
- const steps = Array.isArray(configuredSteps) && configuredSteps.length > 0 ? configuredSteps : null;
2221
- const firstStep = steps?.[0] ?? null;
2222
- const rawComponent = resolveComponentType(
2223
- descriptor?.component,
2224
- descriptor ? null : input,
2225
- firstStep?.component,
2226
- firstStep
2227
- );
2228
- const component = rawComponent ?? (typeof descriptor?.loader === "function" ? lazy(descriptor.loader) : typeof firstStep?.loader === "function" ? lazy(firstStep.loader) : null);
2229
- const explicitContent = resolveRenderableContent(
2230
- descriptor?.content,
2231
- descriptor?.node,
2232
- descriptor?.element,
2233
- firstStep?.content,
2234
- firstStep?.node,
2235
- firstStep?.element
2236
- );
2237
- const fallbackContent = !descriptor && !component && (isValidElement(input) || allowPrimitiveContent && input != null) ? input : null;
2238
- const content = explicitContent ?? fallbackContent;
2239
- if (!component && content == null && !steps) return null;
2240
- const directComponentInput = !descriptor && isValidComponentType(input);
2241
- const extensions = normalizeExtensions(
2242
- descriptor?.extensions ?? config?.extensions
2243
- );
2244
- return {
2245
- renderMode: component ? DOCK_SURFACE_RENDER_MODE.COMPONENT : DOCK_SURFACE_RENDER_MODE.NODE,
2246
- component,
2247
- content: component ? null : content ?? input,
2248
- props: component ? isPlainObject(descriptor?.props) ? descriptor.props : directComponentInput ? config : {} : {},
2249
- action: descriptor?.action ?? config?.action ?? null,
2250
- showAction: descriptor?.showAction ?? config?.showAction ?? defaultShowAction,
2251
- dismissible: descriptor?.dismissible ?? config?.dismissible ?? true,
2252
- onClose: descriptor?.onClose ?? config?.onClose ?? null,
2253
- icon: descriptor?.icon ?? descriptor?.header?.icon ?? config?.icon ?? config?.header?.icon ?? null,
2254
- title: descriptor?.title ?? descriptor?.header?.title ?? config?.title ?? config?.header?.title ?? null,
2255
- description: descriptor?.description ?? descriptor?.header?.description ?? config?.description ?? config?.header?.description ?? null,
2256
- descriptionMaxLines: descriptor?.descriptionMaxLines ?? config?.descriptionMaxLines ?? 2,
2257
- trailing: descriptor?.trailing ?? config?.trailing ?? null,
2258
- headerAction: descriptor?.headerAction ?? config?.headerAction ?? null,
2259
- closeLabel: descriptor?.closeLabel ?? config?.closeLabel ?? null,
2260
- width: descriptor?.width ?? config?.width ?? null,
2261
- allowSwipeDismiss: descriptor?.allowSwipeDismiss ?? config?.allowSwipeDismiss ?? true,
2262
- skipActionDismiss: descriptor?.skipActionDismiss ?? config?.skipActionDismiss ?? true,
2263
- steps,
2264
- currentStepIndex: descriptor?.currentStepIndex ?? config?.currentStepIndex ?? 0,
2265
- syncWithUrl: descriptor?.syncWithUrl ?? config?.syncWithUrl ?? false,
2266
- urlKey: descriptor?.urlKey ?? config?.urlKey ?? null,
2267
- badge: descriptor?.badge ?? config?.badge ?? null,
2268
- extensions
1905
+ var SurfaceHeaderActionStore = class {
1906
+ store = createStore({
1907
+ actionsBySurface: {},
1908
+ version: 0
1909
+ });
1910
+ get version() {
1911
+ return this.store.getSnapshot().version;
1912
+ }
1913
+ subscribe = (listener) => this.store.subscribe(listener);
1914
+ getAction = (surfaceId) => {
1915
+ const sId = surfaceId != null ? String(surfaceId) : "global";
1916
+ const { actionsBySurface } = this.store.getSnapshot();
1917
+ return actionsBySurface[sId] ?? actionsBySurface.global ?? null;
2269
1918
  };
2270
- }
2271
- function createSurfaceEntryDefinition(input, config = {}) {
2272
- return normalizeSurfaceDefinition(input, config);
2273
- }
2274
- function createSurfaceFlowBuilder(definition = {}) {
2275
- const {
2276
- action = null,
2277
- allowSwipeDismiss = true,
2278
- badge = null,
2279
- closeLabel = null,
2280
- component = null,
2281
- loader,
2282
- defaultProps = {},
2283
- description = null,
2284
- descriptionMaxLines = 2,
2285
- dismissible = true,
2286
- extensions = [],
2287
- headerAction = null,
2288
- icon = null,
2289
- id = null,
2290
- onClose = null,
2291
- showAction = null,
2292
- syncWithUrl = false,
2293
- title = null,
2294
- trailing = null,
2295
- urlKey = null,
2296
- width = null,
2297
- ...extraConfig
2298
- } = definition;
2299
- const resolvedComponent = component ?? (typeof loader === "function" ? lazy(loader) : null);
2300
- const createEntry = function surfaceEntryFactory2(props = {}, overrides = {}) {
2301
- const mergedProps = { ...defaultProps, ...props };
2302
- const resolvedTitle = typeof title === "function" ? title(mergedProps) : title;
2303
- const resolvedDescription = typeof description === "function" ? description(mergedProps) : description;
2304
- const resolvedIcon = typeof icon === "function" ? icon(mergedProps) : icon;
2305
- return {
2306
- id: overrides.id ?? id,
2307
- component: overrides.component ?? resolvedComponent,
2308
- loader: overrides.loader ?? loader,
2309
- props: mergedProps,
2310
- title: resolvedTitle,
2311
- description: resolvedDescription,
2312
- icon: resolvedIcon,
2313
- action,
2314
- allowSwipeDismiss,
2315
- badge,
2316
- closeLabel,
2317
- descriptionMaxLines,
2318
- dismissible,
2319
- extensions,
2320
- headerAction,
2321
- onClose,
2322
- showAction,
2323
- syncWithUrl,
2324
- trailing,
2325
- urlKey,
2326
- width,
2327
- ...extraConfig,
2328
- ...overrides
2329
- };
1919
+ setAction = (surfaceId, action) => {
1920
+ const sId = surfaceId != null ? String(surfaceId) : "global";
1921
+ const current = this.store.getSnapshot();
1922
+ if (current.actionsBySurface[sId] === action) return;
1923
+ this.store.setState({
1924
+ actionsBySurface: {
1925
+ ...current.actionsBySurface,
1926
+ [sId]: action
1927
+ },
1928
+ version: current.version + 1
1929
+ });
2330
1930
  };
2331
- const surfaceEntryFactory = Object.assign(
2332
- createEntry,
2333
- {
2334
- component: resolvedComponent,
2335
- id,
2336
- isSurfaceFactory: true,
2337
- open: (openSurfaceFn, props = {}, overrides = {}) => {
2338
- if (typeof openSurfaceFn === "function") {
2339
- return openSurfaceFn(createEntry(props, overrides));
2340
- }
2341
- }
1931
+ removeAction = (surfaceId) => {
1932
+ const sId = surfaceId != null ? String(surfaceId) : "global";
1933
+ const current = this.store.getSnapshot();
1934
+ if (!(sId in current.actionsBySurface)) return;
1935
+ const { [sId]: _, ...nextActions } = current.actionsBySurface;
1936
+ this.store.setState({
1937
+ actionsBySurface: nextActions,
1938
+ version: current.version + 1
1939
+ });
1940
+ };
1941
+ clearSurface = (surfaceId) => this.removeAction(surfaceId);
1942
+ };
1943
+ var SurfaceExtensionsStore = class {
1944
+ extensionsBySurface = /* @__PURE__ */ new Map();
1945
+ cachedListBySurface = /* @__PURE__ */ new Map();
1946
+ store = createStore({ version: 0 });
1947
+ get version() {
1948
+ return this.store.getSnapshot().version;
1949
+ }
1950
+ subscribe = (listener) => this.store.subscribe(listener);
1951
+ notify = () => {
1952
+ this.cachedListBySurface.clear();
1953
+ this.store.setState((prev) => ({ version: prev.version + 1 }));
1954
+ };
1955
+ getExtensionsForSurface = (surfaceId) => {
1956
+ const sId = surfaceId != null ? String(surfaceId) : "global";
1957
+ if (this.cachedListBySurface.has(sId))
1958
+ return this.cachedListBySurface.get(sId);
1959
+ const globalExts = this.extensionsBySurface.get("global");
1960
+ const surfaceExts = this.extensionsBySurface.get(sId);
1961
+ if (!globalExts && !surfaceExts) {
1962
+ const empty = [];
1963
+ this.cachedListBySurface.set(sId, empty);
1964
+ return empty;
2342
1965
  }
2343
- );
2344
- return surfaceEntryFactory;
2345
- }
2346
- function createInlineSurfaceEntry(surface) {
2347
- return normalizeSurfaceDefinition(
2348
- surface,
2349
- {},
2350
- { allowPrimitiveContent: true, defaultShowAction: null }
2351
- );
2352
- }
2353
- var EMPTY_SURFACE_STACK = [];
2354
- function useSurface(surfaceDefinition) {
2355
- const { closeAllSurfaces, closeSurface, openSurface } = useDockActions();
2356
- const rawSurfaceStack = useDockSelector(
2357
- (s) => s.surfaceStack
2358
- );
2359
- const surfaceStack = rawSurfaceStack || EMPTY_SURFACE_STACK;
2360
- const specificSurface = useMemo(() => {
2361
- if (!surfaceDefinition) return null;
2362
- const targetComponent = typeof surfaceDefinition === "string" ? null : typeof surfaceDefinition === "function" ? surfaceDefinition.component || surfaceDefinition : surfaceDefinition.component || null;
2363
- const matchingEntry = surfaceStack.find(
2364
- (entry) => targetComponent && entry.component === targetComponent
2365
- ) ?? null;
2366
- const isThisSurfaceOpen = Boolean(matchingEntry);
2367
- const open = (props, overrides) => {
2368
- const entry = typeof surfaceDefinition === "function" ? surfaceDefinition(props, overrides) : typeof surfaceDefinition === "string" ? { id: surfaceDefinition, props, ...overrides } : {
2369
- ...surfaceDefinition,
2370
- props: { ...surfaceDefinition.props || {}, ...props },
2371
- ...overrides
2372
- };
2373
- return openSurface(entry);
2374
- };
2375
- const close = (res) => closeSurface(res, matchingEntry?.id ?? null);
2376
- const result = [
2377
- open,
2378
- {
2379
- close,
2380
- closeAll: closeAllSurfaces,
2381
- isOpen: isThisSurfaceOpen,
2382
- surfaceStack
2383
- }
2384
- ];
2385
- result.open = open;
2386
- result.close = close;
2387
- result.closeAll = closeAllSurfaces;
2388
- result.isOpen = isThisSurfaceOpen;
2389
- result.surfaceStack = surfaceStack;
1966
+ const merged = new Map([...globalExts || [], ...surfaceExts || []]);
1967
+ const result = Array.from(merged.values()).sort(
1968
+ (a, b) => a.order - b.order
1969
+ );
1970
+ this.cachedListBySurface.set(sId, result);
2390
1971
  return result;
2391
- }, [
2392
- closeAllSurfaces,
2393
- closeSurface,
2394
- openSurface,
2395
- surfaceDefinition,
2396
- surfaceStack
2397
- ]);
2398
- const generalSurface = useMemo(
2399
- () => ({
2400
- closeAllSurfaces,
2401
- closeSurface,
2402
- openSurface,
2403
- surfaceStack
2404
- }),
2405
- [closeAllSurfaces, closeSurface, openSurface, surfaceStack]
2406
- );
2407
- return surfaceDefinition ? specificSurface : generalSurface;
2408
- }
2409
- function defineSurface(definition = {}) {
2410
- const factory = createSurfaceFlowBuilder(definition);
2411
- factory.config = definition;
2412
- factory.use = function useDefinedSurface() {
2413
- return useSurface(factory);
2414
1972
  };
2415
- return factory;
2416
- }
2417
- function defineStepSurface(definition = {}) {
2418
- const {
2419
- currentStepIndex = 0,
2420
- defaultProps = {},
2421
- id = "wizard-surface",
2422
- steps = [],
2423
- title = null,
2424
- width = null,
2425
- ...extraConfig
2426
- } = definition;
2427
- const stepSurfaceFactory = function stepSurfaceFactory2(props = {}, overrides = {}) {
2428
- const mergedProps = { ...defaultProps, ...props };
2429
- const resolvedTitle = typeof title === "function" ? title(mergedProps) : title;
2430
- const resolvedSteps = typeof steps === "function" ? steps(mergedProps) : steps;
2431
- return {
2432
- id: overrides.id ?? id,
2433
- title: overrides.title ?? resolvedTitle,
2434
- steps: resolvedSteps,
2435
- currentStepIndex: overrides.currentStepIndex ?? currentStepIndex,
2436
- props: mergedProps,
2437
- width: overrides.width ?? width,
2438
- ...extraConfig,
2439
- ...overrides
2440
- };
1973
+ setExtension = (surfaceId, extension) => {
1974
+ if (!extension) return;
1975
+ const normalized = normalizeSurfaceExtension(extension);
1976
+ if (!normalized) return;
1977
+ const sId = surfaceId != null ? String(surfaceId) : "global";
1978
+ let surfaceMap = this.extensionsBySurface.get(sId);
1979
+ if (!surfaceMap) {
1980
+ surfaceMap = /* @__PURE__ */ new Map();
1981
+ this.extensionsBySurface.set(sId, surfaceMap);
1982
+ }
1983
+ const prev = surfaceMap.get(normalized.id);
1984
+ if (prev && prev.content === normalized.content && prev.align === normalized.align && prev.order === normalized.order && prev.className === normalized.className && prev.unstyled === normalized.unstyled && prev.component === normalized.component && prev.props === normalized.props)
1985
+ return;
1986
+ surfaceMap.set(normalized.id, normalized);
1987
+ this.notify();
2441
1988
  };
2442
- stepSurfaceFactory.id = id;
2443
- stepSurfaceFactory.config = {
2444
- currentStepIndex,
2445
- defaultProps,
2446
- id,
2447
- steps,
2448
- title,
2449
- width,
2450
- ...extraConfig
1989
+ removeExtension = (surfaceId, extensionId) => {
1990
+ if (!extensionId) return;
1991
+ const sId = surfaceId != null ? String(surfaceId) : "global";
1992
+ const surfaceMap = this.extensionsBySurface.get(sId);
1993
+ if (!surfaceMap || !surfaceMap.has(extensionId)) return;
1994
+ surfaceMap.delete(extensionId);
1995
+ if (surfaceMap.size === 0) this.extensionsBySurface.delete(sId);
1996
+ this.notify();
2451
1997
  };
2452
- stepSurfaceFactory.use = function useDefinedStepSurface() {
2453
- return useSurface(stepSurfaceFactory);
1998
+ clearSurface = (surfaceId) => {
1999
+ const sId = surfaceId != null ? String(surfaceId) : "global";
2000
+ if (!this.extensionsBySurface.has(sId)) return;
2001
+ this.extensionsBySurface.delete(sId);
2002
+ this.notify();
2454
2003
  };
2455
- return stepSurfaceFactory;
2456
- }
2457
- function useSurfaceStep() {
2458
- const { closeSurface, goToStep, popStep, pushStep } = useDockActions();
2459
- const surfaceStack = useDockSelector((s) => s.surfaceStack) || EMPTY_SURFACE_STACK;
2460
- const activeEntry = surfaceStack[surfaceStack.length - 1] || null;
2461
- const steps = Array.isArray(activeEntry?.steps) ? activeEntry.steps : [];
2462
- const stepIndex = Number(activeEntry?.currentStepIndex || 0);
2463
- const stepsTotal = steps.length;
2464
- const currentStep = steps[stepIndex] || null;
2465
- const isFirst = stepIndex <= 0;
2466
- const isLast = stepsTotal > 0 && stepIndex >= stepsTotal - 1;
2467
- const next = useCallback(() => {
2468
- if (!isLast) goToStep(stepIndex + 1);
2469
- }, [goToStep, isLast, stepIndex]);
2470
- const prev = useCallback(() => {
2471
- if (!isFirst) popStep();
2472
- }, [isFirst, popStep]);
2473
- const goTo = useCallback(
2474
- (idx) => {
2475
- goToStep(idx);
2476
- },
2477
- [goToStep]
2478
- );
2479
- const push = useCallback(
2480
- (step) => {
2481
- pushStep(step);
2482
- },
2483
- [pushStep]
2004
+ };
2005
+
2006
+ // src/modules/media/constants.ts
2007
+ var DEFAULT_MEDIA_STATE = Object.freeze({
2008
+ audibleElement: null,
2009
+ element: null,
2010
+ hasMedia: false,
2011
+ isPlaying: false,
2012
+ kind: null,
2013
+ loop: false,
2014
+ sourceId: null
2015
+ });
2016
+ var INERT_MEDIA_ACTIONS = Object.freeze({
2017
+ removeSource: () => {
2018
+ },
2019
+ setMuted: () => {
2020
+ },
2021
+ toggle: () => {
2022
+ },
2023
+ toggleLoop: () => {
2024
+ },
2025
+ upsertSource: () => {
2026
+ }
2027
+ });
2028
+ var MediaContext = getOrCreateGlobalContext(
2029
+ "MediaContext",
2030
+ null
2031
+ );
2032
+ var NOOP_MEDIA_STORE = createStore(DEFAULT_MEDIA_STATE);
2033
+ function useOptionalMediaState(selector, isEqual) {
2034
+ const ctx = useContext(MediaContext);
2035
+ return useStore(
2036
+ ctx?.store ?? NOOP_MEDIA_STORE,
2037
+ selector,
2038
+ isEqual
2484
2039
  );
2485
- return {
2486
- close: closeSurface,
2487
- currentStep,
2488
- goTo,
2489
- isFirst,
2490
- isLast,
2491
- next,
2492
- prev,
2493
- push,
2494
- stepIndex,
2495
- steps,
2496
- stepsTotal
2497
- };
2498
2040
  }
2499
- function createSurfaceError(code, message) {
2500
- const error = new Error(message);
2501
- error.code = code;
2502
- return error;
2041
+ function useOptionalMediaActions() {
2042
+ const ctx = useContext(MediaContext);
2043
+ return ctx?.actions ?? INERT_MEDIA_ACTIONS;
2044
+ }
2045
+
2046
+ // src/modules/loading/constants.ts
2047
+ var DEFAULT_LOADING_STATE = Object.freeze({
2048
+ isLoading: false,
2049
+ skeleton: null,
2050
+ minDuration: 0,
2051
+ showOverlay: true,
2052
+ message: null
2053
+ });
2054
+ var DEFAULT_LOADING_STATE_WITH_PAGE = Object.freeze({
2055
+ ...DEFAULT_LOADING_STATE,
2056
+ isPageLoading: false
2057
+ });
2058
+ var INERT_LOADING_ACTIONS = Object.freeze({
2059
+ setLoading: () => {
2060
+ },
2061
+ setSkeleton: () => {
2062
+ },
2063
+ startLoading: () => {
2064
+ },
2065
+ stopLoading: () => {
2066
+ },
2067
+ withLoading: async (task) => typeof task === "function" ? task() : task
2068
+ });
2069
+ var LoadingContext = getOrCreateGlobalContext("LoadingContext", null);
2070
+ var NOOP_LOADING_STORE = createStore(
2071
+ DEFAULT_LOADING_STATE_WITH_PAGE
2072
+ );
2073
+ function useOptionalLoadingState(selector, isEqual) {
2074
+ const ctx = useContext(LoadingContext);
2075
+ return useStore(
2076
+ ctx?.store ?? NOOP_LOADING_STORE,
2077
+ selector,
2078
+ isEqual
2079
+ );
2503
2080
  }
2504
- function createSurfaceFlowDefinition(input) {
2505
- const id = typeof input?.id === "string" ? input.id.trim() : "";
2506
- if (!id || typeof input?.createSurface !== "function") return null;
2507
- return {
2508
- createSurface: input.createSurface,
2509
- id,
2510
- initialSnapshot: normalizeSurfaceFlowSnapshot(input.initialSnapshot),
2511
- returnHandshake: createSurfaceReturnHandshake(
2512
- input.returnHandshake ?? ("returnTo" in input ? input.returnTo : void 0)
2513
- ),
2514
- restoreFromUrl: input.restoreFromUrl !== false,
2515
- singleton: input.singleton !== false
2516
- };
2081
+ function useOptionalLoadingActions() {
2082
+ const ctx = useContext(LoadingContext);
2083
+ return ctx?.actions ?? INERT_LOADING_ACTIONS;
2517
2084
  }
2518
- function createSurfaceFlowSession(definition, { input = null, snapshot } = {}) {
2519
- if (!definition?.id) return null;
2520
- return {
2521
- flowId: definition.id,
2522
- input,
2523
- returnHandshake: resolveSurfaceFlowReturnHandshake(definition, input),
2524
- snapshot: snapshot === void 0 ? definition.initialSnapshot : normalizeSurfaceFlowSnapshot(snapshot),
2525
- status: DOCK_SURFACE_FLOW_STATUS.OPEN
2526
- };
2085
+ USER_MESSAGES.unauthorized;
2086
+ var INITIAL_NOTIFICATION_STATE = Object.freeze({
2087
+ notifications: {}
2088
+ });
2089
+ var NotificationContext = getOrCreateGlobalContext(
2090
+ "NotificationContext",
2091
+ null
2092
+ );
2093
+ var NOOP_NOTIFICATION_STORE = createStore(
2094
+ INITIAL_NOTIFICATION_STATE
2095
+ );
2096
+ function useOptionalNotificationState(selector, isEqual) {
2097
+ const ctx = useContext(NotificationContext);
2098
+ return useStore(
2099
+ ctx?.store ?? NOOP_NOTIFICATION_STORE,
2100
+ selector,
2101
+ isEqual
2102
+ );
2527
2103
  }
2528
- function updateSurfaceFlowSession(session, snapshot) {
2529
- if (!session?.flowId) return null;
2530
- return { ...session, snapshot: normalizeSurfaceFlowSnapshot(snapshot) };
2104
+ function useNotificationVisible() {
2105
+ return useOptionalNotificationState(
2106
+ ({ notifications }) => Object.keys(notifications).length > 0
2107
+ );
2531
2108
  }
2532
2109
 
2533
2110
  // src/tokens/tokens.ts
@@ -2637,190 +2214,593 @@ Object.freeze({
2637
2214
  duration: DURATION_TOKENS.INSTANT,
2638
2215
  ease: "linear"
2639
2216
  });
2640
- var TAP_SCALE_SUBTLE = 0.97;
2641
- function tween(duration, ease) {
2642
- return Object.freeze({ type: "tween", duration, ease });
2217
+ var TAP_SCALE_SUBTLE = 0.97;
2218
+ function tween(duration, ease) {
2219
+ return Object.freeze({ type: "tween", duration, ease });
2220
+ }
2221
+ function safeNumber(value, fallback) {
2222
+ const source = value && typeof value === "object" && "get" in value && typeof value.get === "function" ? value.get() : value;
2223
+ const number = Number.parseFloat(String(source));
2224
+ return Number.isFinite(number) ? number : fallback;
2225
+ }
2226
+ function gpuTransform(y = 0, scale = 1, x = 0) {
2227
+ const translateY = Math.round(safeNumber(y, 0));
2228
+ const translateX = Math.round(safeNumber(x, 0));
2229
+ const scaleValue = Math.round(safeNumber(scale, 1) * 1e3) / 1e3;
2230
+ return `translate3d(${translateX}px, ${translateY}px, 0) scale(${scaleValue})`;
2231
+ }
2232
+ function durationForDistance(distancePx, {
2233
+ base = DURATION_TOKENS.FAST,
2234
+ min = DURATION_TOKENS.MICRO,
2235
+ max = DURATION_TOKENS.SLOW,
2236
+ reference = 320,
2237
+ gain = 0.22
2238
+ } = {}) {
2239
+ const distance = Math.abs(Number(distancePx) || 0);
2240
+ const safeMin = Math.max(0, min);
2241
+ const safeMax = Math.max(safeMin, max);
2242
+ const safeReference = Math.max(1, reference);
2243
+ const duration = base + Math.sqrt(distance / safeReference) * gain;
2244
+ return Math.min(safeMax, Math.max(safeMin, duration));
2245
+ }
2246
+ function surfaceResizeTransition(distancePx, {
2247
+ base = DURATION_TOKENS.FAST,
2248
+ min = DURATION_TOKENS.MICRO,
2249
+ max = DURATION_TOKENS.SLOW,
2250
+ reference = 320,
2251
+ gain = 0.22,
2252
+ ease = EASING_CURVES.IN_OUT_CUBIC
2253
+ } = {}) {
2254
+ return tween(
2255
+ durationForDistance(distancePx, { base, min, max, reference, gain }),
2256
+ ease
2257
+ );
2258
+ }
2259
+ function contentSwapTransitions({
2260
+ duration = DURATION_TOKENS.FAST,
2261
+ exitDuration = DURATION_TOKENS.MICRO,
2262
+ enterEase = EASING_CURVES.OUT_EXPO,
2263
+ exitEase = EASING_CURVES.IN_CUBIC
2264
+ } = {}) {
2265
+ return Object.freeze({
2266
+ enter: tween(duration, enterEase),
2267
+ exit: tween(exitDuration, exitEase)
2268
+ });
2269
+ }
2270
+ function contentSwap({
2271
+ travel = 6,
2272
+ exitTravel = travel * 0.7,
2273
+ enterScale = 0.99,
2274
+ exitScale = 0.99,
2275
+ ...transitionOptions
2276
+ } = {}) {
2277
+ const transitions = contentSwapTransitions(transitionOptions);
2278
+ return Object.freeze({
2279
+ hidden: Object.freeze({
2280
+ opacity: 0,
2281
+ transform: gpuTransform(travel, enterScale)
2282
+ }),
2283
+ visible: Object.freeze({
2284
+ opacity: 1,
2285
+ transform: gpuTransform(0),
2286
+ transition: transitions.enter
2287
+ }),
2288
+ exit: Object.freeze({
2289
+ opacity: 0,
2290
+ transform: gpuTransform(-exitTravel, exitScale),
2291
+ transition: transitions.exit
2292
+ })
2293
+ });
2294
+ }
2295
+ function overlaySurface({
2296
+ travel = 16,
2297
+ exitTravel = travel * 0.5,
2298
+ enterScale = 0.97,
2299
+ exitScale = 0.985,
2300
+ distance,
2301
+ duration,
2302
+ exitDuration,
2303
+ enterEase = EASING_CURVES.OUT_EXPO,
2304
+ exitEase = EASING_CURVES.IN_CUBIC
2305
+ } = {}) {
2306
+ const enterDuration = duration ?? (distance === void 0 ? DURATION_TOKENS.BASE : durationForDistance(distance));
2307
+ const transitions = contentSwapTransitions({
2308
+ duration: enterDuration,
2309
+ exitDuration: exitDuration ?? enterDuration * 0.68,
2310
+ enterEase,
2311
+ exitEase
2312
+ });
2313
+ return Object.freeze({
2314
+ hidden: Object.freeze({
2315
+ opacity: 0,
2316
+ transform: gpuTransform(travel, enterScale)
2317
+ }),
2318
+ visible: Object.freeze({
2319
+ opacity: 1,
2320
+ transform: gpuTransform(0),
2321
+ transition: transitions.enter
2322
+ }),
2323
+ exit: Object.freeze({
2324
+ opacity: 0,
2325
+ transform: gpuTransform(exitTravel, exitScale),
2326
+ transition: transitions.exit
2327
+ })
2328
+ });
2329
+ }
2330
+ function staggerDelay(index, { base = 0.028, cap = 0.16, exponent = 0.82 } = {}) {
2331
+ const safeIndex = Math.max(0, Number(index) || 0);
2332
+ return Math.min(cap, base * Math.pow(safeIndex, exponent));
2333
+ }
2334
+ function staggerItem({
2335
+ base = 0.028,
2336
+ cap = 0.16,
2337
+ duration = DURATION_TOKENS.BASE,
2338
+ travel = 10,
2339
+ enterEase = EASING_CURVES.OUT_EXPO,
2340
+ exitDuration = DURATION_TOKENS.MICRO,
2341
+ exitEase = EASING_CURVES.IN_CUBIC
2342
+ } = {}) {
2343
+ return Object.freeze({
2344
+ hidden: Object.freeze({
2345
+ opacity: 0,
2346
+ transform: gpuTransform(travel, 0.985)
2347
+ }),
2348
+ visible: (index = 0) => ({
2349
+ opacity: 1,
2350
+ transform: gpuTransform(0),
2351
+ transition: Object.freeze({
2352
+ ...tween(duration, enterEase),
2353
+ delay: staggerDelay(index, { base, cap })
2354
+ })
2355
+ }),
2356
+ exit: Object.freeze({
2357
+ opacity: 0,
2358
+ transform: gpuTransform(-travel * 0.6, 0.99),
2359
+ transition: tween(exitDuration, exitEase)
2360
+ })
2361
+ });
2362
+ }
2363
+ function chip({
2364
+ duration = DURATION_TOKENS.FAST,
2365
+ exitDuration = DURATION_TOKENS.MICRO,
2366
+ enterEase = EASING_CURVES.IN_OUT_CUBIC,
2367
+ exitEase = EASING_CURVES.IN_CUBIC
2368
+ } = {}) {
2369
+ const transitions = contentSwapTransitions({
2370
+ duration,
2371
+ exitDuration,
2372
+ enterEase,
2373
+ exitEase
2374
+ });
2375
+ return Object.freeze({
2376
+ hidden: Object.freeze({ opacity: 0, transform: gpuTransform(0, 0.96) }),
2377
+ visible: Object.freeze({
2378
+ opacity: 1,
2379
+ transform: gpuTransform(0),
2380
+ transition: transitions.enter
2381
+ }),
2382
+ exit: Object.freeze({
2383
+ opacity: 0,
2384
+ transform: gpuTransform(0, 0.96),
2385
+ transition: transitions.exit
2386
+ })
2387
+ });
2388
+ }
2389
+ function press({
2390
+ tapScale = TAP_SCALE_SUBTLE,
2391
+ spring = SPRING_PRESETS.MICRO
2392
+ } = {}) {
2393
+ return Object.freeze({
2394
+ variants: Object.freeze({
2395
+ idle: Object.freeze({ transform: gpuTransform(0, 1) }),
2396
+ hover: Object.freeze({ transform: gpuTransform(0, 1) }),
2397
+ tap: Object.freeze({ transform: gpuTransform(0, tapScale) })
2398
+ }),
2399
+ transition: Object.freeze({ type: "spring", ...spring })
2400
+ });
2401
+ }
2402
+
2403
+ // src/modules/dock/context.ts
2404
+ var DockContext = getOrCreateGlobalContext(
2405
+ "DockContext",
2406
+ null
2407
+ );
2408
+ function useDockState() {
2409
+ const { store } = useRequiredContext(
2410
+ DockContext,
2411
+ "useDockState",
2412
+ "DockProvider"
2413
+ );
2414
+ return useStore(store);
2415
+ }
2416
+ function useDockSelector(selector, isEqual = Object.is) {
2417
+ const { store } = useRequiredContext(
2418
+ DockContext,
2419
+ "useDockSelector",
2420
+ "DockProvider"
2421
+ );
2422
+ return useStore(store, selector, isEqual);
2423
+ }
2424
+ function useDockActions() {
2425
+ return useRequiredContext(DockContext, "useDockActions", "DockProvider").actions;
2426
+ }
2427
+ function useDockHeight() {
2428
+ const dockHeight = useDockSelector((state) => state.dockHeight);
2429
+ return { dockHeight, padding: { paddingBottom: `${dockHeight}px` } };
2430
+ }
2431
+ var EMPTY_DOCK_STATE = Object.freeze({
2432
+ activeItem: null,
2433
+ activeOperation: null,
2434
+ activeSurfaceEntry: null,
2435
+ activeSurfaceId: null,
2436
+ contextActions: [],
2437
+ dockContinuity: [],
2438
+ dockHeight: 0,
2439
+ dockReturnHandoffs: [],
2440
+ expanded: false,
2441
+ hud: null,
2442
+ hudEntries: [],
2443
+ isHudActive: false,
2444
+ isSurfaceOpen: false,
2445
+ locationKey: "",
2446
+ operations: [],
2447
+ pathname: "",
2448
+ searchQuery: "",
2449
+ selectionMode: null,
2450
+ surfaceLifecycle: "",
2451
+ surfacePhase: "",
2452
+ surfaceStack: []
2453
+ });
2454
+ var NOOP_DOCK_STORE = createStore(EMPTY_DOCK_STATE);
2455
+ function useOptionalDockState(selector, isEqual) {
2456
+ const ctx = useContext(DockContext);
2457
+ return useStore(
2458
+ ctx?.store ?? NOOP_DOCK_STORE,
2459
+ selector,
2460
+ isEqual
2461
+ );
2643
2462
  }
2644
- function safeNumber(value, fallback) {
2645
- const source = value && typeof value === "object" && "get" in value && typeof value.get === "function" ? value.get() : value;
2646
- const number = Number.parseFloat(String(source));
2647
- return Number.isFinite(number) ? number : fallback;
2463
+ function useOptionalDockActions() {
2464
+ const ctx = useContext(DockContext);
2465
+ return ctx?.actions ?? null;
2648
2466
  }
2649
- function gpuTransform(y = 0, scale = 1, x = 0) {
2650
- const translateY = Math.round(safeNumber(y, 0));
2651
- const translateX = Math.round(safeNumber(x, 0));
2652
- const scaleValue = Math.round(safeNumber(scale, 1) * 1e3) / 1e3;
2653
- return `translate3d(${translateX}px, ${translateY}px, 0) scale(${scaleValue})`;
2467
+ function useDockMedia() {
2468
+ return useOptionalMediaState((state) => state, shallowEqual);
2654
2469
  }
2655
- function durationForDistance(distancePx, {
2656
- base = DURATION_TOKENS.FAST,
2657
- min = DURATION_TOKENS.MICRO,
2658
- max = DURATION_TOKENS.SLOW,
2659
- reference = 320,
2660
- gain = 0.22
2661
- } = {}) {
2662
- const distance = Math.abs(Number(distancePx) || 0);
2663
- const safeMin = Math.max(0, min);
2664
- const safeMax = Math.max(safeMin, max);
2665
- const safeReference = Math.max(1, reference);
2666
- const duration = base + Math.sqrt(distance / safeReference) * gain;
2667
- return Math.min(safeMax, Math.max(safeMin, duration));
2470
+ function useDockHasMedia() {
2471
+ return useOptionalMediaState((state) => state.hasMedia);
2668
2472
  }
2669
- function surfaceResizeTransition(distancePx, {
2670
- base = DURATION_TOKENS.FAST,
2671
- min = DURATION_TOKENS.MICRO,
2672
- max = DURATION_TOKENS.SLOW,
2673
- reference = 320,
2674
- gain = 0.22,
2675
- ease = EASING_CURVES.IN_OUT_CUBIC
2473
+ var useDockMediaActions = useOptionalMediaActions;
2474
+ function useDockLoadingState() {
2475
+ return useOptionalLoadingState();
2476
+ }
2477
+ var useDockLoadingActions = useOptionalLoadingActions;
2478
+ var useDockNotificationVisible = useNotificationVisible;
2479
+ var statusActionDefaults = { error: null, guard: null };
2480
+
2481
+ // src/modules/dock/surface/definition.ts
2482
+ var isNormalizedExtension = (extension) => extension !== null;
2483
+ function normalizeExtensions(raw) {
2484
+ if (!raw) return [];
2485
+ return (Array.isArray(raw) ? raw : [raw]).map(normalizeSurfaceExtension).filter(isNormalizedExtension);
2486
+ }
2487
+ function normalizeSurfaceDefinition(input, config = {}, {
2488
+ allowPrimitiveContent = false,
2489
+ defaultShowAction = false
2676
2490
  } = {}) {
2677
- return tween(
2678
- durationForDistance(distancePx, { base, min, max, reference, gain }),
2679
- ease
2491
+ const descriptor = isSurfaceDescriptor(input) && (isValidComponentType(input.component) || typeof input.loader === "function" || "content" in input || "node" in input || "element" in input || Array.isArray(input.steps) && input.steps.length > 0) ? input : null;
2492
+ const configuredSteps = descriptor?.steps ?? config?.steps;
2493
+ const steps = Array.isArray(configuredSteps) && configuredSteps.length > 0 ? configuredSteps : null;
2494
+ const firstStep = steps?.[0] ?? null;
2495
+ const rawComponent = resolveComponentType(
2496
+ descriptor?.component,
2497
+ descriptor ? null : input,
2498
+ firstStep?.component,
2499
+ firstStep
2500
+ );
2501
+ const component = rawComponent ?? (typeof descriptor?.loader === "function" ? lazy(descriptor.loader) : typeof firstStep?.loader === "function" ? lazy(firstStep.loader) : null);
2502
+ const explicitContent = resolveRenderableContent(
2503
+ descriptor?.content,
2504
+ descriptor?.node,
2505
+ descriptor?.element,
2506
+ firstStep?.content,
2507
+ firstStep?.node,
2508
+ firstStep?.element
2509
+ );
2510
+ const fallbackContent = !descriptor && !component && (isValidElement(input) || allowPrimitiveContent && input != null) ? input : null;
2511
+ const content = explicitContent ?? fallbackContent;
2512
+ if (!component && content == null && !steps) return null;
2513
+ const directComponentInput = !descriptor && isValidComponentType(input);
2514
+ const extensions = normalizeExtensions(
2515
+ descriptor?.extensions ?? config?.extensions
2516
+ );
2517
+ return {
2518
+ renderMode: component ? DOCK_SURFACE_RENDER_MODE.COMPONENT : DOCK_SURFACE_RENDER_MODE.NODE,
2519
+ component,
2520
+ content: component ? null : content ?? input,
2521
+ props: component ? isPlainObject(descriptor?.props) ? descriptor.props : directComponentInput ? config : {} : {},
2522
+ action: descriptor?.action ?? config?.action ?? null,
2523
+ showAction: descriptor?.showAction ?? config?.showAction ?? defaultShowAction,
2524
+ dismissible: descriptor?.dismissible ?? config?.dismissible ?? true,
2525
+ onClose: descriptor?.onClose ?? config?.onClose ?? null,
2526
+ icon: descriptor?.icon ?? descriptor?.header?.icon ?? config?.icon ?? config?.header?.icon ?? null,
2527
+ title: descriptor?.title ?? descriptor?.header?.title ?? config?.title ?? config?.header?.title ?? null,
2528
+ description: descriptor?.description ?? descriptor?.header?.description ?? config?.description ?? config?.header?.description ?? null,
2529
+ descriptionMaxLines: descriptor?.descriptionMaxLines ?? config?.descriptionMaxLines ?? 2,
2530
+ trailing: descriptor?.trailing ?? config?.trailing ?? null,
2531
+ headerAction: descriptor?.headerAction ?? config?.headerAction ?? null,
2532
+ closeLabel: descriptor?.closeLabel ?? config?.closeLabel ?? null,
2533
+ width: descriptor?.width ?? config?.width ?? null,
2534
+ allowSwipeDismiss: descriptor?.allowSwipeDismiss ?? config?.allowSwipeDismiss ?? true,
2535
+ skipActionDismiss: descriptor?.skipActionDismiss ?? config?.skipActionDismiss ?? true,
2536
+ steps,
2537
+ currentStepIndex: descriptor?.currentStepIndex ?? config?.currentStepIndex ?? 0,
2538
+ syncWithUrl: descriptor?.syncWithUrl ?? config?.syncWithUrl ?? false,
2539
+ urlKey: descriptor?.urlKey ?? config?.urlKey ?? null,
2540
+ badge: descriptor?.badge ?? config?.badge ?? null,
2541
+ extensions
2542
+ };
2543
+ }
2544
+ function createSurfaceEntryDefinition(input, config = {}) {
2545
+ return normalizeSurfaceDefinition(input, config);
2546
+ }
2547
+ function createSurfaceFlowBuilder(definition = {}) {
2548
+ const {
2549
+ action = null,
2550
+ allowSwipeDismiss = true,
2551
+ badge = null,
2552
+ closeLabel = null,
2553
+ component = null,
2554
+ loader,
2555
+ defaultProps = {},
2556
+ description = null,
2557
+ descriptionMaxLines = 2,
2558
+ dismissible = true,
2559
+ extensions = [],
2560
+ headerAction = null,
2561
+ icon = null,
2562
+ id = null,
2563
+ onClose = null,
2564
+ showAction = null,
2565
+ syncWithUrl = false,
2566
+ title = null,
2567
+ trailing = null,
2568
+ urlKey = null,
2569
+ width = null,
2570
+ ...extraConfig
2571
+ } = definition;
2572
+ const resolvedComponent = component ?? (typeof loader === "function" ? lazy(loader) : null);
2573
+ const createEntry = function surfaceEntryFactory2(props = {}, overrides = {}) {
2574
+ const mergedProps = { ...defaultProps, ...props };
2575
+ const resolvedTitle = typeof title === "function" ? title(mergedProps) : title;
2576
+ const resolvedDescription = typeof description === "function" ? description(mergedProps) : description;
2577
+ const resolvedIcon = typeof icon === "function" ? icon(mergedProps) : icon;
2578
+ return {
2579
+ id: overrides.id ?? id,
2580
+ component: overrides.component ?? resolvedComponent,
2581
+ loader: overrides.loader ?? loader,
2582
+ props: mergedProps,
2583
+ title: resolvedTitle,
2584
+ description: resolvedDescription,
2585
+ icon: resolvedIcon,
2586
+ action,
2587
+ allowSwipeDismiss,
2588
+ badge,
2589
+ closeLabel,
2590
+ descriptionMaxLines,
2591
+ dismissible,
2592
+ extensions,
2593
+ headerAction,
2594
+ onClose,
2595
+ showAction,
2596
+ syncWithUrl,
2597
+ trailing,
2598
+ urlKey,
2599
+ width,
2600
+ ...extraConfig,
2601
+ ...overrides
2602
+ };
2603
+ };
2604
+ const surfaceEntryFactory = Object.assign(
2605
+ createEntry,
2606
+ {
2607
+ component: resolvedComponent,
2608
+ id,
2609
+ isSurfaceFactory: true,
2610
+ open: (openSurfaceFn, props = {}, overrides = {}) => {
2611
+ if (typeof openSurfaceFn === "function") {
2612
+ return openSurfaceFn(createEntry(props, overrides));
2613
+ }
2614
+ }
2615
+ }
2616
+ );
2617
+ return surfaceEntryFactory;
2618
+ }
2619
+ function createInlineSurfaceEntry(surface) {
2620
+ return normalizeSurfaceDefinition(
2621
+ surface,
2622
+ {},
2623
+ { allowPrimitiveContent: true, defaultShowAction: null }
2624
+ );
2625
+ }
2626
+ var EMPTY_SURFACE_STACK = [];
2627
+ function useSurface(surfaceDefinition) {
2628
+ const { closeAllSurfaces, closeSurface, openSurface } = useDockActions();
2629
+ const rawSurfaceStack = useDockSelector(
2630
+ (s) => s.surfaceStack
2631
+ );
2632
+ const surfaceStack = rawSurfaceStack || EMPTY_SURFACE_STACK;
2633
+ const specificSurface = useMemo(() => {
2634
+ if (!surfaceDefinition) return null;
2635
+ const targetComponent = typeof surfaceDefinition === "string" ? null : typeof surfaceDefinition === "function" ? surfaceDefinition.component || surfaceDefinition : surfaceDefinition.component || null;
2636
+ const matchingEntry = surfaceStack.find(
2637
+ (entry) => targetComponent && entry.component === targetComponent
2638
+ ) ?? null;
2639
+ const isThisSurfaceOpen = Boolean(matchingEntry);
2640
+ const open = (props, overrides) => {
2641
+ const entry = typeof surfaceDefinition === "function" ? surfaceDefinition(props, overrides) : typeof surfaceDefinition === "string" ? { id: surfaceDefinition, props, ...overrides } : {
2642
+ ...surfaceDefinition,
2643
+ props: { ...surfaceDefinition.props || {}, ...props },
2644
+ ...overrides
2645
+ };
2646
+ return openSurface(entry);
2647
+ };
2648
+ const close = (res) => closeSurface(res, matchingEntry?.id ?? null);
2649
+ const result = [
2650
+ open,
2651
+ {
2652
+ close,
2653
+ closeAll: closeAllSurfaces,
2654
+ isOpen: isThisSurfaceOpen,
2655
+ surfaceStack
2656
+ }
2657
+ ];
2658
+ result.open = open;
2659
+ result.close = close;
2660
+ result.closeAll = closeAllSurfaces;
2661
+ result.isOpen = isThisSurfaceOpen;
2662
+ result.surfaceStack = surfaceStack;
2663
+ return result;
2664
+ }, [
2665
+ closeAllSurfaces,
2666
+ closeSurface,
2667
+ openSurface,
2668
+ surfaceDefinition,
2669
+ surfaceStack
2670
+ ]);
2671
+ const generalSurface = useMemo(
2672
+ () => ({
2673
+ closeAllSurfaces,
2674
+ closeSurface,
2675
+ openSurface,
2676
+ surfaceStack
2677
+ }),
2678
+ [closeAllSurfaces, closeSurface, openSurface, surfaceStack]
2680
2679
  );
2680
+ return surfaceDefinition ? specificSurface : generalSurface;
2681
2681
  }
2682
- function contentSwapTransitions({
2683
- duration = DURATION_TOKENS.FAST,
2684
- exitDuration = DURATION_TOKENS.MICRO,
2685
- enterEase = EASING_CURVES.OUT_EXPO,
2686
- exitEase = EASING_CURVES.IN_CUBIC
2687
- } = {}) {
2688
- return Object.freeze({
2689
- enter: tween(duration, enterEase),
2690
- exit: tween(exitDuration, exitEase)
2691
- });
2682
+ function defineSurface(definition = {}) {
2683
+ const factory = createSurfaceFlowBuilder(definition);
2684
+ factory.config = definition;
2685
+ factory.use = function useDefinedSurface() {
2686
+ return useSurface(factory);
2687
+ };
2688
+ return factory;
2692
2689
  }
2693
- function contentSwap({
2694
- travel = 6,
2695
- exitTravel = travel * 0.7,
2696
- enterScale = 0.99,
2697
- exitScale = 0.99,
2698
- ...transitionOptions
2699
- } = {}) {
2700
- const transitions = contentSwapTransitions(transitionOptions);
2701
- return Object.freeze({
2702
- hidden: Object.freeze({
2703
- opacity: 0,
2704
- transform: gpuTransform(travel, enterScale)
2705
- }),
2706
- visible: Object.freeze({
2707
- opacity: 1,
2708
- transform: gpuTransform(0),
2709
- transition: transitions.enter
2710
- }),
2711
- exit: Object.freeze({
2712
- opacity: 0,
2713
- transform: gpuTransform(-exitTravel, exitScale),
2714
- transition: transitions.exit
2715
- })
2716
- });
2690
+ function defineStepSurface(definition = {}) {
2691
+ const {
2692
+ currentStepIndex = 0,
2693
+ defaultProps = {},
2694
+ id = "wizard-surface",
2695
+ steps = [],
2696
+ title = null,
2697
+ width = null,
2698
+ ...extraConfig
2699
+ } = definition;
2700
+ const stepSurfaceFactory = function stepSurfaceFactory2(props = {}, overrides = {}) {
2701
+ const mergedProps = { ...defaultProps, ...props };
2702
+ const resolvedTitle = typeof title === "function" ? title(mergedProps) : title;
2703
+ const resolvedSteps = typeof steps === "function" ? steps(mergedProps) : steps;
2704
+ return {
2705
+ id: overrides.id ?? id,
2706
+ title: overrides.title ?? resolvedTitle,
2707
+ steps: resolvedSteps,
2708
+ currentStepIndex: overrides.currentStepIndex ?? currentStepIndex,
2709
+ props: mergedProps,
2710
+ width: overrides.width ?? width,
2711
+ ...extraConfig,
2712
+ ...overrides
2713
+ };
2714
+ };
2715
+ stepSurfaceFactory.id = id;
2716
+ stepSurfaceFactory.config = {
2717
+ currentStepIndex,
2718
+ defaultProps,
2719
+ id,
2720
+ steps,
2721
+ title,
2722
+ width,
2723
+ ...extraConfig
2724
+ };
2725
+ stepSurfaceFactory.use = function useDefinedStepSurface() {
2726
+ return useSurface(stepSurfaceFactory);
2727
+ };
2728
+ return stepSurfaceFactory;
2717
2729
  }
2718
- function overlaySurface({
2719
- travel = 16,
2720
- exitTravel = travel * 0.5,
2721
- enterScale = 0.97,
2722
- exitScale = 0.985,
2723
- distance,
2724
- duration,
2725
- exitDuration,
2726
- enterEase = EASING_CURVES.OUT_EXPO,
2727
- exitEase = EASING_CURVES.IN_CUBIC
2728
- } = {}) {
2729
- const enterDuration = duration ?? (distance === void 0 ? DURATION_TOKENS.BASE : durationForDistance(distance));
2730
- const transitions = contentSwapTransitions({
2731
- duration: enterDuration,
2732
- exitDuration: exitDuration ?? enterDuration * 0.68,
2733
- enterEase,
2734
- exitEase
2735
- });
2736
- return Object.freeze({
2737
- hidden: Object.freeze({
2738
- opacity: 0,
2739
- transform: gpuTransform(travel, enterScale)
2740
- }),
2741
- visible: Object.freeze({
2742
- opacity: 1,
2743
- transform: gpuTransform(0),
2744
- transition: transitions.enter
2745
- }),
2746
- exit: Object.freeze({
2747
- opacity: 0,
2748
- transform: gpuTransform(exitTravel, exitScale),
2749
- transition: transitions.exit
2750
- })
2751
- });
2730
+ function useSurfaceStep() {
2731
+ const { closeSurface, goToStep, popStep, pushStep } = useDockActions();
2732
+ const surfaceStack = useDockSelector((s) => s.surfaceStack) || EMPTY_SURFACE_STACK;
2733
+ const activeEntry = surfaceStack[surfaceStack.length - 1] || null;
2734
+ const steps = Array.isArray(activeEntry?.steps) ? activeEntry.steps : [];
2735
+ const stepIndex = Number(activeEntry?.currentStepIndex || 0);
2736
+ const stepsTotal = steps.length;
2737
+ const currentStep = steps[stepIndex] || null;
2738
+ const isFirst = stepIndex <= 0;
2739
+ const isLast = stepsTotal > 0 && stepIndex >= stepsTotal - 1;
2740
+ const next = useCallback(() => {
2741
+ if (!isLast) goToStep(stepIndex + 1);
2742
+ }, [goToStep, isLast, stepIndex]);
2743
+ const prev = useCallback(() => {
2744
+ if (!isFirst) popStep();
2745
+ }, [isFirst, popStep]);
2746
+ const goTo = useCallback(
2747
+ (idx) => {
2748
+ goToStep(idx);
2749
+ },
2750
+ [goToStep]
2751
+ );
2752
+ const push = useCallback(
2753
+ (step) => {
2754
+ pushStep(step);
2755
+ },
2756
+ [pushStep]
2757
+ );
2758
+ return {
2759
+ close: closeSurface,
2760
+ currentStep,
2761
+ goTo,
2762
+ isFirst,
2763
+ isLast,
2764
+ next,
2765
+ prev,
2766
+ push,
2767
+ stepIndex,
2768
+ steps,
2769
+ stepsTotal
2770
+ };
2752
2771
  }
2753
- function staggerDelay(index, { base = 0.028, cap = 0.16, exponent = 0.82 } = {}) {
2754
- const safeIndex = Math.max(0, Number(index) || 0);
2755
- return Math.min(cap, base * Math.pow(safeIndex, exponent));
2772
+ function createSurfaceError(code, message) {
2773
+ const error = new Error(message);
2774
+ error.code = code;
2775
+ return error;
2756
2776
  }
2757
- function staggerItem({
2758
- base = 0.028,
2759
- cap = 0.16,
2760
- duration = DURATION_TOKENS.BASE,
2761
- travel = 10,
2762
- enterEase = EASING_CURVES.OUT_EXPO,
2763
- exitDuration = DURATION_TOKENS.MICRO,
2764
- exitEase = EASING_CURVES.IN_CUBIC
2765
- } = {}) {
2766
- return Object.freeze({
2767
- hidden: Object.freeze({
2768
- opacity: 0,
2769
- transform: gpuTransform(travel, 0.985)
2770
- }),
2771
- visible: (index = 0) => ({
2772
- opacity: 1,
2773
- transform: gpuTransform(0),
2774
- transition: Object.freeze({
2775
- ...tween(duration, enterEase),
2776
- delay: staggerDelay(index, { base, cap })
2777
- })
2778
- }),
2779
- exit: Object.freeze({
2780
- opacity: 0,
2781
- transform: gpuTransform(-travel * 0.6, 0.99),
2782
- transition: tween(exitDuration, exitEase)
2783
- })
2784
- });
2777
+ function createSurfaceFlowDefinition(input) {
2778
+ const id = typeof input?.id === "string" ? input.id.trim() : "";
2779
+ if (!id || typeof input?.createSurface !== "function") return null;
2780
+ return {
2781
+ createSurface: input.createSurface,
2782
+ id,
2783
+ initialSnapshot: normalizeSurfaceFlowSnapshot(input.initialSnapshot),
2784
+ returnHandshake: createSurfaceReturnHandshake(
2785
+ input.returnHandshake ?? ("returnTo" in input ? input.returnTo : void 0)
2786
+ ),
2787
+ restoreFromUrl: input.restoreFromUrl !== false,
2788
+ singleton: input.singleton !== false
2789
+ };
2785
2790
  }
2786
- function chip({
2787
- duration = DURATION_TOKENS.FAST,
2788
- exitDuration = DURATION_TOKENS.MICRO,
2789
- enterEase = EASING_CURVES.IN_OUT_CUBIC,
2790
- exitEase = EASING_CURVES.IN_CUBIC
2791
- } = {}) {
2792
- const transitions = contentSwapTransitions({
2793
- duration,
2794
- exitDuration,
2795
- enterEase,
2796
- exitEase
2797
- });
2798
- return Object.freeze({
2799
- hidden: Object.freeze({ opacity: 0, transform: gpuTransform(0, 0.96) }),
2800
- visible: Object.freeze({
2801
- opacity: 1,
2802
- transform: gpuTransform(0),
2803
- transition: transitions.enter
2804
- }),
2805
- exit: Object.freeze({
2806
- opacity: 0,
2807
- transform: gpuTransform(0, 0.96),
2808
- transition: transitions.exit
2809
- })
2810
- });
2791
+ function createSurfaceFlowSession(definition, { input = null, snapshot } = {}) {
2792
+ if (!definition?.id) return null;
2793
+ return {
2794
+ flowId: definition.id,
2795
+ input,
2796
+ returnHandshake: resolveSurfaceFlowReturnHandshake(definition, input),
2797
+ snapshot: snapshot === void 0 ? definition.initialSnapshot : normalizeSurfaceFlowSnapshot(snapshot),
2798
+ status: DOCK_SURFACE_FLOW_STATUS.OPEN
2799
+ };
2811
2800
  }
2812
- function press({
2813
- tapScale = TAP_SCALE_SUBTLE,
2814
- spring = SPRING_PRESETS.MICRO
2815
- } = {}) {
2816
- return Object.freeze({
2817
- variants: Object.freeze({
2818
- idle: Object.freeze({ transform: gpuTransform(0, 1) }),
2819
- hover: Object.freeze({ transform: gpuTransform(0, 1) }),
2820
- tap: Object.freeze({ transform: gpuTransform(0, tapScale) })
2821
- }),
2822
- transition: Object.freeze({ type: "spring", ...spring })
2823
- });
2801
+ function updateSurfaceFlowSession(session, snapshot) {
2802
+ if (!session?.flowId) return null;
2803
+ return { ...session, snapshot: normalizeSurfaceFlowSnapshot(snapshot) };
2824
2804
  }
2825
2805
 
2826
2806
  // src/modules/dock/motion.ts
@@ -12106,6 +12086,6 @@ var dockModule = defineModule({
12106
12086
  }
12107
12087
  });
12108
12088
 
12109
- export { DOCK_EVENTS, DOCK_FADE_TRANSITION, DOCK_HUD_PRIORITY, DOCK_HUD_RENDER_MODE, DOCK_HUD_VARIANT, Dock, DockCardBanner, DockCardHeader, DockDescription, DockIcon, DockProvider, DockSurfaceAction, DockSurfaceExtension, DockSurfaceHeaderButton, DockTitle, ErrorActions, GuardActions, createDockGuardRegistry, createErrorStatus, createGuardStatus, createInlineSurfaceEntry, createSurfaceEntryDefinition, createSurfaceFlowBuilder, defineBreadcrumb, defineDockAction, defineHud, defineStepSurface, defineSurface, dockFadeVariants, dockListItemVariants, dockModule, dockTheme, isValidBannerUrl, textCrossfadeVariants, useDock, useDockActionClass, useDockActions, useDockBanner, useDockConfig, useDockContextActions, useDockDimensions, useDockGuard, useDockHeight, useDockHud, useDockRegistration, useDockSelector, useDockState, useHud, useSurface, useSurfaceAction, useSurfaceDimensions, useSurfaceFlow, useSurfaceHeader, useSurfaceId, useSurfaceReturn, useSurfaceStep };
12089
+ export { DOCK_EVENTS, DOCK_FADE_TRANSITION, DOCK_HUD_PRIORITY, DOCK_HUD_RENDER_MODE, DOCK_HUD_VARIANT, Dock, DockCardBanner, DockCardHeader, DockDescription, DockIcon, DockProvider, DockSurfaceAction, DockSurfaceExtension, DockSurfaceHeaderButton, DockTitle, ErrorActions, GuardActions, createDockGuardRegistry, createErrorStatus, createGuardStatus, createInlineSurfaceEntry, createSurfaceEntryDefinition, createSurfaceFlowBuilder, defineBreadcrumb, defineDockAction, defineHud, defineStepSurface, defineSurface, dockFadeVariants, dockListItemVariants, dockModule, dockTheme, isValidBannerUrl, textCrossfadeVariants, useDock, useDockActionClass, useDockActions, useDockBanner, useDockConfig, useDockContextActions, useDockDimensions, useDockGuard, useDockHeight, useDockHud, useDockRegistration, useDockSelector, useDockState, useHud, useOptionalDockActions, useOptionalDockState, useSurface, useSurfaceAction, useSurfaceDimensions, useSurfaceFlow, useSurfaceHeader, useSurfaceId, useSurfaceReturn, useSurfaceStep };
12110
12090
  //# sourceMappingURL=index.js.map
12111
12091
  //# sourceMappingURL=index.js.map