canvu-react 0.3.5

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/camera-BwQjm5oh.d.cts +50 -0
  4. package/dist/camera-KwCYYPhm.d.ts +50 -0
  5. package/dist/chatbot.cjs +221 -0
  6. package/dist/chatbot.cjs.map +1 -0
  7. package/dist/chatbot.d.cts +36 -0
  8. package/dist/chatbot.d.ts +36 -0
  9. package/dist/chatbot.js +218 -0
  10. package/dist/chatbot.js.map +1 -0
  11. package/dist/index.cjs +1920 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +276 -0
  14. package/dist/index.d.ts +276 -0
  15. package/dist/index.js +1867 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/native.cjs +2572 -0
  18. package/dist/native.cjs.map +1 -0
  19. package/dist/native.d.cts +217 -0
  20. package/dist/native.d.ts +217 -0
  21. package/dist/native.js +2562 -0
  22. package/dist/native.js.map +1 -0
  23. package/dist/react.cjs +8540 -0
  24. package/dist/react.cjs.map +1 -0
  25. package/dist/react.d.cts +481 -0
  26. package/dist/react.d.ts +481 -0
  27. package/dist/react.js +8492 -0
  28. package/dist/react.js.map +1 -0
  29. package/dist/realtime.cjs +2338 -0
  30. package/dist/realtime.cjs.map +1 -0
  31. package/dist/realtime.d.cts +309 -0
  32. package/dist/realtime.d.ts +309 -0
  33. package/dist/realtime.js +2317 -0
  34. package/dist/realtime.js.map +1 -0
  35. package/dist/shape-builders-DTYvub8W.d.ts +93 -0
  36. package/dist/shape-builders-DxPoOecg.d.cts +93 -0
  37. package/dist/tldraw.cjs +1948 -0
  38. package/dist/tldraw.cjs.map +1 -0
  39. package/dist/tldraw.d.cts +98 -0
  40. package/dist/tldraw.d.ts +98 -0
  41. package/dist/tldraw.js +1941 -0
  42. package/dist/tldraw.js.map +1 -0
  43. package/dist/types--ALu1mF-.d.ts +356 -0
  44. package/dist/types-B58i5k-u.d.cts +35 -0
  45. package/dist/types-CB0TZZuk.d.cts +157 -0
  46. package/dist/types-CB0TZZuk.d.ts +157 -0
  47. package/dist/types-D1ftVsOQ.d.cts +356 -0
  48. package/dist/types-DgEArHkA.d.ts +35 -0
  49. package/package.json +103 -0
@@ -0,0 +1,218 @@
1
+ import { useChat } from '@ai-sdk/react';
2
+ import { DefaultChatTransport } from 'ai';
3
+ import { useId, useMemo, useState } from 'react';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ // src/react/plugins/chatbot/message-text.ts
7
+ function getMessageText(message) {
8
+ const parts = message.parts;
9
+ if (!parts?.length) return "";
10
+ return parts.filter((p) => p.type === "text").map((p) => p.text).join("");
11
+ }
12
+ var shell = {
13
+ position: "absolute",
14
+ right: 12,
15
+ bottom: 12,
16
+ width: 360,
17
+ maxWidth: "calc(100% - 24px)",
18
+ maxHeight: "min(420px, 70dvh)",
19
+ display: "flex",
20
+ flexDirection: "column",
21
+ borderRadius: 12,
22
+ border: "1px solid rgba(0,0,0,0.12)",
23
+ background: "rgba(255,255,255,0.97)",
24
+ boxShadow: "0 8px 24px rgba(0,0,0,0.1)",
25
+ pointerEvents: "auto",
26
+ overflow: "hidden"
27
+ };
28
+ var header = {
29
+ display: "flex",
30
+ alignItems: "center",
31
+ justifyContent: "space-between",
32
+ padding: "8px 12px",
33
+ borderBottom: "1px solid #e4e4e7",
34
+ fontSize: "0.8125rem",
35
+ fontWeight: 700,
36
+ color: "#18181b"
37
+ };
38
+ var messagesBox = {
39
+ flex: 1,
40
+ minHeight: 0,
41
+ overflowY: "auto",
42
+ padding: "10px 12px",
43
+ fontSize: "0.8125rem",
44
+ display: "flex",
45
+ flexDirection: "column",
46
+ gap: 8
47
+ };
48
+ var bubble = (role) => ({
49
+ alignSelf: role === "user" ? "flex-end" : "flex-start",
50
+ maxWidth: "92%",
51
+ padding: "8px 10px",
52
+ borderRadius: 10,
53
+ background: role === "user" ? "#2563eb" : "#f4f4f5",
54
+ color: role === "user" ? "#fff" : "#18181b",
55
+ whiteSpace: "pre-wrap",
56
+ wordBreak: "break-word"
57
+ });
58
+ var formStyle = {
59
+ display: "flex",
60
+ gap: 8,
61
+ padding: 8,
62
+ borderTop: "1px solid #e4e4e7"
63
+ };
64
+ var inputStyle = {
65
+ flex: 1,
66
+ minWidth: 0,
67
+ padding: "8px 10px",
68
+ borderRadius: 8,
69
+ border: "1px solid #d4d4d8",
70
+ fontSize: "0.8125rem",
71
+ outline: "none"
72
+ };
73
+ var btn = {
74
+ padding: "8px 12px",
75
+ borderRadius: 8,
76
+ border: "none",
77
+ background: "#18181b",
78
+ color: "#fff",
79
+ fontSize: "0.8125rem",
80
+ fontWeight: 600,
81
+ cursor: "pointer"
82
+ };
83
+ function ChatbotPluginPanel({
84
+ chatApi,
85
+ title = "Assistente"
86
+ }) {
87
+ const reactId = useId();
88
+ const chatId = useMemo(
89
+ () => `trazo-chatbot-${reactId.replace(/[^a-zA-Z0-9_-]/g, "_")}`,
90
+ [reactId]
91
+ );
92
+ const transport = useMemo(
93
+ () => new DefaultChatTransport({ api: chatApi }),
94
+ [chatApi]
95
+ );
96
+ const { messages, sendMessage, status, error, stop } = useChat({
97
+ id: chatId,
98
+ transport
99
+ });
100
+ const [input, setInput] = useState("");
101
+ const [open, setOpen] = useState(true);
102
+ const onSubmit = (e) => {
103
+ e.preventDefault();
104
+ const t = input.trim();
105
+ if (!t || status === "streaming" || status === "submitted") return;
106
+ void sendMessage({ text: t });
107
+ setInput("");
108
+ };
109
+ if (!open) {
110
+ return /* @__PURE__ */ jsx(
111
+ "button",
112
+ {
113
+ type: "button",
114
+ style: {
115
+ ...btn,
116
+ position: "absolute",
117
+ right: 12,
118
+ bottom: 12,
119
+ pointerEvents: "auto",
120
+ borderRadius: 999,
121
+ padding: "10px 14px"
122
+ },
123
+ onClick: () => setOpen(true),
124
+ children: "Chat"
125
+ }
126
+ );
127
+ }
128
+ return /* @__PURE__ */ jsxs("aside", { style: shell, "aria-label": title, children: [
129
+ /* @__PURE__ */ jsxs("div", { style: header, children: [
130
+ /* @__PURE__ */ jsx("span", { children: title }),
131
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
132
+ (status === "streaming" || status === "submitted") && /* @__PURE__ */ jsx(
133
+ "button",
134
+ {
135
+ type: "button",
136
+ onClick: () => void stop(),
137
+ style: {
138
+ fontSize: "0.75rem",
139
+ color: "#71717a",
140
+ background: "none",
141
+ border: "none",
142
+ cursor: "pointer"
143
+ },
144
+ children: "Parar"
145
+ }
146
+ ),
147
+ /* @__PURE__ */ jsx(
148
+ "button",
149
+ {
150
+ type: "button",
151
+ onClick: () => setOpen(false),
152
+ style: {
153
+ fontSize: "0.75rem",
154
+ color: "#71717a",
155
+ background: "none",
156
+ border: "none",
157
+ cursor: "pointer"
158
+ },
159
+ "aria-label": "Fechar chat",
160
+ children: "\u2212"
161
+ }
162
+ )
163
+ ] })
164
+ ] }),
165
+ /* @__PURE__ */ jsxs("div", { style: messagesBox, children: [
166
+ messages.length === 0 && /* @__PURE__ */ jsxs("p", { style: { margin: 0, color: "#71717a", fontSize: "0.75rem" }, children: [
167
+ "Mensagens aparecem aqui. O backend deve estar em",
168
+ " ",
169
+ /* @__PURE__ */ jsx("code", { style: { fontSize: "0.7rem" }, children: chatApi }),
170
+ "."
171
+ ] }),
172
+ messages.map((m) => /* @__PURE__ */ jsx("div", { style: bubble(m.role), children: getMessageText(m) }, m.id)),
173
+ error && /* @__PURE__ */ jsx(
174
+ "div",
175
+ {
176
+ style: {
177
+ ...bubble("assistant"),
178
+ background: "#fef2f2",
179
+ color: "#b91c1c"
180
+ },
181
+ children: error.message
182
+ }
183
+ )
184
+ ] }),
185
+ /* @__PURE__ */ jsxs("form", { style: formStyle, onSubmit, children: [
186
+ /* @__PURE__ */ jsx(
187
+ "input",
188
+ {
189
+ style: inputStyle,
190
+ value: input,
191
+ onChange: (e) => setInput(e.target.value),
192
+ placeholder: "Escreva uma mensagem\u2026",
193
+ "aria-label": "Mensagem",
194
+ autoComplete: "off"
195
+ }
196
+ ),
197
+ /* @__PURE__ */ jsx(
198
+ "button",
199
+ {
200
+ type: "submit",
201
+ style: btn,
202
+ disabled: status === "streaming" || status === "submitted",
203
+ children: "Enviar"
204
+ }
205
+ )
206
+ ] })
207
+ ] });
208
+ }
209
+ function chatbotPlugin(options) {
210
+ return {
211
+ id: "trazo.plugin.chatbot",
212
+ render: () => /* @__PURE__ */ jsx(ChatbotPluginPanel, { ...options })
213
+ };
214
+ }
215
+
216
+ export { ChatbotPluginPanel, chatbotPlugin };
217
+ //# sourceMappingURL=chatbot.js.map
218
+ //# sourceMappingURL=chatbot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/plugins/chatbot/message-text.ts","../src/react/plugins/chatbot/ChatbotPluginPanel.tsx","../src/react/plugins/chatbot/chatbot-plugin.tsx"],"names":["jsx"],"mappings":"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","file":"chatbot.js","sourcesContent":["import type { UIMessage } from \"ai\";\n\n/** Flattens text parts from a UI message for simple transcript rendering. */\nexport function getMessageText(message: UIMessage): string {\n\tconst parts = message.parts;\n\tif (!parts?.length) return \"\";\n\treturn parts\n\t\t.filter((p): p is { type: \"text\"; text: string } => p.type === \"text\")\n\t\t.map((p) => p.text)\n\t\t.join(\"\");\n}\n","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport { type CSSProperties, type FormEvent, useId, useMemo, useState } from \"react\";\nimport { getMessageText } from \"./message-text\";\n\nconst shell: CSSProperties = {\n\tposition: \"absolute\",\n\tright: 12,\n\tbottom: 12,\n\twidth: 360,\n\tmaxWidth: \"calc(100% - 24px)\",\n\tmaxHeight: \"min(420px, 70dvh)\",\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tborderRadius: 12,\n\tborder: \"1px solid rgba(0,0,0,0.12)\",\n\tbackground: \"rgba(255,255,255,0.97)\",\n\tboxShadow: \"0 8px 24px rgba(0,0,0,0.1)\",\n\tpointerEvents: \"auto\",\n\toverflow: \"hidden\",\n};\n\nconst header: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"space-between\",\n\tpadding: \"8px 12px\",\n\tborderBottom: \"1px solid #e4e4e7\",\n\tfontSize: \"0.8125rem\",\n\tfontWeight: 700,\n\tcolor: \"#18181b\",\n};\n\nconst messagesBox: CSSProperties = {\n\tflex: 1,\n\tminHeight: 0,\n\toverflowY: \"auto\",\n\tpadding: \"10px 12px\",\n\tfontSize: \"0.8125rem\",\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n};\n\nconst bubble = (role: string): CSSProperties => ({\n\talignSelf: role === \"user\" ? \"flex-end\" : \"flex-start\",\n\tmaxWidth: \"92%\",\n\tpadding: \"8px 10px\",\n\tborderRadius: 10,\n\tbackground: role === \"user\" ? \"#2563eb\" : \"#f4f4f5\",\n\tcolor: role === \"user\" ? \"#fff\" : \"#18181b\",\n\twhiteSpace: \"pre-wrap\",\n\twordBreak: \"break-word\",\n});\n\nconst formStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\tgap: 8,\n\tpadding: 8,\n\tborderTop: \"1px solid #e4e4e7\",\n};\n\nconst inputStyle: CSSProperties = {\n\tflex: 1,\n\tminWidth: 0,\n\tpadding: \"8px 10px\",\n\tborderRadius: 8,\n\tborder: \"1px solid #d4d4d8\",\n\tfontSize: \"0.8125rem\",\n\toutline: \"none\",\n};\n\nconst btn: CSSProperties = {\n\tpadding: \"8px 12px\",\n\tborderRadius: 8,\n\tborder: \"none\",\n\tbackground: \"#18181b\",\n\tcolor: \"#fff\",\n\tfontSize: \"0.8125rem\",\n\tfontWeight: 600,\n\tcursor: \"pointer\",\n};\n\nexport type ChatbotPluginPanelProps = {\n\t/**\n\t * Chat completions endpoint (your Next route, Express, or external URL).\n\t * Must implement the [AI SDK UI stream protocol](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot).\n\t */\n\tchatApi: string;\n\ttitle?: string;\n};\n\n/**\n * Floating chat panel powered by `useChat` + {@link DefaultChatTransport}.\n */\nexport function ChatbotPluginPanel({\n\tchatApi,\n\ttitle = \"Assistente\",\n}: ChatbotPluginPanelProps) {\n\tconst reactId = useId();\n\t/** Stable across SSR + hydration; `useChat` otherwise generates a random id per environment. */\n\tconst chatId = useMemo(\n\t\t() => `trazo-chatbot-${reactId.replace(/[^a-zA-Z0-9_-]/g, \"_\")}`,\n\t\t[reactId],\n\t);\n\n\tconst transport = useMemo(\n\t\t() => new DefaultChatTransport({ api: chatApi }),\n\t\t[chatApi],\n\t);\n\n\tconst { messages, sendMessage, status, error, stop } = useChat({\n\t\tid: chatId,\n\t\ttransport,\n\t});\n\n\tconst [input, setInput] = useState(\"\");\n\tconst [open, setOpen] = useState(true);\n\n\tconst onSubmit = (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tconst t = input.trim();\n\t\tif (!t || status === \"streaming\" || status === \"submitted\") return;\n\t\tvoid sendMessage({ text: t });\n\t\tsetInput(\"\");\n\t};\n\n\tif (!open) {\n\t\treturn (\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tstyle={{\n\t\t\t\t\t...btn,\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tright: 12,\n\t\t\t\t\tbottom: 12,\n\t\t\t\t\tpointerEvents: \"auto\",\n\t\t\t\t\tborderRadius: 999,\n\t\t\t\t\tpadding: \"10px 14px\",\n\t\t\t\t}}\n\t\t\t\tonClick={() => setOpen(true)}\n\t\t\t>\n\t\t\t\tChat\n\t\t\t</button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<aside style={shell} aria-label={title}>\n\t\t\t<div style={header}>\n\t\t\t\t<span>{title}</span>\n\t\t\t\t<span style={{ display: \"flex\", gap: 8, alignItems: \"center\" }}>\n\t\t\t\t\t{(status === \"streaming\" || status === \"submitted\") && (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => void stop()}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tfontSize: \"0.75rem\",\n\t\t\t\t\t\t\t\tcolor: \"#71717a\",\n\t\t\t\t\t\t\t\tbackground: \"none\",\n\t\t\t\t\t\t\t\tborder: \"none\",\n\t\t\t\t\t\t\t\tcursor: \"pointer\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tParar\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => setOpen(false)}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontSize: \"0.75rem\",\n\t\t\t\t\t\t\tcolor: \"#71717a\",\n\t\t\t\t\t\t\tbackground: \"none\",\n\t\t\t\t\t\t\tborder: \"none\",\n\t\t\t\t\t\t\tcursor: \"pointer\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t\taria-label=\"Fechar chat\"\n\t\t\t\t\t>\n\t\t\t\t\t\t−\n\t\t\t\t\t</button>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div style={messagesBox}>\n\t\t\t\t{messages.length === 0 && (\n\t\t\t\t\t<p style={{ margin: 0, color: \"#71717a\", fontSize: \"0.75rem\" }}>\n\t\t\t\t\t\tMensagens aparecem aqui. O backend deve estar em{\" \"}\n\t\t\t\t\t\t<code style={{ fontSize: \"0.7rem\" }}>{chatApi}</code>.\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t\t{messages.map((m) => (\n\t\t\t\t\t<div key={m.id} style={bubble(m.role)}>\n\t\t\t\t\t\t{getMessageText(m)}\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t\t{error && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t...bubble(\"assistant\"),\n\t\t\t\t\t\t\tbackground: \"#fef2f2\",\n\t\t\t\t\t\t\tcolor: \"#b91c1c\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{error.message}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<form style={formStyle} onSubmit={onSubmit}>\n\t\t\t\t<input\n\t\t\t\t\tstyle={inputStyle}\n\t\t\t\t\tvalue={input}\n\t\t\t\t\tonChange={(e) => setInput(e.target.value)}\n\t\t\t\t\tplaceholder=\"Escreva uma mensagem…\"\n\t\t\t\t\taria-label=\"Mensagem\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t/>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\tstyle={btn}\n\t\t\t\t\tdisabled={status === \"streaming\" || status === \"submitted\"}\n\t\t\t\t>\n\t\t\t\t\tEnviar\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</aside>\n\t);\n}\n","import type { CanvasPlugin } from \"../types\";\nimport {\n\tChatbotPluginPanel,\n\ttype ChatbotPluginPanelProps,\n} from \"./ChatbotPluginPanel\";\n\nexport type ChatbotPluginOptions = ChatbotPluginPanelProps;\n\n/**\n * First-party plugin: floating chat UI using the Vercel AI SDK (`useChat` + HTTP transport).\n * You **must** host a compatible POST endpoint at `chatApi` (see AI SDK UI chat protocol).\n *\n * @example\n * ```tsx\n * <VectorViewport\n * items={doc.items}\n * onItemsChange={doc.onItemsChange}\n * plugins={[chatbotPlugin({ chatApi: \"/api/chat\" })]}\n * />\n * ```\n */\nexport function chatbotPlugin(options: ChatbotPluginOptions): CanvasPlugin {\n\treturn {\n\t\tid: \"trazo.plugin.chatbot\",\n\t\trender: () => <ChatbotPluginPanel {...options} />,\n\t};\n}\n"]}