@fluxy-chat/create-fluxy-chat 0.3.0 → 0.5.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/CHANGELOG.md +16 -0
- package/dist/index.js +105 -29
- package/package.json +4 -3
- package/readme.md +22 -16
- package/templates/full/.env.example +10 -0
- package/templates/full/README.md +66 -0
- package/templates/full/index.html +12 -0
- package/templates/full/package.json +29 -0
- package/templates/full/scripts/fluxy-dev.mjs +169 -0
- package/templates/full/scripts/fluxy-doctor.mjs +122 -0
- package/templates/full/scripts/fluxy-setup.mjs +323 -0
- package/templates/full/src/App.tsx +223 -0
- package/templates/full/src/index.css +242 -0
- package/templates/full/src/main.tsx +10 -0
- package/templates/full/src/vite-env.d.ts +16 -0
- package/templates/full/tsconfig.json +21 -0
- package/templates/full/vite.config.ts +7 -0
- package/templates/hr-feedback/.env.example +9 -0
- package/templates/hr-feedback/README.md +45 -0
- package/templates/hr-feedback/package.json +19 -0
- package/templates/hr-feedback/src/feedback.ts +71 -0
- package/templates/hr-feedback/src/index.ts +22 -0
- package/templates/hr-feedback/tsconfig.json +18 -0
- package/templates/hr-feedback/wrangler.toml +7 -0
- package/templates/minimal/README.md +1 -1
- package/templates/react/.env.example +4 -3
- package/templates/react/README.md +17 -7
- package/templates/react/src/App.tsx +116 -23
- package/templates/react/src/index.css +10 -0
- package/templates/react/src/vite-env.d.ts +3 -2
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
color: #0f172a;
|
|
5
|
+
background: #fdfbf9;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
* {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
body {
|
|
13
|
+
margin: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.shell {
|
|
17
|
+
max-width: 880px;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
padding: 1.5rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.hero {
|
|
23
|
+
margin-bottom: 1rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.hero h1 {
|
|
27
|
+
font-size: 1.35rem;
|
|
28
|
+
margin: 0 0 0.35rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.hero p {
|
|
32
|
+
margin: 0;
|
|
33
|
+
font-size: 0.875rem;
|
|
34
|
+
color: #64748b;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.hero a {
|
|
38
|
+
color: #c2410c;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.layout {
|
|
42
|
+
display: grid;
|
|
43
|
+
gap: 1rem;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (min-width: 900px) {
|
|
47
|
+
.layout {
|
|
48
|
+
grid-template-columns: 1fr 240px;
|
|
49
|
+
align-items: start;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.chat-panel {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
min-height: 520px;
|
|
57
|
+
border: 1px solid #e7e5e4;
|
|
58
|
+
border-radius: 16px;
|
|
59
|
+
background: #fff;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
box-shadow: 0 8px 30px rgb(15 23 42 / 6%);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.chat-header {
|
|
65
|
+
display: flex;
|
|
66
|
+
justify-content: space-between;
|
|
67
|
+
align-items: center;
|
|
68
|
+
padding: 0.75rem 1rem;
|
|
69
|
+
border-bottom: 1px solid #e7e5e4;
|
|
70
|
+
background: #0f172a;
|
|
71
|
+
color: #fff;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.status {
|
|
75
|
+
font-size: 0.75rem;
|
|
76
|
+
opacity: 0.85;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.messages {
|
|
80
|
+
flex: 1;
|
|
81
|
+
overflow-y: auto;
|
|
82
|
+
list-style: none;
|
|
83
|
+
margin: 0;
|
|
84
|
+
padding: 1rem;
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
gap: 0.5rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.message {
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-direction: column;
|
|
93
|
+
gap: 0.15rem;
|
|
94
|
+
padding: 0.6rem 0.85rem;
|
|
95
|
+
border-radius: 10px;
|
|
96
|
+
background: #f8fafc;
|
|
97
|
+
max-width: 85%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.message.self {
|
|
101
|
+
align-self: flex-end;
|
|
102
|
+
background: #fff7ed;
|
|
103
|
+
border: 1px solid #fed7aa;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.message.agent {
|
|
107
|
+
background: #f1f5f9;
|
|
108
|
+
border: 1px solid #e2e8f0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.author {
|
|
112
|
+
font-size: 0.7rem;
|
|
113
|
+
color: #64748b;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.composer {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
gap: 0.5rem;
|
|
121
|
+
padding: 0.75rem;
|
|
122
|
+
border-top: 1px solid #e7e5e4;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.composer-row {
|
|
126
|
+
display: flex;
|
|
127
|
+
gap: 0.5rem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.composer input {
|
|
131
|
+
flex: 1;
|
|
132
|
+
border: 1px solid #cbd5e1;
|
|
133
|
+
border-radius: 10px;
|
|
134
|
+
padding: 0.55rem 0.75rem;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.composer button {
|
|
138
|
+
border: none;
|
|
139
|
+
border-radius: 10px;
|
|
140
|
+
background: #c2410c;
|
|
141
|
+
color: #fff;
|
|
142
|
+
padding: 0.55rem 1rem;
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
font-weight: 600;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.composer button.secondary {
|
|
148
|
+
background: #0f172a;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.composer button:disabled {
|
|
152
|
+
opacity: 0.55;
|
|
153
|
+
cursor: not-allowed;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.hint {
|
|
157
|
+
font-size: 0.75rem;
|
|
158
|
+
color: #64748b;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.tools {
|
|
162
|
+
margin: 0 1rem 1rem;
|
|
163
|
+
padding: 0.75rem;
|
|
164
|
+
border-radius: 10px;
|
|
165
|
+
border: 1px solid #e2e8f0;
|
|
166
|
+
background: #f8fafc;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.tools h3 {
|
|
170
|
+
margin: 0 0 0.5rem;
|
|
171
|
+
font-size: 0.7rem;
|
|
172
|
+
letter-spacing: 0.06em;
|
|
173
|
+
text-transform: uppercase;
|
|
174
|
+
color: #64748b;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.tools ul {
|
|
178
|
+
margin: 0;
|
|
179
|
+
padding-left: 1rem;
|
|
180
|
+
font-size: 0.8rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.side {
|
|
184
|
+
display: flex;
|
|
185
|
+
flex-direction: column;
|
|
186
|
+
gap: 0.75rem;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.card {
|
|
190
|
+
border: 1px solid #e7e5e4;
|
|
191
|
+
border-radius: 12px;
|
|
192
|
+
padding: 0.85rem 1rem;
|
|
193
|
+
background: #fff;
|
|
194
|
+
font-size: 0.8rem;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.card h2 {
|
|
198
|
+
margin: 0 0 0.5rem;
|
|
199
|
+
font-size: 0.75rem;
|
|
200
|
+
text-transform: uppercase;
|
|
201
|
+
letter-spacing: 0.05em;
|
|
202
|
+
color: #64748b;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.card dl {
|
|
206
|
+
margin: 0;
|
|
207
|
+
display: grid;
|
|
208
|
+
gap: 0.35rem;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.card dt {
|
|
212
|
+
color: #94a3b8;
|
|
213
|
+
font-size: 0.7rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.card dd {
|
|
217
|
+
margin: 0;
|
|
218
|
+
word-break: break-all;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.card a {
|
|
222
|
+
color: #c2410c;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.error-box {
|
|
226
|
+
border: 1px solid #fecaca;
|
|
227
|
+
background: #fef2f2;
|
|
228
|
+
color: #991b1b;
|
|
229
|
+
border-radius: 12px;
|
|
230
|
+
padding: 1rem;
|
|
231
|
+
font-size: 0.875rem;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.error-box code {
|
|
235
|
+
font-size: 0.8rem;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.typing {
|
|
239
|
+
padding: 0 1rem 0.5rem;
|
|
240
|
+
font-size: 0.75rem;
|
|
241
|
+
color: #64748b;
|
|
242
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
interface ImportMetaEnv {
|
|
4
|
+
readonly VITE_FLUXYCHAT_WORKER_URL: string;
|
|
5
|
+
readonly VITE_FLUXYCHAT_MEMBER_JWT: string;
|
|
6
|
+
readonly VITE_FLUXYCHAT_ROOM_ID: string;
|
|
7
|
+
readonly VITE_FLUXYCHAT_AGENT_ID: string;
|
|
8
|
+
readonly VITE_FLUXYCHAT_AGENT_HANDLE: string;
|
|
9
|
+
readonly VITE_FLUXYCHAT_PROJECT_ID: string;
|
|
10
|
+
readonly VITE_FLUXYCHAT_CONSOLE_URL: string;
|
|
11
|
+
readonly VITE_FLUXYCHAT_USER_ID: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ImportMeta {
|
|
15
|
+
readonly env: ImportMetaEnv;
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedSideEffectImports": true
|
|
19
|
+
},
|
|
20
|
+
"include": ["src"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# FluxyChat worker URL + service API key (server-side only)
|
|
2
|
+
FLUXY_BASE_URL=https://your-fluxychat-worker.example.com
|
|
3
|
+
FLUXY_API_KEY=your-api-key-here
|
|
4
|
+
|
|
5
|
+
# Optional dedicated room for aggregated summaries
|
|
6
|
+
HR_FEEDBACK_ROOM_ID=hr-anonymous-feedback
|
|
7
|
+
|
|
8
|
+
# Optional webhook for Path B HR escalation (de-identified payload only)
|
|
9
|
+
HR_ESCALATION_WEBHOOK_URL=
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# HR Anonymous Feedback Starter
|
|
2
|
+
|
|
3
|
+
Production starter for anonymous employee feedback with sensitive classification and privacy-safe audit.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Path A** — routine feedback → aggregated anonymous summary room
|
|
8
|
+
- **Path B** — sensitive categories → HR escalation hook (category + confidence only, no identity)
|
|
9
|
+
- Reuses FluxyChat `approvalChain` + room timeline audit when wired to agent tools
|
|
10
|
+
- Classification runs on the worker via `POST /anonymous-feedback`
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cp .env.example .dev.vars
|
|
16
|
+
# fill FLUXY_BASE_URL + FLUXY_API_KEY
|
|
17
|
+
npm install
|
|
18
|
+
npm run dev
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Submit feedback:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
curl -X POST http://localhost:8787/feedback \
|
|
25
|
+
-H "Content-Type: application/json" \
|
|
26
|
+
-d '{"content":"My manager makes hostile comments in meetings"}'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Endpoints
|
|
30
|
+
|
|
31
|
+
| Route | Description |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `GET /` | Health check |
|
|
34
|
+
| `POST /feedback` | Anonymous submission (no user id stored) |
|
|
35
|
+
|
|
36
|
+
## Privacy
|
|
37
|
+
|
|
38
|
+
- Raw message content is **not** persisted by this template
|
|
39
|
+
- Worker audit stores **category + timestamp + path** only
|
|
40
|
+
- Configure `HR_ESCALATION_WEBHOOK_URL` for your HRIS / ticketing integration
|
|
41
|
+
|
|
42
|
+
## Learn more
|
|
43
|
+
|
|
44
|
+
- [FluxyChat docs](https://github.com/AlessandroFare/fluxychat)
|
|
45
|
+
- Worker route: `POST /anonymous-feedback` (JWT) for in-app widgets
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hr-feedback-bot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "wrangler dev",
|
|
8
|
+
"deploy": "wrangler deploy",
|
|
9
|
+
"type-check": "tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@fluxy-chat/sdk": "latest"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@cloudflare/workers-types": "^4.0.0",
|
|
16
|
+
"typescript": "^5.6.0",
|
|
17
|
+
"wrangler": "^3.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
interface FeedbackEnv {
|
|
2
|
+
FLUXY_BASE_URL: string;
|
|
3
|
+
FLUXY_API_KEY: string;
|
|
4
|
+
HR_FEEDBACK_ROOM_ID?: string;
|
|
5
|
+
HR_ESCALATION_WEBHOOK_URL?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface FeedbackBody {
|
|
9
|
+
content?: string;
|
|
10
|
+
roomId?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface FeedbackResult {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
path?: string;
|
|
16
|
+
category?: string;
|
|
17
|
+
confidence?: number;
|
|
18
|
+
message?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Submit anonymous feedback through the FluxyChat worker classifier.
|
|
24
|
+
* Content is classified server-side; only metadata is returned.
|
|
25
|
+
*/
|
|
26
|
+
export async function submitHrFeedback(
|
|
27
|
+
env: FeedbackEnv,
|
|
28
|
+
body: FeedbackBody,
|
|
29
|
+
): Promise<FeedbackResult> {
|
|
30
|
+
const content = String(body.content ?? "").trim();
|
|
31
|
+
if (!content) return { ok: false, error: "content required" };
|
|
32
|
+
if (content.length > 8000) return { ok: false, error: "content_too_long" };
|
|
33
|
+
|
|
34
|
+
const baseUrl = env.FLUXY_BASE_URL.replace(/\/$/, "");
|
|
35
|
+
const res = await fetch(`${baseUrl}/anonymous-feedback`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
Authorization: `Bearer ${env.FLUXY_API_KEY}`,
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify({
|
|
42
|
+
content,
|
|
43
|
+
roomId: body.roomId ?? env.HR_FEEDBACK_ROOM_ID ?? null,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const data = (await res.json().catch(() => ({}))) as FeedbackResult;
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
return { ok: false, error: data.error ?? `worker_${res.status}` };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (data.path === "hr_escalation" && env.HR_ESCALATION_WEBHOOK_URL) {
|
|
53
|
+
await fetch(env.HR_ESCALATION_WEBHOOK_URL, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
category: data.category,
|
|
58
|
+
confidence: data.confidence,
|
|
59
|
+
at: new Date().toISOString(),
|
|
60
|
+
}),
|
|
61
|
+
}).catch(() => undefined);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
path: data.path,
|
|
67
|
+
category: data.category,
|
|
68
|
+
confidence: data.confidence,
|
|
69
|
+
message: data.message,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { submitHrFeedback } from "./feedback.js";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
async fetch(request: Request, env: Record<string, string>): Promise<Response> {
|
|
5
|
+
const url = new URL(request.url);
|
|
6
|
+
|
|
7
|
+
if (url.pathname === "/" && request.method === "GET") {
|
|
8
|
+
return new Response("HR anonymous feedback bot is running.", { status: 200 });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (url.pathname === "/feedback" && request.method === "POST") {
|
|
12
|
+
const body = (await request.json().catch(() => ({}))) as {
|
|
13
|
+
content?: string;
|
|
14
|
+
roomId?: string;
|
|
15
|
+
};
|
|
16
|
+
const result = await submitHrFeedback(env, body);
|
|
17
|
+
return Response.json(result, { status: result.ok ? 200 : 400 });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return new Response("Not Found", { status: 404 });
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"types": ["@cloudflare/workers-types"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"allowSyntheticDefaultImports": true,
|
|
14
|
+
"noEmit": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# FluxyChat worker URL (hosted or self-hosted)
|
|
2
2
|
VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
|
|
3
3
|
|
|
4
|
-
#
|
|
4
|
+
# Option A — member JWT (~2 min via onboarding)
|
|
5
5
|
VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
|
|
6
|
-
|
|
7
|
-
# Demo room — create in console or onboarding
|
|
8
6
|
VITE_FLUXYCHAT_ROOM_ID=demo
|
|
7
|
+
|
|
8
|
+
# Option B — public guest room (~60s, no signup)
|
|
9
|
+
# VITE_FLUXYCHAT_PUBLIC_ROOM_ID=your-public-room-id
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
# FluxyChat React starter
|
|
2
2
|
|
|
3
|
-
Minimal Vite + React app with `useChat
|
|
3
|
+
Minimal Vite + React app with `useChat`. First message in about 60 seconds via the public guest room.
|
|
4
4
|
|
|
5
|
-
## Quick start
|
|
5
|
+
## Quick start (guest, fastest)
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
cp .env.example .env
|
|
9
|
-
#
|
|
9
|
+
# Set VITE_FLUXYCHAT_WORKER_URL + VITE_FLUXYCHAT_PUBLIC_ROOM_ID (public room from console)
|
|
10
10
|
npm install
|
|
11
11
|
npm run dev
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Open http://localhost:5173
|
|
14
|
+
Open http://localhost:5173. A guest JWT is minted automatically via `joinPublicRoomAsGuest`.
|
|
15
|
+
|
|
16
|
+
## Quick start (member JWT)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cp .env.example .env
|
|
20
|
+
# Set VITE_FLUXYCHAT_WORKER_URL + VITE_FLUXYCHAT_MEMBER_JWT + VITE_FLUXYCHAT_ROOM_ID
|
|
21
|
+
npm install
|
|
22
|
+
npm run dev
|
|
23
|
+
```
|
|
15
24
|
|
|
16
25
|
## Get credentials
|
|
17
26
|
|
|
18
|
-
1. **
|
|
19
|
-
2. **
|
|
27
|
+
1. **Guest:** create a **public** room in console, copy room ID, set `VITE_FLUXYCHAT_PUBLIC_ROOM_ID`
|
|
28
|
+
2. **Member:** [fluxychat.com/onboarding](https://fluxychat.com/onboarding) for Worker URL and JWT
|
|
29
|
+
3. **Local monorepo:** `pnpm run first-message` from the FluxyChat repo
|
|
20
30
|
|
|
21
31
|
## Scripts
|
|
22
32
|
|
|
@@ -28,6 +38,6 @@ Open http://localhost:5173 and send a message.
|
|
|
28
38
|
|
|
29
39
|
## Next steps
|
|
30
40
|
|
|
31
|
-
- Add `@fluxy-chat/ui`
|
|
41
|
+
- Add `@fluxy-chat/ui` themes (`default`, `dark`, `minimal`, `brand`)
|
|
32
42
|
- Use `useInbox` for unified feed
|
|
33
43
|
- See [chat-only quickstart](https://docs.fluxychat.com/docs/getting-started/chat-only-quickstart)
|