organify-ui 0.3.23 → 0.3.26

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.
@@ -1,7 +1,7 @@
1
- import { cn, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ScrollArea, Skeleton, Avatar, AvatarFallback, Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, Badge, useOrganifyApi, useOrganifyUser, useOrganifyWorkspace, useOrganifyGql } from './chunk-276ZWKXB.js';
1
+ import { cn, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ScrollArea, Skeleton, useOrganifyApi, useOrganify, Avatar, AvatarImage, AvatarFallback, Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, Badge, useOrganifyUser, useOrganifyWorkspace, useOrganifyGql } from './chunk-276ZWKXB.js';
2
2
  import { OrgPlus, OrgSearch, OrgComment, OrgClose, OrgEdit, OrgTeam, OrgGlobe, OrgLock, OrgCheckCircle, OrgError, OrgWarning, OrgInfo, OrgChevronDown, OrgChevronRight, OrgDoor, OrgTrash, OrgCheck, OrgFolder, OrgChevronLeft } from './chunk-MZKEDV5W.js';
3
3
  import * as React6 from 'react';
4
- import React6__default, { useState, useCallback, useEffect } from 'react';
4
+ import React6__default, { useState, useEffect, useCallback } from 'react';
5
5
  import { Slot } from '@radix-ui/react-slot';
6
6
  import { cva } from 'class-variance-authority';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -176,9 +176,9 @@ var buttonVariants = cva(
176
176
  variant: {
177
177
  default: "bg-primary !text-white hover:bg-primary/90",
178
178
  destructive: "!bg-destructive !text-white hover:bg-destructive/90 focus-visible:ring-destructive/20",
179
- outline: "border bg-background border-input !text-white hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:text-white dark:hover:bg-input/50",
180
- secondary: "bg-secondary !text-white hover:bg-secondary/80 !dark:text-white",
181
- ghost: "!text-white hover:bg-accent hover:text-accent-foreground !dark:text-white dark:hover:bg-accent/50",
179
+ outline: "border border-theme-strong bg-transparent text-theme hover:bg-theme-subtle",
180
+ secondary: "bg-theme-elevated text-theme hover:bg-theme-subtle",
181
+ ghost: "text-theme hover:bg-theme-subtle",
182
182
  link: "!text-primary underline-offset-4 hover:underline"
183
183
  },
184
184
  size: {
@@ -221,7 +221,7 @@ function Button({
221
221
  );
222
222
  }
223
223
  var inputVariants = cva(
224
- "flex w-full bg-theme-subtle backdrop-blur-md border border-theme-subtle rounded-xl px-4 py-3 text-sm font-light text-theme transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-theme-muted focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
224
+ "flex w-full bg-theme-void backdrop-blur-md border border-theme-subtle rounded-xl px-4 py-3 text-sm font-light text-theme transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-theme-muted focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
225
225
  {
226
226
  variants: {
227
227
  variant: {
@@ -399,6 +399,236 @@ function ChatSidebar({
399
399
  ] }) }) })
400
400
  ] });
401
401
  }
402
+ var _cache = /* @__PURE__ */ new Map();
403
+ var _inflight = /* @__PURE__ */ new Map();
404
+ var _pendingBatch = /* @__PURE__ */ new Set();
405
+ var _batchTimer = null;
406
+ var _batchResolvers = /* @__PURE__ */ new Map();
407
+ function _flushBatch(gatewayUrl) {
408
+ const ids = Array.from(_pendingBatch);
409
+ _pendingBatch.clear();
410
+ _batchTimer = null;
411
+ if (ids.length === 0) return;
412
+ const p = fetch(`${gatewayUrl}/api/users/profiles/batch`, {
413
+ method: "POST",
414
+ headers: { "Content-Type": "application/json" },
415
+ credentials: "include",
416
+ body: JSON.stringify({ ids })
417
+ }).then((r) => r.ok ? r.json() : null).catch(() => null).then((body) => {
418
+ const results = !body ? [] : Array.isArray(body) ? body : Array.isArray(body.data) ? body.data : [];
419
+ const byId = /* @__PURE__ */ new Map();
420
+ for (const u of results) {
421
+ byId.set(String(u.id), u);
422
+ }
423
+ for (const id of ids) {
424
+ const user = byId.get(id) ?? null;
425
+ _cache.set(id, user);
426
+ _inflight.delete(id);
427
+ const resolvers = _batchResolvers.get(id) ?? [];
428
+ _batchResolvers.delete(id);
429
+ for (const resolve of resolvers) resolve(user);
430
+ }
431
+ });
432
+ for (const id of ids) {
433
+ if (!_batchResolvers.has(id)) _batchResolvers.set(id, []);
434
+ _inflight.set(id, p.then(() => _cache.get(id) ?? null));
435
+ }
436
+ }
437
+ function resolveUser(userId, gatewayUrl) {
438
+ if (_cache.has(userId)) {
439
+ return Promise.resolve(_cache.get(userId));
440
+ }
441
+ if (_inflight.has(userId)) {
442
+ return _inflight.get(userId);
443
+ }
444
+ _pendingBatch.add(userId);
445
+ if (!_batchResolvers.has(userId)) _batchResolvers.set(userId, []);
446
+ if (!_batchTimer) {
447
+ _batchTimer = setTimeout(() => _flushBatch(gatewayUrl), 0);
448
+ }
449
+ const p = new Promise((resolve) => {
450
+ _batchResolvers.get(userId).push(resolve);
451
+ });
452
+ _inflight.set(userId, p);
453
+ return p;
454
+ }
455
+ function invalidateUserCache(userId) {
456
+ _cache.delete(userId);
457
+ _inflight.delete(userId);
458
+ }
459
+ function seedUserCache(users) {
460
+ for (const u of users) {
461
+ _cache.set(String(u.id), u);
462
+ }
463
+ }
464
+ function useResolvedUser(userId) {
465
+ const api = useOrganifyApi();
466
+ const { user: currentUser } = useOrganify();
467
+ const key = userId != null ? String(userId) : void 0;
468
+ if (currentUser?.id != null) {
469
+ const ck = String(currentUser.id);
470
+ if (!_cache.has(ck)) {
471
+ _cache.set(ck, {
472
+ id: currentUser.id,
473
+ name: currentUser.name ?? "",
474
+ avatarUrl: currentUser.avatarUrl ?? void 0
475
+ });
476
+ }
477
+ }
478
+ const [user, setUser] = useState(() => {
479
+ if (!key) return void 0;
480
+ return _cache.has(key) ? _cache.get(key) : void 0;
481
+ });
482
+ useEffect(() => {
483
+ if (!key || !api.gatewayUrl) return;
484
+ let cancelled = false;
485
+ if (_cache.has(key)) {
486
+ setUser(_cache.get(key) ?? null);
487
+ return;
488
+ }
489
+ resolveUser(key, api.gatewayUrl).then((u) => {
490
+ if (!cancelled) setUser(u);
491
+ });
492
+ return () => {
493
+ cancelled = true;
494
+ };
495
+ }, [key, api.gatewayUrl]);
496
+ useEffect(() => {
497
+ if (!key || !currentUser?.id) return;
498
+ if (String(currentUser.id) === key) {
499
+ setUser(_cache.get(key) ?? null);
500
+ }
501
+ }, [key, currentUser]);
502
+ return user;
503
+ }
504
+ function getInitials(name) {
505
+ return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
506
+ }
507
+ function nameToGradient(name) {
508
+ let hash = 0;
509
+ for (let i = 0; i < name.length; i++) {
510
+ hash = name.charCodeAt(i) + ((hash << 5) - hash);
511
+ }
512
+ const gradients = [
513
+ "from-violet-500 to-purple-600",
514
+ "from-blue-500 to-cyan-500",
515
+ "from-emerald-500 to-teal-500",
516
+ "from-orange-500 to-amber-500",
517
+ "from-rose-500 to-pink-500",
518
+ "from-indigo-500 to-blue-600",
519
+ "from-fuchsia-500 to-purple-500",
520
+ "from-teal-500 to-green-500"
521
+ ];
522
+ return gradients[Math.abs(hash) % gradients.length];
523
+ }
524
+ function UserAvatar({
525
+ userId,
526
+ name: nameProp,
527
+ src: srcProp,
528
+ email,
529
+ role,
530
+ showHoverInfo = true,
531
+ fallback,
532
+ extraInfo,
533
+ onClick,
534
+ className,
535
+ size = "default",
536
+ shape = "circle",
537
+ status,
538
+ ...props
539
+ }) {
540
+ const resolved = useResolvedUser(!nameProp && userId != null ? userId : null);
541
+ const name = nameProp ?? resolved?.name ?? "\u2026";
542
+ const src = srcProp ?? resolved?.avatarUrl ?? void 0;
543
+ const isLoading = !nameProp && userId != null && resolved === void 0;
544
+ const initials = getInitials(name);
545
+ const gradient = nameToGradient(name || "unknown");
546
+ const avatarElement = /* @__PURE__ */ jsxs(
547
+ Avatar,
548
+ {
549
+ size,
550
+ shape,
551
+ status,
552
+ gradient,
553
+ className: cn(
554
+ onClick && "cursor-pointer",
555
+ isLoading && "animate-pulse opacity-60",
556
+ className
557
+ ),
558
+ onClick,
559
+ ...props,
560
+ children: [
561
+ src ? /* @__PURE__ */ jsx(AvatarImage, { src, alt: name }) : null,
562
+ /* @__PURE__ */ jsx(
563
+ AvatarFallback,
564
+ {
565
+ className: cn(
566
+ "bg-gradient-to-br text-white font-semibold",
567
+ gradient,
568
+ size === "sm" && "text-xs",
569
+ size === "default" && "text-sm",
570
+ size === "lg" && "text-lg",
571
+ size === "xl" && "text-xl"
572
+ ),
573
+ children: fallback ?? initials
574
+ }
575
+ )
576
+ ]
577
+ }
578
+ );
579
+ if (!showHoverInfo) return avatarElement;
580
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
581
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: avatarElement }),
582
+ /* @__PURE__ */ jsxs(
583
+ TooltipContent,
584
+ {
585
+ side: "top",
586
+ className: cn(
587
+ "flex items-center gap-3 rounded-xl border border-theme-subtle",
588
+ "bg-card-surface/95 px-4 py-3 shadow-glass-xl backdrop-blur-2xl",
589
+ "max-w-xs"
590
+ ),
591
+ children: [
592
+ /* @__PURE__ */ jsxs(Avatar, { size: "sm", shape, gradient, children: [
593
+ src ? /* @__PURE__ */ jsx(AvatarImage, { src, alt: name }) : null,
594
+ /* @__PURE__ */ jsx(AvatarFallback, { className: cn("bg-gradient-to-br text-white text-xs font-semibold", gradient), children: initials })
595
+ ] }),
596
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
597
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-white truncate", children: name }),
598
+ email && /* @__PURE__ */ jsx("span", { className: "text-xs text-theme-muted truncate", children: email }),
599
+ role && /* @__PURE__ */ jsx("span", { className: "text-xs text-primary-light/80 capitalize", children: role.toLowerCase() }),
600
+ extraInfo?.map((info) => /* @__PURE__ */ jsxs("span", { className: "text-xs text-theme-muted", children: [
601
+ info.label,
602
+ ": ",
603
+ info.value
604
+ ] }, info.label))
605
+ ] })
606
+ ]
607
+ }
608
+ )
609
+ ] }) });
610
+ }
611
+ UserAvatar.displayName = "UserAvatar";
612
+ function UserDisplayName({
613
+ userId,
614
+ name: nameProp,
615
+ loadingFallback = "\u2026",
616
+ className,
617
+ style
618
+ }) {
619
+ const resolved = useResolvedUser(!nameProp && userId != null ? userId : null);
620
+ const isLoading = !nameProp && userId != null && resolved === void 0;
621
+ const displayName = nameProp ?? resolved?.name ?? (isLoading ? loadingFallback : "?");
622
+ return /* @__PURE__ */ jsx(
623
+ "span",
624
+ {
625
+ className: cn(isLoading && "animate-pulse opacity-60", className),
626
+ style,
627
+ title: displayName,
628
+ children: displayName
629
+ }
630
+ );
631
+ }
402
632
  var actionBarVariants = {
403
633
  hidden: {
404
634
  opacity: 0,
@@ -455,10 +685,6 @@ function formatTime(iso) {
455
685
  minute: "2-digit"
456
686
  });
457
687
  }
458
- function getInitials(name) {
459
- if (!name) return "??";
460
- return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
461
- }
462
688
  function groupReactions(reactions) {
463
689
  const map = /* @__PURE__ */ new Map();
464
690
  reactions.forEach((r) => {
@@ -796,7 +1022,17 @@ function MessageBubble({
796
1022
  showActions && "bg-theme-subtle"
797
1023
  ),
798
1024
  children: [
799
- /* @__PURE__ */ jsx(motion.div, { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 }, children: /* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "mt-0.5 shrink-0", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-xs font-semibold", children: getInitials(message.authorName || message.authorId) }) }) }),
1025
+ /* @__PURE__ */ jsx(motion.div, { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 }, children: /* @__PURE__ */ jsx(
1026
+ UserAvatar,
1027
+ {
1028
+ userId: message.authorId,
1029
+ name: message.authorName,
1030
+ src: message.authorAvatarUrl,
1031
+ size: "sm",
1032
+ showHoverInfo: false,
1033
+ className: "mt-0.5 shrink-0"
1034
+ }
1035
+ ) }),
800
1036
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 relative", children: [
801
1037
  /* @__PURE__ */ jsx(
802
1038
  ActionBar,
@@ -837,7 +1073,7 @@ function MessageBubble({
837
1073
  }
838
1074
  ) }),
839
1075
  /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 mb-0.5", children: [
840
- /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-theme", children: message.authorName || message.authorId.substring(0, 8) }),
1076
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-theme", children: /* @__PURE__ */ jsx(UserDisplayName, { userId: message.authorId, name: message.authorName }) }),
841
1077
  /* @__PURE__ */ jsx("span", { className: "text-[11px] text-theme-muted", children: formatTime(message.createdAt) }),
842
1078
  message.edited && /* @__PURE__ */ jsx(
843
1079
  motion.span,
@@ -2713,7 +2949,6 @@ function useChat(options = {}) {
2713
2949
  }, [userId, user, options.workspaceMembers]);
2714
2950
  const fetchRooms = useCallback(async () => {
2715
2951
  if (initialRooms && initialRooms.length > 0) return;
2716
- if (!workspaceId || workspaceId.startsWith("temp-")) return;
2717
2952
  if (!userId) return;
2718
2953
  setLoadingRooms(true);
2719
2954
  setError(null);
@@ -2739,6 +2974,40 @@ function useChat(options = {}) {
2739
2974
  setLoadingRooms(false);
2740
2975
  }
2741
2976
  }, [workspaceId, userId, gql, initialRooms]);
2977
+ const resolveUnknownUsers = useCallback(async (msgs) => {
2978
+ const unknownIds = [...new Set(msgs.map((m) => m.authorId).filter((id) => !userCache.has(id)))];
2979
+ if (unknownIds.length === 0 || !workspaceId || workspaceId.startsWith("temp-")) return;
2980
+ try {
2981
+ const data = await centralGql(
2982
+ "workspaces",
2983
+ `query($slug: String!) {
2984
+ workspace(slug: $slug) {
2985
+ members {
2986
+ userId
2987
+ user { id name email avatar }
2988
+ }
2989
+ }
2990
+ }`,
2991
+ { slug: workspaceId }
2992
+ // Hack: Assuming workspaceId is the slug for this query, ideally we'd query users directly if there was an endpoint, but this refreshes workspace members
2993
+ );
2994
+ const newMembers = data?.workspace?.members || [];
2995
+ setUserCache((prev) => {
2996
+ const next = new Map(prev);
2997
+ for (const member of newMembers) {
2998
+ if (member.user?.id && !next.has(member.user.id)) {
2999
+ next.set(member.user.id, {
3000
+ displayName: member.user.name || member.user.email || `User`,
3001
+ avatarUrl: member.user.avatar ?? void 0
3002
+ });
3003
+ }
3004
+ }
3005
+ return next;
3006
+ });
3007
+ } catch (e) {
3008
+ console.warn("[organify-chat] resolveUnknownUsers failed:", e);
3009
+ }
3010
+ }, [userCache, workspaceId, centralGql]);
2742
3011
  const MESSAGE_FIELDS = `
2743
3012
  id roomId authorId parentId
2744
3013
  content edited editedAt createdAt
@@ -2767,9 +3036,11 @@ function useChat(options = {}) {
2767
3036
  }`,
2768
3037
  { roomId, filter: { limit: 50 } }
2769
3038
  );
2770
- setMessages(data.messages?.items ?? []);
3039
+ const fetchedMessages = data.messages?.items ?? [];
3040
+ setMessages(fetchedMessages);
2771
3041
  setHasMoreMessages(data.messages?.hasMore ?? false);
2772
3042
  setMessageCursor(data.messages?.nextCursor ?? null);
3043
+ resolveUnknownUsers(fetchedMessages);
2773
3044
  } catch (err) {
2774
3045
  console.error("[organify-chat] fetchMessages:", err);
2775
3046
  setError("Erro ao carregar mensagens.");
@@ -2797,6 +3068,7 @@ function useChat(options = {}) {
2797
3068
  setMessages((prev) => [...olderMessages, ...prev]);
2798
3069
  setHasMoreMessages(data.messages?.hasMore ?? false);
2799
3070
  setMessageCursor(data.messages?.nextCursor ?? null);
3071
+ resolveUnknownUsers(olderMessages);
2800
3072
  } catch (err) {
2801
3073
  console.error("[organify-chat] loadMore:", err);
2802
3074
  } finally {
@@ -4471,6 +4743,6 @@ function useAiInline({ gatewayUrl, workspaceId, projectId }) {
4471
4743
  return { loading, suggestion, requestSuggestion, dismiss };
4472
4744
  }
4473
4745
 
4474
- export { AiChatSidebar, Alert, Button, ChatMessages, ChatSidebar, CommandBar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, InlineAiButton, Input, Label, MOCK_PROJECTS, MOCK_USERS, MentionPopover, MessageBubble, MessageInput, OrgLoader, OrgLoaderInline, OrganifyChat, ResponsiveDialog, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TypingIndicatorMock, alertVariants, buttonVariants, generateAutoReplies, getMockMentionOptions, getRoomPermissions, inputVariants, orgLoaderVariants, typingIndicator, useAiInline, useChat };
4475
- //# sourceMappingURL=chunk-SNGMKLFD.js.map
4476
- //# sourceMappingURL=chunk-SNGMKLFD.js.map
4746
+ export { AiChatSidebar, Alert, Button, ChatMessages, ChatSidebar, CommandBar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, InlineAiButton, Input, Label, MOCK_PROJECTS, MOCK_USERS, MentionPopover, MessageBubble, MessageInput, OrgLoader, OrgLoaderInline, OrganifyChat, ResponsiveDialog, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TypingIndicatorMock, UserAvatar, UserDisplayName, alertVariants, buttonVariants, generateAutoReplies, getMockMentionOptions, getRoomPermissions, inputVariants, invalidateUserCache, orgLoaderVariants, resolveUser, seedUserCache, typingIndicator, useAiInline, useChat, useResolvedUser };
4747
+ //# sourceMappingURL=chunk-AV6TZPXE.js.map
4748
+ //# sourceMappingURL=chunk-AV6TZPXE.js.map