@parlor/client 0.0.0-stage → 0.2.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -2
  3. package/dist/action-client.d.ts +42 -0
  4. package/dist/action-client.d.ts.map +1 -0
  5. package/dist/action-client.js +92 -0
  6. package/dist/action-client.js.map +1 -0
  7. package/dist/index.d.ts +8 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +6 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/plugin-client.d.ts +11 -0
  12. package/dist/plugin-client.d.ts.map +1 -0
  13. package/dist/plugin-client.js +14 -0
  14. package/dist/plugin-client.js.map +1 -0
  15. package/dist/query-keys.d.ts +3 -0
  16. package/dist/query-keys.d.ts.map +1 -0
  17. package/dist/query-keys.js +5 -0
  18. package/dist/query-keys.js.map +1 -0
  19. package/dist/react/action-query.d.ts +26 -0
  20. package/dist/react/action-query.d.ts.map +1 -0
  21. package/dist/react/action-query.js +33 -0
  22. package/dist/react/action-query.js.map +1 -0
  23. package/dist/react/agent-ui/activity-state.d.ts +20 -0
  24. package/dist/react/agent-ui/activity-state.d.ts.map +1 -0
  25. package/dist/react/agent-ui/activity-state.js +14 -0
  26. package/dist/react/agent-ui/activity-state.js.map +1 -0
  27. package/dist/react/agent-ui/chat-activity.d.ts +13 -0
  28. package/dist/react/agent-ui/chat-activity.d.ts.map +1 -0
  29. package/dist/react/agent-ui/chat-activity.js +26 -0
  30. package/dist/react/agent-ui/chat-activity.js.map +1 -0
  31. package/dist/react/agent-ui/chat-confirm.d.ts +28 -0
  32. package/dist/react/agent-ui/chat-confirm.d.ts.map +1 -0
  33. package/dist/react/agent-ui/chat-confirm.js +140 -0
  34. package/dist/react/agent-ui/chat-confirm.js.map +1 -0
  35. package/dist/react/agent-ui/chat.d.ts +7 -0
  36. package/dist/react/agent-ui/chat.d.ts.map +1 -0
  37. package/dist/react/agent-ui/chat.js +7 -0
  38. package/dist/react/agent-ui/chat.js.map +1 -0
  39. package/dist/react/agent-ui/dev.d.ts +12 -0
  40. package/dist/react/agent-ui/dev.d.ts.map +1 -0
  41. package/dist/react/agent-ui/dev.js +80 -0
  42. package/dist/react/agent-ui/dev.js.map +1 -0
  43. package/dist/react/agent-ui/runtime.d.ts +12 -0
  44. package/dist/react/agent-ui/runtime.d.ts.map +1 -0
  45. package/dist/react/agent-ui/runtime.js +50 -0
  46. package/dist/react/agent-ui/runtime.js.map +1 -0
  47. package/dist/react/agent-ui/use-agent-activity.d.ts +3 -0
  48. package/dist/react/agent-ui/use-agent-activity.d.ts.map +1 -0
  49. package/dist/react/agent-ui/use-agent-activity.js +7 -0
  50. package/dist/react/agent-ui/use-agent-activity.js.map +1 -0
  51. package/dist/react/agent-ui/use-agent-confirmations.d.ts +8 -0
  52. package/dist/react/agent-ui/use-agent-confirmations.d.ts.map +1 -0
  53. package/dist/react/agent-ui/use-agent-confirmations.js +12 -0
  54. package/dist/react/agent-ui/use-agent-confirmations.js.map +1 -0
  55. package/dist/react/agent-ui/use-agent-view.d.ts +11 -0
  56. package/dist/react/agent-ui/use-agent-view.d.ts.map +1 -0
  57. package/dist/react/agent-ui/use-agent-view.js +25 -0
  58. package/dist/react/agent-ui/use-agent-view.js.map +1 -0
  59. package/dist/react/chat/ask-fields.d.ts +13 -0
  60. package/dist/react/chat/ask-fields.d.ts.map +1 -0
  61. package/dist/react/chat/ask-fields.js +46 -0
  62. package/dist/react/chat/ask-fields.js.map +1 -0
  63. package/dist/react/chat/chat-ask.d.ts +32 -0
  64. package/dist/react/chat/chat-ask.d.ts.map +1 -0
  65. package/dist/react/chat/chat-ask.js +69 -0
  66. package/dist/react/chat/chat-ask.js.map +1 -0
  67. package/dist/react/chat/chat-composer.d.ts +18 -0
  68. package/dist/react/chat/chat-composer.d.ts.map +1 -0
  69. package/dist/react/chat/chat-composer.js +81 -0
  70. package/dist/react/chat/chat-composer.js.map +1 -0
  71. package/dist/react/chat/chat-root.d.ts +14 -0
  72. package/dist/react/chat/chat-root.d.ts.map +1 -0
  73. package/dist/react/chat/chat-root.js +21 -0
  74. package/dist/react/chat/chat-root.js.map +1 -0
  75. package/dist/react/chat/chat-thread.d.ts +18 -0
  76. package/dist/react/chat/chat-thread.d.ts.map +1 -0
  77. package/dist/react/chat/chat-thread.js +69 -0
  78. package/dist/react/chat/chat-thread.js.map +1 -0
  79. package/dist/react/chat/context.d.ts +6 -0
  80. package/dist/react/chat/context.d.ts.map +1 -0
  81. package/dist/react/chat/context.js +24 -0
  82. package/dist/react/chat/context.js.map +1 -0
  83. package/dist/react/chat/create.d.ts +15 -0
  84. package/dist/react/chat/create.d.ts.map +1 -0
  85. package/dist/react/chat/create.js +23 -0
  86. package/dist/react/chat/create.js.map +1 -0
  87. package/dist/react/chat/open-turns.d.ts +10 -0
  88. package/dist/react/chat/open-turns.d.ts.map +1 -0
  89. package/dist/react/chat/open-turns.js +60 -0
  90. package/dist/react/chat/open-turns.js.map +1 -0
  91. package/dist/react/chat/runtime.d.ts +167 -0
  92. package/dist/react/chat/runtime.d.ts.map +1 -0
  93. package/dist/react/chat/runtime.js +939 -0
  94. package/dist/react/chat/runtime.js.map +1 -0
  95. package/dist/react/chat/use-chat.d.ts +4 -0
  96. package/dist/react/chat/use-chat.d.ts.map +1 -0
  97. package/dist/react/chat/use-chat.js +22 -0
  98. package/dist/react/chat/use-chat.js.map +1 -0
  99. package/dist/react/chat/use-device-setup.d.ts +9 -0
  100. package/dist/react/chat/use-device-setup.d.ts.map +1 -0
  101. package/dist/react/chat/use-device-setup.js +17 -0
  102. package/dist/react/chat/use-device-setup.js.map +1 -0
  103. package/dist/react/hooks.d.ts +17 -0
  104. package/dist/react/hooks.d.ts.map +1 -0
  105. package/dist/react/hooks.js +36 -0
  106. package/dist/react/hooks.js.map +1 -0
  107. package/dist/react/index.d.ts +21 -0
  108. package/dist/react/index.d.ts.map +1 -0
  109. package/dist/react/index.js +14 -0
  110. package/dist/react/index.js.map +1 -0
  111. package/dist/react/provider.d.ts +41 -0
  112. package/dist/react/provider.d.ts.map +1 -0
  113. package/dist/react/provider.js +176 -0
  114. package/dist/react/provider.js.map +1 -0
  115. package/dist/react/retry.d.ts +5 -0
  116. package/dist/react/retry.d.ts.map +1 -0
  117. package/dist/react/retry.js +22 -0
  118. package/dist/react/retry.js.map +1 -0
  119. package/dist/react/sync-bridge.d.ts +28 -0
  120. package/dist/react/sync-bridge.d.ts.map +1 -0
  121. package/dist/react/sync-bridge.js +65 -0
  122. package/dist/react/sync-bridge.js.map +1 -0
  123. package/dist/react/sync.d.ts +51 -0
  124. package/dist/react/sync.d.ts.map +1 -0
  125. package/dist/react/sync.js +154 -0
  126. package/dist/react/sync.js.map +1 -0
  127. package/dist/react/use-capability.d.ts +6 -0
  128. package/dist/react/use-capability.d.ts.map +1 -0
  129. package/dist/react/use-capability.js +21 -0
  130. package/dist/react/use-capability.js.map +1 -0
  131. package/dist/react/use-devices.d.ts +34 -0
  132. package/dist/react/use-devices.d.ts.map +1 -0
  133. package/dist/react/use-devices.js +93 -0
  134. package/dist/react/use-devices.js.map +1 -0
  135. package/dist/react/use-link-device.d.ts +42 -0
  136. package/dist/react/use-link-device.d.ts.map +1 -0
  137. package/dist/react/use-link-device.js +58 -0
  138. package/dist/react/use-link-device.js.map +1 -0
  139. package/dist/register.d.ts +7 -0
  140. package/dist/register.d.ts.map +1 -0
  141. package/dist/register.js +2 -0
  142. package/dist/register.js.map +1 -0
  143. package/dist/router/errors.d.ts +13 -0
  144. package/dist/router/errors.d.ts.map +1 -0
  145. package/dist/router/errors.js +16 -0
  146. package/dist/router/errors.js.map +1 -0
  147. package/dist/router/index.d.ts +4 -0
  148. package/dist/router/index.d.ts.map +1 -0
  149. package/dist/router/index.js +4 -0
  150. package/dist/router/index.js.map +1 -0
  151. package/dist/router/mount.d.ts +22 -0
  152. package/dist/router/mount.d.ts.map +1 -0
  153. package/dist/router/mount.js +27 -0
  154. package/dist/router/mount.js.map +1 -0
  155. package/dist/router/router.d.ts +29 -0
  156. package/dist/router/router.d.ts.map +1 -0
  157. package/dist/router/router.js +44 -0
  158. package/dist/router/router.js.map +1 -0
  159. package/dist/session.d.ts +121 -0
  160. package/dist/session.d.ts.map +1 -0
  161. package/dist/session.js +5 -0
  162. package/dist/session.js.map +1 -0
  163. package/dist/ssr/index.d.ts +2 -0
  164. package/dist/ssr/index.d.ts.map +1 -0
  165. package/dist/ssr/index.js +2 -0
  166. package/dist/ssr/index.js.map +1 -0
  167. package/dist/ssr/render.d.ts +18 -0
  168. package/dist/ssr/render.d.ts.map +1 -0
  169. package/dist/ssr/render.js +96 -0
  170. package/dist/ssr/render.js.map +1 -0
  171. package/package.json +70 -3
@@ -0,0 +1,140 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, Fragment, use, useCallback, useId, useLayoutEffect, useMemo, useRef, } from "react";
3
+ import { useAgentConfirmations } from "./use-agent-confirmations.js";
4
+ const ConfirmScope = createContext(undefined);
5
+ const ListScope = createContext(undefined);
6
+ function assignRef(ref, value) {
7
+ if (typeof ref === "function")
8
+ return ref(value);
9
+ if (ref) {
10
+ ref.current = value;
11
+ return () => {
12
+ ref.current = null;
13
+ };
14
+ }
15
+ }
16
+ function useConfirm(part) {
17
+ const scope = use(ConfirmScope);
18
+ if (!scope)
19
+ throw new Error(`${part} must be rendered inside Chat.Confirm.`);
20
+ return scope;
21
+ }
22
+ /**
23
+ * Lists the agent requests waiting for this user, newest last. Rendering it is what tells
24
+ * the server this client can show a confirmation, so keep it mounted wherever the user can
25
+ * answer. `data-pending` carries the count while any wait. When the card holding focus goes
26
+ * away, focus moves to the next card, or to the list when none is left, so it never falls
27
+ * out of an enclosing dialog.
28
+ */
29
+ export function Confirmations({ empty, children, ref, ...props }) {
30
+ const confirmations = useAgentConfirmations();
31
+ const list = useRef(null);
32
+ const cards = useRef(new Map());
33
+ const shownIds = useRef([]);
34
+ const releasedId = useRef(undefined);
35
+ const attach = useCallback((element) => {
36
+ list.current = element;
37
+ const detach = assignRef(ref, element);
38
+ return () => {
39
+ list.current = null;
40
+ if (detach)
41
+ detach();
42
+ else
43
+ assignRef(ref, null);
44
+ };
45
+ }, [ref]);
46
+ const scope = useMemo(() => ({
47
+ register: (id, element) => {
48
+ cards.current.set(id, element);
49
+ return () => {
50
+ cards.current.delete(id);
51
+ };
52
+ },
53
+ releasingFocus: (id) => {
54
+ releasedId.current = id;
55
+ },
56
+ }), []);
57
+ useLayoutEffect(() => {
58
+ const released = releasedId.current;
59
+ releasedId.current = undefined;
60
+ const ids = confirmations.map((confirmation) => confirmation.id);
61
+ const previousIds = shownIds.current;
62
+ shownIds.current = ids;
63
+ const listElement = list.current;
64
+ if (released === undefined || !listElement)
65
+ return;
66
+ const active = document.activeElement;
67
+ if (active && active !== document.body && listElement.contains(active))
68
+ return;
69
+ const index = previousIds.indexOf(released);
70
+ const nextId = ids[Math.min(index, ids.length - 1)];
71
+ (nextId === undefined
72
+ ? listElement
73
+ : (cards.current.get(nextId) ?? listElement)).focus();
74
+ });
75
+ return (_jsx(ListScope, { value: scope, children: _jsx("div", { "aria-live": "polite", tabIndex: -1, ...props, ref: attach, "data-pending": confirmations.length > 0 ? confirmations.length : undefined, children: confirmations.length === 0
76
+ ? empty
77
+ : confirmations.map((confirmation) => (_jsx(Fragment, { children: children(confirmation) }, confirmation.id))) }) }));
78
+ }
79
+ /**
80
+ * One request as a labelled group. Escape inside it declines, and stops there so an
81
+ * enclosing dialog stays open. It never takes focus by itself, but it can hold it
82
+ * (`tabIndex` -1) so `Chat.Confirmations` can keep focus in the list.
83
+ */
84
+ export function Confirm({ confirmation, onKeyDown, ref, ...props }) {
85
+ const labelId = useId();
86
+ const list = use(ListScope);
87
+ const group = useRef(null);
88
+ const { id } = confirmation;
89
+ useLayoutEffect(() => {
90
+ const element = group.current;
91
+ if (!list || !element)
92
+ return;
93
+ const unregister = list.register(id, element);
94
+ return () => {
95
+ if (element.contains(document.activeElement))
96
+ list.releasingFocus(id);
97
+ unregister();
98
+ };
99
+ }, [list, id]);
100
+ const attach = useCallback((element) => {
101
+ group.current = element;
102
+ const detach = assignRef(ref, element);
103
+ return () => {
104
+ if (detach)
105
+ detach();
106
+ else
107
+ assignRef(ref, null);
108
+ };
109
+ }, [ref]);
110
+ const decline = (event) => {
111
+ onKeyDown?.(event);
112
+ if (event.defaultPrevented || event.key !== "Escape")
113
+ return;
114
+ event.preventDefault();
115
+ event.stopPropagation();
116
+ confirmation.decline();
117
+ };
118
+ return (_jsx(ConfirmScope, { value: { confirmation, labelId }, children: _jsx("div", { role: "group", "aria-labelledby": labelId, "data-action": confirmation.action, tabIndex: -1, ...props, ref: attach, onKeyDown: decline }) }));
119
+ }
120
+ export function ConfirmLabel(props) {
121
+ const { labelId } = useConfirm("Chat.ConfirmLabel");
122
+ return _jsx("p", { ...props, id: labelId });
123
+ }
124
+ export function Approve({ onClick, ...props }) {
125
+ const { confirmation } = useConfirm("Chat.Approve");
126
+ return (_jsx("button", { type: "button", ...props, onClick: (event) => {
127
+ onClick?.(event);
128
+ if (!event.defaultPrevented)
129
+ confirmation.approve();
130
+ } }));
131
+ }
132
+ export function Decline({ onClick, ...props }) {
133
+ const { confirmation } = useConfirm("Chat.Decline");
134
+ return (_jsx("button", { type: "button", ...props, onClick: (event) => {
135
+ onClick?.(event);
136
+ if (!event.defaultPrevented)
137
+ confirmation.decline();
138
+ } }));
139
+ }
140
+ //# sourceMappingURL=chat-confirm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-confirm.js","sourceRoot":"","sources":["../../../src/react/agent-ui/chat-confirm.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { AgentConfirmation } from \"@parlor/agent-ui/client\";\nimport {\n type ComponentProps,\n createContext,\n Fragment,\n type KeyboardEvent,\n type ReactNode,\n type Ref,\n use,\n useCallback,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n} from \"react\";\n\nimport { useAgentConfirmations } from \"./use-agent-confirmations.js\";\n\ninterface ConfirmContext {\n readonly confirmation: AgentConfirmation;\n readonly labelId: string;\n}\n\nconst ConfirmScope = createContext<ConfirmContext | undefined>(undefined);\n\ninterface ListContext {\n readonly register: (id: string, element: HTMLElement) => () => void;\n readonly releasingFocus: (id: string) => void;\n}\n\nconst ListScope = createContext<ListContext | undefined>(undefined);\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null): (() => void) | void {\n if (typeof ref === \"function\") return ref(value);\n if (ref) {\n ref.current = value;\n return () => {\n ref.current = null;\n };\n }\n}\n\nfunction useConfirm(part: string): ConfirmContext {\n const scope = use(ConfirmScope);\n if (!scope) throw new Error(`${part} must be rendered inside Chat.Confirm.`);\n return scope;\n}\n\nexport interface ChatConfirmationsProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n /** Shown while no agent request waits. */\n readonly empty?: ReactNode;\n readonly children: (confirmation: AgentConfirmation) => ReactNode;\n}\n\n/**\n * Lists the agent requests waiting for this user, newest last. Rendering it is what tells\n * the server this client can show a confirmation, so keep it mounted wherever the user can\n * answer. `data-pending` carries the count while any wait. When the card holding focus goes\n * away, focus moves to the next card, or to the list when none is left, so it never falls\n * out of an enclosing dialog.\n */\nexport function Confirmations({\n empty,\n children,\n ref,\n ...props\n}: ChatConfirmationsProps) {\n const confirmations = useAgentConfirmations();\n const list = useRef<HTMLDivElement | null>(null);\n const cards = useRef(new Map<string, HTMLElement>());\n const shownIds = useRef<readonly string[]>([]);\n const releasedId = useRef<string | undefined>(undefined);\n const attach = useCallback(\n (element: HTMLDivElement | null) => {\n list.current = element;\n const detach = assignRef(ref, element);\n return () => {\n list.current = null;\n if (detach) detach();\n else assignRef(ref, null);\n };\n },\n [ref],\n );\n const scope = useMemo<ListContext>(\n () => ({\n register: (id, element) => {\n cards.current.set(id, element);\n return () => {\n cards.current.delete(id);\n };\n },\n releasingFocus: (id) => {\n releasedId.current = id;\n },\n }),\n [],\n );\n\n useLayoutEffect(() => {\n const released = releasedId.current;\n releasedId.current = undefined;\n const ids = confirmations.map((confirmation) => confirmation.id);\n const previousIds = shownIds.current;\n shownIds.current = ids;\n const listElement = list.current;\n if (released === undefined || !listElement) return;\n const active = document.activeElement;\n if (active && active !== document.body && listElement.contains(active)) return;\n const index = previousIds.indexOf(released);\n const nextId = ids[Math.min(index, ids.length - 1)];\n (nextId === undefined\n ? listElement\n : (cards.current.get(nextId) ?? listElement)\n ).focus();\n });\n\n return (\n <ListScope value={scope}>\n <div\n aria-live=\"polite\"\n tabIndex={-1}\n {...props}\n ref={attach}\n data-pending={confirmations.length > 0 ? confirmations.length : undefined}\n >\n {confirmations.length === 0\n ? empty\n : confirmations.map((confirmation) => (\n <Fragment key={confirmation.id}>{children(confirmation)}</Fragment>\n ))}\n </div>\n </ListScope>\n );\n}\n\nexport interface ChatConfirmProps extends ComponentProps<\"div\"> {\n readonly confirmation: AgentConfirmation;\n}\n\n/**\n * One request as a labelled group. Escape inside it declines, and stops there so an\n * enclosing dialog stays open. It never takes focus by itself, but it can hold it\n * (`tabIndex` -1) so `Chat.Confirmations` can keep focus in the list.\n */\nexport function Confirm({ confirmation, onKeyDown, ref, ...props }: ChatConfirmProps) {\n const labelId = useId();\n const list = use(ListScope);\n const group = useRef<HTMLDivElement | null>(null);\n const { id } = confirmation;\n useLayoutEffect(() => {\n const element = group.current;\n if (!list || !element) return;\n const unregister = list.register(id, element);\n return () => {\n if (element.contains(document.activeElement)) list.releasingFocus(id);\n unregister();\n };\n }, [list, id]);\n const attach = useCallback(\n (element: HTMLDivElement | null) => {\n group.current = element;\n const detach = assignRef(ref, element);\n return () => {\n if (detach) detach();\n else assignRef(ref, null);\n };\n },\n [ref],\n );\n const decline = (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented || event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n confirmation.decline();\n };\n return (\n <ConfirmScope value={{ confirmation, labelId }}>\n <div\n role=\"group\"\n aria-labelledby={labelId}\n data-action={confirmation.action}\n tabIndex={-1}\n {...props}\n ref={attach}\n onKeyDown={decline}\n />\n </ConfirmScope>\n );\n}\n\nexport function ConfirmLabel(props: Omit<ComponentProps<\"p\">, \"id\">) {\n const { labelId } = useConfirm(\"Chat.ConfirmLabel\");\n return <p {...props} id={labelId} />;\n}\n\nexport function Approve({ onClick, ...props }: ComponentProps<\"button\">) {\n const { confirmation } = useConfirm(\"Chat.Approve\");\n return (\n <button\n type=\"button\"\n {...props}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) confirmation.approve();\n }}\n />\n );\n}\n\nexport function Decline({ onClick, ...props }: ComponentProps<\"button\">) {\n const { confirmation } = useConfirm(\"Chat.Decline\");\n return (\n <button\n type=\"button\"\n {...props}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) confirmation.decline();\n }}\n />\n );\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export { Activity } from "./chat-activity.js";
2
+ export { Approve, Confirm, ConfirmLabel, Confirmations, Decline, } from "./chat-confirm.js";
3
+ export { Ask, AskChoice, AskForm } from "../chat/chat-ask.js";
4
+ export { Composer, Input, Send, Status, Stop } from "../chat/chat-composer.js";
5
+ export { Root } from "../chat/chat-root.js";
6
+ export { LoadOlder, Thread } from "../chat/chat-thread.js";
7
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,EACb,OAAO,GACR,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { Activity } from "./chat-activity.js";
2
+ export { Approve, Confirm, ConfirmLabel, Confirmations, Decline, } from "./chat-confirm.js";
3
+ export { Ask, AskChoice, AskForm } from "../chat/chat-ask.js";
4
+ export { Composer, Input, Send, Status, Stop } from "../chat/chat-composer.js";
5
+ export { Root } from "../chat/chat-root.js";
6
+ export { LoadOlder, Thread } from "../chat/chat-thread.js";
7
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/react/agent-ui/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,EACb,OAAO,GACR,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["export { Activity } from \"./chat-activity.js\";\nexport {\n Approve,\n Confirm,\n ConfirmLabel,\n Confirmations,\n Decline,\n} from \"./chat-confirm.js\";\nexport { Ask, AskChoice, AskForm } from \"../chat/chat-ask.js\";\nexport { Composer, Input, Send, Status, Stop } from \"../chat/chat-composer.js\";\nexport { Root } from \"../chat/chat-root.js\";\nexport { LoadOlder, Thread } from \"../chat/chat-thread.js\";\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ScreenSnapshot } from "@parlor/agent-ui";
2
+ import type { AgentUiClient } from "@parlor/agent-ui/client";
3
+ declare global {
4
+ interface Window {
5
+ __parlor?: {
6
+ readonly clientId: string;
7
+ screen(): Promise<ScreenSnapshot>;
8
+ } | undefined;
9
+ }
10
+ }
11
+ export declare function installAgentUiDevtools(agentUi: AgentUiClient, clientId: string): () => void;
12
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/dev.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,QAAQ,CAAC,EACL;YAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC,CAAA;SAAE,GAChE,SAAS,CAAC;KACf;CACF;AA4ED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,IAAI,CAOZ"}
@@ -0,0 +1,80 @@
1
+ const TOGGLE_HINT = "Alt+Shift+A";
2
+ const MAX_LOG = 10;
3
+ function overlay(agentUi, clientId) {
4
+ const panel = document.createElement("aside");
5
+ panel.setAttribute("aria-label", "Parlor agent UI");
6
+ panel.hidden = true;
7
+ Object.assign(panel.style, {
8
+ position: "fixed",
9
+ right: "12px",
10
+ bottom: "12px",
11
+ width: "min(420px, calc(100vw - 24px))",
12
+ maxHeight: "60vh",
13
+ overflow: "auto",
14
+ zIndex: "2147483647",
15
+ background: "#111",
16
+ color: "#eee",
17
+ font: "12px/1.4 ui-monospace, monospace",
18
+ padding: "12px",
19
+ borderRadius: "8px",
20
+ boxShadow: "0 4px 24px rgba(0, 0, 0, 0.3)",
21
+ });
22
+ const header = document.createElement("div");
23
+ const log = document.createElement("ol");
24
+ log.style.margin = "8px 0";
25
+ log.style.paddingLeft = "28px";
26
+ const snapshot = document.createElement("pre");
27
+ snapshot.style.margin = "0";
28
+ snapshot.style.whiteSpace = "pre-wrap";
29
+ panel.append(header, log, snapshot);
30
+ document.body.append(panel);
31
+ const entries = [];
32
+ let lastStarted;
33
+ const render = () => {
34
+ header.textContent = `client ${clientId} (${TOGGLE_HINT} to close)`;
35
+ log.replaceChildren(...entries.map((text) => Object.assign(document.createElement("li"), { textContent: text })));
36
+ void agentUi.screen().then((screen) => {
37
+ snapshot.textContent = JSON.stringify(screen, null, 2);
38
+ });
39
+ };
40
+ const unsubscribe = agentUi.activity.subscribe(() => {
41
+ const { busy, last } = agentUi.activity.get();
42
+ if (!busy || !last || last.at === lastStarted)
43
+ return;
44
+ lastStarted = last.at;
45
+ const via = last.origin.name
46
+ ? `${last.origin.via} ${last.origin.name}`
47
+ : last.origin.via;
48
+ entries.unshift(`${new Date(last.at).toLocaleTimeString()} ${last.op} from ${via}`);
49
+ entries.length = Math.min(entries.length, MAX_LOG);
50
+ if (!panel.hidden)
51
+ render();
52
+ });
53
+ let refresh;
54
+ const onKey = (event) => {
55
+ if (!(event.altKey && event.shiftKey && event.code === "KeyA"))
56
+ return;
57
+ panel.hidden = !panel.hidden;
58
+ clearInterval(refresh);
59
+ if (panel.hidden)
60
+ return;
61
+ render();
62
+ refresh = setInterval(render, 1000);
63
+ };
64
+ window.addEventListener("keydown", onKey);
65
+ return () => {
66
+ window.removeEventListener("keydown", onKey);
67
+ clearInterval(refresh);
68
+ unsubscribe();
69
+ panel.remove();
70
+ };
71
+ }
72
+ export function installAgentUiDevtools(agentUi, clientId) {
73
+ window.__parlor = { clientId, screen: () => agentUi.screen() };
74
+ const removeOverlay = overlay(agentUi, clientId);
75
+ return () => {
76
+ removeOverlay();
77
+ delete window.__parlor;
78
+ };
79
+ }
80
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../../src/react/agent-ui/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ScreenSnapshot } from \"@parlor/agent-ui\";\nimport type { AgentUiClient } from \"@parlor/agent-ui/client\";\n\ndeclare global {\n interface Window {\n __parlor?:\n | { readonly clientId: string; screen(): Promise<ScreenSnapshot> }\n | undefined;\n }\n}\n\nconst TOGGLE_HINT = \"Alt+Shift+A\";\nconst MAX_LOG = 10;\n\nfunction overlay(agentUi: AgentUiClient, clientId: string): () => void {\n const panel = document.createElement(\"aside\");\n panel.setAttribute(\"aria-label\", \"Parlor agent UI\");\n panel.hidden = true;\n Object.assign(panel.style, {\n position: \"fixed\",\n right: \"12px\",\n bottom: \"12px\",\n width: \"min(420px, calc(100vw - 24px))\",\n maxHeight: \"60vh\",\n overflow: \"auto\",\n zIndex: \"2147483647\",\n background: \"#111\",\n color: \"#eee\",\n font: \"12px/1.4 ui-monospace, monospace\",\n padding: \"12px\",\n borderRadius: \"8px\",\n boxShadow: \"0 4px 24px rgba(0, 0, 0, 0.3)\",\n });\n const header = document.createElement(\"div\");\n const log = document.createElement(\"ol\");\n log.style.margin = \"8px 0\";\n log.style.paddingLeft = \"28px\";\n const snapshot = document.createElement(\"pre\");\n snapshot.style.margin = \"0\";\n snapshot.style.whiteSpace = \"pre-wrap\";\n panel.append(header, log, snapshot);\n document.body.append(panel);\n\n const entries: string[] = [];\n let lastStarted: number | undefined;\n const render = () => {\n header.textContent = `client ${clientId} (${TOGGLE_HINT} to close)`;\n log.replaceChildren(\n ...entries.map((text) =>\n Object.assign(document.createElement(\"li\"), { textContent: text }),\n ),\n );\n void agentUi.screen().then((screen) => {\n snapshot.textContent = JSON.stringify(screen, null, 2);\n });\n };\n const unsubscribe = agentUi.activity.subscribe(() => {\n const { busy, last } = agentUi.activity.get();\n if (!busy || !last || last.at === lastStarted) return;\n lastStarted = last.at;\n const via = last.origin.name\n ? `${last.origin.via} ${last.origin.name}`\n : last.origin.via;\n entries.unshift(`${new Date(last.at).toLocaleTimeString()} ${last.op} from ${via}`);\n entries.length = Math.min(entries.length, MAX_LOG);\n if (!panel.hidden) render();\n });\n let refresh: ReturnType<typeof setInterval> | undefined;\n const onKey = (event: KeyboardEvent) => {\n if (!(event.altKey && event.shiftKey && event.code === \"KeyA\")) return;\n panel.hidden = !panel.hidden;\n clearInterval(refresh);\n if (panel.hidden) return;\n render();\n refresh = setInterval(render, 1000);\n };\n window.addEventListener(\"keydown\", onKey);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n clearInterval(refresh);\n unsubscribe();\n panel.remove();\n };\n}\n\nexport function installAgentUiDevtools(\n agentUi: AgentUiClient,\n clientId: string,\n): () => void {\n window.__parlor = { clientId, screen: () => agentUi.screen() };\n const removeOverlay = overlay(agentUi, clientId);\n return () => {\n removeOverlay();\n delete window.__parlor;\n };\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import { type AgentUiClient } from "@parlor/agent-ui/client";
2
+ import type { QueryClient } from "@tanstack/react-query";
3
+ import type { AnyRouter } from "@tanstack/react-router";
4
+ import type { ActionClient } from "../../action-client.js";
5
+ import type { RegisteredActions } from "../../register.js";
6
+ export interface AgentUiRuntimeInput {
7
+ readonly client: ActionClient<RegisteredActions>;
8
+ readonly queryClient: QueryClient;
9
+ readonly router: AnyRouter | undefined;
10
+ }
11
+ export declare function createAgentUiRuntime({ client, queryClient, router, }: AgentUiRuntimeInput): AgentUiClient;
12
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,yBAAyB,CAAC;AAElF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;CACxC;AAqBD,wBAAgB,oBAAoB,CAAC,EACnC,MAAM,EACN,WAAW,EACX,MAAM,GACP,EAAE,mBAAmB,GAAG,aAAa,CA2BrC"}
@@ -0,0 +1,50 @@
1
+ import { createAgentUiClient } from "@parlor/agent-ui/client";
2
+ import { detectPlatform } from "@parlor/capabilities";
3
+ import { ACTION_QUERY_PREFIX } from "../../query-keys.js";
4
+ function fetchingActions(queryClient) {
5
+ return queryClient.isFetching({ queryKey: ACTION_QUERY_PREFIX });
6
+ }
7
+ function actionsSettled(queryClient, timeoutMs) {
8
+ if (fetchingActions(queryClient) === 0)
9
+ return Promise.resolve();
10
+ return new Promise((resolve) => {
11
+ const done = () => {
12
+ unsubscribe();
13
+ clearTimeout(timer);
14
+ resolve();
15
+ };
16
+ const unsubscribe = queryClient.getQueryCache().subscribe(() => {
17
+ if (fetchingActions(queryClient) === 0)
18
+ done();
19
+ });
20
+ const timer = setTimeout(done, timeoutMs);
21
+ });
22
+ }
23
+ export function createAgentUiRuntime({ client, queryClient, router, }) {
24
+ return createAgentUiClient({
25
+ router,
26
+ queries: {
27
+ active: () => queryClient
28
+ .getQueryCache()
29
+ .findAll({ queryKey: ACTION_QUERY_PREFIX, type: "active" })
30
+ .map((query) => ({
31
+ action: String(query.queryKey[ACTION_QUERY_PREFIX.length]),
32
+ input: query.queryKey[ACTION_QUERY_PREFIX.length + 1] ?? {},
33
+ status: query.state.status,
34
+ })),
35
+ settled: (timeoutMs) => actionsSettled(queryClient, timeoutMs),
36
+ },
37
+ send: async (message) => {
38
+ const response = await client.request(`${client.basePath}/ui`, {
39
+ method: "POST",
40
+ headers: { "content-type": "application/json" },
41
+ body: JSON.stringify(message),
42
+ keepalive: message.type !== "reply",
43
+ });
44
+ return response.status === 200 ? response.text() : undefined;
45
+ },
46
+ platform: detectPlatform(),
47
+ dev: import.meta.env.DEV,
48
+ });
49
+ }
50
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/react/agent-ui/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAKtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAS1D,SAAS,eAAe,CAAC,WAAwB;IAC/C,OAAO,WAAW,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,cAAc,CAAC,WAAwB,EAAE,SAAiB;IACjE,IAAI,eAAe,CAAC,WAAW,CAAC,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IACjE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,WAAW,EAAE,CAAC;YACd,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,WAAW,GAAG,WAAW,CAAC,aAAa,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE;YAC7D,IAAI,eAAe,CAAC,WAAW,CAAC,KAAK,CAAC;gBAAE,IAAI,EAAE,CAAC;QACjD,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EACnC,MAAM,EACN,WAAW,EACX,MAAM,GACc;IACpB,OAAO,mBAAmB,CAAC;QACzB,MAAM;QACN,OAAO,EAAE;YACP,MAAM,EAAE,GAAG,EAAE,CACX,WAAW;iBACR,aAAa,EAAE;iBACf,OAAO,CAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;iBAC1D,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACf,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;gBAC1D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE;gBAC3D,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;aAC3B,CAAC,CAAC;YACP,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC;SAC/D;QACD,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACtB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,QAAQ,KAAK,EAAE;gBAC7D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,SAAS,EAAE,OAAO,CAAC,IAAI,KAAK,OAAO;aACpC,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,CAAC;QACD,QAAQ,EAAE,cAAc,EAAE;QAC1B,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG;KACzB,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type AgentUiClient, createAgentUiClient } from \"@parlor/agent-ui/client\";\nimport { detectPlatform } from \"@parlor/capabilities\";\nimport type { QueryClient } from \"@tanstack/react-query\";\nimport type { AnyRouter } from \"@tanstack/react-router\";\n\nimport type { ActionClient } from \"../../action-client.js\";\nimport { ACTION_QUERY_PREFIX } from \"../../query-keys.js\";\nimport type { RegisteredActions } from \"../../register.js\";\n\nexport interface AgentUiRuntimeInput {\n readonly client: ActionClient<RegisteredActions>;\n readonly queryClient: QueryClient;\n readonly router: AnyRouter | undefined;\n}\n\nfunction fetchingActions(queryClient: QueryClient): number {\n return queryClient.isFetching({ queryKey: ACTION_QUERY_PREFIX });\n}\n\nfunction actionsSettled(queryClient: QueryClient, timeoutMs: number): Promise<void> {\n if (fetchingActions(queryClient) === 0) return Promise.resolve();\n return new Promise((resolve) => {\n const done = () => {\n unsubscribe();\n clearTimeout(timer);\n resolve();\n };\n const unsubscribe = queryClient.getQueryCache().subscribe(() => {\n if (fetchingActions(queryClient) === 0) done();\n });\n const timer = setTimeout(done, timeoutMs);\n });\n}\n\nexport function createAgentUiRuntime({\n client,\n queryClient,\n router,\n}: AgentUiRuntimeInput): AgentUiClient {\n return createAgentUiClient({\n router,\n queries: {\n active: () =>\n queryClient\n .getQueryCache()\n .findAll({ queryKey: ACTION_QUERY_PREFIX, type: \"active\" })\n .map((query) => ({\n action: String(query.queryKey[ACTION_QUERY_PREFIX.length]),\n input: query.queryKey[ACTION_QUERY_PREFIX.length + 1] ?? {},\n status: query.state.status,\n })),\n settled: (timeoutMs) => actionsSettled(queryClient, timeoutMs),\n },\n send: async (message) => {\n const response = await client.request(`${client.basePath}/ui`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(message),\n keepalive: message.type !== \"reply\",\n });\n return response.status === 200 ? response.text() : undefined;\n },\n platform: detectPlatform(),\n dev: import.meta.env.DEV,\n });\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { AgentActivity } from "@parlor/agent-ui/client";
2
+ export declare function useAgentActivity(): AgentActivity;
3
+ //# sourceMappingURL=use-agent-activity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-activity.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/use-agent-activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAK7D,wBAAgB,gBAAgB,IAAI,aAAa,CAGhD"}
@@ -0,0 +1,7 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { useFramework } from "../provider.js";
3
+ export function useAgentActivity() {
4
+ const { activity } = useFramework().agentUi;
5
+ return useSyncExternalStore(activity.subscribe, activity.get, activity.get);
6
+ }
7
+ //# sourceMappingURL=use-agent-activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-activity.js","sourceRoot":"","sources":["../../../src/react/agent-ui/use-agent-activity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAAC,OAAO,CAAC;IAC5C,OAAO,oBAAoB,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["import type { AgentActivity } from \"@parlor/agent-ui/client\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useFramework } from \"../provider.js\";\n\nexport function useAgentActivity(): AgentActivity {\n const { activity } = useFramework().agentUi;\n return useSyncExternalStore(activity.subscribe, activity.get, activity.get);\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import type { AgentConfirmation } from "@parlor/agent-ui/client";
2
+ /**
3
+ * The agent requests waiting for this user's approval on this client, newest last. Headless:
4
+ * the app draws the card and calls `approve` or `decline`. Rendering this hook is what tells
5
+ * the server this client can show a confirmation; render it where it is always mounted.
6
+ */
7
+ export declare function useAgentConfirmations(): readonly AgentConfirmation[];
8
+ //# sourceMappingURL=use-agent-confirmations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-confirmations.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/use-agent-confirmations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAKjE;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,iBAAiB,EAAE,CAOpE"}
@@ -0,0 +1,12 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { useFramework } from "../provider.js";
3
+ /**
4
+ * The agent requests waiting for this user's approval on this client, newest last. Headless:
5
+ * the app draws the card and calls `approve` or `decline`. Rendering this hook is what tells
6
+ * the server this client can show a confirmation; render it where it is always mounted.
7
+ */
8
+ export function useAgentConfirmations() {
9
+ const { confirmations } = useFramework().agentUi;
10
+ return useSyncExternalStore(confirmations.subscribe, confirmations.get, confirmations.get);
11
+ }
12
+ //# sourceMappingURL=use-agent-confirmations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-confirmations.js","sourceRoot":"","sources":["../../../src/react/agent-ui/use-agent-confirmations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE,CAAC,OAAO,CAAC;IACjD,OAAO,oBAAoB,CACzB,aAAa,CAAC,SAAS,EACvB,aAAa,CAAC,GAAG,EACjB,aAAa,CAAC,GAAG,CAClB,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentConfirmation } from \"@parlor/agent-ui/client\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useFramework } from \"../provider.js\";\n\n/**\n * The agent requests waiting for this user's approval on this client, newest last. Headless:\n * the app draws the card and calls `approve` or `decline`. Rendering this hook is what tells\n * the server this client can show a confirmation; render it where it is always mounted.\n */\nexport function useAgentConfirmations(): readonly AgentConfirmation[] {\n const { confirmations } = useFramework().agentUi;\n return useSyncExternalStore(\n confirmations.subscribe,\n confirmations.get,\n confirmations.get,\n );\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { AgentViewBinding, AnyAgentView, WritableField } from "@parlor/agent-ui";
2
+ export interface AgentViewHandle<D extends AnyAgentView = AnyAgentView> {
3
+ readonly ref: (element: Element | null) => void;
4
+ /**
5
+ * Fields the agent wrote that still hold what it wrote, for the app to style; the user
6
+ * changing a field removes it. Always empty for a read-only view.
7
+ */
8
+ readonly agentFields: ReadonlySet<WritableField<D>>;
9
+ }
10
+ export declare function useAgentView<D extends AnyAgentView>(view: D, binding: AgentViewBinding<D>): AgentViewHandle<D>;
11
+ //# sourceMappingURL=use-agent-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-view.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/use-agent-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOtF,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IACpE,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;CACrD;AAYD,wBAAgB,YAAY,CAAC,CAAC,SAAS,YAAY,EACjD,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC3B,eAAe,CAAC,CAAC,CAAC,CASpB"}
@@ -0,0 +1,25 @@
1
+ import { agentFieldsOf } from "@parlor/agent-ui/client";
2
+ import { useEffect, useInsertionEffect, useState, useSyncExternalStore } from "react";
3
+ import { flushSync } from "react-dom";
4
+ import { useFramework } from "../provider.js";
5
+ function committing(binding) {
6
+ const { set } = binding;
7
+ if (!set)
8
+ return binding;
9
+ return {
10
+ key: binding.key,
11
+ get: binding.get,
12
+ set: (next, changed) => flushSync(() => set(next, changed)),
13
+ };
14
+ }
15
+ export function useAgentView(view, binding) {
16
+ const { agentUi } = useFramework();
17
+ const erased = committing(binding);
18
+ const [slot] = useState(() => agentUi.views.track(view, erased));
19
+ useInsertionEffect(() => slot.bind(erased));
20
+ useEffect(() => slot.mount(binding.key), [slot, binding.key]);
21
+ const writes = useSyncExternalStore(slot.subscribeWrites, slot.writes, slot.writes);
22
+ const agentFields = agentFieldsOf(writes, writes.size === 0 ? {} : binding.get());
23
+ return { ref: slot.ref, agentFields };
24
+ }
25
+ //# sourceMappingURL=use-agent-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-view.js","sourceRoot":"","sources":["../../../src/react/agent-ui/use-agent-view.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAuB,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAW9C,SAAS,UAAU,CAAC,OAAuC;IACzD,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IACxB,IAAI,CAAC,GAAG;QAAE,OAAO,OAAO,CAAC;IACzB,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,IAAO,EACP,OAA4B;IAE5B,MAAM,EAAE,OAAO,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,UAAU,CAAC,OAAoD,CAAC,CAAC;IAChF,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACpF,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,WAAW,EAAwB,CAAC;AAC9D,CAAC","sourcesContent":["import type { AgentViewBinding, AnyAgentView, WritableField } from \"@parlor/agent-ui\";\nimport { agentFieldsOf, type AnyViewBinding } from \"@parlor/agent-ui/client\";\nimport { useEffect, useInsertionEffect, useState, useSyncExternalStore } from \"react\";\nimport { flushSync } from \"react-dom\";\n\nimport { useFramework } from \"../provider.js\";\n\nexport interface AgentViewHandle<D extends AnyAgentView = AnyAgentView> {\n readonly ref: (element: Element | null) => void;\n /**\n * Fields the agent wrote that still hold what it wrote, for the app to style; the user\n * changing a field removes it. Always empty for a read-only view.\n */\n readonly agentFields: ReadonlySet<WritableField<D>>;\n}\n\nfunction committing(binding: AgentViewBinding<AnyAgentView>): AnyViewBinding {\n const { set } = binding;\n if (!set) return binding;\n return {\n key: binding.key,\n get: binding.get,\n set: (next, changed) => flushSync(() => set(next, changed)),\n };\n}\n\nexport function useAgentView<D extends AnyAgentView>(\n view: D,\n binding: AgentViewBinding<D>,\n): AgentViewHandle<D> {\n const { agentUi } = useFramework();\n const erased = committing(binding as unknown as AgentViewBinding<AnyAgentView>);\n const [slot] = useState(() => agentUi.views.track(view, erased));\n useInsertionEffect(() => slot.bind(erased));\n useEffect(() => slot.mount(binding.key), [slot, binding.key]);\n const writes = useSyncExternalStore(slot.subscribeWrites, slot.writes, slot.writes);\n const agentFields = agentFieldsOf(writes, writes.size === 0 ? {} : binding.get());\n return { ref: slot.ref, agentFields } as AgentViewHandle<D>;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ export interface AskField {
2
+ readonly name: string;
3
+ readonly label: string;
4
+ readonly type: "number" | "checkbox" | "text";
5
+ readonly integer: boolean;
6
+ readonly required: boolean;
7
+ }
8
+ export type AskValues = Record<string, string | number | boolean>;
9
+ /** One field per property of a form ask's JSON Schema object, in schema order. */
10
+ export declare function askFields(form: Readonly<Record<string, unknown>>): AskField[];
11
+ /** What a filled form answers: numbers as numbers, checkboxes as booleans, blanks left out. */
12
+ export declare function askValues(fields: readonly AskField[], form: HTMLFormElement): AskValues;
13
+ //# sourceMappingURL=ask-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask-fields.d.ts","sourceRoot":"","sources":["../../../src/react/chat/ask-fields.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAkBlE,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,QAAQ,EAAE,CAY7E;AAED,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,GAAG,SAAS,CAWvF"}
@@ -0,0 +1,46 @@
1
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
+ function typeOf(schema) {
3
+ switch (schema.type) {
4
+ case "integer":
5
+ return { type: "number", integer: true };
6
+ case "number":
7
+ return { type: "number", integer: false };
8
+ case "boolean":
9
+ return { type: "checkbox", integer: false };
10
+ default:
11
+ return { type: "text", integer: false };
12
+ }
13
+ }
14
+ /** One field per property of a form ask's JSON Schema object, in schema order. */
15
+ export function askFields(form) {
16
+ const properties = isRecord(form.properties) ? form.properties : {};
17
+ const required = Array.isArray(form.required) ? form.required : [];
18
+ return Object.entries(properties).map(([name, property]) => {
19
+ const schema = isRecord(property) ? property : {};
20
+ return {
21
+ name,
22
+ label: typeof schema.title === "string" ? schema.title : name,
23
+ required: required.includes(name),
24
+ ...typeOf(schema),
25
+ };
26
+ });
27
+ }
28
+ /** What a filled form answers: numbers as numbers, checkboxes as booleans, blanks left out. */
29
+ export function askValues(fields, form) {
30
+ const values = {};
31
+ for (const field of fields) {
32
+ const input = form.elements.namedItem(field.name);
33
+ if (!(input instanceof HTMLInputElement))
34
+ continue;
35
+ if (field.type === "checkbox")
36
+ values[field.name] = input.checked;
37
+ else if (input.value === "")
38
+ continue;
39
+ else if (field.type === "number")
40
+ values[field.name] = Number(input.value);
41
+ else
42
+ values[field.name] = input.value;
43
+ }
44
+ return values;
45
+ }
46
+ //# sourceMappingURL=ask-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask-fields.js","sourceRoot":"","sources":["../../../src/react/chat/ask-fields.ts"],"names":[],"mappings":"AAUA,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,SAAS,MAAM,CAAC,MAA+B;IAC7C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3C,KAAK,QAAQ;YACX,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5C,KAAK,SAAS;YACZ,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9C;YACE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,SAAS,CAAC,IAAuC;IAC/D,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;QACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YAC7D,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC,GAAG,MAAM,CAAC,MAAM,CAAC;SAClB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,SAAS,CAAC,MAA2B,EAAE,IAAqB;IAC1E,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,CAAC,KAAK,YAAY,gBAAgB,CAAC;YAAE,SAAS;QACnD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;aAC7D,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE;YAAE,SAAS;aACjC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;;YACtE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["export interface AskField {\n readonly name: string;\n readonly label: string;\n readonly type: \"number\" | \"checkbox\" | \"text\";\n readonly integer: boolean;\n readonly required: boolean;\n}\n\nexport type AskValues = Record<string, string | number | boolean>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nfunction typeOf(schema: Record<string, unknown>): Pick<AskField, \"type\" | \"integer\"> {\n switch (schema.type) {\n case \"integer\":\n return { type: \"number\", integer: true };\n case \"number\":\n return { type: \"number\", integer: false };\n case \"boolean\":\n return { type: \"checkbox\", integer: false };\n default:\n return { type: \"text\", integer: false };\n }\n}\n\n/** One field per property of a form ask's JSON Schema object, in schema order. */\nexport function askFields(form: Readonly<Record<string, unknown>>): AskField[] {\n const properties = isRecord(form.properties) ? form.properties : {};\n const required = Array.isArray(form.required) ? form.required : [];\n return Object.entries(properties).map(([name, property]) => {\n const schema = isRecord(property) ? property : {};\n return {\n name,\n label: typeof schema.title === \"string\" ? schema.title : name,\n required: required.includes(name),\n ...typeOf(schema),\n };\n });\n}\n\n/** What a filled form answers: numbers as numbers, checkboxes as booleans, blanks left out. */\nexport function askValues(fields: readonly AskField[], form: HTMLFormElement): AskValues {\n const values: AskValues = {};\n for (const field of fields) {\n const input = form.elements.namedItem(field.name);\n if (!(input instanceof HTMLInputElement)) continue;\n if (field.type === \"checkbox\") values[field.name] = input.checked;\n else if (input.value === \"\") continue;\n else if (field.type === \"number\") values[field.name] = Number(input.value);\n else values[field.name] = input.value;\n }\n return values;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import type { AgentConfirmation } from "@parlor/agent-ui/client";
2
+ import type { ChatItem } from "@parlor/chat";
3
+ import { type ComponentProps, type ReactNode } from "react";
4
+ type AskItem = Extract<ChatItem<AgentConfirmation>, {
5
+ kind: "ask";
6
+ }>;
7
+ export interface ChatAskProps extends ComponentProps<"div"> {
8
+ readonly item: AskItem;
9
+ }
10
+ /**
11
+ * The agent's own permission prompt or question: `role="group"` named by its question.
12
+ * `data-approval` is `presence` (answering triggers the OS prompt) or `computer` (answered
13
+ * on the computer; choices disabled). `data-state` is `open`, `answered` or `closed`.
14
+ * Escape declines.
15
+ */
16
+ export declare function Ask({ item, onKeyDown, ...props }: ChatAskProps): ReactNode;
17
+ export interface ChatAskChoiceProps extends ComponentProps<"button"> {
18
+ readonly choice: AskItem["choices"][number];
19
+ }
20
+ /** `type="button"`; answers the enclosing ask with this choice. */
21
+ export declare function AskChoice({ choice, onClick, ...props }: ChatAskChoiceProps): ReactNode;
22
+ export type ChatAskFormProps = ComponentProps<"form">;
23
+ /**
24
+ * A form ask's `<form>`: one `<input name="{property}">` per property of its schema (`number`
25
+ * for numbers and integers, `checkbox` for booleans, text otherwise). Submitting answers the
26
+ * ask with its allow choice and the values typed per the schema. Its children, a submit
27
+ * button for one, go after the inputs. Everything is disabled once the ask is not open or
28
+ * is answered on the computer. Renders nothing for an ask without a form.
29
+ */
30
+ export declare function AskForm({ onSubmit, children, ...props }: ChatAskFormProps): ReactNode;
31
+ export {};
32
+ //# sourceMappingURL=chat-ask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-ask.d.ts","sourceRoot":"","sources":["../../../src/react/chat/chat-ask.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAKf,KAAK,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAOrE,MAAM,WAAW,YAAa,SAAQ,cAAc,CAAC,KAAK,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,GAAG,SAAS,CAyB1E;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc,CAAC,QAAQ,CAAC;IAClE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAmBtF;AAID,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,GAAG,SAAS,CA2BrF"}