dynim-react 1.0.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.
- package/README.md +447 -0
- package/dist/builder/BuilderProvider.d.ts +99 -0
- package/dist/builder/BuilderProvider.d.ts.map +1 -0
- package/dist/builder/BuilderProvider.js +339 -0
- package/dist/builder/ChatContext.d.ts +11 -0
- package/dist/builder/ChatContext.d.ts.map +1 -0
- package/dist/builder/ChatContext.js +18 -0
- package/dist/builder/ChatInput.d.ts +11 -0
- package/dist/builder/ChatInput.d.ts.map +1 -0
- package/dist/builder/ChatInput.js +20 -0
- package/dist/builder/CodeChatPanel.d.ts +24 -0
- package/dist/builder/CodeChatPanel.d.ts.map +1 -0
- package/dist/builder/CodeChatPanel.js +299 -0
- package/dist/builder/EditableZone.d.ts +73 -0
- package/dist/builder/EditableZone.d.ts.map +1 -0
- package/dist/builder/EditableZone.js +83 -0
- package/dist/builder/MessageList.d.ts +14 -0
- package/dist/builder/MessageList.d.ts.map +1 -0
- package/dist/builder/MessageList.js +23 -0
- package/dist/builder/index.d.ts +18 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +15 -0
- package/dist/builder/useChatbot.d.ts +24 -0
- package/dist/builder/useChatbot.d.ts.map +1 -0
- package/dist/builder/useChatbot.js +85 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/inference/DynimProvider.d.ts +23 -0
- package/dist/inference/DynimProvider.d.ts.map +1 -0
- package/dist/inference/DynimProvider.js +231 -0
- package/dist/inference/InferenceProvider.d.ts +23 -0
- package/dist/inference/InferenceProvider.d.ts.map +1 -0
- package/dist/inference/InferenceProvider.js +270 -0
- package/dist/inference/createDynimSDK.d.ts +39 -0
- package/dist/inference/createDynimSDK.d.ts.map +1 -0
- package/dist/inference/createDynimSDK.js +61 -0
- package/dist/inference/index.d.ts +7 -0
- package/dist/inference/index.d.ts.map +1 -0
- package/dist/inference/index.js +7 -0
- package/dist/inference/sharedContext.d.ts +39 -0
- package/dist/inference/sharedContext.d.ts.map +1 -0
- package/dist/inference/sharedContext.js +61 -0
- package/dist/inference/types.d.ts +67 -0
- package/dist/inference/types.d.ts.map +1 -0
- package/dist/inference/types.js +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* CodeChatPanel - Self-contained code chat UI component
|
|
4
|
+
*
|
|
5
|
+
* Handles all the complexity internally:
|
|
6
|
+
* - Streaming responses with thinking/text differentiation
|
|
7
|
+
* - Code edit display with accept/reject
|
|
8
|
+
* - Loading states, errors
|
|
9
|
+
*
|
|
10
|
+
* Consuming app just drops this in and it works.
|
|
11
|
+
*/
|
|
12
|
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
13
|
+
import { useBuilder } from './BuilderProvider';
|
|
14
|
+
// Inline styles to make component self-contained
|
|
15
|
+
const styles = {
|
|
16
|
+
container: {
|
|
17
|
+
display: 'flex',
|
|
18
|
+
flexDirection: 'column',
|
|
19
|
+
height: '100%',
|
|
20
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
21
|
+
fontSize: '14px',
|
|
22
|
+
backgroundColor: '#fafafa',
|
|
23
|
+
borderRadius: '8px',
|
|
24
|
+
overflow: 'hidden',
|
|
25
|
+
},
|
|
26
|
+
messagesArea: {
|
|
27
|
+
flex: 1,
|
|
28
|
+
overflowY: 'auto',
|
|
29
|
+
padding: '16px',
|
|
30
|
+
},
|
|
31
|
+
messageBlock: {
|
|
32
|
+
marginBottom: '16px',
|
|
33
|
+
},
|
|
34
|
+
thinkingSection: {
|
|
35
|
+
marginBottom: '12px',
|
|
36
|
+
},
|
|
37
|
+
thinkingHeader: {
|
|
38
|
+
display: 'flex',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
gap: '8px',
|
|
41
|
+
padding: '8px 12px',
|
|
42
|
+
backgroundColor: '#f0f0f0',
|
|
43
|
+
borderRadius: '6px',
|
|
44
|
+
cursor: 'pointer',
|
|
45
|
+
userSelect: 'none',
|
|
46
|
+
fontSize: '13px',
|
|
47
|
+
color: '#666',
|
|
48
|
+
},
|
|
49
|
+
thinkingContent: {
|
|
50
|
+
padding: '12px',
|
|
51
|
+
backgroundColor: '#f8f8f8',
|
|
52
|
+
borderRadius: '0 0 6px 6px',
|
|
53
|
+
borderLeft: '3px solid #ddd',
|
|
54
|
+
marginTop: '-6px',
|
|
55
|
+
fontSize: '13px',
|
|
56
|
+
color: '#555',
|
|
57
|
+
whiteSpace: 'pre-wrap',
|
|
58
|
+
lineHeight: '1.5',
|
|
59
|
+
},
|
|
60
|
+
responseText: {
|
|
61
|
+
padding: '12px 16px',
|
|
62
|
+
backgroundColor: '#fff',
|
|
63
|
+
borderRadius: '8px',
|
|
64
|
+
border: '1px solid #e0e0e0',
|
|
65
|
+
whiteSpace: 'pre-wrap',
|
|
66
|
+
lineHeight: '1.6',
|
|
67
|
+
},
|
|
68
|
+
editsSection: {
|
|
69
|
+
marginTop: '16px',
|
|
70
|
+
},
|
|
71
|
+
editsHeader: {
|
|
72
|
+
display: 'flex',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
justifyContent: 'space-between',
|
|
75
|
+
marginBottom: '12px',
|
|
76
|
+
},
|
|
77
|
+
editsTitle: {
|
|
78
|
+
fontWeight: 600,
|
|
79
|
+
color: '#333',
|
|
80
|
+
},
|
|
81
|
+
editsActions: {
|
|
82
|
+
display: 'flex',
|
|
83
|
+
gap: '8px',
|
|
84
|
+
},
|
|
85
|
+
editCard: {
|
|
86
|
+
backgroundColor: '#fff',
|
|
87
|
+
border: '1px solid #e0e0e0',
|
|
88
|
+
borderRadius: '6px',
|
|
89
|
+
marginBottom: '8px',
|
|
90
|
+
overflow: 'hidden',
|
|
91
|
+
},
|
|
92
|
+
editHeader: {
|
|
93
|
+
display: 'flex',
|
|
94
|
+
alignItems: 'center',
|
|
95
|
+
justifyContent: 'space-between',
|
|
96
|
+
padding: '8px 12px',
|
|
97
|
+
backgroundColor: '#f5f5f5',
|
|
98
|
+
borderBottom: '1px solid #e0e0e0',
|
|
99
|
+
},
|
|
100
|
+
editPath: {
|
|
101
|
+
fontFamily: 'monospace',
|
|
102
|
+
fontSize: '12px',
|
|
103
|
+
color: '#666',
|
|
104
|
+
},
|
|
105
|
+
editType: {
|
|
106
|
+
fontSize: '11px',
|
|
107
|
+
padding: '2px 6px',
|
|
108
|
+
borderRadius: '3px',
|
|
109
|
+
fontWeight: 500,
|
|
110
|
+
},
|
|
111
|
+
editTypeCreate: {
|
|
112
|
+
backgroundColor: '#d4edda',
|
|
113
|
+
color: '#155724',
|
|
114
|
+
},
|
|
115
|
+
editTypeReplace: {
|
|
116
|
+
backgroundColor: '#fff3cd',
|
|
117
|
+
color: '#856404',
|
|
118
|
+
},
|
|
119
|
+
editTypeDelete: {
|
|
120
|
+
backgroundColor: '#f8d7da',
|
|
121
|
+
color: '#721c24',
|
|
122
|
+
},
|
|
123
|
+
editContent: {
|
|
124
|
+
padding: '8px 12px',
|
|
125
|
+
fontFamily: 'monospace',
|
|
126
|
+
fontSize: '12px',
|
|
127
|
+
maxHeight: '200px',
|
|
128
|
+
overflowY: 'auto',
|
|
129
|
+
},
|
|
130
|
+
diffLine: {
|
|
131
|
+
padding: '1px 4px',
|
|
132
|
+
whiteSpace: 'pre',
|
|
133
|
+
},
|
|
134
|
+
diffAdd: {
|
|
135
|
+
backgroundColor: '#d4edda',
|
|
136
|
+
color: '#155724',
|
|
137
|
+
},
|
|
138
|
+
diffRemove: {
|
|
139
|
+
backgroundColor: '#f8d7da',
|
|
140
|
+
color: '#721c24',
|
|
141
|
+
},
|
|
142
|
+
button: {
|
|
143
|
+
padding: '6px 12px',
|
|
144
|
+
borderRadius: '4px',
|
|
145
|
+
border: 'none',
|
|
146
|
+
cursor: 'pointer',
|
|
147
|
+
fontSize: '13px',
|
|
148
|
+
fontWeight: 500,
|
|
149
|
+
transition: 'background-color 0.2s',
|
|
150
|
+
},
|
|
151
|
+
buttonPrimary: {
|
|
152
|
+
backgroundColor: '#4CAF50',
|
|
153
|
+
color: '#fff',
|
|
154
|
+
},
|
|
155
|
+
buttonSecondary: {
|
|
156
|
+
backgroundColor: '#f0f0f0',
|
|
157
|
+
color: '#333',
|
|
158
|
+
},
|
|
159
|
+
buttonDisabled: {
|
|
160
|
+
opacity: 0.5,
|
|
161
|
+
cursor: 'not-allowed',
|
|
162
|
+
},
|
|
163
|
+
inputArea: {
|
|
164
|
+
padding: '12px 16px',
|
|
165
|
+
borderTop: '1px solid #e0e0e0',
|
|
166
|
+
backgroundColor: '#fff',
|
|
167
|
+
},
|
|
168
|
+
form: {
|
|
169
|
+
display: 'flex',
|
|
170
|
+
gap: '8px',
|
|
171
|
+
},
|
|
172
|
+
input: {
|
|
173
|
+
flex: 1,
|
|
174
|
+
padding: '10px 14px',
|
|
175
|
+
borderRadius: '6px',
|
|
176
|
+
border: '1px solid #ddd',
|
|
177
|
+
fontSize: '14px',
|
|
178
|
+
outline: 'none',
|
|
179
|
+
},
|
|
180
|
+
statusBar: {
|
|
181
|
+
padding: '8px 16px',
|
|
182
|
+
fontSize: '12px',
|
|
183
|
+
color: '#666',
|
|
184
|
+
backgroundColor: '#f5f5f5',
|
|
185
|
+
borderTop: '1px solid #e0e0e0',
|
|
186
|
+
},
|
|
187
|
+
streamingDot: {
|
|
188
|
+
display: 'inline-block',
|
|
189
|
+
width: '8px',
|
|
190
|
+
height: '8px',
|
|
191
|
+
borderRadius: '50%',
|
|
192
|
+
backgroundColor: '#4CAF50',
|
|
193
|
+
marginRight: '8px',
|
|
194
|
+
animation: 'pulse 1s infinite',
|
|
195
|
+
},
|
|
196
|
+
errorBar: {
|
|
197
|
+
padding: '12px 16px',
|
|
198
|
+
backgroundColor: '#f8d7da',
|
|
199
|
+
color: '#721c24',
|
|
200
|
+
borderTop: '1px solid #f5c6cb',
|
|
201
|
+
},
|
|
202
|
+
emptyState: {
|
|
203
|
+
display: 'flex',
|
|
204
|
+
alignItems: 'center',
|
|
205
|
+
justifyContent: 'center',
|
|
206
|
+
height: '100%',
|
|
207
|
+
color: '#999',
|
|
208
|
+
fontSize: '14px',
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
export function CodeChatPanel({ placeholder = 'Ask me to make changes to your code...', className = '', onSave, onAbandon, onError, }) {
|
|
212
|
+
const { sendCode, saveCode, abandonCode, codeMessage } = useBuilder();
|
|
213
|
+
const [inputValue, setInputValue] = useState('');
|
|
214
|
+
const [thinkingExpanded, setThinkingExpanded] = useState(false);
|
|
215
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
216
|
+
const messagesRef = useRef(null);
|
|
217
|
+
const { thinking, text, edits, status, error } = codeMessage;
|
|
218
|
+
// Auto-scroll on new content
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
if (messagesRef.current) {
|
|
221
|
+
messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
|
|
222
|
+
}
|
|
223
|
+
}, [thinking, text, edits]);
|
|
224
|
+
// Notify on errors
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (error) {
|
|
227
|
+
onError?.(error);
|
|
228
|
+
}
|
|
229
|
+
}, [error, onError]);
|
|
230
|
+
const handleSubmit = useCallback(async (e) => {
|
|
231
|
+
e.preventDefault();
|
|
232
|
+
const query = inputValue.trim();
|
|
233
|
+
if (!query || status === 'streaming')
|
|
234
|
+
return;
|
|
235
|
+
setInputValue('');
|
|
236
|
+
setThinkingExpanded(false);
|
|
237
|
+
await sendCode(query);
|
|
238
|
+
}, [inputValue, status, sendCode]);
|
|
239
|
+
const handleSave = useCallback(async () => {
|
|
240
|
+
setIsSaving(true);
|
|
241
|
+
try {
|
|
242
|
+
await saveCode();
|
|
243
|
+
onSave?.();
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
onError?.(err.message);
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
setIsSaving(false);
|
|
250
|
+
}
|
|
251
|
+
}, [saveCode, onSave, onError]);
|
|
252
|
+
const handleAbandon = useCallback(async () => {
|
|
253
|
+
setIsSaving(true);
|
|
254
|
+
try {
|
|
255
|
+
await abandonCode();
|
|
256
|
+
onAbandon?.();
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
onError?.(err.message);
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
setIsSaving(false);
|
|
263
|
+
}
|
|
264
|
+
}, [abandonCode, onAbandon, onError]);
|
|
265
|
+
const isStreaming = status === 'streaming';
|
|
266
|
+
const hasContent = thinking || text || edits.length > 0;
|
|
267
|
+
const hasEdits = edits.length > 0;
|
|
268
|
+
return (_jsxs("div", { style: styles.container, className: className, children: [_jsx("style", { children: `
|
|
269
|
+
@keyframes pulse {
|
|
270
|
+
0%, 100% { opacity: 1; }
|
|
271
|
+
50% { opacity: 0.4; }
|
|
272
|
+
}
|
|
273
|
+
` }), _jsxs("div", { ref: messagesRef, style: styles.messagesArea, children: [!hasContent && status === 'idle' && (_jsx("div", { style: styles.emptyState, children: "Send a message to start editing code" })), hasContent && (_jsxs("div", { style: styles.messageBlock, children: [thinking && (_jsxs("div", { style: styles.thinkingSection, children: [_jsxs("div", { style: styles.thinkingHeader, onClick: () => setThinkingExpanded(!thinkingExpanded), children: [_jsx("span", { children: thinkingExpanded ? '▼' : '▶' }), _jsx("span", { children: "Thinking..." }), _jsxs("span", { style: { marginLeft: 'auto', fontSize: '11px' }, children: [thinking.length, " chars"] })] }), thinkingExpanded && (_jsx("div", { style: styles.thinkingContent, children: thinking }))] })), text && _jsx("div", { style: styles.responseText, children: text }), hasEdits && (_jsxs("div", { style: styles.editsSection, children: [_jsxs("div", { style: styles.editsHeader, children: [_jsxs("span", { style: styles.editsTitle, children: [edits.length, " file", edits.length !== 1 ? 's' : '', " changed"] }), status === 'done' && (_jsxs("div", { style: styles.editsActions, children: [_jsx("button", { style: {
|
|
274
|
+
...styles.button,
|
|
275
|
+
...styles.buttonSecondary,
|
|
276
|
+
...(isSaving ? styles.buttonDisabled : {}),
|
|
277
|
+
}, onClick: handleAbandon, disabled: isSaving, children: "Reject" }), _jsx("button", { style: {
|
|
278
|
+
...styles.button,
|
|
279
|
+
...styles.buttonPrimary,
|
|
280
|
+
...(isSaving ? styles.buttonDisabled : {}),
|
|
281
|
+
}, onClick: handleSave, disabled: isSaving, children: "Accept Changes" })] }))] }), edits.map((edit, index) => (_jsx(EditCard, { edit: edit }, `${edit.file_path}-${index}`)))] }))] }))] }), isStreaming && (_jsxs("div", { style: styles.statusBar, children: [_jsx("span", { style: styles.streamingDot }), "Generating response..."] })), status === 'error' && error && (_jsx("div", { style: styles.errorBar, children: error })), _jsx("div", { style: styles.inputArea, children: _jsxs("form", { style: styles.form, onSubmit: handleSubmit, children: [_jsx("input", { type: "text", style: styles.input, placeholder: placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), disabled: isStreaming }), _jsx("button", { type: "submit", style: {
|
|
282
|
+
...styles.button,
|
|
283
|
+
...styles.buttonPrimary,
|
|
284
|
+
...(isStreaming || !inputValue.trim() ? styles.buttonDisabled : {}),
|
|
285
|
+
}, disabled: isStreaming || !inputValue.trim(), children: "Send" })] }) })] }));
|
|
286
|
+
}
|
|
287
|
+
/** Individual edit card component */
|
|
288
|
+
function EditCard({ edit }) {
|
|
289
|
+
const [expanded, setExpanded] = useState(true);
|
|
290
|
+
const typeStyle = {
|
|
291
|
+
...styles.editType,
|
|
292
|
+
...(edit.type === 'create'
|
|
293
|
+
? styles.editTypeCreate
|
|
294
|
+
: edit.type === 'delete'
|
|
295
|
+
? styles.editTypeDelete
|
|
296
|
+
: styles.editTypeReplace),
|
|
297
|
+
};
|
|
298
|
+
return (_jsxs("div", { style: styles.editCard, children: [_jsxs("div", { style: { ...styles.editHeader, cursor: 'pointer' }, onClick: () => setExpanded(!expanded), children: [_jsxs("span", { style: styles.editPath, children: [expanded ? '▼' : '▶', " ", edit.file_path] }), _jsx("span", { style: typeStyle, children: edit.type })] }), expanded && (_jsxs("div", { style: styles.editContent, children: [edit.type === 'delete' && edit.old_content && (_jsx("div", { children: edit.old_content.split('\n').map((line, i) => (_jsxs("div", { style: { ...styles.diffLine, ...styles.diffRemove }, children: ["- ", line] }, i))) })), edit.type === 'create' && edit.new_content && (_jsx("div", { children: edit.new_content.split('\n').map((line, i) => (_jsxs("div", { style: { ...styles.diffLine, ...styles.diffAdd }, children: ["+ ", line] }, i))) })), edit.type === 'replace' && (_jsxs("div", { children: [edit.old_content?.split('\n').map((line, i) => (_jsxs("div", { style: { ...styles.diffLine, ...styles.diffRemove }, children: ["- ", line] }, `old-${i}`))), edit.new_content?.split('\n').map((line, i) => (_jsxs("div", { style: { ...styles.diffLine, ...styles.diffAdd }, children: ["+ ", line] }, `new-${i}`)))] }))] }))] }));
|
|
299
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type ReactNode, type HTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props for zone wrapper components
|
|
4
|
+
*/
|
|
5
|
+
export interface ZoneProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Content to wrap */
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
/** HTML tag to render (default: 'div') */
|
|
9
|
+
as?: keyof JSX.IntrinsicElements;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* LockedZone - Wraps content to prevent builder customization
|
|
13
|
+
*
|
|
14
|
+
* Elements inside a LockedZone cannot be:
|
|
15
|
+
* - Dragged/moved by the builder
|
|
16
|
+
* - Used as drop targets
|
|
17
|
+
*
|
|
18
|
+
* Use this to protect parts of your UI that should not be editable,
|
|
19
|
+
* like navigation, footers, or critical UI elements.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <LockedZone>
|
|
24
|
+
* <Navigation /> {/* Cannot be moved or modified *\/}
|
|
25
|
+
* </LockedZone>
|
|
26
|
+
*
|
|
27
|
+
* <div>
|
|
28
|
+
* {/* This content is editable *\/}
|
|
29
|
+
* </div>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example Nested zones
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <LockedZone>
|
|
35
|
+
* <Sidebar>
|
|
36
|
+
* <Logo /> {/* Locked *\/}
|
|
37
|
+
* <EditableZone>
|
|
38
|
+
* <CustomWidgets /> {/* Editable "island" inside locked zone *\/}
|
|
39
|
+
* </EditableZone>
|
|
40
|
+
* </Sidebar>
|
|
41
|
+
* </LockedZone>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare const LockedZone: import("react").ForwardRefExoticComponent<ZoneProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
45
|
+
/**
|
|
46
|
+
* EditableZone - Explicitly marks content as editable in the builder
|
|
47
|
+
*
|
|
48
|
+
* Use this to:
|
|
49
|
+
* - Create editable "islands" inside a LockedZone
|
|
50
|
+
* - Explicitly mark content as editable (though content is editable by default)
|
|
51
|
+
*
|
|
52
|
+
* @example Create editable area inside locked zone
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <LockedZone>
|
|
55
|
+
* <Header>
|
|
56
|
+
* <Logo /> {/* Locked *\/}
|
|
57
|
+
* <EditableZone>
|
|
58
|
+
* <UserMenu /> {/* Editable despite being inside LockedZone *\/}
|
|
59
|
+
* </EditableZone>
|
|
60
|
+
* </Header>
|
|
61
|
+
* </LockedZone>
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const EditableZone: import("react").ForwardRefExoticComponent<ZoneProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
65
|
+
/**
|
|
66
|
+
* Alias for LockedZone - more explicit naming
|
|
67
|
+
*/
|
|
68
|
+
export declare const NonCustomizable: import("react").ForwardRefExoticComponent<ZoneProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
69
|
+
/**
|
|
70
|
+
* Alias for EditableZone - more explicit naming
|
|
71
|
+
*/
|
|
72
|
+
export declare const Customizable: import("react").ForwardRefExoticComponent<ZoneProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
73
|
+
//# sourceMappingURL=EditableZone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditableZone.d.ts","sourceRoot":"","sources":["../../src/builder/EditableZone.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAc,MAAM,OAAO,CAAC;AAGxE;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,cAAc,CAAC,cAAc,CAAC;IAC/D,sBAAsB;IACtB,QAAQ,EAAE,SAAS,CAAC;IACpB,0CAA0C;IAC1C,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,iBAAiB,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,UAAU,sGAYtB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY,sGAYxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,sGAAa,CAAC;AAE1C;;GAEG;AACH,eAAO,MAAM,YAAY,sGAAe,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { VISIBILITY_ATTR } from 'dynim-core';
|
|
4
|
+
/**
|
|
5
|
+
* LockedZone - Wraps content to prevent builder customization
|
|
6
|
+
*
|
|
7
|
+
* Elements inside a LockedZone cannot be:
|
|
8
|
+
* - Dragged/moved by the builder
|
|
9
|
+
* - Used as drop targets
|
|
10
|
+
*
|
|
11
|
+
* Use this to protect parts of your UI that should not be editable,
|
|
12
|
+
* like navigation, footers, or critical UI elements.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <LockedZone>
|
|
17
|
+
* <Navigation /> {/* Cannot be moved or modified *\/}
|
|
18
|
+
* </LockedZone>
|
|
19
|
+
*
|
|
20
|
+
* <div>
|
|
21
|
+
* {/* This content is editable *\/}
|
|
22
|
+
* </div>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example Nested zones
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <LockedZone>
|
|
28
|
+
* <Sidebar>
|
|
29
|
+
* <Logo /> {/* Locked *\/}
|
|
30
|
+
* <EditableZone>
|
|
31
|
+
* <CustomWidgets /> {/* Editable "island" inside locked zone *\/}
|
|
32
|
+
* </EditableZone>
|
|
33
|
+
* </Sidebar>
|
|
34
|
+
* </LockedZone>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const LockedZone = forwardRef(function LockedZone({ children, as: Component = 'div', ...props }, ref) {
|
|
38
|
+
const zoneProps = {
|
|
39
|
+
...props,
|
|
40
|
+
[VISIBILITY_ATTR]: 'false',
|
|
41
|
+
ref,
|
|
42
|
+
};
|
|
43
|
+
// Use createElement to support dynamic tag
|
|
44
|
+
const Element = Component;
|
|
45
|
+
return _jsx(Element, { ...zoneProps, children: children });
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* EditableZone - Explicitly marks content as editable in the builder
|
|
49
|
+
*
|
|
50
|
+
* Use this to:
|
|
51
|
+
* - Create editable "islands" inside a LockedZone
|
|
52
|
+
* - Explicitly mark content as editable (though content is editable by default)
|
|
53
|
+
*
|
|
54
|
+
* @example Create editable area inside locked zone
|
|
55
|
+
* ```tsx
|
|
56
|
+
* <LockedZone>
|
|
57
|
+
* <Header>
|
|
58
|
+
* <Logo /> {/* Locked *\/}
|
|
59
|
+
* <EditableZone>
|
|
60
|
+
* <UserMenu /> {/* Editable despite being inside LockedZone *\/}
|
|
61
|
+
* </EditableZone>
|
|
62
|
+
* </Header>
|
|
63
|
+
* </LockedZone>
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export const EditableZone = forwardRef(function EditableZone({ children, as: Component = 'div', ...props }, ref) {
|
|
67
|
+
const zoneProps = {
|
|
68
|
+
...props,
|
|
69
|
+
[VISIBILITY_ATTR]: 'true',
|
|
70
|
+
ref,
|
|
71
|
+
};
|
|
72
|
+
// Use createElement to support dynamic tag
|
|
73
|
+
const Element = Component;
|
|
74
|
+
return _jsx(Element, { ...zoneProps, children: children });
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* Alias for LockedZone - more explicit naming
|
|
78
|
+
*/
|
|
79
|
+
export const NonCustomizable = LockedZone;
|
|
80
|
+
/**
|
|
81
|
+
* Alias for EditableZone - more explicit naming
|
|
82
|
+
*/
|
|
83
|
+
export const Customizable = EditableZone;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message list component
|
|
3
|
+
* Renders messages with optional custom renderer
|
|
4
|
+
*/
|
|
5
|
+
import { type ReactNode } from 'react';
|
|
6
|
+
import type { Message } from 'dynim-core';
|
|
7
|
+
export interface MessageListProps {
|
|
8
|
+
className?: string;
|
|
9
|
+
renderMessage?: (message: Message) => ReactNode;
|
|
10
|
+
showAvatars?: boolean;
|
|
11
|
+
showTimestamps?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function MessageList({ className, renderMessage, showAvatars, showTimestamps, }: MessageListProps): JSX.Element;
|
|
14
|
+
//# sourceMappingURL=MessageList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/builder/MessageList.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAc,EACd,aAAa,EACb,WAAkB,EAClB,cAAsB,GACvB,EAAE,gBAAgB,GAAG,GAAG,CAAC,OAAO,CA6ChC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Message list component
|
|
4
|
+
* Renders messages with optional custom renderer
|
|
5
|
+
*/
|
|
6
|
+
import { useRef, useEffect } from 'react';
|
|
7
|
+
import { useChatContext } from './ChatContext';
|
|
8
|
+
export function MessageList({ className = '', renderMessage, showAvatars = true, showTimestamps = false, }) {
|
|
9
|
+
const { messages, isTyping } = useChatContext();
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
// Auto-scroll to bottom on new messages
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (containerRef.current) {
|
|
14
|
+
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
|
15
|
+
}
|
|
16
|
+
}, [messages]);
|
|
17
|
+
// Default message renderer
|
|
18
|
+
const defaultRenderMessage = (message) => (_jsxs("div", { className: `chatbot-message chatbot-message--${message.role}`, children: [showAvatars && (_jsx("div", { className: "chatbot-avatar", children: message.role === 'user' ? '\u{1F464}' : '\u{1F916}' })), _jsxs("div", { className: "chatbot-bubble", children: [_jsx("div", { className: "chatbot-text", children: message.text }), showTimestamps && (_jsx("div", { className: "chatbot-time", children: new Date(message.timestamp).toLocaleTimeString([], {
|
|
19
|
+
hour: '2-digit',
|
|
20
|
+
minute: '2-digit',
|
|
21
|
+
}) }))] })] }, message.id));
|
|
22
|
+
return (_jsxs("div", { ref: containerRef, className: `chatbot-messages ${className}`, children: [messages.map((message) => renderMessage ? renderMessage(message) : defaultRenderMessage(message)), isTyping && (_jsxs("div", { className: "chatbot-typing", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] }))] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder React exports
|
|
3
|
+
*/
|
|
4
|
+
export { BuilderProvider, useBuilder } from './BuilderProvider';
|
|
5
|
+
export type { BuilderConfig, BuilderContextValue, BuilderProviderProps } from './BuilderProvider';
|
|
6
|
+
export { ChatProvider, useChatContext } from './ChatContext';
|
|
7
|
+
export type { ChatProviderProps } from './ChatContext';
|
|
8
|
+
export { MessageList } from './MessageList';
|
|
9
|
+
export type { MessageListProps } from './MessageList';
|
|
10
|
+
export { ChatInput } from './ChatInput';
|
|
11
|
+
export type { ChatInputProps } from './ChatInput';
|
|
12
|
+
export { CodeChatPanel } from './CodeChatPanel';
|
|
13
|
+
export type { CodeChatPanelProps } from './CodeChatPanel';
|
|
14
|
+
export { useChatbot } from './useChatbot';
|
|
15
|
+
export type { UseChatbotConfig, UseChatbotReturn } from './useChatbot';
|
|
16
|
+
export { LockedZone, EditableZone, NonCustomizable, Customizable, } from './EditableZone';
|
|
17
|
+
export type { ZoneProps } from './EditableZone';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGlG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGvE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,eAAe,EACf,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder React exports
|
|
3
|
+
*/
|
|
4
|
+
// Builder provider with capture integration
|
|
5
|
+
export { BuilderProvider, useBuilder } from './BuilderProvider';
|
|
6
|
+
// Chat context and provider
|
|
7
|
+
export { ChatProvider, useChatContext } from './ChatContext';
|
|
8
|
+
// Components
|
|
9
|
+
export { MessageList } from './MessageList';
|
|
10
|
+
export { ChatInput } from './ChatInput';
|
|
11
|
+
export { CodeChatPanel } from './CodeChatPanel';
|
|
12
|
+
// Headless hook for custom UI
|
|
13
|
+
export { useChatbot } from './useChatbot';
|
|
14
|
+
// Zone wrappers for builder customization control
|
|
15
|
+
export { LockedZone, EditableZone, NonCustomizable, Customizable, } from './EditableZone';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless hook for full UI control
|
|
3
|
+
* Use this when you want to build your own UI
|
|
4
|
+
*/
|
|
5
|
+
import type { Message } from 'dynim-core';
|
|
6
|
+
export interface UseChatbotConfig {
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
onMessage?: (data: {
|
|
10
|
+
text?: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
}) => void;
|
|
13
|
+
onError?: (error: Error) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface UseChatbotReturn {
|
|
16
|
+
messages: Message[];
|
|
17
|
+
isLoading: boolean;
|
|
18
|
+
isTyping: boolean;
|
|
19
|
+
error: string | null;
|
|
20
|
+
sendMessage: (text: string) => Promise<void>;
|
|
21
|
+
clearMessages: () => void;
|
|
22
|
+
}
|
|
23
|
+
export declare function useChatbot(config?: UseChatbotConfig): UseChatbotReturn;
|
|
24
|
+
//# sourceMappingURL=useChatbot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChatbot.d.ts","sourceRoot":"","sources":["../../src/builder/useChatbot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,OAAO,EAA4B,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,MAAM,GAAE,gBAAqB,GAAG,gBAAgB,CAyF1E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless hook for full UI control
|
|
3
|
+
* Use this when you want to build your own UI
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
6
|
+
import { createState, createClient } from 'dynim-core';
|
|
7
|
+
export function useChatbot(config = {}) {
|
|
8
|
+
const { apiKey, endpoint, onMessage, onError } = config;
|
|
9
|
+
const [messages, setMessages] = useState([]);
|
|
10
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
11
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const stateRef = useRef(null);
|
|
14
|
+
const clientRef = useRef(null);
|
|
15
|
+
// Initialize on mount
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const state = createState();
|
|
18
|
+
stateRef.current = state;
|
|
19
|
+
const client = createClient({
|
|
20
|
+
apiKey,
|
|
21
|
+
endpoint,
|
|
22
|
+
onMessage: (data) => {
|
|
23
|
+
const currentMessages = state.getState().messages;
|
|
24
|
+
const lastMessage = currentMessages[currentMessages.length - 1];
|
|
25
|
+
if (lastMessage?.role === 'assistant') {
|
|
26
|
+
const newText = lastMessage.text + (data.text || data.content || '');
|
|
27
|
+
state.updateMessage(lastMessage.id, { text: newText });
|
|
28
|
+
}
|
|
29
|
+
onMessage?.(data);
|
|
30
|
+
},
|
|
31
|
+
onError: (err) => {
|
|
32
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
33
|
+
setError(errorMessage);
|
|
34
|
+
if (err instanceof Error) {
|
|
35
|
+
onError?.(err);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
clientRef.current = client;
|
|
40
|
+
// Subscribe to state changes
|
|
41
|
+
const unsubscribe = state.subscribe((newState) => {
|
|
42
|
+
setMessages([...newState.messages]);
|
|
43
|
+
setIsLoading(newState.isLoading);
|
|
44
|
+
setIsTyping(newState.isTyping);
|
|
45
|
+
});
|
|
46
|
+
return () => {
|
|
47
|
+
unsubscribe();
|
|
48
|
+
};
|
|
49
|
+
}, [apiKey, endpoint]);
|
|
50
|
+
// Send message
|
|
51
|
+
const sendMessage = useCallback(async (text) => {
|
|
52
|
+
const state = stateRef.current;
|
|
53
|
+
const client = clientRef.current;
|
|
54
|
+
if (!state || !client)
|
|
55
|
+
return;
|
|
56
|
+
setError(null);
|
|
57
|
+
state.addMessage({ role: 'user', text });
|
|
58
|
+
const botMessage = state.addMessage({ role: 'assistant', text: '' });
|
|
59
|
+
setIsLoading(true);
|
|
60
|
+
setIsTyping(true);
|
|
61
|
+
try {
|
|
62
|
+
await client.send(text);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
state.updateMessage(botMessage.id, { text: 'Sorry, something went wrong.' });
|
|
66
|
+
setError(err.message);
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
setIsLoading(false);
|
|
70
|
+
setIsTyping(false);
|
|
71
|
+
}
|
|
72
|
+
}, []);
|
|
73
|
+
// Clear messages
|
|
74
|
+
const clearMessages = useCallback(() => {
|
|
75
|
+
stateRef.current?.clearMessages();
|
|
76
|
+
}, []);
|
|
77
|
+
return {
|
|
78
|
+
messages,
|
|
79
|
+
isLoading,
|
|
80
|
+
isTyping,
|
|
81
|
+
error,
|
|
82
|
+
sendMessage,
|
|
83
|
+
clearMessages,
|
|
84
|
+
};
|
|
85
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dynim-react - React wrapper for dynim-core
|
|
3
|
+
*
|
|
4
|
+
* This package provides:
|
|
5
|
+
* - Re-exports everything from dynim-core (for convenience)
|
|
6
|
+
* - React components and hooks for the builder
|
|
7
|
+
* - Inference SDK for tenant customization loading
|
|
8
|
+
*/
|
|
9
|
+
export * from 'dynim-core';
|
|
10
|
+
export { BuilderProvider, useBuilder, ChatProvider, useChatContext, MessageList, ChatInput, CodeChatPanel, useChatbot, LockedZone, EditableZone, NonCustomizable, Customizable, } from './builder';
|
|
11
|
+
export type { BuilderConfig, BuilderContextValue, BuilderProviderProps, ChatProviderProps, MessageListProps, ChatInputProps, CodeChatPanelProps, UseChatbotConfig, UseChatbotReturn, ZoneProps, } from './builder';
|
|
12
|
+
export { InferenceProvider, InferenceProviderDefault, createSharedContext, getSharedContext, isSharedContextReady, } from './inference';
|
|
13
|
+
export type { DynimSDKConfig, InferenceProviderProps, InferenceState, DynimGlobal, } from './inference';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|