@agent-native/toolkit 0.2.0 → 0.4.1
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/README.md +17 -5
- package/dist/collab-ui/AgentPresenceChip.d.ts +12 -0
- package/dist/collab-ui/AgentPresenceChip.d.ts.map +1 -0
- package/dist/collab-ui/AgentPresenceChip.js +42 -0
- package/dist/collab-ui/AgentPresenceChip.js.map +1 -0
- package/dist/collab-ui/LiveCursorOverlay.d.ts +46 -0
- package/dist/collab-ui/LiveCursorOverlay.d.ts.map +1 -0
- package/dist/collab-ui/LiveCursorOverlay.js +124 -0
- package/dist/collab-ui/LiveCursorOverlay.js.map +1 -0
- package/dist/collab-ui/PresenceBar.d.ts +27 -0
- package/dist/collab-ui/PresenceBar.d.ts.map +1 -0
- package/dist/collab-ui/PresenceBar.js +165 -0
- package/dist/collab-ui/PresenceBar.js.map +1 -0
- package/dist/collab-ui/RecentEditHighlights.d.ts +44 -0
- package/dist/collab-ui/RecentEditHighlights.d.ts.map +1 -0
- package/dist/collab-ui/RecentEditHighlights.js +146 -0
- package/dist/collab-ui/RecentEditHighlights.js.map +1 -0
- package/dist/collab-ui/RemoteSelectionRings.d.ts +53 -0
- package/dist/collab-ui/RemoteSelectionRings.d.ts.map +1 -0
- package/dist/collab-ui/RemoteSelectionRings.js +130 -0
- package/dist/collab-ui/RemoteSelectionRings.js.map +1 -0
- package/dist/collab-ui/index.d.ts +7 -0
- package/dist/collab-ui/index.d.ts.map +1 -0
- package/dist/collab-ui/index.js +7 -0
- package/dist/collab-ui/index.js.map +1 -0
- package/dist/collab-ui/types.d.ts +48 -0
- package/dist/collab-ui/types.d.ts.map +1 -0
- package/dist/collab-ui/types.js +43 -0
- package/dist/collab-ui/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +14 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +15 -0
- package/dist/provider.js.map +1 -0
- package/dist/sharing/VisibilityBadge.d.ts +11 -0
- package/dist/sharing/VisibilityBadge.d.ts.map +1 -0
- package/dist/sharing/VisibilityBadge.js +19 -0
- package/dist/sharing/VisibilityBadge.js.map +1 -0
- package/dist/sharing/index.d.ts +2 -0
- package/dist/sharing/index.d.ts.map +1 -0
- package/dist/sharing/index.js +2 -0
- package/dist/sharing/index.js.map +1 -0
- package/dist/styles.css +19 -0
- package/dist/ui/button.d.ts +2 -1
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +14 -2
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/dropdown-menu.d.ts +3 -1
- package/dist/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/ui/dropdown-menu.js +1 -1
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/package.json +34 -9
package/README.md
CHANGED
|
@@ -3,19 +3,31 @@
|
|
|
3
3
|
Reusable app-building UI and helpers for Agent-Native apps.
|
|
4
4
|
|
|
5
5
|
`@agent-native/core` owns the foundational runtime contracts: actions, server
|
|
6
|
-
plugins, DB, app state, agent chat transport, sharing stores,
|
|
7
|
-
framework primitives. `@agent-native/toolkit` owns
|
|
8
|
-
|
|
9
|
-
and
|
|
6
|
+
plugins, DB, app state, agent chat transport, sharing stores, collaboration
|
|
7
|
+
transport, and other framework primitives. `@agent-native/toolkit` owns reusable
|
|
8
|
+
app-building surfaces: shadcn-style UI primitives, app-shell helpers, shared
|
|
9
|
+
hooks, sharing display UI, and collaboration display UI.
|
|
10
10
|
|
|
11
11
|
Existing `@agent-native/core` imports remain supported during the migration
|
|
12
|
-
window through compatibility re-exports.
|
|
12
|
+
window through compatibility re-exports. Those re-exports are temporary
|
|
13
|
+
migration support; the long-term dependency direction is Toolkit composing core
|
|
14
|
+
runtime APIs where needed, not core permanently depending on Toolkit for app UI.
|
|
15
|
+
|
|
16
|
+
Extract future behaviorful kits one at a time before broadening the split.
|
|
17
|
+
Sharing is the first candidate because it validates runtime composition, access
|
|
18
|
+
checks, action-backed data, and share-link UI together.
|
|
13
19
|
|
|
14
20
|
## Imports
|
|
15
21
|
|
|
16
22
|
```tsx
|
|
23
|
+
import { ToolkitProvider } from "@agent-native/toolkit/provider";
|
|
24
|
+
import { PresenceBar } from "@agent-native/toolkit/collab-ui";
|
|
25
|
+
import { VisibilityBadge } from "@agent-native/toolkit/sharing";
|
|
17
26
|
import { Button } from "@agent-native/toolkit/ui/button";
|
|
18
27
|
import { Toaster } from "@agent-native/toolkit/ui/sonner";
|
|
19
28
|
import { useToast } from "@agent-native/toolkit/hooks/use-toast";
|
|
20
29
|
import { useSetHeaderActions } from "@agent-native/toolkit/app-shell";
|
|
21
30
|
```
|
|
31
|
+
|
|
32
|
+
Inside template apps, prefer local adapters such as `@/components/ui/button` so
|
|
33
|
+
apps can replace their primitives without changing every callsite.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface AgentPresenceChipProps {
|
|
2
|
+
/** Whether the agent is actively editing this element. */
|
|
3
|
+
active: boolean;
|
|
4
|
+
/** Label text. Default: "AI editing" */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Color. Default: "#00B5FF" */
|
|
7
|
+
color?: string;
|
|
8
|
+
/** Additional CSS classes. */
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function AgentPresenceChip({ active, label, color, className, }: AgentPresenceChipProps): import("react").JSX.Element | null;
|
|
12
|
+
//# sourceMappingURL=AgentPresenceChip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentPresenceChip.d.ts","sourceRoot":"","sources":["../../src/collab-ui/AgentPresenceChip.tsx"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmBD,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,KAAoB,EACpB,KAAiB,EACjB,SAAS,GACV,EAAE,sBAAsB,sCAmCxB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const pulseKeyframes = `
|
|
3
|
+
@keyframes _anChipPulse {
|
|
4
|
+
0%, 100% { opacity: 1; }
|
|
5
|
+
50% { opacity: 0.6; }
|
|
6
|
+
}
|
|
7
|
+
`;
|
|
8
|
+
let styleInjected = false;
|
|
9
|
+
function injectStyles() {
|
|
10
|
+
if (styleInjected || typeof document === "undefined")
|
|
11
|
+
return;
|
|
12
|
+
const style = document.createElement("style");
|
|
13
|
+
style.textContent = pulseKeyframes;
|
|
14
|
+
document.head.appendChild(style);
|
|
15
|
+
styleInjected = true;
|
|
16
|
+
}
|
|
17
|
+
export function AgentPresenceChip({ active, label = "AI editing", color = "#00B5FF", className, }) {
|
|
18
|
+
if (!active)
|
|
19
|
+
return null;
|
|
20
|
+
injectStyles();
|
|
21
|
+
return (_jsxs("span", { className: className, style: {
|
|
22
|
+
display: "inline-flex",
|
|
23
|
+
alignItems: "center",
|
|
24
|
+
gap: 4,
|
|
25
|
+
height: 20,
|
|
26
|
+
padding: "0 8px",
|
|
27
|
+
borderRadius: 9999,
|
|
28
|
+
backgroundColor: `${color}20`,
|
|
29
|
+
color,
|
|
30
|
+
fontSize: 11,
|
|
31
|
+
fontWeight: 600,
|
|
32
|
+
whiteSpace: "nowrap",
|
|
33
|
+
}, children: [_jsx("span", { style: {
|
|
34
|
+
width: 6,
|
|
35
|
+
height: 6,
|
|
36
|
+
borderRadius: "50%",
|
|
37
|
+
backgroundColor: color,
|
|
38
|
+
animation: "_anChipPulse 2s infinite",
|
|
39
|
+
flexShrink: 0,
|
|
40
|
+
} }), label] }));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=AgentPresenceChip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentPresenceChip.js","sourceRoot":"","sources":["../../src/collab-ui/AgentPresenceChip.tsx"],"names":[],"mappings":";AAWA,MAAM,cAAc,GAAG;;;;;CAKtB,CAAC;AAEF,IAAI,aAAa,GAAG,KAAK,CAAC;AAE1B,SAAS,YAAY;IACnB,IAAI,aAAa,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,WAAW,GAAG,cAAc,CAAC;IACnC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,aAAa,GAAG,IAAI,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,MAAM,EACN,KAAK,GAAG,YAAY,EACpB,KAAK,GAAG,SAAS,EACjB,SAAS,GACc;IACvB,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,YAAY,EAAE,CAAC;IAEf,OAAO,CACL,gBACE,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE;YACL,OAAO,EAAE,aAAa;YACtB,UAAU,EAAE,QAAQ;YACpB,GAAG,EAAE,CAAC;YACN,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,OAAO;YAChB,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,GAAG,KAAK,IAAI;YAC7B,KAAK;YACL,QAAQ,EAAE,EAAE;YACZ,UAAU,EAAE,GAAG;YACf,UAAU,EAAE,QAAQ;SACrB,aAED,eACE,KAAK,EAAE;oBACL,KAAK,EAAE,CAAC;oBACR,MAAM,EAAE,CAAC;oBACT,YAAY,EAAE,KAAK;oBACnB,eAAe,EAAE,KAAK;oBACtB,SAAS,EAAE,0BAA0B;oBACrC,UAAU,EAAE,CAAC;iBACd,GACD,EACD,KAAK,IACD,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiveCursorOverlay — renders remote users' cursors over an absolutely-
|
|
3
|
+
* positioned container.
|
|
4
|
+
*
|
|
5
|
+
* Cursor positions are expected as normalized coordinates (0–1 relative to
|
|
6
|
+
* the container's content size) so different zoom/scroll positions map
|
|
7
|
+
* correctly. Pass a `mapCoords` prop to handle non-identity transforms
|
|
8
|
+
* (e.g. a zoomed canvas).
|
|
9
|
+
*
|
|
10
|
+
* The agent uses the same pointer shape with an "AI" label.
|
|
11
|
+
*
|
|
12
|
+
* Cursors fade out after 10 seconds of no movement.
|
|
13
|
+
*/
|
|
14
|
+
import { type RefObject } from "react";
|
|
15
|
+
import type { OtherPresence, NormalizedPoint } from "./types.js";
|
|
16
|
+
export interface CursorMapFn {
|
|
17
|
+
/** Convert normalized coords to pixel offsets within the overlay container. */
|
|
18
|
+
(norm: NormalizedPoint): {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface LiveCursorOverlayProps {
|
|
24
|
+
/** Remote participants with presence payload. */
|
|
25
|
+
others: OtherPresence[];
|
|
26
|
+
/**
|
|
27
|
+
* Key inside presence payload that carries the cursor position.
|
|
28
|
+
* Default: "cursor"
|
|
29
|
+
* Expected shape: { x: number; y: number } (normalized 0–1).
|
|
30
|
+
*/
|
|
31
|
+
cursorKey?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Override coordinate mapping. Default: scale by container clientWidth/Height.
|
|
34
|
+
* Pass this when the container uses transform: scale() or has virtual scroll.
|
|
35
|
+
*/
|
|
36
|
+
mapCoords?: CursorMapFn;
|
|
37
|
+
/**
|
|
38
|
+
* Container element ref. Required when mapCoords is not provided —
|
|
39
|
+
* used to compute pixel positions from normalized coords.
|
|
40
|
+
*/
|
|
41
|
+
containerRef?: RefObject<HTMLElement | null>;
|
|
42
|
+
/** Additional CSS class for the overlay div. */
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function LiveCursorOverlay({ others, cursorKey, mapCoords, containerRef, className, }: LiveCursorOverlayProps): import("react").JSX.Element;
|
|
46
|
+
//# sourceMappingURL=LiveCursorOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveCursorOverlay.d.ts","sourceRoot":"","sources":["../../src/collab-ui/LiveCursorOverlay.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAqC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,CAAC,IAAI,EAAE,eAAe,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED,MAAM,WAAW,sBAAsB;IACrC,iDAAiD;IACjD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsGD,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,SAAoB,EACpB,SAAS,EACT,YAAY,EACZ,SAAS,GACV,EAAE,sBAAsB,+BAuFxB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* LiveCursorOverlay — renders remote users' cursors over an absolutely-
|
|
4
|
+
* positioned container.
|
|
5
|
+
*
|
|
6
|
+
* Cursor positions are expected as normalized coordinates (0–1 relative to
|
|
7
|
+
* the container's content size) so different zoom/scroll positions map
|
|
8
|
+
* correctly. Pass a `mapCoords` prop to handle non-identity transforms
|
|
9
|
+
* (e.g. a zoomed canvas).
|
|
10
|
+
*
|
|
11
|
+
* The agent uses the same pointer shape with an "AI" label.
|
|
12
|
+
*
|
|
13
|
+
* Cursors fade out after 10 seconds of no movement.
|
|
14
|
+
*/
|
|
15
|
+
import { useState, useEffect, useRef, memo } from "react";
|
|
16
|
+
const STALE_MS = 10_000; // Fade out cursors older than 10s
|
|
17
|
+
function CursorPointer({ color }) {
|
|
18
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "25", height: "29", viewBox: "0 0 25 29", fill: "none", "aria-hidden": true, style: {
|
|
19
|
+
display: "block",
|
|
20
|
+
filter: "drop-shadow(0 2px 3px rgba(15, 23, 42, 0.3))",
|
|
21
|
+
}, children: _jsx("path", { d: "M2.5 2.5L21.5 11L12.6 14.1L8.3 24.5L2.5 2.5Z", fill: color, stroke: "white", strokeLinejoin: "round", strokeWidth: "3" }) }));
|
|
22
|
+
}
|
|
23
|
+
const CursorLabel = memo(function CursorLabel({ entry, }) {
|
|
24
|
+
const { other, x, y } = entry;
|
|
25
|
+
const color = other.user.color || "#94a3b8";
|
|
26
|
+
const label = other.isAgent ? "AI" : other.user.name || other.user.email;
|
|
27
|
+
return (_jsxs("div", { "aria-label": `${label} cursor`, style: {
|
|
28
|
+
position: "absolute",
|
|
29
|
+
left: x,
|
|
30
|
+
top: y,
|
|
31
|
+
pointerEvents: "none",
|
|
32
|
+
userSelect: "none",
|
|
33
|
+
transform: "translate(-2px, -2px)",
|
|
34
|
+
zIndex: 9999,
|
|
35
|
+
transition: "left 120ms linear, top 120ms linear",
|
|
36
|
+
width: 0,
|
|
37
|
+
height: 0,
|
|
38
|
+
}, children: [_jsx(CursorPointer, { color: color }), _jsxs("div", { style: {
|
|
39
|
+
position: "absolute",
|
|
40
|
+
left: 31,
|
|
41
|
+
top: 31,
|
|
42
|
+
display: "flex",
|
|
43
|
+
alignItems: "center",
|
|
44
|
+
gap: 5,
|
|
45
|
+
backgroundColor: color,
|
|
46
|
+
color: "#fff",
|
|
47
|
+
fontSize: 12,
|
|
48
|
+
fontWeight: 500,
|
|
49
|
+
lineHeight: "16px",
|
|
50
|
+
padding: "2px 7px",
|
|
51
|
+
borderRadius: 2,
|
|
52
|
+
whiteSpace: "nowrap",
|
|
53
|
+
boxShadow: "0 5px 12px rgba(15, 23, 42, 0.16)",
|
|
54
|
+
maxWidth: 150,
|
|
55
|
+
overflow: "hidden",
|
|
56
|
+
textOverflow: "ellipsis",
|
|
57
|
+
}, children: [other.user.avatarUrl ? (_jsx("img", { src: other.user.avatarUrl, alt: "", style: {
|
|
58
|
+
width: 14,
|
|
59
|
+
height: 14,
|
|
60
|
+
borderRadius: "50%",
|
|
61
|
+
flexShrink: 0,
|
|
62
|
+
} })) : null, label] })] }));
|
|
63
|
+
});
|
|
64
|
+
export function LiveCursorOverlay({ others, cursorKey = "cursor", mapCoords, containerRef, className, }) {
|
|
65
|
+
const overlayRef = useRef(null);
|
|
66
|
+
const [, tick] = useState(0); // Force re-render to prune stale cursors
|
|
67
|
+
const entriesRef = useRef(new Map());
|
|
68
|
+
// Tick every 5s to prune stale cursors (no re-render storm).
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const id = setInterval(() => tick((n) => n + 1), 5_000);
|
|
71
|
+
return () => clearInterval(id);
|
|
72
|
+
}, []);
|
|
73
|
+
// Build entries from others, computing pixel positions.
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const visible = [];
|
|
76
|
+
for (const other of others) {
|
|
77
|
+
const pos = other.presence[cursorKey];
|
|
78
|
+
if (!pos || typeof pos.x !== "number" || typeof pos.y !== "number")
|
|
79
|
+
continue;
|
|
80
|
+
// Compute pixel position.
|
|
81
|
+
let px;
|
|
82
|
+
let py;
|
|
83
|
+
if (mapCoords) {
|
|
84
|
+
const mapped = mapCoords(pos);
|
|
85
|
+
px = mapped.x;
|
|
86
|
+
py = mapped.y;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const container = containerRef?.current ?? overlayRef.current?.parentElement;
|
|
90
|
+
const w = container ? container.clientWidth : 0;
|
|
91
|
+
const h = container ? container.clientHeight : 0;
|
|
92
|
+
px = pos.x * w;
|
|
93
|
+
py = pos.y * h;
|
|
94
|
+
}
|
|
95
|
+
const prev = entriesRef.current.get(other.clientId);
|
|
96
|
+
const lastSeen = prev && prev.x === px && prev.y === py ? prev.lastSeen : now;
|
|
97
|
+
const entry = { other, x: px, y: py, lastSeen };
|
|
98
|
+
entriesRef.current.set(other.clientId, entry);
|
|
99
|
+
if (now - lastSeen < STALE_MS) {
|
|
100
|
+
visible.push(entry);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Remove entries for participants who left.
|
|
104
|
+
for (const clientId of entriesRef.current.keys()) {
|
|
105
|
+
if (!others.find((o) => o.clientId === clientId)) {
|
|
106
|
+
entriesRef.current.delete(clientId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (visible.length === 0) {
|
|
110
|
+
return (_jsx("div", { ref: overlayRef, "aria-hidden": true, style: {
|
|
111
|
+
position: "absolute",
|
|
112
|
+
inset: 0,
|
|
113
|
+
pointerEvents: "none",
|
|
114
|
+
overflow: "hidden",
|
|
115
|
+
}, className: className }));
|
|
116
|
+
}
|
|
117
|
+
return (_jsx("div", { ref: overlayRef, "aria-hidden": true, style: {
|
|
118
|
+
position: "absolute",
|
|
119
|
+
inset: 0,
|
|
120
|
+
pointerEvents: "none",
|
|
121
|
+
overflow: "hidden",
|
|
122
|
+
}, className: className, children: visible.map((entry) => (_jsx(CursorLabel, { entry: entry }, entry.other.clientId))) }));
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=LiveCursorOverlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveCursorOverlay.js","sourceRoot":"","sources":["../../src/collab-ui/LiveCursorOverlay.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CollabUser } from "./types.js";
|
|
2
|
+
export interface PresenceBarProps {
|
|
3
|
+
/** Active collaborators on this document. */
|
|
4
|
+
activeUsers: CollabUser[];
|
|
5
|
+
/** Whether the agent has a durable presence entry. */
|
|
6
|
+
agentPresent?: boolean;
|
|
7
|
+
/** Whether the agent is actively making edits right now. */
|
|
8
|
+
agentActive?: boolean;
|
|
9
|
+
/** Current user's email (to exclude from the list). */
|
|
10
|
+
currentUserEmail?: string;
|
|
11
|
+
/** Max visible avatars before "+N" overflow. Default: 5 */
|
|
12
|
+
maxVisible?: number;
|
|
13
|
+
/** Additional CSS classes. */
|
|
14
|
+
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Called when an avatar is clicked. Receives the user being clicked
|
|
17
|
+
* (or null for the agent avatar). Use this to start/stop follow mode.
|
|
18
|
+
*/
|
|
19
|
+
onAvatarClick?: (user: CollabUser | null) => void;
|
|
20
|
+
/**
|
|
21
|
+
* The email of the user currently being followed. Highlighted with a
|
|
22
|
+
* blue ring to indicate active follow mode.
|
|
23
|
+
*/
|
|
24
|
+
followingEmail?: string | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function PresenceBar({ activeUsers, agentPresent, agentActive, currentUserEmail, maxVisible, className, onAvatarClick, followingEmail, }: PresenceBarProps): import("react").JSX.Element | null;
|
|
27
|
+
//# sourceMappingURL=PresenceBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenceBar.d.ts","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.tsx"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,sDAAsD;IACtD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AA2OD,wBAAgB,WAAW,CAAC,EAC1B,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,UAAc,EACd,SAAS,EACT,aAAa,EACb,cAAc,GACf,EAAE,gBAAgB,sCA+DlB"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../ui/tooltip.js";
|
|
4
|
+
import { dedupeCollabUsersByEmail, emailToColor, emailToName, } from "./types.js";
|
|
5
|
+
const AVATAR_SIZE = 28;
|
|
6
|
+
const OVERLAP = -8;
|
|
7
|
+
const BORDER_WIDTH = 2;
|
|
8
|
+
const FONT_SIZE = 12;
|
|
9
|
+
const AGENT_COLOR = "#00B5FF";
|
|
10
|
+
const baseAvatarStyle = {
|
|
11
|
+
width: AVATAR_SIZE,
|
|
12
|
+
height: AVATAR_SIZE,
|
|
13
|
+
borderRadius: "50%",
|
|
14
|
+
display: "flex",
|
|
15
|
+
alignItems: "center",
|
|
16
|
+
justifyContent: "center",
|
|
17
|
+
fontSize: FONT_SIZE,
|
|
18
|
+
fontWeight: 700,
|
|
19
|
+
color: "#fff",
|
|
20
|
+
border: `${BORDER_WIDTH}px solid #fff`,
|
|
21
|
+
flexShrink: 0,
|
|
22
|
+
position: "relative",
|
|
23
|
+
cursor: "default",
|
|
24
|
+
boxSizing: "border-box",
|
|
25
|
+
};
|
|
26
|
+
const containerStyle = {
|
|
27
|
+
display: "flex",
|
|
28
|
+
alignItems: "center",
|
|
29
|
+
flexDirection: "row",
|
|
30
|
+
};
|
|
31
|
+
const pulseKeyframes = `
|
|
32
|
+
@keyframes _anPresencePulse {
|
|
33
|
+
0%, 100% { opacity: 1; }
|
|
34
|
+
50% { opacity: 0.6; }
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
let styleInjected = false;
|
|
38
|
+
function injectStyles() {
|
|
39
|
+
if (styleInjected || typeof document === "undefined")
|
|
40
|
+
return;
|
|
41
|
+
const style = document.createElement("style");
|
|
42
|
+
style.textContent = pulseKeyframes;
|
|
43
|
+
document.head.appendChild(style);
|
|
44
|
+
styleInjected = true;
|
|
45
|
+
}
|
|
46
|
+
function UserAvatar({ user, isFirst, onClick, isFollowing, }) {
|
|
47
|
+
const color = user.color || emailToColor(user.email);
|
|
48
|
+
const name = user.name || emailToName(user.email);
|
|
49
|
+
const initial = name.charAt(0).toUpperCase();
|
|
50
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { style: {
|
|
51
|
+
...baseAvatarStyle,
|
|
52
|
+
backgroundColor: color,
|
|
53
|
+
marginLeft: isFirst ? 0 : OVERLAP,
|
|
54
|
+
cursor: onClick ? "pointer" : "default",
|
|
55
|
+
boxShadow: isFollowing
|
|
56
|
+
? `0 0 0 2px #3b82f6, 0 0 0 4px #fff`
|
|
57
|
+
: `0 0 0 2px #fff`,
|
|
58
|
+
}, "aria-label": `${name} (${user.email})${isFollowing ? " — following" : ""}`, tabIndex: onClick ? 0 : undefined, role: onClick ? "button" : undefined, onClick: onClick, onKeyDown: (e) => {
|
|
59
|
+
if (e.key === "Enter" || e.key === " ")
|
|
60
|
+
onClick?.();
|
|
61
|
+
}, children: user.avatarUrl ? (_jsx("img", { src: user.avatarUrl, alt: "", style: {
|
|
62
|
+
width: "100%",
|
|
63
|
+
height: "100%",
|
|
64
|
+
borderRadius: "50%",
|
|
65
|
+
objectFit: "cover",
|
|
66
|
+
} })) : (initial) }) }), _jsx(TooltipContent, { side: "bottom", children: isFollowing
|
|
67
|
+
? `Following ${name} — click to stop`
|
|
68
|
+
: onClick
|
|
69
|
+
? `${name} — click to follow`
|
|
70
|
+
: user.email })] }));
|
|
71
|
+
}
|
|
72
|
+
function AgentAvatar({ active, onClick, isFollowing, }) {
|
|
73
|
+
injectStyles();
|
|
74
|
+
return (_jsxs("div", { style: {
|
|
75
|
+
display: "flex",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
gap: 4,
|
|
78
|
+
}, children: [_jsx("div", { style: {
|
|
79
|
+
...baseAvatarStyle,
|
|
80
|
+
backgroundColor: AGENT_COLOR,
|
|
81
|
+
marginLeft: 0,
|
|
82
|
+
animation: active ? "_anPresencePulse 2s infinite" : undefined,
|
|
83
|
+
cursor: onClick ? "pointer" : "default",
|
|
84
|
+
boxShadow: isFollowing
|
|
85
|
+
? `0 0 0 2px #3b82f6, 0 0 0 4px #fff`
|
|
86
|
+
: undefined,
|
|
87
|
+
}, title: isFollowing
|
|
88
|
+
? "Following AI — click to stop"
|
|
89
|
+
: active
|
|
90
|
+
? "AI is editing"
|
|
91
|
+
: "AI agent", onClick: onClick, tabIndex: onClick ? 0 : undefined, onKeyDown: (e) => {
|
|
92
|
+
if (e.key === "Enter" || e.key === " ")
|
|
93
|
+
onClick?.();
|
|
94
|
+
}, role: onClick ? "button" : undefined, children: "A" }), active && !isFollowing && _jsx(AgentEditingChip, {}), isFollowing && (_jsx("span", { style: {
|
|
95
|
+
display: "inline-flex",
|
|
96
|
+
alignItems: "center",
|
|
97
|
+
height: 20,
|
|
98
|
+
padding: "0 8px",
|
|
99
|
+
borderRadius: 9999,
|
|
100
|
+
backgroundColor: `#3b82f620`,
|
|
101
|
+
color: "#3b82f6",
|
|
102
|
+
fontSize: 11,
|
|
103
|
+
fontWeight: 600,
|
|
104
|
+
whiteSpace: "nowrap",
|
|
105
|
+
}, children: "Following AI" }))] }));
|
|
106
|
+
}
|
|
107
|
+
function AgentEditingChip() {
|
|
108
|
+
return (_jsxs("span", { style: {
|
|
109
|
+
display: "inline-flex",
|
|
110
|
+
alignItems: "center",
|
|
111
|
+
gap: 4,
|
|
112
|
+
height: 20,
|
|
113
|
+
padding: "0 8px",
|
|
114
|
+
borderRadius: 9999,
|
|
115
|
+
backgroundColor: `${AGENT_COLOR}20`,
|
|
116
|
+
color: AGENT_COLOR,
|
|
117
|
+
fontSize: 11,
|
|
118
|
+
fontWeight: 600,
|
|
119
|
+
whiteSpace: "nowrap",
|
|
120
|
+
}, children: [_jsx("span", { style: {
|
|
121
|
+
width: 6,
|
|
122
|
+
height: 6,
|
|
123
|
+
borderRadius: "50%",
|
|
124
|
+
backgroundColor: AGENT_COLOR,
|
|
125
|
+
animation: "_anPresencePulse 2s infinite",
|
|
126
|
+
flexShrink: 0,
|
|
127
|
+
} }), "AI editing"] }));
|
|
128
|
+
}
|
|
129
|
+
function OverflowBadge({ count, isFirst, }) {
|
|
130
|
+
return (_jsxs("div", { style: {
|
|
131
|
+
...baseAvatarStyle,
|
|
132
|
+
backgroundColor: "rgba(255,255,255,0.1)",
|
|
133
|
+
color: "rgba(255,255,255,0.5)",
|
|
134
|
+
marginLeft: isFirst ? 0 : OVERLAP,
|
|
135
|
+
fontSize: 10,
|
|
136
|
+
}, title: `${count} more collaborator${count === 1 ? "" : "s"}`, children: ["+", count] }));
|
|
137
|
+
}
|
|
138
|
+
export function PresenceBar({ activeUsers, agentPresent, agentActive, currentUserEmail, maxVisible = 5, className, onAvatarClick, followingEmail, }) {
|
|
139
|
+
const { humanUsers, showAgent } = useMemo(() => {
|
|
140
|
+
const currentEmail = currentUserEmail?.trim().toLowerCase();
|
|
141
|
+
const uniqueUsers = dedupeCollabUsersByEmail(activeUsers);
|
|
142
|
+
const humans = uniqueUsers.filter((u) => {
|
|
143
|
+
const email = u.email.trim().toLowerCase();
|
|
144
|
+
return email !== currentEmail && email !== "agent@system";
|
|
145
|
+
});
|
|
146
|
+
const hasAgentUser = uniqueUsers.some((u) => u.email.trim().toLowerCase() === "agent@system");
|
|
147
|
+
return {
|
|
148
|
+
humanUsers: humans,
|
|
149
|
+
showAgent: agentPresent || agentActive || hasAgentUser,
|
|
150
|
+
};
|
|
151
|
+
}, [activeUsers, currentUserEmail, agentPresent, agentActive]);
|
|
152
|
+
const visibleUsers = humanUsers.slice(0, maxVisible);
|
|
153
|
+
const overflowCount = humanUsers.length - visibleUsers.length;
|
|
154
|
+
if (!showAgent && humanUsers.length === 0)
|
|
155
|
+
return null;
|
|
156
|
+
const followingLower = followingEmail?.trim().toLowerCase() ?? null;
|
|
157
|
+
const isFollowingAgent = followingLower === "agent@system";
|
|
158
|
+
return (_jsx(TooltipProvider, { delayDuration: 150, children: _jsxs("div", { style: containerStyle, className: className, children: [showAgent && (_jsx(AgentAvatar, { active: !!agentActive, onClick: onAvatarClick ? () => onAvatarClick(null) : undefined, isFollowing: isFollowingAgent })), visibleUsers.length > 0 && (_jsxs("div", { style: {
|
|
159
|
+
display: "flex",
|
|
160
|
+
alignItems: "center",
|
|
161
|
+
marginLeft: showAgent ? 6 : 0,
|
|
162
|
+
}, children: [visibleUsers.map((u, i) => (_jsx(UserAvatar, { user: u, isFirst: i === 0, onClick: onAvatarClick ? () => onAvatarClick(u) : undefined, isFollowing: followingLower != null &&
|
|
163
|
+
u.email.trim().toLowerCase() === followingLower }, u.email))), overflowCount > 0 && (_jsx(OverflowBadge, { count: overflowCount, isFirst: false }))] }))] }) }));
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=PresenceBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenceBar.js","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecentEditHighlights — lingering, fading highlights over regions another
|
|
3
|
+
* participant (human or agent) just edited, with their name/avatar flag.
|
|
4
|
+
*
|
|
5
|
+
* The Google-Docs/Figma feel: when a collaborator or the AI edits something,
|
|
6
|
+
* the changed region glows in their color and the flag identifies them; the
|
|
7
|
+
* highlight fades out over the last portion of its lifetime instead of
|
|
8
|
+
* vanishing.
|
|
9
|
+
*
|
|
10
|
+
* Feed it `useRecentEdits(others)` (which reads participants' `recentEdits`
|
|
11
|
+
* awareness rings) and a `resolveRect` that maps an edit descriptor to a
|
|
12
|
+
* viewport DOMRect for your surface:
|
|
13
|
+
*
|
|
14
|
+
* const others = usePresence(awareness, ydoc?.clientID).others;
|
|
15
|
+
* const edits = useRecentEdits(others);
|
|
16
|
+
* <RecentEditHighlights
|
|
17
|
+
* edits={edits}
|
|
18
|
+
* resolveRect={(edit) =>
|
|
19
|
+
* edit.descriptor.kind === "selector"
|
|
20
|
+
* ? container.querySelector(edit.descriptor.selector)?.getBoundingClientRect() ?? null
|
|
21
|
+
* : null
|
|
22
|
+
* }
|
|
23
|
+
* containerRef={containerRef}
|
|
24
|
+
* />
|
|
25
|
+
*/
|
|
26
|
+
import { type RefObject } from "react";
|
|
27
|
+
import { type AttributedRecentEdit } from "./types.js";
|
|
28
|
+
export interface RecentEditHighlightsProps {
|
|
29
|
+
/** Attributed recent edits (from `useRecentEdits`). */
|
|
30
|
+
edits: AttributedRecentEdit[];
|
|
31
|
+
/**
|
|
32
|
+
* Resolver: maps an edit to a viewport-relative DOMRect, or null when the
|
|
33
|
+
* region can't be located (the edit is skipped).
|
|
34
|
+
*/
|
|
35
|
+
resolveRect: (edit: AttributedRecentEdit) => DOMRect | null;
|
|
36
|
+
/** Container the highlights are positioned within (position: relative). */
|
|
37
|
+
containerRef: RefObject<HTMLElement | null>;
|
|
38
|
+
/** Highlight lifetime; should match useRecentEdits ttlMs. Default 6000. */
|
|
39
|
+
ttlMs?: number;
|
|
40
|
+
/** Additional CSS class for the overlay div. */
|
|
41
|
+
className?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function RecentEditHighlights({ edits, resolveRect, containerRef, ttlMs, className, }: RecentEditHighlightsProps): import("react").JSX.Element;
|
|
44
|
+
//# sourceMappingURL=RecentEditHighlights.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecentEditHighlights.d.ts","sourceRoot":"","sources":["../../src/collab-ui/RecentEditHighlights.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B;;;OAGG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,GAAG,IAAI,CAAC;IAC5D,2EAA2E;IAC3E,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAuED,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,WAAW,EACX,YAAY,EACZ,KAA0B,EAC1B,SAAS,GACV,EAAE,yBAAyB,+BAgG3B"}
|