@may-db/core 0.1.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.
Files changed (69) hide show
  1. package/dist/Client.d.ts +20 -0
  2. package/dist/Client.d.ts.map +1 -0
  3. package/dist/Client.js +163 -0
  4. package/dist/Client.js.map +1 -0
  5. package/dist/MayDb.d.ts +20 -0
  6. package/dist/MayDb.d.ts.map +1 -0
  7. package/dist/MayDb.js +162 -0
  8. package/dist/MayDb.js.map +1 -0
  9. package/dist/Room.d.ts +81 -0
  10. package/dist/Room.d.ts.map +1 -0
  11. package/dist/Room.js +382 -0
  12. package/dist/Room.js.map +1 -0
  13. package/dist/authStorage.d.ts +8 -0
  14. package/dist/authStorage.d.ts.map +1 -0
  15. package/dist/authStorage.js +29 -0
  16. package/dist/authStorage.js.map +1 -0
  17. package/dist/hooks/useAllowedRooms.d.ts +12 -0
  18. package/dist/hooks/useAllowedRooms.d.ts.map +1 -0
  19. package/dist/hooks/useAllowedRooms.js +29 -0
  20. package/dist/hooks/useAllowedRooms.js.map +1 -0
  21. package/dist/hooks/useAsync.d.ts +8 -0
  22. package/dist/hooks/useAsync.d.ts.map +1 -0
  23. package/dist/hooks/useAsync.js +29 -0
  24. package/dist/hooks/useAsync.js.map +1 -0
  25. package/dist/hooks/useAuth.d.ts +11 -0
  26. package/dist/hooks/useAuth.d.ts.map +1 -0
  27. package/dist/hooks/useAuth.js +45 -0
  28. package/dist/hooks/useAuth.js.map +1 -0
  29. package/dist/hooks/usePresence.d.ts +30 -0
  30. package/dist/hooks/usePresence.d.ts.map +1 -0
  31. package/dist/hooks/usePresence.js +95 -0
  32. package/dist/hooks/usePresence.js.map +1 -0
  33. package/dist/hooks/useRoom.d.ts +11 -0
  34. package/dist/hooks/useRoom.d.ts.map +1 -0
  35. package/dist/hooks/useRoom.js +37 -0
  36. package/dist/hooks/useRoom.js.map +1 -0
  37. package/dist/hooks/useRoomMembers.d.ts +12 -0
  38. package/dist/hooks/useRoomMembers.d.ts.map +1 -0
  39. package/dist/hooks/useRoomMembers.js +37 -0
  40. package/dist/hooks/useRoomMembers.js.map +1 -0
  41. package/dist/index.d.ts +10 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +10 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/loginFlow.d.ts +4 -0
  46. package/dist/loginFlow.d.ts.map +1 -0
  47. package/dist/loginFlow.js +48 -0
  48. package/dist/loginFlow.js.map +1 -0
  49. package/dist/matrixId.d.ts +9 -0
  50. package/dist/matrixId.d.ts.map +1 -0
  51. package/dist/matrixId.js +14 -0
  52. package/dist/matrixId.js.map +1 -0
  53. package/dist/presence.d.ts +36 -0
  54. package/dist/presence.d.ts.map +1 -0
  55. package/dist/presence.js +63 -0
  56. package/dist/presence.js.map +1 -0
  57. package/dist/singletonRoom.d.ts +3 -0
  58. package/dist/singletonRoom.d.ts.map +1 -0
  59. package/dist/singletonRoom.js +39 -0
  60. package/dist/singletonRoom.js.map +1 -0
  61. package/dist/types.d.ts +38 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/dist/types.js +2 -0
  64. package/dist/types.js.map +1 -0
  65. package/dist/utils/presenceColors.d.ts +3 -0
  66. package/dist/utils/presenceColors.d.ts.map +1 -0
  67. package/dist/utils/presenceColors.js +21 -0
  68. package/dist/utils/presenceColors.js.map +1 -0
  69. package/package.json +33 -0
@@ -0,0 +1,95 @@
1
+ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
+ import { getUserColor } from '../utils/presenceColors';
3
+ const PRESENCE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
4
+ const SYNC_POLL_INTERVAL_MS = 2500; // Poll every 2.5 seconds
5
+ export const PRESENCE_EVENT = "com.matrixsheets.presence";
6
+ /**
7
+ * Hook for managing presence via Matrix to-device messages.
8
+ * Broadcasts local focus changes to room members and receives their presence updates.
9
+ */
10
+ export function usePresence({ room }) {
11
+ const roomId = room.roomId;
12
+ const localUserId = room.userId;
13
+ const localUserName = room.userDisplayName || localUserId;
14
+ const localColor = useMemo(() => getUserColor(localUserId), [localUserId]);
15
+ const [remotePresences, setRemotePresences] = useState([]);
16
+ // Track sync token for incremental sync
17
+ const syncTokenRef = useRef(null);
18
+ // Track current local focus to avoid duplicate sends
19
+ const localFocusRef = useRef(null);
20
+ const sendPresence = useCallback(async (focus) => {
21
+ const presence = {
22
+ roomId,
23
+ focus,
24
+ timestamp: Date.now(),
25
+ user: {
26
+ id: localUserId,
27
+ name: localUserName,
28
+ color: localColor,
29
+ },
30
+ };
31
+ try {
32
+ await room.sendToDevice(PRESENCE_EVENT, presence);
33
+ }
34
+ catch (err) {
35
+ console.error('Failed to send presence:', err);
36
+ }
37
+ }, [room, roomId, localUserId, localUserName, localColor]);
38
+ const setLocalFocus = useCallback((focus) => {
39
+ if (localFocusRef.current === focus)
40
+ return;
41
+ localFocusRef.current = focus;
42
+ sendPresence(focus);
43
+ }, [sendPresence]);
44
+ useEffect(() => {
45
+ let stopped = false;
46
+ const poll = async () => {
47
+ try {
48
+ const { events, nextBatch } = await room.pollToDeviceEvents(syncTokenRef.current);
49
+ syncTokenRef.current = nextBatch;
50
+ const now = Date.now();
51
+ for (const event of events) {
52
+ if (event.type !== PRESENCE_EVENT)
53
+ continue;
54
+ const content = event.content;
55
+ if (content.roomId !== roomId)
56
+ continue;
57
+ if (content.user.id === localUserId)
58
+ continue;
59
+ if (now - content.timestamp >= PRESENCE_TIMEOUT_MS)
60
+ continue;
61
+ const userId = content.user.id;
62
+ if (content.focus === null) {
63
+ setRemotePresences(prev => prev.filter(p => p.userId !== userId));
64
+ }
65
+ else {
66
+ setRemotePresences(prev => [
67
+ ...prev.filter(p => p.userId !== userId),
68
+ { userId, name: content.user.name, color: content.user.color, focus: content.focus },
69
+ ]);
70
+ }
71
+ }
72
+ }
73
+ catch (err) {
74
+ console.error('Presence poll error:', err);
75
+ }
76
+ };
77
+ poll();
78
+ const interval = setInterval(() => {
79
+ if (!stopped)
80
+ poll();
81
+ }, SYNC_POLL_INTERVAL_MS);
82
+ return () => {
83
+ stopped = true;
84
+ clearInterval(interval);
85
+ };
86
+ }, [room, roomId, localUserId]);
87
+ // Send null presence when unmounting (leaving room)
88
+ useEffect(() => {
89
+ return () => {
90
+ sendPresence(null);
91
+ };
92
+ }, [sendPresence]);
93
+ return { remotePresences, setLocalFocus };
94
+ }
95
+ //# sourceMappingURL=usePresence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePresence.js","sourceRoot":"","sources":["../../src/hooks/usePresence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Room } from "../Room";
2
+ import type { RoomStatus, RoomMetadata } from "../types";
3
+ export declare function useRoom(room: Room): {
4
+ room: Room;
5
+ status: RoomStatus;
6
+ isConnected: boolean;
7
+ canWrite: boolean;
8
+ metadata: RoomMetadata | null;
9
+ error: Error | null;
10
+ };
11
+ //# sourceMappingURL=useRoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRoom.d.ts","sourceRoot":"","sources":["../../src/hooks/useRoom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI;;;;;;;EAqCjC"}
@@ -0,0 +1,37 @@
1
+ import { useState, useEffect } from "react";
2
+ export function useRoom(room) {
3
+ const [status, setStatus] = useState(room.status);
4
+ const [canWrite, setCanWrite] = useState(room.canWrite);
5
+ const [metadata, setMetadata] = useState(room.metadata);
6
+ const [error, setError] = useState(null);
7
+ useEffect(() => {
8
+ const unsubscribe = room.subscribe({
9
+ onStatusChange: setStatus,
10
+ onCanWriteChange: setCanWrite,
11
+ onMetadataChange: setMetadata,
12
+ });
13
+ return unsubscribe;
14
+ }, [room]);
15
+ // Auto-connect effect (no cleanup - rooms are cached and stay connected)
16
+ useEffect(() => {
17
+ if (room.isConnected)
18
+ return;
19
+ (async () => {
20
+ try {
21
+ await room.connect();
22
+ }
23
+ catch (err) {
24
+ setError(err instanceof Error ? err : new Error("Failed to connect"));
25
+ }
26
+ })();
27
+ }, [room]);
28
+ return {
29
+ room,
30
+ status,
31
+ isConnected: status === "connected",
32
+ canWrite,
33
+ metadata,
34
+ error,
35
+ };
36
+ }
37
+ //# sourceMappingURL=useRoom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRoom.js","sourceRoot":"","sources":["../../src/hooks/useRoom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI5C,MAAM,UAAU,OAAO,CAAC,IAAU;IAChC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC;YACjC,cAAc,EAAE,SAAS;YACzB,gBAAgB,EAAE,WAAW;YAC7B,gBAAgB,EAAE,WAAW;SAC9B,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,yEAAyE;IACzE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAE7B,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;YACxE,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO;QACL,IAAI;QACJ,MAAM;QACN,WAAW,EAAE,MAAM,KAAK,WAAW;QACnC,QAAQ;QACR,QAAQ;QACR,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { Room } from "../Room";
2
+ import type { RoomMember, AccessLevel } from "../types";
3
+ export declare function useRoomMembers(room: Room): {
4
+ members: RoomMember[];
5
+ loading: boolean;
6
+ error: Error | null;
7
+ refresh: () => Promise<void>;
8
+ invite: (userId: string, access: AccessLevel) => Promise<void>;
9
+ setAccess: (userId: string, access: AccessLevel) => Promise<void>;
10
+ kick: (userId: string) => Promise<void>;
11
+ };
12
+ //# sourceMappingURL=useRoomMembers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRoomMembers.d.ts","sourceRoot":"","sources":["../../src/hooks/useRoomMembers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAExD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI;;;;;qBA0BO,MAAM,UAAU,WAAW;wBAQ3B,MAAM,UAAU,WAAW;mBAQ3B,MAAM;EAQxB"}
@@ -0,0 +1,37 @@
1
+ import { useState, useEffect, useCallback } from "react";
2
+ export function useRoomMembers(room) {
3
+ const [members, setMembers] = useState([]);
4
+ const [loading, setLoading] = useState(true);
5
+ const [error, setError] = useState(null);
6
+ const refresh = useCallback(async () => {
7
+ setLoading(true);
8
+ setError(null);
9
+ try {
10
+ const memberList = await room.getMembers();
11
+ setMembers(memberList);
12
+ }
13
+ catch (err) {
14
+ setError(err instanceof Error ? err : new Error("Failed to load members"));
15
+ }
16
+ finally {
17
+ setLoading(false);
18
+ }
19
+ }, [room]);
20
+ useEffect(() => {
21
+ refresh();
22
+ }, [refresh]);
23
+ const invite = useCallback(async (userId, access) => {
24
+ await room.invite(userId, access);
25
+ await refresh();
26
+ }, [room, refresh]);
27
+ const setAccess = useCallback(async (userId, access) => {
28
+ await room.setAccess(userId, access);
29
+ await refresh();
30
+ }, [room, refresh]);
31
+ const kick = useCallback(async (userId) => {
32
+ await room.kick(userId);
33
+ await refresh();
34
+ }, [room, refresh]);
35
+ return { members, loading, error, refresh, invite, setAccess, kick };
36
+ }
37
+ //# sourceMappingURL=useRoomMembers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRoomMembers.js","sourceRoot":"","sources":["../../src/hooks/useRoomMembers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAIzD,MAAM,UAAU,cAAc,CAC5B,IAAU;IAEV,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAe,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3C,UAAU,CAAC,UAAU,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CACN,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CACjE,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,MAAM,GAAG,WAAW,CACxB,KAAK,EAAE,MAAc,EAAE,MAAmB,EAAE,EAAE;QAC5C,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,EACD,CAAC,IAAI,EAAE,OAAO,CAAC,CAChB,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,CAC3B,KAAK,EAAE,MAAc,EAAE,MAAmB,EAAE,EAAE;QAC5C,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,EACD,CAAC,IAAI,EAAE,OAAO,CAAC,CAChB,CAAC;IAEF,MAAM,IAAI,GAAG,WAAW,CACtB,KAAK,EAAE,MAAc,EAAE,EAAE;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,EACD,CAAC,IAAI,EAAE,OAAO,CAAC,CAChB,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { MayDb } from "./MayDb";
2
+ export { Room } from "./Room";
3
+ export { startLogin, completeLogin } from "./loginFlow";
4
+ export { getAuth, saveAuth, removeAuth, getHomeserver, saveHomeserver, removeHomeserver, } from "./authStorage";
5
+ export { getOrCreateSingletonRoomId } from "./singletonRoom";
6
+ export { isValidMatrixId, extractLocalpart } from "./matrixId";
7
+ export { PRESENCE_EVENT, PRESENCE_TIMEOUT_MS, applyPresenceUpdates, filterRelevantPresenceEvents, isPresenceContent, type PresenceContent, type PresenceUpdate, type RemotePresence, type ToDeviceEvent, } from "./presence";
8
+ export { getUserColor, PRESENCE_COLORS } from "./utils/presenceColors";
9
+ export * from "./types";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,OAAO,EACP,QAAQ,EACR,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { MayDb } from "./MayDb";
2
+ export { Room } from "./Room";
3
+ export { startLogin, completeLogin } from "./loginFlow";
4
+ export { getAuth, saveAuth, removeAuth, getHomeserver, saveHomeserver, removeHomeserver, } from "./authStorage";
5
+ export { getOrCreateSingletonRoomId } from "./singletonRoom";
6
+ export { isValidMatrixId, extractLocalpart } from "./matrixId";
7
+ export { PRESENCE_EVENT, PRESENCE_TIMEOUT_MS, applyPresenceUpdates, filterRelevantPresenceEvents, isPresenceContent, } from "./presence";
8
+ export { getUserColor, PRESENCE_COLORS } from "./utils/presenceColors";
9
+ export * from "./types";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,OAAO,EACP,QAAQ,EACR,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,GAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,cAAc,SAAS,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AuthData } from "./types";
2
+ export declare function startLogin(): void;
3
+ export declare function completeLogin(): Promise<AuthData | null>;
4
+ //# sourceMappingURL=loginFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loginFlow.d.ts","sourceRoot":"","sources":["../src/loginFlow.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQxC,wBAAgB,UAAU,IAAI,IAAI,CASjC;AAgCD,wBAAsB,aAAa,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoB9D"}
@@ -0,0 +1,48 @@
1
+ import { saveAuth, saveHomeserver, getHomeserver, removeHomeserver, } from "./authStorage";
2
+ export function startLogin() {
3
+ // For now, just login with matrix.org all the time. Later, findhomeserver.com will make this good.
4
+ const homeserver = "https://matrix.org";
5
+ saveHomeserver(homeserver);
6
+ const redirectUrl = encodeURIComponent(window.location.origin + window.location.pathname);
7
+ window.location.href = `${homeserver}/_matrix/client/v3/login/sso/redirect?redirectUrl=${redirectUrl}`;
8
+ }
9
+ async function exchangeLoginToken(homeserver, loginToken) {
10
+ const response = await fetch(`${homeserver}/_matrix/client/v3/login`, {
11
+ method: "POST",
12
+ headers: {
13
+ "Content-Type": "application/json",
14
+ },
15
+ body: JSON.stringify({
16
+ type: "m.login.token",
17
+ token: loginToken,
18
+ }),
19
+ });
20
+ if (!response.ok) {
21
+ const error = await response.json();
22
+ throw new Error(error.error || "Login failed");
23
+ }
24
+ const data = await response.json();
25
+ return {
26
+ homeserver,
27
+ accessToken: data.access_token,
28
+ userId: data.user_id,
29
+ deviceId: data.device_id,
30
+ };
31
+ }
32
+ export async function completeLogin() {
33
+ const params = new URLSearchParams(window.location.search);
34
+ const loginToken = params.get("loginToken");
35
+ if (!loginToken) {
36
+ return null;
37
+ }
38
+ window.history.replaceState({}, document.title, window.location.pathname);
39
+ const homeserver = getHomeserver();
40
+ if (!homeserver) {
41
+ throw new Error("No homeserver stored for SSO callback");
42
+ }
43
+ const auth = await exchangeLoginToken(homeserver, loginToken);
44
+ saveAuth(auth);
45
+ removeHomeserver();
46
+ return auth;
47
+ }
48
+ //# sourceMappingURL=loginFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loginFlow.js","sourceRoot":"","sources":["../src/loginFlow.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,eAAe,CAAC;AASvB,MAAM,UAAU,UAAU;IACxB,mGAAmG;IACnG,MAAM,UAAU,GAAG,oBAAoB,CAAC;IACxC,cAAc,CAAC,UAAU,CAAC,CAAC;IAE3B,MAAM,WAAW,GAAG,kBAAkB,CACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAClD,CAAC;IACF,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,UAAU,qDAAqD,WAAW,EAAE,CAAC;AACzG,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,UAAkB,EAClB,UAAkB;IAElB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,0BAA0B,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,UAAU;SAClB,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,IAAI,GAAkB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAElD,OAAO;QACL,UAAU;QACV,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,MAAM,EAAE,IAAI,CAAC,OAAO;QACpB,QAAQ,EAAE,IAAI,CAAC,SAAS;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAE5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE1E,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC9D,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,gBAAgB,EAAE,CAAC;IAEnB,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Validate a Matrix user ID (e.g. @user:matrix.org).
3
+ */
4
+ export declare function isValidMatrixId(id: string): boolean;
5
+ /**
6
+ * Extract the localpart from a Matrix user ID (@user:server.com -> user).
7
+ */
8
+ export declare function extractLocalpart(matrixId: string): string;
9
+ //# sourceMappingURL=matrixId.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matrixId.d.ts","sourceRoot":"","sources":["../src/matrixId.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Validate a Matrix user ID (e.g. @user:matrix.org).
3
+ */
4
+ export function isValidMatrixId(id) {
5
+ return /^@[^:]+:.+$/.test(id);
6
+ }
7
+ /**
8
+ * Extract the localpart from a Matrix user ID (@user:server.com -> user).
9
+ */
10
+ export function extractLocalpart(matrixId) {
11
+ const match = matrixId.match(/^@([^:]+)/);
12
+ return match ? match[1] : matrixId;
13
+ }
14
+ //# sourceMappingURL=matrixId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matrixId.js","sourceRoot":"","sources":["../src/matrixId.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC"}
@@ -0,0 +1,36 @@
1
+ export declare const PRESENCE_EVENT = "com.matrixsheets.presence";
2
+ export declare const PRESENCE_TIMEOUT_MS: number;
3
+ export interface PresenceContent {
4
+ roomId: string;
5
+ focus: string | null;
6
+ timestamp: number;
7
+ user: {
8
+ id: string;
9
+ name: string;
10
+ color: string;
11
+ };
12
+ }
13
+ export interface PresenceUpdate {
14
+ userId: string;
15
+ name: string;
16
+ color: string;
17
+ focus: string | null;
18
+ timestamp: number;
19
+ }
20
+ export interface RemotePresence {
21
+ userId: string;
22
+ name: string;
23
+ color: string;
24
+ focus: string;
25
+ }
26
+ export interface ToDeviceEvent {
27
+ type: string;
28
+ content: unknown;
29
+ }
30
+ export declare function isPresenceContent(value: unknown): value is PresenceContent;
31
+ export declare function applyPresenceUpdates(current: RemotePresence[], updates: PresenceUpdate[]): RemotePresence[];
32
+ export declare function filterRelevantPresenceEvents(events: ToDeviceEvent[], options: {
33
+ roomId: string;
34
+ localUserId: string | null;
35
+ }): PresenceUpdate[];
36
+ //# sourceMappingURL=presence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,8BAA8B,CAAC;AAC1D,eAAO,MAAM,mBAAmB,QAAgB,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAkB1E;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,EAAE,cAAc,EAAE,GACxB,cAAc,EAAE,CAsBlB;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,GACA,cAAc,EAAE,CAwBlB"}
@@ -0,0 +1,63 @@
1
+ export const PRESENCE_EVENT = "com.matrixsheets.presence";
2
+ export const PRESENCE_TIMEOUT_MS = 5 * 60 * 1000;
3
+ export function isPresenceContent(value) {
4
+ if (!value || typeof value !== "object")
5
+ return false;
6
+ const content = value;
7
+ const user = content.user && typeof content.user === "object"
8
+ ? content.user
9
+ : null;
10
+ return (typeof content.roomId === "string" &&
11
+ (content.focus === null || typeof content.focus === "string") &&
12
+ typeof content.timestamp === "number" &&
13
+ !!user &&
14
+ typeof user.id === "string" &&
15
+ typeof user.name === "string" &&
16
+ typeof user.color === "string");
17
+ }
18
+ export function applyPresenceUpdates(current, updates) {
19
+ if (updates.length === 0)
20
+ return current;
21
+ const byUserId = new Map(current.map((presence) => [presence.userId, presence]));
22
+ for (const update of updates) {
23
+ if (update.focus === null) {
24
+ byUserId.delete(update.userId);
25
+ continue;
26
+ }
27
+ byUserId.set(update.userId, {
28
+ userId: update.userId,
29
+ name: update.name,
30
+ color: update.color,
31
+ focus: update.focus,
32
+ });
33
+ }
34
+ return Array.from(byUserId.values());
35
+ }
36
+ export function filterRelevantPresenceEvents(events, options) {
37
+ const { roomId, localUserId } = options;
38
+ if (!localUserId)
39
+ return [];
40
+ const now = Date.now();
41
+ const updates = [];
42
+ for (const event of events) {
43
+ if (event.type !== PRESENCE_EVENT)
44
+ continue;
45
+ if (!isPresenceContent(event.content))
46
+ continue;
47
+ if (event.content.roomId !== roomId)
48
+ continue;
49
+ if (event.content.user.id === localUserId)
50
+ continue;
51
+ if (now - event.content.timestamp >= PRESENCE_TIMEOUT_MS)
52
+ continue;
53
+ updates.push({
54
+ userId: event.content.user.id,
55
+ name: event.content.user.name,
56
+ color: event.content.user.color,
57
+ focus: event.content.focus,
58
+ timestamp: event.content.timestamp,
59
+ });
60
+ }
61
+ return updates;
62
+ }
63
+ //# sourceMappingURL=presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { MayDb } from "./MayDb";
2
+ export declare function getOrCreateSingletonRoomId(db: MayDb, accountDataKey: string, roomName: string): Promise<string>;
3
+ //# sourceMappingURL=singletonRoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singletonRoom.d.ts","sourceRoot":"","sources":["../src/singletonRoom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,KAAK,EACT,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
@@ -0,0 +1,39 @@
1
+ export async function getOrCreateSingletonRoomId(db, accountDataKey, roomName) {
2
+ const { matrixClient, namespace } = db;
3
+ const userId = matrixClient.getUserId();
4
+ if (!userId)
5
+ throw new Error("Not logged in");
6
+ let roomId = null;
7
+ try {
8
+ const accountData = (await matrixClient.getAccountDataFromServer(namespace));
9
+ roomId = accountData?.[accountDataKey] ?? null;
10
+ }
11
+ catch {
12
+ // No account data yet
13
+ }
14
+ if (roomId) {
15
+ try {
16
+ if (!matrixClient.getRoom(roomId)) {
17
+ await matrixClient.joinRoom(roomId);
18
+ }
19
+ }
20
+ catch {
21
+ // Room not accessible, create a new one
22
+ roomId = null;
23
+ }
24
+ }
25
+ if (!roomId) {
26
+ roomId = await db.createMatrixRoom(roomName);
27
+ let existingData = {};
28
+ try {
29
+ existingData =
30
+ (await matrixClient.getAccountDataFromServer(namespace)) || {};
31
+ }
32
+ catch {
33
+ // No existing data
34
+ }
35
+ await matrixClient.setAccountData(namespace, { ...existingData, [accountDataKey]: roomId });
36
+ }
37
+ return roomId;
38
+ }
39
+ //# sourceMappingURL=singletonRoom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singletonRoom.js","sourceRoot":"","sources":["../src/singletonRoom.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,EAAS,EACT,cAAsB,EACtB,QAAgB;IAEhB,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,CAAC;IACxC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IAE9C,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,CAAC,MAAM,YAAY,CAAC,wBAAwB,CAC9D,SAEI,CACL,CAAkC,CAAC;QACpC,MAAM,GAAG,WAAW,EAAE,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,MAAM,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;YACxC,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,MAAM,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAE7C,IAAI,YAAY,GAA2B,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,YAAY;gBACT,CAAC,MAAM,YAAY,CAAC,wBAAwB,CAC3C,SAEI,CACL,CAA4B,IAAI,EAAE,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,mBAAmB;QACrB,CAAC;QAED,MAAM,YAAY,CAAC,cAAc,CAC/B,SAEI,EACJ,EAAE,GAAG,YAAY,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAC9C,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type * as sdk from "matrix-js-sdk";
2
+ export interface AuthData {
3
+ homeserver: string;
4
+ accessToken: string;
5
+ userId: string;
6
+ deviceId: string;
7
+ }
8
+ export type AccessLevel = "view" | "edit" | "owner";
9
+ export interface RoomMember {
10
+ userId: string;
11
+ displayName: string;
12
+ accessLevel: AccessLevel;
13
+ }
14
+ export interface InviteInfo {
15
+ roomId: string;
16
+ roomName: string;
17
+ inviter: string;
18
+ alias: string | null;
19
+ }
20
+ export interface InviteMonitorOptions {
21
+ shouldJoin?: (invite: InviteInfo) => boolean;
22
+ onJoined: (invite: InviteInfo) => void | Promise<void>;
23
+ pollIntervalMs?: number;
24
+ }
25
+ export type RoomStatus = "disconnected" | "connecting" | "connected" | "error";
26
+ export interface RoomMetadata {
27
+ title: string;
28
+ }
29
+ export type MatrixClient = sdk.MatrixClient;
30
+ /**
31
+ * Interface for what a Room needs from its host client.
32
+ * This allows Room to work with different client implementations.
33
+ */
34
+ export interface RoomServices {
35
+ matrixClient: MatrixClient;
36
+ removeRoom(roomId: string): void;
37
+ }
38
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IAC7C,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;AAE5C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export declare const PRESENCE_COLORS: string[];
2
+ export declare function getUserColor(userId: string): string;
3
+ //# sourceMappingURL=presenceColors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presenceColors.d.ts","sourceRoot":"","sources":["../../src/utils/presenceColors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,UAW3B,CAAC;AAEF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOnD"}
@@ -0,0 +1,21 @@
1
+ export const PRESENCE_COLORS = [
2
+ "#E91E63", // Pink
3
+ "#9C27B0", // Purple
4
+ "#3F51B5", // Indigo
5
+ "#2196F3", // Blue
6
+ "#00BCD4", // Cyan
7
+ "#009688", // Teal
8
+ "#4CAF50", // Green
9
+ "#FF9800", // Orange
10
+ "#795548", // Brown
11
+ "#607D8B", // Blue Grey
12
+ ];
13
+ export function getUserColor(userId) {
14
+ let hash = 0;
15
+ for (let i = 0; i < userId.length; i++) {
16
+ hash = (hash << 5) - hash + userId.charCodeAt(i);
17
+ hash = hash & hash; // Convert to 32-bit integer
18
+ }
19
+ return PRESENCE_COLORS[Math.abs(hash) % PRESENCE_COLORS.length];
20
+ }
21
+ //# sourceMappingURL=presenceColors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presenceColors.js","sourceRoot":"","sources":["../../src/utils/presenceColors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,YAAY;CACxB,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,4BAA4B;IAClD,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAClE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@may-db/core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "matrix-crdt": "^0.2.1-alpha.1",
19
+ "matrix-js-sdk": "^40.2.0-rc.0",
20
+ "y-protocols": "^1.0.7",
21
+ "yjs": "^13.6.29"
22
+ },
23
+ "devDependencies": {
24
+ "eslint": "^9.39.1",
25
+ "patch-package": "^8.0.1",
26
+ "typescript": "~5.9.3"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc -p tsconfig.json",
30
+ "lint": "eslint src",
31
+ "postinstall": "patch-package"
32
+ }
33
+ }