@nimbalyst/extension-sdk 0.3.0 → 0.5.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.
- package/CHANGELOG.md +24 -0
- package/dist/__tests__/buildEmitStability.test.d.ts +2 -0
- package/dist/__tests__/buildEmitStability.test.d.ts.map +1 -0
- package/dist/__tests__/buildEmitStability.test.js +33 -0
- package/dist/__tests__/createTextCollabContentAdapter.test.js +1 -0
- package/dist/__tests__/editorHostCapabilities.test.d.ts +2 -0
- package/dist/__tests__/editorHostCapabilities.test.d.ts.map +1 -0
- package/dist/__tests__/editorHostCapabilities.test.js +24 -0
- package/dist/__tests__/fileDirectoryTree.test.js +1 -0
- package/dist/__tests__/fileMask.test.d.ts +2 -0
- package/dist/__tests__/fileMask.test.d.ts.map +1 -0
- package/dist/__tests__/fileMask.test.js +33 -0
- package/dist/__tests__/manifestValidation.agentProviders.test.js +1 -0
- package/dist/__tests__/textReplacement.test.d.ts +2 -0
- package/dist/__tests__/textReplacement.test.d.ts.map +1 -0
- package/dist/__tests__/textReplacement.test.js +31 -0
- package/dist/__tests__/useCollaborativeEditor.offline.test.d.ts +2 -0
- package/dist/__tests__/useCollaborativeEditor.offline.test.d.ts.map +1 -0
- package/dist/__tests__/useCollaborativeEditor.offline.test.js +76 -0
- package/dist/__tests__/useEditorLifecycle.test.d.ts +2 -0
- package/dist/__tests__/useEditorLifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/useEditorLifecycle.test.js +60 -0
- package/dist/agents/AgentProtocol.d.ts +12 -0
- package/dist/agents/AgentProtocol.d.ts.map +1 -1
- package/dist/collab/index.d.ts +14 -0
- package/dist/collab/index.d.ts.map +1 -0
- package/dist/collab/index.js +13 -0
- package/dist/collab/origins.d.ts +15 -0
- package/dist/collab/origins.d.ts.map +1 -0
- package/dist/collab/origins.js +14 -0
- package/dist/collab/textReplacement.d.ts +7 -0
- package/dist/collab/textReplacement.d.ts.map +1 -0
- package/dist/collab/textReplacement.js +27 -0
- package/dist/editorHostCapabilities.d.ts +31 -0
- package/dist/editorHostCapabilities.d.ts.map +1 -0
- package/dist/editorHostCapabilities.js +35 -0
- package/dist/electronMain.d.ts +12 -0
- package/dist/electronMain.d.ts.map +1 -0
- package/dist/electronMain.js +11 -0
- package/dist/fileMask.d.ts +17 -0
- package/dist/fileMask.d.ts.map +1 -0
- package/dist/fileMask.js +45 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/types/editor.d.ts +126 -2
- package/dist/types/editor.d.ts.map +1 -1
- package/dist/types/extension.d.ts +50 -5
- package/dist/types/extension.d.ts.map +1 -1
- package/dist/types/panel.d.ts +43 -0
- package/dist/types/panel.d.ts.map +1 -1
- package/dist/useCollaborativeEditor.d.ts +22 -29
- package/dist/useCollaborativeEditor.d.ts.map +1 -1
- package/dist/useCollaborativeEditor.js +105 -95
- package/dist/useEditorLifecycle.d.ts.map +1 -1
- package/dist/useEditorLifecycle.js +2 -0
- package/package.json +12 -3
|
@@ -29,29 +29,16 @@
|
|
|
29
29
|
* }
|
|
30
30
|
* ```
|
|
31
31
|
*
|
|
32
|
-
* Bootstrap-race safety:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* Bootstrap-race safety: two clients seeding the same file must assign the
|
|
33
|
+
* same keys, so seed identity must be deterministic from stable file data or
|
|
34
|
+
* order, not random. In a keyed structured document, assign each key once and
|
|
35
|
+
* never recompute it from mutable content; changing it replaces the shared
|
|
36
|
+
* type and loses its history and concurrent edits.
|
|
37
37
|
*/
|
|
38
38
|
import * as Y from 'yjs';
|
|
39
39
|
import type { CollaborationStatus, CollaboratorInfo, EditorHost } from './types/editor.js';
|
|
40
40
|
import type { CollabCodec } from './types/collab.js';
|
|
41
|
-
|
|
42
|
-
* Origin tag used when the SDK wraps `initializeFromContent` in a Y.Doc
|
|
43
|
-
* transaction. Extension bindings can compare a transaction's origin
|
|
44
|
-
* against this to suppress their own change handlers during seeding
|
|
45
|
-
* (otherwise the binding would echo the seed back into local-edit state).
|
|
46
|
-
*
|
|
47
|
-
* ```ts
|
|
48
|
-
* yDoc.on('update', (update, origin) => {
|
|
49
|
-
* if (origin === COLLAB_INIT_ORIGIN) return;
|
|
50
|
-
* // ... apply remote change
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export declare const COLLAB_INIT_ORIGIN: unique symbol;
|
|
41
|
+
export { COLLAB_INIT_ORIGIN } from './collab/origins.js';
|
|
55
42
|
/** Context passed to the binding factory (`bind` / `createBinding`). */
|
|
56
43
|
export interface CollabBindContext {
|
|
57
44
|
yDoc: Y.Doc;
|
|
@@ -62,11 +49,7 @@ export interface CollabBindContext {
|
|
|
62
49
|
color: string;
|
|
63
50
|
};
|
|
64
51
|
}
|
|
65
|
-
export type CollabBindResult =
|
|
66
|
-
destroy: () => void;
|
|
67
|
-
} | Promise<{
|
|
68
|
-
destroy: () => void;
|
|
69
|
-
}>;
|
|
52
|
+
export type CollabBindResult = CollaborativeBindingHandle | Promise<CollaborativeBindingHandle>;
|
|
70
53
|
/**
|
|
71
54
|
* Preferred config: pass the SAME pure {@link CollabCodec} the extension
|
|
72
55
|
* registered via `context.services.collab.registerContentAdapter(...)`, plus
|
|
@@ -119,8 +102,9 @@ export interface UseCollaborativeEditorLegacyConfig {
|
|
|
119
102
|
/**
|
|
120
103
|
* @deprecated Provide `seedFromFile` on the codec. Populate the Y.Doc from
|
|
121
104
|
* raw file content when this client is first. Called inside a
|
|
122
|
-
* `yDoc.transact(..., COLLAB_INIT_ORIGIN)`.
|
|
123
|
-
*
|
|
105
|
+
* `yDoc.transact(..., COLLAB_INIT_ORIGIN)`. Seed keys deterministically from
|
|
106
|
+
* the file so concurrent seeds converge. In keyed structured documents,
|
|
107
|
+
* never recompute an assigned key from mutable content.
|
|
124
108
|
*/
|
|
125
109
|
initializeFromContent(yDoc: Y.Doc, content: string | ArrayBuffer): void;
|
|
126
110
|
onSeedOutcome?(outcome: {
|
|
@@ -129,6 +113,17 @@ export interface UseCollaborativeEditorLegacyConfig {
|
|
|
129
113
|
}): void;
|
|
130
114
|
}
|
|
131
115
|
export type UseCollaborativeEditorConfig = UseCollaborativeEditorCodecConfig | UseCollaborativeEditorLegacyConfig;
|
|
116
|
+
/**
|
|
117
|
+
* What a binding factory returns. `syncNow` is optional and opt-in: a binding
|
|
118
|
+
* that debounces its local pushes should expose it so the hook can register it
|
|
119
|
+
* as the host's pending-content drain (see
|
|
120
|
+
* `CollaborationContext.registerContentFlush`). Bindings that write through to
|
|
121
|
+
* the Y.Doc synchronously can omit it.
|
|
122
|
+
*/
|
|
123
|
+
export interface CollaborativeBindingHandle {
|
|
124
|
+
destroy: () => void;
|
|
125
|
+
syncNow?: () => void | Promise<void>;
|
|
126
|
+
}
|
|
132
127
|
export interface UseCollaborativeEditorResult {
|
|
133
128
|
/** True when `host.collaboration` is defined. */
|
|
134
129
|
isCollaborative: boolean;
|
|
@@ -143,9 +138,7 @@ export interface UseCollaborativeEditorResult {
|
|
|
143
138
|
* The binding handle once the binding factory has run, or `null` until
|
|
144
139
|
* collaboration is ready / when not collab.
|
|
145
140
|
*/
|
|
146
|
-
binding:
|
|
147
|
-
destroy: () => void;
|
|
148
|
-
} | null;
|
|
141
|
+
binding: CollaborativeBindingHandle | null;
|
|
149
142
|
/**
|
|
150
143
|
* Non-null when the first-open seed failed or its flush was not confirmed by
|
|
151
144
|
* the server. Hosts read this to surface the failure (pending-seed toast)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCollaborativeEditor.d.ts","sourceRoot":"","sources":["../src/useCollaborativeEditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,KAAK,EAEV,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"useCollaborativeEditor.d.ts","sourceRoot":"","sources":["../src/useCollaborativeEditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,KAAK,EAEV,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AAG9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC;IACZ,SAAS,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC;IACrD,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED,MAAM,MAAM,gBAAgB,GACxB,0BAA0B,GAC1B,OAAO,CAAC,0BAA0B,CAAC,CAAC;AAExC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iCAAiC;IAChD,kFAAkF;IAClF,KAAK,EAAE,WAAW,CAAC;IAEnB;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;IAE/C;;;;OAIG;IACH,aAAa,CAAC,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED;;;;;GAKG;AACH,MAAM,WAAW,kCAAkC;IACjD,kDAAkD;IAClD,aAAa,CAAC,GAAG,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;IAExD;;;OAGG;IACH,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC;IAE/B;;;;;;OAMG;IACH,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAExE,aAAa,CAAC,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,MAAM,4BAA4B,GACpC,iCAAiC,GACjC,kCAAkC,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,4BAA4B;IAC3C,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB,0EAA0E;IAC1E,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;OAGG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC7C;;;OAGG;IACH,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC3C;;;;OAIG;IACH,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAyCD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAC5D,OAAO,CAIT;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,4BAA4B,GACnC,4BAA4B,CAoM9B"}
|
|
@@ -29,28 +29,16 @@
|
|
|
29
29
|
* }
|
|
30
30
|
* ```
|
|
31
31
|
*
|
|
32
|
-
* Bootstrap-race safety:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* Bootstrap-race safety: two clients seeding the same file must assign the
|
|
33
|
+
* same keys, so seed identity must be deterministic from stable file data or
|
|
34
|
+
* order, not random. In a keyed structured document, assign each key once and
|
|
35
|
+
* never recompute it from mutable content; changing it replaces the shared
|
|
36
|
+
* type and loses its history and concurrent edits.
|
|
37
37
|
*/
|
|
38
38
|
import { useEffect, useRef, useState } from 'react';
|
|
39
39
|
import * as Y from 'yjs';
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* transaction. Extension bindings can compare a transaction's origin
|
|
43
|
-
* against this to suppress their own change handlers during seeding
|
|
44
|
-
* (otherwise the binding would echo the seed back into local-edit state).
|
|
45
|
-
*
|
|
46
|
-
* ```ts
|
|
47
|
-
* yDoc.on('update', (update, origin) => {
|
|
48
|
-
* if (origin === COLLAB_INIT_ORIGIN) return;
|
|
49
|
-
* // ... apply remote change
|
|
50
|
-
* });
|
|
51
|
-
* ```
|
|
52
|
-
*/
|
|
53
|
-
export const COLLAB_INIT_ORIGIN = Symbol('nimbalyst:collab-init');
|
|
40
|
+
import { COLLAB_INIT_ORIGIN } from './collab/origins.js';
|
|
41
|
+
export { COLLAB_INIT_ORIGIN } from './collab/origins.js';
|
|
54
42
|
function defaultIsEmpty(yDoc) {
|
|
55
43
|
// A fully empty Y.Doc encodes to ~2 bytes (header only).
|
|
56
44
|
return Y.encodeStateAsUpdate(yDoc).byteLength <= 2;
|
|
@@ -151,92 +139,112 @@ export function useCollaborativeEditor(host, config) {
|
|
|
151
139
|
return;
|
|
152
140
|
let cancelled = false;
|
|
153
141
|
let handle = null;
|
|
142
|
+
let starting = false;
|
|
143
|
+
let unregisterContentFlush = null;
|
|
154
144
|
const tryStart = async () => {
|
|
155
|
-
if (cancelled || handle)
|
|
156
|
-
return;
|
|
157
|
-
if (collab.getStatus() !== 'connected')
|
|
145
|
+
if (cancelled || handle || starting)
|
|
158
146
|
return;
|
|
159
147
|
const cfg = resolveConfig(configRef.current);
|
|
160
|
-
|
|
161
|
-
// the Y.Doc
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
cfg.seed(collab.yDoc, content);
|
|
187
|
-
}, COLLAB_INIT_ORIGIN);
|
|
188
|
-
// Durability: the seed the user sees locally must reach the server
|
|
189
|
-
// before this provider can tear down. flushWithAck resolves only
|
|
190
|
-
// after a server-persisted ack; flushLocalState is the deprecated
|
|
191
|
-
// fire-and-forget fallback for older hosts.
|
|
192
|
-
const flushed = collab.flushWithAck
|
|
193
|
-
? await collab.flushWithAck()
|
|
194
|
-
: (await collab.flushLocalState?.(), true);
|
|
195
|
-
if (!flushed) {
|
|
196
|
-
const err = new Error('Seed flush was not confirmed by the server before timeout; content may not have persisted.');
|
|
197
|
-
console.warn('[useCollaborativeEditor]', err.message);
|
|
198
|
-
setSeedError(err);
|
|
199
|
-
collab.reportSeedOutcome?.({ ok: false, error: err });
|
|
200
|
-
cfg.onSeedOutcome?.({ ok: false, error: err });
|
|
148
|
+
const connected = collab.getStatus() === 'connected';
|
|
149
|
+
// A complete local replica can hydrate the Y.Doc before any network is
|
|
150
|
+
// available. Bind that non-empty document immediately so custom editors
|
|
151
|
+
// can reopen offline; only first-open seeding requires a live transport.
|
|
152
|
+
if (!connected && cfg.isEmpty(collab.yDoc))
|
|
153
|
+
return;
|
|
154
|
+
starting = true;
|
|
155
|
+
try {
|
|
156
|
+
// NEVER seed when the transport skipped payloads it could not decode:
|
|
157
|
+
// the Y.Doc looking empty then means "content exists but is unreadable
|
|
158
|
+
// on this client", and seeding would write a default document over the
|
|
159
|
+
// real content for every client (the "Untitled map" clobber).
|
|
160
|
+
const undecoded = collab.hasUndecodedContent?.() === true;
|
|
161
|
+
if (connected && !undecoded && cfg.isEmpty(collab.yDoc)) {
|
|
162
|
+
try {
|
|
163
|
+
const content = await collab.loadInitialContent();
|
|
164
|
+
if (cancelled)
|
|
165
|
+
return;
|
|
166
|
+
// NEVER seed from empty content. A host that has no bytes for this
|
|
167
|
+
// document (reopen of an already-shared doc: no in-memory share
|
|
168
|
+
// payload, no file) returns ''/empty -- seeding from that writes a
|
|
169
|
+
// default document into the shared room and clobbers whatever the
|
|
170
|
+
// room's real content is for every client. Fall through to bind:
|
|
171
|
+
// the room content (or a teammate's seed) will populate the doc.
|
|
172
|
+
if (!hasSeedableContent(content)) {
|
|
173
|
+
console.warn('[useCollaborativeEditor] Skipping first-open seed: host returned empty initial content.');
|
|
201
174
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
175
|
+
// Re-check emptiness in case another client seeded while we were
|
|
176
|
+
// awaiting -- they would have raced through the WebSocket and
|
|
177
|
+
// applied their update during our await gap. Avoid double-seeding
|
|
178
|
+
// in that case; CRDT merge would otherwise insert duplicate
|
|
179
|
+
// content unless the seed is fully deterministic.
|
|
180
|
+
else if (cfg.isEmpty(collab.yDoc)) {
|
|
181
|
+
collab.yDoc.transact(() => {
|
|
182
|
+
cfg.seed(collab.yDoc, content);
|
|
183
|
+
}, COLLAB_INIT_ORIGIN);
|
|
184
|
+
// Durability: the seed the user sees locally must reach the server
|
|
185
|
+
// before this provider can tear down. flushWithAck resolves only
|
|
186
|
+
// after a server-persisted ack; flushLocalState is the deprecated
|
|
187
|
+
// fire-and-forget fallback for older hosts.
|
|
188
|
+
const flushed = collab.flushWithAck
|
|
189
|
+
? await collab.flushWithAck()
|
|
190
|
+
: (await collab.flushLocalState?.(), true);
|
|
191
|
+
if (!flushed) {
|
|
192
|
+
const err = new Error('Seed flush was not confirmed by the server before timeout; content may not have persisted.');
|
|
193
|
+
console.warn('[useCollaborativeEditor]', err.message);
|
|
194
|
+
setSeedError(err);
|
|
195
|
+
collab.reportSeedOutcome?.({ ok: false, error: err });
|
|
196
|
+
cfg.onSeedOutcome?.({ ok: false, error: err });
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
setSeedError(null);
|
|
200
|
+
collab.reportSeedOutcome?.({ ok: true });
|
|
201
|
+
cfg.onSeedOutcome?.({ ok: true });
|
|
202
|
+
}
|
|
206
203
|
}
|
|
207
204
|
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
console.error('[useCollaborativeEditor] Failed to load/seed initial content:', err);
|
|
207
|
+
setSeedError(err);
|
|
208
|
+
collab.reportSeedOutcome?.({ ok: false, error: err });
|
|
209
|
+
cfg.onSeedOutcome?.({ ok: false, error: err });
|
|
210
|
+
// Continue with bind -- the doc may still be usable once another
|
|
211
|
+
// client seeds it.
|
|
212
|
+
}
|
|
208
213
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
collab.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// imperative API. Destroy the freshly-built handle so any
|
|
229
|
-
// observers/awareness subscriptions inside it get cleaned up.
|
|
230
|
-
try {
|
|
231
|
-
resolved.destroy();
|
|
214
|
+
if (cancelled)
|
|
215
|
+
return;
|
|
216
|
+
const created = cfg.bind({
|
|
217
|
+
yDoc: collab.yDoc,
|
|
218
|
+
awareness: collab.awareness,
|
|
219
|
+
user: collab.user,
|
|
220
|
+
});
|
|
221
|
+
const resolved = created instanceof Promise ? await created : created;
|
|
222
|
+
if (cancelled) {
|
|
223
|
+
// Effect unmounted while we were awaiting the extension's
|
|
224
|
+
// imperative API. Destroy the freshly-built handle so any
|
|
225
|
+
// observers/awareness subscriptions inside it get cleaned up.
|
|
226
|
+
try {
|
|
227
|
+
resolved.destroy();
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
console.error('[useCollaborativeEditor] post-cancel destroy failed:', err);
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
232
233
|
}
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
handle = resolved;
|
|
235
|
+
// A binding that debounces its local pushes holds the newest edit
|
|
236
|
+
// outside the CRDT. Hand the host a drain so it can complete a write
|
|
237
|
+
// before reporting it done -- notably after a mutating AI tool, which
|
|
238
|
+
// otherwise returns success while the edit is still in the debounce.
|
|
239
|
+
if (typeof handle.syncNow === 'function') {
|
|
240
|
+
const syncNow = handle.syncNow.bind(handle);
|
|
241
|
+
unregisterContentFlush = collab.registerContentFlush?.(syncNow) ?? null;
|
|
235
242
|
}
|
|
236
|
-
|
|
243
|
+
setBinding(handle);
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
starting = false;
|
|
237
247
|
}
|
|
238
|
-
handle = resolved;
|
|
239
|
-
setBinding(handle);
|
|
240
248
|
};
|
|
241
249
|
void tryStart();
|
|
242
250
|
const unsubscribe = collab.onStatusChange(() => {
|
|
@@ -245,6 +253,8 @@ export function useCollaborativeEditor(host, config) {
|
|
|
245
253
|
return () => {
|
|
246
254
|
cancelled = true;
|
|
247
255
|
unsubscribe();
|
|
256
|
+
unregisterContentFlush?.();
|
|
257
|
+
unregisterContentFlush = null;
|
|
248
258
|
if (handle) {
|
|
249
259
|
handle.destroy();
|
|
250
260
|
handle = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditorLifecycle.d.ts","sourceRoot":"","sources":["../src/useEditorLifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAMhE,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,YAAY,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;IAEnC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;IAE5B;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;IAExC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7B;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAE/C;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,iCAAiC;IACjC,QAAQ,EAAE,CAAC,CAAC;IAEZ,qDAAqD;IACrD,QAAQ,EAAE,CAAC,CAAC;IAEZ,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IAEd,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC;;;OAGG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;IAEtB,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IAEnB,wCAAwC;IACxC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IAEd,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAE/B,iFAAiF;IACjF,gBAAgB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAE3C,+CAA+C;IAC/C,YAAY,EAAE,OAAO,CAAC;CACvB;AAMD,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,EAC3C,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,GACpC,wBAAwB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"useEditorLifecycle.d.ts","sourceRoot":"","sources":["../src/useEditorLifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAMhE,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,YAAY,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;IAEnC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;IAE5B;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;IAExC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7B;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAE/C;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,iCAAiC;IACjC,QAAQ,EAAE,CAAC,CAAC;IAEZ,qDAAqD;IACrD,QAAQ,EAAE,CAAC,CAAC;IAEZ,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IAEd,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC;;;OAGG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;IAEtB,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IAEnB,wCAAwC;IACxC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IAEd,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAE/B,iFAAiF;IACjF,gBAAgB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAE3C,+CAA+C;IAC/C,YAAY,EAAE,OAAO,CAAC;CACvB;AAMD,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,EAC3C,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,GACpC,wBAAwB,CAAC,CAAC,CAAC,CAmS7B"}
|
|
@@ -146,6 +146,7 @@ export function useEditorLifecycle(host, options) {
|
|
|
146
146
|
}
|
|
147
147
|
if (!mounted)
|
|
148
148
|
return;
|
|
149
|
+
setError(null);
|
|
149
150
|
setIsLoading(false);
|
|
150
151
|
optionsRef.current.onLoaded?.();
|
|
151
152
|
}
|
|
@@ -204,6 +205,7 @@ export function useEditorLifecycle(host, options) {
|
|
|
204
205
|
const parsed = parseContent(newRawContent);
|
|
205
206
|
lastSavedContentRef.current = newRawContent;
|
|
206
207
|
opts.applyContent(parsed);
|
|
208
|
+
setError(null);
|
|
207
209
|
clearDirty();
|
|
208
210
|
opts.onExternalChange?.(parsed);
|
|
209
211
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimbalyst/extension-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "SDK for building Nimbalyst extensions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"import": "./dist/index.js"
|
|
31
31
|
},
|
|
32
|
+
"./collab": {
|
|
33
|
+
"types": "./dist/collab/index.d.ts",
|
|
34
|
+
"import": "./dist/collab/index.js"
|
|
35
|
+
},
|
|
32
36
|
"./vite": {
|
|
33
37
|
"types": "./dist/vite.d.ts",
|
|
34
38
|
"import": "./dist/vite.js"
|
|
@@ -44,6 +48,10 @@
|
|
|
44
48
|
"./file-tree": {
|
|
45
49
|
"types": "./dist/fileDirectoryTree.d.ts",
|
|
46
50
|
"import": "./dist/fileDirectoryTree.js"
|
|
51
|
+
},
|
|
52
|
+
"./file-mask": {
|
|
53
|
+
"types": "./dist/fileMask.d.ts",
|
|
54
|
+
"import": "./dist/fileMask.js"
|
|
47
55
|
}
|
|
48
56
|
},
|
|
49
57
|
"files": [
|
|
@@ -52,10 +60,11 @@
|
|
|
52
60
|
"CHANGELOG.md"
|
|
53
61
|
],
|
|
54
62
|
"scripts": {
|
|
55
|
-
"build": "
|
|
63
|
+
"build": "tsc",
|
|
64
|
+
"build:clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('.tsbuildinfo',{force:true})\" && tsc",
|
|
56
65
|
"dev": "tsc --watch",
|
|
57
66
|
"typecheck": "tsc --noEmit",
|
|
58
|
-
"prepublishOnly": "npm run build"
|
|
67
|
+
"prepublishOnly": "npm run build:clean"
|
|
59
68
|
},
|
|
60
69
|
"dependencies": {},
|
|
61
70
|
"peerDependencies": {
|