@handled-ai/design-system 0.20.12 → 0.20.13

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.
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const badgeVariants: (props?: ({
6
- variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
9
9
  asChild?: boolean;
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
@@ -34,84 +34,64 @@ function CommentComposer({
34
34
  "data-slot": "comment-composer",
35
35
  "data-open": open ? "true" : void 0,
36
36
  className: cn(
37
- "flex items-start gap-4 rounded-xl transition-colors",
37
+ "border-border bg-background flex items-start gap-2 rounded-lg border px-2 py-1.5 transition-colors",
38
+ open && "ring-ring/30 ring-2",
38
39
  className
39
40
  ),
40
41
  children: [
41
- /* @__PURE__ */ jsxs(Avatar, { size: "sm", className: "mt-1", children: [
42
+ /* @__PURE__ */ jsxs(Avatar, { size: "sm", className: "mt-px", children: [
42
43
  (author == null ? void 0 : author.avatarUrl) ? /* @__PURE__ */ jsx(AvatarImage, { src: author.avatarUrl, alt: (_a = author.name) != null ? _a : "You" }) : null,
43
- /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-slate-700 text-[10px] font-semibold uppercase text-white dark:bg-slate-200 dark:text-slate-900", children: getInitials({ name: author == null ? void 0 : author.name, email: author == null ? void 0 : author.email }) })
44
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-muted text-muted-foreground text-[10px] font-medium uppercase", children: getInitials({ name: author == null ? void 0 : author.name, email: author == null ? void 0 : author.email }) })
44
45
  ] }),
45
- /* @__PURE__ */ jsxs(
46
- "div",
47
- {
48
- "data-slot": "comment-composer-shell",
49
- className: cn(
50
- "min-w-0 flex-1 rounded-xl border border-border bg-background transition-[box-shadow,border-color]",
51
- open ? "overflow-hidden shadow-sm" : "shadow-none"
52
- ),
53
- children: [
46
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
47
+ /* @__PURE__ */ jsx(
48
+ Textarea,
49
+ {
50
+ "data-slot": "comment-composer-input",
51
+ value: text,
52
+ onChange: (e) => setText(e.target.value),
53
+ onFocus: () => setFocused(true),
54
+ placeholder,
55
+ rows: open ? 3 : 1,
56
+ className: cn(
57
+ "resize-none border-0 bg-transparent px-1 py-0.5 text-sm leading-snug shadow-none focus-visible:ring-0",
58
+ !open && "min-h-0"
59
+ ),
60
+ onKeyDown: (e) => {
61
+ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
62
+ e.preventDefault();
63
+ post();
64
+ }
65
+ }
66
+ }
67
+ ),
68
+ open ? /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2", children: [
69
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground inline-flex items-center gap-1 text-[11px]", children: [
70
+ /* @__PURE__ */ jsx(Lock, { size: 12 }),
71
+ " ",
72
+ hint
73
+ ] }),
74
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
54
75
  /* @__PURE__ */ jsx(
55
- Textarea,
76
+ Button,
56
77
  {
57
- "data-slot": "comment-composer-input",
58
- value: text,
59
- onChange: (e) => setText(e.target.value),
60
- onFocus: () => setFocused(true),
61
- placeholder,
62
- rows: open ? 4 : 1,
63
- className: cn(
64
- "resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0",
65
- open ? "min-h-32" : "min-h-14"
66
- ),
67
- onKeyDown: (e) => {
68
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
69
- e.preventDefault();
70
- post();
71
- }
72
- }
78
+ type: "button",
79
+ variant: "ghost",
80
+ size: "sm",
81
+ onClick: () => {
82
+ setText("");
83
+ setFocused(false);
84
+ },
85
+ children: "Cancel"
73
86
  }
74
87
  ),
75
- open ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4", children: [
76
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [
77
- /* @__PURE__ */ jsx(Lock, { size: 16, strokeWidth: 1.75 }),
78
- " ",
79
- hint
80
- ] }),
81
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-3", children: [
82
- /* @__PURE__ */ jsx(
83
- Button,
84
- {
85
- type: "button",
86
- variant: "ghost",
87
- size: "sm",
88
- className: "px-2 text-sm font-medium text-muted-foreground hover:bg-transparent hover:text-foreground",
89
- onClick: () => {
90
- setText("");
91
- setFocused(false);
92
- },
93
- children: "Cancel"
94
- }
95
- ),
96
- /* @__PURE__ */ jsxs(
97
- Button,
98
- {
99
- type: "button",
100
- size: "sm",
101
- disabled: !canPost,
102
- onClick: post,
103
- className: "rounded-lg bg-foreground px-4 text-sm font-semibold text-background shadow-none hover:bg-foreground/90",
104
- children: [
105
- "Comment",
106
- /* @__PURE__ */ jsx("kbd", { className: "ml-1 rounded px-1 text-[10px] text-background/70", children: "\u2318\u21B5" })
107
- ]
108
- }
109
- )
110
- ] })
111
- ] }) : null
112
- ]
113
- }
114
- )
88
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", disabled: !canPost, onClick: post, children: [
89
+ "Comment",
90
+ /* @__PURE__ */ jsx("kbd", { className: "bg-primary-foreground/15 ml-1 rounded px-1 text-[10px]", children: "\u2318\u21B5" })
91
+ ] })
92
+ ] })
93
+ ] }) : null
94
+ ] })
115
95
  ]
116
96
  }
117
97
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/comment-composer.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * comment-composer.tsx — an internal-note composer for the case activity\n * timeline. Posting a comment prepends an `operatorNote` event to the log\n * (wired by the consumer). Collapses to a single line; expands on focus or\n * when it has text. ⌘↵ / Ctrl↵ posts.\n *\n * Presentational: `onPost` does the work (the consumer persists the note and\n * adds it to the timeline). Reuses Avatar / Button / Textarea primitives.\n */\n\nimport * as React from \"react\"\nimport { Lock } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Textarea } from \"./textarea\"\n\nexport interface CommentComposerProps {\n /** Called with the trimmed note text when the operator posts. */\n onPost: (text: string) => void\n /** Current operator (for the avatar). */\n author?: { name?: string; email?: string; avatarUrl?: string | null }\n placeholder?: string\n /** Hint shown in the footer; defaults to the internal-note reassurance. */\n hint?: string\n className?: string\n}\n\nfunction CommentComposer({\n onPost,\n author,\n placeholder = \"Add a comment or internal note…\",\n hint = \"Internal note: only your team sees this\",\n className,\n}: CommentComposerProps) {\n const [text, setText] = React.useState(\"\")\n const [focused, setFocused] = React.useState(false)\n const open = focused || text.length > 0\n const canPost = text.trim().length > 0\n\n const post = () => {\n const value = text.trim()\n if (!value) return\n onPost(value)\n setText(\"\")\n setFocused(false)\n }\n\n return (\n <div\n data-slot=\"comment-composer\"\n data-open={open ? \"true\" : undefined}\n className={cn(\n \"flex items-start gap-4 rounded-xl transition-colors\",\n className\n )}\n >\n <Avatar size=\"sm\" className=\"mt-1\">\n {author?.avatarUrl ? <AvatarImage src={author.avatarUrl} alt={author.name ?? \"You\"} /> : null}\n <AvatarFallback className=\"bg-slate-700 text-[10px] font-semibold uppercase text-white dark:bg-slate-200 dark:text-slate-900\">\n {getInitials({ name: author?.name, email: author?.email })}\n </AvatarFallback>\n </Avatar>\n\n <div\n data-slot=\"comment-composer-shell\"\n className={cn(\n \"min-w-0 flex-1 rounded-xl border border-border bg-background transition-[box-shadow,border-color]\",\n open ? \"overflow-hidden shadow-sm\" : \"shadow-none\"\n )}\n >\n <Textarea\n data-slot=\"comment-composer-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n onFocus={() => setFocused(true)}\n placeholder={placeholder}\n rows={open ? 4 : 1}\n className={cn(\n \"resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0\",\n open ? \"min-h-32\" : \"min-h-14\"\n )}\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault()\n post()\n }\n }}\n />\n\n {open ? (\n <div className=\"flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4\">\n <span className=\"inline-flex items-center gap-2 text-sm text-muted-foreground\">\n <Lock size={16} strokeWidth={1.75} /> {hint}\n </span>\n <span className=\"flex items-center gap-3\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-sm font-medium text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => {\n setText(\"\")\n setFocused(false)\n }}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={!canPost}\n onClick={post}\n className=\"rounded-lg bg-foreground px-4 text-sm font-semibold text-background shadow-none hover:bg-foreground/90\"\n >\n Comment\n <kbd className=\"ml-1 rounded px-1 text-[10px] text-background/70\">⌘↵</kbd>\n </Button>\n </span>\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport { CommentComposer }\n"],"mappings":";AA6DM,SACuB,KADvB;AAjDN,YAAY,WAAW;AACvB,SAAS,YAAY;AAErB,SAAS,UAAU;AACnB,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAazB,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AACF,GAAyB;AAtCzB;AAuCE,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,EAAE;AACzC,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,OAAO,WAAW,KAAK,SAAS;AACtC,QAAM,UAAU,KAAK,KAAK,EAAE,SAAS;AAErC,QAAM,OAAO,MAAM;AACjB,UAAM,QAAQ,KAAK,KAAK;AACxB,QAAI,CAAC,MAAO;AACZ,WAAO,KAAK;AACZ,YAAQ,EAAE;AACV,eAAW,KAAK;AAAA,EAClB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,aAAW,OAAO,SAAS;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,UAAO,MAAK,MAAK,WAAU,QACzB;AAAA,4CAAQ,aAAY,oBAAC,eAAY,KAAK,OAAO,WAAW,MAAK,YAAO,SAAP,YAAe,OAAO,IAAK;AAAA,UACzF,oBAAC,kBAAe,WAAU,qGACvB,sBAAY,EAAE,MAAM,iCAAQ,MAAM,OAAO,iCAAQ,MAAM,CAAC,GAC3D;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,OAAO,8BAA8B;AAAA,YACvC;AAAA,YAEA;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,kBACvC,SAAS,MAAM,WAAW,IAAI;AAAA,kBAC9B;AAAA,kBACA,MAAM,OAAO,IAAI;AAAA,kBACjB,WAAW;AAAA,oBACT;AAAA,oBACA,OAAO,aAAa;AAAA,kBACtB;AAAA,kBACA,WAAW,CAAC,MAAM;AAChB,yBAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAS;AACjD,wBAAE,eAAe;AACjB,2BAAK;AAAA,oBACP;AAAA,kBACF;AAAA;AAAA,cACF;AAAA,cAEC,OACC,qBAAC,SAAI,WAAU,wFACb;AAAA,qCAAC,UAAK,WAAU,gEACd;AAAA,sCAAC,QAAK,MAAM,IAAI,aAAa,MAAM;AAAA,kBAAE;AAAA,kBAAE;AAAA,mBACzC;AAAA,gBACA,qBAAC,UAAK,WAAU,2BACd;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,SAAS,MAAM;AACb,gCAAQ,EAAE;AACV,mCAAW,KAAK;AAAA,sBAClB;AAAA,sBACD;AAAA;AAAA,kBAED;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,MAAK;AAAA,sBACL,UAAU,CAAC;AAAA,sBACX,SAAS;AAAA,sBACT,WAAU;AAAA,sBACX;AAAA;AAAA,wBAEC,oBAAC,SAAI,WAAU,oDAAmD,0BAAE;AAAA;AAAA;AAAA,kBACtE;AAAA,mBACF;AAAA,iBACF,IACE;AAAA;AAAA;AAAA,QACN;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/comment-composer.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * comment-composer.tsx — an internal-note composer for the case activity\n * timeline. Posting a comment prepends an `operatorNote` event to the log\n * (wired by the consumer). Collapses to a single line; expands on focus or\n * when it has text. ⌘↵ / Ctrl↵ posts.\n *\n * Presentational: `onPost` does the work (the consumer persists the note and\n * adds it to the timeline). Reuses Avatar / Button / Textarea primitives.\n */\n\nimport * as React from \"react\"\nimport { Lock } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Textarea } from \"./textarea\"\n\nexport interface CommentComposerProps {\n /** Called with the trimmed note text when the operator posts. */\n onPost: (text: string) => void\n /** Current operator (for the avatar). */\n author?: { name?: string; email?: string; avatarUrl?: string | null }\n placeholder?: string\n /** Hint shown in the footer; defaults to the internal-note reassurance. */\n hint?: string\n className?: string\n}\n\nfunction CommentComposer({\n onPost,\n author,\n placeholder = \"Add a comment or internal note…\",\n hint = \"Internal note: only your team sees this\",\n className,\n}: CommentComposerProps) {\n const [text, setText] = React.useState(\"\")\n const [focused, setFocused] = React.useState(false)\n const open = focused || text.length > 0\n const canPost = text.trim().length > 0\n\n const post = () => {\n const value = text.trim()\n if (!value) return\n onPost(value)\n setText(\"\")\n setFocused(false)\n }\n\n return (\n <div\n data-slot=\"comment-composer\"\n data-open={open ? \"true\" : undefined}\n className={cn(\n \"border-border bg-background flex items-start gap-2 rounded-lg border px-2 py-1.5 transition-colors\",\n open && \"ring-ring/30 ring-2\",\n className\n )}\n >\n <Avatar size=\"sm\" className=\"mt-px\">\n {author?.avatarUrl ? <AvatarImage src={author.avatarUrl} alt={author.name ?? \"You\"} /> : null}\n <AvatarFallback className=\"bg-muted text-muted-foreground text-[10px] font-medium uppercase\">\n {getInitials({ name: author?.name, email: author?.email })}\n </AvatarFallback>\n </Avatar>\n\n <div className=\"min-w-0 flex-1\">\n <Textarea\n data-slot=\"comment-composer-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n onFocus={() => setFocused(true)}\n placeholder={placeholder}\n rows={open ? 3 : 1}\n className={cn(\n \"resize-none border-0 bg-transparent px-1 py-0.5 text-sm leading-snug shadow-none focus-visible:ring-0\",\n !open && \"min-h-0\"\n )}\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault()\n post()\n }\n }}\n />\n\n {open ? (\n <div className=\"mt-0.5 flex items-center justify-between gap-2\">\n <span className=\"text-muted-foreground inline-flex items-center gap-1 text-[11px]\">\n <Lock size={12} /> {hint}\n </span>\n <span className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setText(\"\")\n setFocused(false)\n }}\n >\n Cancel\n </Button>\n <Button type=\"button\" size=\"sm\" disabled={!canPost} onClick={post}>\n Comment\n <kbd className=\"bg-primary-foreground/15 ml-1 rounded px-1 text-[10px]\">⌘↵</kbd>\n </Button>\n </span>\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport { CommentComposer }\n"],"mappings":";AA8DM,SACuB,KADvB;AAlDN,YAAY,WAAW;AACvB,SAAS,YAAY;AAErB,SAAS,UAAU;AACnB,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAazB,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AACF,GAAyB;AAtCzB;AAuCE,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,EAAE;AACzC,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,OAAO,WAAW,KAAK,SAAS;AACtC,QAAM,UAAU,KAAK,KAAK,EAAE,SAAS;AAErC,QAAM,OAAO,MAAM;AACjB,UAAM,QAAQ,KAAK,KAAK;AACxB,QAAI,CAAC,MAAO;AACZ,WAAO,KAAK;AACZ,YAAQ,EAAE;AACV,eAAW,KAAK;AAAA,EAClB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,aAAW,OAAO,SAAS;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,UAAO,MAAK,MAAK,WAAU,SACzB;AAAA,4CAAQ,aAAY,oBAAC,eAAY,KAAK,OAAO,WAAW,MAAK,YAAO,SAAP,YAAe,OAAO,IAAK;AAAA,UACzF,oBAAC,kBAAe,WAAU,oEACvB,sBAAY,EAAE,MAAM,iCAAQ,MAAM,OAAO,iCAAQ,MAAM,CAAC,GAC3D;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,kBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,aAAU;AAAA,cACV,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,cACvC,SAAS,MAAM,WAAW,IAAI;AAAA,cAC9B;AAAA,cACA,MAAM,OAAO,IAAI;AAAA,cACjB,WAAW;AAAA,gBACT;AAAA,gBACA,CAAC,QAAQ;AAAA,cACX;AAAA,cACA,WAAW,CAAC,MAAM;AAChB,qBAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAS;AACjD,oBAAE,eAAe;AACjB,uBAAK;AAAA,gBACP;AAAA,cACF;AAAA;AAAA,UACF;AAAA,UAEC,OACC,qBAAC,SAAI,WAAU,kDACb;AAAA,iCAAC,UAAK,WAAU,oEACd;AAAA,kCAAC,QAAK,MAAM,IAAI;AAAA,cAAE;AAAA,cAAE;AAAA,eACtB;AAAA,YACA,qBAAC,UAAK,WAAU,2BACd;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM;AACb,4BAAQ,EAAE;AACV,+BAAW,KAAK;AAAA,kBAClB;AAAA,kBACD;AAAA;AAAA,cAED;AAAA,cACA,qBAAC,UAAO,MAAK,UAAS,MAAK,MAAK,UAAU,CAAC,SAAS,SAAS,MAAM;AAAA;AAAA,gBAEjE,oBAAC,SAAI,WAAU,0DAAyD,0BAAE;AAAA,iBAC5E;AAAA,eACF;AAAA,aACF,IACE;AAAA,WACN;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -145,17 +145,30 @@ function PersonAvatar({ person, size = "sm" }) {
145
145
  ] });
146
146
  }
147
147
  const STATUS_PILL = {
148
- responded: { label: "New reply", cls: "bg-status-active-bg text-status-active-fg border-status-active-border" },
148
+ responded: { label: "NEW REPLY", cls: "bg-status-warning-bg text-status-warning-fg border-status-warning-border" },
149
149
  draft: { label: "Draft", cls: "bg-background text-foreground/80 border-border" },
150
- awaiting: { label: "Awaiting", cls: "bg-status-pending-bg text-status-pending-fg border-status-pending-border" },
150
+ awaiting: { label: "SENT", cls: "bg-status-info-bg text-status-info-fg border-status-info-border" },
151
151
  viewing: { label: "Viewing", cls: "bg-muted text-muted-foreground border-border" }
152
152
  };
153
153
  const STATUS_DOT = {
154
- responded: "bg-status-active-fg",
154
+ responded: "bg-status-warning-fg",
155
155
  draft: "bg-status-pending-fg",
156
- awaiting: "bg-status-pending-fg",
156
+ awaiting: "bg-status-info-fg",
157
157
  viewing: "bg-muted-foreground/50"
158
158
  };
159
+ const THREAD_ROW_ACCENT = {
160
+ responded: "border-l-4 border-l-status-warning-border bg-status-warning-bg/25",
161
+ awaiting: "border-l-4 border-l-status-info-border bg-status-info-bg/25",
162
+ draft: "border-l-4 border-l-status-pending-border bg-status-pending-bg/20",
163
+ viewing: ""
164
+ };
165
+ const RECEIPT_CHIP = {
166
+ new: "border-status-warning-border bg-status-warning-bg text-status-warning-fg",
167
+ read: "border-status-info-border bg-status-info-bg text-status-info-fg",
168
+ opened: "border-status-info-border bg-status-info-bg text-status-info-fg",
169
+ sent: "border-status-info-border bg-status-info-bg text-status-info-fg",
170
+ draft: "border-status-pending-border bg-status-pending-bg text-status-pending-fg"
171
+ };
159
172
  function effectiveStatus(t) {
160
173
  return t.canReply === false ? "viewing" : t.status;
161
174
  }
@@ -258,8 +271,8 @@ function MessageView({
258
271
  ] })
259
272
  ] }),
260
273
  /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2", children: [
261
- message.receipt ? /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground inline-flex items-center gap-1 text-[11px]", children: [
262
- message.receipt.kind === "new" ? /* @__PURE__ */ jsx(CornerUpLeft, { size: 11 }) : message.receipt.kind === "read" ? /* @__PURE__ */ jsx(CheckCheck, { size: 11 }) : message.receipt.kind === "draft" ? /* @__PURE__ */ jsx(FilePenLine, { size: 11 }) : /* @__PURE__ */ jsx(MailOpen, { size: 11 }),
274
+ message.receipt ? /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center gap-1 rounded-md border px-1.5 py-px text-[10px] font-semibold leading-4", RECEIPT_CHIP[message.receipt.kind]), children: [
275
+ message.receipt.kind === "new" ? /* @__PURE__ */ jsx(CornerUpLeft, { size: 11 }) : message.receipt.kind === "read" || message.receipt.kind === "sent" ? /* @__PURE__ */ jsx(CheckCheck, { size: 11 }) : message.receipt.kind === "draft" ? /* @__PURE__ */ jsx(FilePenLine, { size: 11 }) : /* @__PURE__ */ jsx(MailOpen, { size: 11 }),
263
276
  message.receipt.label
264
277
  ] }) : null,
265
278
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 text-xs", children: message.date }),
@@ -582,13 +595,13 @@ function ThreadBody({
582
595
  }, [sortedMessages]);
583
596
  const toggle = (id) => setExpanded((e) => __spreadProps(__spreadValues({}, e), { [id]: !e[id] }));
584
597
  return /* @__PURE__ */ jsxs("div", { "data-slot": "conv-thread-body", className: "space-y-2", children: [
585
- canReply && thread.paused ? /* @__PURE__ */ jsxs("div", { className: "border-status-pending-border bg-status-pending-bg text-status-pending-fg flex items-start gap-2 rounded-md border p-2.5 text-[12px]", children: [
598
+ canReply && thread.paused ? /* @__PURE__ */ jsxs("div", { className: "border-status-warning-border bg-status-warning-bg text-status-warning-fg flex items-start gap-2 rounded-md border border-l-4 p-2.5 text-[12px]", children: [
586
599
  /* @__PURE__ */ jsx(Pause, { size: 13, className: "mt-0.5 shrink-0" }),
587
600
  /* @__PURE__ */ jsxs("span", { children: [
588
- /* @__PURE__ */ jsx("b", { children: "Follow-up actions stopped." }),
589
- " Your ",
601
+ /* @__PURE__ */ jsx("b", { children: "Playbook stopped." }),
602
+ " Follow-up actions for ",
590
603
  thread.paused.playbook,
591
- " next steps won\u2019t send automatically while this conversation is live. Continue it in ",
604
+ " won\u2019t send automatically while this conversation is live. Continue it in ",
592
605
  tenantName != null ? tenantName : "the app",
593
606
  " or Gmail."
594
607
  ] })
@@ -694,47 +707,56 @@ function ThreadRow({
694
707
  const who = (last == null ? void 0 : last.direction) === "outbound" && sameEmail(lastSender == null ? void 0 : lastSender.email, meDisplay == null ? void 0 : meDisplay.email) ? "You" : firstName((_a = lastSender == null ? void 0 : lastSender.name) != null ? _a : "");
695
708
  const lastSnippet = last ? messageBodySnippet(last, 120) : "";
696
709
  const pill = STATUS_PILL[status];
697
- return /* @__PURE__ */ jsxs("div", { "data-slot": "conv-thread", "data-open": open ? "true" : void 0, className: "border-border border-b last:border-b-0", children: [
698
- /* @__PURE__ */ jsxs(
699
- "button",
700
- {
701
- type: "button",
702
- onClick: onToggleOpen,
703
- "aria-expanded": open,
704
- className: "flex w-full items-center gap-2.5 px-3 py-2.5 text-left hover:bg-muted/30",
705
- children: [
706
- /* @__PURE__ */ jsx("span", { className: cn("size-2 shrink-0 rounded-full", STATUS_DOT[status]), "aria-hidden": true }),
707
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
708
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
709
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: thread.subject }),
710
- /* @__PURE__ */ jsx("span", { className: cn("shrink-0 rounded-md border px-1.5 py-px text-[10px] font-medium leading-4", pill.cls), children: pill.label })
711
- ] }),
712
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground block truncate text-xs", children: [
713
- /* @__PURE__ */ jsx("b", { className: "text-foreground/80", children: displayParticipant(thread.contact).name }),
714
- " \xB7 ",
715
- who,
716
- ": ",
717
- lastSnippet
718
- ] })
719
- ] }),
720
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 shrink-0 text-xs", children: thread.lastWhen }),
721
- open ? /* @__PURE__ */ jsx(ChevronUp, { size: 15, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 15, className: "text-muted-foreground shrink-0" })
722
- ]
723
- }
724
- ),
725
- open ? /* @__PURE__ */ jsx("div", { className: "px-3 pb-3", children: /* @__PURE__ */ jsx(
726
- ThreadBody,
727
- {
728
- thread,
729
- me,
730
- tenantName,
731
- onSendReply,
732
- onCreateGmailDraft,
733
- onPreviewReply,
734
- onOpenInGmail
735
- }
736
- ) }) : null
737
- ] });
710
+ return /* @__PURE__ */ jsxs(
711
+ "div",
712
+ {
713
+ "data-slot": "conv-thread",
714
+ "data-status": status,
715
+ "data-open": open ? "true" : void 0,
716
+ className: cn("border-border border-b last:border-b-0", THREAD_ROW_ACCENT[status]),
717
+ children: [
718
+ /* @__PURE__ */ jsxs(
719
+ "button",
720
+ {
721
+ type: "button",
722
+ onClick: onToggleOpen,
723
+ "aria-expanded": open,
724
+ className: "flex w-full items-center gap-2.5 px-3 py-2.5 text-left hover:bg-muted/30",
725
+ children: [
726
+ /* @__PURE__ */ jsx("span", { className: cn("size-2 shrink-0 rounded-full", STATUS_DOT[status]), "aria-hidden": true }),
727
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
728
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
729
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: thread.subject }),
730
+ /* @__PURE__ */ jsx("span", { className: cn("shrink-0 rounded-md border px-1.5 py-px text-[10px] font-medium leading-4", pill.cls), children: pill.label })
731
+ ] }),
732
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground block truncate text-xs", children: [
733
+ /* @__PURE__ */ jsx("b", { className: "text-foreground/80", children: displayParticipant(thread.contact).name }),
734
+ " \xB7 ",
735
+ who,
736
+ ": ",
737
+ lastSnippet
738
+ ] })
739
+ ] }),
740
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 shrink-0 text-xs", children: thread.lastWhen }),
741
+ open ? /* @__PURE__ */ jsx(ChevronUp, { size: 15, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 15, className: "text-muted-foreground shrink-0" })
742
+ ]
743
+ }
744
+ ),
745
+ open ? /* @__PURE__ */ jsx("div", { className: "px-3 pb-3", children: /* @__PURE__ */ jsx(
746
+ ThreadBody,
747
+ {
748
+ thread,
749
+ me,
750
+ tenantName,
751
+ onSendReply,
752
+ onCreateGmailDraft,
753
+ onPreviewReply,
754
+ onOpenInGmail
755
+ }
756
+ ) }) : null
757
+ ]
758
+ }
759
+ );
738
760
  }
739
761
  function ConversationPanel({
740
762
  threads,
@@ -751,61 +773,82 @@ function ConversationPanel({
751
773
  const draft = threads.filter((t) => effectiveStatus(t) === "draft").length;
752
774
  const awaiting = threads.filter((t) => effectiveStatus(t) === "awaiting").length;
753
775
  const anyPaused = threads.some((t) => t.paused);
776
+ const hubGmailThread = threads.find((t) => canOpenInGmail(t, onOpenInGmail));
777
+ const firstAwaiting = threads.find((t) => effectiveStatus(t) === "awaiting");
754
778
  const [hubOpen, setHubOpen] = React.useState(true);
755
779
  const [openId, setOpenId] = React.useState(() => {
756
780
  if (defaultOpenThreadId) return defaultOpenThreadId;
757
- const firstActionable = threads.find((t) => ["responded", "draft"].includes(t.status) && t.canReply !== false);
781
+ const firstActionable = threads.find((t) => ["responded", "draft", "awaiting"].includes(t.status) && t.canReply !== false);
758
782
  return firstActionable ? firstActionable.threadId : null;
759
783
  });
760
784
  if (!threads.length) return null;
761
- const badge = responded > 0 ? { label: "Email response detected", dot: "bg-status-active-fg", ring: "bg-status-active-fg/30" } : draft > 0 ? { label: "Draft ready", dot: "bg-status-pending-fg", ring: "bg-status-pending-fg/30" } : awaiting > 0 ? { label: "Awaiting response", dot: "bg-status-pending-fg", ring: "bg-status-pending-fg/30" } : { label: "Conversations", dot: "bg-muted-foreground/50", ring: "bg-muted-foreground/20" };
762
- const headTitle = responded > 0 ? `${responded} ${responded === 1 ? "reply needs" : "replies need"} your response` : draft > 0 ? `Draft ready on ${draft} ${draft === 1 ? "thread" : "threads"}` : awaiting > 0 ? `Awaiting response on ${awaiting} ${awaiting === 1 ? "thread" : "threads"}` : `${threads.length} email ${threads.length === 1 ? "thread" : "threads"}`;
785
+ const badge = responded > 0 ? { label: "Email response detected", dot: "bg-status-warning-fg", ring: "bg-status-warning-fg/30" } : draft > 0 ? { label: "Draft ready", dot: "bg-status-pending-fg", ring: "bg-status-pending-fg/30" } : awaiting > 0 ? { label: "Email sent \xB7 awaiting reply", dot: "bg-status-info-fg", ring: "bg-status-info-fg/30" } : { label: "Conversations", dot: "bg-muted-foreground/50", ring: "bg-muted-foreground/20" };
786
+ const headTitle = responded > 0 ? `${responded} ${responded === 1 ? "reply needs" : "replies need"} your response` : draft > 0 ? `Draft ready on ${draft} ${draft === 1 ? "thread" : "threads"}` : awaiting > 0 ? awaiting === 1 && firstAwaiting ? `Awaiting a response from ${firstName(displayParticipant(firstAwaiting.contact).name)}` : `Awaiting responses on ${awaiting} threads` : `${threads.length} email ${threads.length === 1 ? "thread" : "threads"}`;
787
+ const panelState = responded > 0 ? "responded" : draft > 0 ? "draft" : awaiting > 0 ? "awaiting" : "viewing";
763
788
  return /* @__PURE__ */ jsxs(
764
789
  "section",
765
790
  {
766
791
  "data-slot": "conversation-panel",
767
792
  "data-responded": responded > 0 ? "true" : void 0,
768
- className: cn("border-border bg-background overflow-hidden rounded-xl border", className),
793
+ "data-state": panelState,
794
+ className: cn(
795
+ "bg-background overflow-hidden rounded-xl border",
796
+ panelState === "responded" ? "border-status-warning-border" : panelState === "awaiting" ? "border-status-info-border" : "border-border",
797
+ className
798
+ ),
769
799
  children: [
770
800
  /* @__PURE__ */ jsxs(
771
- "button",
801
+ "div",
772
802
  {
773
- type: "button",
774
- onClick: () => setHubOpen((v) => !v),
775
- "aria-expanded": hubOpen,
776
- className: "flex w-full items-center gap-3 px-3 py-2.5 text-left",
803
+ "data-slot": "conversation-panel-header",
804
+ className: cn(
805
+ "flex w-full items-center gap-2 px-3 py-2.5",
806
+ panelState === "responded" ? "bg-status-warning-bg/45" : panelState === "awaiting" ? "bg-status-info-bg/55" : "bg-background"
807
+ ),
777
808
  children: [
778
809
  /* @__PURE__ */ jsxs(
779
- "span",
810
+ "button",
780
811
  {
781
- "data-slot": "conversation-badge",
782
- className: cn(
783
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-semibold",
784
- responded > 0 ? "bg-status-active-bg text-status-active-fg" : draft > 0 || awaiting > 0 ? "bg-status-pending-bg text-status-pending-fg" : "bg-muted text-muted-foreground"
785
- ),
812
+ type: "button",
813
+ onClick: () => setHubOpen((v) => !v),
814
+ "aria-expanded": hubOpen,
815
+ className: "flex min-w-0 flex-1 items-center gap-3 text-left",
786
816
  children: [
787
- /* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-2", children: [
788
- /* @__PURE__ */ jsx("span", { className: cn("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", badge.ring) }),
789
- /* @__PURE__ */ jsx("span", { className: cn("relative inline-flex size-2 rounded-full", badge.dot) })
817
+ /* @__PURE__ */ jsxs(
818
+ "span",
819
+ {
820
+ "data-slot": "conversation-badge",
821
+ className: cn(
822
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-semibold",
823
+ responded > 0 ? "bg-status-warning-bg text-status-warning-fg" : awaiting > 0 ? "bg-status-info-bg text-status-info-fg" : draft > 0 ? "bg-status-pending-bg text-status-pending-fg" : "bg-muted text-muted-foreground"
824
+ ),
825
+ children: [
826
+ /* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-2", children: [
827
+ /* @__PURE__ */ jsx("span", { className: cn("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", badge.ring) }),
828
+ /* @__PURE__ */ jsx("span", { className: cn("relative inline-flex size-2 rounded-full", badge.dot) })
829
+ ] }),
830
+ badge.label
831
+ ]
832
+ }
833
+ ),
834
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
835
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold leading-tight", children: headTitle }),
836
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mt-0.5 block truncate text-xs", children: [
837
+ threads.length,
838
+ " ",
839
+ threads.length === 1 ? "thread" : "threads",
840
+ " on this action",
841
+ anyPaused ? /* @__PURE__ */ jsxs(Fragment, { children: [
842
+ " \xB7 ",
843
+ /* @__PURE__ */ jsx("b", { children: "playbook stopped" })
844
+ ] }) : null
845
+ ] })
790
846
  ] }),
791
- badge.label
847
+ hubOpen ? /* @__PURE__ */ jsx(ChevronUp, { size: 16, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 16, className: "text-muted-foreground shrink-0" })
792
848
  ]
793
849
  }
794
850
  ),
795
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
796
- /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold leading-tight", children: headTitle }),
797
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mt-0.5 block truncate text-xs", children: [
798
- threads.length,
799
- " ",
800
- threads.length === 1 ? "thread" : "threads",
801
- " on this action",
802
- anyPaused ? /* @__PURE__ */ jsxs(Fragment, { children: [
803
- " \xB7 ",
804
- /* @__PURE__ */ jsx("b", { children: "playbook stopped" })
805
- ] }) : null
806
- ] })
807
- ] }),
808
- hubOpen ? /* @__PURE__ */ jsx(ChevronUp, { size: 16, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 16, className: "text-muted-foreground shrink-0" })
851
+ hubGmailThread ? /* @__PURE__ */ jsx("div", { className: "shrink-0", onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx(OpenInGmailButton, { thread: hubGmailThread, onOpenInGmail }) }) : null
809
852
  ]
810
853
  }
811
854
  ),