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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Canvu contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # canvu
2
+
3
+ `canvu` is a vector-first SVG canvas for the browser with pan, zoom, tools, persistence, plugins, and realtime collaboration.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install canvu react react-dom lucide-react
9
+ ```
10
+
11
+ Required peers:
12
+ - `react`
13
+ - `react-dom`
14
+ - `lucide-react`
15
+
16
+ Optional peers:
17
+ - `@ai-sdk/react`
18
+ - `ai`
19
+
20
+ ## Entry points
21
+
22
+ | Import | Purpose |
23
+ | --- | --- |
24
+ | `canvu` | Core primitives: camera, scene, SVG renderer, input helpers, shape builders |
25
+ | `canvu/react` | React runtime: `VectorViewport`, `VectorToolbar`, persistence, plugin system |
26
+ | `canvu/plugins/chatbot` | Plug and play chatbot plugin |
27
+ | `canvu/plugins/realtime` | Plug and play realtime collaboration plugin + advanced hooks |
28
+ | `canvu/plugins/tldraw` | tldraw import helpers |
29
+
30
+ ## Quick start
31
+
32
+ ```tsx
33
+ import { useState } from "react";
34
+ import {
35
+ useVectorCanvasDocument,
36
+ VectorCanvas,
37
+ VectorToolbar,
38
+ VectorViewport,
39
+ } from "canvu/react";
40
+
41
+ export function Board() {
42
+ const [toolId, setToolId] = useState("hand");
43
+ const [toolLocked, setToolLocked] = useState(false);
44
+ const doc = useVectorCanvasDocument({ persistenceKey: "board.v1" });
45
+
46
+ if (!doc.isHydrated) return null;
47
+
48
+ return (
49
+ <VectorCanvas.Root style={{ height: "100dvh", width: "100%" }}>
50
+ <VectorCanvas.Body>
51
+ <VectorCanvas.Main>
52
+ <VectorCanvas.ViewportSurface>
53
+ <VectorViewport
54
+ ariaLabel="Board"
55
+ toolId={toolId}
56
+ toolLocked={toolLocked}
57
+ onToolChangeRequest={setToolId}
58
+ items={doc.items}
59
+ onItemsChange={doc.onItemsChange}
60
+ interactive
61
+ plugins={[]}
62
+ toolbar={
63
+ <VectorCanvas.Toolbar>
64
+ <VectorToolbar
65
+ value={toolId}
66
+ onChange={setToolId}
67
+ toolLocked={toolLocked}
68
+ onToolLockedChange={setToolLocked}
69
+ aria-label="Canvas tools"
70
+ />
71
+ </VectorCanvas.Toolbar>
72
+ }
73
+ />
74
+ </VectorCanvas.ViewportSurface>
75
+ </VectorCanvas.Main>
76
+ </VectorCanvas.Body>
77
+ </VectorCanvas.Root>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ## Plugin-first architecture
83
+
84
+ The recommended React DX is plugin-first:
85
+
86
+ ```tsx
87
+ import { chatbotPlugin } from "canvu/plugins/chatbot";
88
+ import { realtimeCollaborationPlugin } from "canvu/plugins/realtime";
89
+
90
+ <VectorViewport
91
+ ariaLabel="Board"
92
+ items={doc.items}
93
+ onItemsChange={doc.onItemsChange}
94
+ toolId={toolId}
95
+ onToolChangeRequest={setToolId}
96
+ interactive
97
+ plugins={[
98
+ chatbotPlugin({ chatApi: "/api/chat" }),
99
+ realtimeCollaborationPlugin({
100
+ url,
101
+ roomId,
102
+ peer: {
103
+ id: peerId,
104
+ displayName: "Kalmon",
105
+ color: "#7c3aed",
106
+ image: avatarUrl,
107
+ },
108
+ }),
109
+ ]}
110
+ />
111
+ ```
112
+
113
+ No `CanvasPluginHost` is required. The plugin runtime lives inside `VectorViewport`.
114
+
115
+ ## Custom tools
116
+
117
+ Use `createToolPlugin(...)` for isolated tools.
118
+
119
+ ```tsx
120
+ import { createToolPlugin } from "canvu/react";
121
+ import { Pin } from "lucide-react";
122
+
123
+ const reviewPinPlugin = createToolPlugin({
124
+ id: "review-pin",
125
+ label: "Review",
126
+ shortcutHint: "R",
127
+ icon: <Pin aria-hidden />,
128
+ createItem: ({ id, bounds }) => createReviewPinItem(id, bounds),
129
+ });
130
+
131
+ <VectorViewport plugins={[reviewPinPlugin]} ... />
132
+ ```
133
+
134
+ If the tool belongs to a larger feature, keep it inside that feature plugin.
135
+
136
+ ## Advanced hooks
137
+
138
+ Low-level hooks remain public for advanced customization:
139
+ - `useRealtimeSession(...)`
140
+ - `useRealtimeComments(...)`
141
+ - `useCanvuPluginContext()`
142
+ - `useCanvuViewportContext()`
143
+ - `useCanvuDocumentContext()`
144
+ - `useCanvuPluginContribution(...)`
145
+ - `createCanvuPlugin(...)`
146
+
147
+ Use them for custom plugins and bespoke UIs, not for the common path.
148
+
149
+ ## CSS requirement
150
+
151
+ The interactive surface must use `touch-action: none` so the browser does not steal gestures.
152
+
153
+ ## Security
154
+
155
+ `childrenSvg` is injected via `innerHTML`.
156
+ Only pass trusted or sanitized SVG.
@@ -0,0 +1,50 @@
1
+ import { R as Rect } from './types-CB0TZZuk.cjs';
2
+
3
+ type Camera2DOptions = {
4
+ /** Minimum zoom scale (world units per CSS pixel). */
5
+ minZoom?: number;
6
+ /** Maximum zoom scale. */
7
+ maxZoom?: number;
8
+ };
9
+ /**
10
+ * 2D camera: pan (`x`, `y`) in **screen space** (CSS pixels) and uniform zoom `zoom`.
11
+ *
12
+ * Screen mapping: `screen = world * zoom + cameraOffset`
13
+ * (world origin maps to `(x, y)` on screen).
14
+ */
15
+ declare class Camera2D {
16
+ x: number;
17
+ y: number;
18
+ /** Scale: world units per CSS pixel (larger = more zoomed in). */
19
+ zoom: number;
20
+ readonly minZoom: number;
21
+ readonly maxZoom: number;
22
+ constructor(options?: Camera2DOptions);
23
+ /**
24
+ * Converts a point from world coordinates to CSS pixel coordinates relative to the viewport top-left.
25
+ */
26
+ worldToScreen(worldX: number, worldY: number): {
27
+ screenX: number;
28
+ screenY: number;
29
+ };
30
+ /**
31
+ * Converts a point from CSS pixel coordinates (viewport-relative) to world coordinates.
32
+ */
33
+ screenToWorld(screenX: number, screenY: number): {
34
+ worldX: number;
35
+ worldY: number;
36
+ };
37
+ /**
38
+ * Sets zoom, clamped to `[minZoom, maxZoom]`, optionally anchoring a screen point so it stays under the cursor.
39
+ */
40
+ setZoom(nextZoom: number, anchorScreen?: {
41
+ x: number;
42
+ y: number;
43
+ }): void;
44
+ /**
45
+ * Returns the world-space rectangle visible in a viewport of `viewportWidth` x `viewportHeight` CSS pixels.
46
+ */
47
+ getVisibleWorldRect(viewportWidth: number, viewportHeight: number): Rect;
48
+ }
49
+
50
+ export { Camera2D as C, type Camera2DOptions as a };
@@ -0,0 +1,50 @@
1
+ import { R as Rect } from './types-CB0TZZuk.js';
2
+
3
+ type Camera2DOptions = {
4
+ /** Minimum zoom scale (world units per CSS pixel). */
5
+ minZoom?: number;
6
+ /** Maximum zoom scale. */
7
+ maxZoom?: number;
8
+ };
9
+ /**
10
+ * 2D camera: pan (`x`, `y`) in **screen space** (CSS pixels) and uniform zoom `zoom`.
11
+ *
12
+ * Screen mapping: `screen = world * zoom + cameraOffset`
13
+ * (world origin maps to `(x, y)` on screen).
14
+ */
15
+ declare class Camera2D {
16
+ x: number;
17
+ y: number;
18
+ /** Scale: world units per CSS pixel (larger = more zoomed in). */
19
+ zoom: number;
20
+ readonly minZoom: number;
21
+ readonly maxZoom: number;
22
+ constructor(options?: Camera2DOptions);
23
+ /**
24
+ * Converts a point from world coordinates to CSS pixel coordinates relative to the viewport top-left.
25
+ */
26
+ worldToScreen(worldX: number, worldY: number): {
27
+ screenX: number;
28
+ screenY: number;
29
+ };
30
+ /**
31
+ * Converts a point from CSS pixel coordinates (viewport-relative) to world coordinates.
32
+ */
33
+ screenToWorld(screenX: number, screenY: number): {
34
+ worldX: number;
35
+ worldY: number;
36
+ };
37
+ /**
38
+ * Sets zoom, clamped to `[minZoom, maxZoom]`, optionally anchoring a screen point so it stays under the cursor.
39
+ */
40
+ setZoom(nextZoom: number, anchorScreen?: {
41
+ x: number;
42
+ y: number;
43
+ }): void;
44
+ /**
45
+ * Returns the world-space rectangle visible in a viewport of `viewportWidth` x `viewportHeight` CSS pixels.
46
+ */
47
+ getVisibleWorldRect(viewportWidth: number, viewportHeight: number): Rect;
48
+ }
49
+
50
+ export { Camera2D as C, type Camera2DOptions as a };
@@ -0,0 +1,221 @@
1
+ 'use strict';
2
+
3
+ var react$1 = require('@ai-sdk/react');
4
+ var ai = require('ai');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ // src/react/plugins/chatbot/message-text.ts
9
+ function getMessageText(message) {
10
+ const parts = message.parts;
11
+ if (!parts?.length) return "";
12
+ return parts.filter((p) => p.type === "text").map((p) => p.text).join("");
13
+ }
14
+ var shell = {
15
+ position: "absolute",
16
+ right: 12,
17
+ bottom: 12,
18
+ width: 360,
19
+ maxWidth: "calc(100% - 24px)",
20
+ maxHeight: "min(420px, 70dvh)",
21
+ display: "flex",
22
+ flexDirection: "column",
23
+ borderRadius: 12,
24
+ border: "1px solid rgba(0,0,0,0.12)",
25
+ background: "rgba(255,255,255,0.97)",
26
+ boxShadow: "0 8px 24px rgba(0,0,0,0.1)",
27
+ pointerEvents: "auto",
28
+ overflow: "hidden"
29
+ };
30
+ var header = {
31
+ display: "flex",
32
+ alignItems: "center",
33
+ justifyContent: "space-between",
34
+ padding: "8px 12px",
35
+ borderBottom: "1px solid #e4e4e7",
36
+ fontSize: "0.8125rem",
37
+ fontWeight: 700,
38
+ color: "#18181b"
39
+ };
40
+ var messagesBox = {
41
+ flex: 1,
42
+ minHeight: 0,
43
+ overflowY: "auto",
44
+ padding: "10px 12px",
45
+ fontSize: "0.8125rem",
46
+ display: "flex",
47
+ flexDirection: "column",
48
+ gap: 8
49
+ };
50
+ var bubble = (role) => ({
51
+ alignSelf: role === "user" ? "flex-end" : "flex-start",
52
+ maxWidth: "92%",
53
+ padding: "8px 10px",
54
+ borderRadius: 10,
55
+ background: role === "user" ? "#2563eb" : "#f4f4f5",
56
+ color: role === "user" ? "#fff" : "#18181b",
57
+ whiteSpace: "pre-wrap",
58
+ wordBreak: "break-word"
59
+ });
60
+ var formStyle = {
61
+ display: "flex",
62
+ gap: 8,
63
+ padding: 8,
64
+ borderTop: "1px solid #e4e4e7"
65
+ };
66
+ var inputStyle = {
67
+ flex: 1,
68
+ minWidth: 0,
69
+ padding: "8px 10px",
70
+ borderRadius: 8,
71
+ border: "1px solid #d4d4d8",
72
+ fontSize: "0.8125rem",
73
+ outline: "none"
74
+ };
75
+ var btn = {
76
+ padding: "8px 12px",
77
+ borderRadius: 8,
78
+ border: "none",
79
+ background: "#18181b",
80
+ color: "#fff",
81
+ fontSize: "0.8125rem",
82
+ fontWeight: 600,
83
+ cursor: "pointer"
84
+ };
85
+ function ChatbotPluginPanel({
86
+ chatApi,
87
+ title = "Assistente"
88
+ }) {
89
+ const reactId = react.useId();
90
+ const chatId = react.useMemo(
91
+ () => `trazo-chatbot-${reactId.replace(/[^a-zA-Z0-9_-]/g, "_")}`,
92
+ [reactId]
93
+ );
94
+ const transport = react.useMemo(
95
+ () => new ai.DefaultChatTransport({ api: chatApi }),
96
+ [chatApi]
97
+ );
98
+ const { messages, sendMessage, status, error, stop } = react$1.useChat({
99
+ id: chatId,
100
+ transport
101
+ });
102
+ const [input, setInput] = react.useState("");
103
+ const [open, setOpen] = react.useState(true);
104
+ const onSubmit = (e) => {
105
+ e.preventDefault();
106
+ const t = input.trim();
107
+ if (!t || status === "streaming" || status === "submitted") return;
108
+ void sendMessage({ text: t });
109
+ setInput("");
110
+ };
111
+ if (!open) {
112
+ return /* @__PURE__ */ jsxRuntime.jsx(
113
+ "button",
114
+ {
115
+ type: "button",
116
+ style: {
117
+ ...btn,
118
+ position: "absolute",
119
+ right: 12,
120
+ bottom: 12,
121
+ pointerEvents: "auto",
122
+ borderRadius: 999,
123
+ padding: "10px 14px"
124
+ },
125
+ onClick: () => setOpen(true),
126
+ children: "Chat"
127
+ }
128
+ );
129
+ }
130
+ return /* @__PURE__ */ jsxRuntime.jsxs("aside", { style: shell, "aria-label": title, children: [
131
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: header, children: [
132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
133
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
134
+ (status === "streaming" || status === "submitted") && /* @__PURE__ */ jsxRuntime.jsx(
135
+ "button",
136
+ {
137
+ type: "button",
138
+ onClick: () => void stop(),
139
+ style: {
140
+ fontSize: "0.75rem",
141
+ color: "#71717a",
142
+ background: "none",
143
+ border: "none",
144
+ cursor: "pointer"
145
+ },
146
+ children: "Parar"
147
+ }
148
+ ),
149
+ /* @__PURE__ */ jsxRuntime.jsx(
150
+ "button",
151
+ {
152
+ type: "button",
153
+ onClick: () => setOpen(false),
154
+ style: {
155
+ fontSize: "0.75rem",
156
+ color: "#71717a",
157
+ background: "none",
158
+ border: "none",
159
+ cursor: "pointer"
160
+ },
161
+ "aria-label": "Fechar chat",
162
+ children: "\u2212"
163
+ }
164
+ )
165
+ ] })
166
+ ] }),
167
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: messagesBox, children: [
168
+ messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: 0, color: "#71717a", fontSize: "0.75rem" }, children: [
169
+ "Mensagens aparecem aqui. O backend deve estar em",
170
+ " ",
171
+ /* @__PURE__ */ jsxRuntime.jsx("code", { style: { fontSize: "0.7rem" }, children: chatApi }),
172
+ "."
173
+ ] }),
174
+ messages.map((m) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: bubble(m.role), children: getMessageText(m) }, m.id)),
175
+ error && /* @__PURE__ */ jsxRuntime.jsx(
176
+ "div",
177
+ {
178
+ style: {
179
+ ...bubble("assistant"),
180
+ background: "#fef2f2",
181
+ color: "#b91c1c"
182
+ },
183
+ children: error.message
184
+ }
185
+ )
186
+ ] }),
187
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { style: formStyle, onSubmit, children: [
188
+ /* @__PURE__ */ jsxRuntime.jsx(
189
+ "input",
190
+ {
191
+ style: inputStyle,
192
+ value: input,
193
+ onChange: (e) => setInput(e.target.value),
194
+ placeholder: "Escreva uma mensagem\u2026",
195
+ "aria-label": "Mensagem",
196
+ autoComplete: "off"
197
+ }
198
+ ),
199
+ /* @__PURE__ */ jsxRuntime.jsx(
200
+ "button",
201
+ {
202
+ type: "submit",
203
+ style: btn,
204
+ disabled: status === "streaming" || status === "submitted",
205
+ children: "Enviar"
206
+ }
207
+ )
208
+ ] })
209
+ ] });
210
+ }
211
+ function chatbotPlugin(options) {
212
+ return {
213
+ id: "trazo.plugin.chatbot",
214
+ render: () => /* @__PURE__ */ jsxRuntime.jsx(ChatbotPluginPanel, { ...options })
215
+ };
216
+ }
217
+
218
+ exports.ChatbotPluginPanel = ChatbotPluginPanel;
219
+ exports.chatbotPlugin = chatbotPlugin;
220
+ //# sourceMappingURL=chatbot.cjs.map
221
+ //# sourceMappingURL=chatbot.cjs.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":["useId","useMemo","DefaultChatTransport","useChat","useState","jsx","jsxs"],"mappings":"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","file":"chatbot.cjs","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"]}
@@ -0,0 +1,36 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { C as CanvasPlugin } from './types-D1ftVsOQ.cjs';
3
+ import 'react';
4
+ import './types-CB0TZZuk.cjs';
5
+ import './camera-BwQjm5oh.cjs';
6
+
7
+ type ChatbotPluginPanelProps = {
8
+ /**
9
+ * Chat completions endpoint (your Next route, Express, or external URL).
10
+ * Must implement the [AI SDK UI stream protocol](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot).
11
+ */
12
+ chatApi: string;
13
+ title?: string;
14
+ };
15
+ /**
16
+ * Floating chat panel powered by `useChat` + {@link DefaultChatTransport}.
17
+ */
18
+ declare function ChatbotPluginPanel({ chatApi, title, }: ChatbotPluginPanelProps): react_jsx_runtime.JSX.Element;
19
+
20
+ type ChatbotPluginOptions = ChatbotPluginPanelProps;
21
+ /**
22
+ * First-party plugin: floating chat UI using the Vercel AI SDK (`useChat` + HTTP transport).
23
+ * You **must** host a compatible POST endpoint at `chatApi` (see AI SDK UI chat protocol).
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <VectorViewport
28
+ * items={doc.items}
29
+ * onItemsChange={doc.onItemsChange}
30
+ * plugins={[chatbotPlugin({ chatApi: "/api/chat" })]}
31
+ * />
32
+ * ```
33
+ */
34
+ declare function chatbotPlugin(options: ChatbotPluginOptions): CanvasPlugin;
35
+
36
+ export { type ChatbotPluginOptions, ChatbotPluginPanel, type ChatbotPluginPanelProps, chatbotPlugin };
@@ -0,0 +1,36 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { C as CanvasPlugin } from './types--ALu1mF-.js';
3
+ import 'react';
4
+ import './types-CB0TZZuk.js';
5
+ import './camera-KwCYYPhm.js';
6
+
7
+ type ChatbotPluginPanelProps = {
8
+ /**
9
+ * Chat completions endpoint (your Next route, Express, or external URL).
10
+ * Must implement the [AI SDK UI stream protocol](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot).
11
+ */
12
+ chatApi: string;
13
+ title?: string;
14
+ };
15
+ /**
16
+ * Floating chat panel powered by `useChat` + {@link DefaultChatTransport}.
17
+ */
18
+ declare function ChatbotPluginPanel({ chatApi, title, }: ChatbotPluginPanelProps): react_jsx_runtime.JSX.Element;
19
+
20
+ type ChatbotPluginOptions = ChatbotPluginPanelProps;
21
+ /**
22
+ * First-party plugin: floating chat UI using the Vercel AI SDK (`useChat` + HTTP transport).
23
+ * You **must** host a compatible POST endpoint at `chatApi` (see AI SDK UI chat protocol).
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <VectorViewport
28
+ * items={doc.items}
29
+ * onItemsChange={doc.onItemsChange}
30
+ * plugins={[chatbotPlugin({ chatApi: "/api/chat" })]}
31
+ * />
32
+ * ```
33
+ */
34
+ declare function chatbotPlugin(options: ChatbotPluginOptions): CanvasPlugin;
35
+
36
+ export { type ChatbotPluginOptions, ChatbotPluginPanel, type ChatbotPluginPanelProps, chatbotPlugin };