@object-ui/collaboration 5.1.1 → 5.3.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 CHANGED
@@ -1,5 +1,42 @@
1
1
  # @object-ui/collaboration
2
2
 
3
+ ## 5.3.0
4
+
5
+ ### Patch Changes
6
+
7
+ - @object-ui/types@5.3.0
8
+
9
+ ## 5.2.1
10
+
11
+ ### Patch Changes
12
+
13
+ - @object-ui/types@5.2.1
14
+
15
+ ## 5.2.0
16
+
17
+ ### Minor Changes
18
+
19
+ - a4a0e1d: Add `<PresenceProvider>` abstraction with `useTenantPresence()` and
20
+ `useRecordPresence(objectName, recordId)` hooks. The default source is a
21
+ no-op so hooks return `[]` until a host app wires in a realtime
22
+ transport (WebSocket / SSE). Replaces the two architectural TODOs in
23
+ `AppHeader` (tenant scope) and `RecordDetailView` (record scope) that
24
+ were waiting on this abstraction.
25
+
26
+ `AppHeader` now falls back to `useTenantPresence()` when the
27
+ `presenceUsers` prop is omitted, and `RecordDetailView` renders
28
+ `<PresenceAvatars>` next to the lifecycle badge when other users are
29
+ viewing the same record. Both code paths render exactly as before when
30
+ no provider is mounted, so this change is non-visual for existing
31
+ consumers.
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [de0c5e6]
36
+ - Updated dependencies [9997cae]
37
+ - Updated dependencies [70b5570]
38
+ - @object-ui/types@5.2.0
39
+
3
40
  ## 5.1.1
4
41
 
5
42
  ### Patch Changes
@@ -0,0 +1,75 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ import * as React from 'react';
9
+ import type { PresenceUser } from './usePresence';
10
+ /**
11
+ * Scope key for record-level presence ("who else is viewing this record").
12
+ */
13
+ export interface RecordPresenceScope {
14
+ objectName: string;
15
+ recordId: string;
16
+ }
17
+ /**
18
+ * Transport-agnostic source of presence updates. Implementations would
19
+ * typically be backed by a WebSocket / SSE channel; the platform ships a
20
+ * no-op default so consumers can read presence hooks unconditionally and
21
+ * simply render nothing until a real transport is wired in.
22
+ *
23
+ * Both `subscribeTenant` and `subscribeRecord` follow the same contract:
24
+ *
25
+ * 1. Push the *current* known user list to the callback synchronously
26
+ * (or as soon as it's available).
27
+ * 2. Push subsequent updates as users join / leave / change status.
28
+ * 3. Return an unsubscribe function that the hook will invoke on unmount
29
+ * or when the scope changes.
30
+ */
31
+ export interface PresenceSource {
32
+ /** Tenant-wide "who else is online in this workspace". */
33
+ subscribeTenant?: (cb: (users: PresenceUser[]) => void) => () => void;
34
+ /** Record-scoped "who else is viewing this record". */
35
+ subscribeRecord?: (scope: RecordPresenceScope, cb: (users: PresenceUser[]) => void) => () => void;
36
+ }
37
+ export interface PresenceProviderProps {
38
+ /**
39
+ * Concrete presence source. When omitted, all presence hooks resolve to
40
+ * an empty user list — useful for development and for environments
41
+ * without a realtime transport yet.
42
+ */
43
+ source?: PresenceSource;
44
+ children: React.ReactNode;
45
+ }
46
+ /**
47
+ * Provides a presence source to descendants. Wrap the application shell
48
+ * once; nested components consume the source via `useTenantPresence()`
49
+ * or `useRecordPresence()`.
50
+ *
51
+ * The default value is a no-op source so unwrapped trees still render
52
+ * correctly — the hooks simply return `[]`.
53
+ */
54
+ export declare function PresenceProvider({ source, children }: PresenceProviderProps): import("react/jsx-runtime").JSX.Element;
55
+ /**
56
+ * Read tenant-wide presence ("who else is in this workspace right now").
57
+ * Returns an empty array when no `PresenceProvider` is mounted or the
58
+ * source doesn't implement `subscribeTenant`.
59
+ */
60
+ export declare function useTenantPresence(): PresenceUser[];
61
+ /**
62
+ * Read presence for a specific record ("who else is viewing this customer
63
+ * detail page"). Returns an empty array when no `PresenceProvider` is
64
+ * mounted, the source doesn't implement `subscribeRecord`, or the scope
65
+ * is incomplete.
66
+ */
67
+ export declare function useRecordPresence(objectName: string | undefined, recordId: string | undefined): PresenceUser[];
68
+ /**
69
+ * Internal-only test helper that exposes the raw context value. Not part
70
+ * of the stable API; do not import from outside the package.
71
+ *
72
+ * @internal
73
+ */
74
+ export declare function __unsafe_usePresenceContext(): PresenceSource;
75
+ //# sourceMappingURL=PresenceProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PresenceProvider.d.ts","sourceRoot":"","sources":["../src/PresenceProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACtE,uDAAuD;IACvD,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,mBAAmB,EAC1B,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,KAChC,MAAM,IAAI,CAAC;CACjB;AAMD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,qBAAqB,2CAK3E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,EAAE,CAelD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,YAAY,EAAE,CAkBhB;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,cAAc,CAE5D"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ObjectUI
4
+ * Copyright (c) 2024-present ObjectStack Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+ import * as React from 'react';
10
+ const NOOP_SOURCE = {};
11
+ const PresenceContext = React.createContext(NOOP_SOURCE);
12
+ /**
13
+ * Provides a presence source to descendants. Wrap the application shell
14
+ * once; nested components consume the source via `useTenantPresence()`
15
+ * or `useRecordPresence()`.
16
+ *
17
+ * The default value is a no-op source so unwrapped trees still render
18
+ * correctly — the hooks simply return `[]`.
19
+ */
20
+ export function PresenceProvider({ source, children }) {
21
+ const value = source ?? NOOP_SOURCE;
22
+ return (_jsx(PresenceContext.Provider, { value: value, children: children }));
23
+ }
24
+ /**
25
+ * Read tenant-wide presence ("who else is in this workspace right now").
26
+ * Returns an empty array when no `PresenceProvider` is mounted or the
27
+ * source doesn't implement `subscribeTenant`.
28
+ */
29
+ export function useTenantPresence() {
30
+ const source = React.useContext(PresenceContext);
31
+ const [users, setUsers] = React.useState([]);
32
+ React.useEffect(() => {
33
+ if (!source.subscribeTenant) {
34
+ setUsers([]);
35
+ return;
36
+ }
37
+ return source.subscribeTenant((next) => {
38
+ setUsers(Array.isArray(next) ? next : []);
39
+ });
40
+ }, [source]);
41
+ return users;
42
+ }
43
+ /**
44
+ * Read presence for a specific record ("who else is viewing this customer
45
+ * detail page"). Returns an empty array when no `PresenceProvider` is
46
+ * mounted, the source doesn't implement `subscribeRecord`, or the scope
47
+ * is incomplete.
48
+ */
49
+ export function useRecordPresence(objectName, recordId) {
50
+ const source = React.useContext(PresenceContext);
51
+ const [users, setUsers] = React.useState([]);
52
+ React.useEffect(() => {
53
+ if (!source.subscribeRecord || !objectName || !recordId) {
54
+ setUsers([]);
55
+ return;
56
+ }
57
+ return source.subscribeRecord({ objectName, recordId }, (next) => {
58
+ setUsers(Array.isArray(next) ? next : []);
59
+ });
60
+ }, [source, objectName, recordId]);
61
+ return users;
62
+ }
63
+ /**
64
+ * Internal-only test helper that exposes the raw context value. Not part
65
+ * of the stable API; do not import from outside the package.
66
+ *
67
+ * @internal
68
+ */
69
+ export function __unsafe_usePresenceContext() {
70
+ return React.useContext(PresenceContext);
71
+ }
package/dist/index.d.ts CHANGED
@@ -26,5 +26,6 @@ export { useMentionNotifications, type MentionNotificationsConfig, type MentionN
26
26
  export { useCommentSearch, type CommentSearchConfig, type CommentSearchReturn, } from './useCommentSearch';
27
27
  export { LiveCursors, type LiveCursorsProps, } from './LiveCursors';
28
28
  export { PresenceAvatars, type PresenceAvatarsProps, } from './PresenceAvatars';
29
+ export { PresenceProvider, useTenantPresence, useRecordPresence, type PresenceSource, type PresenceProviderProps, type RecordPresenceScope, } from './PresenceProvider';
29
30
  export type { CollaborationPresence, CollaborationOperation, CollaborationConfig, } from '@object-ui/types';
30
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,KAAK,OAAO,EACZ,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,uBAAuB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,KAAK,OAAO,EACZ,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,uBAAuB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -26,3 +26,4 @@ export { useMentionNotifications, } from './useMentionNotifications';
26
26
  export { useCommentSearch, } from './useCommentSearch';
27
27
  export { LiveCursors, } from './LiveCursors';
28
28
  export { PresenceAvatars, } from './PresenceAvatars';
29
+ export { PresenceProvider, useTenantPresence, useRecordPresence, } from './PresenceProvider';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/collaboration",
3
- "version": "5.1.1",
3
+ "version": "5.3.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Real-time collaboration for Object UI with presence tracking, live cursors, conflict resolution, and comment threads.",
@@ -30,7 +30,7 @@
30
30
  "react": "^18.0.0 || ^19.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@object-ui/types": "5.1.1"
33
+ "@object-ui/types": "5.3.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/react": "19.2.14",