@ai-matrx/office 0.8.13 → 0.8.14

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/docs.d.cts CHANGED
@@ -240,10 +240,19 @@ interface DocsOtPort {
240
240
  }> | null;
241
241
  }
242
242
 
243
+ declare const CARET_HEARTBEAT_MS = 2000;
244
+ declare const CARET_STALE_MS = 12000;
245
+ /** Most ranges one seat publishes (a multi-range selection is rare; the rest is dropped, never the first). */
246
+ declare const CARET_MAX_RANGES = 4;
243
247
  interface CaretRange {
244
248
  start: number;
245
249
  end: number;
246
250
  }
251
+ /** The `caret` field of a seat's awareness state. */
252
+ interface PublishedCaret {
253
+ ranges: CaretRange[];
254
+ at: number;
255
+ }
247
256
  /** One other seat's caret, ready to draw. */
248
257
  interface RemoteCaret {
249
258
  /** The seat (Yjs client id as a string): two tabs of one person are two carets. */
@@ -253,6 +262,25 @@ interface RemoteCaret {
253
262
  color: string;
254
263
  ranges: CaretRange[];
255
264
  }
265
+ type SelectionRange = {
266
+ startOffset: number;
267
+ endOffset: number;
268
+ segmentId?: string;
269
+ };
270
+ /** What a seat publishes for its selection, or null when there is nothing to show (header/footer, no selection). */
271
+ declare function publishedCaretFromSelection(sel: SelectionRange[] | null | undefined, now: number): PublishedCaret | null;
272
+ declare class RemoteCaretBook {
273
+ private readonly selfClientId;
274
+ private readonly now;
275
+ private readonly carets;
276
+ constructor(selfClientId: number, now?: () => number);
277
+ /** Take the room's awareness states: new or republished carets replace ours, seats that left are dropped. Returns whether anything changed. */
278
+ ingest(states: Map<number, unknown>): boolean;
279
+ /** Move every caret through one body edit that was just applied (`actions` of a rich-text mutation). Returns whether any moved. */
280
+ shiftThrough(actions: unknown): boolean;
281
+ /** The carets to draw now (stale ones left out). */
282
+ list(): RemoteCaret[];
283
+ }
256
284
 
257
285
  /**
258
286
  * THE DOCUMENT MODEL — the engine of one cloud document per tab.
@@ -746,4 +774,4 @@ declare function documentBodyWriteHandler(deps: {
746
774
  assertWritable: (whatItBlocks: string) => void;
747
775
  }): DocumentBodyWriteHandler;
748
776
 
749
- export { type BodyEdit, type BodyText, type CreateDocumentArgs, DOCUMENT_BODY_MAX_LENGTH, DOCUMENT_DESCRIPTION_MAX_LENGTH, DOCUMENT_NAME_MAX_LENGTH, DOCUMENT_SAVE_DELAY_MS, type DocsHostPorts, type DocsSupabase, type DocumentAwareness, type DocumentBodyPort, type DocumentBodyWriteHandler, type DocumentCollabBase, type DocumentCollabFactory, type DocumentCollabHandle, DocumentModel, type DocumentModelDeps, type DocumentOtFactory, type DocumentOtHandle, type DocumentSnapshotData, type DocumentViewPort, EMOJI_GROUPS, type Glyph, MIN_FIT_ZOOM, type ParkedDocumentEditor, SPECIAL_GROUPS, type SaveDocumentSnapshotArgs, bodyTextOf, configureDocsHost, copyDocument, createDocument, deleteDocument, discardFailedDocument, documentBodyWriteHandler, documentSourceLabel, fitZoomRatio, getDocsHost, getDocument, getLatestDocumentSnapshot, listAccessibleDocuments, listDocumentSnapshots, listTrashedDocuments, moveDocument, moveRangesThroughBodyEdit, planBodyEdit, renameDocument, restoreDocument, sanitizeUniverDocSnapshot, saveDocumentSnapshot, searchSymbols, updateDocumentDescription, validateDocumentBody, validateDocumentDescription, validateDocumentName, writeBodyText };
777
+ export { type BodyEdit, type BodyText, CARET_HEARTBEAT_MS, CARET_MAX_RANGES, CARET_STALE_MS, type CaretRange, type CreateDocumentArgs, DOCUMENT_BODY_MAX_LENGTH, DOCUMENT_DESCRIPTION_MAX_LENGTH, DOCUMENT_NAME_MAX_LENGTH, DOCUMENT_SAVE_DELAY_MS, type DocsHostPorts, type DocsSupabase, type DocumentAwareness, type DocumentBodyPort, type DocumentBodyWriteHandler, type DocumentCollabBase, type DocumentCollabFactory, type DocumentCollabHandle, DocumentModel, type DocumentModelDeps, type DocumentOtFactory, type DocumentOtHandle, type DocumentSnapshotData, type DocumentViewPort, EMOJI_GROUPS, type Glyph, MIN_FIT_ZOOM, type ParkedDocumentEditor, type PublishedCaret, type RemoteCaret, RemoteCaretBook, SPECIAL_GROUPS, type SaveDocumentSnapshotArgs, bodyTextOf, configureDocsHost, copyDocument, createDocument, deleteDocument, discardFailedDocument, documentBodyWriteHandler, documentSourceLabel, fitZoomRatio, getDocsHost, getDocument, getLatestDocumentSnapshot, listAccessibleDocuments, listDocumentSnapshots, listTrashedDocuments, moveDocument, moveRangesThroughBodyEdit, planBodyEdit, publishedCaretFromSelection, renameDocument, restoreDocument, sanitizeUniverDocSnapshot, saveDocumentSnapshot, searchSymbols, updateDocumentDescription, validateDocumentBody, validateDocumentDescription, validateDocumentName, writeBodyText };
package/dist/docs.d.ts CHANGED
@@ -240,10 +240,19 @@ interface DocsOtPort {
240
240
  }> | null;
241
241
  }
242
242
 
243
+ declare const CARET_HEARTBEAT_MS = 2000;
244
+ declare const CARET_STALE_MS = 12000;
245
+ /** Most ranges one seat publishes (a multi-range selection is rare; the rest is dropped, never the first). */
246
+ declare const CARET_MAX_RANGES = 4;
243
247
  interface CaretRange {
244
248
  start: number;
245
249
  end: number;
246
250
  }
251
+ /** The `caret` field of a seat's awareness state. */
252
+ interface PublishedCaret {
253
+ ranges: CaretRange[];
254
+ at: number;
255
+ }
247
256
  /** One other seat's caret, ready to draw. */
248
257
  interface RemoteCaret {
249
258
  /** The seat (Yjs client id as a string): two tabs of one person are two carets. */
@@ -253,6 +262,25 @@ interface RemoteCaret {
253
262
  color: string;
254
263
  ranges: CaretRange[];
255
264
  }
265
+ type SelectionRange = {
266
+ startOffset: number;
267
+ endOffset: number;
268
+ segmentId?: string;
269
+ };
270
+ /** What a seat publishes for its selection, or null when there is nothing to show (header/footer, no selection). */
271
+ declare function publishedCaretFromSelection(sel: SelectionRange[] | null | undefined, now: number): PublishedCaret | null;
272
+ declare class RemoteCaretBook {
273
+ private readonly selfClientId;
274
+ private readonly now;
275
+ private readonly carets;
276
+ constructor(selfClientId: number, now?: () => number);
277
+ /** Take the room's awareness states: new or republished carets replace ours, seats that left are dropped. Returns whether anything changed. */
278
+ ingest(states: Map<number, unknown>): boolean;
279
+ /** Move every caret through one body edit that was just applied (`actions` of a rich-text mutation). Returns whether any moved. */
280
+ shiftThrough(actions: unknown): boolean;
281
+ /** The carets to draw now (stale ones left out). */
282
+ list(): RemoteCaret[];
283
+ }
256
284
 
257
285
  /**
258
286
  * THE DOCUMENT MODEL — the engine of one cloud document per tab.
@@ -746,4 +774,4 @@ declare function documentBodyWriteHandler(deps: {
746
774
  assertWritable: (whatItBlocks: string) => void;
747
775
  }): DocumentBodyWriteHandler;
748
776
 
749
- export { type BodyEdit, type BodyText, type CreateDocumentArgs, DOCUMENT_BODY_MAX_LENGTH, DOCUMENT_DESCRIPTION_MAX_LENGTH, DOCUMENT_NAME_MAX_LENGTH, DOCUMENT_SAVE_DELAY_MS, type DocsHostPorts, type DocsSupabase, type DocumentAwareness, type DocumentBodyPort, type DocumentBodyWriteHandler, type DocumentCollabBase, type DocumentCollabFactory, type DocumentCollabHandle, DocumentModel, type DocumentModelDeps, type DocumentOtFactory, type DocumentOtHandle, type DocumentSnapshotData, type DocumentViewPort, EMOJI_GROUPS, type Glyph, MIN_FIT_ZOOM, type ParkedDocumentEditor, SPECIAL_GROUPS, type SaveDocumentSnapshotArgs, bodyTextOf, configureDocsHost, copyDocument, createDocument, deleteDocument, discardFailedDocument, documentBodyWriteHandler, documentSourceLabel, fitZoomRatio, getDocsHost, getDocument, getLatestDocumentSnapshot, listAccessibleDocuments, listDocumentSnapshots, listTrashedDocuments, moveDocument, moveRangesThroughBodyEdit, planBodyEdit, renameDocument, restoreDocument, sanitizeUniverDocSnapshot, saveDocumentSnapshot, searchSymbols, updateDocumentDescription, validateDocumentBody, validateDocumentDescription, validateDocumentName, writeBodyText };
777
+ export { type BodyEdit, type BodyText, CARET_HEARTBEAT_MS, CARET_MAX_RANGES, CARET_STALE_MS, type CaretRange, type CreateDocumentArgs, DOCUMENT_BODY_MAX_LENGTH, DOCUMENT_DESCRIPTION_MAX_LENGTH, DOCUMENT_NAME_MAX_LENGTH, DOCUMENT_SAVE_DELAY_MS, type DocsHostPorts, type DocsSupabase, type DocumentAwareness, type DocumentBodyPort, type DocumentBodyWriteHandler, type DocumentCollabBase, type DocumentCollabFactory, type DocumentCollabHandle, DocumentModel, type DocumentModelDeps, type DocumentOtFactory, type DocumentOtHandle, type DocumentSnapshotData, type DocumentViewPort, EMOJI_GROUPS, type Glyph, MIN_FIT_ZOOM, type ParkedDocumentEditor, type PublishedCaret, type RemoteCaret, RemoteCaretBook, SPECIAL_GROUPS, type SaveDocumentSnapshotArgs, bodyTextOf, configureDocsHost, copyDocument, createDocument, deleteDocument, discardFailedDocument, documentBodyWriteHandler, documentSourceLabel, fitZoomRatio, getDocsHost, getDocument, getLatestDocumentSnapshot, listAccessibleDocuments, listDocumentSnapshots, listTrashedDocuments, moveDocument, moveRangesThroughBodyEdit, planBodyEdit, publishedCaretFromSelection, renameDocument, restoreDocument, sanitizeUniverDocSnapshot, saveDocumentSnapshot, searchSymbols, updateDocumentDescription, validateDocumentBody, validateDocumentDescription, validateDocumentName, writeBodyText };
package/dist/docs.js CHANGED
@@ -863,7 +863,94 @@ function documentBodyWriteHandler(deps) {
863
863
  }
864
864
  };
865
865
  }
866
+
867
+ // src/docs/remote-carets.ts
868
+ var CARET_HEARTBEAT_MS = 2e3;
869
+ var CARET_STALE_MS = 12e3;
870
+ var CARET_MAX_RANGES = 4;
871
+ function publishedCaretFromSelection(sel, now) {
872
+ if (!sel || sel.length === 0) return null;
873
+ if (sel.some((r) => r.segmentId)) return null;
874
+ const ranges = sel.slice(0, CARET_MAX_RANGES).map((r) => ({ start: Math.min(r.startOffset, r.endOffset), end: Math.max(r.startOffset, r.endOffset) }));
875
+ return { ranges, at: now };
876
+ }
877
+ var sameRanges = (a, b) => a.length === b.length && a.every((r, i) => r.start === b[i].start && r.end === b[i].end);
878
+ function readCaret(raw) {
879
+ const c = raw?.caret;
880
+ if (!c || !Array.isArray(c.ranges) || typeof c.at !== "number") return null;
881
+ const ranges = [];
882
+ for (const r of c.ranges.slice(0, CARET_MAX_RANGES)) {
883
+ const s2 = r?.start;
884
+ const e2 = r?.end;
885
+ if (typeof s2 !== "number" || typeof e2 !== "number" || !Number.isFinite(s2) || !Number.isFinite(e2) || s2 < 0 || e2 < 0) return null;
886
+ ranges.push({ start: s2, end: e2 });
887
+ }
888
+ return ranges.length ? { ranges, at: c.at } : null;
889
+ }
890
+ var RemoteCaretBook = class {
891
+ constructor(selfClientId, now = Date.now) {
892
+ this.selfClientId = selfClientId;
893
+ this.now = now;
894
+ }
895
+ selfClientId;
896
+ now;
897
+ carets = /* @__PURE__ */ new Map();
898
+ /** Take the room's awareness states: new or republished carets replace ours, seats that left are dropped. Returns whether anything changed. */
899
+ ingest(states) {
900
+ let changed = false;
901
+ const seen = /* @__PURE__ */ new Set();
902
+ states.forEach((raw, id) => {
903
+ if (id === this.selfClientId) return;
904
+ const s2 = raw;
905
+ const caret = readCaret(raw);
906
+ if (!s2 || typeof s2.uid !== "string" || !caret) return;
907
+ const key = String(id);
908
+ seen.add(key);
909
+ const prev = this.carets.get(key);
910
+ const name = typeof s2.name === "string" && s2.name ? s2.name : "Someone";
911
+ const color = typeof s2.color === "string" && s2.color ? s2.color : "#3b82f6";
912
+ if (prev && prev.at === caret.at && prev.name === name && prev.color === color) return;
913
+ this.carets.set(key, { key, uid: s2.uid, name, color, ranges: caret.ranges, at: caret.at, seenAt: this.now() });
914
+ changed = true;
915
+ });
916
+ for (const key of [...this.carets.keys()]) {
917
+ if (!seen.has(key)) {
918
+ this.carets.delete(key);
919
+ changed = true;
920
+ }
921
+ }
922
+ return changed;
923
+ }
924
+ /** Move every caret through one body edit that was just applied (`actions` of a rich-text mutation). Returns whether any moved. */
925
+ shiftThrough(actions) {
926
+ let moved = false;
927
+ for (const c of this.carets.values()) {
928
+ const next = moveRangesThroughBodyEdit(c.ranges.map((r) => ({ startOffset: r.start, endOffset: r.end })), actions);
929
+ if (!next) return false;
930
+ const ranges = next.map((r) => ({ start: r.startOffset, end: r.endOffset }));
931
+ if (!sameRanges(ranges, c.ranges)) {
932
+ c.ranges = ranges;
933
+ moved = true;
934
+ }
935
+ }
936
+ return moved;
937
+ }
938
+ /** The carets to draw now (stale ones left out). */
939
+ list() {
940
+ const t = this.now();
941
+ const out = [];
942
+ for (const c of this.carets.values()) {
943
+ if (t - c.seenAt > CARET_STALE_MS) continue;
944
+ out.push({ key: c.key, uid: c.uid, name: c.name, color: c.color, ranges: c.ranges.map((r) => ({ ...r })) });
945
+ }
946
+ out.sort((a, b) => a.key.localeCompare(b.key));
947
+ return out;
948
+ }
949
+ };
866
950
  export {
951
+ CARET_HEARTBEAT_MS,
952
+ CARET_MAX_RANGES,
953
+ CARET_STALE_MS,
867
954
  DOCUMENT_BODY_MAX_LENGTH,
868
955
  DOCUMENT_DESCRIPTION_MAX_LENGTH,
869
956
  DOCUMENT_NAME_MAX_LENGTH,
@@ -871,6 +958,7 @@ export {
871
958
  DocumentModel,
872
959
  EMOJI_GROUPS,
873
960
  MIN_FIT_ZOOM,
961
+ RemoteCaretBook,
874
962
  SPECIAL_GROUPS,
875
963
  bodyTextOf,
876
964
  configureDocsHost,
@@ -890,6 +978,7 @@ export {
890
978
  moveDocument,
891
979
  moveRangesThroughBodyEdit,
892
980
  planBodyEdit,
981
+ publishedCaretFromSelection,
893
982
  renameDocument,
894
983
  restoreDocument,
895
984
  sanitizeUniverDocSnapshot,