@kanzo-tech/ai 0.16.0 → 0.17.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.
Files changed (65) hide show
  1. package/dist/ai-mark.d.ts +1 -1
  2. package/dist/ai-mark.d.ts.map +1 -1
  3. package/dist/ai-mark.js +12 -12
  4. package/dist/ai-mark.js.map +1 -1
  5. package/dist/assist.d.ts +71 -0
  6. package/dist/assist.d.ts.map +1 -0
  7. package/dist/assist.js +394 -0
  8. package/dist/assist.js.map +1 -0
  9. package/dist/chat.d.ts +41 -0
  10. package/dist/chat.d.ts.map +1 -0
  11. package/dist/chat.js +77 -0
  12. package/dist/chat.js.map +1 -0
  13. package/dist/engine.d.ts +101 -0
  14. package/dist/engine.d.ts.map +1 -0
  15. package/dist/engine.js +197 -0
  16. package/dist/engine.js.map +1 -0
  17. package/dist/index.d.ts +7 -24
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -56
  20. package/dist/index.js.map +1 -1
  21. package/dist/message.js +25 -66
  22. package/dist/message.js.map +1 -1
  23. package/dist/prompt-input.d.ts +2 -2
  24. package/dist/prompt-input.d.ts.map +1 -1
  25. package/dist/prompt-input.js +47 -49
  26. package/dist/prompt-input.js.map +1 -1
  27. package/dist/proposals.d.ts +28 -0
  28. package/dist/proposals.d.ts.map +1 -0
  29. package/dist/proposals.js +56 -0
  30. package/dist/proposals.js.map +1 -0
  31. package/dist/reasoning.d.ts +4 -3
  32. package/dist/reasoning.d.ts.map +1 -1
  33. package/dist/reasoning.js +60 -55
  34. package/dist/reasoning.js.map +1 -1
  35. package/dist/tool.d.ts +25 -50
  36. package/dist/tool.d.ts.map +1 -1
  37. package/dist/tool.js +83 -95
  38. package/dist/tool.js.map +1 -1
  39. package/package.json +18 -34
  40. package/dist/complete.d.ts +0 -87
  41. package/dist/complete.d.ts.map +0 -1
  42. package/dist/complete.js +0 -278
  43. package/dist/complete.js.map +0 -1
  44. package/dist/message-part.d.ts +0 -122
  45. package/dist/message-part.d.ts.map +0 -1
  46. package/dist/message-part.js +0 -8
  47. package/dist/message-part.js.map +0 -1
  48. package/dist/message-text.d.ts +0 -44
  49. package/dist/message-text.d.ts.map +0 -1
  50. package/dist/message-text.js +0 -49
  51. package/dist/message-text.js.map +0 -1
  52. package/dist/suggest.d.ts +0 -82
  53. package/dist/suggest.d.ts.map +0 -1
  54. package/dist/suggest.js +0 -123
  55. package/dist/suggest.js.map +0 -1
  56. package/dist/task.d.ts +0 -48
  57. package/dist/task.d.ts.map +0 -1
  58. package/dist/task.js +0 -91
  59. package/dist/task.js.map +0 -1
  60. package/dist/types.d.ts +0 -10
  61. package/dist/types.d.ts.map +0 -1
  62. package/dist/use-ai.d.ts +0 -170
  63. package/dist/use-ai.d.ts.map +0 -1
  64. package/dist/use-ai.js +0 -186
  65. package/dist/use-ai.js.map +0 -1
package/dist/message.js CHANGED
@@ -1,8 +1,8 @@
1
- import { jsx as r, jsxs as g, Fragment as u } from "react/jsx-runtime";
2
- import { ark as l } from "@ark-ui/react/factory";
3
- import { tv as p } from "tailwind-variants";
4
- import { cn as o, Avatar as d, AvatarImage as f, AvatarFallback as x } from "@kanzo-tech/ui";
5
- const v = p({
1
+ import { jsx as r } from "react/jsx-runtime";
2
+ import { ark as o } from "@ark-ui/react/factory";
3
+ import { tv as n } from "tailwind-variants";
4
+ import { cn as l } from "@kanzo-tech/ui";
5
+ const g = n({
6
6
  base: [
7
7
  "group/message",
8
8
  "flex w-full flex-wrap items-start gap-3",
@@ -31,90 +31,49 @@ const v = p({
31
31
  defaultVariants: {
32
32
  role: "assistant"
33
33
  }
34
- }), M = (a) => {
35
- const { role: s = "assistant", className: e, slot: t, ...n } = a;
34
+ }), p = (s) => {
35
+ const { role: e = "assistant", className: a, slot: t, ...m } = s;
36
36
  return /* @__PURE__ */ r(
37
- l.li,
37
+ o.li,
38
38
  {
39
- className: o(v({ role: s }), e),
40
- "data-role": s,
41
- ...n,
39
+ className: l(g({ role: e }), a),
40
+ "data-role": e,
41
+ ...m,
42
42
  "data-slot": t ?? "message"
43
43
  }
44
44
  );
45
- }, b = (a) => {
46
- const { className: s, slot: e, ...t } = a;
45
+ }, f = (s) => {
46
+ const { className: e, slot: a, ...t } = s;
47
47
  return /* @__PURE__ */ r(
48
- l.ul,
48
+ o.ul,
49
49
  {
50
- className: o("flex w-full list-none flex-col gap-6", s),
50
+ className: l("flex w-full list-none flex-col gap-6", e),
51
51
  role: "list",
52
52
  ...t,
53
- "data-slot": e ?? "message-list"
53
+ "data-slot": a ?? "message-list"
54
54
  }
55
55
  );
56
- }, y = (a) => (a ?? "").split(/\s+/).filter(Boolean).slice(0, 2).map((s) => {
57
- var e;
58
- return ((e = s[0]) == null ? void 0 : e.toUpperCase()) ?? "";
59
- }).join(""), j = (a) => {
60
- const { src: s, name: e, className: t, children: n, size: i = "sm", slot: m, ...c } = a;
56
+ }, x = (s) => {
57
+ const { className: e, slot: a, ...t } = s;
61
58
  return /* @__PURE__ */ r(
62
- d,
59
+ o.div,
63
60
  {
64
- className: o(
65
- "shrink-0",
66
- "group-data-[role=system]/message:hidden",
67
- t
68
- ),
69
- size: i,
70
- ...c,
71
- slot: m ?? "message-avatar",
72
- children: n ?? /* @__PURE__ */ g(u, { children: [
73
- s ? /* @__PURE__ */ r(f, { alt: e ?? "", src: s }) : null,
74
- /* @__PURE__ */ r(x, { children: y(e) })
75
- ] })
76
- }
77
- );
78
- }, k = (a) => {
79
- const { className: s, slot: e, ...t } = a;
80
- return /* @__PURE__ */ r(
81
- l.div,
82
- {
83
- className: o(
61
+ className: l(
84
62
  "flex min-w-0 flex-1 flex-col gap-2 leading-relaxed",
85
63
  "group-data-[role=user]/message:max-w-[85%] group-data-[role=user]/message:flex-none",
86
64
  "group-data-[role=user]/message:rounded-xl",
87
65
  "group-data-[role=user]/message:bg-secondary group-data-[role=user]/message:text-secondary-foreground",
88
66
  "group-data-[role=user]/message:px-3.5 group-data-[role=user]/message:py-2.5",
89
- s
90
- ),
91
- ...t,
92
- "data-slot": e ?? "message-content"
93
- }
94
- );
95
- }, C = (a) => {
96
- const { className: s, slot: e, ...t } = a;
97
- return /* @__PURE__ */ r(
98
- l.div,
99
- {
100
- className: o(
101
- "flex basis-full items-center gap-1",
102
- "opacity-0 transition-opacity motion-reduce:transition-none!",
103
- "group-hover/message:opacity-100 group-focus-within/message:opacity-100",
104
- "pointer-coarse:opacity-100",
105
- "group-data-[role=user]/message:justify-end",
106
- s
67
+ e
107
68
  ),
108
69
  ...t,
109
- "data-slot": e ?? "message-actions"
70
+ "data-slot": a ?? "message-content"
110
71
  }
111
72
  );
112
73
  };
113
74
  export {
114
- M as Message,
115
- C as MessageActions,
116
- j as MessageAvatar,
117
- k as MessageContent,
118
- b as MessageList
75
+ p as Message,
76
+ x as MessageContent,
77
+ f as MessageList
119
78
  };
120
79
  //# sourceMappingURL=message.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"message.js","sources":["../src/message.tsx"],"sourcesContent":["import { ark } from \"@ark-ui/react/factory\";\nimport type React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { Avatar, AvatarFallback, AvatarImage, cn } from \"@kanzo-tech/ui\";\n\nexport type MessageRole = \"user\" | \"assistant\" | \"system\";\n\nconst messageVariants = tv({\n base: [\n \"group/message\",\n \"flex w-full flex-wrap items-start gap-3\",\n \"text-sm\",\n ],\n variants: {\n role: {\n // Reversed rather than `justify-end`: the row packs against the reading end and the avatar\n // goes with it, and `row-reverse` is direction-aware where a physical side is not.\n user: \"flex-row-reverse\",\n assistant: \"\",\n // **No surface of its own, and that is the correction.** A system note used to be a centred\n // `rounded-full` pill painted by `MessageContent` — a shape the library already has, spelled\n // again, and one that cannot hold what it was given: a full-round pill around a wrapped\n // sentence is a stadium with dead corners, and `Badge`, the component it was imitating, is\n // `whitespace-nowrap` at a fixed `h-5` and could never have rendered it.\n //\n // AI Elements types `from` as the AI SDK's `UIMessage[\"role\"]` — system included — and gives\n // it no treatment at all: `is-user` or `is-assistant`, nothing else. So the role stays, the\n // invented surface goes, and a caller who wants the note to read as an aside composes an\n // `Alert` inside the row. That is a component with a wash, a border, an icon slot, an action\n // slot, `role=\"status\"` and the ability to wrap.\n system: \"\",\n },\n },\n defaultVariants: {\n role: \"assistant\",\n },\n});\n\nexport interface MessageProps\n extends Omit<React.ComponentProps<typeof ark.li>, \"role\">,\n VariantProps<typeof messageVariants> {}\n\n/**\n * One turn. The role is declared once, here, and every part below reads it off `data-role`\n * through a group selector — repeating it as a prop on each part is how the two drift.\n *\n * `role` is the speaker and not the ARIA attribute, which is why the HTML one is omitted rather\n * than merged: two meanings under one name, and the wrong one reaching the DOM, is worse than an\n * `<li>` nobody can re-role.\n */\nexport const Message = (props: MessageProps) => {\n const { role = \"assistant\", className, slot, ...rest } = props;\n\n return (\n <ark.li\n className={cn(messageVariants({ role }), className)}\n data-role={role}\n {...rest}\n data-slot={slot ?? \"message\"}\n />\n );\n};\n\n/**\n * **`role=\"list\"` on top of the `<ul>`, because `list-none` takes the implicit one away.** WebKit\n * drops list semantics from a list whose `list-style` is `none` — items included — so a styled\n * transcript announces nothing in VoiceOver. jsdom does not model it, so the role queries passed\n * throughout. See `DiagnosticList`, which carries the argument in full.\n */\nexport const MessageList = (props: React.ComponentProps<typeof ark.ul>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.ul\n className={cn(\"flex w-full list-none flex-col gap-6\", className)}\n role=\"list\"\n {...rest}\n data-slot={slot ?? \"message-list\"}\n />\n );\n};\n\nconst initials = (name?: string) =>\n (name ?? \"\")\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((word) => word[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n\nexport interface MessageAvatarProps extends React.ComponentProps<typeof Avatar> {\n src?: string;\n /** Names the image and, when there is no image, supplies the fallback's initials. */\n name?: string;\n}\n\nexport const MessageAvatar = (props: MessageAvatarProps) => {\n const { src, name, className, children, size = \"sm\", slot, ...rest } = props;\n\n return (\n <Avatar\n className={cn(\n \"shrink-0\",\n \"group-data-[role=system]/message:hidden\",\n className\n )}\n size={size}\n {...rest}\n slot={slot ?? \"message-avatar\"}\n >\n {children ?? (\n <>\n {src ? <AvatarImage alt={name ?? \"\"} src={src} /> : null}\n <AvatarFallback>{initials(name)}</AvatarFallback>\n </>\n )}\n </Avatar>\n );\n};\n\n/**\n * The turn's words. The two speakers are told apart by surface before they are read: the user gets\n * a bubble and the assistant the page itself. A system note takes neither — it is not a speaker.\n *\n * **One treatment per speaker, not two.** The bubble was `bg-muted` plus a border, and it needed\n * the border because `--muted` is one step off `--background` (#efefef on #fafafa) — a fill nobody\n * could see, rescued by an outline. `--secondary` is the step that shows on its own, which is the\n * fill the source uses for exactly this, and it takes the outline back out. The tucked corner went\n * with it: a radius the rest of the library does not use, pointing at an avatar that is already\n * beside it.\n *\n * **Two treatments, not three.** The system pill that used to be here is gone; see `Message`.\n */\nexport const MessageContent = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.div\n className={cn(\n \"flex min-w-0 flex-1 flex-col gap-2 leading-relaxed\",\n \"group-data-[role=user]/message:max-w-[85%] group-data-[role=user]/message:flex-none\",\n \"group-data-[role=user]/message:rounded-xl\",\n \"group-data-[role=user]/message:bg-secondary group-data-[role=user]/message:text-secondary-foreground\",\n \"group-data-[role=user]/message:px-3.5 group-data-[role=user]/message:py-2.5\",\n className\n )}\n {...rest}\n data-slot={slot ?? \"message-content\"}\n />\n );\n};\n\n/**\n * A row of affordances for the turn above it — copy, retry, a source. `basis-full` breaks it onto\n * its own line, which is what the root's `flex-wrap` is there for.\n *\n * Hidden until the turn is pointed at, so a transcript is words rather than a column of buttons;\n * revealed by focus as well as hover, and always present on a coarse pointer, where there is no\n * hover to reveal it with.\n */\nexport const MessageActions = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.div\n className={cn(\n \"flex basis-full items-center gap-1\",\n \"opacity-0 transition-opacity motion-reduce:transition-none!\",\n \"group-hover/message:opacity-100 group-focus-within/message:opacity-100\",\n \"pointer-coarse:opacity-100\",\n \"group-data-[role=user]/message:justify-end\",\n className\n )}\n {...rest}\n data-slot={slot ?? \"message-actions\"}\n />\n );\n};\n"],"names":["messageVariants","tv","Message","props","role","className","slot","rest","jsx","ark","cn","MessageList","initials","name","word","_a","MessageAvatar","src","children","size","Avatar","jsxs","Fragment","AvatarImage","AvatarFallback","MessageContent","MessageActions"],"mappings":";;;;AAOA,MAAMA,IAAkBC,EAAG;AAAA,EACzB,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF,UAAU;AAAA,IACR,MAAM;AAAA;AAAA;AAAA,MAGJ,MAAM;AAAA,MACN,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYX,QAAQ;AAAA,IAAA;AAAA,EACV;AAAA,EAEF,iBAAiB;AAAA,IACf,MAAM;AAAA,EAAA;AAEV,CAAC,GAcYC,IAAU,CAACC,MAAwB;AAC9C,QAAM,EAAE,MAAAC,IAAO,aAAa,WAAAC,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAEzD,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC,EAAGV,EAAgB,EAAE,MAAAI,EAAA,CAAM,GAAGC,CAAS;AAAA,MAClD,aAAWD;AAAA,MACV,GAAGG;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB,GAQaK,IAAc,CAACR,MAA+C;AACzE,QAAM,EAAE,WAAAE,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAErC,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC,EAAG,wCAAwCL,CAAS;AAAA,MAC/D,MAAK;AAAA,MACJ,GAAGE;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB,GAEMM,IAAW,CAACC,OACfA,KAAQ,IACN,MAAM,KAAK,EACX,OAAO,OAAO,EACd,MAAM,GAAG,CAAC,EACV,IAAI,CAACC,MAAA;;AAAS,WAAAC,IAAAD,EAAK,CAAC,MAAN,gBAAAC,EAAS,kBAAiB;AAAA,CAAE,EAC1C,KAAK,EAAE,GAQCC,IAAgB,CAACb,MAA8B;AAC1D,QAAM,EAAE,KAAAc,GAAK,MAAAJ,GAAM,WAAAR,GAAW,UAAAa,GAAU,MAAAC,IAAO,MAAM,MAAAb,GAAM,GAAGC,EAAA,IAASJ;AAEvE,SACE,gBAAAK;AAAA,IAACY;AAAA,IAAA;AAAA,MACC,WAAWV;AAAA,QACT;AAAA,QACA;AAAA,QACAL;AAAA,MAAA;AAAA,MAEF,MAAAc;AAAA,MACC,GAAGZ;AAAA,MACJ,MAAMD,KAAQ;AAAA,MAEb,eACC,gBAAAe,EAAAC,GAAA,EACG,UAAA;AAAA,QAAAL,sBAAOM,GAAA,EAAY,KAAKV,KAAQ,IAAI,KAAAI,GAAU,IAAK;AAAA,QACpD,gBAAAT,EAACgB,GAAA,EAAgB,UAAAZ,EAASC,CAAI,EAAA,CAAE;AAAA,MAAA,EAAA,CAClC;AAAA,IAAA;AAAA,EAAA;AAIR,GAeaY,IAAiB,CAACtB,MAAgD;AAC7E,QAAM,EAAE,WAAAE,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAErC,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAL;AAAA,MAAA;AAAA,MAED,GAAGE;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB,GAUaoB,IAAiB,CAACvB,MAAgD;AAC7E,QAAM,EAAE,WAAAE,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAErC,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAL;AAAA,MAAA;AAAA,MAED,GAAGE;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB;"}
1
+ {"version":3,"file":"message.js","sources":["../src/message.tsx"],"sourcesContent":["import { ark } from \"@ark-ui/react/factory\";\nimport type React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { Avatar, AvatarFallback, AvatarImage, cn } from \"@kanzo-tech/ui\";\n\nexport type MessageRole = \"user\" | \"assistant\" | \"system\";\n\nconst messageVariants = tv({\n base: [\n \"group/message\",\n \"flex w-full flex-wrap items-start gap-3\",\n \"text-sm\",\n ],\n variants: {\n role: {\n // Reversed rather than `justify-end`: the row packs against the reading end and the avatar\n // goes with it, and `row-reverse` is direction-aware where a physical side is not.\n user: \"flex-row-reverse\",\n assistant: \"\",\n // **No surface of its own, and that is the correction.** A system note used to be a centred\n // `rounded-full` pill painted by `MessageContent` — a shape the library already has, spelled\n // again, and one that cannot hold what it was given: a full-round pill around a wrapped\n // sentence is a stadium with dead corners, and `Badge`, the component it was imitating, is\n // `whitespace-nowrap` at a fixed `h-5` and could never have rendered it.\n //\n // AI Elements types `from` as the AI SDK's `UIMessage[\"role\"]` — system included — and gives\n // it no treatment at all: `is-user` or `is-assistant`, nothing else. So the role stays, the\n // invented surface goes, and a caller who wants the note to read as an aside composes an\n // `Alert` inside the row. That is a component with a wash, a border, an icon slot, an action\n // slot, `role=\"status\"` and the ability to wrap.\n system: \"\",\n },\n },\n defaultVariants: {\n role: \"assistant\",\n },\n});\n\nexport interface MessageProps\n extends Omit<React.ComponentProps<typeof ark.li>, \"role\">,\n VariantProps<typeof messageVariants> {}\n\n/**\n * One turn. The role is declared once, here, and every part below reads it off `data-role`\n * through a group selector — repeating it as a prop on each part is how the two drift.\n *\n * `role` is the speaker and not the ARIA attribute, which is why the HTML one is omitted rather\n * than merged: two meanings under one name, and the wrong one reaching the DOM, is worse than an\n * `<li>` nobody can re-role.\n */\nexport const Message = (props: MessageProps) => {\n const { role = \"assistant\", className, slot, ...rest } = props;\n\n return (\n <ark.li\n className={cn(messageVariants({ role }), className)}\n data-role={role}\n {...rest}\n data-slot={slot ?? \"message\"}\n />\n );\n};\n\n/**\n * **`role=\"list\"` on top of the `<ul>`, because `list-none` takes the implicit one away.** WebKit\n * drops list semantics from a list whose `list-style` is `none` — items included — so a styled\n * transcript announces nothing in VoiceOver. jsdom does not model it, so the role queries passed\n * throughout. See `DiagnosticList`, which carries the argument in full.\n */\nexport const MessageList = (props: React.ComponentProps<typeof ark.ul>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.ul\n className={cn(\"flex w-full list-none flex-col gap-6\", className)}\n role=\"list\"\n {...rest}\n data-slot={slot ?? \"message-list\"}\n />\n );\n};\n\nconst initials = (name?: string) =>\n (name ?? \"\")\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((word) => word[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n\nexport interface MessageAvatarProps extends React.ComponentProps<typeof Avatar> {\n src?: string;\n /** Names the image and, when there is no image, supplies the fallback's initials. */\n name?: string;\n}\n\nexport const MessageAvatar = (props: MessageAvatarProps) => {\n const { src, name, className, children, size = \"sm\", slot, ...rest } = props;\n\n return (\n <Avatar\n className={cn(\n \"shrink-0\",\n \"group-data-[role=system]/message:hidden\",\n className\n )}\n size={size}\n {...rest}\n slot={slot ?? \"message-avatar\"}\n >\n {children ?? (\n <>\n {src ? <AvatarImage alt={name ?? \"\"} src={src} /> : null}\n <AvatarFallback>{initials(name)}</AvatarFallback>\n </>\n )}\n </Avatar>\n );\n};\n\n/**\n * The turn's words. The two speakers are told apart by surface before they are read: the user gets\n * a bubble and the assistant the page itself. A system note takes neither — it is not a speaker.\n *\n * **One treatment per speaker, not two.** The bubble was `bg-muted` plus a border, and it needed\n * the border because `--muted` is one step off `--background` (#efefef on #fafafa) — a fill nobody\n * could see, rescued by an outline. `--secondary` is the step that shows on its own, which is the\n * fill the source uses for exactly this, and it takes the outline back out. The tucked corner went\n * with it: a radius the rest of the library does not use, pointing at an avatar that is already\n * beside it.\n *\n * **Two treatments, not three.** The system pill that used to be here is gone; see `Message`.\n */\nexport const MessageContent = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.div\n className={cn(\n \"flex min-w-0 flex-1 flex-col gap-2 leading-relaxed\",\n \"group-data-[role=user]/message:max-w-[85%] group-data-[role=user]/message:flex-none\",\n \"group-data-[role=user]/message:rounded-xl\",\n \"group-data-[role=user]/message:bg-secondary group-data-[role=user]/message:text-secondary-foreground\",\n \"group-data-[role=user]/message:px-3.5 group-data-[role=user]/message:py-2.5\",\n className\n )}\n {...rest}\n data-slot={slot ?? \"message-content\"}\n />\n );\n};\n\n/**\n * A row of affordances for the turn above it — copy, retry, a source. `basis-full` breaks it onto\n * its own line, which is what the root's `flex-wrap` is there for.\n *\n * Hidden until the turn is pointed at, so a transcript is words rather than a column of buttons;\n * revealed by focus as well as hover, and always present on a coarse pointer, where there is no\n * hover to reveal it with.\n */\nexport const MessageActions = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.div\n className={cn(\n \"flex basis-full items-center gap-1\",\n \"opacity-0 transition-opacity motion-reduce:transition-none!\",\n \"group-hover/message:opacity-100 group-focus-within/message:opacity-100\",\n \"pointer-coarse:opacity-100\",\n \"group-data-[role=user]/message:justify-end\",\n className\n )}\n {...rest}\n data-slot={slot ?? \"message-actions\"}\n />\n );\n};\n"],"names":["messageVariants","tv","Message","props","role","className","slot","rest","jsx","ark","cn","MessageList","MessageContent"],"mappings":";;;;AAOA,MAAMA,IAAkBC,EAAG;AAAA,EACzB,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF,UAAU;AAAA,IACR,MAAM;AAAA;AAAA;AAAA,MAGJ,MAAM;AAAA,MACN,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYX,QAAQ;AAAA,IAAA;AAAA,EACV;AAAA,EAEF,iBAAiB;AAAA,IACf,MAAM;AAAA,EAAA;AAEV,CAAC,GAcYC,IAAU,CAACC,MAAwB;AAC9C,QAAM,EAAE,MAAAC,IAAO,aAAa,WAAAC,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAEzD,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC,EAAGV,EAAgB,EAAE,MAAAI,EAAA,CAAM,GAAGC,CAAS;AAAA,MAClD,aAAWD;AAAA,MACV,GAAGG;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB,GAQaK,IAAc,CAACR,MAA+C;AACzE,QAAM,EAAE,WAAAE,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAErC,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC,EAAG,wCAAwCL,CAAS;AAAA,MAC/D,MAAK;AAAA,MACJ,GAAGE;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB,GAqDaM,IAAiB,CAACT,MAAgD;AAC7E,QAAM,EAAE,WAAAE,GAAW,MAAAC,GAAM,GAAGC,MAASJ;AAErC,SACE,gBAAAK;AAAA,IAACC,EAAI;AAAA,IAAJ;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAL;AAAA,MAAA;AAAA,MAED,GAAGE;AAAA,MACJ,aAAWD,KAAQ;AAAA,IAAA;AAAA,EAAA;AAGzB;"}
@@ -1,7 +1,7 @@
1
1
  import { ark } from '@ark-ui/react/factory';
2
2
  import { default as React } from 'react';
3
3
  import { InputGroupAddon, InputGroupButton, InputGroupTextarea } from '@kanzo-tech/ui';
4
- import { AiStatus } from './use-ai.js';
4
+ import { ChatStatus } from '@kanzo-tech/llm';
5
5
  /**
6
6
  * The composer: the library's `InputGroup` as a form, so Enter and the button are one path.
7
7
  *
@@ -24,7 +24,7 @@ export declare const PromptInputTextarea: (props: React.ComponentProps<typeof In
24
24
  */
25
25
  export declare const PromptInputToolbar: (props: React.ComponentProps<typeof InputGroupAddon>) => React.JSX.Element;
26
26
  export interface PromptInputSubmitProps extends React.ComponentProps<typeof InputGroupButton> {
27
- status?: AiStatus;
27
+ status?: ChatStatus;
28
28
  }
29
29
  export declare const PromptInputSubmit: (props: PromptInputSubmitProps) => React.JSX.Element;
30
30
  //# sourceMappingURL=prompt-input.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-input.d.ts","sourceRoot":"","sources":["../src/prompt-input.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAE5C,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAGL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,sBAUvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,kBAAkB,CAAC,sBAuBzF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,sBAiBrF,CAAC;AAqDF,MAAM,WAAW,sBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC;IACrD,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,eAAO,MAAM,iBAAiB,GAAI,OAAO,sBAAsB,sBA6B9D,CAAC"}
1
+ {"version":3,"file":"prompt-input.d.ts","sourceRoot":"","sources":["../src/prompt-input.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAE5C,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAGL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,sBAUvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,kBAAkB,CAAC,sBAuBzF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,sBAiBrF,CAAC;AAyDF,MAAM,WAAW,sBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC;IACrD,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,GAAI,OAAO,sBAAsB,sBA6B9D,CAAC"}
@@ -1,72 +1,70 @@
1
1
  "use client";
2
- import { jsx as a, jsxs as f } from "react/jsx-runtime";
3
- import { ark as b } from "@ark-ui/react/factory";
2
+ import { jsx as a, jsxs as b } from "react/jsx-runtime";
3
+ import { ark as f } from "@ark-ui/react/factory";
4
4
  import { RefreshCcwIcon as h, SquareIcon as x, SendIcon as I } from "lucide-react";
5
5
  import { tv as N } from "tailwind-variants";
6
- import { InputGroup as g, cn as l, InputGroupButton as S, InputGroupTextarea as v, InputGroupAddon as y, Spinner as w } from "@kanzo-tech/ui";
7
- const j = (o) => {
8
- const { className: e, slot: r, ...s } = o;
6
+ import { InputGroup as S, cn as c, InputGroupTextarea as g, InputGroupAddon as y, InputGroupButton as v, Spinner as T } from "@kanzo-tech/ui";
7
+ const q = (o) => {
8
+ const { className: r, slot: e, ...s } = o;
9
9
  return (
10
10
  // A composer is a panel rather than a one-line control, so it takes the next radius up and a
11
11
  // shadow that puts it on top of the page instead of flush with it.
12
- /* @__PURE__ */ a(g, { asChild: !0, className: l("rounded-xl shadow-xs/5", e), children: /* @__PURE__ */ a(b.form, { ...s, "data-slot": r ?? "prompt-input" }) })
12
+ /* @__PURE__ */ a(S, { asChild: !0, className: c("rounded-xl shadow-xs/5", r), children: /* @__PURE__ */ a(f.form, { ...s, "data-slot": e ?? "prompt-input" }) })
13
13
  );
14
- }, q = (o) => {
15
- const { className: e, onKeyDown: r, rows: s = 1, slot: n, ...i } = o;
14
+ }, B = (o) => {
15
+ const { className: r, onKeyDown: e, rows: s = 1, slot: n, ...i } = o;
16
16
  return /* @__PURE__ */ a(
17
- v,
17
+ g,
18
18
  {
19
- className: l("max-h-48 min-h-16", e),
19
+ className: c("max-h-48 min-h-16", r),
20
20
  onKeyDown: (t) => {
21
21
  var u;
22
- r == null || r(t), !t.defaultPrevented && (t.key !== "Enter" || t.shiftKey || t.nativeEvent.isComposing || (t.preventDefault(), (u = t.currentTarget.form) == null || u.requestSubmit()));
22
+ e == null || e(t), !t.defaultPrevented && (t.key !== "Enter" || t.shiftKey || t.nativeEvent.isComposing || (t.preventDefault(), (u = t.currentTarget.form) == null || u.requestSubmit()));
23
23
  },
24
24
  rows: s,
25
25
  ...i,
26
26
  slot: n ?? "prompt-input-textarea"
27
27
  }
28
28
  );
29
- }, B = (o) => {
30
- const { className: e, slot: r, ...s } = o;
29
+ }, D = (o) => {
30
+ const { className: r, slot: e, ...s } = o;
31
31
  return /* @__PURE__ */ a(
32
32
  y,
33
33
  {
34
34
  align: "block-end",
35
- className: l("min-w-0 flex-wrap gap-1", e),
35
+ className: c("min-w-0 flex-wrap gap-1", r),
36
36
  onClick: (n) => {
37
37
  var i, t;
38
38
  n.target.closest("button, a, input, textarea") || (t = (i = n.currentTarget.parentElement) == null ? void 0 : i.querySelector("textarea")) == null || t.focus();
39
39
  },
40
40
  ...s,
41
- slot: r ?? "prompt-input-toolbar"
41
+ slot: e ?? "prompt-input-toolbar"
42
42
  }
43
43
  );
44
- }, c = { icon: /* @__PURE__ */ a(I, {}), label: "Send" }, T = {
45
- idle: c,
46
- // An answer that has arrived leaves the composer ready for the next question, so `ready` and
47
- // `idle` are one button. They are two states of the *stream*, not two states of this control.
48
- ready: c,
49
- loading: {
50
- // The stop block inside the spinner it is stopping. A bare square changes only the glyph, and
51
- // at a glance across a panel that reads as the same idle button; the motion is what says the
52
- // answer is still arriving. `aria-hidden` on both — the accessible name already changed to
53
- // "Stop", and a `role="status"` inside a named button announces the state twice.
54
- icon: /* @__PURE__ */ f("span", { className: "relative flex size-4 items-center justify-center", children: [
55
- /* @__PURE__ */ a(w, { "aria-hidden": !0, className: "absolute size-4" }),
56
- /* @__PURE__ */ a(x, { "aria-hidden": !0, className: "size-2.5" })
57
- ] }),
58
- label: "Stop"
59
- },
44
+ }, w = { icon: /* @__PURE__ */ a(I, {}), label: "Send" }, l = {
45
+ // The stop block inside the spinner it is stopping. A bare square changes only the glyph, and
46
+ // at a glance across a panel that reads as the same idle button; the motion is what says the
47
+ // answer is still arriving. `aria-hidden` on both — the accessible name already changed to
48
+ // "Stop", and a `role="status"` inside a named button announces the state twice.
49
+ icon: /* @__PURE__ */ b("span", { className: "relative flex size-4 items-center justify-center", children: [
50
+ /* @__PURE__ */ a(T, { "aria-hidden": !0, className: "absolute size-4" }),
51
+ /* @__PURE__ */ a(x, { "aria-hidden": !0, className: "size-2.5" })
52
+ ] }),
53
+ label: "Stop"
54
+ }, P = {
55
+ ready: w,
56
+ submitted: l,
57
+ streaming: l,
60
58
  error: { icon: /* @__PURE__ */ a(h, {}), label: "Retry" }
61
- }, P = N({
59
+ }, k = N({
62
60
  // No transition of its own: `Button`'s base already carries `transition-all` and the
63
61
  // `motion-reduce` escape, and a narrower `transition-colors` here would replace both.
64
62
  base: ["ms-auto"],
65
63
  variants: {
66
64
  status: {
67
- idle: "",
68
65
  ready: "",
69
- loading: "",
66
+ submitted: "",
67
+ streaming: "",
70
68
  error: [
71
69
  "bg-destructive/7 hover:bg-destructive/14",
72
70
  "border-destructive/30",
@@ -76,38 +74,38 @@ const j = (o) => {
76
74
  }
77
75
  },
78
76
  defaultVariants: {
79
- status: "idle"
77
+ status: "ready"
80
78
  }
81
- }), D = (o) => {
79
+ }), R = (o) => {
82
80
  const {
83
- status: e = "idle",
84
- className: r,
81
+ status: r = "ready",
82
+ className: e,
85
83
  children: s,
86
84
  type: n = "submit",
87
85
  variant: i = "default",
88
86
  size: t = s === void 0 ? "icon-sm" : "sm",
89
87
  slot: u,
90
- ...p
91
- } = o, { icon: m, label: d } = T[e];
88
+ ...m
89
+ } = o, { icon: p, label: d } = P[r];
92
90
  return /* @__PURE__ */ a(
93
- S,
91
+ v,
94
92
  {
95
93
  "aria-label": s === void 0 ? d : void 0,
96
- className: l(P({ status: e }), r),
97
- "data-status": e,
94
+ className: c(k({ status: r }), e),
95
+ "data-status": r,
98
96
  size: t,
99
97
  type: n,
100
98
  variant: i,
101
- ...p,
99
+ ...m,
102
100
  slot: u ?? "prompt-input-submit",
103
- children: s ?? m
101
+ children: s ?? p
104
102
  }
105
103
  );
106
104
  };
107
105
  export {
108
- j as PromptInput,
109
- D as PromptInputSubmit,
110
- q as PromptInputTextarea,
111
- B as PromptInputToolbar
106
+ q as PromptInput,
107
+ R as PromptInputSubmit,
108
+ B as PromptInputTextarea,
109
+ D as PromptInputToolbar
112
110
  };
113
111
  //# sourceMappingURL=prompt-input.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-input.js","sources":["../src/prompt-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { ark } from \"@ark-ui/react/factory\";\nimport { RefreshCcwIcon, SendIcon, SquareIcon } from \"lucide-react\";\nimport type React from \"react\";\nimport { tv } from \"tailwind-variants\";\nimport {\n cn,\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupTextarea,\n Spinner,\n} from \"@kanzo-tech/ui\";\nimport type { AiStatus } from \"./use-ai.js\";\n\n/**\n * The composer: the library's `InputGroup` as a form, so Enter and the button are one path.\n *\n * `asChild` rather than a wrapping `<form>`: `InputGroup`'s recipe selects its own direct children\n * (`has-[>textarea]`, `has-[>[data-align=block-end]]`), and an element between the group and the\n * field silently unsets half of it.\n */\nexport const PromptInput = (props: React.ComponentProps<typeof ark.form>) => {\n const { className, slot, ...rest } = props;\n\n return (\n // A composer is a panel rather than a one-line control, so it takes the next radius up and a\n // shadow that puts it on top of the page instead of flush with it.\n <InputGroup asChild className={cn(\"rounded-xl shadow-xs/5\", className)}>\n <ark.form {...rest} data-slot={slot ?? \"prompt-input\"} />\n </InputGroup>\n );\n};\n\nexport const PromptInputTextarea = (props: React.ComponentProps<typeof InputGroupTextarea>) => {\n const { className, onKeyDown, rows = 1, slot, ...rest } = props;\n\n return (\n <InputGroupTextarea\n // `Textarea`'s base already sets `field-sizing-content`, so the field grows with what is\n // typed; the ceiling is what this adds. Without it a pasted document swallows the panel and\n // takes the transcript with it. Past 12rem the field scrolls and the composer stops moving.\n className={cn(\"max-h-48 min-h-16\", className)}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n // `isComposing` is the trap: an IME commits its candidate with Enter, and without this the\n // half-typed word is sent instead of finished.\n if (event.key !== \"Enter\" || event.shiftKey || event.nativeEvent.isComposing) return;\n event.preventDefault();\n event.currentTarget.form?.requestSubmit();\n }}\n rows={rows}\n {...rest}\n slot={slot ?? \"prompt-input-textarea\"}\n />\n );\n};\n\n/**\n * The strip of controls under the field — a model picker, an attachment, the ✨, the submit.\n *\n * `align=\"block-end\"` is not decoration: a popover trigger living here anchors to the composer, so\n * its surface opens over the text it writes. Hung off a label row above the field instead, it\n * opened against the panel's edge, far from the value. Nothing here may become `overflow-hidden`\n * for the same reason.\n *\n * It wraps rather than overflows, because everything in it is `shrink-0`: four controls in a narrow\n * panel pushed the submit past the composer's edge instead of taking a second line.\n */\nexport const PromptInputToolbar = (props: React.ComponentProps<typeof InputGroupAddon>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <InputGroupAddon\n align=\"block-end\"\n className={cn(\"min-w-0 flex-wrap gap-1\", className)}\n // The addon's own handler focuses an `input`, and a composer has a `textarea` — so the strip\n // carried `cursor-text` and gave no caret. Same guard: a press on a control is that control's.\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button, a, input, textarea\")) return;\n event.currentTarget.parentElement?.querySelector(\"textarea\")?.focus();\n }}\n {...rest}\n slot={slot ?? \"prompt-input-toolbar\"}\n />\n );\n};\n\nconst SEND = { icon: <SendIcon />, label: \"Send\" };\n\nconst SUBMIT: Record<AiStatus, { icon: React.ReactNode; label: string }> = {\n idle: SEND,\n // An answer that has arrived leaves the composer ready for the next question, so `ready` and\n // `idle` are one button. They are two states of the *stream*, not two states of this control.\n ready: SEND,\n loading: {\n // The stop block inside the spinner it is stopping. A bare square changes only the glyph, and\n // at a glance across a panel that reads as the same idle button; the motion is what says the\n // answer is still arriving. `aria-hidden` on both — the accessible name already changed to\n // \"Stop\", and a `role=\"status\"` inside a named button announces the state twice.\n icon: (\n <span className=\"relative flex size-4 items-center justify-center\">\n <Spinner aria-hidden className=\"absolute size-4\" />\n <SquareIcon aria-hidden className=\"size-2.5\" />\n </span>\n ),\n label: \"Stop\",\n },\n error: { icon: <RefreshCcwIcon />, label: \"Retry\" },\n};\n\n/**\n * One control in three looks, so the fill is what carries the state and the box never moves: same\n * slot, same size, same corner in all of them. `idle`, `ready` and `loading` share `Button`'s\n * `default` variant outright — the glyph is the whole difference — and `error` takes the wash\n * `Alert` uses for the same family, which is a button you can still press, not a disabled one.\n */\nconst promptInputSubmitVariants = tv({\n // No transition of its own: `Button`'s base already carries `transition-all` and the\n // `motion-reduce` escape, and a narrower `transition-colors` here would replace both.\n base: [\"ms-auto\"],\n variants: {\n status: {\n idle: \"\",\n ready: \"\",\n loading: \"\",\n error: [\n \"bg-destructive/7 hover:bg-destructive/14\",\n \"border-destructive/30\",\n \"text-destructive-foreground\",\n \"shadow-none\",\n ],\n },\n },\n defaultVariants: {\n status: \"idle\",\n },\n});\n\nexport interface PromptInputSubmitProps\n extends React.ComponentProps<typeof InputGroupButton> {\n status?: AiStatus;\n}\n\nexport const PromptInputSubmit = (props: PromptInputSubmitProps) => {\n const {\n status = \"idle\",\n className,\n children,\n type = \"submit\",\n variant = \"default\",\n size = children === undefined ? \"icon-sm\" : \"sm\",\n slot,\n ...rest\n } = props;\n const { icon, label } = SUBMIT[status];\n\n return (\n <InputGroupButton\n // Only where the button has no visible text of its own: a label over a legible one renames\n // it, which is worse than not having one.\n aria-label={children === undefined ? label : undefined}\n className={cn(promptInputSubmitVariants({ status }), className)}\n data-status={status}\n size={size}\n type={type}\n variant={variant}\n {...rest}\n slot={slot ?? \"prompt-input-submit\"}\n >\n {children ?? icon}\n </InputGroupButton>\n );\n};\n"],"names":[],"mappings":";;;;;;AAuBO;AACL;AAEA;AAAA;AAAA;AAAA;AAKE;AAEJ;AAGE;AAEA;AACE;AAAC;AAAA;AAI6C;;AAG1C;AAK0B;AAC5B;AACA;AACI;AACU;AAAA;AAGpB;AAcE;AAEA;AACE;AAAC;AAAA;AACO;AAC4C;;AAIhD;AAC8D;AAChE;AACI;AACU;AAAA;AAGpB;AAI2E;AACnE;AAAA;AAAA;AAGC;AACE;AAAA;AAAA;AAAA;AAAA;AAOH;AAAiD;AACJ;AAC/C;AAEK;AAAA;AAGX;AAQqC;AAAA;AAAA;AAGnB;AACN;AACA;AACA;AACC;AACE;AACF;AACL;AACA;AACA;AACA;AAAA;AACF;AACF;AAEe;AACP;AAEZ;AAQE;AAAM;AACK;AACT;AACA;AACO;AACG;AACkC;AAC5C;AACG;AAIL;AACE;AAAC;AAAA;AAG8C;AACiB;AACjD;AACb;AACA;AACA;AACI;AACU;AAED;AAAA;AAGnB;;;;;;;"}
1
+ {"version":3,"file":"prompt-input.js","sources":["../src/prompt-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { ark } from \"@ark-ui/react/factory\";\nimport { RefreshCcwIcon, SendIcon, SquareIcon } from \"lucide-react\";\nimport type React from \"react\";\nimport { tv } from \"tailwind-variants\";\nimport {\n cn,\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupTextarea,\n Spinner,\n} from \"@kanzo-tech/ui\";\nimport type { ChatStatus } from \"@kanzo-tech/llm\";\n\n/**\n * The composer: the library's `InputGroup` as a form, so Enter and the button are one path.\n *\n * `asChild` rather than a wrapping `<form>`: `InputGroup`'s recipe selects its own direct children\n * (`has-[>textarea]`, `has-[>[data-align=block-end]]`), and an element between the group and the\n * field silently unsets half of it.\n */\nexport const PromptInput = (props: React.ComponentProps<typeof ark.form>) => {\n const { className, slot, ...rest } = props;\n\n return (\n // A composer is a panel rather than a one-line control, so it takes the next radius up and a\n // shadow that puts it on top of the page instead of flush with it.\n <InputGroup asChild className={cn(\"rounded-xl shadow-xs/5\", className)}>\n <ark.form {...rest} data-slot={slot ?? \"prompt-input\"} />\n </InputGroup>\n );\n};\n\nexport const PromptInputTextarea = (props: React.ComponentProps<typeof InputGroupTextarea>) => {\n const { className, onKeyDown, rows = 1, slot, ...rest } = props;\n\n return (\n <InputGroupTextarea\n // `Textarea`'s base already sets `field-sizing-content`, so the field grows with what is\n // typed; the ceiling is what this adds. Without it a pasted document swallows the panel and\n // takes the transcript with it. Past 12rem the field scrolls and the composer stops moving.\n className={cn(\"max-h-48 min-h-16\", className)}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n // `isComposing` is the trap: an IME commits its candidate with Enter, and without this the\n // half-typed word is sent instead of finished.\n if (event.key !== \"Enter\" || event.shiftKey || event.nativeEvent.isComposing) return;\n event.preventDefault();\n event.currentTarget.form?.requestSubmit();\n }}\n rows={rows}\n {...rest}\n slot={slot ?? \"prompt-input-textarea\"}\n />\n );\n};\n\n/**\n * The strip of controls under the field — a model picker, an attachment, the ✨, the submit.\n *\n * `align=\"block-end\"` is not decoration: a popover trigger living here anchors to the composer, so\n * its surface opens over the text it writes. Hung off a label row above the field instead, it\n * opened against the panel's edge, far from the value. Nothing here may become `overflow-hidden`\n * for the same reason.\n *\n * It wraps rather than overflows, because everything in it is `shrink-0`: four controls in a narrow\n * panel pushed the submit past the composer's edge instead of taking a second line.\n */\nexport const PromptInputToolbar = (props: React.ComponentProps<typeof InputGroupAddon>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <InputGroupAddon\n align=\"block-end\"\n className={cn(\"min-w-0 flex-wrap gap-1\", className)}\n // The addon's own handler focuses an `input`, and a composer has a `textarea` — so the strip\n // carried `cursor-text` and gave no caret. Same guard: a press on a control is that control's.\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button, a, input, textarea\")) return;\n event.currentTarget.parentElement?.querySelector(\"textarea\")?.focus();\n }}\n {...rest}\n slot={slot ?? \"prompt-input-toolbar\"}\n />\n );\n};\n\nconst SEND = { icon: <SendIcon />, label: \"Send\" };\n\nconst STOP = {\n // The stop block inside the spinner it is stopping. A bare square changes only the glyph, and\n // at a glance across a panel that reads as the same idle button; the motion is what says the\n // answer is still arriving. `aria-hidden` on both — the accessible name already changed to\n // \"Stop\", and a `role=\"status\"` inside a named button announces the state twice.\n icon: (\n <span className=\"relative flex size-4 items-center justify-center\">\n <Spinner aria-hidden className=\"absolute size-4\" />\n <SquareIcon aria-hidden className=\"size-2.5\" />\n </span>\n ),\n label: \"Stop\",\n};\n\n/**\n * The AI SDK's `ChatStatus`, drawn. `submitted` (asked, nothing back yet) and `streaming` are one\n * button: both are an answer that can be stopped.\n */\nconst SUBMIT: Record<ChatStatus, { icon: React.ReactNode; label: string }> = {\n ready: SEND,\n submitted: STOP,\n streaming: STOP,\n error: { icon: <RefreshCcwIcon />, label: \"Retry\" },\n};\n\n/**\n * One control in three looks, so the fill is what carries the state and the box never moves: same\n * slot, same size, same corner in all of them. `ready`, `submitted` and `streaming` share `Button`'s\n * `default` variant outright — the glyph is the whole difference — and `error` takes the wash\n * `Alert` uses for the same family, which is a button you can still press, not a disabled one.\n */\nconst promptInputSubmitVariants = tv({\n // No transition of its own: `Button`'s base already carries `transition-all` and the\n // `motion-reduce` escape, and a narrower `transition-colors` here would replace both.\n base: [\"ms-auto\"],\n variants: {\n status: {\n ready: \"\",\n submitted: \"\",\n streaming: \"\",\n error: [\n \"bg-destructive/7 hover:bg-destructive/14\",\n \"border-destructive/30\",\n \"text-destructive-foreground\",\n \"shadow-none\",\n ],\n },\n },\n defaultVariants: {\n status: \"ready\",\n },\n});\n\nexport interface PromptInputSubmitProps\n extends React.ComponentProps<typeof InputGroupButton> {\n status?: ChatStatus;\n}\n\nexport const PromptInputSubmit = (props: PromptInputSubmitProps) => {\n const {\n status = \"ready\",\n className,\n children,\n type = \"submit\",\n variant = \"default\",\n size = children === undefined ? \"icon-sm\" : \"sm\",\n slot,\n ...rest\n } = props;\n const { icon, label } = SUBMIT[status];\n\n return (\n <InputGroupButton\n // Only where the button has no visible text of its own: a label over a legible one renames\n // it, which is worse than not having one.\n aria-label={children === undefined ? label : undefined}\n className={cn(promptInputSubmitVariants({ status }), className)}\n data-status={status}\n size={size}\n type={type}\n variant={variant}\n {...rest}\n slot={slot ?? \"prompt-input-submit\"}\n >\n {children ?? icon}\n </InputGroupButton>\n );\n};\n"],"names":[],"mappings":";;;;;;AAuBO;AACL;AAEA;AAAA;AAAA;AAAA;AAKE;AAEJ;AAGE;AAEA;AACE;AAAC;AAAA;AAI6C;;AAG1C;AAK0B;AAC5B;AACA;AACI;AACU;AAAA;AAGpB;AAcE;AAEA;AACE;AAAC;AAAA;AACO;AAC4C;;AAIhD;AAC8D;AAChE;AACI;AACU;AAAA;AAGpB;AAIa;AAAA;AAAA;AAAA;AAAA;AAOP;AAAiD;AACJ;AAC/C;AAGJ;AAM6E;AACpE;AACI;AACA;AAEb;AAQqC;AAAA;AAAA;AAGnB;AACN;AACA;AACC;AACI;AACA;AACJ;AACL;AACA;AACA;AACA;AAAA;AACF;AACF;AAEe;AACP;AAEZ;AAQE;AAAM;AACK;AACT;AACA;AACO;AACG;AACkC;AAC5C;AACG;AAIL;AACE;AAAC;AAAA;AAG8C;AACiB;AACjD;AACb;AACA;AACA;AACI;AACU;AAED;AAAA;AAGnB;;;;;;;"}
@@ -0,0 +1,28 @@
1
+ import { LanguageModel } from '@kanzo-tech/llm';
2
+ import { ContinuationRequest, Proposal } from './engine.js';
3
+ /** What the model is told about the field it is filling. */
4
+ export interface FieldBrief {
5
+ /** The accessible name — the label a person reads. */
6
+ name: string;
7
+ /** The accessible description — the helper text under it. */
8
+ description?: string;
9
+ /** The host's own instruction for this field (`<Assist instructions>`). */
10
+ instructions?: string;
11
+ /** What the rest of the form says (`<AssistProvider context>`). */
12
+ context?: string;
13
+ }
14
+ /** One continuation at the caret, streamed. */
15
+ export declare function continuation(model: LanguageModel, field: FieldBrief, { value, position, trigger, avoid, signal }: ContinuationRequest): AsyncIterable<string>;
16
+ /**
17
+ * Whole values for the field, each arriving as soon as it is complete (`elementStream`), so the
18
+ * first candidate is on screen while the model writes the rest.
19
+ */
20
+ export declare function candidates(model: LanguageModel, field: FieldBrief, { value, existing, signal, count, list }: {
21
+ value: string;
22
+ existing: string[];
23
+ signal: AbortSignal;
24
+ count: number;
25
+ /** The field holds a list (tags): a candidate is one more item, not a replacement. */
26
+ list: boolean;
27
+ }): AsyncIterable<Proposal>;
28
+ //# sourceMappingURL=proposals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.d.ts","sourceRoot":"","sources":["../src/proposals.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAkC,MAAM,iBAAiB,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjE,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgBD,+CAA+C;AAC/C,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,UAAU,EACjB,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,mBAAmB,GAC/D,aAAa,CAAC,MAAM,CAAC,CAWvB;AAcD;;;GAGG;AACH,wBAAuB,UAAU,CAC/B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,UAAU,EACjB,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,IAAI,EAAE,OAAO,CAAC;CACf,GACA,aAAa,CAAC,QAAQ,CAAC,CAgBzB"}
@@ -0,0 +1,56 @@
1
+ import { streamText as u, Output as p, jsonSchema as h } from "@kanzo-tech/llm";
2
+ const m = (e) => [
3
+ `Field: ${e.name || "(unnamed)"}`,
4
+ e.description && `Field description: ${e.description}`,
5
+ e.instructions && `Instructions: ${e.instructions}`,
6
+ e.context && `Form context:
7
+ ${e.context}`
8
+ ].filter(Boolean).join(`
9
+ `), d = `You complete text inside a form field, like an editor's inline completion.
10
+ Reply with ONLY the text to insert at <caret/>: no quotes, no commentary, never repeat text that is already there.
11
+ Keep the language, tone and format of the existing text. Stop at a natural point: the end of the sentence for an automatic suggestion; up to a short paragraph when explicitly asked.`;
12
+ function y(e, i, { value: t, position: o, trigger: s, avoid: r, signal: a }) {
13
+ const l = `${t.slice(0, o)}<caret/>${t.slice(o)}`, c = r.length ? `
14
+ Offer something different from these earlier suggestions:
15
+ ${r.map((n) => `- ${n.trim()}`).join(`
16
+ `)}` : "";
17
+ return u({
18
+ model: e,
19
+ system: d,
20
+ prompt: `${m(i)}
21
+ Request: ${s}${c}
22
+
23
+ Text:
24
+ ${l}`,
25
+ abortSignal: a
26
+ }).textStream;
27
+ }
28
+ const f = h({
29
+ type: "object",
30
+ properties: {
31
+ value: { type: "string", description: "The whole value, exactly as it would be entered." },
32
+ rationale: { type: "string", description: "Why it fits, in one short sentence." }
33
+ },
34
+ required: ["value", "rationale"],
35
+ additionalProperties: !1
36
+ }), g = "You suggest values for a form field. Each value must be complete and ready to enter as-is, in the language of the form.";
37
+ async function* x(e, i, { value: t, existing: o, signal: s, count: r, list: a }) {
38
+ const l = a ? `Current items: ${o.length ? o.join(", ") : "(none)"}
39
+ Suggest NEW items, not already present.` : `Current value: ${t || "(empty)"}
40
+ Suggest alternatives to replace it${t ? ", different from it" : ""}.`, c = u({
41
+ model: e,
42
+ system: g,
43
+ prompt: `${m(i)}
44
+ ${l}
45
+ Give ${r}.`,
46
+ output: p.array({ element: f }),
47
+ abortSignal: s
48
+ });
49
+ for await (const n of c.elementStream)
50
+ yield a ? { text: n.value, rationale: n.rationale } : { text: n.value, rationale: n.rationale, range: [0, t.length] };
51
+ }
52
+ export {
53
+ x as candidates,
54
+ y as continuation
55
+ };
56
+ //# sourceMappingURL=proposals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.js","sources":["../src/proposals.ts"],"sourcesContent":["// What a field asks the model, written once. A host names the model and, at most, a sentence of\n// instructions; the field describes itself through its accessible name and description, which is\n// what a person filling it in reads too.\n\nimport { type LanguageModel, Output, jsonSchema, streamText } from \"@kanzo-tech/llm\";\nimport type { ContinuationRequest, Proposal } from \"./engine.js\";\n\n/** What the model is told about the field it is filling. */\nexport interface FieldBrief {\n /** The accessible name — the label a person reads. */\n name: string;\n /** The accessible description — the helper text under it. */\n description?: string;\n /** The host's own instruction for this field (`<Assist instructions>`). */\n instructions?: string;\n /** What the rest of the form says (`<AssistProvider context>`). */\n context?: string;\n}\n\nconst brief = (field: FieldBrief) =>\n [\n `Field: ${field.name || \"(unnamed)\"}`,\n field.description && `Field description: ${field.description}`,\n field.instructions && `Instructions: ${field.instructions}`,\n field.context && `Form context:\\n${field.context}`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\nconst CONTINUE = `You complete text inside a form field, like an editor's inline completion.\nReply with ONLY the text to insert at <caret/>: no quotes, no commentary, never repeat text that is already there.\nKeep the language, tone and format of the existing text. Stop at a natural point: the end of the sentence for an automatic suggestion; up to a short paragraph when explicitly asked.`;\n\n/** One continuation at the caret, streamed. */\nexport function continuation(\n model: LanguageModel,\n field: FieldBrief,\n { value, position, trigger, avoid, signal }: ContinuationRequest,\n): AsyncIterable<string> {\n const text = `${value.slice(0, position)}<caret/>${value.slice(position)}`;\n const differ = avoid.length\n ? `\\nOffer something different from these earlier suggestions:\\n${avoid.map((a) => `- ${a.trim()}`).join(\"\\n\")}`\n : \"\";\n return streamText({\n model,\n system: CONTINUE,\n prompt: `${brief(field)}\\nRequest: ${trigger}${differ}\\n\\nText:\\n${text}`,\n abortSignal: signal,\n }).textStream;\n}\n\nconst CANDIDATE = jsonSchema<{ value: string; rationale: string }>({\n type: \"object\",\n properties: {\n value: { type: \"string\", description: \"The whole value, exactly as it would be entered.\" },\n rationale: { type: \"string\", description: \"Why it fits, in one short sentence.\" },\n },\n required: [\"value\", \"rationale\"],\n additionalProperties: false,\n});\n\nconst CANDIDATES = `You suggest values for a form field. Each value must be complete and ready to enter as-is, in the language of the form.`;\n\n/**\n * Whole values for the field, each arriving as soon as it is complete (`elementStream`), so the\n * first candidate is on screen while the model writes the rest.\n */\nexport async function* candidates(\n model: LanguageModel,\n field: FieldBrief,\n { value, existing, signal, count, list }: {\n value: string;\n existing: string[];\n signal: AbortSignal;\n count: number;\n /** The field holds a list (tags): a candidate is one more item, not a replacement. */\n list: boolean;\n },\n): AsyncIterable<Proposal> {\n const holds = list\n ? `Current items: ${existing.length ? existing.join(\", \") : \"(none)\"}\\nSuggest NEW items, not already present.`\n : `Current value: ${value || \"(empty)\"}\\nSuggest alternatives to replace it${value ? \", different from it\" : \"\"}.`;\n const result = streamText({\n model,\n system: CANDIDATES,\n prompt: `${brief(field)}\\n${holds}\\nGive ${count}.`,\n output: Output.array({ element: CANDIDATE }),\n abortSignal: signal,\n });\n for await (const item of result.elementStream) {\n yield list\n ? { text: item.value, rationale: item.rationale }\n : { text: item.value, rationale: item.rationale, range: [0, value.length] };\n }\n}\n"],"names":["brief","field","CONTINUE","continuation","model","value","position","trigger","avoid","signal","text","differ","a","streamText","CANDIDATE","jsonSchema","CANDIDATES","candidates","existing","count","list","holds","result","Output","item"],"mappings":";AAmBA,MAAMA,IAAQ,CAACC,MACb;AAAA,EACE,UAAUA,EAAM,QAAQ,WAAW;AAAA,EACnCA,EAAM,eAAe,sBAAsBA,EAAM,WAAW;AAAA,EAC5DA,EAAM,gBAAgB,iBAAiBA,EAAM,YAAY;AAAA,EACzDA,EAAM,WAAW;AAAA,EAAkBA,EAAM,OAAO;AAClD,EACG,OAAO,OAAO,EACd,KAAK;AAAA,CAAI,GAERC,IAAW;AAAA;AAAA;AAKV,SAASC,EACdC,GACAH,GACA,EAAE,OAAAI,GAAO,UAAAC,GAAU,SAAAC,GAAS,OAAAC,GAAO,QAAAC,KACZ;AACvB,QAAMC,IAAO,GAAGL,EAAM,MAAM,GAAGC,CAAQ,CAAC,WAAWD,EAAM,MAAMC,CAAQ,CAAC,IAClEK,IAASH,EAAM,SACjB;AAAA;AAAA,EAAgEA,EAAM,IAAI,CAACI,MAAM,KAAKA,EAAE,KAAA,CAAM,EAAE,EAAE,KAAK;AAAA,CAAI,CAAC,KAC5G;AACJ,SAAOC,EAAW;AAAA,IAChB,OAAAT;AAAA,IACA,QAAQF;AAAA,IACR,QAAQ,GAAGF,EAAMC,CAAK,CAAC;AAAA,WAAcM,CAAO,GAAGI,CAAM;AAAA;AAAA;AAAA,EAAcD,CAAI;AAAA,IACvE,aAAaD;AAAA,EAAA,CACd,EAAE;AACL;AAEA,MAAMK,IAAYC,EAAiD;AAAA,EACjE,MAAM;AAAA,EACN,YAAY;AAAA,IACV,OAAO,EAAE,MAAM,UAAU,aAAa,mDAAA;AAAA,IACtC,WAAW,EAAE,MAAM,UAAU,aAAa,sCAAA;AAAA,EAAsC;AAAA,EAElF,UAAU,CAAC,SAAS,WAAW;AAAA,EAC/B,sBAAsB;AACxB,CAAC,GAEKC,IAAa;AAMnB,gBAAuBC,EACrBb,GACAH,GACA,EAAE,OAAAI,GAAO,UAAAa,GAAU,QAAAT,GAAQ,OAAAU,GAAO,MAAAC,KAQT;AACzB,QAAMC,IAAQD,IACV,kBAAkBF,EAAS,SAASA,EAAS,KAAK,IAAI,IAAI,QAAQ;AAAA,2CAClE,kBAAkBb,KAAS,SAAS;AAAA,oCAAuCA,IAAQ,wBAAwB,EAAE,KAC3GiB,IAAST,EAAW;AAAA,IACxB,OAAAT;AAAA,IACA,QAAQY;AAAA,IACR,QAAQ,GAAGhB,EAAMC,CAAK,CAAC;AAAA,EAAKoB,CAAK;AAAA,OAAUF,CAAK;AAAA,IAChD,QAAQI,EAAO,MAAM,EAAE,SAAST,GAAW;AAAA,IAC3C,aAAaL;AAAA,EAAA,CACd;AACD,mBAAiBe,KAAQF,EAAO;AAC9B,UAAMF,IACF,EAAE,MAAMI,EAAK,OAAO,WAAWA,EAAK,cACpC,EAAE,MAAMA,EAAK,OAAO,WAAWA,EAAK,WAAW,OAAO,CAAC,GAAGnB,EAAM,MAAM,EAAA;AAE9E;"}
@@ -1,8 +1,9 @@
1
1
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@kanzo-tech/ui';
2
+ import { ReasoningUIPart } from '@kanzo-tech/llm';
2
3
  import * as React from "react";
3
- export interface ReasoningProps extends React.ComponentProps<typeof Collapsible> {
4
- /** The model is still thinking. */
5
- streaming?: boolean;
4
+ export interface ReasoningProps extends Omit<React.ComponentProps<typeof Collapsible>, "part"> {
5
+ /** The reasoning as the AI SDK streams it: its text, and whether it is still arriving. */
6
+ part: ReasoningUIPart;
6
7
  }
7
8
  /**
8
9
  * A model's thinking, folded away.
@@ -1 +1 @@
1
- {"version":3,"file":"reasoning.d.ts","sourceRoot":"","sources":["../src/reasoning.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAEL,WAAW,EACX,kBAAkB,EAElB,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AAexB,MAAM,WAAW,cAAe,SAAQ,KAAK,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC;IAC9E,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,cAAc,sBAmE9C,CAAC;AAkBF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,kBAAkB,CAAC,sBAwBtF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,kBAAkB,CAAC,sBAiBtF,CAAC"}
1
+ {"version":3,"file":"reasoning.d.ts","sourceRoot":"","sources":["../src/reasoning.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAEL,WAAW,EACX,kBAAkB,EAElB,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAgBvD,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC,EAAE,MAAM,CAAC;IAC5F,0FAA0F;IAC1F,IAAI,EAAE,eAAe,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,cAAc,sBAyE9C,CAAC;AAkBF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,kBAAkB,CAAC,sBAwBtF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,kBAAkB,CAAC,sBAoBtF,CAAC"}