@photon-ai/pho-ui 4.6.1 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,17 @@
1
1
  "use client";
2
2
  import { t as r } from "./cn-ChIgwEl3.js";
3
- import { createContext as l, useContext as d } from "react";
3
+ import { createContext as f, useContext as m } from "react";
4
4
  import { jsx as s } from "react/jsx-runtime";
5
- var i = l("start");
6
- function m({ className: e, ...a }) {
5
+ var u = f("start");
6
+ function g({ className: e, ...a }) {
7
7
  return /* @__PURE__ */ s("div", {
8
8
  "data-message-group": "",
9
9
  className: r("flex min-w-0 flex-col gap-2", e),
10
10
  ...a
11
11
  });
12
12
  }
13
- function f({ align: e = "start", className: a, ...t }) {
14
- return /* @__PURE__ */ s(i.Provider, {
13
+ function c({ align: e = "start", className: a, ...t }) {
14
+ return /* @__PURE__ */ s(u.Provider, {
15
15
  value: e,
16
16
  children: /* @__PURE__ */ s("div", {
17
17
  "data-message": "",
@@ -21,69 +21,72 @@ function f({ align: e = "start", className: a, ...t }) {
21
21
  })
22
22
  });
23
23
  }
24
- function g({ className: e, ...a }) {
24
+ function x({ className: e, ...a }) {
25
25
  return /* @__PURE__ */ s("div", {
26
26
  "data-message-avatar": "",
27
27
  className: r("flex shrink-0 self-end group-has-[[data-message-footer]]/message:-translate-y-[1.375rem]", e),
28
28
  ...a
29
29
  });
30
30
  }
31
- function c({ className: e, ...a }) {
32
- const t = d(i);
31
+ function p({ className: e, ...a }) {
32
+ const t = m(u);
33
33
  return /* @__PURE__ */ s("div", {
34
34
  "data-message-content": "",
35
35
  className: r("grid w-max max-w-full flex-none items-end gap-x-1.5 gap-y-1 break-words", "[&>[data-message-header]]:[grid-area:header]", "[&>[data-bubble]]:max-w-[80cqw] [&>[data-bubble]]:[grid-area:main]", "[&>[data-bubble-group]]:max-w-[80cqw] [&>[data-bubble-group]]:[grid-area:main]", "[&>[data-bubble][data-variant=ghost]]:max-w-full", "[&>[data-message-time]]:[grid-area:time]", "[&>[data-message-footer]]:[grid-area:status]", t === "end" ? "ml-auto grid-cols-[auto_minmax(0,max-content)] [grid-template-areas:'._header'_'time_main'_'._status'] [&>[data-bubble-group]]:justify-self-end [&>[data-bubble]]:justify-self-end [&>[data-message-header]]:justify-self-end" : "grid-cols-[minmax(0,max-content)_auto] [grid-template-areas:'header_.'_'main_time'_'status_.'] [&>[data-bubble-group]]:justify-self-start [&>[data-bubble]]:justify-self-start", e),
36
36
  ...a
37
37
  });
38
38
  }
39
- function p({ className: e, ...a }) {
39
+ function b({ className: e, ...a }) {
40
40
  return /* @__PURE__ */ s("div", {
41
41
  "data-message-header": "",
42
42
  className: r("text-pho-secondary flex w-full max-w-full min-w-0 items-center gap-1.5 px-1 text-xs font-medium group-has-[[data-bubble][data-variant=ghost]]/message:px-0", e),
43
43
  ...a
44
44
  });
45
45
  }
46
- function x({ className: e, ...a }) {
46
+ function h({ className: e, ...a }) {
47
47
  return /* @__PURE__ */ s("div", {
48
48
  "data-message-time": "",
49
49
  className: r("text-pho-description shrink-0 self-end text-xs whitespace-nowrap tabular-nums opacity-0 transition-opacity duration-100 ease-linear group-focus-within/message:opacity-100 group-hover/message:opacity-100", e),
50
50
  ...a
51
51
  });
52
52
  }
53
- function b({ className: e, ...a }) {
54
- const t = d(i);
53
+ function w({ className: e, ...a }) {
54
+ const t = m(u);
55
55
  return /* @__PURE__ */ s("div", {
56
56
  "data-message-footer": "",
57
57
  className: r("text-pho-description flex max-w-full min-w-0 items-center gap-1.5 px-1 text-xs group-has-[[data-bubble][data-variant=ghost]]/message:px-0", t === "end" && "justify-self-end", e),
58
58
  ...a
59
59
  });
60
60
  }
61
- var y = {
62
- Root: f,
63
- Group: m,
64
- Avatar: g,
65
- Content: c,
66
- Header: p,
67
- Time: x,
68
- Footer: b
61
+ var j = {
62
+ Root: c,
63
+ Group: g,
64
+ Avatar: x,
65
+ Content: p,
66
+ Header: b,
67
+ Time: h,
68
+ Footer: w
69
69
  };
70
- function u(e, a) {
70
+ function d(e, a) {
71
71
  if (!e || a == null) return null;
72
72
  const t = a.trim().toLowerCase(), n = t.split(/[\s.]+/)[0];
73
73
  return n ? n === "failed" ? t : n : null;
74
74
  }
75
- function _(e, a) {
75
+ function N(e, a) {
76
76
  const t = e[a];
77
77
  if (!t) return !1;
78
- const n = u(t.outbound === !0, t.status);
78
+ const n = d(t.outbound === !0, t.status);
79
79
  if (n == null) return !1;
80
- const o = e[a + 1];
81
- return o ? u(o.outbound === !0, o.status) !== n : !0;
80
+ for (let o = a + 1; o < e.length; o++) {
81
+ const i = e[o], l = d(i?.outbound === !0, i?.status);
82
+ if (l != null) return l !== n;
83
+ }
84
+ return !0;
82
85
  }
83
86
  export {
84
- _ as n,
85
- y as r,
86
- u as t
87
+ N as n,
88
+ j as r,
89
+ d as t
87
90
  };
88
91
 
89
- //# sourceMappingURL=message-CTml-TrB.js.map
92
+ //# sourceMappingURL=message-Dz2-numf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"message-CTml-TrB.js","names":[],"sources":["../../src/components/message/message.tsx","../../src/components/message/message-status.ts"],"sourcesContent":["import { createContext, useContext, type ComponentProps } from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/**\n * Message — one turn in a thread: the row that seats an avatar beside a\n * column of header, bubble, and footer, on the author's side. It owns the\n * row layout and nothing else: the surface is `Bubble`'s, the scrolling\n * and the composer are `Chat`'s.\n *\n * Design notes:\n * - `align=\"end\"` mirrors the row — avatar on the right, column\n * right-aligned — for the reader's own messages.\n * - The avatar sits at the row's bottom, level with the bubble. When a\n * `Footer` (status) sits under the bubble, the avatar shifts up by one\n * line so it stays beside the surface.\n * - `Time` sits beside the bubble, last-line aligned — right of inbound,\n * left of outbound — and is invisible until the row is hovered or\n * focused. `Footer` is the status caption under the bubble (Delivered,\n * Failed). Inbound has no status.\n */\n\nexport type MessageAlign = \"start\" | \"end\";\n\nconst MessageContext = createContext<MessageAlign>(\"start\");\n\n/* --------------------------------- group -------------------------------- */\n\n/** Consecutive messages from one author, spaced as one turn. */\nfunction Group({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-group=\"\"\n className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n {...props}\n />\n );\n}\n\n/* --------------------------------- root --------------------------------- */\n\nexport interface MessageRootProps extends ComponentProps<\"div\"> {\n /** Which side the author sits on. @default \"start\" */\n align?: MessageAlign;\n}\n\n/** The row: avatar beside the content column, mirrored for `end`. */\nfunction Root({ align = \"start\", className, ...props }: MessageRootProps) {\n return (\n <MessageContext.Provider value={align}>\n <div\n data-message=\"\"\n data-align={align}\n className={cn(\n \"group/message @container relative flex w-full min-w-0 gap-2 text-base\",\n align === \"end\" && \"flex-row-reverse\",\n className,\n )}\n {...props}\n />\n </MessageContext.Provider>\n );\n}\n\n/* -------------------------------- avatar -------------------------------- */\n\n/**\n * The avatar's seat — pass an `Avatar.Root`. Bottom-aligned with the\n * bubble; lifts by one footer line when the message has a `Footer`.\n */\nfunction Avatar({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-avatar=\"\"\n className={cn(\n // Footer line = xs line-height (18px) + the column's 4px gap.\n \"flex shrink-0 self-end group-has-[[data-message-footer]]/message:-translate-y-[1.375rem]\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/* -------------------------------- content ------------------------------- */\n\n/**\n * Header over the bubble; time beside it; status under it. Inbound is\n * `[bubble][time]`. Outbound is `[time][bubble]` with `Footer` below\n * the bubble column.\n */\nfunction Content({ className, ...props }: ComponentProps<\"div\">) {\n const align = useContext(MessageContext);\n return (\n <div\n data-message-content=\"\"\n className={cn(\n \"grid w-max max-w-full flex-none items-end gap-x-1.5 gap-y-1 break-words\",\n \"[&>[data-message-header]]:[grid-area:header]\",\n \"[&>[data-bubble]]:max-w-[80cqw] [&>[data-bubble]]:[grid-area:main]\",\n \"[&>[data-bubble-group]]:max-w-[80cqw] [&>[data-bubble-group]]:[grid-area:main]\",\n \"[&>[data-bubble][data-variant=ghost]]:max-w-full\",\n \"[&>[data-message-time]]:[grid-area:time]\",\n \"[&>[data-message-footer]]:[grid-area:status]\",\n align === \"end\"\n ? \"ml-auto grid-cols-[auto_minmax(0,max-content)] [grid-template-areas:'._header'_'time_main'_'._status'] [&>[data-bubble-group]]:justify-self-end [&>[data-bubble]]:justify-self-end [&>[data-message-header]]:justify-self-end\"\n : \"grid-cols-[minmax(0,max-content)_auto] [grid-template-areas:'header_.'_'main_time'_'status_.'] [&>[data-bubble-group]]:justify-self-start [&>[data-bubble]]:justify-self-start\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Who — the sender's name over the bubble, secondary ink. */\nfunction Header({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-header=\"\"\n className={cn(\n \"text-pho-secondary flex w-full max-w-full min-w-0 items-center gap-1.5 px-1 text-xs font-medium group-has-[[data-bubble][data-variant=ghost]]/message:px-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Clock beside the bubble, last-line aligned. Description ink, tabular.\n * Inbound sits to the right; outbound to the left. The slot stays in\n * layout; the digits show when the row is hovered or focused.\n */\nfunction Time({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-time=\"\"\n className={cn(\n \"text-pho-description shrink-0 self-end text-xs whitespace-nowrap tabular-nums opacity-0 transition-opacity duration-100 ease-linear group-focus-within/message:opacity-100 group-hover/message:opacity-100\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Status under the bubble — Delivered, Failed. Inbound leaves it off.\n * Description ink.\n */\nfunction Footer({ className, ...props }: ComponentProps<\"div\">) {\n const align = useContext(MessageContext);\n return (\n <div\n data-message-footer=\"\"\n className={cn(\n \"text-pho-description flex max-w-full min-w-0 items-center gap-1.5 px-1 text-xs group-has-[[data-bubble][data-variant=ghost]]/message:px-0\",\n align === \"end\" && \"justify-self-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * A turn in a thread. `Root` takes `align`; `Avatar` seats an\n * `Avatar.Root`; `Content` places `Header` over the bubble, `Time`\n * beside it, and `Footer` under it.\n *\n * ```tsx\n * <Message.Root align=\"end\">\n * <Message.Content>\n * <Bubble.Root variant=\"inverse\" align=\"end\">\n * <Bubble.Content>On my way.</Bubble.Content>\n * </Bubble.Root>\n * <Message.Time>2:47 PM</Message.Time>\n * <Message.Footer>Delivered.</Message.Footer>\n * </Message.Content>\n * </Message.Root>\n * ```\n */\nexport const Message = { Root, Group, Avatar, Content, Header, Time, Footer };\n","/**\n * One row in a thread, enough to decide whether its outbound status\n * caption should show. Inbound rows leave `outbound` off.\n */\nexport interface MessageStatusItem {\n outbound?: boolean;\n /** Delivered, Sent, Failed — or a token like `delivered`. */\n status?: string | null;\n}\n\n/**\n * The coalesce key for a row: outbound status word, lowercased. Inbound\n * and empty statuses are `null`, so they break a run. A failure keeps its\n * whole text (`failed. 30007`) so two different error codes never\n * collapse into one caption.\n */\nexport function messageStatusKey(\n outbound: boolean,\n status: string | null | undefined,\n): string | null {\n if (!outbound || status == null) return null;\n const text = status.trim().toLowerCase();\n const word = text.split(/[\\s.]+/)[0];\n if (!word) return null;\n return word === \"failed\" ? text : word;\n}\n\n/**\n * Whether this row should render its outbound status (Delivered, Sent,\n * Failed). Only the last message in a consecutive same-status run shows\n * it. Inbound never does.\n */\nexport function shouldShowMessageStatus(\n messages: readonly MessageStatusItem[],\n index: number,\n): boolean {\n const current = messages[index];\n if (!current) return false;\n const key = messageStatusKey(current.outbound === true, current.status);\n if (key == null) return false;\n const next = messages[index + 1];\n if (!next) return true;\n return messageStatusKey(next.outbound === true, next.status) !== key;\n}\n"],"mappings":";;;;AAuBA,IAAM,IAAiB,EAA4B,OAAO;AAK1D,SAAS,EAAM,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgC;AAC7D,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,sBAAmB;AAAA,IACnB,WAAW,EAAG,+BAA+B,CAAS;AAAA,IACtD,GAAI;AAAA,EACL,CAAA;AAEL;AAUA,SAAS,EAAK,EAAE,OAAA,IAAQ,SAAS,WAAA,GAAW,GAAG,EAAA,GAA2B;AACxE,SACE,gBAAA,EAAC,EAAe,UAAhB;AAAA,IAAyB,OAAO;AAAA,IAC9B,UAAA,gBAAA,EAAC,OAAD;AAAA,MACE,gBAAa;AAAA,MACb,cAAY;AAAA,MACZ,WAAW,EACT,yEACA,MAAU,SAAS,oBACnB,CACF;AAAA,MACA,GAAI;AAAA,IACL,CAAA;AAAA,EACsB,CAAA;AAE7B;AAQA,SAAS,EAAO,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgC;AAC9D,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,uBAAoB;AAAA,IACpB,WAAW,EAET,4FACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AASA,SAAS,EAAQ,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgC;AAC/D,QAAM,IAAQ,EAAW,CAAc;AACvC,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,wBAAqB;AAAA,IACrB,WAAW,EACT,2EACA,gDACA,sEACA,kFACA,oDACA,4CACA,gDACA,MAAU,QACN,kOACA,kLACJ,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAGA,SAAS,EAAO,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgC;AAC9D,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,uBAAoB;AAAA,IACpB,WAAW,EACT,8JACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAOA,SAAS,EAAK,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgC;AAC5D,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,qBAAkB;AAAA,IAClB,WAAW,EACT,8MACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAMA,SAAS,EAAO,EAAE,WAAA,GAAW,GAAG,EAAA,GAAgC;AAC9D,QAAM,IAAQ,EAAW,CAAc;AACvC,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,uBAAoB;AAAA,IACpB,WAAW,EACT,6IACA,MAAU,SAAS,oBACnB,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAmBA,IAAa,IAAU;AAAA,EAAE,MAAA;AAAA,EAAM,OAAA;AAAA,EAAO,QAAA;AAAA,EAAQ,SAAA;AAAA,EAAS,QAAA;AAAA,EAAQ,MAAA;AAAA,EAAM,QAAA;AAAO;ACrK5E,SAAgB,EACd,GACA,GACe;AACf,MAAI,CAAC,KAAY,KAAU,KAAM,QAAO;AACxC,QAAM,IAAO,EAAO,KAAK,EAAE,YAAY,GACjC,IAAO,EAAK,MAAM,QAAQ,EAAE,CAAA;AAClC,SAAK,IACE,MAAS,WAAW,IAAO,IADhB;AAEpB;AAOA,SAAgB,EACd,GACA,GACS;AACT,QAAM,IAAU,EAAS,CAAA;AACzB,MAAI,CAAC,EAAS,QAAO;AACrB,QAAM,IAAM,EAAiB,EAAQ,aAAa,IAAM,EAAQ,MAAM;AACtE,MAAI,KAAO,KAAM,QAAO;AACxB,QAAM,IAAO,EAAS,IAAQ,CAAA;AAC9B,SAAK,IACE,EAAiB,EAAK,aAAa,IAAM,EAAK,MAAM,MAAM,IAD/C;AAEpB"}
1
+ {"version":3,"file":"message-Dz2-numf.js","names":[],"sources":["../../src/components/message/message.tsx","../../src/components/message/message-status.ts"],"sourcesContent":["import { createContext, useContext, type ComponentProps } from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/**\n * Message — one turn in a thread: the row that seats an avatar beside a\n * column of header, bubble, and footer, on the author's side. It owns the\n * row layout and nothing else: the surface is `Bubble`'s, the scrolling\n * and the composer are `Chat`'s.\n *\n * Design notes:\n * - `align=\"end\"` mirrors the row — avatar on the right, column\n * right-aligned — for the reader's own messages.\n * - The avatar sits at the row's bottom, level with the bubble. When a\n * `Footer` (status) sits under the bubble, the avatar shifts up by one\n * line so it stays beside the surface.\n * - `Time` sits beside the bubble, last-line aligned — right of inbound,\n * left of outbound — and is invisible until the row is hovered or\n * focused. `Footer` is the status caption under the bubble (Delivered,\n * Failed). Inbound has no status.\n */\n\nexport type MessageAlign = \"start\" | \"end\";\n\nconst MessageContext = createContext<MessageAlign>(\"start\");\n\n/* --------------------------------- group -------------------------------- */\n\n/** Consecutive messages from one author, spaced as one turn. */\nfunction Group({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-group=\"\"\n className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n {...props}\n />\n );\n}\n\n/* --------------------------------- root --------------------------------- */\n\nexport interface MessageRootProps extends ComponentProps<\"div\"> {\n /** Which side the author sits on. @default \"start\" */\n align?: MessageAlign;\n}\n\n/** The row: avatar beside the content column, mirrored for `end`. */\nfunction Root({ align = \"start\", className, ...props }: MessageRootProps) {\n return (\n <MessageContext.Provider value={align}>\n <div\n data-message=\"\"\n data-align={align}\n className={cn(\n \"group/message @container relative flex w-full min-w-0 gap-2 text-base\",\n align === \"end\" && \"flex-row-reverse\",\n className,\n )}\n {...props}\n />\n </MessageContext.Provider>\n );\n}\n\n/* -------------------------------- avatar -------------------------------- */\n\n/**\n * The avatar's seat — pass an `Avatar.Root`. Bottom-aligned with the\n * bubble; lifts by one footer line when the message has a `Footer`.\n */\nfunction Avatar({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-avatar=\"\"\n className={cn(\n // Footer line = xs line-height (18px) + the column's 4px gap.\n \"flex shrink-0 self-end group-has-[[data-message-footer]]/message:-translate-y-[1.375rem]\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/* -------------------------------- content ------------------------------- */\n\n/**\n * Header over the bubble; time beside it; status under it. Inbound is\n * `[bubble][time]`. Outbound is `[time][bubble]` with `Footer` below\n * the bubble column.\n */\nfunction Content({ className, ...props }: ComponentProps<\"div\">) {\n const align = useContext(MessageContext);\n return (\n <div\n data-message-content=\"\"\n className={cn(\n \"grid w-max max-w-full flex-none items-end gap-x-1.5 gap-y-1 break-words\",\n \"[&>[data-message-header]]:[grid-area:header]\",\n \"[&>[data-bubble]]:max-w-[80cqw] [&>[data-bubble]]:[grid-area:main]\",\n \"[&>[data-bubble-group]]:max-w-[80cqw] [&>[data-bubble-group]]:[grid-area:main]\",\n \"[&>[data-bubble][data-variant=ghost]]:max-w-full\",\n \"[&>[data-message-time]]:[grid-area:time]\",\n \"[&>[data-message-footer]]:[grid-area:status]\",\n align === \"end\"\n ? \"ml-auto grid-cols-[auto_minmax(0,max-content)] [grid-template-areas:'._header'_'time_main'_'._status'] [&>[data-bubble-group]]:justify-self-end [&>[data-bubble]]:justify-self-end [&>[data-message-header]]:justify-self-end\"\n : \"grid-cols-[minmax(0,max-content)_auto] [grid-template-areas:'header_.'_'main_time'_'status_.'] [&>[data-bubble-group]]:justify-self-start [&>[data-bubble]]:justify-self-start\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Who — the sender's name over the bubble, secondary ink. */\nfunction Header({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-header=\"\"\n className={cn(\n \"text-pho-secondary flex w-full max-w-full min-w-0 items-center gap-1.5 px-1 text-xs font-medium group-has-[[data-bubble][data-variant=ghost]]/message:px-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Clock beside the bubble, last-line aligned. Description ink, tabular.\n * Inbound sits to the right; outbound to the left. The slot stays in\n * layout; the digits show when the row is hovered or focused.\n */\nfunction Time({ className, ...props }: ComponentProps<\"div\">) {\n return (\n <div\n data-message-time=\"\"\n className={cn(\n \"text-pho-description shrink-0 self-end text-xs whitespace-nowrap tabular-nums opacity-0 transition-opacity duration-100 ease-linear group-focus-within/message:opacity-100 group-hover/message:opacity-100\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Status under the bubble — Delivered, Failed. Inbound leaves it off.\n * Description ink.\n */\nfunction Footer({ className, ...props }: ComponentProps<\"div\">) {\n const align = useContext(MessageContext);\n return (\n <div\n data-message-footer=\"\"\n className={cn(\n \"text-pho-description flex max-w-full min-w-0 items-center gap-1.5 px-1 text-xs group-has-[[data-bubble][data-variant=ghost]]/message:px-0\",\n align === \"end\" && \"justify-self-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * A turn in a thread. `Root` takes `align`; `Avatar` seats an\n * `Avatar.Root`; `Content` places `Header` over the bubble, `Time`\n * beside it, and `Footer` under it.\n *\n * ```tsx\n * <Message.Root align=\"end\">\n * <Message.Content>\n * <Bubble.Root variant=\"inverse\" align=\"end\">\n * <Bubble.Content>On my way.</Bubble.Content>\n * </Bubble.Root>\n * <Message.Time>2:47 PM</Message.Time>\n * <Message.Footer>Delivered.</Message.Footer>\n * </Message.Content>\n * </Message.Root>\n * ```\n */\nexport const Message = { Root, Group, Avatar, Content, Header, Time, Footer };\n","/**\n * One row in a thread, enough to decide whether its outbound status\n * caption should show. Inbound rows leave `outbound` off.\n */\nexport interface MessageStatusItem {\n outbound?: boolean;\n /** Delivered, Sent, Failed — or a token like `delivered`. */\n status?: string | null;\n}\n\n/**\n * The coalesce key for a row: outbound status word, lowercased. Inbound\n * and empty statuses are `null`: they carry no status and sit outside the\n * runs (see `shouldShowMessageStatus`). A failure keeps its\n * whole text (`failed. 30007`) so two different error codes never\n * collapse into one caption.\n */\nexport function messageStatusKey(\n outbound: boolean,\n status: string | null | undefined,\n): string | null {\n if (!outbound || status == null) return null;\n const text = status.trim().toLowerCase();\n const word = text.split(/[\\s.]+/)[0];\n if (!word) return null;\n return word === \"failed\" ? text : word;\n}\n\n/**\n * Whether this row should render its outbound status (Delivered, Sent,\n * Failed). Only the last message in a same-status run shows it. Inbound\n * never does, and a reply in between does not break the run: the run is\n * over the outbound messages alone, so a conversation going back and forth\n * wears one Delivered, under the latest message it holds for, not one\n * under every turn.\n */\nexport function shouldShowMessageStatus(\n messages: readonly MessageStatusItem[],\n index: number,\n): boolean {\n const current = messages[index];\n if (!current) return false;\n const key = messageStatusKey(current.outbound === true, current.status);\n if (key == null) return false;\n for (let at = index + 1; at < messages.length; at++) {\n const next = messages[at];\n const nextKey = messageStatusKey(next?.outbound === true, next?.status);\n if (nextKey != null) return nextKey !== key;\n }\n return true;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { n as e, r as a, t } from "../chunks/message-CTml-TrB.js";
2
+ import { n as e, r as a, t } from "../chunks/message-Dz2-numf.js";
3
3
  export {
4
4
  a as Message,
5
5
  t as messageStatusKey,
package/dist/index.js CHANGED
@@ -26,14 +26,14 @@ import { n as gs, t as Ms } from "./chunks/card-N-tvFsuV.js";
26
26
  import { n as Fs, t as ks } from "./chunks/dialog-MPvem7yV.js";
27
27
  import { i as ws, n as Ws, r as xs, t as ys } from "./chunks/status-view-C8tabjDu.js";
28
28
  import { t as Ks } from "./chunks/description-list-8qm83WZB.js";
29
- import { a as Xs, c as Ys, d as Zs, f as js, i as Js, l as Qs, m as qs, n as $s, o as at, p as st, r as tt, s as rt, t as ot, u as et } from "./chunks/basic-page-C0DvO_Vt.js";
29
+ import { a as Xs, c as Ys, d as Zs, f as js, i as Js, l as Qs, m as qs, n as $s, o as at, p as st, r as tt, s as rt, t as ot, u as et } from "./chunks/basic-page-E_on0m1X.js";
30
30
  import { i as Et, n as St, r as _t, t as nt } from "./chunks/legend-C0mU7dqd.js";
31
31
  import "./components/chart.js";
32
32
  import { t as Tt } from "./chunks/bar-chart-DIALC2_z.js";
33
33
  import { t as At } from "./chunks/line-chart-VuMYKzW3.js";
34
34
  import { i as lt, n as Ct, r as Bt, t as ft } from "./chunks/log-list-tNuCFGlP.js";
35
35
  import { n as Nt, t as Pt } from "./chunks/bubble-eaoLJP4N.js";
36
- import { n as ct, r as Dt, t as Ot } from "./chunks/message-CTml-TrB.js";
36
+ import { n as ct, r as Dt, t as Ot } from "./chunks/message-Dz2-numf.js";
37
37
  export {
38
38
  ot as AUTO_SAVE_DELAY_MS,
39
39
  n as BAR_RISE_PX,
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { a as s } from "../chunks/data-table-Mog6m3d9.js";
3
- import { a as E, c as a, d as C, f as S, i as I, l as e, m as o, n as B, o as P, p as N, r, s as t, t as D, u as R } from "../chunks/basic-page-C0DvO_Vt.js";
3
+ import { a as E, c as a, d as C, f as S, i as I, l as e, m as o, n as B, o as P, p as N, r, s as t, t as D, u as R } from "../chunks/basic-page-E_on0m1X.js";
4
4
  export {
5
5
  D as AUTO_SAVE_DELAY_MS,
6
6
  B as BASIC_PAGE_CALLOUT,
@@ -9,14 +9,18 @@ export interface MessageStatusItem {
9
9
  }
10
10
  /**
11
11
  * The coalesce key for a row: outbound status word, lowercased. Inbound
12
- * and empty statuses are `null`, so they break a run. A failure keeps its
12
+ * and empty statuses are `null`: they carry no status and sit outside the
13
+ * runs (see `shouldShowMessageStatus`). A failure keeps its
13
14
  * whole text (`failed. 30007`) so two different error codes never
14
15
  * collapse into one caption.
15
16
  */
16
17
  export declare function messageStatusKey(outbound: boolean, status: string | null | undefined): string | null;
17
18
  /**
18
19
  * Whether this row should render its outbound status (Delivered, Sent,
19
- * Failed). Only the last message in a consecutive same-status run shows
20
- * it. Inbound never does.
20
+ * Failed). Only the last message in a same-status run shows it. Inbound
21
+ * never does, and a reply in between does not break the run: the run is
22
+ * over the outbound messages alone, so a conversation going back and forth
23
+ * wears one Delivered, under the latest message it holds for, not one
24
+ * under every turn.
21
25
  */
22
26
  export declare function shouldShowMessageStatus(messages: readonly MessageStatusItem[], index: number): boolean;
@@ -95,8 +95,13 @@ export interface BasicPageRootProps extends ComponentProps<"div"> {
95
95
  * and its control drift out of one glance. Either width can host an
96
96
  * `Aside` — the column keeps its cap beside the panel, and leftover
97
97
  * space is margin, not content width.
98
+ *
99
+ * `full` takes the cap and the gutters off: the column is the pane, edge
100
+ * to edge, with no trim under it. For a page that is one surface filling
101
+ * the pane (an inbox, a board) — pair it with `condense="pinned"`, and
102
+ * `Stack` sits flush under the bar. Its content brings its own padding.
98
103
  */
99
- width?: "base" | "wide";
104
+ width?: "base" | "wide" | "full";
100
105
  /**
101
106
  * This page was painted before React — rendered to a string and inlined
102
107
  * ahead of the bundle (a boot skeleton), or the React twin re-rendering
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "4.6.1",
3
+ "version": "4.7.0",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {