sticky-horse
Version:
With StickyHorse allow your users to send feedback to your team.
712 lines (711 loc) • 36 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useStickyHorse = exports.StickyHorseProvider = void 0;
var react_1 = __importStar(require("react"));
var socket_1 = require("../utils/socket");
var __1 = require("..");
var getUserId_1 = require("../utils/getUserId");
var StickyHorseContext = (0, react_1.createContext)(null);
var StickyHorseProvider = function (_a) {
var children = _a.children;
var _b = (0, react_1.useState)([]), activeUsers = _b[0], setActiveUsers = _b[1];
var _c = (0, react_1.useState)([]), comments = _c[0], setComments = _c[1];
var _d = (0, react_1.useState)([]), stickyNotes = _d[0], setStickyNotes = _d[1];
var _e = (0, react_1.useState)([]), cursors = _e[0], setCursors = _e[1];
var _f = (0, react_1.useState)(''), currentUserId = _f[0], setCurrentUserId = _f[1];
var _g = (0, react_1.useState)([]), allUsers = _g[0], setAllUsers = _g[1];
var _h = (0, react_1.useState)([]), apiKeyStats = _h[0], setApiKeyStats = _h[1];
var _j = (0, react_1.useState)(window.location.pathname), currentPage = _j[0], setCurrentPage = _j[1];
var _k = (0, react_1.useState)(false), isBeingTracked = _k[0], setIsBeingTracked = _k[1];
var _l = (0, react_1.useState)(null), trackingAdmin = _l[0], setTrackingAdmin = _l[1];
var _m = (0, react_1.useState)(null), trackingInfo = _m[0], setTrackingInfo = _m[1];
var _o = (0, react_1.useState)({
isTracking: false
}), trackingSession = _o[0], setTrackingSession = _o[1];
var _p = (0, react_1.useState)(false), isConnected = _p[0], setIsConnected = _p[1];
var _q = (0, react_1.useState)(false), isActive = _q[0], setIsActive = _q[1];
var _r = (0, react_1.useState)({ x: 0, y: 0 }), laserPosition = _r[0], setLaserPosition = _r[1];
var _s = (0, react_1.useState)(false), isLaserActive = _s[0], setIsLaserActive = _s[1];
var _t = (0, react_1.useState)(null), isDraggingComment = _t[0], setIsDraggingComment = _t[1];
var _u = (0, react_1.useState)({ x: 0, y: 0 }), commentDragOffset = _u[0], setCommentDragOffset = _u[1];
var _v = (0, react_1.useState)(true), isServiceValid = _v[0], setIsServiceValid = _v[1];
var _w = (0, react_1.useState)(null), emailLimitError = _w[0], setEmailLimitError = _w[1];
var handleCommentMouseMove = (0, react_1.useCallback)(function (e) {
if (!isDraggingComment)
return;
var comment = comments.find(function (c) { return c.id === isDraggingComment; });
if (!comment)
return;
var newPosition = {
x: e.clientX - commentDragOffset.x,
y: e.clientY - commentDragOffset.y
};
// Update local state immediately for smooth dragging
setComments(function (prev) { return prev.map(function (c) {
return c.id === isDraggingComment
? __assign(__assign({}, c), { position: newPosition }) : c;
}); });
// Update position through socket
var socket = (0, socket_1.getSocket)();
if (socket) {
socket.emit('update_comment_position', {
commentId: isDraggingComment,
position: newPosition
});
}
}, [isDraggingComment, commentDragOffset, comments]);
(0, react_1.useEffect)(function () {
if (isDraggingComment) {
window.addEventListener('mousemove', handleCommentMouseMove);
window.addEventListener('mouseup', function () { return setIsDraggingComment(null); });
}
return function () {
window.removeEventListener('mousemove', handleCommentMouseMove);
window.removeEventListener('mouseup', function () { return setIsDraggingComment(null); });
};
}, [isDraggingComment, handleCommentMouseMove]);
var handleCommentMouseDown = function (e, commentId, position) {
var target = e.target;
if (target.tagName === 'BUTTON' ||
target.tagName === 'TEXTAREA' ||
target.tagName === 'INPUT' ||
target.closest('.text-content')) {
return;
}
e.preventDefault();
setIsDraggingComment(commentId);
setCommentDragOffset({
x: e.clientX - position.x,
y: e.clientY - position.y
});
};
(0, react_1.useEffect)(function () {
var checkLimits = function () { return __awaiter(void 0, void 0, void 0, function () {
var isValid, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, (0, __1.isStickyHorseValid)()];
case 1:
isValid = _a.sent();
setIsServiceValid(isValid);
if (!isValid) {
setEmailLimitError('Email limit exceeded. Service is disabled. Please upgrade your plan to continue.');
}
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
console.error('Failed to check service validity:', error_1);
setIsServiceValid(false);
setEmailLimitError(error_1 instanceof Error ? error_1.message : 'Service validation failed');
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
}); };
// Check immediately and then every minute
checkLimits();
var interval = setInterval(checkLimits, 60000);
return function () { return clearInterval(interval); };
}, []);
(0, react_1.useEffect)(function () {
var initializeService = function () { return __awaiter(void 0, void 0, void 0, function () {
var socket, userId_1, urlParams, isTracking, targetId, handleCursorMove_1, handlePageChange_1;
return __generator(this, function (_a) {
socket = (0, socket_1.getSocket)();
try {
if (!socket) {
setIsServiceValid(false);
setEmailLimitError('Service configuration is invalid');
return [2 /*return*/];
}
userId_1 = (0, getUserId_1.getUserId)();
setCurrentUserId(userId_1);
// Verify API key on connection
socket.on('connect_error', function (error) {
if (error.message.includes('Invalid API key')) {
setIsServiceValid(false);
socket.disconnect();
}
});
socket.on('api_key_invalid', function () {
setIsServiceValid(false);
socket.disconnect();
});
// Only proceed with socket events if service is valid
if (!isServiceValid) {
return [2 /*return*/];
}
socket.emit('user_connected', {
userId: userId_1,
connectionTime: new Date().toISOString(),
currentPage: window.location.pathname,
origin: window.location.origin,
// apiKey: config.apiKey
});
urlParams = new URLSearchParams(window.location.search);
isTracking = urlParams.get('tracking') === 'true';
targetId = urlParams.get('targetId');
if (isTracking || (trackingInfo === null || trackingInfo === void 0 ? void 0 : trackingInfo.userId) === currentUserId || targetId) {
setTrackingSession({
isTracking: true,
targetUserId: targetId || undefined
});
setTrackingInfo({ userId: targetId || currentUserId });
}
// If we're in tracking mode, initialize tracking
// if (isTracking && targetId) {
socket.emit('initialize_tracking', {
userId: userId_1,
targetId: targetId,
page: window.location.pathname
});
// }
socket.on('being_tracked', function (data) {
setTrackingAdmin(data.adminId);
setIsBeingTracked(true);
setTrackingInfo({ userId: data.userId });
// // Show notification
// setTrackingNotification({
// show: true,
// message: 'You are being tracked by an admin',
// type: 'user'
// });
// Start emitting cursor position
var handleUserCursorMove = function (e) {
var cursorData = {
userId: userId_1,
cursor: {
x: e.clientX + window.scrollX,
y: e.clientY + window.scrollY
},
timestamp: Date.now(),
page: window.location.pathname,
isAdmin: false
};
// console.log('Emitting user cursor:', cursorData);
socket.emit('cursor_move', cursorData);
};
window.addEventListener('mousemove', handleUserCursorMove);
return function () { return window.removeEventListener('mousemove', handleUserCursorMove); };
});
socket.on('users_update', function (users) {
setActiveUsers(users);
setAllUsers(users);
// console.log("✅ users_update ", users, activeUsers);
});
handleCursorMove_1 = function (e) {
var urlParams = new URLSearchParams(window.location.search);
var isTracking = urlParams.get('tracking') === 'true';
var targetId = urlParams.get('targetId');
// Always emit cursor move when being tracked OR tracking
if (isBeingTracked || isTracking) {
var cursorData = {
userId: userId_1,
cursor: {
x: e.clientX + window.scrollX,
y: e.clientY + window.scrollY
},
timestamp: Date.now(),
page: window.location.pathname,
targetId: targetId,
isAdmin: isTracking
};
// console.log('Emitting cursor:', cursorData);
socket.emit('cursor_move', cursorData);
}
};
// Add cursor tracking immediately when component mounts
window.addEventListener('mousemove', handleCursorMove_1);
socket.on('cursor_move', function (cursor) {
setCursors(function (prev) {
var filtered = prev.filter(function (c) { return c.userId !== cursor.userId; });
return __spreadArray(__spreadArray([], filtered, true), [cursor], false);
});
// Clear cursor after inactivity
// const timeoutId = setTimeout(() => {
// setCursors(prev => prev.filter(c => c.userId !== cursor.userId));
// }, 2500); // Increase timeout to 5 seconds
// return () => clearTimeout(timeoutId);
});
socket.on('receive_comment', function (comment) {
setComments(function (prev) {
if (prev.some(function (c) { return c.id === comment.id; })) {
return prev;
}
return __spreadArray(__spreadArray([], prev, true), [__assign(__assign({}, comment), { replies: [] })], false);
});
});
socket.on('receive_reply', function (reply) {
setComments(function (prev) { return prev.map(function (comment) {
if (comment.id === reply.parentId) {
return __assign(__assign({}, comment), { replies: __spreadArray(__spreadArray([], (comment.replies || []), true), [reply], false) });
}
return comment;
}); });
});
socket.on('sticky_note_added', function (note) {
setStickyNotes(function (prev) { return __spreadArray(__spreadArray([], prev, true), [note], false); });
});
socket.on('sticky_note_removed', function (_a) {
var noteId = _a.noteId;
setStickyNotes(function (prev) { return prev.filter(function (note) { return note.id !== noteId; }); });
});
socket.on('tracking_started', function (data) {
setTrackingInfo(data.userInfo);
// Open tracking page instead of user's page
var trackingUrl = "".concat(window.location.origin, "/tracking?tracking=true&targetId=").concat(data.targetId);
window.open(trackingUrl, '_blank');
});
socket.on('tracking_stopped', function () {
setTrackingAdmin(null);
setTrackingInfo(null);
setIsActive(false);
});
socket.on('connect', function () {
setIsConnected(true);
});
// Listen for laser pointer
socket.on('laser-dot', function (data) {
setLaserPosition(data.position);
setIsActive(true);
// Auto-hide laser after delay
var timeout = setTimeout(function () {
setIsActive(false);
}, 2000);
return function () { return clearTimeout(timeout); };
});
socket.on('laser-deactivated', function () {
setIsActive(false);
});
handlePageChange_1 = function () {
var newPage = window.location.pathname;
// setCurrentPage(newPage);
// Emit page change with tracking info if needed
socket.emit('page_change', {
userId: currentUserId,
page: newPage,
isTracking: trackingInfo !== null,
targetId: trackingInfo === null || trackingInfo === void 0 ? void 0 : trackingInfo.userId
});
// If tracking, update the URL to maintain tracking state
// if (trackingInfo) {
// const trackingUrl = `${trackingInfo.page || window.location.origin}${trackingInfo.page || '/'}?tracking=true&targetId=${trackingInfo.userId}`;
// window.open(trackingUrl, '_blank');
// }
};
window.addEventListener('popstate', handlePageChange_1);
// Add socket listener for comment removal
socket.on('comment_removed', function (data) {
setComments(function (prev) { return prev.filter(function (comment) { return comment.id !== data.commentId; }); });
});
// Add socket listener for comment position updates
socket.on('comment_position_updated', function (data) {
setComments(function (prev) { return prev.map(function (comment) {
return comment.id === data.commentId
? __assign(__assign({}, comment), { position: data.position }) : comment;
}); });
});
// Clean up
return [2 /*return*/, function () {
window.removeEventListener('mousemove', handleCursorMove_1);
window.removeEventListener('popstate', handlePageChange_1);
socket.off('tracking_started');
socket.off('being_tracked');
socket.off('tracking_stopped');
socket.off('cursor_move');
socket.off('comment_removed');
socket.off('receive_comment');
socket.off('comment_position_updated');
socket.off('receive_reply');
}];
}
catch (error) {
console.error('Service initialization failed:', error);
setIsServiceValid(false);
setEmailLimitError(error instanceof Error ? error.message : 'Service initialization failed');
}
return [2 /*return*/];
});
}); };
initializeService();
}, []);
var trackUser = function (userId) {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.emit('track_user', userId);
};
var stopTracking = function (userId) {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.emit('stop_tracking', userId);
};
var addComment = function (comment) {
var _a;
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
var commentData = __assign(__assign({}, comment), { fromUserId: currentUserId, timestamp: new Date().toISOString(), targetUserId: comment.parentId ?
(_a = comments.find(function (c) { return c.id === comment.parentId; })) === null || _a === void 0 ? void 0 : _a.fromUserId :
trackingInfo === null || trackingInfo === void 0 ? void 0 : trackingInfo.userId, page: window.location.pathname });
// Use different events for comments and replies
if (comment.parentId) {
socket.emit('send_reply', commentData);
}
else {
socket.emit('send_comment', commentData);
}
};
var addStickyNote = function (note) {
var socket = (0, socket_1.getSocket)();
if (!socket || !trackingInfo)
return;
var noteData = __assign(__assign({}, note), { userId: currentUserId, targetUserId: trackingInfo.userId, page: window.location.pathname });
socket.emit('add_sticky_note', noteData);
};
var removeComment = function (id) {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.emit('remove_comment', { commentId: id });
// Don't update state here, let the socket event handle it
};
var removeStickyNote = function (id) {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.emit('remove_sticky_note', { noteId: id });
};
var activateLaser = function () {
setIsLaserActive(true);
setIsActive(true);
var socket = (0, socket_1.getSocket)();
if (socket && trackingInfo) {
var position = {
x: window.innerWidth / 2,
y: window.innerHeight / 2
};
setLaserPosition(position);
socket.emit('laser-dot', {
userId: currentUserId,
targetUserId: trackingInfo.userId,
position: position
});
}
};
var deactivateLaser = function () {
setIsLaserActive(false);
setIsActive(false);
var socket = (0, socket_1.getSocket)();
if (socket && trackingInfo) {
socket.emit('laser-deactivated', {
userId: currentUserId,
targetUserId: trackingInfo.userId
});
}
};
// Update laser position on mouse move when active
(0, react_1.useEffect)(function () {
if (!isLaserActive)
return;
var handleMouseMove = function (e) {
var position = {
x: e.pageX,
y: e.pageY
};
setLaserPosition(position);
var socket = (0, socket_1.getSocket)();
if (socket && isLaserActive) {
socket.emit('laser-dot', {
userId: currentUserId,
targetUserId: trackingInfo === null || trackingInfo === void 0 ? void 0 : trackingInfo.userId,
position: position
});
}
};
window.addEventListener('mousemove', handleMouseMove);
return function () { return window.removeEventListener('mousemove', handleMouseMove); };
}, [isLaserActive, currentUserId, trackingInfo]);
var _x = (0, react_1.useState)(false), isFollowing = _x[0], setIsFollowing = _x[1];
// Toggle cursor follow
var scrollFollowers = function () {
var socket = (0, socket_1.getSocket)();
var newFollowState = !isFollowing;
setIsFollowing(newFollowState);
socket === null || socket === void 0 ? void 0 : socket.emit('toggle_cursor_follow', newFollowState);
};
(0, react_1.useEffect)(function () {
var animationFrameId;
var lastScrollTime = 0;
var scrollInterval = trackingAdmin ? 80 : 8; // Different intervals for admin/user
var scrollThreshold = 30;
var smoothFollow = function () {
if (!isFollowing) {
cancelAnimationFrame(animationFrameId);
return;
}
var currentTime = Date.now();
if (currentTime - lastScrollTime < scrollInterval) {
animationFrameId = requestAnimationFrame(smoothFollow);
return;
}
var targetCursor = cursors.find(function (c) { return c.userId === (trackingInfo === null || trackingInfo === void 0 ? void 0 : trackingInfo.userId); });
if (!(targetCursor === null || targetCursor === void 0 ? void 0 : targetCursor.cursor)) {
animationFrameId = requestAnimationFrame(smoothFollow);
return;
}
try {
var targetY = targetCursor.cursor.y - (window.innerHeight / 2);
var currentY = window.scrollY;
var differenceY = Math.abs(targetY - currentY);
if (differenceY > scrollThreshold) {
// Adjust speed based on whether we're admin or being tracked
var maxSpeed = trackingAdmin ? 120 : 30;
var speedY = Math.min(Math.max(differenceY * 0.6, 12), maxSpeed);
var stepY = (targetY - currentY) * (speedY / (trackingAdmin ? 35 : 30));
window.scrollBy({
top: stepY,
behavior: 'auto'
});
lastScrollTime = currentTime;
}
}
catch (error) {
console.error('Scroll error:', error);
cancelAnimationFrame(animationFrameId);
}
animationFrameId = requestAnimationFrame(smoothFollow);
};
if (isFollowing) {
animationFrameId = requestAnimationFrame(smoothFollow);
}
return function () {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
};
}, [isFollowing, cursors, trackingInfo, trackingAdmin]);
// Handle page changes for following
(0, react_1.useEffect)(function () {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.on('page_change', function (data) {
if (isFollowing && data.userId === (trackingInfo === null || trackingInfo === void 0 ? void 0 : trackingInfo.userId)) {
// Navigate to the new page while maintaining tracking
var newUrl = new URL(data.page, window.location.origin);
newUrl.searchParams.set('tracking', 'true');
newUrl.searchParams.set('targetId', data.userId);
window.location.href = newUrl.toString();
}
});
return function () {
socket.off('page_change');
};
}, [isFollowing, trackingInfo]);
// Handle cursor follow status and manual scroll detection
(0, react_1.useEffect)(function () {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.on('cursor_follow_status', function (status) {
setIsFollowing(status);
});
// Handle manual scroll
var lastScrollPosition = window.scrollY;
var scrollTimeout;
var isScrollingProgrammatically = false;
var handleScroll = function () {
if (!isFollowing)
return;
clearTimeout(scrollTimeout);
var currentScroll = window.scrollY;
var scrollDifference = Math.abs(currentScroll - lastScrollPosition);
if (scrollDifference > 10 && !isScrollingProgrammatically) {
scrollTimeout = setTimeout(function () {
setIsFollowing(false);
socket.emit('toggle_cursor_follow', false);
}, 150);
}
lastScrollPosition = currentScroll;
};
window.addEventListener('scroll', handleScroll, { passive: true });
return function () {
socket.off('cursor_follow_status');
window.removeEventListener('scroll', handleScroll);
clearTimeout(scrollTimeout);
};
}, [isFollowing]);
(0, react_1.useEffect)(function () {
var socket = (0, socket_1.getSocket)();
if (!socket)
return;
socket.on('sticky_note_position_updated', function (data) {
setStickyNotes(function (prev) { return prev.map(function (note) {
return note.id === data.noteId
? __assign(__assign({}, note), { position: data.position }) : note;
}); });
});
return function () {
socket.off('sticky_note_position_updated');
};
}, []);
// If service is invalid, render error message
if (!isServiceValid) {
console.error('StickyHorse service error:', emailLimitError);
return (react_1.default.createElement("div", { className: "fixed bottom-4 right-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded" },
react_1.default.createElement("strong", { className: "font-bold" }, "Service Disabled: "),
react_1.default.createElement("span", { className: "block sm:inline" }, emailLimitError)));
}
return (react_1.default.createElement(StickyHorseContext.Provider, { value: {
activeUsers: activeUsers,
comments: comments,
setComments: setComments,
stickyNotes: stickyNotes,
setStickyNotes: setStickyNotes,
cursors: cursors,
currentUserId: currentUserId,
addComment: addComment,
addStickyNote: addStickyNote,
removeComment: removeComment,
removeStickyNote: removeStickyNote,
trackUser: trackUser,
stopTracking: stopTracking,
allUsers: allUsers,
apiKeyStats: apiKeyStats,
currentPage: currentPage,
isBeingTracked: isBeingTracked,
trackingAdmin: trackingAdmin,
trackingInfo: trackingInfo,
isConnected: isConnected,
activateLaser: activateLaser,
deactivateLaser: deactivateLaser,
isLaserActive: isLaserActive,
handleCommentMouseDown: handleCommentMouseDown,
isDraggingComment: isDraggingComment,
isFollowing: isFollowing,
scrollFollowers: scrollFollowers,
isActive: isActive,
emailLimitError: emailLimitError,
isServiceValid: isServiceValid
} }, children));
};
exports.StickyHorseProvider = StickyHorseProvider;
var useStickyHorse = function () {
var context = (0, react_1.useContext)(StickyHorseContext);
if (!context) {
throw new Error('useStickyHorse must be used within a StickyHorseProvider');
}
return context;
};
exports.useStickyHorse = useStickyHorse;
var CursorDisplay = function () {
var _a = (0, exports.useStickyHorse)(), cursors = _a.cursors, currentUserId = _a.currentUserId;
return (react_1.default.createElement("div", null, cursors.map(function (cursor) {
// Don't show our own cursor
if (cursor.userId === currentUserId)
return null; // !cursor?.cursor ||
return (react_1.default.createElement("div", { key: cursor.userId, className: "absolute transform -translate-x-1/2 -translate-y-1/2 transition-all duration-100", style: {
left: cursor.cursor.x,
top: cursor.cursor.y,
transition: 'all 0.1s ease'
} },
react_1.default.createElement("svg", { width: "18", height: "18", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg",
// className="transform rotate-90"
style: { transform: 'rotate(15deg)' } },
react_1.default.createElement("path", { d: "M1 1L7.5 15L9.5 9L15 7.5L1 1Z", fill: cursor.isAdmin ? "#DC2626" : "#4F46E5", stroke: cursor.isAdmin ? "#991B1B" : "#312E81", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })),
react_1.default.createElement("div", { className: "absolute left-4 top-4 px-2 py-1 ".concat(cursor.isAdmin ? 'bg-red-600' : 'bg-blue-600', " text-white text-xs rounded whitespace-nowrap") }, cursor.isAdmin ? 'Admin' : 'User')));
})));
};
;