@arbidocs/blocks 0.3.127 → 0.3.129

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/dist/index.d.cts CHANGED
@@ -520,40 +520,73 @@ interface WorkspaceCardMember {
520
520
  }
521
521
  interface WorkspaceCardProps {
522
522
  name: string;
523
- /** Uppercase eyebrow above the description, e.g. the matter/project name. */
523
+ /** Uppercase eyebrow under the name, e.g. the matter reference. Omit to let
524
+ * the name and description carry the card on their own. */
524
525
  matterRef?: string;
525
526
  description?: string;
526
527
  docCount: number;
528
+ /** Conversation count, shown as a third stat. Omit to hide the stat entirely. */
529
+ chatCount?: number;
527
530
  /** Defaults to `members.length` when omitted. */
528
531
  memberCount?: number;
529
- /** Drives the overlapping avatar stack (shows up to 4, then a `+N` overflow). */
532
+ /** Drives the overlapping avatar stack (shows up to 3, then a `+N` overflow). */
530
533
  members?: WorkspaceCardMember[];
531
- /** ISO timestamp — renders "Updated {relative} ago" when present. */
534
+ /**
535
+ * The member list is not visible to this viewer (e.g. row-level security hides
536
+ * the members of a public workspace you haven't joined). Renders an em dash in
537
+ * place of the count and drops the avatar stack, rather than lying with a `0`.
538
+ */
539
+ membersHidden?: boolean;
540
+ /** ISO timestamp — renders "Updated {relative} ago" at the footer's right edge. */
532
541
  updatedAt?: string;
533
- /** Marks this as the current workspace: a done-badge + disabled footer button. */
542
+ /** Marks this as the current workspace: an accented card surface and a footer label. */
534
543
  active?: boolean;
535
- /** Shows a spinner in the footer button while a switch is in flight. */
544
+ /** Shows a spinner in the Open affordance while a switch is in flight. */
536
545
  busy?: boolean;
537
546
  onOpen?: () => void;
538
547
  /** Icon in the header tile. Defaults to a lock/folder glyph. */
539
548
  icon?: LucideIcon;
540
- /** Stable id for the card root; the footer button gets `${testId}-open`. */
549
+ /** Replaces the whole header icon tile — for a richer, app-supplied glyph. */
550
+ iconSlot?: ReactNode;
551
+ /** Canonical workspace entity pill shown at the top of the description tooltip. */
552
+ tooltipEntitySlot?: ReactNode;
553
+ /**
554
+ * Secondary context for the card (the ARBI app puts the owning project here).
555
+ * Sits top-right and is revealed on hover/focus, so the resting card stays
556
+ * about the workspace itself.
557
+ */
558
+ cornerSlot?: ReactNode;
559
+ /**
560
+ * Accent as a theme colour-token key (e.g. `emerald`, or a firm's own brand
561
+ * token), resolved to `var(--color-<accent>)` at render — same contract as
562
+ * MetricCard. Tints the icon tile and the Open affordance, so a firm can accent
563
+ * the card without its brand classes leaking into the kit. `'default'` (or
564
+ * omitted) uses the semantic `primary` token.
565
+ */
566
+ accent?: string;
567
+ /** Stable id for the card root; the footer action gets `${testId}-open`. */
541
568
  testId?: string;
542
- /** Overrides the footer button's test id when a caller needs a specific one. */
569
+ /** Overrides the footer action's test id when a caller needs a specific one. */
543
570
  buttonTestId?: string;
544
571
  className?: string;
545
572
  }
546
573
  /**
547
- * The one true workspace card. A themeable, source-agnostic tile — icon,
548
- * Active/Encrypted badge, title + uppercase matter eyebrow, a two-line
549
- * description, a doc/member stat row, an overlapping member avatar stack, an
550
- * "Updated … ago" line and a full-width Open / Current-workspace footer button.
574
+ * The one true workspace card. A themeable, source-agnostic tile, in three bands:
575
+ *
576
+ * 1. header — icon + workspace name; secondary context (`cornerSlot`) hides
577
+ * top-right until hover, keeping the resting card about the workspace.
578
+ * 2. body — an expanded description area with the centred Open/current
579
+ * affordance layered over it only on hover or keyboard focus.
580
+ * 3. footer — doc/chat counts stay left and membership stays right, followed
581
+ * by a final left-aligned update timestamp.
582
+ *
583
+ * State is said once, in words. There is deliberately no status badge.
551
584
  *
552
585
  * Styled with the kit's SEMANTIC tokens only (foreground, muted-foreground,
553
586
  * primary, card, border, accent), so every firm gets it in its own colours
554
587
  * without any firm-branded classes leaking in.
555
588
  */
556
- declare function WorkspaceCard({ name, matterRef, description, docCount, memberCount, members, updatedAt, active, busy, onOpen, icon: Icon, testId, buttonTestId, className, }: WorkspaceCardProps): react.JSX.Element;
589
+ declare function WorkspaceCard({ name, matterRef, description, docCount, chatCount, memberCount, members, membersHidden, updatedAt, active, busy, onOpen, icon: Icon, iconSlot, tooltipEntitySlot, cornerSlot, accent, testId, buttonTestId, className, }: WorkspaceCardProps): react.JSX.Element;
557
590
 
558
591
  interface StatItem {
559
592
  value: string;
package/dist/index.d.ts CHANGED
@@ -520,40 +520,73 @@ interface WorkspaceCardMember {
520
520
  }
521
521
  interface WorkspaceCardProps {
522
522
  name: string;
523
- /** Uppercase eyebrow above the description, e.g. the matter/project name. */
523
+ /** Uppercase eyebrow under the name, e.g. the matter reference. Omit to let
524
+ * the name and description carry the card on their own. */
524
525
  matterRef?: string;
525
526
  description?: string;
526
527
  docCount: number;
528
+ /** Conversation count, shown as a third stat. Omit to hide the stat entirely. */
529
+ chatCount?: number;
527
530
  /** Defaults to `members.length` when omitted. */
528
531
  memberCount?: number;
529
- /** Drives the overlapping avatar stack (shows up to 4, then a `+N` overflow). */
532
+ /** Drives the overlapping avatar stack (shows up to 3, then a `+N` overflow). */
530
533
  members?: WorkspaceCardMember[];
531
- /** ISO timestamp — renders "Updated {relative} ago" when present. */
534
+ /**
535
+ * The member list is not visible to this viewer (e.g. row-level security hides
536
+ * the members of a public workspace you haven't joined). Renders an em dash in
537
+ * place of the count and drops the avatar stack, rather than lying with a `0`.
538
+ */
539
+ membersHidden?: boolean;
540
+ /** ISO timestamp — renders "Updated {relative} ago" at the footer's right edge. */
532
541
  updatedAt?: string;
533
- /** Marks this as the current workspace: a done-badge + disabled footer button. */
542
+ /** Marks this as the current workspace: an accented card surface and a footer label. */
534
543
  active?: boolean;
535
- /** Shows a spinner in the footer button while a switch is in flight. */
544
+ /** Shows a spinner in the Open affordance while a switch is in flight. */
536
545
  busy?: boolean;
537
546
  onOpen?: () => void;
538
547
  /** Icon in the header tile. Defaults to a lock/folder glyph. */
539
548
  icon?: LucideIcon;
540
- /** Stable id for the card root; the footer button gets `${testId}-open`. */
549
+ /** Replaces the whole header icon tile — for a richer, app-supplied glyph. */
550
+ iconSlot?: ReactNode;
551
+ /** Canonical workspace entity pill shown at the top of the description tooltip. */
552
+ tooltipEntitySlot?: ReactNode;
553
+ /**
554
+ * Secondary context for the card (the ARBI app puts the owning project here).
555
+ * Sits top-right and is revealed on hover/focus, so the resting card stays
556
+ * about the workspace itself.
557
+ */
558
+ cornerSlot?: ReactNode;
559
+ /**
560
+ * Accent as a theme colour-token key (e.g. `emerald`, or a firm's own brand
561
+ * token), resolved to `var(--color-<accent>)` at render — same contract as
562
+ * MetricCard. Tints the icon tile and the Open affordance, so a firm can accent
563
+ * the card without its brand classes leaking into the kit. `'default'` (or
564
+ * omitted) uses the semantic `primary` token.
565
+ */
566
+ accent?: string;
567
+ /** Stable id for the card root; the footer action gets `${testId}-open`. */
541
568
  testId?: string;
542
- /** Overrides the footer button's test id when a caller needs a specific one. */
569
+ /** Overrides the footer action's test id when a caller needs a specific one. */
543
570
  buttonTestId?: string;
544
571
  className?: string;
545
572
  }
546
573
  /**
547
- * The one true workspace card. A themeable, source-agnostic tile — icon,
548
- * Active/Encrypted badge, title + uppercase matter eyebrow, a two-line
549
- * description, a doc/member stat row, an overlapping member avatar stack, an
550
- * "Updated … ago" line and a full-width Open / Current-workspace footer button.
574
+ * The one true workspace card. A themeable, source-agnostic tile, in three bands:
575
+ *
576
+ * 1. header — icon + workspace name; secondary context (`cornerSlot`) hides
577
+ * top-right until hover, keeping the resting card about the workspace.
578
+ * 2. body — an expanded description area with the centred Open/current
579
+ * affordance layered over it only on hover or keyboard focus.
580
+ * 3. footer — doc/chat counts stay left and membership stays right, followed
581
+ * by a final left-aligned update timestamp.
582
+ *
583
+ * State is said once, in words. There is deliberately no status badge.
551
584
  *
552
585
  * Styled with the kit's SEMANTIC tokens only (foreground, muted-foreground,
553
586
  * primary, card, border, accent), so every firm gets it in its own colours
554
587
  * without any firm-branded classes leaking in.
555
588
  */
556
- declare function WorkspaceCard({ name, matterRef, description, docCount, memberCount, members, updatedAt, active, busy, onOpen, icon: Icon, testId, buttonTestId, className, }: WorkspaceCardProps): react.JSX.Element;
589
+ declare function WorkspaceCard({ name, matterRef, description, docCount, chatCount, memberCount, members, membersHidden, updatedAt, active, busy, onOpen, icon: Icon, iconSlot, tooltipEntitySlot, cornerSlot, accent, testId, buttonTestId, className, }: WorkspaceCardProps): react.JSX.Element;
557
590
 
558
591
  interface StatItem {
559
592
  value: string;
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { tid, initials, fromNow, fmtDate, assetRefId, applyStoredTheme, AssetResolverProvider, ThemeEditor, applyThemeVars, SectionHeading, PricingTable, Tabs as Tabs$1, Accordion, Gallery, VideoEmbed, ContactForm, Banner, MediaText, TeamGrid, Steps, TestimonialGrid, Columns, Spacer, Container, Hero, FeatureGrid, Testimonial, CTABanner, LogoCloud, FAQ, RichTextBlock, Callout, BlockImage, AvatarBlock, Navbar, Footer, ListBlock, isAssetRef, asAssetRef } from './chunk-L6GG4KN2.js';
2
2
  export { ASSET_REF_PREFIX, Accordion, AssetResolverProvider, AvatarBlock, Banner, BlockImage, BlockLink, CTABanner, Callout, Columns, ContactForm, Container, FAQ, FONT_PAIRINGS, FeatureGrid, Footer, Gallery, Hero, ListBlock, LogoCloud, MediaText, Navbar, PricingTable, RichTextBlock, SectionHeading, Spacer, Steps, THEME_STYLE_ID, Tabs, TeamGrid, Testimonial, TestimonialGrid, ThemeEditor, VideoEmbed, applyFontVars, applyStoredTheme, applyThemeVars, asAssetRef, assetRefId, clearFontVars, clearThemeVars, createThemeStore, daysUntil, fmtDate, fmtDateTime, fontPackages, fromNow, gbp, getFontPairing, hrs, initials, isAssetRef, matchFontPairing, pct, tid, toEmbedUrl, toHslTriplet, useAssetRenderer, useAssetResolverActive } from './chunk-L6GG4KN2.js';
3
3
  import { createContext, useState, useCallback, useMemo, useContext, useRef, useEffect } from 'react';
4
- import { useArbi, ArbiProvider, useAiTask, useWorkspaceDocs as useWorkspaceDocs$1, useSemanticSearch as useSemanticSearch$1, cn, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Input, Card, CardHeader, Badge, CardTitle, CardDescription, CardContent, Avatar, AvatarImage, AvatarFallback, CardFooter, Button, TooltipProvider, NotificationBell, UserMenu, useThumbnails, Switch, useConfigs, useAgents, Checkbox, Tabs, TabsList, TabsTrigger, TabsContent, Separator, ArbiWebSocketProvider, useDocuments, ImageCropModal, DEFAULT_ASPECT_OPTIONS, useConversations } from '@arbidocs/react';
4
+ import { useArbi, ArbiProvider, useAiTask, useWorkspaceDocs as useWorkspaceDocs$1, useSemanticSearch as useSemanticSearch$1, cn, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Input, Card, CardHeader, CardTitle, CardDescription, CardContent, Tooltip, TooltipTrigger, TooltipContent, CardFooter, Avatar, AvatarImage, AvatarFallback, TooltipProvider, Button, NotificationBell, UserMenu, useThumbnails, Badge, Switch, useConfigs, useAgents, Checkbox, Tabs, TabsList, TabsTrigger, TabsContent, Separator, ArbiWebSocketProvider, useDocuments, ImageCropModal, DEFAULT_ASPECT_OPTIONS, useConversations } from '@arbidocs/react';
5
5
  export { DocumentCard, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, redlineStats, textToArtifact, toRedlineMarkdown } from '@arbidocs/react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query';
8
8
  import { AgGridReact } from 'ag-grid-react';
9
9
  import { themeQuartz, AllCommunityModule } from 'ag-grid-community';
10
- import { MessageSquare, Cpu, Code, Cloud, Layers, Target, Award, MapPin, Phone, Mail, Lock, Globe, Heart, Star, Rocket, Shield, Zap, Sparkles, Gavel, Scale, Bell, Activity, BarChart3, DollarSign, TrendingUp, Info, AlertCircle, CheckCircle, Clock, Calendar, Briefcase, Users, Folder, FileText, Loader2, UploadCloud, Search, ArrowUpRight, ArrowDownRight, FolderLock, CheckCircle2, ArrowLeftRight, X, Menu, ArrowLeft, Palette, RotateCcw, ChevronUp, ChevronDown, BrainCircuit, Bot, LayoutGrid, FileJson, Download, Crop, ImagePlus, Upload, Link2, Check, MessagesSquare } from 'lucide-react';
10
+ import { MessageSquare, Cpu, Code, Cloud, Layers, Target, Award, MapPin, Phone, Mail, Lock, Globe, Heart, Star, Rocket, Shield, Zap, Sparkles, Gavel, Scale, Bell, Activity, BarChart3, DollarSign, TrendingUp, Info, AlertCircle, CheckCircle, Clock, Calendar, Briefcase, Users, Folder, FileText, Loader2, UploadCloud, Search, ArrowUpRight, ArrowDownRight, FolderLock, CheckCircle2, ArrowLeftRight, MessagesSquare, X, Menu, ArrowLeft, Palette, RotateCcw, ChevronUp, ChevronDown, BrainCircuit, Bot, LayoutGrid, FileJson, Download, Crop, ImagePlus, Upload, Link2, Check } from 'lucide-react';
11
11
  import { useLocation, Link, useParams, NavLink } from 'react-router-dom';
12
12
  import { Puck, Render } from '@measured/puck';
13
13
  import { create } from 'zustand';
@@ -1057,83 +1057,179 @@ function MetricCard({
1057
1057
  ] })
1058
1058
  ] });
1059
1059
  }
1060
- var MAX_AVATARS = 4;
1060
+ var MAX_AVATARS = 3;
1061
+ var ON_HOVER = "opacity-0 transition-opacity group-hover/workspace-card:opacity-100 group-focus-within/workspace-card:opacity-100";
1061
1062
  function WorkspaceCard({
1062
1063
  name,
1063
1064
  matterRef,
1064
1065
  description,
1065
1066
  docCount,
1067
+ chatCount,
1066
1068
  memberCount,
1067
1069
  members = [],
1070
+ membersHidden = false,
1068
1071
  updatedAt,
1069
1072
  active = false,
1070
1073
  busy = false,
1071
1074
  onOpen,
1072
1075
  icon: Icon = FolderLock,
1076
+ iconSlot,
1077
+ tooltipEntitySlot,
1078
+ cornerSlot,
1079
+ accent = "default",
1073
1080
  testId,
1074
1081
  buttonTestId,
1075
1082
  className
1076
1083
  }) {
1077
1084
  const totalMembers = memberCount ?? members.length;
1078
- const shownMembers = members.slice(0, MAX_AVATARS);
1085
+ const shownMembers = membersHidden ? [] : members.slice(0, MAX_AVATARS);
1079
1086
  const extraMembers = totalMembers - shownMembers.length;
1080
- const btnTestId = buttonTestId ?? (testId ? `${testId}-open` : void 0);
1087
+ const actionTestId = buttonTestId ?? (testId ? `${testId}-open` : void 0);
1088
+ const canOpen = Boolean(onOpen) && !busy;
1089
+ const hue = accent && accent !== "default" ? `var(--color-${accent})` : null;
1090
+ const tileStyle = hue ? { backgroundColor: `color-mix(in oklab, ${hue} 14%, transparent)`, color: hue } : void 0;
1091
+ const openStyle = hue ? { backgroundColor: hue, color: "var(--color-background)" } : void 0;
1092
+ const handleKeyDown = (event) => {
1093
+ if (!canOpen || event.target !== event.currentTarget) return;
1094
+ if (event.key === "Enter" || event.key === " ") {
1095
+ event.preventDefault();
1096
+ onOpen?.();
1097
+ }
1098
+ };
1081
1099
  return /* @__PURE__ */ jsxs(
1082
1100
  Card,
1083
1101
  {
1084
- variant: "outline",
1085
- className: cn("card-hover flex flex-col p-5", className),
1102
+ variant: "flat",
1103
+ className: cn(
1104
+ "card-hover group/workspace-card relative flex h-48 w-full flex-col gap-3 overflow-hidden rounded-xl bg-card p-4 shadow-sm",
1105
+ canOpen && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2",
1106
+ active && "bg-primary/10 shadow-md",
1107
+ className
1108
+ ),
1086
1109
  "data-testid": testId,
1110
+ "data-active": active || void 0,
1111
+ role: canOpen ? "button" : void 0,
1112
+ tabIndex: canOpen ? 0 : void 0,
1113
+ "aria-current": active ? "page" : void 0,
1114
+ "aria-label": canOpen ? `Open workspace ${name}` : void 0,
1115
+ "aria-busy": busy || void 0,
1116
+ onClick: canOpen ? onOpen : void 0,
1117
+ onKeyDown: handleKeyDown,
1087
1118
  children: [
1088
- /* @__PURE__ */ jsxs(CardHeader, { className: "p-0", children: [
1089
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
1090
- /* @__PURE__ */ jsx("div", { className: "flex size-10 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Icon, { className: "size-5" }) }),
1091
- active ? /* @__PURE__ */ jsxs(Badge, { children: [
1092
- /* @__PURE__ */ jsx(CheckCircle2, { className: "mr-1 size-3" }),
1093
- " Active"
1094
- ] }) : /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: "Encrypted" })
1119
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center gap-3 space-y-0 p-0", children: [
1120
+ iconSlot ?? /* @__PURE__ */ jsx(
1121
+ "div",
1122
+ {
1123
+ className: "flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary",
1124
+ style: tileStyle,
1125
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-5" })
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1129
+ /* @__PURE__ */ jsx(
1130
+ CardTitle,
1131
+ {
1132
+ className: "line-clamp-2 text-base font-bold leading-tight",
1133
+ "data-testid": testId && `${testId}-title`,
1134
+ children: name
1135
+ }
1136
+ ),
1137
+ matterRef && /* @__PURE__ */ jsx(CardDescription, { className: "mt-0.5 truncate text-xs font-medium uppercase tracking-wide text-muted-foreground", children: matterRef })
1095
1138
  ] }),
1096
- /* @__PURE__ */ jsx(CardTitle, { className: "mt-3 text-base", children: name }),
1097
- matterRef && /* @__PURE__ */ jsx(CardDescription, { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: matterRef })
1139
+ cornerSlot && /* @__PURE__ */ jsx("div", { className: cn("shrink-0", ON_HOVER), children: cornerSlot })
1098
1140
  ] }),
1099
- /* @__PURE__ */ jsxs(CardContent, { className: "mt-3 flex-1 space-y-4 p-0", children: [
1100
- description && /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm text-muted-foreground", children: description }),
1101
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 text-sm", children: [
1102
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-foreground", children: [
1103
- /* @__PURE__ */ jsx(FileText, { className: "size-4 text-muted-foreground" }),
1104
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: docCount.toLocaleString() }),
1105
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "docs" })
1106
- ] }),
1107
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-foreground", children: [
1108
- /* @__PURE__ */ jsx(Users, { className: "size-4 text-muted-foreground" }),
1109
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: totalMembers }),
1110
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "members" })
1111
- ] })
1141
+ /* @__PURE__ */ jsxs(CardContent, { className: "relative flex min-h-0 flex-1 flex-col p-0", children: [
1142
+ description && /* @__PURE__ */ jsxs(Tooltip, { children: [
1143
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm text-muted-foreground transition-opacity group-hover/workspace-card:opacity-0 group-focus-within/workspace-card:opacity-0", children: description }) }),
1144
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "max-w-sm p-0", children: /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md", children: [
1145
+ /* @__PURE__ */ jsx("div", { className: "flex items-center bg-muted/70 px-3 py-2.5", children: tooltipEntitySlot ?? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: name }) }),
1146
+ /* @__PURE__ */ jsx("p", { className: "px-3 py-3 text-sm leading-relaxed text-foreground/90", children: description })
1147
+ ] }) })
1112
1148
  ] }),
1113
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
1114
- /* @__PURE__ */ jsxs("div", { className: "flex -space-x-2", children: [
1115
- shownMembers.map((m, i) => /* @__PURE__ */ jsxs(Avatar, { className: "size-7 border-2 border-card", title: m.name, children: [
1116
- m.picture && /* @__PURE__ */ jsx(AvatarImage, { src: m.picture, alt: m.name }),
1117
- /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-foreground text-[10px] font-medium text-background", children: initials(m.name || "?") })
1118
- ] }, i)),
1119
- extraMembers > 0 && /* @__PURE__ */ jsx(Avatar, { className: "size-7 border-2 border-card", children: /* @__PURE__ */ jsxs(AvatarFallback, { className: "bg-accent text-[10px] font-medium text-muted-foreground", children: [
1120
- "+",
1121
- extraMembers
1122
- ] }) })
1123
- ] }),
1124
- updatedAt && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", title: fmtDate(updatedAt), children: [
1125
- "Updated ",
1126
- fromNow(updatedAt)
1127
- ] })
1128
- ] })
1149
+ /* @__PURE__ */ jsx(
1150
+ "span",
1151
+ {
1152
+ className: cn(
1153
+ "pointer-events-none absolute inset-0 z-10 inline-flex items-center justify-center whitespace-nowrap",
1154
+ !busy && "opacity-0 transition-opacity group-hover/workspace-card:opacity-100 group-focus-within/workspace-card:opacity-100"
1155
+ ),
1156
+ "data-testid": actionTestId,
1157
+ children: /* @__PURE__ */ jsx(
1158
+ "span",
1159
+ {
1160
+ className: cn(
1161
+ "inline-flex w-full items-center justify-center gap-2 rounded-md px-4 py-2 text-base font-semibold shadow-md",
1162
+ active ? "bg-muted-foreground text-background" : "bg-primary text-primary-foreground"
1163
+ ),
1164
+ style: active ? void 0 : openStyle,
1165
+ children: active ? /* @__PURE__ */ jsxs(Fragment, { children: [
1166
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4" }),
1167
+ " Current workspace"
1168
+ ] }) : busy ? /* @__PURE__ */ jsxs(Fragment, { children: [
1169
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
1170
+ " Opening"
1171
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1172
+ /* @__PURE__ */ jsx(ArrowLeftRight, { className: "size-4" }),
1173
+ " Open"
1174
+ ] })
1175
+ }
1176
+ )
1177
+ }
1178
+ )
1129
1179
  ] }),
1130
- /* @__PURE__ */ jsx(CardFooter, { className: "mt-4 p-0", children: active ? /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "w-full", disabled: true, "data-testid": btnTestId, children: [
1131
- /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4" }),
1132
- " Current workspace"
1133
- ] }) : /* @__PURE__ */ jsxs(Button, { className: "w-full", onClick: onOpen, disabled: busy, "data-testid": btnTestId, children: [
1134
- busy ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(ArrowLeftRight, { className: "size-4" }),
1135
- "Open"
1136
- ] }) })
1180
+ /* @__PURE__ */ jsxs(CardFooter, { className: "mt-auto flex w-full min-w-0 flex-col items-stretch gap-3 overflow-hidden p-0", children: [
1181
+ /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-x-3 text-sm", children: [
1182
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-3", children: [
1183
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-foreground", title: "Documents", children: [
1184
+ /* @__PURE__ */ jsx(FileText, { className: "size-3.5 text-muted-foreground" }),
1185
+ /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: docCount.toLocaleString() })
1186
+ ] }),
1187
+ chatCount !== void 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-foreground", title: "Chats", children: [
1188
+ /* @__PURE__ */ jsx(MessagesSquare, { className: "size-3.5 text-muted-foreground" }),
1189
+ /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: chatCount.toLocaleString() })
1190
+ ] }),
1191
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-foreground", title: "Members", children: [
1192
+ /* @__PURE__ */ jsx(Users, { className: "size-3.5 text-muted-foreground" }),
1193
+ /* @__PURE__ */ jsx(
1194
+ "span",
1195
+ {
1196
+ className: "font-medium tabular-nums",
1197
+ "data-testid": testId && `${testId}-member-count`,
1198
+ children: membersHidden ? "\u2014" : totalMembers
1199
+ }
1200
+ )
1201
+ ] })
1202
+ ] }),
1203
+ /* @__PURE__ */ jsxs(
1204
+ "div",
1205
+ {
1206
+ className: "ml-auto flex shrink-0 -space-x-2",
1207
+ "data-testid": testId && `${testId}-member-avatars`,
1208
+ children: [
1209
+ shownMembers.map((m, i) => /* @__PURE__ */ jsxs(Avatar, { className: "size-6 border-2 border-card", title: m.name, children: [
1210
+ m.picture && /* @__PURE__ */ jsx(AvatarImage, { src: m.picture, alt: m.name }),
1211
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-foreground text-[9px] font-medium text-background", children: initials(m.name || "?") })
1212
+ ] }, i)),
1213
+ !membersHidden && extraMembers > 0 && /* @__PURE__ */ jsx(Avatar, { className: "size-6 border-2 border-card", children: /* @__PURE__ */ jsxs(AvatarFallback, { className: "bg-accent text-[9px] font-medium text-muted-foreground", children: [
1214
+ "+",
1215
+ extraMembers
1216
+ ] }) })
1217
+ ]
1218
+ }
1219
+ )
1220
+ ] }),
1221
+ updatedAt && /* @__PURE__ */ jsxs(
1222
+ "span",
1223
+ {
1224
+ className: "min-w-0 max-w-full self-start truncate text-left text-xs text-muted-foreground",
1225
+ title: fmtDate(updatedAt),
1226
+ children: [
1227
+ "Updated ",
1228
+ fromNow(updatedAt)
1229
+ ]
1230
+ }
1231
+ )
1232
+ ] })
1137
1233
  ]
1138
1234
  }
1139
1235
  );