@fluxy-chat/create-fluxy-chat 0.1.0 → 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fluxychat 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/dist/index.js CHANGED
@@ -30,7 +30,7 @@ function isPackageManager(value) {
30
30
  return PACKAGE_MANAGERS.has(value);
31
31
  }
32
32
  function isAdapterType(value) {
33
- return ["basic", "slack", "telegram", "discord", "web"].includes(value);
33
+ return ["basic", "slack", "telegram", "discord", "web", "react"].includes(value);
34
34
  }
35
35
  function detectPackageManagerFromLockfiles(cwd) {
36
36
  if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
@@ -79,6 +79,25 @@ function writeJson(projectDir, filePath, value) {
79
79
  writeFile(projectDir, filePath, `${JSON.stringify(value, null, 2)}
80
80
  `);
81
81
  }
82
+ function copyDir(source, destination) {
83
+ fs.mkdirSync(destination, { recursive: true });
84
+ for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
85
+ const srcPath = path.join(source, entry.name);
86
+ const destPath = path.join(destination, entry.name);
87
+ if (entry.isDirectory()) {
88
+ copyDir(srcPath, destPath);
89
+ } else {
90
+ fs.copyFileSync(srcPath, destPath);
91
+ }
92
+ }
93
+ }
94
+ function templatesDir() {
95
+ return path.resolve(
96
+ path.dirname(new URL(import.meta.url).pathname.replace(/^\//, "")),
97
+ "..",
98
+ "templates"
99
+ );
100
+ }
82
101
 
83
102
  // src/prompts.ts
84
103
  var DEFAULT_PROJECT_NAME = "my-fluxy-bot";
@@ -101,15 +120,18 @@ async function runPrompts(inputs) {
101
120
  if (nameError) {
102
121
  throw new Error(nameError);
103
122
  }
123
+ const minimal = inputs.minimal ?? false;
104
124
  let adapter = inputs.adapter;
105
- if (!adapter) {
125
+ if (!minimal && !adapter) {
106
126
  if (inputs.yes) {
107
- adapter = "basic";
127
+ adapter = "react";
108
128
  } else {
109
129
  const result = await select({
110
130
  message: "Select an adapter:",
111
131
  options: [
112
- { label: "Basic (Cloudflare Workers)", value: "basic" },
132
+ { label: "Minimal chat widget (ui-kit, recommended)", value: "minimal" },
133
+ { label: "React chat app (Vite + useChat)", value: "react" },
134
+ { label: "Basic (Cloudflare Workers bot)", value: "basic" },
113
135
  { label: "Slack", value: "slack" },
114
136
  { label: "Telegram", value: "telegram" },
115
137
  { label: "Discord", value: "discord" },
@@ -117,6 +139,9 @@ async function runPrompts(inputs) {
117
139
  ]
118
140
  });
119
141
  if (isCancel(result)) return null;
142
+ if (result === "minimal") {
143
+ return runPrompts({ ...inputs, minimal: true, adapter: "react" });
144
+ }
120
145
  adapter = result;
121
146
  }
122
147
  }
@@ -167,11 +192,12 @@ async function runPrompts(inputs) {
167
192
  if (isCancel(shouldInitGit)) return null;
168
193
  return {
169
194
  name,
170
- adapter,
195
+ adapter: adapter ?? "react",
171
196
  packageManager,
172
197
  language,
173
198
  shouldInstall,
174
- shouldInitGit
199
+ shouldInitGit,
200
+ minimal: minimal || inputs.minimal === true
175
201
  };
176
202
  }
177
203
 
@@ -775,6 +801,7 @@ var execAsync = promisify(exec);
775
801
  function parseArgs(argv) {
776
802
  const args = {
777
803
  yes: false,
804
+ minimal: false,
778
805
  skipInstall: false,
779
806
  noGit: false,
780
807
  help: false
@@ -786,16 +813,28 @@ function parseArgs(argv) {
786
813
  args.help = true;
787
814
  } else if (arg === "-y" || arg === "--yes") {
788
815
  args.yes = true;
816
+ } else if (arg === "--minimal") {
817
+ args.minimal = true;
789
818
  } else if (arg === "--skip-install") {
790
819
  args.skipInstall = true;
791
820
  } else if (arg === "--no-git") {
792
821
  args.noGit = true;
822
+ } else if (arg === "--template" || arg === "-t") {
823
+ const value = argv[++i];
824
+ if (value && isAdapterType(value)) {
825
+ args.adapter = value;
826
+ } else if (value === "react") {
827
+ args.adapter = "react";
828
+ } else {
829
+ console.error(`Invalid template: ${value}. Choose: react, basic, slack, telegram, discord, web`);
830
+ process.exit(1);
831
+ }
793
832
  } else if (arg === "--adapter" || arg === "-a") {
794
833
  const value = argv[++i];
795
834
  if (value && isAdapterType(value)) {
796
835
  args.adapter = value;
797
836
  } else {
798
- console.error(`Invalid adapter: ${value}. Choose: basic, slack, telegram, discord, web`);
837
+ console.error(`Invalid adapter: ${value}. Choose: react, basic, slack, telegram, discord, web`);
799
838
  process.exit(1);
800
839
  }
801
840
  } else if (arg === "--pm" || arg === "--package-manager") {
@@ -833,16 +872,20 @@ ${pc.bold("Usage:")}
833
872
  npx create-fluxy-chat [project-name] [options]
834
873
 
835
874
  ${pc.bold("Options:")}
836
- -a, --adapter <type> Adapter: basic, slack, telegram, discord, web
875
+ -a, --adapter <type> Adapter: react, basic, slack, telegram, discord, web
876
+ -t, --template <type> Alias for --adapter (e.g. react)
837
877
  --pm <manager> Package manager: npm, pnpm, yarn
838
878
  -l, --language <lang> Language: typescript (default) or javascript
839
879
  -y, --yes Skip prompts and accept defaults
880
+ --minimal Chat-only widget (ui-kit) \u2014 no platform modules
840
881
  --skip-install Skip dependency installation
841
882
  --no-git Skip git repository initialization
842
883
  -h, --help Show this help
843
884
 
844
885
  ${pc.bold("Examples:")}
845
- ${pc.cyan("npx create-fluxy-chat my-bot")}
886
+ ${pc.cyan("npx create-fluxy-chat my-chat --minimal")}
887
+ ${pc.cyan("npx create-fluxy-chat my-chat --template react")}
888
+ ${pc.cyan("npx create-fluxy-chat my-bot --adapter basic")}
846
889
  ${pc.cyan("npx create-fluxy-chat my-bot --adapter slack")}
847
890
  ${pc.cyan("npx create-fluxy-chat my-bot --adapter telegram --pm pnpm")}
848
891
  ${pc.cyan("npx create-fluxy-chat my-bot -y --adapter discord")}
@@ -860,6 +903,7 @@ async function main() {
860
903
  packageManager: args.pm,
861
904
  language: args.language,
862
905
  yes: args.yes,
906
+ minimal: args.minimal,
863
907
  shouldInstall: args.skipInstall ? false : void 0,
864
908
  shouldInitGit: args.noGit ? false : void 0
865
909
  });
@@ -882,19 +926,37 @@ async function main() {
882
926
  s.start("Creating project files");
883
927
  try {
884
928
  fs2.mkdirSync(projectDir, { recursive: true });
885
- writeJson(projectDir, "package.json", generatePackageJson(config));
886
- if (config.language === "typescript") {
887
- writeJson(projectDir, "tsconfig.json", generateTsConfig());
929
+ if (config.minimal) {
930
+ const templateRoot = path2.join(templatesDir(), "minimal");
931
+ copyDir(templateRoot, projectDir);
932
+ const pkgPath = path2.join(projectDir, "package.json");
933
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
934
+ pkg.name = config.name;
935
+ writeJson(projectDir, "package.json", pkg);
936
+ s.stop("Minimal chat widget created.");
937
+ } else if (config.adapter === "react") {
938
+ const templateRoot = path2.join(templatesDir(), "react");
939
+ copyDir(templateRoot, projectDir);
940
+ const pkgPath = path2.join(projectDir, "package.json");
941
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
942
+ pkg.name = config.name;
943
+ writeJson(projectDir, "package.json", pkg);
944
+ s.stop("React chat app created.");
945
+ } else {
946
+ writeJson(projectDir, "package.json", generatePackageJson(config));
947
+ if (config.language === "typescript") {
948
+ writeJson(projectDir, "tsconfig.json", generateTsConfig());
949
+ }
950
+ writeFile(projectDir, "wrangler.toml", generateWranglerToml(config));
951
+ writeFile(projectDir, ".dev.vars", generateDevVars());
952
+ writeFile(projectDir, ".env.example", generateEnvExample(config));
953
+ writeFile(projectDir, ".gitignore", generateGitignore());
954
+ const ext = config.language === "typescript" ? "ts" : "js";
955
+ writeFile(projectDir, `src/index.${ext}`, generateWorkerIndex(config));
956
+ writeFile(projectDir, `src/bot.${ext}`, generateBotHandler(config));
957
+ writeFile(projectDir, "README.md", generateReadme(config));
958
+ s.stop("Project files created.");
888
959
  }
889
- writeFile(projectDir, "wrangler.toml", generateWranglerToml(config));
890
- writeFile(projectDir, ".dev.vars", generateDevVars());
891
- writeFile(projectDir, ".env.example", generateEnvExample(config));
892
- writeFile(projectDir, ".gitignore", generateGitignore());
893
- const ext = config.language === "typescript" ? "ts" : "js";
894
- writeFile(projectDir, `src/index.${ext}`, generateWorkerIndex(config));
895
- writeFile(projectDir, `src/bot.${ext}`, generateBotHandler(config));
896
- writeFile(projectDir, "README.md", generateReadme(config));
897
- s.stop("Project files created.");
898
960
  } catch (error) {
899
961
  s.stop("Failed to create project files.");
900
962
  throw error;
@@ -928,7 +990,11 @@ async function main() {
928
990
  }
929
991
  }
930
992
  note(
931
- [
993
+ config.adapter === "react" ? [
994
+ `cd ${config.name}`,
995
+ "cp .env.example .env",
996
+ `${config.packageManager === "npm" ? "npm run" : config.packageManager} dev`
997
+ ].join("\n") : [
932
998
  `cd ${config.name}`,
933
999
  "cp .env.example .dev.vars",
934
1000
  `${config.packageManager === "npm" ? "npm run" : config.packageManager} dev`
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "name": "@fluxy-chat/create-fluxy-chat",
3
- "version": "0.1.0",
4
- "type": "module",
5
- "main": "./dist/index.js",
6
- "bin": {
7
- "create-fluxy-chat": "./dist/index.js"
8
- },
9
- "engines": {
10
- "node": "\u003e=20.12"
11
- },
12
- "files": [
13
- "dist",
14
- "templates",
15
- "README.md"
16
- ],
17
- "scripts": {
18
- "build": "tsup src/index.ts --format esm --dts",
19
- "dev": "tsup src/index.ts --format esm --watch",
20
- "typecheck": "tsc --noEmit",
21
- "prepublishOnly": "pnpm run build"
22
- },
23
- "dependencies": {
24
- "@clack/prompts": "^0.7.0",
25
- "picocolors": "^1.1.1"
26
- },
27
- "devDependencies": {
28
- "@types/node": "^22.0.0",
29
- "tsup": "^8.3.5",
30
- "typescript": "^5.6.0"
31
- },
32
- "repository": {
33
- "type": "git",
34
- "url": "git+https://github.com/AlessandroFare/fluxychat.git",
35
- "directory": "packages/create-fluxy-chat"
36
- },
37
- "homepage": "https://github.com/AlessandroFare/fluxychat/tree/main/packages/create-fluxy-chat#readme",
38
- "bugs": {
39
- "url": "https://github.com/AlessandroFare/fluxychat/issues"
40
- },
41
- "publishConfig": {
42
- "access": "public"
43
- },
44
- "license": "MIT"
45
- }
1
+ {
2
+ "name": "@fluxy-chat/create-fluxy-chat",
3
+ "version": "0.3.0",
4
+ "description": "Scaffold a new FluxyChat bot project with a single command",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "create-fluxy-chat": "./dist/index.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20.12"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "templates",
16
+ "README.md"
17
+ ],
18
+ "dependencies": {
19
+ "@clack/prompts": "^0.7.0",
20
+ "picocolors": "^1.1.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.0.0",
24
+ "tsup": "^8.3.5",
25
+ "typescript": "^5.6.0"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/AlessandroFare/fluxychat.git",
30
+ "directory": "packages/create-fluxy-chat"
31
+ },
32
+ "homepage": "https://github.com/AlessandroFare/fluxychat/tree/main/packages/create-fluxy-chat#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/AlessandroFare/fluxychat/issues"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "license": "MIT",
40
+ "scripts": {
41
+ "build": "tsup src/index.ts --format esm --dts",
42
+ "dev": "tsup src/index.ts --format esm --watch",
43
+ "typecheck": "tsc --noEmit"
44
+ }
45
+ }
package/readme.md CHANGED
@@ -2,17 +2,24 @@
2
2
 
3
3
  Scaffold a new [FluxyChat](https://github.com/AlessandroFare/fluxychat) bot project with a single command.
4
4
 
5
- ## Quick Start
5
+ ## Quick start
6
6
 
7
7
  ```bash
8
- npx create-fluxy-chat my-bot
8
+ # Minimal chat widget (recommended — 3 lines in App.tsx)
9
+ npx create-fluxy-chat my-chat --minimal
10
+
11
+ # React + useChat (more control)
12
+ npx create-fluxy-chat my-chat --template react
9
13
  ```
10
14
 
11
- This launches an interactive prompt to configure your bot project.
15
+ Interactive mode defaults to the **React chat app** template.
12
16
 
13
- ## Non-Interactive Usage
17
+ ## Non-interactive usage
14
18
 
15
19
  ```bash
20
+ # React + Vite + useChat (recommended)
21
+ npx create-fluxy-chat my-chat --template react -y
22
+
16
23
  # Create a Slack bot with pnpm
17
24
  npx create-fluxy-chat my-bot --adapter slack --pm pnpm
18
25
 
@@ -22,7 +29,7 @@ npx create-fluxy-chat my-bot --adapter telegram --skip-install
22
29
  # Create a Discord bot with defaults
23
30
  npx create-fluxy-chat my-bot -y --adapter discord
24
31
 
25
- # Create a basic webhook bot
32
+ # Create a basic webhook bot (includes fluxy.config.ts template)
26
33
  npx create-fluxy-chat my-bot --adapter basic
27
34
  ```
28
35
 
@@ -30,10 +37,12 @@ npx create-fluxy-chat my-bot --adapter basic
30
37
 
31
38
  | Flag | Short | Description |
32
39
  | --- | --- | --- |
33
- | `--adapter <type>` | `-a` | Adapter: `basic`, `slack`, `telegram`, `discord`, `web` |
40
+ | `--adapter <type>` | `-a` | Adapter: `react`, `basic`, `slack`, `telegram`, `discord`, `web` |
41
+ | `--template <type>` | `-t` | Alias for `--adapter (e.g. react)` |
34
42
  | `--pm <manager>` | | Package manager: `npm`, `pnpm`, `yarn` |
35
43
  | `--language <lang>` | `-l` | Language: `typescript` (default) or `javascript` |
36
44
  | `--yes` | `-y` | Skip prompts and accept defaults |
45
+ | `--minimal` | | Chat-only widget (`@fluxy-chat/ui-kit`) — no platform modules |
37
46
  | `--skip-install` | | Skip dependency installation |
38
47
  | `--no-git` | | Skip git repository initialization |
39
48
  | `--help` | `-h` | Show help |
@@ -42,26 +51,27 @@ npx create-fluxy-chat my-bot --adapter basic
42
51
 
43
52
  | Adapter | Description | Platform |
44
53
  | --- | --- | --- |
54
+ | `react` | Vite + React chat UI with `useChat` | Browser / SPA |
45
55
  | `basic` | Generic webhook bot | Cloudflare Workers |
46
56
  | `slack` | Slack Events API bot | Slack |
47
57
  | `telegram` | Telegram webhook bot | Telegram |
48
58
  | `discord` | Discord interactions bot | Discord |
49
59
  | `web` | Web chat HTTP API bot | Any HTTP client |
50
60
 
51
- ## What You Get
61
+ ## What you get
52
62
 
53
63
  Each generated project includes:
54
64
 
55
65
  - **`src/index.ts`** — Cloudflare Workers entry point with route handling
56
66
  - **`src/bot.ts`** — Bot handler using `@fluxy-chat/sdk`
67
+ - **`fluxy.config.ts`** — Room authz and publish middleware (basic template)
57
68
  - **`wrangler.toml`** — Cloudflare Workers deployment config
58
69
  - **`.dev.vars`** — Local development environment variables
59
70
  - **`.env.example`** — Example environment variables for your adapter
60
71
  - **`tsconfig.json`** — TypeScript configuration (for TS projects)
61
72
  - **`README.md`** — Project-specific setup instructions
62
- - **`.gitignore`** — Standard ignore patterns
63
73
 
64
- ## Package Manager Detection
74
+ ## Package manager detection
65
75
 
66
76
  The CLI auto-detects your package manager from lockfiles:
67
77
 
@@ -69,20 +79,13 @@ The CLI auto-detects your package manager from lockfiles:
69
79
  - `yarn.lock` → yarn
70
80
  - `package-lock.json` or none → npm
71
81
 
72
- You can override with `--pm`.
73
-
74
- ## Development
82
+ Override with `--pm`.
75
83
 
76
- ```bash
77
- # Install dependencies
78
- pnpm install
79
-
80
- # Build the CLI
81
- pnpm build
84
+ ## Related packages
82
85
 
83
- # Run locally
84
- node dist/index.js my-test-bot -y --adapter basic
85
- ```
86
+ - [`@fluxy-chat/sdk`](https://www.npmjs.com/package/@fluxy-chat/sdk)
87
+ - [`@fluxy-chat/agent`](https://www.npmjs.com/package/@fluxy-chat/agent)
88
+ - [`@fluxy-chat/config`](https://www.npmjs.com/package/@fluxy-chat/config)
86
89
 
87
90
  ## License
88
91
 
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Example fluxy.config.ts — copy to your worker project root.
3
+ * See packages/config/README.md
4
+ */
5
+ import {
6
+ defineConfig,
7
+ allow,
8
+ block,
9
+ defineMiddleware,
10
+ allowPublish,
11
+ } from "@fluxy-chat/config";
12
+
13
+ const moderate = defineMiddleware("publish", (ctx) => {
14
+ if (ctx.capabilities.publish === false) {
15
+ return { action: "block", reason: "You cannot post here." };
16
+ }
17
+ return allowPublish();
18
+ });
19
+
20
+ export default defineConfig({
21
+ client: { readOn: "visible", wsCache: "on", historyLimit: 50 },
22
+ rooms: {
23
+ "room-*": {
24
+ anonymous: true,
25
+ authz: () => allow({ publish: true }),
26
+ onPublish: [moderate.handler],
27
+ },
28
+ },
29
+ });
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "latest"
12
+ "@fluxy-chat/sdk": "^0.6.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@cloudflare/workers-types": "^4.0.0",
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "latest",
12
+ "@fluxy-chat/sdk": "^0.6.0",
13
13
  "discord.js": "^14.0.0"
14
14
  },
15
15
  "devDependencies": {
@@ -0,0 +1,3 @@
1
+ VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.com
2
+ VITE_FLUXYCHAT_MEMBER_JWT=your-member-jwt
3
+ VITE_FLUXYCHAT_ROOM_ID=general
@@ -0,0 +1,14 @@
1
+ # FluxyChat minimal starter
2
+
3
+ Three-line chat widget — no platform modules in the generated project.
4
+
5
+ ```bash
6
+ cp .env.example .env
7
+ pnpm dev
8
+ ```
9
+
10
+ Uses `@fluxy-chat/ui-kit` (`FluxyChatWidget`).
11
+
12
+ ## Full platform
13
+
14
+ See [docs.fluxychat.com](https://docs.fluxychat.com/docs) for agents, stream, IoT, and self-host.
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FluxyChat</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "fluxy-chat-minimal",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@fluxy-chat/ui-kit": "^0.1.0",
13
+ "@fluxy-chat/ui": "^0.1.2",
14
+ "@fluxy-chat/react": "^0.1.1",
15
+ "@fluxy-chat/sdk": "^0.6.0",
16
+ "react": "^19.0.0",
17
+ "react-dom": "^19.0.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/react": "^19.0.0",
21
+ "@types/react-dom": "^19.0.0",
22
+ "@vitejs/plugin-react": "^4.3.0",
23
+ "typescript": "^5.6.0",
24
+ "vite": "^6.0.0"
25
+ }
26
+ }
@@ -0,0 +1,24 @@
1
+ import { FluxyChatWidget } from "@fluxy-chat/ui-kit";
2
+
3
+ const workerUrl = import.meta.env.VITE_FLUXYCHAT_WORKER_URL;
4
+ const token = import.meta.env.VITE_FLUXYCHAT_MEMBER_JWT;
5
+ const roomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID || "general";
6
+
7
+ export function App() {
8
+ return (
9
+ <main style={{ maxWidth: 720, margin: "2rem auto", padding: "0 1rem" }}>
10
+ <h1 style={{ fontSize: "1.25rem", marginBottom: "1rem" }}>FluxyChat</h1>
11
+ <FluxyChatWidget
12
+ roomId={roomId}
13
+ workerUrl={workerUrl}
14
+ token={token}
15
+ theme="default"
16
+ height={520}
17
+ />
18
+ <p style={{ marginTop: "1rem", fontSize: 13, color: "#71717a" }}>
19
+ Need stream, IoT, or agents? See{" "}
20
+ <a href="https://docs.fluxychat.com/docs">full platform docs</a>.
21
+ </p>
22
+ </main>
23
+ );
24
+ }
@@ -0,0 +1,13 @@
1
+ :root {
2
+ font-family: system-ui, sans-serif;
3
+ color: #18181b;
4
+ background: #fafafa;
5
+ }
6
+
7
+ body {
8
+ margin: 0;
9
+ }
10
+
11
+ a {
12
+ color: #c2410c;
13
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { App } from "./App";
4
+ import "./index.css";
5
+
6
+ createRoot(document.getElementById("root")!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ );
@@ -0,0 +1,7 @@
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
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "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
+ },
16
+ "include": ["src"]
17
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: { port: 5173 },
7
+ });
@@ -0,0 +1,8 @@
1
+ # FluxyChat worker URL (hosted or self-hosted)
2
+ VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
3
+
4
+ # Member JWT from POST /auth/token or onboarding wizard
5
+ VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
6
+
7
+ # Demo room — create in console or onboarding
8
+ VITE_FLUXYCHAT_ROOM_ID=demo
@@ -0,0 +1,33 @@
1
+ # FluxyChat React starter
2
+
3
+ Minimal Vite + React app with `useChat` — first message in minutes.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ cp .env.example .env
9
+ # Edit .env with your Worker URL + member JWT
10
+ npm install
11
+ npm run dev
12
+ ```
13
+
14
+ Open http://localhost:5173 and send a message.
15
+
16
+ ## Get credentials
17
+
18
+ 1. **Hosted:** [fluxychat.com/onboarding](https://fluxychat.com/onboarding) → copy Worker URL + JWT
19
+ 2. **Local monorepo:** `pnpm run first-message` from the FluxyChat repo
20
+
21
+ ## Scripts
22
+
23
+ | Command | Description |
24
+ |---------|-------------|
25
+ | `npm run dev` | Vite dev server |
26
+ | `npm run build` | Production build |
27
+ | `npm run preview` | Preview production build |
28
+
29
+ ## Next steps
30
+
31
+ - Add `@fluxy-chat/ui` for polished message bubbles
32
+ - Use `useInbox` for unified feed
33
+ - See [chat-only quickstart](https://docs.fluxychat.com/docs/getting-started/chat-only-quickstart)
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FluxyChat</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "fluxy-chat-app",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@fluxy-chat/react": "^0.1.1",
13
+ "@fluxy-chat/sdk": "^0.6.0",
14
+ "react": "^19.0.0",
15
+ "react-dom": "^19.0.0",
16
+ "zustand": "^5.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^19.0.0",
20
+ "@types/react-dom": "^19.0.0",
21
+ "@vitejs/plugin-react": "^4.3.0",
22
+ "typescript": "^5.6.0",
23
+ "vite": "^6.0.0"
24
+ }
25
+ }
@@ -0,0 +1,95 @@
1
+ import { useMemo, useState } from "react";
2
+ import { FluxyChatClient } from "@fluxy-chat/sdk";
3
+ import { FluxyRealtimeProvider, useChat } from "@fluxy-chat/react";
4
+
5
+ const workerUrl = import.meta.env.VITE_FLUXYCHAT_WORKER_URL;
6
+ const memberJwt = import.meta.env.VITE_FLUXYCHAT_MEMBER_JWT;
7
+ const defaultRoomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID || "demo";
8
+
9
+ function ChatPanel({ roomId }: { roomId: string }) {
10
+ const { messages, sendMessage, connectionState } = useChat({
11
+ roomId,
12
+ markReadLatest: true,
13
+ });
14
+ const [draft, setDraft] = useState("");
15
+
16
+ return (
17
+ <section className="chat-panel">
18
+ <header className="chat-header">
19
+ <strong>{roomId}</strong>
20
+ <span className="status">{connectionState.status}</span>
21
+ </header>
22
+ <ul className="messages">
23
+ {messages.map((m) => (
24
+ <li key={m.id} className="message">
25
+ <span className="author">{m.userId}</span>
26
+ <span>{m.content}</span>
27
+ </li>
28
+ ))}
29
+ </ul>
30
+ <form
31
+ className="composer"
32
+ onSubmit={(e) => {
33
+ e.preventDefault();
34
+ const text = draft.trim();
35
+ if (!text) return;
36
+ void sendMessage(text);
37
+ setDraft("");
38
+ }}
39
+ >
40
+ <input
41
+ value={draft}
42
+ onChange={(e) => setDraft(e.target.value)}
43
+ placeholder="Type a message…"
44
+ aria-label="Message"
45
+ />
46
+ <button type="submit">Send</button>
47
+ </form>
48
+ </section>
49
+ );
50
+ }
51
+
52
+ export function App() {
53
+ const [roomId, setRoomId] = useState(defaultRoomId);
54
+
55
+ const client = useMemo(() => {
56
+ if (!workerUrl?.trim() || !memberJwt?.trim()) return null;
57
+ return new FluxyChatClient({
58
+ baseUrl: workerUrl.trim(),
59
+ userId: "demo-user",
60
+ token: memberJwt.trim(),
61
+ });
62
+ }, []);
63
+
64
+ if (!client) {
65
+ return (
66
+ <main className="shell">
67
+ <h1>FluxyChat — chat-only starter</h1>
68
+ <p>
69
+ Copy <code>.env.example</code> to <code>.env</code> and set{" "}
70
+ <code>VITE_FLUXYCHAT_WORKER_URL</code> and <code>VITE_FLUXYCHAT_MEMBER_JWT</code>.
71
+ </p>
72
+ <p>
73
+ Get credentials from{" "}
74
+ <a href="https://fluxychat.com/onboarding" target="_blank" rel="noreferrer">
75
+ onboarding
76
+ </a>{" "}
77
+ or <code>pnpm run first-message</code> in the monorepo.
78
+ </p>
79
+ </main>
80
+ );
81
+ }
82
+
83
+ return (
84
+ <main className="shell">
85
+ <h1>FluxyChat</h1>
86
+ <label className="room-picker">
87
+ Room
88
+ <input value={roomId} onChange={(e) => setRoomId(e.target.value)} />
89
+ </label>
90
+ <FluxyRealtimeProvider client={client}>
91
+ <ChatPanel roomId={roomId.trim() || defaultRoomId} />
92
+ </FluxyRealtimeProvider>
93
+ </main>
94
+ );
95
+ }
@@ -0,0 +1,106 @@
1
+ :root {
2
+ font-family: system-ui, -apple-system, sans-serif;
3
+ line-height: 1.5;
4
+ color: #0f172a;
5
+ background: #f8fafc;
6
+ }
7
+
8
+ * {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ body {
13
+ margin: 0;
14
+ }
15
+
16
+ .shell {
17
+ max-width: 720px;
18
+ margin: 0 auto;
19
+ padding: 1.5rem;
20
+ }
21
+
22
+ .chat-panel {
23
+ display: flex;
24
+ flex-direction: column;
25
+ height: min(70vh, 560px);
26
+ border: 1px solid #e2e8f0;
27
+ border-radius: 12px;
28
+ background: #fff;
29
+ overflow: hidden;
30
+ }
31
+
32
+ .chat-header {
33
+ display: flex;
34
+ justify-content: space-between;
35
+ padding: 0.75rem 1rem;
36
+ border-bottom: 1px solid #e2e8f0;
37
+ background: #2563eb;
38
+ color: #fff;
39
+ }
40
+
41
+ .status {
42
+ font-size: 0.75rem;
43
+ opacity: 0.9;
44
+ }
45
+
46
+ .messages {
47
+ flex: 1;
48
+ overflow-y: auto;
49
+ list-style: none;
50
+ margin: 0;
51
+ padding: 1rem;
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 0.5rem;
55
+ }
56
+
57
+ .message {
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: 0.15rem;
61
+ padding: 0.5rem 0.75rem;
62
+ border-radius: 8px;
63
+ background: #f1f5f9;
64
+ }
65
+
66
+ .author {
67
+ font-size: 0.7rem;
68
+ color: #64748b;
69
+ }
70
+
71
+ .composer {
72
+ display: flex;
73
+ gap: 0.5rem;
74
+ padding: 0.75rem;
75
+ border-top: 1px solid #e2e8f0;
76
+ }
77
+
78
+ .composer input {
79
+ flex: 1;
80
+ border: 1px solid #cbd5e1;
81
+ border-radius: 8px;
82
+ padding: 0.5rem 0.75rem;
83
+ }
84
+
85
+ .composer button {
86
+ border: none;
87
+ border-radius: 8px;
88
+ background: #2563eb;
89
+ color: #fff;
90
+ padding: 0.5rem 1rem;
91
+ cursor: pointer;
92
+ }
93
+
94
+ .room-picker {
95
+ display: flex;
96
+ flex-direction: column;
97
+ gap: 0.25rem;
98
+ margin-bottom: 1rem;
99
+ font-size: 0.875rem;
100
+ }
101
+
102
+ .room-picker input {
103
+ padding: 0.5rem 0.75rem;
104
+ border: 1px solid #cbd5e1;
105
+ border-radius: 8px;
106
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { App } from "./App";
4
+ import "./index.css";
5
+
6
+ createRoot(document.getElementById("root")!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ );
@@ -0,0 +1,11 @@
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
+ }
8
+
9
+ interface ImportMeta {
10
+ readonly env: ImportMetaEnv;
11
+ }
@@ -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,7 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: { port: 5173 },
7
+ });
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "latest",
12
+ "@fluxy-chat/sdk": "^0.6.0",
13
13
  "@slack/bolt": "^4.0.0",
14
14
  "@slack/web-api": "^7.0.0"
15
15
  },
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "latest",
12
+ "@fluxy-chat/sdk": "^0.6.0",
13
13
  "node-telegram-bot-api": "^0.66.0"
14
14
  },
15
15
  "devDependencies": {