@ai-matrx/summon 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -1
- package/dist/chunk-server-C4KQZFIN.js +411 -0
- package/dist/chunk-server-C4KQZFIN.js.map +1 -0
- package/dist/chunk-server-H7NUWPEM.js +94 -0
- package/dist/chunk-server-H7NUWPEM.js.map +1 -0
- package/dist/chunk-server-WP7NOTZQ.js +208 -0
- package/dist/chunk-server-WP7NOTZQ.js.map +1 -0
- package/dist/dock.cjs +41 -112
- package/dist/dock.cjs.map +1 -1
- package/dist/dock.js +42 -113
- package/dist/dock.js.map +1 -1
- package/dist/handles.cjs +63 -50
- package/dist/handles.cjs.map +1 -1
- package/dist/handles.d.cts +2 -5
- package/dist/handles.d.ts +2 -5
- package/dist/handles.js +9 -259
- package/dist/handles.js.map +1 -1
- package/dist/{helpers-2NY1ha0V.d.cts → ids-83h05Ed-.d.cts} +4 -1
- package/dist/{helpers-2NY1ha0V.d.ts → ids-83h05Ed-.d.ts} +4 -1
- package/dist/index.cjs +63 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +350 -797
- package/dist/index.js.map +1 -1
- package/dist/lite.cjs +16 -76
- package/dist/lite.cjs.map +1 -1
- package/dist/lite.js +17 -77
- package/dist/lite.js.map +1 -1
- package/dist/open.cjs +86 -2
- package/dist/open.cjs.map +1 -1
- package/dist/open.d.cts +22 -2
- package/dist/open.d.ts +22 -2
- package/dist/open.js +78 -359
- package/dist/open.js.map +1 -1
- package/dist/window.cjs +167 -238
- package/dist/window.cjs.map +1 -1
- package/dist/window.d.cts +3 -1
- package/dist/window.d.ts +3 -1
- package/dist/window.js +55 -126
- package/dist/window.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/open/ids.ts","../src/handles/slot.ts","../src/open/handleEntries.ts"],"sourcesContent":["/** A fresh handle id: `crypto.randomUUID` when present, else a v4 UUID from `getRandomValues`. */\nexport function newHandleId(): string {\n const c: Crypto | undefined = typeof globalThis.crypto === \"undefined\" ? undefined : globalThis.crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const bytes = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/** The global slot that holds the handles registry (one registry per realm, whatever the bundling). */\nexport const HANDLES_SLOT = \"ai-matrx.summon.handles\";\n","// The handles registry's STATE and its insert/remove path, without the registry's verbs (trigger,\n// subscribe, requests, channels). A boot-time opener that only needs an id NOW — register a\n// callback, a payload group, an object handle; drop it on close — writes through here and never\n// puts `@ai-matrx/summon/handles` in first load; whoever later triggers or reads loads the full\n// registry, which works on the same global slot through these same functions (one removal path).\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { HANDLES_SLOT } from \"../handles/slot\";\nimport { newHandleId } from \"./ids\";\n\n/** A registry slot: a callback (invoked by triggers) or an object handle (only ever read). */\nexport type HandleEntrySlot = ((...args: never[]) => unknown) | object;\n\n/** @internal A registry entry (the handles section adds `listeners`). */\nexport interface HandleEntry {\n callback: HandleEntrySlot;\n context?: Record<string, unknown>;\n groupId?: string;\n owner?: string;\n listeners?: Array<(...args: never[]) => unknown>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\n/** @internal The registry state in its global slot (one per realm, whatever the bundling). */\nexport interface HandlesState {\n readonly callbacks: Map<string, HandleEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport interface HandleEntryOptions {\n /** A fixed id (default: a fresh `newHandleId()`); an existing entry with it is replaced. */\n id?: string;\n context?: Record<string, unknown>;\n /** Join (or start) this group; `removeHandleGroup` drops every member. */\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n onRemove?: () => void;\n}\n\n/** @internal */\nexport const handlesState = (): HandlesState =>\n globalSlot<HandlesState>(HANDLES_SLOT, () => ({\n callbacks: new Map(),\n groups: new Map(),\n owners: new Map(),\n registryListeners: new Set(),\n }));\n\n/** @internal */\nexport function notifyHandlesRegistry(): void {\n for (const listener of [...handlesState().registryListeners]) listener();\n}\n\n/**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink,\n * then runs the entry's removal hook.\n */\nexport function deleteHandleEntry(id: string, notify = true): void {\n const s = handlesState();\n const entry = s.callbacks.get(id);\n if (!entry) return;\n s.callbacks.delete(id);\n if (entry.groupId !== undefined) {\n const group = s.groups.get(entry.groupId);\n group?.delete(id);\n if (group?.size === 0) s.groups.delete(entry.groupId);\n }\n if (entry.owner !== undefined) {\n const owned = s.owners.get(entry.owner);\n owned?.delete(id);\n if (owned?.size === 0) s.owners.delete(entry.owner);\n }\n if (notify) notifyHandlesRegistry();\n entry.onRemove?.();\n}\n\n/** Register a callback or object handle NOW (no registry load); returns its id. */\nexport function registerHandleEntry(slot: HandleEntrySlot, options: HandleEntryOptions = {}): string {\n const s = handlesState();\n const id = options.id ?? newHandleId();\n if (s.callbacks.has(id)) deleteHandleEntry(id, false);\n const entry: HandleEntry = { callback: slot };\n if (options.context !== undefined) entry.context = options.context;\n if (options.groupId !== undefined) entry.groupId = options.groupId;\n if (options.owner !== undefined) entry.owner = options.owner;\n if (options.onRemove !== undefined) entry.onRemove = options.onRemove;\n s.callbacks.set(id, entry);\n if (options.groupId !== undefined) {\n const group = s.groups.get(options.groupId) ?? new Set<string>();\n group.add(id);\n s.groups.set(options.groupId, group);\n }\n if (options.owner !== undefined) {\n const owned = s.owners.get(options.owner) ?? new Set<string>();\n owned.add(id);\n s.owners.set(options.owner, owned);\n }\n notifyHandlesRegistry();\n return id;\n}\n\n/** `callbackManager.unregister(id)` without loading the registry. */\nexport function unregisterHandleEntry(id: string): void {\n deleteHandleEntry(id, true);\n}\n\n/** `callbackManager.removeGroup(groupId)` without loading the registry. */\nexport function removeHandleGroup(groupId: string): void {\n const s = handlesState();\n const group = s.groups.get(groupId);\n if (!group) return;\n s.groups.delete(groupId);\n for (const id of [...group]) deleteHandleEntry(id, false);\n notifyHandlesRegistry();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deleteHandleEntry,
|
|
3
|
+
handlesState,
|
|
4
|
+
newHandleId,
|
|
5
|
+
notifyHandlesRegistry,
|
|
6
|
+
registerHandleEntry,
|
|
7
|
+
removeHandleGroup
|
|
8
|
+
} from "./chunk-server-H7NUWPEM.js";
|
|
9
|
+
|
|
10
|
+
// src/handles/callbackManager.ts
|
|
11
|
+
var handlesState2 = () => handlesState();
|
|
12
|
+
function invoke(slot, data, context) {
|
|
13
|
+
if (typeof slot !== "function") return void 0;
|
|
14
|
+
return slot(data, context);
|
|
15
|
+
}
|
|
16
|
+
function invokeListener(listener, data, context) {
|
|
17
|
+
return listener(data, context);
|
|
18
|
+
}
|
|
19
|
+
var CallbackManager = class {
|
|
20
|
+
get s() {
|
|
21
|
+
return handlesState2();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Observe the registry itself: `listener` runs after any entry is added or removed. This lets a
|
|
25
|
+
* view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).
|
|
26
|
+
*/
|
|
27
|
+
subscribeToRegistry(listener) {
|
|
28
|
+
const listeners = this.s.registryListeners;
|
|
29
|
+
listeners.add(listener);
|
|
30
|
+
return () => {
|
|
31
|
+
listeners.delete(listener);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
notifyRegistry() {
|
|
35
|
+
notifyHandlesRegistry();
|
|
36
|
+
}
|
|
37
|
+
/** Register a callback; returns its id. */
|
|
38
|
+
register(callback) {
|
|
39
|
+
return this.registerEntry(callback, {});
|
|
40
|
+
}
|
|
41
|
+
/** Register with optional context, group and owner. */
|
|
42
|
+
registerWithContext(callback, options = {}) {
|
|
43
|
+
return this.registerEntry(callback, options);
|
|
44
|
+
}
|
|
45
|
+
/** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */
|
|
46
|
+
registerEntry(slot, options) {
|
|
47
|
+
return registerHandleEntry(slot, options);
|
|
48
|
+
}
|
|
49
|
+
/** Subscribe to results for a callback id. */
|
|
50
|
+
subscribe(callbackId, listener) {
|
|
51
|
+
const entry = this.s.callbacks.get(callbackId);
|
|
52
|
+
if (!entry) return false;
|
|
53
|
+
(entry.listeners ??= []).push(listener);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/** Unsubscribe a listener from a callback id. */
|
|
57
|
+
unsubscribe(callbackId, listener) {
|
|
58
|
+
const entry = this.s.callbacks.get(callbackId);
|
|
59
|
+
if (!entry || !entry.listeners) return false;
|
|
60
|
+
const index = entry.listeners.indexOf(listener);
|
|
61
|
+
if (index === -1) return false;
|
|
62
|
+
entry.listeners.splice(index, 1);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
/** Run a callback once, notify its listeners, then remove it. */
|
|
66
|
+
trigger(callbackId, data) {
|
|
67
|
+
const entry = this.s.callbacks.get(callbackId);
|
|
68
|
+
if (!entry) return;
|
|
69
|
+
void invoke(entry.callback, data);
|
|
70
|
+
if (entry.listeners && entry.listeners.length > 0) {
|
|
71
|
+
for (const listener of [...entry.listeners]) void invokeListener(listener, data);
|
|
72
|
+
}
|
|
73
|
+
this.deleteEntry(callbackId, true);
|
|
74
|
+
}
|
|
75
|
+
/** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */
|
|
76
|
+
triggerWithContext(callbackId, data, options) {
|
|
77
|
+
const entry = this.s.callbacks.get(callbackId);
|
|
78
|
+
if (!entry) return;
|
|
79
|
+
const mergedContext = {
|
|
80
|
+
...entry.context,
|
|
81
|
+
...options?.context,
|
|
82
|
+
...options?.progress && { progress: options.progress }
|
|
83
|
+
};
|
|
84
|
+
void invoke(entry.callback, data, mergedContext);
|
|
85
|
+
if (entry.listeners && entry.listeners.length > 0) {
|
|
86
|
+
for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);
|
|
87
|
+
}
|
|
88
|
+
if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);
|
|
89
|
+
}
|
|
90
|
+
/** Trigger every callback in a group. */
|
|
91
|
+
triggerGroup(groupId, data, options) {
|
|
92
|
+
const group = this.s.groups.get(groupId);
|
|
93
|
+
if (!group) return;
|
|
94
|
+
for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);
|
|
95
|
+
if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Run the one durable command registered for a group. A command is retained on rejection so its
|
|
99
|
+
* caller can retry, and is removed only after its promise settles successfully.
|
|
100
|
+
*/
|
|
101
|
+
async triggerGroupCommand(groupId, data, options) {
|
|
102
|
+
const group = this.s.groups.get(groupId);
|
|
103
|
+
const callbackIds = group ? [...group] : [];
|
|
104
|
+
const callbackId = callbackIds[0];
|
|
105
|
+
if (callbackIds.length !== 1 || callbackId === void 0) {
|
|
106
|
+
throw new Error(`Expected exactly one command callback for group "${groupId}", found ${callbackIds.length}`);
|
|
107
|
+
}
|
|
108
|
+
const entry = this.s.callbacks.get(callbackId);
|
|
109
|
+
if (!entry) throw new Error(`Command callback "${callbackId}" is no longer available`);
|
|
110
|
+
const mergedContext = { ...entry.context, ...options?.context };
|
|
111
|
+
await invoke(entry.callback, data, mergedContext);
|
|
112
|
+
for (const listener of entry.listeners ?? []) {
|
|
113
|
+
try {
|
|
114
|
+
await invokeListener(listener, data, mergedContext);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error("[overlays/handles] command observer failed", error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (options?.removeAfterSuccess !== false) {
|
|
120
|
+
this.deleteEntry(callbackId, true);
|
|
121
|
+
this.s.groups.delete(groupId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Report progress to a callback (or every callback of a group) without removing it. */
|
|
125
|
+
updateProgress(id, progress, options) {
|
|
126
|
+
const progressInfo = { progress, status: options?.status || "running" };
|
|
127
|
+
if (options?.error !== void 0) progressInfo.error = options.error;
|
|
128
|
+
if (options?.groupId) {
|
|
129
|
+
this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });
|
|
130
|
+
} else {
|
|
131
|
+
this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Create a new, empty group and return its id. */
|
|
135
|
+
createGroup() {
|
|
136
|
+
const groupId = newHandleId();
|
|
137
|
+
this.s.groups.set(groupId, /* @__PURE__ */ new Set());
|
|
138
|
+
return groupId;
|
|
139
|
+
}
|
|
140
|
+
/** Remove one entry (and its group/owner membership). */
|
|
141
|
+
remove(callbackId) {
|
|
142
|
+
this.deleteEntry(callbackId, true);
|
|
143
|
+
}
|
|
144
|
+
/** Alias of `remove` — the preferred name when unregistering a handle on unmount. */
|
|
145
|
+
unregister(callbackId) {
|
|
146
|
+
this.remove(callbackId);
|
|
147
|
+
}
|
|
148
|
+
/** The stored callback or object handle for an id, without invoking it. */
|
|
149
|
+
get(callbackId) {
|
|
150
|
+
return this.s.callbacks.get(callbackId)?.callback;
|
|
151
|
+
}
|
|
152
|
+
/** True when the id is registered. */
|
|
153
|
+
has(callbackId) {
|
|
154
|
+
return this.s.callbacks.has(callbackId);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Register an object handle (a widget handle, an editor API, …). The object occupies the slot;
|
|
158
|
+
* it is never invoked by `trigger`, only retrieved with `get`.
|
|
159
|
+
*/
|
|
160
|
+
registerWidgetHandle(handle, options = {}) {
|
|
161
|
+
return this.registerEntry(handle, options.owner === void 0 ? {} : { owner: options.owner });
|
|
162
|
+
}
|
|
163
|
+
/** Remove a group and all its callbacks. */
|
|
164
|
+
removeGroup(groupId) {
|
|
165
|
+
removeHandleGroup(groupId);
|
|
166
|
+
}
|
|
167
|
+
/** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */
|
|
168
|
+
disposeOwner(owner) {
|
|
169
|
+
const s = this.s;
|
|
170
|
+
const owned = s.owners.get(owner);
|
|
171
|
+
if (!owned) return 0;
|
|
172
|
+
s.owners.delete(owner);
|
|
173
|
+
const ids = [...owned];
|
|
174
|
+
for (const callbackId of ids) this.deleteEntry(callbackId, false);
|
|
175
|
+
if (ids.length > 0) this.notifyRegistry();
|
|
176
|
+
return ids.length;
|
|
177
|
+
}
|
|
178
|
+
/** Ids currently owned by `owner`. */
|
|
179
|
+
ownedBy(owner) {
|
|
180
|
+
return [...this.s.owners.get(owner) ?? []];
|
|
181
|
+
}
|
|
182
|
+
/** The member ids of a group (empty when unknown). */
|
|
183
|
+
groupMembers(groupId) {
|
|
184
|
+
return [...this.s.groups.get(groupId) ?? []];
|
|
185
|
+
}
|
|
186
|
+
/** Total registered entries (diagnostics, leak tests). */
|
|
187
|
+
size() {
|
|
188
|
+
return this.s.callbacks.size;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink
|
|
192
|
+
* (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.
|
|
193
|
+
*/
|
|
194
|
+
deleteEntry(callbackId, notify) {
|
|
195
|
+
deleteHandleEntry(callbackId, notify);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
var callbackManager = new CallbackManager();
|
|
199
|
+
function disposeOwner(owner) {
|
|
200
|
+
return callbackManager.disposeOwner(owner);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export {
|
|
204
|
+
CallbackManager,
|
|
205
|
+
callbackManager,
|
|
206
|
+
disposeOwner
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=chunk-server-WP7NOTZQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/handles/callbackManager.ts"],"sourcesContent":["/**\n * The handles registry — everything that cannot travel through Redux (callbacks, event channels,\n * one-shot requests, imperative object handles) lives here under a string id; only the id goes\n * into overlay open data.\n *\n * Successor of `@ai-matrx/chat` `utils/callbackManager.ts` with the SAME public verbs and types, so\n * moving a caller is an import-path swap. Changes (CHANGELOG 0.1.0):\n * 1. State lives in the global slot \"ai-matrx.summon.handles\", never in module scope: a\n * duplicated module graph (ESM + CJS, unsplit bundles, two packages) sees one registry.\n * 2. Removal bug fixed: `trigger` / `triggerWithContext` / `remove` deleted the entry BEFORE\n * `removeFromGroups` read it, so group membership never shrank and emptied groups were never\n * dropped. Removal now reads the entry first, in one place (`deleteEntry`).\n * 3. Owner scoping: `registerWithContext(cb, { owner })` (an InstanceKey `overlayId:instanceId`)\n * ties the entry to an open overlay instance; `disposeOwner(owner)` removes everything it\n * owns. The overlays close middleware calls it, so no opener disposes by hand.\n * 4. The object slot is handle-agnostic: `registerWidgetHandle<H extends object>(handle)` stores\n * any object (no chat `WidgetHandle` import).\n * 5. Ids come from `crypto.randomUUID` (no `uuid` dependency), with a fallback for insecure\n * contexts where `randomUUID` is absent.\n *\n * Framework-free: no React, no Redux.\n */\nimport { newHandleId } from \"./ids\";\nimport { HANDLES_SLOT } from \"./slot\";\nimport {\n deleteHandleEntry,\n handlesState as sharedHandlesState,\n notifyHandlesRegistry,\n registerHandleEntry,\n removeHandleGroup,\n type HandlesState as SharedHandlesState,\n} from \"../open/handleEntries\";\n\nexport type CallbackContext = Record<string, unknown>;\n\nexport interface ProgressInfo {\n progress?: number;\n status?: \"pending\" | \"running\" | \"completed\" | \"error\";\n error?: Error;\n}\n\nexport type Callback<T = unknown, C extends CallbackContext = CallbackContext> = (\n data: T,\n context?: C,\n) => void | Promise<void>;\n\n/** What an entry's slot holds: a callback, or an object handle retrieved with `get`. */\nexport type HandleSlot = Callback<never, never> | object;\n\nexport interface RegisterOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n}\n\nexport interface TriggerOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n progress?: ProgressInfo;\n removeAfterTrigger?: boolean;\n}\n\n/** A registry entry. `callback` is the heterogeneous slot (function or object handle). */\ninterface CallbackEntry {\n callback: HandleSlot;\n context?: CallbackContext;\n groupId?: string;\n owner?: string;\n listeners?: Array<Callback<never, never>>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\ninterface HandlesState {\n readonly callbacks: Map<string, CallbackEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport { HANDLES_SLOT };\n\n// State, insertion and removal are the light path on `./open` (`open/handleEntries.ts`), so a\n// boot-time registrant and this registry share one slot and one removal path.\nconst handlesState = (): HandlesState => sharedHandlesState() as SharedHandlesState as HandlesState;\n\n/** Invoke a slot as a callback (a function slot only; object handles are never invoked). */\nfunction invoke(slot: HandleSlot, data: unknown, context?: CallbackContext): void | Promise<void> {\n if (typeof slot !== \"function\") return undefined;\n return (slot as Callback<unknown, CallbackContext>)(data, context);\n}\n\nfunction invokeListener(listener: Callback<never, never>, data: unknown, context?: CallbackContext): void | Promise<void> {\n return (listener as Callback<unknown, CallbackContext>)(data, context);\n}\n\n/** Internal registration options beyond the public ones (used by the typed helpers). */\nexport interface InternalRegisterOptions extends RegisterOptions {\n id?: string;\n onRemove?: () => void;\n}\n\nexport class CallbackManager {\n private get s(): HandlesState {\n return handlesState();\n }\n\n /**\n * Observe the registry itself: `listener` runs after any entry is added or removed. This lets a\n * view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).\n */\n subscribeToRegistry(listener: () => void): () => void {\n const listeners = this.s.registryListeners;\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n private notifyRegistry(): void {\n notifyHandlesRegistry();\n }\n\n /** Register a callback; returns its id. */\n register<T>(callback: Callback<T>): string {\n return this.registerEntry(callback as HandleSlot, {});\n }\n\n /** Register with optional context, group and owner. */\n registerWithContext<T, C extends CallbackContext = CallbackContext>(\n callback: Callback<T, C>,\n options: RegisterOptions<C> = {},\n ): string {\n return this.registerEntry(callback as HandleSlot, options);\n }\n\n /** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */\n registerEntry(slot: HandleSlot, options: InternalRegisterOptions): string {\n return registerHandleEntry(slot, options);\n }\n\n /** Subscribe to results for a callback id. */\n subscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return false;\n (entry.listeners ??= []).push(listener as Callback<never, never>);\n return true;\n }\n\n /** Unsubscribe a listener from a callback id. */\n unsubscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry || !entry.listeners) return false;\n const index = entry.listeners.indexOf(listener as Callback<never, never>);\n if (index === -1) return false;\n entry.listeners.splice(index, 1);\n return true;\n }\n\n /** Run a callback once, notify its listeners, then remove it. */\n trigger<T>(callbackId: string, data: T): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n void invoke(entry.callback, data);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data);\n }\n this.deleteEntry(callbackId, true);\n }\n\n /** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */\n triggerWithContext<T, C extends CallbackContext = CallbackContext>(\n callbackId: string,\n data: T,\n options?: TriggerOptions<C>,\n ): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n const mergedContext: CallbackContext = {\n ...entry.context,\n ...options?.context,\n ...(options?.progress && { progress: options.progress }),\n };\n void invoke(entry.callback, data, mergedContext);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);\n }\n if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);\n }\n\n /** Trigger every callback in a group. */\n triggerGroup<T, C extends CallbackContext = CallbackContext>(groupId: string, data: T, options?: TriggerOptions<C>): void {\n const group = this.s.groups.get(groupId);\n if (!group) return;\n for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);\n if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);\n }\n\n /**\n * Run the one durable command registered for a group. A command is retained on rejection so its\n * caller can retry, and is removed only after its promise settles successfully.\n */\n async triggerGroupCommand<T, C extends CallbackContext = CallbackContext>(\n groupId: string,\n data: T,\n options?: { context?: C; removeAfterSuccess?: boolean },\n ): Promise<void> {\n const group = this.s.groups.get(groupId);\n const callbackIds = group ? [...group] : [];\n const callbackId = callbackIds[0];\n if (callbackIds.length !== 1 || callbackId === undefined) {\n throw new Error(`Expected exactly one command callback for group \"${groupId}\", found ${callbackIds.length}`);\n }\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) throw new Error(`Command callback \"${callbackId}\" is no longer available`);\n\n const mergedContext: CallbackContext = { ...entry.context, ...options?.context };\n await invoke(entry.callback, data, mergedContext);\n\n // Observers never control command settlement: the command already acknowledged.\n for (const listener of entry.listeners ?? []) {\n try {\n await invokeListener(listener, data, mergedContext);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"[overlays/handles] command observer failed\", error);\n }\n }\n\n if (options?.removeAfterSuccess !== false) {\n this.deleteEntry(callbackId, true);\n this.s.groups.delete(groupId);\n }\n }\n\n /** Report progress to a callback (or every callback of a group) without removing it. */\n updateProgress(\n id: string,\n progress: number,\n options?: { status?: ProgressInfo[\"status\"]; error?: Error; groupId?: boolean },\n ): void {\n const progressInfo: ProgressInfo = { progress, status: options?.status || \"running\" };\n if (options?.error !== undefined) progressInfo.error = options.error;\n if (options?.groupId) {\n this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });\n } else {\n this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });\n }\n }\n\n /** Create a new, empty group and return its id. */\n createGroup(): string {\n const groupId = newHandleId();\n this.s.groups.set(groupId, new Set());\n return groupId;\n }\n\n /** Remove one entry (and its group/owner membership). */\n remove(callbackId: string): void {\n this.deleteEntry(callbackId, true);\n }\n\n /** Alias of `remove` — the preferred name when unregistering a handle on unmount. */\n unregister(callbackId: string): void {\n this.remove(callbackId);\n }\n\n /** The stored callback or object handle for an id, without invoking it. */\n get<T = unknown>(callbackId: string): T | undefined {\n return this.s.callbacks.get(callbackId)?.callback as T | undefined;\n }\n\n /** True when the id is registered. */\n has(callbackId: string): boolean {\n return this.s.callbacks.has(callbackId);\n }\n\n /**\n * Register an object handle (a widget handle, an editor API, …). The object occupies the slot;\n * it is never invoked by `trigger`, only retrieved with `get`.\n */\n registerWidgetHandle<H extends object>(handle: H, options: { owner?: string } = {}): string {\n return this.registerEntry(handle, options.owner === undefined ? {} : { owner: options.owner });\n }\n\n /** Remove a group and all its callbacks. */\n removeGroup(groupId: string): void {\n removeHandleGroup(groupId);\n }\n\n /** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */\n disposeOwner(owner: string): number {\n const s = this.s;\n const owned = s.owners.get(owner);\n if (!owned) return 0;\n s.owners.delete(owner);\n const ids = [...owned];\n for (const callbackId of ids) this.deleteEntry(callbackId, false);\n if (ids.length > 0) this.notifyRegistry();\n return ids.length;\n }\n\n /** Ids currently owned by `owner`. */\n ownedBy(owner: string): readonly string[] {\n return [...(this.s.owners.get(owner) ?? [])];\n }\n\n /** The member ids of a group (empty when unknown). */\n groupMembers(groupId: string): readonly string[] {\n return [...(this.s.groups.get(groupId) ?? [])];\n }\n\n /** Total registered entries (diagnostics, leak tests). */\n size(): number {\n return this.s.callbacks.size;\n }\n\n /**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink\n * (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.\n */\n private deleteEntry(callbackId: string, notify: boolean): void {\n deleteHandleEntry(callbackId, notify);\n }\n}\n\n/** The process-wide handles registry (state in the global slot; every instance shares it). */\nexport const callbackManager = new CallbackManager();\n\n/** Remove everything owned by an open instance (`overlayId:instanceId`). */\nexport function disposeOwner(owner: string): number {\n return callbackManager.disposeOwner(owner);\n}\n"],"mappings":"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","names":["handlesState"]}
|
package/dist/dock.cjs
CHANGED
|
@@ -207,10 +207,6 @@ function useDock(options) {
|
|
|
207
207
|
const [pending, setPending] = (0, import_react2.useState)(null);
|
|
208
208
|
const [geometry, setGeometry] = (0, import_react2.useState)({ viewport: SSR_VIEWPORT, size: { width: 0, height: 0 } });
|
|
209
209
|
const [nudge, setNudge] = (0, import_react2.useState)(null);
|
|
210
|
-
const [dragging, setDragging] = (0, import_react2.useState)(false);
|
|
211
|
-
const pressRef = (0, import_react2.useRef)(null);
|
|
212
|
-
const frameRef = (0, import_react2.useRef)(null);
|
|
213
|
-
const adoptingRef = (0, import_react2.useRef)(false);
|
|
214
210
|
const restoredRef = (0, import_react2.useRef)(null);
|
|
215
211
|
const chosen = isControlled ? pending && samePosition(pending.forProp, controlled) ? pending.value : controlled : local;
|
|
216
212
|
const raw = chosen ?? defaultPosition;
|
|
@@ -307,11 +303,6 @@ function useDock(options) {
|
|
|
307
303
|
}
|
|
308
304
|
}
|
|
309
305
|
const renderedOffset = placed && offset ? fromScreen(placed, rawCorner, geometry.size, geometry.viewport) : offset;
|
|
310
|
-
(0, import_react2.useLayoutEffect)(() => {
|
|
311
|
-
if (!adoptingRef.current) return;
|
|
312
|
-
adoptingRef.current = false;
|
|
313
|
-
element?.style.removeProperty("translate");
|
|
314
|
-
});
|
|
315
306
|
const summonStore = (0, import_react3.useOptionalSummonStore)();
|
|
316
307
|
const laneZ = (0, import_react2.useSyncExternalStore)(
|
|
317
308
|
summonStore ? summonStore.subscribe : noopSubscribe,
|
|
@@ -326,16 +317,8 @@ function useDock(options) {
|
|
|
326
317
|
};
|
|
327
318
|
}, [summonStore, id]);
|
|
328
319
|
const zIndex = laneZ ?? baseZ;
|
|
329
|
-
(0, import_react2.useEffect)(
|
|
330
|
-
() => () => {
|
|
331
|
-
pressRef.current?.cleanup();
|
|
332
|
-
pressRef.current = null;
|
|
333
|
-
},
|
|
334
|
-
[]
|
|
335
|
-
);
|
|
336
320
|
const commit = (0, import_react2.useCallback)((next) => {
|
|
337
321
|
const current = live.current;
|
|
338
|
-
adoptingRef.current = true;
|
|
339
322
|
if (current.isControlled) setPending({ value: next, forProp: current.controlled ?? null });
|
|
340
323
|
else {
|
|
341
324
|
setLocal(next);
|
|
@@ -343,106 +326,52 @@ function useDock(options) {
|
|
|
343
326
|
}
|
|
344
327
|
current.onPositionChange?.(next);
|
|
345
328
|
}, []);
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
pressRef.current?.cleanup();
|
|
329
|
+
const pressRef = (0, import_react2.useRef)({ corner, viewport: SSR_VIEWPORT });
|
|
330
|
+
const keepScreen = (point, size, viewport) => toScreen(clampOffset(fromScreen(point, "top-left", size, viewport), "top-left", size, viewport, live.current.clamp), "top-left", size, viewport);
|
|
331
|
+
const drag = (0, import_design_system.useDragMove)({
|
|
332
|
+
element,
|
|
333
|
+
enabled: draggable,
|
|
334
|
+
touch,
|
|
335
|
+
threshold,
|
|
336
|
+
ignore,
|
|
337
|
+
onPress: () => {
|
|
356
338
|
if (raiseOnPress && summonStore) summonStore.dispatch((0, import_summon.raiseTransientWindow)(id));
|
|
357
|
-
|
|
358
|
-
|
|
339
|
+
},
|
|
340
|
+
// A placed dock starts from its committed offset (the host may add lifts on top of it);
|
|
341
|
+
// a resting dock starts from where it is on screen.
|
|
342
|
+
getRect: () => {
|
|
359
343
|
const viewport = readViewport();
|
|
360
344
|
const size = measure(element);
|
|
361
|
-
const press = {
|
|
362
|
-
pointerId: event.pointerId,
|
|
363
|
-
startX: event.clientX,
|
|
364
|
-
startY: event.clientY,
|
|
365
|
-
origin: { left: rect.left, top: rect.top },
|
|
366
|
-
size,
|
|
367
|
-
viewport,
|
|
368
|
-
active: false,
|
|
369
|
-
latest: { left: rect.left, top: rect.top },
|
|
370
|
-
release: null,
|
|
371
|
-
cleanup: () => void 0
|
|
372
|
-
};
|
|
373
|
-
const startOffset = live.current.offset ?? fromScreen(press.origin, live.current.rawCorner, size, viewport);
|
|
374
345
|
const startCorner = live.current.rawCorner;
|
|
375
|
-
const
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
element.style.translate = `${active.latest.left - startScreen.left}px ${active.latest.top - startScreen.top}px`;
|
|
381
|
-
};
|
|
382
|
-
const move = (e) => {
|
|
383
|
-
if (e.pointerId !== press.pointerId) return;
|
|
384
|
-
const dx = e.clientX - press.startX;
|
|
385
|
-
const dy = e.clientY - press.startY;
|
|
386
|
-
const opts = live.current;
|
|
387
|
-
if (!press.active) {
|
|
388
|
-
if (Math.abs(dx) < opts.threshold && Math.abs(dy) < opts.threshold) return;
|
|
389
|
-
press.active = true;
|
|
390
|
-
press.release = (0, import_design_system.holdDragCursor)("grabbing");
|
|
391
|
-
setDragging(true);
|
|
392
|
-
}
|
|
393
|
-
const desired = fromScreen({ left: startScreen.left + dx, top: startScreen.top + dy }, startCorner, size, viewport);
|
|
394
|
-
let next = toScreen(clampOffset(desired, startCorner, size, viewport, opts.clamp), startCorner, size, viewport);
|
|
395
|
-
if (opts.avoid) {
|
|
396
|
-
const keep = (p) => toScreen(clampOffset(fromScreen(p, "top-left", size, viewport), "top-left", size, viewport, opts.clamp), "top-left", size, viewport);
|
|
397
|
-
next = avoidExclusions(next, size, excludedRects(opts.avoid, element), opts.avoid.gap ?? DEFAULT_DOCK_MARGIN, keep);
|
|
398
|
-
}
|
|
399
|
-
press.latest = next;
|
|
400
|
-
if (frameRef.current === null) {
|
|
401
|
-
if (typeof window.requestAnimationFrame === "function") frameRef.current = window.requestAnimationFrame(paint);
|
|
402
|
-
else paint();
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
const end = (e) => {
|
|
406
|
-
if (e.pointerId !== press.pointerId) return;
|
|
407
|
-
press.cleanup();
|
|
408
|
-
pressRef.current = null;
|
|
409
|
-
if (!press.active) return;
|
|
410
|
-
press.release?.();
|
|
411
|
-
setDragging(false);
|
|
412
|
-
const opts = live.current;
|
|
413
|
-
const finalCorner = opts.snapToCorner ? nearestCorner(press.latest, size, viewport) : startCorner;
|
|
414
|
-
const finalOffset = clampOffset(fromScreen(press.latest, finalCorner, size, viewport), finalCorner, size, viewport, opts.clamp);
|
|
415
|
-
commit(finalCorner === corner ? { x: finalOffset.x, y: finalOffset.y } : { ...finalOffset, corner: finalCorner });
|
|
416
|
-
const swallow = (click) => {
|
|
417
|
-
click.stopPropagation();
|
|
418
|
-
click.preventDefault();
|
|
419
|
-
};
|
|
420
|
-
window.addEventListener("click", swallow, { capture: true, once: true });
|
|
421
|
-
window.setTimeout(() => window.removeEventListener("click", swallow, { capture: true }), 0);
|
|
422
|
-
};
|
|
423
|
-
press.cleanup = () => {
|
|
424
|
-
window.removeEventListener("pointermove", move);
|
|
425
|
-
window.removeEventListener("pointerup", end);
|
|
426
|
-
window.removeEventListener("pointercancel", end);
|
|
427
|
-
if (frameRef.current !== null) {
|
|
428
|
-
window.cancelAnimationFrame?.(frameRef.current);
|
|
429
|
-
frameRef.current = null;
|
|
430
|
-
}
|
|
431
|
-
if (!press.active) return;
|
|
432
|
-
press.release?.();
|
|
433
|
-
press.release = null;
|
|
434
|
-
};
|
|
435
|
-
pressRef.current = press;
|
|
436
|
-
window.addEventListener("pointermove", move);
|
|
437
|
-
window.addEventListener("pointerup", end);
|
|
438
|
-
window.addEventListener("pointercancel", end);
|
|
346
|
+
const rect = element?.getBoundingClientRect();
|
|
347
|
+
const startOffset = live.current.offset ?? fromScreen({ left: rect?.left ?? 0, top: rect?.top ?? 0 }, startCorner, size, viewport);
|
|
348
|
+
pressRef.current = { corner: startCorner, viewport };
|
|
349
|
+
const start = toScreen(startOffset, startCorner, size, viewport);
|
|
350
|
+
return { x: start.left, y: start.top, ...size };
|
|
439
351
|
},
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
352
|
+
constrain: (rect) => {
|
|
353
|
+
const { corner: startCorner, viewport } = pressRef.current;
|
|
354
|
+
const size = { width: rect.width, height: rect.height };
|
|
355
|
+
const opts = live.current;
|
|
356
|
+
const desired = fromScreen({ left: rect.x, top: rect.y }, startCorner, size, viewport);
|
|
357
|
+
let next = toScreen(clampOffset(desired, startCorner, size, viewport, opts.clamp), startCorner, size, viewport);
|
|
358
|
+
if (opts.avoid)
|
|
359
|
+
next = avoidExclusions(next, size, excludedRects(opts.avoid, element), opts.avoid.gap ?? DEFAULT_DOCK_MARGIN, (p) => keepScreen(p, size, viewport));
|
|
360
|
+
return { ...rect, x: next.left, y: next.top };
|
|
361
|
+
},
|
|
362
|
+
onCommit: (rect) => {
|
|
363
|
+
const { corner: startCorner, viewport } = pressRef.current;
|
|
364
|
+
const size = { width: rect.width, height: rect.height };
|
|
365
|
+
const latest = { left: rect.x, top: rect.y };
|
|
366
|
+
const opts = live.current;
|
|
367
|
+
const finalCorner = opts.snapToCorner ? nearestCorner(latest, size, viewport) : startCorner;
|
|
368
|
+
const finalOffset = clampOffset(fromScreen(latest, finalCorner, size, viewport), finalCorner, size, viewport, opts.clamp);
|
|
369
|
+
commit(finalCorner === corner ? { x: finalOffset.x, y: finalOffset.y } : { ...finalOffset, corner: finalCorner });
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
const dragging = drag.dragging;
|
|
373
|
+
const onPointerDown = drag.onPointerDown;
|
|
374
|
+
const reset = (0, import_react2.useCallback)(() => commit(null), [commit]);
|
|
446
375
|
let style;
|
|
447
376
|
if (renderedOffset) {
|
|
448
377
|
const left = rawCorner.endsWith("left");
|