@omerdlw/base-framework 1.0.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/dist/atoms/index.js +1 -103
  2. package/dist/atoms/index.js.map +1 -1
  3. package/dist/chunk-3BUP64Q6.js +105 -0
  4. package/dist/chunk-3BUP64Q6.js.map +1 -0
  5. package/dist/chunk-3MSK4XGK.js +9888 -0
  6. package/dist/chunk-3MSK4XGK.js.map +1 -0
  7. package/dist/chunk-4GMQKD6F.js +353 -0
  8. package/dist/chunk-4GMQKD6F.js.map +1 -0
  9. package/dist/chunk-65ZB4QTQ.js +888 -0
  10. package/dist/chunk-65ZB4QTQ.js.map +1 -0
  11. package/dist/chunk-6SG7V77J.js +577 -0
  12. package/dist/chunk-6SG7V77J.js.map +1 -0
  13. package/dist/chunk-7HQMXTNP.js +419 -0
  14. package/dist/chunk-7HQMXTNP.js.map +1 -0
  15. package/dist/chunk-7TAVYJ3A.js +371 -0
  16. package/dist/chunk-7TAVYJ3A.js.map +1 -0
  17. package/dist/chunk-AQ4BNXA2.js +349 -0
  18. package/dist/chunk-AQ4BNXA2.js.map +1 -0
  19. package/dist/chunk-CGHZSSFR.js +2166 -0
  20. package/dist/chunk-CGHZSSFR.js.map +1 -0
  21. package/dist/chunk-DGCQWQLZ.js +61 -0
  22. package/dist/chunk-DGCQWQLZ.js.map +1 -0
  23. package/dist/chunk-FIMPHODX.js +67 -0
  24. package/dist/chunk-FIMPHODX.js.map +1 -0
  25. package/dist/chunk-GF7TUX5I.js +1889 -0
  26. package/dist/chunk-GF7TUX5I.js.map +1 -0
  27. package/dist/chunk-JELG4JLT.js +454 -0
  28. package/dist/chunk-JELG4JLT.js.map +1 -0
  29. package/dist/chunk-K676GL7Z.js +51 -0
  30. package/dist/chunk-K676GL7Z.js.map +1 -0
  31. package/dist/chunk-KKQT46TP.js +26 -0
  32. package/dist/chunk-KKQT46TP.js.map +1 -0
  33. package/dist/chunk-QI3ALLIB.js +38 -0
  34. package/dist/chunk-QI3ALLIB.js.map +1 -0
  35. package/dist/chunk-QXR55VVS.js +1139 -0
  36. package/dist/chunk-QXR55VVS.js.map +1 -0
  37. package/dist/chunk-R2YGFJDJ.js +408 -0
  38. package/dist/chunk-R2YGFJDJ.js.map +1 -0
  39. package/dist/chunk-RFZQWLC5.js +27 -0
  40. package/dist/chunk-RFZQWLC5.js.map +1 -0
  41. package/dist/chunk-SYAVGTOX.js +521 -0
  42. package/dist/chunk-SYAVGTOX.js.map +1 -0
  43. package/dist/chunk-TV3WEF5G.js +784 -0
  44. package/dist/chunk-TV3WEF5G.js.map +1 -0
  45. package/dist/chunk-XASQT4CQ.js +280 -0
  46. package/dist/chunk-XASQT4CQ.js.map +1 -0
  47. package/dist/error/index.js +5 -682
  48. package/dist/error/index.js.map +1 -1
  49. package/dist/error/reporter.d.ts.map +1 -1
  50. package/dist/error/theme.d.ts +1 -1
  51. package/dist/error/theme.d.ts.map +1 -1
  52. package/dist/events.js +1 -7
  53. package/dist/events.js.map +1 -1
  54. package/dist/hooks/index.js +1 -358
  55. package/dist/hooks/index.js.map +1 -1
  56. package/dist/hooks/use-global-event.d.ts +1 -1
  57. package/dist/index.js +18 -20019
  58. package/dist/index.js.map +1 -1
  59. package/dist/kernel/index.js +2 -2035
  60. package/dist/kernel/index.js.map +1 -1
  61. package/dist/kernel/types.d.ts +1 -1
  62. package/dist/modules/ambient/analyze.d.ts +46 -0
  63. package/dist/modules/ambient/analyze.d.ts.map +1 -0
  64. package/dist/modules/ambient/constants.d.ts +1 -5
  65. package/dist/modules/ambient/constants.d.ts.map +1 -1
  66. package/dist/modules/ambient/context.d.ts.map +1 -1
  67. package/dist/modules/ambient/dom.d.ts +11 -12
  68. package/dist/modules/ambient/dom.d.ts.map +1 -1
  69. package/dist/modules/ambient/extractor.d.ts +12 -4
  70. package/dist/modules/ambient/extractor.d.ts.map +1 -1
  71. package/dist/modules/ambient/hooks.d.ts +1 -1
  72. package/dist/modules/ambient/hooks.d.ts.map +1 -1
  73. package/dist/modules/ambient/index.d.ts +6 -4
  74. package/dist/modules/ambient/index.d.ts.map +1 -1
  75. package/dist/modules/ambient/index.js +7 -753
  76. package/dist/modules/ambient/index.js.map +1 -1
  77. package/dist/modules/ambient/module.d.ts +1 -1
  78. package/dist/modules/ambient/module.d.ts.map +1 -1
  79. package/dist/modules/ambient/oklab.d.ts +36 -0
  80. package/dist/modules/ambient/oklab.d.ts.map +1 -0
  81. package/dist/modules/ambient/pipeline.d.ts +28 -0
  82. package/dist/modules/ambient/pipeline.d.ts.map +1 -0
  83. package/dist/modules/ambient/quantize.d.ts +22 -0
  84. package/dist/modules/ambient/quantize.d.ts.map +1 -0
  85. package/dist/modules/ambient/server/address.d.ts +8 -0
  86. package/dist/modules/ambient/server/address.d.ts.map +1 -0
  87. package/dist/modules/ambient/server/index.d.ts +53 -0
  88. package/dist/modules/ambient/server/index.d.ts.map +1 -0
  89. package/dist/modules/ambient/server/index.js +221 -0
  90. package/dist/modules/ambient/server/index.js.map +1 -0
  91. package/dist/modules/ambient/settings.d.ts +19 -0
  92. package/dist/modules/ambient/settings.d.ts.map +1 -0
  93. package/dist/modules/ambient/types.d.ts +105 -9
  94. package/dist/modules/ambient/types.d.ts.map +1 -1
  95. package/dist/modules/ambient/worker/index.d.ts +15 -0
  96. package/dist/modules/ambient/worker/index.d.ts.map +1 -0
  97. package/dist/modules/ambient/worker/index.js +114 -0
  98. package/dist/modules/ambient/worker/index.js.map +1 -0
  99. package/dist/modules/background/constants.d.ts +2 -2
  100. package/dist/modules/background/constants.d.ts.map +1 -1
  101. package/dist/modules/background/hooks.d.ts +1 -1
  102. package/dist/modules/background/index.js +6 -3078
  103. package/dist/modules/background/index.js.map +1 -1
  104. package/dist/modules/background/motion.d.ts +9 -7
  105. package/dist/modules/background/motion.d.ts.map +1 -1
  106. package/dist/modules/background/overlay.d.ts +3 -8
  107. package/dist/modules/background/overlay.d.ts.map +1 -1
  108. package/dist/modules/background/types.d.ts +16 -2
  109. package/dist/modules/background/types.d.ts.map +1 -1
  110. package/dist/modules/context-menu/constants.d.ts +2 -2
  111. package/dist/modules/context-menu/constants.d.ts.map +1 -1
  112. package/dist/modules/context-menu/hooks.d.ts +1 -1
  113. package/dist/modules/context-menu/index.js +10 -1896
  114. package/dist/modules/context-menu/index.js.map +1 -1
  115. package/dist/modules/context-menu/motion.d.ts +1 -8
  116. package/dist/modules/context-menu/motion.d.ts.map +1 -1
  117. package/dist/modules/context-menu/overlay.d.ts.map +1 -1
  118. package/dist/modules/context-menu/types.d.ts +16 -2
  119. package/dist/modules/context-menu/types.d.ts.map +1 -1
  120. package/dist/modules/controls/constants.d.ts +1 -1
  121. package/dist/modules/controls/constants.d.ts.map +1 -1
  122. package/dist/modules/controls/entries.d.ts +1 -1
  123. package/dist/modules/controls/hooks.d.ts +1 -1
  124. package/dist/modules/controls/index.js +4 -1110
  125. package/dist/modules/controls/index.js.map +1 -1
  126. package/dist/modules/controls/overlay.d.ts +1 -1
  127. package/dist/modules/controls/types.d.ts +2 -2
  128. package/dist/modules/dock/cards.d.ts +708 -24
  129. package/dist/modules/dock/cards.d.ts.map +1 -1
  130. package/dist/modules/dock/constants.d.ts +2 -2
  131. package/dist/modules/dock/constants.d.ts.map +1 -1
  132. package/dist/modules/dock/helpers.d.ts +2 -2
  133. package/dist/modules/dock/hooks.d.ts +2 -2
  134. package/dist/modules/dock/index.d.ts +1 -1
  135. package/dist/modules/dock/index.d.ts.map +1 -1
  136. package/dist/modules/dock/index.js +9 -12130
  137. package/dist/modules/dock/index.js.map +1 -1
  138. package/dist/modules/dock/module.d.ts +1 -1
  139. package/dist/modules/dock/motion.d.ts +41 -915
  140. package/dist/modules/dock/motion.d.ts.map +1 -1
  141. package/dist/modules/dock/overlay.d.ts.map +1 -1
  142. package/dist/modules/dock/runtime/dock.d.ts +1 -23
  143. package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
  144. package/dist/modules/dock/styles.d.ts +4 -4
  145. package/dist/modules/dock/surface/context.d.ts.map +1 -1
  146. package/dist/modules/dock/surface/hooks.d.ts +10 -23
  147. package/dist/modules/dock/surface/hooks.d.ts.map +1 -1
  148. package/dist/modules/dock/surface/machine.d.ts +4 -3
  149. package/dist/modules/dock/surface/machine.d.ts.map +1 -1
  150. package/dist/modules/dock/surface/stack.d.ts.map +1 -1
  151. package/dist/modules/dock/types.d.ts +158 -3
  152. package/dist/modules/dock/types.d.ts.map +1 -1
  153. package/dist/modules/index.js +16 -17873
  154. package/dist/modules/index.js.map +1 -1
  155. package/dist/modules/loading/constants.d.ts +1 -1
  156. package/dist/modules/loading/constants.d.ts.map +1 -1
  157. package/dist/modules/loading/context.d.ts +1 -1
  158. package/dist/modules/loading/hooks.d.ts +1 -1
  159. package/dist/modules/loading/index.js +5 -1153
  160. package/dist/modules/loading/index.js.map +1 -1
  161. package/dist/modules/loading/types.d.ts +2 -2
  162. package/dist/modules/media/context.d.ts +1 -1
  163. package/dist/modules/media/index.js +3 -570
  164. package/dist/modules/media/index.js.map +1 -1
  165. package/dist/modules/media/types.d.ts +2 -2
  166. package/dist/modules/modal/constants.d.ts +2 -2
  167. package/dist/modules/modal/constants.d.ts.map +1 -1
  168. package/dist/modules/modal/context.d.ts +1 -1
  169. package/dist/modules/modal/hooks.d.ts +3 -3
  170. package/dist/modules/modal/index.js +8 -2355
  171. package/dist/modules/modal/index.js.map +1 -1
  172. package/dist/modules/modal/motion.d.ts +3 -8
  173. package/dist/modules/modal/motion.d.ts.map +1 -1
  174. package/dist/modules/modal/overlay.d.ts.map +1 -1
  175. package/dist/modules/modal/types.d.ts +21 -0
  176. package/dist/modules/modal/types.d.ts.map +1 -1
  177. package/dist/modules/notification/constants.d.ts +2 -2
  178. package/dist/modules/notification/constants.d.ts.map +1 -1
  179. package/dist/modules/notification/context.d.ts +1 -1
  180. package/dist/modules/notification/hooks.d.ts +1 -1
  181. package/dist/modules/notification/index.js +4 -828
  182. package/dist/modules/notification/index.js.map +1 -1
  183. package/dist/modules/notification/motion.d.ts +1 -7
  184. package/dist/modules/notification/motion.d.ts.map +1 -1
  185. package/dist/modules/notification/overlay.d.ts.map +1 -1
  186. package/dist/modules/notification/types.d.ts +9 -1
  187. package/dist/modules/notification/types.d.ts.map +1 -1
  188. package/dist/overlay-TZ6L52A5.js +9 -0
  189. package/dist/overlay-TZ6L52A5.js.map +1 -0
  190. package/dist/provider.d.ts +1 -1
  191. package/dist/provider.d.ts.map +1 -1
  192. package/dist/provider.js +6 -1968
  193. package/dist/provider.js.map +1 -1
  194. package/dist/result.js +2 -63
  195. package/dist/result.js.map +1 -1
  196. package/dist/theme.d.ts +45 -8
  197. package/dist/theme.d.ts.map +1 -1
  198. package/dist/theme.js +3 -41
  199. package/dist/theme.js.map +1 -1
  200. package/dist/tokens/index.d.ts +1 -0
  201. package/dist/tokens/index.d.ts.map +1 -1
  202. package/dist/tokens/index.js +126 -155
  203. package/dist/tokens/index.js.map +1 -1
  204. package/dist/tokens/motion-config.d.ts +44 -0
  205. package/dist/tokens/motion-config.d.ts.map +1 -0
  206. package/dist/tokens/motion.d.ts +23 -174
  207. package/dist/tokens/motion.d.ts.map +1 -1
  208. package/dist/utils/index.js +4 -82
  209. package/dist/utils/index.js.map +1 -1
  210. package/dist/utils/report.d.ts.map +1 -1
  211. package/docs/README.md +1 -0
  212. package/docs/architecture-and-rules.md +4 -3
  213. package/docs/core-engine.md +7 -10
  214. package/docs/getting-started.md +4 -2
  215. package/docs/modules/ambient.md +194 -80
  216. package/docs/modules/background.md +3 -3
  217. package/docs/modules/context-menu.md +2 -2
  218. package/docs/modules/dock.md +8 -5
  219. package/docs/modules/modal.md +2 -2
  220. package/docs/modules/notification.md +2 -2
  221. package/docs/theming.md +81 -9
  222. package/package.json +22 -5
  223. package/templates/AGENTS.md +1 -1
  224. package/templates/motion/background.ts +17 -0
  225. package/templates/motion/context-menu.ts +63 -0
  226. package/templates/motion/dock.ts +327 -0
  227. package/templates/motion/modal.ts +79 -0
  228. package/templates/motion/notification.ts +27 -0
  229. package/templates/motion/tokens.ts +47 -0
  230. package/templates/tsconfig.json +7 -0
  231. package/dist/modules/ambient/color.d.ts +0 -16
  232. package/dist/modules/ambient/color.d.ts.map +0 -1
@@ -1,1899 +1,13 @@
1
1
  "use client";
2
- import { createContext, isValidElement, useState, useRef, useMemo, useEffect, useLayoutEffect, useCallback, useSyncExternalStore, use, useContext, useId, cloneElement, Children } from 'react';
3
- import { usePathname } from 'next/navigation';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
5
- import { Icon as Icon$1 } from '@iconify-icon/react';
6
- import { createPortal } from 'react-dom';
7
- import { motion, AnimatePresence } from 'motion/react';
8
-
9
- function useRequiredContext(context, hookName, providerName) {
10
- const value = use(context);
11
- if (value === null) {
12
- throw new Error(`${hookName} must be used within ${providerName}`);
13
- }
14
- return value;
15
- }
16
- function useStore(store, selector = (state) => state, isEqual = Object.is) {
17
- const cacheRef = useRef({
18
- isEqual: null,
19
- selected: void 0,
20
- selector: null,
21
- snapshot: null
22
- });
23
- const getSelectedSnapshot = useCallback(() => {
24
- const snapshot = store.getSnapshot();
25
- const cache = cacheRef.current;
26
- if (cache.snapshot === snapshot && cache.selector === selector && cache.isEqual === isEqual) {
27
- return cache.selected;
28
- }
29
- const selected = selector(snapshot);
30
- if (cache.snapshot !== null && cache.isEqual === isEqual && isEqual(cache.selected, selected)) {
31
- cache.snapshot = snapshot;
32
- cache.selector = selector;
33
- return cache.selected;
34
- }
35
- cacheRef.current = {
36
- isEqual,
37
- selected,
38
- selector,
39
- snapshot
40
- };
41
- return selected;
42
- }, [isEqual, selector, store]);
43
- return useSyncExternalStore(
44
- store.subscribe,
45
- getSelectedSnapshot,
46
- getSelectedSnapshot
47
- );
48
- }
49
-
50
- // src/utils/string.ts
51
- function trimToNull(value) {
52
- if (value === null || value === void 0) return null;
53
- const trimmed = String(value).trim();
54
- return trimmed.length > 0 ? trimmed : null;
55
- }
56
- function isImageIconSource(icon) {
57
- return typeof icon === "string" && (icon.startsWith("http://") || icon.startsWith("https://") || icon.startsWith("/") || icon.startsWith("data:image/"));
58
- }
59
-
60
- // src/utils/dom.ts
61
- var isBrowser = typeof window !== "undefined";
62
-
63
- // src/utils/number.ts
64
- function toFiniteNumber(value, fallback = 0) {
65
- const parsed = Number(value);
66
- return Number.isFinite(parsed) ? parsed : fallback;
67
- }
68
-
69
- // src/utils/object.ts
70
- function isObject(value) {
71
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
72
- }
73
- function toArray(value) {
74
- if (value === null || value === void 0) return [];
75
- return Array.isArray(value) ? value : [value];
76
- }
77
- function report(scope, error, level = "error") {
78
- if (process.env.NODE_ENV === "production" && typeof window !== "undefined") {
79
- return;
80
- }
81
- console[level](`[${scope}]`, error);
82
- }
83
-
84
- // src/utils/store.ts
85
- function freezeStoreSnapshot(state) {
86
- if (process.env.NODE_ENV === "production" || !state || typeof state !== "object") {
87
- return state;
88
- }
89
- const seen = /* @__PURE__ */ new WeakSet();
90
- const freeze = (value) => {
91
- if (seen.has(value) || Object.isFrozen(value)) return;
92
- seen.add(value);
93
- const prototype = Object.getPrototypeOf(value);
94
- if (Array.isArray(value) || prototype === Object.prototype || prototype === null) {
95
- for (const key of Reflect.ownKeys(value)) {
96
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
97
- if (descriptor && "value" in descriptor && descriptor.value !== null) {
98
- if (typeof descriptor.value === "object") freeze(descriptor.value);
99
- }
100
- }
101
- }
102
- Object.freeze(value);
103
- };
104
- freeze(state);
105
- return state;
106
- }
107
- function createStore(initialState, { freezeSnapshots = true } = {}) {
108
- const listeners = /* @__PURE__ */ new Set();
109
- const prepare = freezeSnapshots ? freezeStoreSnapshot : (state) => state;
110
- let snapshot = prepare(initialState);
111
- const publish = (nextStateOrUpdater) => {
112
- const nextState = typeof nextStateOrUpdater === "function" ? nextStateOrUpdater(snapshot) : nextStateOrUpdater;
113
- if (Object.is(snapshot, nextState)) return false;
114
- snapshot = prepare(nextState);
115
- for (const listener of listeners) {
116
- try {
117
- listener();
118
- } catch (error) {
119
- report("CoreStore subscriber", error, "warn");
120
- }
121
- }
122
- return true;
123
- };
124
- const setState = (updater) => {
125
- if (typeof updater === "function") {
126
- return publish(updater);
127
- }
128
- if (snapshot && typeof snapshot === "object" && updater && typeof updater === "object") {
129
- return publish({ ...snapshot, ...updater });
130
- }
131
- return publish(updater);
132
- };
133
- return Object.freeze({
134
- getSnapshot() {
135
- return snapshot;
136
- },
137
- publish,
138
- setState,
139
- subscribe(listener) {
140
- if (typeof listener !== "function") return () => {
141
- };
142
- listeners.add(listener);
143
- return () => listeners.delete(listener);
144
- }
145
- });
146
- }
147
-
148
- // src/hooks/use-isomorphic-layout-effect.ts
149
- var useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;
150
-
151
- // src/kernel/constants.ts
152
- var DEFAULT_SOURCE = "dynamic";
153
- var REGISTRY_SOURCES = Object.freeze({
154
- STATIC: "static",
155
- DYNAMIC: "dynamic",
156
- USER: "user"
157
- });
158
- var REGISTRY_LIFECYCLES = Object.freeze({
159
- IMMEDIATE: "immediate",
160
- GRACEFUL: "graceful",
161
- PERSISTENT: "persistent",
162
- ROUTE: "route"
163
- });
164
- var REGISTRY_VALIDATION_MODES = Object.freeze({
165
- WARN: "warn",
166
- STRICT: "strict"
167
- });
168
- var REGISTRY_METADATA_KEYS = Object.freeze([
169
- "cleanup",
170
- "cleanupDelayMs",
171
- "instanceId",
172
- "lifecycle",
173
- "priority",
174
- "source",
175
- "scope",
176
- "validation"
177
- ]);
178
- Object.freeze({
179
- [REGISTRY_SOURCES.STATIC]: 100,
180
- [REGISTRY_SOURCES.DYNAMIC]: 200,
181
- [REGISTRY_SOURCES.USER]: 300
182
- });
183
- Object.freeze({
184
- [REGISTRY_SOURCES.STATIC]: 10,
185
- [REGISTRY_SOURCES.DYNAMIC]: 20,
186
- [REGISTRY_SOURCES.USER]: 30
187
- });
188
- var REGISTRY_LIFECYCLE_VALUES = new Set(
189
- Object.values(REGISTRY_LIFECYCLES)
190
- );
191
- var REGISTRY_METADATA_KEY_SET = new Set(
192
- REGISTRY_METADATA_KEYS
193
- );
194
- var REGISTRY_SCOPE_KINDS = Object.freeze({
195
- APP: "app",
196
- SESSION: "session",
197
- ROUTE: "route",
198
- INSTANCE: "instance",
199
- WORKSPACE: "workspace"
200
- });
201
- REGISTRY_SCOPE_KINDS.APP;
202
- function getOrCreateGlobalContext(name, defaultValue) {
203
- const sym = /* @__PURE__ */ Symbol.for(`@omerdlw/base-framework/context/${name}`);
204
- const store = globalThis;
205
- if (!store[sym]) {
206
- store[sym] = createContext(defaultValue);
207
- }
208
- return store[sym];
209
- }
210
-
211
- // src/kernel/utils.ts
212
- function parseFiniteNumber(value) {
213
- const parsed = Number(value);
214
- return Number.isFinite(parsed) ? parsed : null;
215
- }
216
- function getValidString(value, fallback = null) {
217
- return trimToNull(value) ?? fallback;
218
- }
219
-
220
- // src/kernel/schema.ts
221
- function resolveLifecycle(metadata, fallback) {
222
- const lifecycle = metadata?.lifecycle ?? metadata?.cleanup;
223
- return lifecycle && REGISTRY_LIFECYCLE_VALUES.has(lifecycle) ? lifecycle : fallback;
224
- }
225
- function validateRegistryMetadata(metadata) {
226
- if (metadata === void 0 || metadata === null)
227
- return { issues: [], valid: true };
228
- if (!isObject(metadata))
229
- return { issues: ["registry metadata must be an object"], valid: false };
230
- const issues = [];
231
- Object.keys(metadata).forEach((key) => {
232
- if (!REGISTRY_METADATA_KEY_SET.has(key))
233
- issues.push(`unknown registry metadata field: ${key}`);
234
- });
235
- if (metadata.source !== void 0 && !getValidString(metadata.source))
236
- issues.push("source must be a non-empty string");
237
- if (metadata.instanceId !== void 0 && !getValidString(metadata.instanceId))
238
- issues.push("instanceId must be a non-empty string");
239
- if (metadata.scope !== void 0 && !getValidString(metadata.scope))
240
- issues.push("scope must be a non-empty string");
241
- if (metadata.priority !== void 0 && parseFiniteNumber(metadata.priority) === null)
242
- issues.push("priority must be a finite number");
243
- if (metadata.cleanupDelayMs !== void 0) {
244
- const delay = parseFiniteNumber(metadata.cleanupDelayMs);
245
- if (delay === null || delay < 0)
246
- issues.push("cleanupDelayMs must be a non-negative finite number");
247
- }
248
- const lifecycle = metadata.lifecycle ?? metadata.cleanup;
249
- if (lifecycle !== void 0 && !REGISTRY_LIFECYCLE_VALUES.has(String(lifecycle))) {
250
- issues.push(
251
- `lifecycle must be one of: ${[...REGISTRY_LIFECYCLE_VALUES].join(", ")}`
252
- );
253
- }
254
- if (metadata.validation !== void 0 && !Object.values(REGISTRY_VALIDATION_MODES).includes(
255
- metadata.validation
256
- )) {
257
- issues.push(
258
- `validation must be one of: ${Object.values(REGISTRY_VALIDATION_MODES).join(", ")}`
259
- );
260
- }
261
- return { issues, valid: issues.length === 0 };
262
- }
263
- function normalizeRegistryMetadata(metadata, {
264
- defaultCleanupDelayMs = null,
265
- defaultLifecycle = null,
266
- defaultSource = DEFAULT_SOURCE
267
- } = {}) {
268
- const registryMeta = isObject(metadata) ? metadata : {};
269
- const normalizedDefaultDelay = parseFiniteNumber(defaultCleanupDelayMs);
270
- const fallbackCleanupDelayMs = normalizedDefaultDelay !== null && normalizedDefaultDelay >= 0 ? normalizedDefaultDelay : null;
271
- const fallbackLifecycle = defaultLifecycle && REGISTRY_LIFECYCLE_VALUES.has(defaultLifecycle) ? defaultLifecycle : null;
272
- const source = getValidString(registryMeta.source) || getValidString(defaultSource, DEFAULT_SOURCE) || DEFAULT_SOURCE;
273
- const scope = getValidString(registryMeta.scope);
274
- const priority = parseFiniteNumber(registryMeta.priority);
275
- const lifecycle = resolveLifecycle(registryMeta, fallbackLifecycle);
276
- const validation = isValidationMode(registryMeta.validation) ? registryMeta.validation : null;
277
- const cleanupDelayCandidate = parseFiniteNumber(registryMeta.cleanupDelayMs);
278
- let cleanupDelayMs = cleanupDelayCandidate !== null && cleanupDelayCandidate >= 0 ? cleanupDelayCandidate : fallbackCleanupDelayMs;
279
- if (lifecycle === REGISTRY_LIFECYCLES.IMMEDIATE) {
280
- cleanupDelayMs = 0;
281
- } else if ((lifecycle === REGISTRY_LIFECYCLES.GRACEFUL || lifecycle === REGISTRY_LIFECYCLES.ROUTE) && cleanupDelayMs === null) {
282
- cleanupDelayMs = fallbackCleanupDelayMs;
283
- }
284
- return {
285
- cleanupDelayMs,
286
- lifecycle,
287
- priority,
288
- registerOptions: {
289
- ...priority !== null && { priority },
290
- ...validation === REGISTRY_VALIDATION_MODES.STRICT && { validation },
291
- ...scope && { scope }
292
- },
293
- source,
294
- ...scope && { scope },
295
- ...validation && { validation }
296
- };
297
- }
298
- function isValidationMode(value) {
299
- return Object.values(REGISTRY_VALIDATION_MODES).includes(value);
300
- }
301
- function pickRegistryMetadata(options) {
302
- if (!isObject(options)) return {};
303
- return Object.fromEntries(
304
- REGISTRY_METADATA_KEYS.filter((key) => options[key] !== void 0).map(
305
- (key) => [key, options[key]]
306
- )
307
- );
308
- }
309
- var RegistryContext = getOrCreateGlobalContext(
310
- "RegistryContext",
311
- null
312
- );
313
- function useRegistryActions() {
314
- return useRequiredContext(
315
- RegistryContext,
316
- "useRegistryActions",
317
- "RegistryProvider"
318
- ).actions;
319
- }
320
- function useRegistrySubscription() {
321
- return useRequiredContext(
322
- RegistryContext,
323
- "useRegistrySubscription",
324
- "RegistryProvider"
325
- ).subscription;
326
- }
327
- function useRegistryEntries(type) {
328
- const { getEntriesSnapshot, subscribe } = useRegistrySubscription();
329
- const subscribeToType = useCallback(
330
- (listener) => subscribe(type, null, listener),
331
- [subscribe, type]
332
- );
333
- const getEntries = useCallback(
334
- () => getEntriesSnapshot(type),
335
- [getEntriesSnapshot, type]
336
- );
337
- return useSyncExternalStore(subscribeToType, getEntries, getEntries);
338
- }
339
- var EMPTY_MODULES = Object.freeze([]);
340
- var ModuleHostContext = getOrCreateGlobalContext("ModuleHostContext", null);
341
- function selectModuleSlice(coreModule, config) {
342
- const slice = coreModule.page?.select ? coreModule.page.select(config) : config[coreModule.id];
343
- return slice ?? null;
344
- }
345
- function createModulePayload(modules, config, metadata = {}) {
346
- const payload = {};
347
- let hasSlices = false;
348
- for (const coreModule of modules) {
349
- if (!coreModule.page?.entries) continue;
350
- const slice = selectModuleSlice(coreModule, config);
351
- if (slice === null) continue;
352
- payload[coreModule.id] = slice;
353
- hasSlices = true;
354
- }
355
- if (!hasSlices) return null;
356
- return {
357
- ...payload,
358
- registry: {
359
- ...metadata,
360
- ...isObject(config.registry) ? config.registry : {}
361
- }
362
- };
363
- }
364
- function useInstalledModules() {
365
- return use(ModuleHostContext)?.modules ?? EMPTY_MODULES;
366
- }
367
-
368
- // src/kernel/handlers.ts
369
- function isPersistentLifecycle(lifecycle) {
370
- return lifecycle === REGISTRY_LIFECYCLES.PERSISTENT;
371
- }
372
- function runRegistrationBatch(context, executor) {
373
- return typeof context.batch === "function" ? context.batch(executor) : executor(context);
374
- }
375
- function createEntryCleanupKey(type, key, source, instanceId, scope) {
376
- return JSON.stringify([
377
- "entry",
378
- type,
379
- key,
380
- scope ?? null,
381
- source,
382
- instanceId
383
- ]);
384
- }
385
- function getCleanupScope(context) {
386
- return context?.cleanupScope || /* @__PURE__ */ new Map();
387
- }
388
- function clearCleanupTimer(scope, cleanupKey) {
389
- const lifecycle = scope.get(cleanupKey);
390
- if (!lifecycle) return;
391
- lifecycle.cancelled = true;
392
- if (lifecycle.timerId !== null) clearTimeout(lifecycle.timerId);
393
- scope.delete(cleanupKey);
394
- }
395
- function scheduleCleanup(scope, cleanupKey, callback, delayMs) {
396
- clearCleanupTimer(scope, cleanupKey);
397
- const lifecycle = {
398
- cancelled: false,
399
- timerId: null
400
- };
401
- lifecycle.timerId = setTimeout(() => {
402
- if (lifecycle.cancelled || scope.get(cleanupKey) !== lifecycle) return;
403
- scope.delete(cleanupKey);
404
- callback();
405
- }, delayMs);
406
- scope.set(cleanupKey, lifecycle);
407
- }
408
- function collectModuleRegistrations(coreModule, slice, pageMetadata, context) {
409
- const definition = coreModule.registry;
410
- const toEntries = coreModule.page?.entries;
411
- if (!definition || !toEntries || slice == null || slice === false) return [];
412
- const type = coreModule.id;
413
- const items = Array.isArray(slice) ? slice : [slice];
414
- const registrations = [];
415
- for (const item of items) {
416
- if (item == null || item === false) continue;
417
- const itemMetadata = isObject(item) && isObject(item.registry) ? item.registry : {};
418
- const metadata = { ...pageMetadata, ...itemMetadata };
419
- if (metadata.validation === "strict" && !validateRegistryMetadata(metadata).valid) {
420
- continue;
421
- }
422
- const payload = isObject(item) ? Object.fromEntries(
423
- Object.entries(item).filter(([key]) => key !== "registry")
424
- ) : item;
425
- const { cleanupDelayMs, lifecycle, registerOptions, scope, source } = normalizeRegistryMetadata(metadata, {
426
- defaultCleanupDelayMs: definition.cleanupDelayMs ?? null,
427
- defaultLifecycle: definition.lifecycle,
428
- defaultSource: DEFAULT_SOURCE
429
- });
430
- for (const { key, value } of toEntries(payload, context) ?? []) {
431
- const validation = definition.validate?.(value);
432
- if (validation && !validation.valid) {
433
- report(`Registry ${type} config is invalid`, validation.issues, "warn");
434
- continue;
435
- }
436
- registrations.push({
437
- cleanupDelayMs,
438
- cleanupKey: createEntryCleanupKey(
439
- type,
440
- key,
441
- source,
442
- context.instanceId,
443
- scope
444
- ),
445
- key,
446
- lifecycle,
447
- registerOptions,
448
- scope,
449
- source,
450
- type,
451
- value
452
- });
453
- }
454
- }
455
- return registrations;
456
- }
457
- var NO_CLEANUP = () => {
458
- };
459
- function applyRegistryConfig(config, context) {
460
- const modules = context.modules ?? [];
461
- if (!config || modules.length === 0) return NO_CLEANUP;
462
- const pageMetadata = isObject(config.registry) ? config.registry : {};
463
- const slices = config;
464
- const registrations = modules.flatMap((coreModule) => {
465
- try {
466
- return collectModuleRegistrations(
467
- coreModule,
468
- slices[coreModule.id],
469
- pageMetadata,
470
- context
471
- );
472
- } catch (error) {
473
- report(`Registry apply (${coreModule.id})`, error);
474
- return [];
475
- }
476
- });
477
- if (registrations.length === 0) return NO_CLEANUP;
478
- const cleanupScope = getCleanupScope(context);
479
- const owner = (registration) => ({
480
- instanceId: context.instanceId,
481
- scope: registration.scope,
482
- source: registration.source
483
- });
484
- runRegistrationBatch(context, (queue) => {
485
- registrations.forEach(
486
- ({ cleanupKey, key, registerOptions, source, type, value }) => {
487
- clearCleanupTimer(cleanupScope, cleanupKey);
488
- queue.register(type, key, value, source, registerOptions);
489
- }
490
- );
491
- });
492
- return () => {
493
- const immediate = [];
494
- registrations.forEach((registration) => {
495
- if (isPersistentLifecycle(registration.lifecycle)) return;
496
- const { cleanupDelayMs, cleanupKey, key, type } = registration;
497
- if (typeof cleanupDelayMs === "number" && cleanupDelayMs > 0) {
498
- scheduleCleanup(
499
- cleanupScope,
500
- cleanupKey,
501
- () => context.unregister(type, key, owner(registration)),
502
- cleanupDelayMs
503
- );
504
- } else {
505
- immediate.push(registration);
506
- }
507
- });
508
- if (immediate.length === 0) return;
509
- runRegistrationBatch(context, (queue) => {
510
- immediate.forEach(
511
- (registration) => queue.unregister(
512
- registration.type,
513
- registration.key,
514
- owner(registration)
515
- )
516
- );
517
- });
518
- };
519
- }
520
-
521
- // src/kernel/hooks.ts
522
- function withInstanceId(instanceId, sourceOrOptions, optionsArg) {
523
- if (typeof sourceOrOptions === "string") {
524
- return {
525
- optionsArg: { ...isObject(optionsArg) ? optionsArg : {}, instanceId },
526
- sourceOrOptions
527
- };
528
- }
529
- if (isObject(sourceOrOptions)) {
530
- return {
531
- optionsArg,
532
- sourceOrOptions: { ...sourceOrOptions, instanceId }
533
- };
534
- }
535
- return {
536
- optionsArg: void 0,
537
- sourceOrOptions: {
538
- ...isObject(optionsArg) ? optionsArg : {},
539
- instanceId
540
- }
541
- };
542
- }
543
- function withInstanceIdForUnregister(instanceId, sourceOrOptions) {
544
- if (typeof sourceOrOptions === "string") {
545
- return { instanceId, source: sourceOrOptions };
546
- }
547
- if (isObject(sourceOrOptions)) {
548
- return { ...sourceOrOptions, instanceId };
549
- }
550
- return { instanceId };
551
- }
552
- function resolveRegisterArgsWithInstance(instanceId, sourceOrOptions, optionsArg) {
553
- const input = withInstanceId(instanceId, sourceOrOptions, optionsArg);
554
- return [input.sourceOrOptions, input.optionsArg];
555
- }
556
- var MAX_STABILIZATION_DEPTH = 64;
557
- function createStableFunctionEntry(fn) {
558
- const entry = {
559
- current: fn,
560
- stable(...args) {
561
- return entry.current?.apply(this, args);
562
- }
563
- };
564
- return entry;
565
- }
566
- function isDirectRegistryComponentPath(path, componentSlices) {
567
- const match = /^config\.([^.[]+)\.[^.[]+$/.exec(path);
568
- return Boolean(match && componentSlices.has(match[1]));
569
- }
570
- function isReactNodeLike(value) {
571
- return value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" || typeof value === "number" || isValidElement(value);
572
- }
573
- function isStabilizableObject(value) {
574
- return typeof value === "object" && value !== null && !Array.isArray(value) && !isValidElement(value);
575
- }
576
- function hasSameObjectKeys(previousValue, nextValue) {
577
- if (!isStabilizableObject(previousValue) || !isStabilizableObject(nextValue)) {
578
- return false;
579
- }
580
- const previousKeys = Object.keys(previousValue);
581
- const nextKeys = Object.keys(nextValue);
582
- if (previousKeys.length !== nextKeys.length) return false;
583
- return nextKeys.every(
584
- (key) => Object.prototype.hasOwnProperty.call(previousValue, key)
585
- );
586
- }
587
- function stabilizeRegistryValue(value, path, scope, previousValue, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
588
- if (depth > MAX_STABILIZATION_DEPTH) return value;
589
- if (typeof value === "function") {
590
- const isComponent = value.name && /^[A-Z]/.test(value.name);
591
- if (isComponent || isDirectRegistryComponentPath(path, scope.componentSlices)) {
592
- return value;
593
- }
594
- scope.usedPaths.add(path);
595
- let entry = scope.functionEntries.get(path);
596
- if (!entry) {
597
- entry = createStableFunctionEntry(value);
598
- scope.functionEntries.set(path, entry);
599
- } else {
600
- entry.current = value;
601
- }
602
- return entry.stable;
603
- }
604
- if (value && typeof value === "object") {
605
- if (seen.has(value)) return value;
606
- seen.add(value);
607
- }
608
- if (isValidElement(value)) {
609
- const nextProps = stabilizeRegistryValue(
610
- value.props,
611
- `${path}.props`,
612
- scope,
613
- isValidElement(previousValue) ? previousValue.props : void 0,
614
- seen,
615
- depth + 1
616
- );
617
- if (isValidElement(previousValue) && previousValue.type === value.type && previousValue.key === value.key && previousValue.props === nextProps) {
618
- return previousValue;
619
- }
620
- return cloneElement(value, nextProps);
621
- }
622
- if (Array.isArray(value)) {
623
- const nextValue = value.every(isReactNodeLike) ? Children.toArray(value) : value;
624
- const previousArray = Array.isArray(previousValue) ? previousValue : null;
625
- let hasChanged2 = !previousArray || previousArray.length !== nextValue.length;
626
- const stabilizedValue2 = nextValue.map((item, index) => {
627
- const nextItem = stabilizeRegistryValue(
628
- item,
629
- `${path}[${index}]`,
630
- scope,
631
- previousArray?.[index],
632
- seen,
633
- depth + 1
634
- );
635
- if (!previousArray || nextItem !== previousArray[index]) {
636
- hasChanged2 = true;
637
- }
638
- return nextItem;
639
- });
640
- return !hasChanged2 ? previousArray : stabilizedValue2;
641
- }
642
- if (typeof value !== "object" || value === null) {
643
- return value;
644
- }
645
- const source = value;
646
- const previousRecord = previousValue;
647
- const stabilizedValue = {};
648
- const canReusePrevious = hasSameObjectKeys(previousValue, value);
649
- let hasChanged = !canReusePrevious;
650
- Object.keys(source).forEach((key) => {
651
- const nextValue = stabilizeRegistryValue(
652
- source[key],
653
- `${path}.${key}`,
654
- scope,
655
- previousRecord?.[key],
656
- seen,
657
- depth + 1
658
- );
659
- stabilizedValue[key] = nextValue;
660
- if (!canReusePrevious || nextValue !== previousRecord?.[key]) {
661
- hasChanged = true;
662
- }
663
- });
664
- return !hasChanged ? previousValue : stabilizedValue;
665
- }
666
- function useAppliedRegistryConfig(payload, context, componentSlices) {
667
- const functionEntriesRef = useRef(/* @__PURE__ */ new Map());
668
- const appliedRef = useRef(null);
669
- useIsomorphicLayoutEffect(() => {
670
- const previous = appliedRef.current;
671
- if (previous && previous.payload === payload && previous.context === context) {
672
- return;
673
- }
674
- const functionEntries = functionEntriesRef.current;
675
- const usedPaths = /* @__PURE__ */ new Set();
676
- const config = stabilizeRegistryValue(
677
- payload,
678
- "config",
679
- { componentSlices, functionEntries, usedPaths },
680
- previous?.config
681
- );
682
- functionEntries.forEach((_entry, path) => {
683
- if (!usedPaths.has(path)) functionEntries.delete(path);
684
- });
685
- if (previous && previous.config === config && previous.context === context) {
686
- previous.payload = payload;
687
- return;
688
- }
689
- previous?.cleanup();
690
- appliedRef.current = {
691
- cleanup: applyRegistryConfig(config, context),
692
- config,
693
- context,
694
- payload
695
- };
696
- });
697
- useIsomorphicLayoutEffect(
698
- () => () => {
699
- appliedRef.current?.cleanup();
700
- appliedRef.current = null;
701
- },
702
- []
703
- );
704
- }
705
- function createRegistryApplyContext(actions, {
706
- cleanupScope = /* @__PURE__ */ new Map(),
707
- instanceId,
708
- modules = [],
709
- pathname
710
- }) {
711
- const stamp = (queue) => ({
712
- register: (type, key, item, sourceOrOptions, optionsArg) => queue.register(
713
- type,
714
- key,
715
- item,
716
- ...resolveRegisterArgsWithInstance(
717
- instanceId,
718
- sourceOrOptions,
719
- optionsArg
720
- )
721
- ),
722
- unregister: (type, key, sourceOrOptions) => queue.unregister(
723
- type,
724
- key,
725
- withInstanceIdForUnregister(instanceId, sourceOrOptions)
726
- )
727
- });
728
- return {
729
- ...stamp(actions),
730
- batch: (executor) => typeof executor === "function" ? actions.batch((queue) => executor(stamp(queue))) : 0,
731
- cleanupScope,
732
- instanceId,
733
- modules,
734
- pathname
735
- };
736
- }
737
- function useRegistry(config, metadata) {
738
- const { batch, register, unregister } = useRegistryActions();
739
- const pathname = usePathname();
740
- const modules = useInstalledModules();
741
- const instanceId = `registry-instance-${useId()}`;
742
- const [cleanupScope] = useState(
743
- () => /* @__PURE__ */ new Map()
744
- );
745
- const context = useMemo(
746
- () => createRegistryApplyContext(
747
- { batch, register, unregister },
748
- {
749
- cleanupScope,
750
- instanceId,
751
- modules,
752
- pathname
753
- }
754
- ),
755
- [batch, cleanupScope, instanceId, modules, pathname, register, unregister]
756
- );
757
- const payload = useMemo(
758
- () => config ? createModulePayload(modules, config, metadata) : null,
759
- [config, metadata, modules]
760
- );
761
- const componentSlices = useMemo(
762
- () => new Set(
763
- modules.filter(
764
- (coreModule) => coreModule.registry?.valueKind === "component"
765
- ).map((coreModule) => coreModule.id)
766
- ),
767
- [modules]
768
- );
769
- useAppliedRegistryConfig(payload, context, componentSlices);
770
- }
771
- function defineModule(coreModule) {
772
- return Object.freeze({ ...coreModule });
773
- }
774
- createStore(void 0);
775
-
776
- // src/kernel/adapters.ts
777
- function useModuleRegistration(id, value, options) {
778
- const coreModule = useInstalledModules().find(
779
- (candidate) => candidate.id === id
780
- );
781
- const config = coreModule && options?.enabled !== false && value != null ? { [id]: value } : null;
782
- useRegistry(config, pickRegistryMetadata(options));
783
- }
784
- function defineThemeSpec(id) {
785
- return Object.freeze({ id });
786
- }
787
- var EMPTY = /* @__PURE__ */ new Map();
788
- var ThemeContext = getOrCreateGlobalContext("ThemeContext", EMPTY);
789
- function useTheme(spec) {
790
- const config = useContext(ThemeContext).get(spec.id);
791
- if (!config) {
792
- throw new Error(
793
- `Theme "${spec.id}" is missing. Ensure its theme configuration is provided to <ThemeProvider themes={[...]}> (e.g. in src/config/ or your theme setup).`
794
- );
795
- }
796
- return {
797
- slots: config.slots,
798
- styles: config.styles ?? {}
799
- };
800
- }
801
-
802
- // src/modules/context-menu/constants.ts
803
- var contextMenuTheme = defineThemeSpec("context-menu");
804
- var CURRENT_PAGE_KEY = "current-page";
805
- var GLOBAL_MENU_KEY = "*";
806
- var MENU_SCREEN_MARGIN = 10;
807
- var CONTEXT_MENU_VISIBILITY_EVENT = "context-menu:visibility";
808
- var INITIAL_POSITION = Object.freeze({
809
- x: 0,
810
- y: 0
811
- });
812
- function extractNodeText(value) {
813
- if (typeof value === "string") return value;
814
- if (typeof value === "number" || typeof value === "boolean") {
815
- return String(value);
816
- }
817
- if (Array.isArray(value)) {
818
- return value.map(extractNodeText).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
819
- }
820
- if (isValidElement(value)) {
821
- return extractNodeText(value.props?.children);
822
- }
823
- return "";
824
- }
825
- function resolveAsBoolean(value, context, defaultValue = true) {
826
- if (typeof value === "function") {
827
- try {
828
- return Boolean(value(context));
829
- } catch {
830
- return false;
831
- }
832
- }
833
- if (value === void 0) {
834
- return defaultValue;
835
- }
836
- return Boolean(value);
837
- }
838
- function resolveAsValue(value, context, fallback) {
839
- if (typeof value === "function") {
840
- try {
841
- const resolved = value(context);
842
- return resolved === void 0 ? fallback : resolved;
843
- } catch {
844
- return fallback;
845
- }
846
- }
847
- return value === void 0 ? fallback : value;
848
- }
849
- function safeInvoke(handler, ...args) {
850
- if (typeof handler !== "function") return void 0;
851
- try {
852
- const result = handler(...args);
853
- if (result && typeof result.then === "function") {
854
- result.catch(
855
- (err) => report("Context menu async callback", err)
856
- );
857
- }
858
- return result;
859
- } catch (error) {
860
- report("Context menu callback", error);
861
- return void 0;
862
- }
863
- }
864
- function resolveContextMenuPageMeta(dockItem, pathname = "") {
865
- if (!isObject(dockItem)) return null;
866
- const title = dockItem.contextMenuTitle ?? dockItem.title ?? null;
867
- const description = dockItem.contextMenuDescription ?? dockItem.description ?? null;
868
- const eyebrow = dockItem.contextMenuEyebrow ?? dockItem.eyebrow ?? null;
869
- const icon = dockItem.contextMenuIcon ?? dockItem.icon ?? null;
870
- if (!title && !description && !icon && !eyebrow) return null;
871
- return {
872
- description,
873
- descriptionText: extractNodeText(description),
874
- eyebrow,
875
- icon,
876
- path: typeof dockItem.path === "string" && dockItem.path ? dockItem.path : pathname,
877
- title,
878
- titleText: extractNodeText(title) || (typeof dockItem.name === "string" ? dockItem.name : "") || ""
879
- };
880
- }
881
- function resolveItem(item, index, context) {
882
- if (!item) return null;
883
- if (item === "separator" || item.type === "separator") {
884
- return {
885
- key: item !== "separator" && item.key || `separator-${index}`,
886
- type: "separator"
887
- };
888
- }
889
- if (resolveAsBoolean(item.hidden, context, false) || !resolveAsBoolean(item.visible, context, true)) {
890
- return null;
891
- }
892
- const label = resolveAsValue(item.label, context, "");
893
- if (typeof label !== "string" && typeof label !== "number" || !String(label).trim()) {
894
- return null;
895
- }
896
- const handler = item.onSelect ?? item.onClick ?? null;
897
- const shortcut = resolveAsValue(item.shortcut, context, null);
898
- return {
899
- ...item,
900
- className: resolveAsValue(item.className, context, "") || "",
901
- closeOnSelect: item.closeOnSelect !== false,
902
- danger: resolveAsBoolean(item.danger, context, false),
903
- disabled: resolveAsBoolean(item.disabled, context, false),
904
- icon: resolveAsValue(item.icon, context, null) || null,
905
- itemIconClassName: resolveAsValue(item.itemIconClassName, context, "") || "",
906
- key: item.key || `item-${index}`,
907
- label: String(label),
908
- onClick: handler,
909
- onSelect: handler,
910
- shortcut: typeof shortcut === "string" ? shortcut : null,
911
- type: "action"
912
- };
913
- }
914
- function resolveMenuItems(config, context) {
915
- const rawItems = resolveAsValue(config?.items, context, []);
916
- const items = [];
917
- toArray(rawItems).forEach((raw, index) => {
918
- const item = resolveItem(raw, index, context);
919
- if (!item) return;
920
- const previous = items[items.length - 1];
921
- if (item.type === "separator" && (!previous || previous.type === "separator")) {
922
- return;
923
- }
924
- items.push(item);
925
- });
926
- if (items[items.length - 1]?.type === "separator") items.pop();
927
- return items;
928
- }
929
- function resolveMenuHeader(config, context) {
930
- if (config?.header === false || config?.showPageHeader === false) return null;
931
- const configured = resolveAsValue(config?.header, context, null);
932
- const source = isObject(configured) ? configured : isObject(context.page) ? context.page : null;
933
- if (!source) return null;
934
- const title = resolveAsValue(source.title, context, null);
935
- const description = resolveAsValue(source.description, context, null);
936
- const icon = resolveAsValue(source.icon, context, null);
937
- const eyebrow = resolveAsValue(source.eyebrow, context, null);
938
- if (!title && !description && !icon && !eyebrow) return null;
939
- return {
940
- description,
941
- descriptionText: extractNodeText(description),
942
- eyebrow,
943
- icon,
944
- title,
945
- titleText: extractNodeText(title)
946
- };
947
- }
948
-
949
- // src/modules/context-menu/resolver.ts
950
- function normalizeMenuCandidates(registryMenus) {
951
- const candidates = [];
952
- for (const [registryKey, rawConfig] of Object.entries(registryMenus || {})) {
953
- if (!isObject(rawConfig)) continue;
954
- const { menus, ...sharedConfig } = rawConfig;
955
- const nestedMenus = toArray(menus);
956
- const hasSharedItems = Array.isArray(sharedConfig.items) || typeof sharedConfig.items === "function";
957
- if (hasSharedItems || nestedMenus.length === 0) {
958
- candidates.push({
959
- config: sharedConfig,
960
- order: candidates.length,
961
- registryKey
962
- });
963
- }
964
- for (const menu of nestedMenus) {
965
- candidates.push({
966
- config: {
967
- ...sharedConfig,
968
- ...menu,
969
- classNames: { ...sharedConfig.classNames, ...menu.classNames }
970
- },
971
- order: candidates.length,
972
- registryKey
973
- });
974
- }
975
- }
976
- return candidates;
977
- }
978
- function isPathAllowed(config, registryKey, pathname) {
979
- if (!pathname) return true;
980
- if (config.path === pathname) return true;
981
- const explicitPaths = toArray(config.paths).filter(Boolean);
982
- if (explicitPaths.length > 0) return explicitPaths.includes(pathname);
983
- if (typeof config.pathMatcher === "function") {
984
- try {
985
- return Boolean(config.pathMatcher(pathname));
986
- } catch {
987
- return false;
988
- }
989
- }
990
- return registryKey === pathname || registryKey === CURRENT_PAGE_KEY || registryKey === GLOBAL_MENU_KEY;
991
- }
992
- function getElementDepth(from, ancestor) {
993
- let depth = 0;
994
- for (let node = from; node && node !== ancestor; node = node.parentElement) {
995
- depth += 1;
996
- }
997
- return depth;
998
- }
999
- function getTargetScore(config, targetElement) {
1000
- const selectors = toArray(config.target).filter(
1001
- (selector) => typeof selector === "string" && selector.trim()
1002
- );
1003
- if (selectors.length === 0) return 0;
1004
- if (!targetElement) return null;
1005
- let minDepth = Infinity;
1006
- for (const selector of selectors) {
1007
- try {
1008
- const matched = targetElement.closest(selector);
1009
- if (matched) {
1010
- minDepth = Math.min(minDepth, getElementDepth(targetElement, matched));
1011
- }
1012
- } catch {
1013
- }
1014
- }
1015
- return minDepth === Infinity ? null : Math.max(0, 100 - minDepth);
1016
- }
1017
- function getRouteScore(config, registryKey, pathname) {
1018
- if (!pathname) return 0;
1019
- if (config.path === pathname || toArray(config.paths).includes(pathname) || registryKey === pathname) {
1020
- return 100;
1021
- }
1022
- if (registryKey === CURRENT_PAGE_KEY) return 70;
1023
- if (registryKey === GLOBAL_MENU_KEY) return 40;
1024
- return 10;
1025
- }
1026
- function buildMenuContext(config, event, pathname, targetElement) {
1027
- let context = {
1028
- currentTarget: event.currentTarget ?? null,
1029
- event,
1030
- pathname: pathname || "",
1031
- point: { x: Number(event.clientX) || 0, y: Number(event.clientY) || 0 },
1032
- target: targetElement
1033
- };
1034
- if (config.payload !== void 0) context.payload = config.payload;
1035
- try {
1036
- const payload = config.resolvePayload?.(event, context);
1037
- if (payload !== void 0) context.payload = payload;
1038
- } catch {
1039
- }
1040
- try {
1041
- const extra = config.resolveContext?.(event, context);
1042
- if (isObject(extra)) context = { ...context, ...extra };
1043
- } catch {
1044
- }
1045
- return context;
1046
- }
1047
- function isMenuOverlayTarget(element) {
1048
- return Boolean(
1049
- element?.closest?.(
1050
- '[data-context-menu-ignore], [data-context-menu-overlay], [role="menu"]'
1051
- ) || element?.classList?.contains("context-menu-overlay")
1052
- );
1053
- }
1054
- function resolveEventTarget(event) {
1055
- const initialTarget = event.target instanceof Element ? event.target : null;
1056
- if (initialTarget && !isMenuOverlayTarget(initialTarget)) {
1057
- return initialTarget;
1058
- }
1059
- if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) {
1060
- return initialTarget;
1061
- }
1062
- try {
1063
- const underneath = document.elementsFromPoint(event.clientX, event.clientY).find((element) => !isMenuOverlayTarget(element));
1064
- return underneath || initialTarget;
1065
- } catch {
1066
- return initialTarget;
1067
- }
1068
- }
1069
- function passesWhen(config, event, info) {
1070
- if (typeof config.when !== "function") return config.when !== false;
1071
- try {
1072
- return Boolean(config.when(event, info));
1073
- } catch {
1074
- return false;
1075
- }
1076
- }
1077
- function resolveContextMenu(registryMenus, pathname, event) {
1078
- const targetElement = resolveEventTarget(event);
1079
- let winner = null;
1080
- for (const { config, order, registryKey } of normalizeMenuCandidates(
1081
- registryMenus
1082
- )) {
1083
- if (!isPathAllowed(config, registryKey, pathname)) continue;
1084
- const context = buildMenuContext(config, event, pathname, targetElement);
1085
- if (!resolveAsBoolean(config.enabled, context, true)) continue;
1086
- if (!passesWhen(config, event, {
1087
- context,
1088
- pathname: pathname || "",
1089
- target: targetElement
1090
- })) {
1091
- continue;
1092
- }
1093
- const items = resolveMenuItems(config, context);
1094
- if (!items.length) continue;
1095
- const targetScore = getTargetScore(config, targetElement);
1096
- if (targetScore === null) continue;
1097
- const priority = Number(config.priority);
1098
- const score = (Number.isFinite(priority) ? priority : 0) * 1e4 + getRouteScore(config, registryKey, pathname) * 100 + targetScore;
1099
- if (!winner || score > winner.score) {
1100
- winner = { config, context, items, order, score };
1101
- }
1102
- }
1103
- return winner;
1104
- }
1105
- function prepareMenu(config, context, event) {
1106
- const onOpenResult = safeInvoke(config.onOpen, event, context);
1107
- if (onOpenResult === false) return null;
1108
- const nextContext = isObject(onOpenResult) ? { ...context, ...onOpenResult } : context;
1109
- const items = resolveMenuItems(config, nextContext);
1110
- return items.length ? { context: nextContext, items } : null;
1111
- }
1112
-
1113
- // src/modules/context-menu/state.ts
1114
- function createInitialMenuState() {
1115
- return {
1116
- position: INITIAL_POSITION,
1117
- context: null,
1118
- isOpen: false,
1119
- config: null,
1120
- items: []
1121
- };
1122
- }
1123
- function resolveNextOpenState({
1124
- config,
1125
- context,
1126
- items,
1127
- position
1128
- }) {
1129
- if (!config) return null;
1130
- return {
1131
- config,
1132
- context: context ?? null,
1133
- isOpen: true,
1134
- items: items ?? [],
1135
- position: {
1136
- x: Math.round(toFiniteNumber(position?.x, 0)),
1137
- y: Math.round(toFiniteNumber(position?.y, 0))
1138
- }
1139
- };
1140
- }
1141
- function emitContextMenuVisibility(isOpen) {
1142
- if (!isBrowser) return;
1143
- window.dispatchEvent(
1144
- new CustomEvent(CONTEXT_MENU_VISIBILITY_EVENT, {
1145
- detail: { isOpen: Boolean(isOpen) }
1146
- })
1147
- );
1148
- }
1149
- var ContextMenuContext = getOrCreateGlobalContext(
1150
- "ContextMenuContext",
1151
- null
1152
- );
1153
- function selectIsOpen(state) {
1154
- return state.isOpen;
1155
- }
1156
- function ContextMenuProvider({ children }) {
1157
- const pathname = usePathname();
1158
- const [store] = useState(
1159
- () => createStore(createInitialMenuState(), {
1160
- freezeSnapshots: false
1161
- })
1162
- );
1163
- const isOpen = useStore(store, selectIsOpen);
1164
- const pathnameRef = useRef(pathname);
1165
- useIsomorphicLayoutEffect(() => {
1166
- pathnameRef.current = pathname;
1167
- }, [pathname]);
1168
- const actions = useMemo(() => {
1169
- const openMenu = (input) => {
1170
- const nextState = resolveNextOpenState(input);
1171
- if (nextState) store.publish(nextState);
1172
- };
1173
- const closeMenu = () => {
1174
- const current = store.getSnapshot();
1175
- if (!current.isOpen) return;
1176
- store.publish(createInitialMenuState());
1177
- safeInvoke(current.config?.onClose, current.context);
1178
- };
1179
- const bind = (payload, configOverride) => ({
1180
- onContextMenu: (event) => {
1181
- const config = configOverride || store.getSnapshot().config;
1182
- if (!config) return;
1183
- event.preventDefault?.();
1184
- event.stopPropagation?.();
1185
- const point = {
1186
- x: Number(event.clientX ?? 0),
1187
- y: Number(event.clientY ?? 0)
1188
- };
1189
- const context = {
1190
- currentTarget: event.currentTarget ?? null,
1191
- event,
1192
- pathname: pathnameRef.current || "",
1193
- payload,
1194
- point,
1195
- target: event.target ?? null
1196
- };
1197
- const prepared = prepareMenu(config, context, event);
1198
- if (!prepared) return;
1199
- openMenu({
1200
- config,
1201
- context: prepared.context,
1202
- items: prepared.items,
1203
- position: point
1204
- });
1205
- }
1206
- });
1207
- return { bind, closeMenu, openMenu };
1208
- }, [store]);
1209
- const contextValue = useMemo(
1210
- () => ({ actions, store }),
1211
- [actions, store]
1212
- );
1213
- useEffect(() => emitContextMenuVisibility(isOpen), [isOpen]);
1214
- return /* @__PURE__ */ jsx(ContextMenuContext, { value: contextValue, children });
1215
- }
1216
- function Icon({
1217
- className = "center",
1218
- color,
1219
- icon,
1220
- onClick,
1221
- size = 20,
1222
- style,
1223
- ...props
1224
- }) {
1225
- return /* @__PURE__ */ jsx(
1226
- Icon$1,
1227
- {
1228
- className,
1229
- height: size,
1230
- icon,
1231
- onClick,
1232
- style: { color, ...style },
1233
- width: size,
1234
- ...props
1235
- }
1236
- );
1237
- }
1238
- Icon.displayName = "Icon";
1239
-
1240
- // src/tokens/motion.ts
1241
- var DURATION_TOKENS = Object.freeze({
1242
- INSTANT: 0.08,
1243
- MICRO: 0.18,
1244
- FAST: 0.28,
1245
- BASE: 0.36,
1246
- MODERATE: 0.44,
1247
- SLOW: 0.58,
1248
- CINEMATIC: 0.86
1249
- });
1250
- var EASING_CURVES = Object.freeze({
1251
- OUT_EXPO: [0.16, 1, 0.3, 1],
1252
- OUT_QUART: [0.25, 1, 0.5, 1],
1253
- OUT_QUINT: [0.22, 1, 0.36, 1],
1254
- IN_OUT_CUBIC: [0.65, 0, 0.35, 1],
1255
- IN_CUBIC: [0.5, 0, 1, 1],
1256
- OUT_BACK: [0.34, 1.56, 0.64, 1]
1257
- });
1258
- var MOTION_EASINGS = Object.freeze({
1259
- ENTER: EASING_CURVES.OUT_QUART,
1260
- ENTER_EMPHASIZED: EASING_CURVES.OUT_EXPO,
1261
- EXIT: EASING_CURVES.IN_CUBIC
1262
- });
1263
- Object.freeze({
1264
- MICRO: Object.freeze({
1265
- distance: 4,
1266
- duration: DURATION_TOKENS.MICRO,
1267
- ease: MOTION_EASINGS.ENTER_EMPHASIZED,
1268
- scaleDelta: 8e-3
1269
- }),
1270
- FAST: Object.freeze({
1271
- distance: 9,
1272
- duration: DURATION_TOKENS.FAST,
1273
- ease: MOTION_EASINGS.ENTER_EMPHASIZED,
1274
- scaleDelta: 0.012
1275
- }),
1276
- STANDARD: Object.freeze({
1277
- distance: 18,
1278
- duration: DURATION_TOKENS.MODERATE,
1279
- ease: MOTION_EASINGS.ENTER,
1280
- scaleDelta: 0.018
1281
- }),
1282
- SURFACE: Object.freeze({
1283
- distance: 28,
1284
- duration: DURATION_TOKENS.SLOW,
1285
- ease: MOTION_EASINGS.ENTER_EMPHASIZED,
1286
- scaleDelta: 0.024
1287
- })
1288
- });
1289
- Object.freeze({
1290
- duration: DURATION_TOKENS.INSTANT,
1291
- ease: "linear"
1292
- });
1293
-
1294
- // src/modules/dock/context.ts
1295
- var DockContext = getOrCreateGlobalContext(
1296
- "DockContext",
1297
- null
1298
- );
1299
- var EMPTY_DOCK_STATE = Object.freeze({
1300
- activeItem: null,
1301
- activeOperation: null,
1302
- activeSurfaceEntry: null,
1303
- activeSurfaceId: null,
1304
- contextActions: [],
1305
- dockContinuity: [],
1306
- dockHeight: 0,
1307
- dockReturnHandoffs: [],
1308
- expanded: false,
1309
- hud: null,
1310
- hudEntries: [],
1311
- isHudActive: false,
1312
- isSurfaceOpen: false,
1313
- locationKey: "",
1314
- operations: [],
1315
- pathname: "",
1316
- searchQuery: "",
1317
- selectionMode: null,
1318
- surfaceLifecycle: "",
1319
- surfacePhase: "",
1320
- surfaceStack: []
1321
- });
1322
- var NOOP_DOCK_STORE = createStore(EMPTY_DOCK_STATE);
1323
- function useOptionalDockState(selector, isEqual) {
1324
- const ctx = useContext(DockContext);
1325
- return useStore(
1326
- ctx?.store ?? NOOP_DOCK_STORE,
1327
- selector,
1328
- isEqual
1329
- );
1330
- }
1331
-
1332
- // src/modules/context-menu/dom.ts
1333
- function joinClassNames(...classes) {
1334
- return classes.filter(Boolean).join(" ");
1335
- }
1336
- var SCROLL_LOCK_KEYS = /* @__PURE__ */ new Set([
1337
- "ArrowDown",
1338
- "ArrowUp",
1339
- "End",
1340
- "Home",
1341
- "PageDown",
1342
- "PageUp",
1343
- " ",
1344
- "Spacebar"
1345
- ]);
1346
- function isScrollLockKey(event) {
1347
- return SCROLL_LOCK_KEYS.has(event.key);
1348
- }
1349
- function positionMenu(menuElement, position) {
1350
- if (!menuElement) return;
1351
- const { height, width } = menuElement.getBoundingClientRect();
1352
- const maxX = window.innerWidth - width - MENU_SCREEN_MARGIN;
1353
- const maxY = window.innerHeight - height - MENU_SCREEN_MARGIN;
1354
- const x = Math.min(Number(position?.x) || 0, maxX);
1355
- const y = Math.min(Number(position?.y) || 0, maxY);
1356
- menuElement.style.left = `${Math.round(Math.max(MENU_SCREEN_MARGIN, x))}px`;
1357
- menuElement.style.top = `${Math.round(Math.max(MENU_SCREEN_MARGIN, y))}px`;
1358
- }
1359
- function getNextActiveIndex(items, current, direction) {
1360
- const count = items.length;
1361
- const start = current < 0 ? direction === 1 ? 0 : count - 1 : current + direction;
1362
- for (let step = 0; step < count; step++) {
1363
- const index = ((start + step * direction) % count + count) % count;
1364
- if (items[index].type === "action" && !items[index].disabled) return index;
1365
- }
1366
- return current;
1367
- }
1368
-
1369
- // src/modules/context-menu/hooks.ts
1370
- function useContextMenuRegistration(config, options) {
1371
- useModuleRegistration("contextMenu", config, options);
1372
- }
1373
- function useContextMenu(config, options) {
1374
- useContextMenuRegistration(config || null, options);
1375
- const { actions, store } = useRequiredContext(
1376
- ContextMenuContext,
1377
- "useContextMenu",
1378
- "ContextMenuProvider"
1379
- );
1380
- const state = useStore(store);
1381
- const ctx = useMemo(
1382
- () => ({ ...state, ...actions }),
1383
- [actions, state]
1384
- );
1385
- return useMemo(() => {
1386
- if (!config) return ctx;
1387
- return {
1388
- ...ctx,
1389
- bind: (payload, configOverride) => ctx.bind(payload, { ...config, ...configOverride })
1390
- };
1391
- }, [config, ctx]);
1392
- }
1393
- function useContextMenuActions() {
1394
- return useRequiredContext(
1395
- ContextMenuContext,
1396
- "useContextMenuActions",
1397
- "ContextMenuProvider"
1398
- ).actions;
1399
- }
1400
- function useContextMenuState() {
1401
- const { store } = useRequiredContext(
1402
- ContextMenuContext,
1403
- "useContextMenuState",
1404
- "ContextMenuProvider"
1405
- );
1406
- return useStore(store);
1407
- }
1408
- function selectDockPageCard(dock) {
1409
- const card = dock.activeItem;
1410
- return card && !card.isSurface ? card : null;
1411
- }
1412
- function useContextMenuListener() {
1413
- const menus = useRegistryEntries(
1414
- "contextMenu"
1415
- );
1416
- const dockCard = useOptionalDockState(selectDockPageCard);
1417
- const { openMenu } = useContextMenuActions();
1418
- const pathname = usePathname();
1419
- useEffect(() => {
1420
- const handleContextMenu = (event) => {
1421
- const match = resolveContextMenu(menus, pathname || void 0, event);
1422
- if (!match) return;
1423
- event.preventDefault();
1424
- event.stopPropagation();
1425
- const page = resolveContextMenuPageMeta(dockCard, pathname || "");
1426
- const prepared = prepareMenu(
1427
- match.config,
1428
- page ? { ...match.context, page } : match.context,
1429
- event
1430
- );
1431
- if (!prepared) return;
1432
- openMenu({
1433
- config: match.config,
1434
- context: prepared.context,
1435
- items: prepared.items,
1436
- position: { x: event.clientX, y: event.clientY }
1437
- });
1438
- };
1439
- document.addEventListener("contextmenu", handleContextMenu, true);
1440
- return () => document.removeEventListener("contextmenu", handleContextMenu, true);
1441
- }, [dockCard, menus, openMenu, pathname]);
1442
- }
1443
- function useMenuDismissal(menuRef, onClose) {
1444
- useEffect(() => {
1445
- const preventScroll = (event) => event.preventDefault();
1446
- const listenerOptions = { capture: true, passive: false };
1447
- const handleInteraction = (event) => {
1448
- const isInside = menuRef.current?.contains(event.target);
1449
- if (event.type === "keydown") {
1450
- if (event.key === "Escape") onClose();
1451
- if (!isInside && isScrollLockKey(event)) {
1452
- event.preventDefault();
1453
- }
1454
- } else if (!isInside) {
1455
- onClose();
1456
- }
1457
- };
1458
- document.addEventListener("mousedown", handleInteraction, true);
1459
- document.addEventListener("keydown", handleInteraction, true);
1460
- window.addEventListener("wheel", preventScroll, listenerOptions);
1461
- window.addEventListener("touchmove", preventScroll, listenerOptions);
1462
- return () => {
1463
- document.removeEventListener("mousedown", handleInteraction, true);
1464
- document.removeEventListener("keydown", handleInteraction, true);
1465
- window.removeEventListener("wheel", preventScroll, true);
1466
- window.removeEventListener("touchmove", preventScroll, true);
1467
- };
1468
- }, [menuRef, onClose]);
1469
- }
1470
- function useContextMenuContentModel({
1471
- config,
1472
- items,
1473
- menuContext,
1474
- onClose,
1475
- position
1476
- }) {
1477
- const menuRef = useRef(null);
1478
- const itemRefs = useRef([]);
1479
- const theme = useTheme(contextMenuTheme);
1480
- const classes = useMemo(() => {
1481
- const overrides = config.classNames ?? {};
1482
- const merged = { ...theme.slots };
1483
- for (const slot of Object.keys(merged)) {
1484
- merged[slot] = joinClassNames(theme.slots[slot], overrides[slot]);
1485
- }
1486
- return merged;
1487
- }, [config.classNames, theme.slots]);
1488
- const header = useMemo(
1489
- () => resolveMenuHeader(config, menuContext),
1490
- [config, menuContext]
1491
- );
1492
- const [activeIndex, setActiveIndex] = useState(-1);
1493
- const [prevItems, setPrevItems] = useState(items);
1494
- if (items !== prevItems) {
1495
- setPrevItems(items);
1496
- setActiveIndex(-1);
1497
- }
1498
- useLayoutEffect(() => {
1499
- itemRefs.current = [];
1500
- positionMenu(menuRef.current, position);
1501
- }, [header, items, position]);
1502
- useEffect(() => {
1503
- const target = activeIndex < 0 ? menuRef.current : itemRefs.current[activeIndex];
1504
- target?.focus({ preventScroll: true });
1505
- }, [activeIndex]);
1506
- useMenuDismissal(menuRef, onClose);
1507
- const handleItemSelect = useCallback(
1508
- (item, event) => {
1509
- event.preventDefault();
1510
- event.stopPropagation();
1511
- if (item.disabled) return;
1512
- safeInvoke(item.onSelect || item.onClick, event, menuContext);
1513
- if (item.closeOnSelect !== false) onClose();
1514
- },
1515
- [menuContext, onClose]
1516
- );
1517
- const handleMenuKeyDown = (event) => {
1518
- if (event.key === "Escape") {
1519
- event.preventDefault();
1520
- onClose();
1521
- } else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1522
- event.preventDefault();
1523
- const direction = event.key === "ArrowDown" ? 1 : -1;
1524
- setActiveIndex(
1525
- (current) => getNextActiveIndex(items, current, direction)
1526
- );
1527
- } else if ((event.key === "Enter" || event.key === " ") && items[activeIndex]) {
1528
- event.preventDefault();
1529
- handleItemSelect(items[activeIndex], event);
1530
- }
1531
- };
1532
- return {
1533
- menuRef,
1534
- itemRefs,
1535
- classes,
1536
- styles: theme.styles,
1537
- header,
1538
- activeIndex,
1539
- setActiveIndex,
1540
- handleItemSelect,
1541
- handleMenuKeyDown
1542
- };
1543
- }
1544
-
1545
- // src/modules/context-menu/motion.ts
1546
- var CONTEXT_MENU_EASINGS = Object.freeze({
1547
- EMPHASIZED: EASING_CURVES.OUT_EXPO,
1548
- SOFT: EASING_CURVES.OUT_QUINT,
1549
- EXIT: EASING_CURVES.IN_CUBIC
1550
- });
1551
- var CONTEXT_MENU_TIERS = Object.freeze({
1552
- MICRO: {
1553
- duration: DURATION_TOKENS.FAST,
1554
- distance: 4,
1555
- scaleDelta: 8e-3,
1556
- ease: CONTEXT_MENU_EASINGS.EMPHASIZED
1557
- }
1558
- });
1559
- function toGpuTransform({
1560
- x = 0,
1561
- y = 0,
1562
- scale = 1
1563
- } = {}) {
1564
- return `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
1565
- }
1566
- var CONTEXT_MENU_MICRO_SPRING = Object.freeze({
1567
- type: "spring",
1568
- stiffness: 520,
1569
- damping: 30,
1570
- mass: 0.28
1571
- });
1572
- var CONTEXT_MENU_ITEM_TAP = Object.freeze({
1573
- transform: toGpuTransform({
1574
- scale: 0.97
1575
- })
1576
- });
1577
- var menuContentVariants = Object.freeze({
1578
- hidden: {
1579
- opacity: 0
1580
- },
1581
- visible: {
1582
- opacity: 1,
1583
- transition: {
1584
- duration: CONTEXT_MENU_TIERS.MICRO.duration,
1585
- ease: CONTEXT_MENU_EASINGS.SOFT,
1586
- delay: 0.04
1587
- }
1588
- },
1589
- exit: {
1590
- opacity: 0,
1591
- transition: {
1592
- duration: DURATION_TOKENS.MICRO,
1593
- ease: CONTEXT_MENU_EASINGS.EXIT
1594
- }
1595
- }
1596
- });
1597
- var menuItemVariants = Object.freeze({
1598
- hidden: {
1599
- opacity: 0,
1600
- transform: toGpuTransform({
1601
- y: CONTEXT_MENU_TIERS.MICRO.distance,
1602
- scale: 0.992
1603
- })
1604
- },
1605
- visible: (index = 0) => ({
1606
- opacity: 1,
1607
- transform: toGpuTransform(),
1608
- transition: {
1609
- duration: CONTEXT_MENU_TIERS.MICRO.duration,
1610
- ease: CONTEXT_MENU_EASINGS.EMPHASIZED,
1611
- delay: 0.04 + Math.min(Math.max(Number(index) || 0, 0) * 0.042, 0.21)
1612
- }
1613
- }),
1614
- exit: {
1615
- opacity: 0,
1616
- transform: toGpuTransform({
1617
- y: -3,
1618
- scale: 0.994
1619
- }),
1620
- transition: {
1621
- duration: DURATION_TOKENS.MICRO,
1622
- ease: CONTEXT_MENU_EASINGS.EXIT
1623
- }
1624
- }
1625
- });
1626
- var menuPopVariants = Object.freeze({
1627
- hidden: {
1628
- opacity: 0,
1629
- transform: toGpuTransform({
1630
- scale: 0.96
1631
- }),
1632
- transformOrigin: "top left"
1633
- },
1634
- visible: {
1635
- opacity: 1,
1636
- transform: toGpuTransform(),
1637
- transformOrigin: "top left",
1638
- transition: {
1639
- duration: CONTEXT_MENU_TIERS.MICRO.duration,
1640
- ease: CONTEXT_MENU_EASINGS.EMPHASIZED
1641
- }
1642
- },
1643
- exit: {
1644
- opacity: 0,
1645
- transform: toGpuTransform({
1646
- scale: 0.98
1647
- }),
1648
- transformOrigin: "top left",
1649
- transition: {
1650
- duration: DURATION_TOKENS.MICRO,
1651
- ease: CONTEXT_MENU_EASINGS.EXIT
1652
- }
1653
- }
1654
- });
1655
- function ContextMenuHeaderIcon({
1656
- classes,
1657
- icon
1658
- }) {
1659
- const iconClassName = classes.headerIcon;
1660
- if (isImageIconSource(icon)) {
1661
- return /* @__PURE__ */ jsx(
1662
- "div",
1663
- {
1664
- className: iconClassName,
1665
- style: { backgroundImage: `url(${icon})` }
1666
- }
1667
- );
1668
- }
1669
- return /* @__PURE__ */ jsx("div", { className: iconClassName, children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon, size: 20 }) : icon });
1670
- }
1671
- function ContextMenuHeader({
1672
- classes,
1673
- header
1674
- }) {
1675
- if (!header) return null;
1676
- return /* @__PURE__ */ jsxs("div", { className: classes.header, children: [
1677
- header.icon && /* @__PURE__ */ jsx(ContextMenuHeaderIcon, { classes, icon: header.icon }),
1678
- /* @__PURE__ */ jsxs("div", { className: classes.headerText, children: [
1679
- header.eyebrow && /* @__PURE__ */ jsx("div", { className: classes.headerEyebrow, children: header.eyebrow }),
1680
- header.title && /* @__PURE__ */ jsx("div", { className: classes.headerTitle, children: header.title }),
1681
- header.description && /* @__PURE__ */ jsx("div", { className: classes.headerDescription, children: header.description })
1682
- ] })
1683
- ] });
1684
- }
1685
- function ContextMenuItem({
1686
- classes,
1687
- isActive,
1688
- item,
1689
- onHover,
1690
- onSelect,
1691
- setButtonRef
1692
- }) {
1693
- if (item.type === "separator") {
1694
- return /* @__PURE__ */ jsx("div", { className: classes.separator, role: "separator" });
1695
- }
1696
- const itemClassName = joinClassNames(classes.item, item.className);
1697
- const itemIconClassName = joinClassNames(
1698
- classes.itemIcon,
1699
- item.itemIconClassName
1700
- );
1701
- return /* @__PURE__ */ jsxs(
1702
- motion.button,
1703
- {
1704
- ref: setButtonRef,
1705
- className: itemClassName,
1706
- "data-active": isActive ? "true" : void 0,
1707
- "aria-disabled": item.disabled,
1708
- disabled: item.disabled,
1709
- role: "menuitem",
1710
- type: "button",
1711
- whileTap: CONTEXT_MENU_ITEM_TAP,
1712
- transition: CONTEXT_MENU_MICRO_SPRING,
1713
- onMouseEnter: onHover,
1714
- onClick: (event) => onSelect(item, event),
1715
- children: [
1716
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon: item.icon, className: itemIconClassName, size: 18 }) : item.icon),
1717
- /* @__PURE__ */ jsx("span", { className: classes.itemLabel, children: item.label }),
1718
- item.shortcut && /* @__PURE__ */ jsx("span", { className: classes.itemShortcut, children: item.shortcut })
1719
- ]
1720
- }
1721
- );
1722
- }
1723
- var EMPTY_MENU_CONTEXT = Object.freeze({
1724
- pathname: "",
1725
- point: { x: 0, y: 0 }
1726
- });
1727
- function ContextMenuContent({
1728
- config,
1729
- items,
1730
- menuContext,
1731
- onClose,
1732
- position
1733
- }) {
1734
- const {
1735
- menuRef,
1736
- itemRefs,
1737
- classes,
1738
- styles,
1739
- header,
1740
- activeIndex,
1741
- setActiveIndex,
1742
- handleItemSelect,
1743
- handleMenuKeyDown
1744
- } = useContextMenuContentModel({
1745
- config,
1746
- items,
1747
- menuContext,
1748
- onClose,
1749
- position
1750
- });
1751
- return /* @__PURE__ */ jsxs("div", { children: [
1752
- /* @__PURE__ */ jsx(
1753
- "div",
1754
- {
1755
- "data-context-menu-overlay": true,
1756
- className: classes.backdrop,
1757
- onMouseDown: onClose,
1758
- style: styles.backdrop
1759
- }
1760
- ),
1761
- /* @__PURE__ */ jsxs(
1762
- motion.div,
1763
- {
1764
- ref: menuRef,
1765
- "data-context-menu-ignore": true,
1766
- variants: menuPopVariants,
1767
- initial: "hidden",
1768
- animate: "visible",
1769
- exit: "exit",
1770
- className: classes.menu,
1771
- role: "menu",
1772
- tabIndex: -1,
1773
- style: {
1774
- ...styles.menu,
1775
- left: position.x || 0,
1776
- top: position.y || 0,
1777
- position: "fixed"
1778
- },
1779
- onMouseLeave: () => setActiveIndex(-1),
1780
- onKeyDown: handleMenuKeyDown,
1781
- children: [
1782
- /* @__PURE__ */ jsx(
1783
- motion.div,
1784
- {
1785
- variants: menuContentVariants,
1786
- initial: "hidden",
1787
- animate: "visible",
1788
- children: /* @__PURE__ */ jsx(ContextMenuHeader, { classes, header })
1789
- }
1790
- ),
1791
- items.map((item, index) => /* @__PURE__ */ jsx(
1792
- motion.div,
1793
- {
1794
- variants: menuItemVariants,
1795
- custom: index,
1796
- initial: "hidden",
1797
- animate: "visible",
1798
- children: /* @__PURE__ */ jsx(
1799
- ContextMenuItem,
1800
- {
1801
- item,
1802
- classes,
1803
- isActive: index === activeIndex,
1804
- onHover: () => {
1805
- if (item.type === "action" && !item.disabled) {
1806
- setActiveIndex(index);
1807
- }
1808
- },
1809
- setButtonRef: (node) => {
1810
- itemRefs.current[index] = node;
1811
- },
1812
- onSelect: handleItemSelect
1813
- }
1814
- )
1815
- },
1816
- item.key || `menu-item-${index}`
1817
- ))
1818
- ]
1819
- }
1820
- )
1821
- ] });
1822
- }
1823
- var emptySubscribe = () => () => {
1824
- };
1825
- function ContextMenuRenderer() {
1826
- const { closeMenu, config, context, isOpen, items, position } = useContextMenu();
1827
- const isMounted = useSyncExternalStore(
1828
- emptySubscribe,
1829
- () => true,
1830
- () => false
1831
- );
1832
- if (!isMounted) return null;
1833
- const menuContext = context ?? EMPTY_MENU_CONTEXT;
1834
- const resolvedItems = items.length ? items : resolveMenuItems(config, menuContext);
1835
- return createPortal(
1836
- /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && config && resolvedItems.length > 0 && /* @__PURE__ */ jsx(
1837
- ContextMenuContent,
1838
- {
1839
- config,
1840
- items: resolvedItems,
1841
- menuContext,
1842
- position,
1843
- onClose: closeMenu
1844
- },
1845
- "context-menu-content"
1846
- ) }),
1847
- document.body
1848
- );
1849
- }
1850
- function ContextMenuGlobal() {
1851
- useContextMenuListener();
1852
- return /* @__PURE__ */ jsx(ContextMenuRenderer, {});
1853
- }
1854
- function selectPageContextMenu(config) {
1855
- return config.contextMenu ?? null;
1856
- }
1857
- function useContextMenuPage(_slice, page) {
1858
- const actions = useContextMenuActions();
1859
- return useMemo(
1860
- () => ({
1861
- ...actions,
1862
- set: (contextMenu) => page.set({ contextMenu })
1863
- }),
1864
- [actions, page]
1865
- );
1866
- }
1867
- var contextMenuModule = defineModule({
1868
- id: "contextMenu",
1869
- context: ContextMenuContext,
1870
- Provider: ContextMenuProvider,
1871
- uses: ["dock"],
1872
- Overlay: ContextMenuGlobal,
1873
- registry: {
1874
- cleanupDelayMs: 600,
1875
- keyPolicy: "route",
1876
- lifecycle: "immediate",
1877
- reservedKeys: [CURRENT_PAGE_KEY, GLOBAL_MENU_KEY]
1878
- },
1879
- page: {
1880
- select: selectPageContextMenu,
1881
- entries: (slice, { pathname }) => [
1882
- { key: pathname || CURRENT_PAGE_KEY, value: slice }
1883
- ],
1884
- use: useContextMenuPage
1885
- }
1886
- });
1887
- function defineContextMenu(definition = {}) {
1888
- return Object.freeze({
1889
- config: definition,
1890
- id: definition.id ?? "context-menu",
1891
- use: function useDefinedContextMenu(options = {}) {
1892
- return useContextMenu({ ...definition, ...options });
1893
- }
1894
- });
1895
- }
1896
-
1897
- export { CONTEXT_MENU_VISIBILITY_EVENT, CURRENT_PAGE_KEY, ContextMenuContext, ContextMenuGlobal, ContextMenuHeader, ContextMenuHeaderIcon, ContextMenuItem, ContextMenuProvider, ContextMenuRenderer, GLOBAL_MENU_KEY, INITIAL_POSITION, MENU_SCREEN_MARGIN, contextMenuModule, contextMenuTheme, createInitialMenuState, defineContextMenu, emitContextMenuVisibility, extractNodeText, getNextActiveIndex, isScrollLockKey, joinClassNames, positionMenu, prepareMenu, resolveAsBoolean, resolveAsValue, resolveContextMenu, resolveContextMenuPageMeta, resolveMenuHeader, resolveMenuItems, resolveNextOpenState, safeInvoke, useContextMenu, useContextMenuActions, useContextMenuContentModel, useContextMenuListener, useContextMenuRegistration, useContextMenuState, useMenuDismissal };
2
+ export { CONTEXT_MENU_VISIBILITY_EVENT, CURRENT_PAGE_KEY, ContextMenuContext, ContextMenuGlobal, ContextMenuHeader, ContextMenuHeaderIcon, ContextMenuItem, ContextMenuProvider, ContextMenuRenderer, GLOBAL_MENU_KEY, INITIAL_POSITION, MENU_SCREEN_MARGIN, contextMenuModule, contextMenuTheme, createInitialMenuState, defineContextMenu, emitContextMenuVisibility, extractNodeText, getNextActiveIndex, isScrollLockKey, joinClassNames, positionMenu, prepareMenu, resolveAsBoolean, resolveAsValue, resolveContextMenu, resolveContextMenuPageMeta, resolveMenuHeader, resolveMenuItems, resolveNextOpenState, safeInvoke, useContextMenu, useContextMenuActions, useContextMenuContentModel, useContextMenuListener, useContextMenuRegistration, useContextMenuState, useMenuDismissal } from '../../chunk-65ZB4QTQ.js';
3
+ import '../../chunk-3MSK4XGK.js';
4
+ import '../../chunk-QI3ALLIB.js';
5
+ import '../../chunk-SYAVGTOX.js';
6
+ import '../../chunk-R2YGFJDJ.js';
7
+ import '../../chunk-XASQT4CQ.js';
8
+ import '../../chunk-7TAVYJ3A.js';
9
+ import '../../chunk-GF7TUX5I.js';
10
+ import '../../chunk-TV3WEF5G.js';
11
+ import '../../chunk-3BUP64Q6.js';
1898
12
  //# sourceMappingURL=index.js.map
1899
13
  //# sourceMappingURL=index.js.map