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