@copilotkit/channels-ui 0.1.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/dist/bind.d.ts +6 -0
  4. package/dist/bind.d.ts.map +1 -0
  5. package/dist/bind.js +18 -0
  6. package/dist/bind.test.d.ts +2 -0
  7. package/dist/bind.test.d.ts.map +1 -0
  8. package/dist/bind.test.js +14 -0
  9. package/dist/components.d.ts +154 -0
  10. package/dist/components.d.ts.map +1 -0
  11. package/dist/components.js +32 -0
  12. package/dist/components.test.d.ts +2 -0
  13. package/dist/components.test.d.ts.map +1 -0
  14. package/dist/components.test.js +89 -0
  15. package/dist/emoji.d.ts +104 -0
  16. package/dist/emoji.d.ts.map +1 -0
  17. package/dist/emoji.js +77 -0
  18. package/dist/emoji.test.d.ts +2 -0
  19. package/dist/emoji.test.d.ts.map +1 -0
  20. package/dist/emoji.test.js +63 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +7 -0
  24. package/dist/ir.d.ts +11 -0
  25. package/dist/ir.d.ts.map +1 -0
  26. package/dist/ir.js +1 -0
  27. package/dist/jsx-dev-runtime.d.ts +7 -0
  28. package/dist/jsx-dev-runtime.d.ts.map +1 -0
  29. package/dist/jsx-dev-runtime.js +4 -0
  30. package/dist/jsx-runtime.d.ts +30 -0
  31. package/dist/jsx-runtime.d.ts.map +1 -0
  32. package/dist/jsx-runtime.js +6 -0
  33. package/dist/jsx-runtime.test.d.ts +2 -0
  34. package/dist/jsx-runtime.test.d.ts.map +1 -0
  35. package/dist/jsx-runtime.test.js +17 -0
  36. package/dist/modal.d.ts +60 -0
  37. package/dist/modal.d.ts.map +1 -0
  38. package/dist/modal.js +13 -0
  39. package/dist/modal.test.d.ts +2 -0
  40. package/dist/modal.test.d.ts.map +1 -0
  41. package/dist/modal.test.js +29 -0
  42. package/dist/render.d.ts +3 -0
  43. package/dist/render.d.ts.map +1 -0
  44. package/dist/render.js +37 -0
  45. package/dist/render.test.d.ts +2 -0
  46. package/dist/render.test.d.ts.map +1 -0
  47. package/dist/render.test.js +39 -0
  48. package/dist/types.d.ts +212 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +1 -0
  51. package/package.json +61 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emoji.d.ts","sourceRoot":"","sources":["../src/emoji.ts"],"names":[],"mappings":"AAEA,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,0EAA0E;AAC1E,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BrB,CAAC;AAEJ,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAEpD,8EAA8E;AAC9E,eAAO,MAAM,KAAK,EAEb,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAkBpC;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,aAAa,GACtB,MAAM,GAAG,SAAS,CAQpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAQ9D;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,aAAa,GACtB,UAAU,GAAG,SAAS,CAKxB"}
package/dist/emoji.js ADDED
@@ -0,0 +1,77 @@
1
+ // packages/channels-ui/src/emoji.ts
2
+ /** Starter set; unknown emoji pass through unnormalized as `rawEmoji`. */
3
+ export const EMOJI_TABLE = [
4
+ { name: "thumbs_up", unicode: "👍", slack: ["+1", "thumbsup"] },
5
+ { name: "thumbs_down", unicode: "👎", slack: ["-1", "thumbsdown"] },
6
+ { name: "heart", unicode: "❤️", slack: ["heart"] },
7
+ { name: "fire", unicode: "🔥", slack: ["fire"] },
8
+ { name: "eyes", unicode: "👀", slack: ["eyes"] },
9
+ { name: "bug", unicode: "🐛", slack: ["bug"] },
10
+ {
11
+ name: "check",
12
+ unicode: "✅",
13
+ slack: ["white_check_mark", "heavy_check_mark"],
14
+ },
15
+ { name: "cross", unicode: "❌", slack: ["x"] },
16
+ { name: "tada", unicode: "🎉", slack: ["tada"] },
17
+ { name: "rocket", unicode: "🚀", slack: ["rocket"] },
18
+ { name: "warning", unicode: "⚠️", slack: ["warning"] },
19
+ { name: "question", unicode: "❓", slack: ["question"] },
20
+ { name: "raised_hands", unicode: "🙌", slack: ["raised_hands"] },
21
+ { name: "clap", unicode: "👏", slack: ["clap"] },
22
+ { name: "pray", unicode: "🙏", slack: ["pray"] },
23
+ { name: "smile", unicode: "😄", slack: ["smile"] },
24
+ { name: "thinking", unicode: "🤔", slack: ["thinking_face"] },
25
+ ];
26
+ /** Typed accessor map, e.g. `emoji.thumbs_up`. Each value is its own name. */
27
+ export const emoji = Object.freeze(Object.fromEntries(EMOJI_TABLE.map((e) => [e.name, e.name])));
28
+ /** Strip the Unicode variation selector U+FE0F (VS16) wherever it appears. */
29
+ const stripVs16 = (token) => token.replace(/\uFE0F/g, "");
30
+ const byName = new Map(EMOJI_TABLE.map((e) => [e.name, e]));
31
+ const slackToName = new Map();
32
+ const unicodeToName = new Map();
33
+ for (const e of EMOJI_TABLE) {
34
+ unicodeToName.set(e.unicode, e.name);
35
+ // Also index the VS16-stripped form so a bare codepoint (e.g. "❤" without the
36
+ // trailing U+FE0F that the table stores) normalizes to the same name.
37
+ unicodeToName.set(stripVs16(e.unicode), e.name);
38
+ for (const code of e.slack)
39
+ slackToName.set(code, e.name);
40
+ }
41
+ /**
42
+ * Resolves any known emoji form — canonical name, Slack shortcode/alias, or
43
+ * Unicode token — to the platform-native token, or `undefined` if unknown.
44
+ */
45
+ export function toPlatformEmoji(value, platform) {
46
+ // Accept any legal `EmojiValue`: canonical name, Slack alias, or Unicode token.
47
+ const name = byName.has(value)
48
+ ? value
49
+ : (slackToName.get(value) ?? unicodeToName.get(value));
50
+ const entry = name ? byName.get(name) : undefined;
51
+ if (!entry)
52
+ return undefined;
53
+ return platform === "slack" ? entry.slack[0] : entry.unicode;
54
+ }
55
+ /**
56
+ * Resolve any known emoji form — canonical name, Slack shortcode/alias, or
57
+ * Unicode token (with or without VS16) — to its canonical name,
58
+ * platform-agnostically. Unknown tokens (e.g. custom/server emoji) pass through
59
+ * unchanged. Used to normalize caller-supplied reaction filters so they match
60
+ * the canonical names ingress produces.
61
+ */
62
+ export function toCanonicalEmoji(value) {
63
+ if (byName.has(value))
64
+ return value;
65
+ return (slackToName.get(value) ??
66
+ unicodeToName.get(value) ??
67
+ unicodeToName.get(stripVs16(value)) ??
68
+ value);
69
+ }
70
+ /** Platform-native token → canonical name, or `undefined` if unrecognized. */
71
+ export function normalizeEmoji(token, platform) {
72
+ if (platform === "slack")
73
+ return slackToName.get(token);
74
+ // Discord/Telegram: try the token as-is, then retry with VS16 stripped, since
75
+ // the platform may deliver/cache a bare codepoint without the table's U+FE0F.
76
+ return unicodeToName.get(token) ?? unicodeToName.get(stripVs16(token));
77
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=emoji.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emoji.test.d.ts","sourceRoot":"","sources":["../src/emoji.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,63 @@
1
+ // packages/channels-ui/src/emoji.test.ts
2
+ import { describe, it, expect } from "vitest";
3
+ import { emoji, toPlatformEmoji, toCanonicalEmoji, normalizeEmoji, EMOJI_TABLE, } from "./emoji.js";
4
+ describe("emoji table", () => {
5
+ it("exposes typed canonical names via the `emoji` map", () => {
6
+ expect(emoji.thumbs_up).toBe("thumbs_up");
7
+ expect(emoji.heart).toBe("heart");
8
+ });
9
+ it("round-trips every entry through each platform", () => {
10
+ for (const e of EMOJI_TABLE) {
11
+ // Slack egress uses the first shortcode; ingress normalizes any alias.
12
+ expect(toPlatformEmoji(e.name, "slack")).toBe(e.slack[0]);
13
+ for (const code of e.slack) {
14
+ expect(normalizeEmoji(code, "slack")).toBe(e.name);
15
+ }
16
+ // Discord + Telegram use the Unicode token both ways.
17
+ expect(toPlatformEmoji(e.name, "discord")).toBe(e.unicode);
18
+ expect(toPlatformEmoji(e.name, "telegram")).toBe(e.unicode);
19
+ expect(normalizeEmoji(e.unicode, "discord")).toBe(e.name);
20
+ expect(normalizeEmoji(e.unicode, "telegram")).toBe(e.name);
21
+ }
22
+ });
23
+ it("resolves cross-form egress inputs (unicode + slack alias)", () => {
24
+ // Unicode token → Slack shortcode.
25
+ expect(toPlatformEmoji("👍", "slack")).toBe("+1");
26
+ // Slack alias → canonical Slack shortcode.
27
+ expect(toPlatformEmoji("thumbsup", "slack")).toBe("+1");
28
+ // Unicode token → Discord stays Unicode.
29
+ expect(toPlatformEmoji("👍", "discord")).toBe("👍");
30
+ // Canonical name still works.
31
+ expect(toPlatformEmoji(emoji.thumbs_up, "slack")).toBe("+1");
32
+ // Unknown / custom emoji still passes through as undefined.
33
+ expect(toPlatformEmoji("party_parrot", "slack")).toBeUndefined();
34
+ });
35
+ it("tolerates a missing trailing variation selector (U+FE0F) on ingress", () => {
36
+ // Discord/Telegram sometimes deliver/cache the bare codepoint without the
37
+ // VS16 the table stores. Both the qualified and bare forms must normalize.
38
+ expect(normalizeEmoji("❤️", "discord")).toBe("heart"); // qualified (U+2764 U+FE0F)
39
+ expect(normalizeEmoji("❤", "discord")).toBe("heart"); // bare (U+2764)
40
+ expect(normalizeEmoji("⚠️", "telegram")).toBe("warning"); // qualified (U+26A0 U+FE0F)
41
+ expect(normalizeEmoji("⚠", "telegram")).toBe("warning"); // bare (U+26A0)
42
+ });
43
+ it("normalizes the bug emoji across platforms", () => {
44
+ expect(normalizeEmoji("🐛", "discord")).toBe("bug");
45
+ expect(normalizeEmoji("bug", "slack")).toBe("bug");
46
+ expect(toPlatformEmoji("bug", "slack")).toBe("bug");
47
+ });
48
+ it("resolves any form to its canonical name (platform-agnostic)", () => {
49
+ expect(toCanonicalEmoji("👍")).toBe("thumbs_up"); // unicode
50
+ expect(toCanonicalEmoji("thumbsup")).toBe("thumbs_up"); // slack alias
51
+ expect(toCanonicalEmoji("+1")).toBe("thumbs_up"); // canonical slack shortcode
52
+ expect(toCanonicalEmoji("thumbs_up")).toBe("thumbs_up"); // already canonical
53
+ expect(toCanonicalEmoji("❤")).toBe("heart"); // bare codepoint (no VS16)
54
+ expect(toCanonicalEmoji("❤️")).toBe("heart"); // qualified (VS16)
55
+ // Unknown / custom emoji passes through unchanged.
56
+ expect(toCanonicalEmoji("party_parrot")).toBe("party_parrot");
57
+ });
58
+ it("returns undefined for unknown tokens (passthrough)", () => {
59
+ expect(toPlatformEmoji("party_parrot", "slack")).toBeUndefined();
60
+ expect(normalizeEmoji(":shrug:", "slack")).toBeUndefined();
61
+ expect(normalizeEmoji("🦜", "discord")).toBeUndefined();
62
+ });
63
+ });
@@ -0,0 +1,8 @@
1
+ export * from "./ir.js";
2
+ export * from "./render.js";
3
+ export * from "./types.js";
4
+ export * from "./bind.js";
5
+ export * from "./components.js";
6
+ export * from "./emoji.js";
7
+ export * from "./modal.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./ir.js";
2
+ export * from "./render.js";
3
+ export * from "./types.js";
4
+ export * from "./bind.js";
5
+ export * from "./components.js";
6
+ export * from "./emoji.js";
7
+ export * from "./modal.js";
package/dist/ir.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export type ComponentFn = (props: Record<string, unknown>) => BotNode | BotNode[] | string | null;
2
+ export interface BotNode {
3
+ type: string | ComponentFn | symbol;
4
+ props: Record<string, unknown>;
5
+ key?: string | number;
6
+ }
7
+ export type Renderable = string | BotNode | BotNode[] | {
8
+ raw: unknown;
9
+ };
10
+ export declare const Fragment: unique symbol;
11
+ //# sourceMappingURL=ir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../src/ir.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC;AACzC,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AACD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,EAAE,GAAG;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC;AACzE,eAAO,MAAM,QAAQ,EAAE,OAAO,MAAiD,CAAC"}
package/dist/ir.js ADDED
@@ -0,0 +1 @@
1
+ export const Fragment = Symbol.for("copilotkit.bot-ui.Fragment");
@@ -0,0 +1,7 @@
1
+ export { jsx, jsxs, Fragment } from "./jsx-runtime.js";
2
+ export declare function jsxDEV(type: any, props: any, key?: any): {
3
+ type: any;
4
+ props: any;
5
+ key: any;
6
+ };
7
+ //# sourceMappingURL=jsx-dev-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-dev-runtime.d.ts","sourceRoot":"","sources":["../src/jsx-dev-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,wBAAgB,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG;;;;EAEtD"}
@@ -0,0 +1,4 @@
1
+ export { jsx, jsxs, Fragment } from "./jsx-runtime.js";
2
+ export function jsxDEV(type, props, key) {
3
+ return { type, props: props ?? {}, key };
4
+ }
@@ -0,0 +1,30 @@
1
+ import { Fragment, type BotNode } from "./ir.js";
2
+ export { Fragment };
3
+ export declare function jsx(type: string | ((props: never) => unknown) | symbol, props: Record<string, unknown> | null, key?: string | number): BotNode;
4
+ export declare const jsxs: typeof jsx;
5
+ /**
6
+ * The JSX type contract for this runtime. Declaring it here (rather than
7
+ * relying on a global `JSX` namespace) is what makes the compiler actually
8
+ * check element props: unknown attributes and bad children are errors, and
9
+ * every element returns an {@link BotNode}.
10
+ *
11
+ * Resolved by TypeScript because `jsxImportSource` points at this package, so
12
+ * `<Section foo={1} />` is checked against `SectionProps` with excess-property
13
+ * checking — there are no lowercase intrinsic tags.
14
+ */
15
+ export declare namespace JSX {
16
+ /** The result of evaluating a JSX expression. */
17
+ type Element = BotNode;
18
+ /** Tells TypeScript which prop receives nested children. */
19
+ interface ElementChildrenAttribute {
20
+ children: {};
21
+ }
22
+ /** Props implicitly accepted by every element. */
23
+ interface IntrinsicAttributes {
24
+ key?: string | number;
25
+ }
26
+ /** No lowercase intrinsic tags — the vocabulary is the capitalized components. */
27
+ interface IntrinsicElements {
28
+ }
29
+ }
30
+ //# sourceMappingURL=jsx-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-runtime.d.ts","sourceRoot":"","sources":["../src/jsx-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,CAAC;AAEpB,wBAAgB,GAAG,CACjB,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,MAAM,EACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACrC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GACpB,OAAO,CAET;AACD,eAAO,MAAM,IAAI,YAAM,CAAC;AAExB;;;;;;;;;GASG;AACH,yBAAiB,GAAG,CAAC;IACnB,iDAAiD;IACjD,KAAY,OAAO,GAAG,OAAO,CAAC;IAC9B,4DAA4D;IAC5D,UAAiB,wBAAwB;QACvC,QAAQ,EAAE,EAAE,CAAC;KACd;IACD,kDAAkD;IAClD,UAAiB,mBAAmB;QAClC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB;IACD,kFAAkF;IAClF,UAAiB,iBAAiB;KAAG;CACtC"}
@@ -0,0 +1,6 @@
1
+ import { Fragment } from "./ir.js";
2
+ export { Fragment };
3
+ export function jsx(type, props, key) {
4
+ return { type: type, props: props ?? {}, key };
5
+ }
6
+ export const jsxs = jsx;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jsx-runtime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-runtime.test.d.ts","sourceRoot":"","sources":["../src/jsx-runtime.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { jsx, jsxs, Fragment } from "./jsx-runtime.js";
3
+ describe("jsx factory", () => {
4
+ it("builds an BotNode with type and props", () => {
5
+ const node = jsx("section", { children: "hi" });
6
+ expect(node.type).toBe("section");
7
+ expect(node.props.children).toBe("hi");
8
+ });
9
+ it("jsxs keeps array children", () => {
10
+ const node = jsxs("actions", { children: ["a", "b"] });
11
+ expect(Array.isArray(node.props.children)).toBe(true);
12
+ });
13
+ it("Fragment is a stable sentinel", () => {
14
+ const node = jsx(Fragment, { children: ["a", "b"] });
15
+ expect(node.type).toBe(Fragment);
16
+ });
17
+ });
@@ -0,0 +1,60 @@
1
+ import type { BotNode } from "./ir.js";
2
+ import type { BotChildren } from "./components.js";
3
+ interface WithModalChildren {
4
+ children?: BotChildren;
5
+ }
6
+ export interface ModalProps extends WithModalChildren {
7
+ /** Stable id routed to `bot.onModalSubmit` / `bot.onModalClose`. */
8
+ callbackId: string;
9
+ title: string;
10
+ submitLabel?: string;
11
+ closeLabel?: string;
12
+ /** Slack: emit a `view_closed` event when the user dismisses the modal. */
13
+ notifyOnClose?: boolean;
14
+ /** Opaque string echoed back to the submit/close handler. */
15
+ privateMetadata?: string;
16
+ }
17
+ export interface TextInputProps {
18
+ id: string;
19
+ label: string;
20
+ placeholder?: string;
21
+ multiline?: boolean;
22
+ optional?: boolean;
23
+ maxLength?: number;
24
+ initialValue?: string;
25
+ children?: never;
26
+ }
27
+ export interface ModalSelectProps extends WithModalChildren {
28
+ id: string;
29
+ label: string;
30
+ placeholder?: string;
31
+ optional?: boolean;
32
+ /** `value` of the option selected by default. */
33
+ initialOption?: string;
34
+ }
35
+ export interface ModalSelectOptionProps {
36
+ label: string;
37
+ value: string;
38
+ children?: never;
39
+ }
40
+ export interface RadioButtonsProps extends WithModalChildren {
41
+ id: string;
42
+ label: string;
43
+ optional?: boolean;
44
+ initialOption?: string;
45
+ }
46
+ /** A modal view IR root. Distinct from message IR; rendered via `renderModal`. */
47
+ export type ModalView = BotNode & {
48
+ type: "modal";
49
+ };
50
+ /** Thrown by an adapter's `renderModal` when a view uses an unsupported element. */
51
+ export declare class ModalRenderError extends Error {
52
+ constructor(message: string);
53
+ }
54
+ export declare const Modal: (props: ModalProps) => ModalView;
55
+ export declare const TextInput: (props: TextInputProps) => BotNode;
56
+ export declare const ModalSelect: (props: ModalSelectProps) => BotNode;
57
+ export declare const ModalSelectOption: (props: ModalSelectOptionProps) => BotNode;
58
+ export declare const RadioButtons: (props: RadioButtonsProps) => BotNode;
59
+ export {};
60
+ //# sourceMappingURL=modal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../src/modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,UAAU,iBAAiB;IACzB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,MAAM,WAAW,UAAW,SAAQ,iBAAiB;IACnD,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AACD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AACD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AACD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AACD,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpD,oFAAoF;AACpF,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,KAAK,EAAqC,CACrD,KAAK,EAAE,UAAU,KACd,SAAS,CAAC;AACf,eAAO,MAAM,SAAS,oCAAgD,CAAC;AACvE,eAAO,MAAM,WAAW,sCAA8C,CAAC;AACvE,eAAO,MAAM,iBAAiB,4CAE7B,CAAC;AACF,eAAO,MAAM,YAAY,uCAA8C,CAAC"}
package/dist/modal.js ADDED
@@ -0,0 +1,13 @@
1
+ import { intrinsic } from "./components.js";
2
+ /** Thrown by an adapter's `renderModal` when a view uses an unsupported element. */
3
+ export class ModalRenderError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "ModalRenderError";
7
+ }
8
+ }
9
+ export const Modal = intrinsic("modal");
10
+ export const TextInput = intrinsic("modal_text_input");
11
+ export const ModalSelect = intrinsic("modal_select");
12
+ export const ModalSelectOption = intrinsic("modal_select_option");
13
+ export const RadioButtons = intrinsic("modal_radio");
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=modal.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal.test.d.ts","sourceRoot":"","sources":["../src/modal.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@copilotkit/channels-ui/jsx-runtime";
2
+ import { describe, it, expect } from "vitest";
3
+ import { renderToIR } from "./render.js";
4
+ import { Modal, TextInput, ModalSelect, ModalSelectOption, RadioButtons, } from "./modal.js";
5
+ describe("modal vocabulary", () => {
6
+ it("lowers a modal tree to IR with distinct type tags", () => {
7
+ const view = (_jsxs(Modal, { callbackId: "triage", title: "Triage", submitLabel: "File", children: [_jsx(TextInput, { id: "summary", label: "Summary", multiline: true, optional: true }), _jsxs(ModalSelect, { id: "prio", label: "Priority", initialOption: "high", children: [_jsx(ModalSelectOption, { label: "High", value: "high" }), _jsx(ModalSelectOption, { label: "Low", value: "low" })] }), _jsx(RadioButtons, { id: "team", label: "Team", children: _jsx(ModalSelectOption, { label: "Core", value: "core" }) })] }));
8
+ const [root] = renderToIR(view);
9
+ expect(root.type).toBe("modal");
10
+ expect(root.props.callbackId).toBe("triage");
11
+ expect(root.props.title).toBe("Triage");
12
+ const kids = root.props.children;
13
+ expect(kids.map((k) => k.type)).toEqual([
14
+ "modal_text_input",
15
+ "modal_select",
16
+ "modal_radio",
17
+ ]);
18
+ expect(kids[0].props).toMatchObject({
19
+ id: "summary",
20
+ multiline: true,
21
+ optional: true,
22
+ });
23
+ const select = kids[1];
24
+ expect(select.props.initialOption).toBe("high");
25
+ const opts = select.props.children;
26
+ expect(opts.map((o) => o.props.value)).toEqual(["high", "low"]);
27
+ expect(opts[0].type).toBe("modal_select_option");
28
+ });
29
+ });
@@ -0,0 +1,3 @@
1
+ import { type BotNode, type Renderable } from "./ir.js";
2
+ export declare function renderToIR(ui: Renderable): BotNode[];
3
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAkClE,wBAAgB,UAAU,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,EAAE,CAKpD"}
package/dist/render.js ADDED
@@ -0,0 +1,37 @@
1
+ import { Fragment } from "./ir.js";
2
+ function isBotNode(v) {
3
+ return typeof v === "object" && v !== null && "type" in v && "props" in v;
4
+ }
5
+ function expand(node) {
6
+ if (node == null || node === false || node === true)
7
+ return [];
8
+ if (typeof node === "string" || typeof node === "number") {
9
+ return [{ type: "text", props: { value: String(node) } }];
10
+ }
11
+ if (Array.isArray(node))
12
+ return node.flatMap(expand);
13
+ if (!isBotNode(node))
14
+ return [];
15
+ if (node.type === Fragment)
16
+ return expand(node.props.children);
17
+ if (typeof node.type === "function") {
18
+ return expand(node.type(node.props));
19
+ }
20
+ const { children, ...rest } = node.props;
21
+ const expandedChildren = children === undefined ? undefined : expand(children);
22
+ return [
23
+ {
24
+ type: node.type,
25
+ props: expandedChildren === undefined
26
+ ? rest
27
+ : { ...rest, children: expandedChildren },
28
+ key: node.key,
29
+ },
30
+ ];
31
+ }
32
+ export function renderToIR(ui) {
33
+ if (typeof ui === "object" && ui !== null && "raw" in ui) {
34
+ return [{ type: "raw", props: { value: ui.raw } }];
35
+ }
36
+ return expand(ui);
37
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=render.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.test.d.ts","sourceRoot":"","sources":["../src/render.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@copilotkit/channels-ui/jsx-runtime";
2
+ import { describe, it, expect } from "vitest";
3
+ import { renderToIR } from "./render.js";
4
+ function Card(props) {
5
+ return { type: "section", props: { children: props.title } };
6
+ }
7
+ describe("renderToIR", () => {
8
+ it("wraps a bare string into a text node", () => {
9
+ expect(renderToIR("hi")).toEqual([
10
+ { type: "text", props: { value: "hi" } },
11
+ ]);
12
+ });
13
+ it("expands a component function with its props", () => {
14
+ const out = renderToIR(_jsx(Card, { title: "Flights" }));
15
+ expect(out).toEqual([
16
+ {
17
+ type: "section",
18
+ props: { children: [{ type: "text", props: { value: "Flights" } }] },
19
+ },
20
+ ]);
21
+ });
22
+ it("flattens Fragment children and nested components", () => {
23
+ const out = renderToIR(_jsxs(_Fragment, { children: [_jsx(Card, { title: "A" }), _jsx(Card, { title: "B" })] }));
24
+ expect(out.map((n) => n.type)).toEqual(["section", "section"]);
25
+ });
26
+ it("wraps string children inside intrinsic nodes recursively", () => {
27
+ const out = renderToIR({ type: "actions", props: { children: ["x"] } });
28
+ const actions = out[0];
29
+ expect(actions.props.children[0]).toEqual({
30
+ type: "text",
31
+ props: { value: "x" },
32
+ });
33
+ });
34
+ it("passes {raw} through as a raw node", () => {
35
+ expect(renderToIR({ raw: [{ block: 1 }] })).toEqual([
36
+ { type: "raw", props: { value: [{ block: 1 }] } },
37
+ ]);
38
+ });
39
+ });