@melony/react 0.1.12 → 0.1.14
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 +55 -194
- package/dist/index.cjs +539 -275
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -8
- package/dist/index.d.ts +117 -8
- package/dist/index.js +492 -233
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,237 +1,98 @@
|
|
|
1
1
|
# @melony/react
|
|
2
2
|
|
|
3
|
-
React
|
|
3
|
+
React UI + providers/hooks for building chat experiences on top of **Melony’s event stream**, including automatic rendering for **Server‑Driven UI (SDUI)** (`event.ui`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @melony/react
|
|
8
|
+
npm install @melony/react melony react
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Quick
|
|
11
|
+
## Quick start
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
|
-
import
|
|
14
|
+
import React from "react";
|
|
15
|
+
import { MelonyClient, createHttpTransport } from "melony/client";
|
|
16
|
+
import { MelonyClientProvider, Thread } from "@melony/react";
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
const { threads, activeThreadId, messages, isLoading } = useMelony();
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div style={{ display: 'flex', height: '100vh' }}>
|
|
21
|
-
<ThreadSidebar threads={threads} activeThreadId={activeThreadId} />
|
|
22
|
-
<Thread messages={messages} isLoading={isLoading} />
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
18
|
+
const client = new MelonyClient(createHttpTransport("/api/chat"));
|
|
26
19
|
|
|
27
20
|
export default function App() {
|
|
28
21
|
return (
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
</
|
|
22
|
+
<MelonyClientProvider client={client}>
|
|
23
|
+
<Thread />
|
|
24
|
+
</MelonyClientProvider>
|
|
32
25
|
);
|
|
33
26
|
}
|
|
34
27
|
```
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Melony React uses an **event-based architecture** where all actions are dispatched as events:
|
|
39
|
-
|
|
40
|
-
```tsx
|
|
41
|
-
const { dispatchEvent } = useMelony();
|
|
42
|
-
|
|
43
|
-
// Create a thread
|
|
44
|
-
dispatchEvent({ type: 'createThread' });
|
|
45
|
-
|
|
46
|
-
// Switch thread
|
|
47
|
-
dispatchEvent({ type: 'switchThread', data: { threadId: 'abc' } });
|
|
48
|
-
|
|
49
|
-
// Send a message
|
|
50
|
-
dispatchEvent({
|
|
51
|
-
type: 'sendMessage',
|
|
52
|
-
data: {
|
|
53
|
-
role: 'user',
|
|
54
|
-
content: [{ type: 'text', data: { content: 'Hello!' } }]
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Core Components
|
|
60
|
-
|
|
61
|
-
### `MelonyStoreProvider`
|
|
62
|
-
|
|
63
|
-
The main provider that manages threads, messages, and API communication.
|
|
64
|
-
|
|
65
|
-
```tsx
|
|
66
|
-
<MelonyStoreProvider
|
|
67
|
-
api="/api/chat"
|
|
68
|
-
onLoadHistory={async (threadId) => {
|
|
69
|
-
// Load message history when switching threads
|
|
70
|
-
const res = await fetch(`/api/threads/${threadId}/messages`);
|
|
71
|
-
return res.json();
|
|
72
|
-
}}
|
|
73
|
-
onThreadsChange={(threads) => {
|
|
74
|
-
// Persist threads (e.g., to localStorage)
|
|
75
|
-
localStorage.setItem('threads', JSON.stringify(threads));
|
|
76
|
-
}}
|
|
77
|
-
onEvent={(event) => {
|
|
78
|
-
// Handle custom events
|
|
79
|
-
console.log('Event:', event.type);
|
|
80
|
-
}}
|
|
81
|
-
>
|
|
82
|
-
<App />
|
|
83
|
-
</MelonyStoreProvider>
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### `Thread`
|
|
29
|
+
### Send a message from UI
|
|
87
30
|
|
|
88
|
-
|
|
31
|
+
`Thread` already wires this up, but if you want manual control:
|
|
89
32
|
|
|
90
33
|
```tsx
|
|
91
|
-
|
|
92
|
-
messages={messages}
|
|
93
|
-
isLoading={isLoading}
|
|
94
|
-
placeholder="Type a message..."
|
|
95
|
-
components={{
|
|
96
|
-
// Custom components for Server-Driven UI
|
|
97
|
-
'weather-card': WeatherCard,
|
|
98
|
-
}}
|
|
99
|
-
/>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### `ThreadSidebar`
|
|
103
|
-
|
|
104
|
-
Sidebar showing list of conversation threads.
|
|
34
|
+
import { useMelony } from "@melony/react";
|
|
105
35
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
36
|
+
function Controls() {
|
|
37
|
+
const { sendEvent, isLoading } = useMelony();
|
|
38
|
+
return (
|
|
39
|
+
<button
|
|
40
|
+
disabled={isLoading}
|
|
41
|
+
onClick={() =>
|
|
42
|
+
sendEvent({ type: "text", role: "user", data: { content: "Hello!" } })
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
Send
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
112
49
|
```
|
|
113
50
|
|
|
114
|
-
##
|
|
115
|
-
|
|
116
|
-
### `useMelony()`
|
|
117
|
-
|
|
118
|
-
Main hook to access store state and dispatch events.
|
|
119
|
-
|
|
120
|
-
```tsx
|
|
121
|
-
const {
|
|
122
|
-
// State
|
|
123
|
-
threads, // Thread[] - all threads
|
|
124
|
-
activeThreadId, // string | null
|
|
125
|
-
activeThread, // Thread | undefined
|
|
126
|
-
messages, // ChatMessage[] - messages in active thread
|
|
127
|
-
isLoading, // boolean
|
|
128
|
-
error, // Error | null
|
|
129
|
-
|
|
130
|
-
// Methods
|
|
131
|
-
dispatchEvent, // (event: Event) => void
|
|
132
|
-
getThread, // (id: string) => Thread | undefined
|
|
133
|
-
getThreadMessages, // (id: string) => ChatMessage[]
|
|
134
|
-
} = useMelony();
|
|
135
|
-
```
|
|
51
|
+
## Components
|
|
136
52
|
|
|
137
|
-
|
|
53
|
+
- **`Thread`**: a full chat thread (composer + message list + streaming).
|
|
54
|
+
- **`ChatSidebar`**: a sidebar-style chat UI container.
|
|
55
|
+
- **`ChatFull` / `ChatPopup`**: ready-to-embed layouts (see exports).
|
|
56
|
+
- **`UIRenderer`**: renders SDUI `UINode` trees from `melony`.
|
|
138
57
|
|
|
139
|
-
|
|
58
|
+
## Providers & hooks
|
|
140
59
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
console.log('Message sent!');
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
```
|
|
60
|
+
- **`MelonyClientProvider`** + **`useMelony()`**
|
|
61
|
+
- wraps a `MelonyClient` from `melony/client`
|
|
62
|
+
- exposes `events`, `messages`, `isLoading`, `error`, and `sendEvent()`
|
|
148
63
|
|
|
149
|
-
|
|
64
|
+
- **`ThreadProvider`** + **`useThreads()`** (optional)
|
|
65
|
+
- adds “thread list / active thread” state
|
|
66
|
+
- you bring a `ThreadService` (load threads, load events, delete thread, etc.)
|
|
150
67
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
| `createThread` | `{ initialMessage?, title? }` | Create a new thread |
|
|
154
|
-
| `switchThread` | `{ threadId }` | Switch to a thread |
|
|
155
|
-
| `deleteThread` | `{ threadId }` | Delete a thread |
|
|
156
|
-
| `updateThreadTitle` | `{ threadId, title }` | Update thread title |
|
|
157
|
-
| `sendMessage` | `{ role, content, threadId? }` | Send a message |
|
|
158
|
-
| `clearThread` | `{ threadId? }` | Clear thread messages |
|
|
68
|
+
- **`AuthProvider`** + **`useAuth()`** (optional)
|
|
69
|
+
- plug in an `AuthService` (login/logout/me/token) for authenticated apps
|
|
159
70
|
|
|
160
|
-
## UI
|
|
71
|
+
## SDUI (Server‑Driven UI)
|
|
161
72
|
|
|
162
|
-
|
|
163
|
-
Content: `Text`, `Heading`, `Image`, `Icon`, `Badge`, `Chart`
|
|
164
|
-
Forms: `Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `RadioGroup`, `Form`, `Label`
|
|
165
|
-
Containers: `Card`
|
|
73
|
+
If the backend yields events with `event.ui`, Melony React renders them automatically inside assistant messages.
|
|
166
74
|
|
|
167
|
-
|
|
75
|
+
Backend example:
|
|
168
76
|
|
|
169
|
-
|
|
77
|
+
```ts
|
|
78
|
+
import { ui } from "melony";
|
|
170
79
|
|
|
171
|
-
```tsx
|
|
172
|
-
// Server sends:
|
|
173
80
|
yield {
|
|
174
|
-
type:
|
|
175
|
-
ui: {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
{ type: 'text', props: { value: '72°F' } }
|
|
180
|
-
]
|
|
181
|
-
}
|
|
81
|
+
type: "ui",
|
|
82
|
+
ui: ui.card({
|
|
83
|
+
title: "Weather",
|
|
84
|
+
children: [ui.text("72°F and sunny")],
|
|
85
|
+
}),
|
|
182
86
|
};
|
|
183
|
-
|
|
184
|
-
// Renderer automatically displays the Card with Text
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Theming
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
<MelonyStoreProvider
|
|
191
|
-
api="/api/chat"
|
|
192
|
-
theme={{
|
|
193
|
-
colors: {
|
|
194
|
-
primary: '#6366f1',
|
|
195
|
-
background: '#ffffff',
|
|
196
|
-
},
|
|
197
|
-
radius: {
|
|
198
|
-
md: '8px',
|
|
199
|
-
},
|
|
200
|
-
}}
|
|
201
|
-
>
|
|
202
|
-
<App />
|
|
203
|
-
</MelonyStoreProvider>
|
|
204
87
|
```
|
|
205
88
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
If you're using the deprecated hooks (`useMelonyRuntime`, `useMelonyThread`, `useMelonyThreads`):
|
|
209
|
-
|
|
210
|
-
```tsx
|
|
211
|
-
// Before:
|
|
212
|
-
const { messages, sendMessage } = useMelonyThread({ api: '/api/chat' });
|
|
213
|
-
const { threads, createThread } = useMelonyThreads({ threads, activeThreadId });
|
|
214
|
-
|
|
215
|
-
// After:
|
|
216
|
-
// 1. Wrap with MelonyStoreProvider
|
|
217
|
-
// 2. Use useMelony()
|
|
218
|
-
const { messages, threads, dispatchEvent } = useMelony();
|
|
219
|
-
|
|
220
|
-
// Send message via event
|
|
221
|
-
dispatchEvent({
|
|
222
|
-
type: 'sendMessage',
|
|
223
|
-
data: { role: 'user', content: [...] }
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Create thread via event
|
|
227
|
-
dispatchEvent({ type: 'createThread' });
|
|
228
|
-
```
|
|
89
|
+
Frontend: no extra work — it shows up in the message stream.
|
|
229
90
|
|
|
230
91
|
## Development
|
|
231
92
|
|
|
232
93
|
```bash
|
|
233
|
-
pnpm build
|
|
234
|
-
pnpm dev
|
|
235
|
-
pnpm typecheck
|
|
236
|
-
pnpm clean
|
|
94
|
+
pnpm build
|
|
95
|
+
pnpm dev
|
|
96
|
+
pnpm typecheck
|
|
97
|
+
pnpm clean
|
|
237
98
|
```
|