mindcache 3.3.1 → 3.4.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/dist/index.mjs CHANGED
@@ -183,6 +183,7 @@ var init_CloudAdapter = __esm({
183
183
  if (!config.baseUrl) {
184
184
  throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
185
185
  }
186
+ this.setupNetworkDetection();
186
187
  }
187
188
  ws = null;
188
189
  mindcache = null;
@@ -190,8 +191,52 @@ var init_CloudAdapter = __esm({
190
191
  reconnectAttempts = 0;
191
192
  reconnectTimeout = null;
192
193
  _state = "disconnected";
194
+ _isOnline = true;
195
+ // Browser network status
193
196
  listeners = {};
194
197
  token = null;
198
+ handleOnline = null;
199
+ handleOffline = null;
200
+ /** Browser network status - instantly updated via navigator.onLine */
201
+ get isOnline() {
202
+ return this._isOnline;
203
+ }
204
+ setupNetworkDetection() {
205
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
206
+ return;
207
+ }
208
+ this._isOnline = navigator.onLine;
209
+ this.handleOnline = () => {
210
+ console.log("\u2601\uFE0F CloudAdapter: Network is back online");
211
+ this._isOnline = true;
212
+ this.emit("network_online");
213
+ if (this._state === "disconnected" || this._state === "error") {
214
+ this.connect();
215
+ }
216
+ };
217
+ this.handleOffline = () => {
218
+ console.log("\u2601\uFE0F CloudAdapter: Network went offline");
219
+ this._isOnline = false;
220
+ this.emit("network_offline");
221
+ if (this._state === "connected" || this._state === "connecting") {
222
+ this._state = "disconnected";
223
+ this.emit("disconnected");
224
+ }
225
+ };
226
+ window.addEventListener("online", this.handleOnline);
227
+ window.addEventListener("offline", this.handleOffline);
228
+ }
229
+ cleanupNetworkDetection() {
230
+ if (typeof window === "undefined") {
231
+ return;
232
+ }
233
+ if (this.handleOnline) {
234
+ window.removeEventListener("online", this.handleOnline);
235
+ }
236
+ if (this.handleOffline) {
237
+ window.removeEventListener("offline", this.handleOffline);
238
+ }
239
+ }
195
240
  setToken(token) {
196
241
  this.token = token;
197
242
  }
@@ -285,6 +330,7 @@ var init_CloudAdapter = __esm({
285
330
  this.ws.close();
286
331
  this.ws = null;
287
332
  }
333
+ this.cleanupNetworkDetection();
288
334
  this._state = "disconnected";
289
335
  this.emit("disconnected");
290
336
  }
@@ -401,7 +447,7 @@ var MindCache = class {
401
447
  listeners = {};
402
448
  globalListeners = [];
403
449
  // Metadata
404
- version = "3.3.1";
450
+ version = "3.3.2";
405
451
  // Internal flag to prevent sync loops when receiving remote updates
406
452
  // (Less critical with Yjs but kept for API compat)
407
453
  _isRemoteUpdate = false;
@@ -474,14 +520,30 @@ var MindCache = class {
474
520
  break;
475
521
  }
476
522
  }
523
+ } else {
524
+ let current = event.target;
525
+ while (current && current.parent) {
526
+ if (current.parent.parent === this.rootMap) {
527
+ for (const [key, val] of this.rootMap) {
528
+ if (val === current.parent) {
529
+ keysAffected.add(key);
530
+ break;
531
+ }
532
+ }
533
+ break;
534
+ }
535
+ current = current.parent;
536
+ }
477
537
  }
478
538
  });
479
539
  keysAffected.forEach((key) => {
480
540
  const entryMap = this.rootMap.get(key);
481
541
  if (entryMap) {
482
542
  const value = entryMap.get("value");
543
+ const attrs = entryMap.get("attributes");
544
+ const resolvedValue = attrs?.type === "document" && value instanceof Y.Text ? value.toString() : value;
483
545
  if (this.listeners[key]) {
484
- this.listeners[key].forEach((l) => l(value));
546
+ this.listeners[key].forEach((l) => l(resolvedValue));
485
547
  }
486
548
  } else {
487
549
  if (this.listeners[key]) {
@@ -611,12 +673,19 @@ var MindCache = class {
611
673
  if (event.target === this.rootMap) {
612
674
  const mapEvent = event;
613
675
  mapEvent.keysChanged.forEach((key) => keysAffected.add(key));
614
- } else if (event.target.parent === this.rootMap) {
615
- for (const [key, val] of this.rootMap) {
616
- if (val === event.target) {
617
- keysAffected.add(key);
676
+ } else {
677
+ let current = event.target;
678
+ while (current && current.parent) {
679
+ if (current.parent === this.rootMap) {
680
+ for (const [key, val] of this.rootMap) {
681
+ if (val === current) {
682
+ keysAffected.add(key);
683
+ break;
684
+ }
685
+ }
618
686
  break;
619
687
  }
688
+ current = current.parent;
620
689
  }
621
690
  }
622
691
  });
@@ -756,6 +825,19 @@ var MindCache = class {
756
825
  get isCloud() {
757
826
  return this._cloudConfig !== null;
758
827
  }
828
+ /**
829
+ * Browser network status. Returns true if online or in local-only mode.
830
+ * In cloud mode, this updates instantly when network status changes.
831
+ */
832
+ get isOnline() {
833
+ if (!this._cloudAdapter) {
834
+ if (typeof navigator !== "undefined") {
835
+ return navigator.onLine;
836
+ }
837
+ return true;
838
+ }
839
+ return this._cloudAdapter.isOnline;
840
+ }
759
841
  async waitForSync() {
760
842
  if (this._isLoaded) {
761
843
  return;
@@ -1000,11 +1082,15 @@ var MindCache = class {
1000
1082
  const existingAttrs = existingEntry.get("attributes");
1001
1083
  if (existingAttrs?.type === "document") {
1002
1084
  if (typeof value === "string") {
1003
- this.replace_document_text(key, value);
1085
+ this._replaceDocumentText(key, value);
1004
1086
  }
1005
1087
  return;
1006
1088
  }
1007
1089
  }
1090
+ if (!existingEntry && attributes?.type === "document") {
1091
+ this.set_document(key, typeof value === "string" ? value : "", attributes);
1092
+ return;
1093
+ }
1008
1094
  let entryMap = this.rootMap.get(key);
1009
1095
  const isNewEntry = !entryMap;
1010
1096
  if (isNewEntry) {
@@ -1745,14 +1831,6 @@ var MindCache = class {
1745
1831
  }
1746
1832
  return void 0;
1747
1833
  }
1748
- /**
1749
- * Get plain text content of a document key.
1750
- * For collaborative editing, use get_document() and bind to an editor.
1751
- */
1752
- get_document_text(key) {
1753
- const yText = this.get_document(key);
1754
- return yText?.toString();
1755
- }
1756
1834
  /**
1757
1835
  * Insert text at a position in a document key.
1758
1836
  */
@@ -1772,15 +1850,11 @@ var MindCache = class {
1772
1850
  }
1773
1851
  }
1774
1852
  /**
1775
- * Replace all text in a document key.
1853
+ * Replace all text in a document key (private - use set_value for public API).
1776
1854
  * Uses diff-based updates when changes are < diffThreshold (default 80%).
1777
1855
  * This preserves concurrent edits and provides better undo granularity.
1778
- *
1779
- * @param key - The document key
1780
- * @param newText - The new text content
1781
- * @param diffThreshold - Percentage (0-1) of change above which full replace is used (default: 0.8)
1782
1856
  */
1783
- replace_document_text(key, newText, diffThreshold = 0.8) {
1857
+ _replaceDocumentText(key, newText, diffThreshold = 0.8) {
1784
1858
  const yText = this.get_document(key);
1785
1859
  if (!yText) {
1786
1860
  return;
@@ -1886,7 +1960,7 @@ var MindCache = class {
1886
1960
  },
1887
1961
  execute: async ({ value }) => {
1888
1962
  if (isDocument) {
1889
- this.replace_document_text(key, value);
1963
+ this._replaceDocumentText(key, value);
1890
1964
  } else {
1891
1965
  this.set_value(key, value);
1892
1966
  }
@@ -2042,7 +2116,7 @@ var MindCache = class {
2042
2116
  switch (action) {
2043
2117
  case "write":
2044
2118
  if (isDocument) {
2045
- this.replace_document_text(key, value);
2119
+ this._replaceDocumentText(key, value);
2046
2120
  } else {
2047
2121
  this.set_value(key, value);
2048
2122
  }