@handled-ai/design-system 0.20.9 → 0.20.10

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.
@@ -34,64 +34,84 @@ function CommentComposer({
34
34
  "data-slot": "comment-composer",
35
35
  "data-open": open ? "true" : void 0,
36
36
  className: cn(
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",
37
+ "flex items-start gap-4 rounded-xl transition-colors",
39
38
  className
40
39
  ),
41
40
  children: [
42
- /* @__PURE__ */ jsxs(Avatar, { size: "sm", className: "mt-px", children: [
41
+ /* @__PURE__ */ jsxs(Avatar, { size: "sm", className: "mt-1", children: [
43
42
  (author == null ? void 0 : author.avatarUrl) ? /* @__PURE__ */ jsx(AvatarImage, { src: author.avatarUrl, alt: (_a = author.name) != null ? _a : "You" }) : null,
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 }) })
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 }) })
45
44
  ] }),
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: [
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: [
75
54
  /* @__PURE__ */ jsx(
76
- Button,
55
+ Textarea,
77
56
  {
78
- type: "button",
79
- variant: "ghost",
80
- size: "sm",
81
- onClick: () => {
82
- setText("");
83
- setFocused(false);
84
- },
85
- children: "Cancel"
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
+ }
86
73
  }
87
74
  ),
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
- ] })
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
+ )
95
115
  ]
96
116
  }
97
117
  );
@@ -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 \"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":[]}
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":[]}
@@ -145,30 +145,17 @@ function PersonAvatar({ person, size = "sm" }) {
145
145
  ] });
146
146
  }
147
147
  const STATUS_PILL = {
148
- responded: { label: "NEW REPLY", cls: "bg-status-warning-bg text-status-warning-fg border-status-warning-border" },
148
+ responded: { label: "New reply", cls: "bg-status-active-bg text-status-active-fg border-status-active-border" },
149
149
  draft: { label: "Draft", cls: "bg-background text-foreground/80 border-border" },
150
- awaiting: { label: "SENT", cls: "bg-status-info-bg text-status-info-fg border-status-info-border" },
150
+ awaiting: { label: "Awaiting", cls: "bg-status-pending-bg text-status-pending-fg border-status-pending-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-warning-fg",
154
+ responded: "bg-status-active-fg",
155
155
  draft: "bg-status-pending-fg",
156
- awaiting: "bg-status-info-fg",
156
+ awaiting: "bg-status-pending-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
- };
172
159
  function effectiveStatus(t) {
173
160
  return t.canReply === false ? "viewing" : t.status;
174
161
  }
@@ -271,8 +258,8 @@ function MessageView({
271
258
  ] })
272
259
  ] }),
273
260
  /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2", children: [
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 }),
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 }),
276
263
  message.receipt.label
277
264
  ] }) : null,
278
265
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 text-xs", children: message.date }),
@@ -595,13 +582,13 @@ function ThreadBody({
595
582
  }, [sortedMessages]);
596
583
  const toggle = (id) => setExpanded((e) => __spreadProps(__spreadValues({}, e), { [id]: !e[id] }));
597
584
  return /* @__PURE__ */ jsxs("div", { "data-slot": "conv-thread-body", className: "space-y-2", 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: [
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: [
599
586
  /* @__PURE__ */ jsx(Pause, { size: 13, className: "mt-0.5 shrink-0" }),
600
587
  /* @__PURE__ */ jsxs("span", { children: [
601
- /* @__PURE__ */ jsx("b", { children: "Playbook stopped." }),
602
- " Follow-up actions for ",
588
+ /* @__PURE__ */ jsx("b", { children: "Follow-up actions stopped." }),
589
+ " Your ",
603
590
  thread.paused.playbook,
604
- " won\u2019t send automatically while this conversation is live. Continue it in ",
591
+ " next steps won\u2019t send automatically while this conversation is live. Continue it in ",
605
592
  tenantName != null ? tenantName : "the app",
606
593
  " or Gmail."
607
594
  ] })
@@ -707,56 +694,47 @@ function ThreadRow({
707
694
  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 : "");
708
695
  const lastSnippet = last ? messageBodySnippet(last, 120) : "";
709
696
  const pill = STATUS_PILL[status];
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
- );
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
+ ] });
760
738
  }
761
739
  function ConversationPanel({
762
740
  threads,
@@ -773,82 +751,61 @@ function ConversationPanel({
773
751
  const draft = threads.filter((t) => effectiveStatus(t) === "draft").length;
774
752
  const awaiting = threads.filter((t) => effectiveStatus(t) === "awaiting").length;
775
753
  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");
778
754
  const [hubOpen, setHubOpen] = React.useState(true);
779
755
  const [openId, setOpenId] = React.useState(() => {
780
756
  if (defaultOpenThreadId) return defaultOpenThreadId;
781
- const firstActionable = threads.find((t) => ["responded", "draft", "awaiting"].includes(t.status) && t.canReply !== false);
757
+ const firstActionable = threads.find((t) => ["responded", "draft"].includes(t.status) && t.canReply !== false);
782
758
  return firstActionable ? firstActionable.threadId : null;
783
759
  });
784
760
  if (!threads.length) return null;
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";
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"}`;
788
763
  return /* @__PURE__ */ jsxs(
789
764
  "section",
790
765
  {
791
766
  "data-slot": "conversation-panel",
792
767
  "data-responded": responded > 0 ? "true" : void 0,
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
- ),
768
+ className: cn("border-border bg-background overflow-hidden rounded-xl border", className),
799
769
  children: [
800
770
  /* @__PURE__ */ jsxs(
801
- "div",
771
+ "button",
802
772
  {
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
- ),
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",
808
777
  children: [
809
778
  /* @__PURE__ */ jsxs(
810
- "button",
779
+ "span",
811
780
  {
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",
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
+ ),
816
786
  children: [
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
- ] })
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) })
846
790
  ] }),
847
- hubOpen ? /* @__PURE__ */ jsx(ChevronUp, { size: 16, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 16, className: "text-muted-foreground shrink-0" })
791
+ badge.label
848
792
  ]
849
793
  }
850
794
  ),
851
- hubGmailThread ? /* @__PURE__ */ jsx("div", { className: "shrink-0", onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx(OpenInGmailButton, { thread: hubGmailThread, onOpenInGmail }) }) : null
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" })
852
809
  ]
853
810
  }
854
811
  ),