@fluxy-chat/create-fluxy-chat 0.5.9 → 0.5.13
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 +36 -0
- package/dist/index.js +111 -1
- package/package.json +1 -1
- package/readme.md +11 -0
- package/templates/basic/package.json +1 -1
- package/templates/comments-board/.env.example +11 -0
- package/templates/comments-board/README.md +9 -0
- package/templates/comments-board/index.html +12 -0
- package/templates/comments-board/package.json +26 -0
- package/templates/comments-board/src/App.tsx +86 -0
- package/templates/comments-board/src/index.css +75 -0
- package/templates/comments-board/src/main.tsx +10 -0
- package/templates/comments-board/src/session.ts +73 -0
- package/templates/comments-board/src/vite-env.d.ts +13 -0
- package/templates/comments-board/tsconfig.json +21 -0
- package/templates/comments-board/vite.config.ts +7 -0
- package/templates/deal-room/.env.example +11 -0
- package/templates/deal-room/README.md +11 -0
- package/templates/deal-room/index.html +12 -0
- package/templates/deal-room/package.json +25 -0
- package/templates/deal-room/src/App.tsx +110 -0
- package/templates/deal-room/src/index.css +75 -0
- package/templates/deal-room/src/main.tsx +10 -0
- package/templates/deal-room/src/session.ts +73 -0
- package/templates/deal-room/src/vite-env.d.ts +13 -0
- package/templates/deal-room/tsconfig.json +21 -0
- package/templates/deal-room/vite.config.ts +7 -0
- package/templates/discord/package.json +1 -1
- package/templates/draw/.env.example +7 -0
- package/templates/draw/README.md +7 -0
- package/templates/draw/index.html +12 -0
- package/templates/draw/package.json +25 -0
- package/templates/draw/src/App.tsx +91 -0
- package/templates/draw/src/index.css +75 -0
- package/templates/draw/src/main.tsx +10 -0
- package/templates/draw/src/session.ts +73 -0
- package/templates/draw/src/vite-env.d.ts +13 -0
- package/templates/draw/tsconfig.json +21 -0
- package/templates/draw/vite.config.ts +7 -0
- package/templates/fleet-panel/.env.example +11 -0
- package/templates/fleet-panel/README.md +9 -0
- package/templates/fleet-panel/index.html +12 -0
- package/templates/fleet-panel/package.json +25 -0
- package/templates/fleet-panel/src/App.tsx +83 -0
- package/templates/fleet-panel/src/index.css +75 -0
- package/templates/fleet-panel/src/main.tsx +10 -0
- package/templates/fleet-panel/src/session.ts +73 -0
- package/templates/fleet-panel/src/vite-env.d.ts +13 -0
- package/templates/fleet-panel/tsconfig.json +21 -0
- package/templates/fleet-panel/vite.config.ts +7 -0
- package/templates/full/package.json +4 -4
- package/templates/full/scripts/hoist-fluxy-sdk.mjs +1 -1
- package/templates/full/src/App.tsx +20 -6
- package/templates/game-tick/.env.example +11 -0
- package/templates/game-tick/README.md +9 -0
- package/templates/game-tick/index.html +12 -0
- package/templates/game-tick/package.json +25 -0
- package/templates/game-tick/src/App.tsx +87 -0
- package/templates/game-tick/src/index.css +75 -0
- package/templates/game-tick/src/main.tsx +10 -0
- package/templates/game-tick/src/session.ts +73 -0
- package/templates/game-tick/src/vite-env.d.ts +13 -0
- package/templates/game-tick/tsconfig.json +21 -0
- package/templates/game-tick/vite.config.ts +7 -0
- package/templates/iot-panel/.env.example +7 -0
- package/templates/iot-panel/README.md +16 -0
- package/templates/iot-panel/index.html +12 -0
- package/templates/iot-panel/package.json +25 -0
- package/templates/iot-panel/src/App.tsx +97 -0
- package/templates/iot-panel/src/index.css +75 -0
- package/templates/iot-panel/src/main.tsx +10 -0
- package/templates/iot-panel/src/session.ts +73 -0
- package/templates/iot-panel/src/vite-env.d.ts +13 -0
- package/templates/iot-panel/tsconfig.json +21 -0
- package/templates/iot-panel/vite.config.ts +7 -0
- package/templates/javascript-live-cursors/.env.example +9 -0
- package/templates/javascript-live-cursors/README.md +9 -0
- package/templates/javascript-live-cursors/index.html +17 -0
- package/templates/javascript-live-cursors/package.json +18 -0
- package/templates/javascript-live-cursors/src/index.css +31 -0
- package/templates/javascript-live-cursors/src/main.ts +89 -0
- package/templates/javascript-live-cursors/src/vite-env.d.ts +12 -0
- package/templates/javascript-live-cursors/tsconfig.json +13 -0
- package/templates/javascript-live-cursors/vite.config.ts +5 -0
- package/templates/live-cursors/.env.example +9 -0
- package/templates/live-cursors/README.md +20 -0
- package/templates/live-cursors/index.html +12 -0
- package/templates/live-cursors/package.json +25 -0
- package/templates/live-cursors/src/App.tsx +214 -0
- package/templates/live-cursors/src/index.css +94 -0
- package/templates/live-cursors/src/main.tsx +10 -0
- package/templates/live-cursors/src/vite-env.d.ts +12 -0
- package/templates/live-cursors/tsconfig.json +21 -0
- package/templates/live-cursors/vite.config.ts +7 -0
- package/templates/live-cursors-chat/.env.example +9 -0
- package/templates/live-cursors-chat/README.md +11 -0
- package/templates/live-cursors-chat/index.html +12 -0
- package/templates/live-cursors-chat/package.json +25 -0
- package/templates/live-cursors-chat/src/App.tsx +241 -0
- package/templates/live-cursors-chat/src/index.css +94 -0
- package/templates/live-cursors-chat/src/main.tsx +10 -0
- package/templates/live-cursors-chat/src/vite-env.d.ts +12 -0
- package/templates/live-cursors-chat/tsconfig.json +21 -0
- package/templates/live-cursors-chat/vite.config.ts +7 -0
- package/templates/minimal/package.json +4 -4
- package/templates/react/package.json +2 -2
- package/templates/react/src/App.tsx +8 -2
- package/templates/slack/package.json +1 -1
- package/templates/telegram/package.json +1 -1
- package/templates/tiptap-room/.env.example +9 -0
- package/templates/tiptap-room/README.md +14 -0
- package/templates/tiptap-room/index.html +12 -0
- package/templates/tiptap-room/package.json +29 -0
- package/templates/tiptap-room/src/App.tsx +217 -0
- package/templates/tiptap-room/src/index.css +107 -0
- package/templates/tiptap-room/src/main.tsx +10 -0
- package/templates/tiptap-room/src/vite-env.d.ts +12 -0
- package/templates/tiptap-room/tsconfig.json +21 -0
- package/templates/tiptap-room/vite.config.ts +7 -0
- package/templates/voice-stage/.env.example +11 -0
- package/templates/voice-stage/README.md +11 -0
- package/templates/voice-stage/index.html +12 -0
- package/templates/voice-stage/package.json +25 -0
- package/templates/voice-stage/src/App.tsx +61 -0
- package/templates/voice-stage/src/index.css +75 -0
- package/templates/voice-stage/src/main.tsx +10 -0
- package/templates/voice-stage/src/session.ts +73 -0
- package/templates/voice-stage/src/vite-env.d.ts +13 -0
- package/templates/voice-stage/tsconfig.json +21 -0
- package/templates/voice-stage/vite.config.ts +7 -0
- package/templates/war-room/.env.example +11 -0
- package/templates/war-room/README.md +11 -0
- package/templates/war-room/index.html +12 -0
- package/templates/war-room/package.json +26 -0
- package/templates/war-room/src/App.tsx +120 -0
- package/templates/war-room/src/index.css +75 -0
- package/templates/war-room/src/main.tsx +10 -0
- package/templates/war-room/src/session.ts +73 -0
- package/templates/war-room/src/vite-env.d.ts +13 -0
- package/templates/war-room/tsconfig.json +21 -0
- package/templates/war-room/vite.config.ts +7 -0
- package/templates/whiteboard/.env.example +11 -0
- package/templates/whiteboard/README.md +9 -0
- package/templates/whiteboard/index.html +12 -0
- package/templates/whiteboard/package.json +26 -0
- package/templates/whiteboard/src/App.tsx +122 -0
- package/templates/whiteboard/src/index.css +75 -0
- package/templates/whiteboard/src/main.tsx +10 -0
- package/templates/whiteboard/src/session.ts +73 -0
- package/templates/whiteboard/src/vite-env.d.ts +13 -0
- package/templates/whiteboard/tsconfig.json +21 -0
- package/templates/whiteboard/vite.config.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.13] - 2026-08-27
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Templates pin `@fluxy-chat/sdk@^0.6.4` (game `listLeaderboard`). Publish `@fluxy-chat/sdk@0.6.4` **before** this CLI, or `pnpm create` / StackBlitz will miss that version.
|
|
8
|
+
|
|
9
|
+
## [0.5.12] - 2026-08-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Gallery `--example` apps: `live-cursors`, `live-cursors-chat`, `javascript-live-cursors`, `tiptap-room`, `whiteboard`, `draw`, `comments-board`, `war-room`, `iot-panel`, `deal-room`, `fleet-panel`, `game-tick`, `voice-stage`.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Templates that use React pin `@fluxy-chat/react@^0.1.4`. Publish `@fluxy-chat/protocol@0.1.5` and `@fluxy-chat/react@0.1.4` **before** this CLI, or `pnpm create` / StackBlitz will miss those versions.
|
|
18
|
+
|
|
19
|
+
## [0.5.11] - 2026-08-25
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- React and full templates expose `stopAgentStream` (Stop / Stop generation) while an agent reply is streaming. Comes from `@fluxy-chat/sdk@0.6.3` via `useChat`.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Templates pin `@fluxy-chat/sdk@^0.6.3`, `@fluxy-chat/react@^0.1.3`, `@fluxy-chat/ui@^0.1.4`, and `@fluxy-chat/ui-kit@^0.1.1` (minimal). Publish those packages **before** this CLI, or `pnpm create` will 404.
|
|
28
|
+
|
|
29
|
+
## [0.5.10] - 2026-08-25
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Gold-path outro mentions `@assistant` in the generated React app.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Templates still depend on `@fluxy-chat/sdk@^0.6.2` (and `@fluxy-chat/react@^0.1.2` where used) so `pnpm create` works **before and after** sdk `0.6.3` is on npm. `^0.6.2` installs `0.6.3` once published.
|
|
38
|
+
|
|
3
39
|
## [0.5.9] - 2026-08-21
|
|
4
40
|
|
|
5
41
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -875,6 +875,21 @@ import { exec } from "child_process";
|
|
|
875
875
|
import { promisify } from "util";
|
|
876
876
|
var execAsync = promisify(exec);
|
|
877
877
|
var TEMPLATE_CHOICES = "react, full, basic, slack, telegram, discord, web, hr-feedback";
|
|
878
|
+
var GALLERY_EXAMPLES = [
|
|
879
|
+
"live-cursors",
|
|
880
|
+
"live-cursors-chat",
|
|
881
|
+
"javascript-live-cursors",
|
|
882
|
+
"tiptap-room",
|
|
883
|
+
"war-room",
|
|
884
|
+
"iot-panel",
|
|
885
|
+
"draw",
|
|
886
|
+
"deal-room",
|
|
887
|
+
"fleet-panel",
|
|
888
|
+
"game-tick",
|
|
889
|
+
"voice-stage",
|
|
890
|
+
"comments-board",
|
|
891
|
+
"whiteboard"
|
|
892
|
+
];
|
|
878
893
|
function parseArgs(argv) {
|
|
879
894
|
const args = {
|
|
880
895
|
yes: false,
|
|
@@ -896,6 +911,13 @@ function parseArgs(argv) {
|
|
|
896
911
|
} else if (arg === "--full") {
|
|
897
912
|
args.full = true;
|
|
898
913
|
args.adapter = "full";
|
|
914
|
+
} else if (arg === "--example") {
|
|
915
|
+
const value = argv[++i]?.trim();
|
|
916
|
+
if (!value || !GALLERY_EXAMPLES.includes(value)) {
|
|
917
|
+
console.error(`Invalid example: ${value ?? ""}. Choose: ${GALLERY_EXAMPLES.join(", ")}`);
|
|
918
|
+
process.exit(1);
|
|
919
|
+
}
|
|
920
|
+
args.example = value;
|
|
899
921
|
} else if (arg === "--mode") {
|
|
900
922
|
const value = argv[++i]?.trim().toLowerCase();
|
|
901
923
|
if (value === "local" || value === "hosted" || value === "self-host") {
|
|
@@ -975,6 +997,7 @@ ${pc.bold("Options:")}
|
|
|
975
997
|
hosted = Clerk on fluxychat.com (no wrangler)
|
|
976
998
|
local / self-host = your Worker (asks for URL + keys)
|
|
977
999
|
--minimal Chat-only widget (ui-kit)
|
|
1000
|
+
--example <name> Gallery app: ${GALLERY_EXAMPLES.join(", ")}
|
|
978
1001
|
--skip-install Skip dependency installation
|
|
979
1002
|
--no-git Skip git repository initialization
|
|
980
1003
|
-h, --help Show this help
|
|
@@ -982,6 +1005,10 @@ ${pc.bold("Options:")}
|
|
|
982
1005
|
${pc.bold("Examples:")}
|
|
983
1006
|
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-app --mode hosted -y")}
|
|
984
1007
|
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-app --mode self-host")}
|
|
1008
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-cursors --example live-cursors")}
|
|
1009
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-doc --example tiptap-room")}
|
|
1010
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-war --example war-room")}
|
|
1011
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-iot --example iot-panel")}
|
|
985
1012
|
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-chat --minimal")}
|
|
986
1013
|
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack")}
|
|
987
1014
|
`;
|
|
@@ -992,6 +1019,88 @@ async function main() {
|
|
|
992
1019
|
process.exit(0);
|
|
993
1020
|
}
|
|
994
1021
|
intro(pc.bgCyan(pc.black(" create-fluxy-chat ")));
|
|
1022
|
+
if (args.example) {
|
|
1023
|
+
const exampleName = args.example;
|
|
1024
|
+
const projectName = args.name ?? `my-${exampleName}`;
|
|
1025
|
+
const nameError = validateProjectName(projectName);
|
|
1026
|
+
if (nameError) {
|
|
1027
|
+
outro(pc.red(nameError));
|
|
1028
|
+
process.exitCode = 1;
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
const projectDir2 = path2.resolve(process.cwd(), projectName);
|
|
1032
|
+
if (fs2.existsSync(projectDir2) && fs2.readdirSync(projectDir2).length > 0) {
|
|
1033
|
+
outro(pc.red(`Directory "${projectName}" already exists and is not empty.`));
|
|
1034
|
+
process.exitCode = 1;
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const s2 = spinner();
|
|
1038
|
+
s2.start(`Copying example "${exampleName}"`);
|
|
1039
|
+
const templateRoot = path2.join(templatesDir(), exampleName);
|
|
1040
|
+
if (!fs2.existsSync(templateRoot)) {
|
|
1041
|
+
s2.stop("Example template missing.");
|
|
1042
|
+
outro(pc.red(`No template at ${templateRoot}`));
|
|
1043
|
+
process.exitCode = 1;
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
fs2.mkdirSync(projectDir2, { recursive: true });
|
|
1047
|
+
copyDir(templateRoot, projectDir2);
|
|
1048
|
+
const pkgPath = path2.join(projectDir2, "package.json");
|
|
1049
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
|
|
1050
|
+
pkg.name = projectName;
|
|
1051
|
+
writeJson(projectDir2, "package.json", pkg);
|
|
1052
|
+
s2.stop(`Example "${exampleName}" created.`);
|
|
1053
|
+
if (!args.noGit) {
|
|
1054
|
+
const gitSpinner = spinner();
|
|
1055
|
+
gitSpinner.start("Initializing git repository");
|
|
1056
|
+
try {
|
|
1057
|
+
await execAsync("git init", { cwd: projectDir2 });
|
|
1058
|
+
gitSpinner.stop("Git repository initialized.");
|
|
1059
|
+
} catch {
|
|
1060
|
+
gitSpinner.stop("Failed to initialize git repository.");
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
if (!args.skipInstall) {
|
|
1064
|
+
const pm2 = args.pm ?? detectPackageManager(process.env.npm_config_user_agent ?? "");
|
|
1065
|
+
const installSpinner = spinner();
|
|
1066
|
+
installSpinner.start(`Installing dependencies with ${pm2}`);
|
|
1067
|
+
try {
|
|
1068
|
+
await execAsync(installCommand(pm2), { cwd: projectDir2 });
|
|
1069
|
+
installSpinner.stop("Dependencies installed.");
|
|
1070
|
+
} catch {
|
|
1071
|
+
installSpinner.stop("Failed to install dependencies.");
|
|
1072
|
+
log.warning(`Run "${installCommand(pm2)}" manually in the project directory.`);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const pm = args.pm ?? detectPackageManager(process.env.npm_config_user_agent ?? "");
|
|
1076
|
+
const devCmd2 = pm === "npm" ? "npm run" : pm;
|
|
1077
|
+
const tryHints = {
|
|
1078
|
+
"tiptap-room": "# Open two tabs \u2014 type in the editor",
|
|
1079
|
+
"war-room": "# Open two tabs \u2014 chat; set AGENT_ID to invokeAgent",
|
|
1080
|
+
"iot-panel": "# Keep this tab open; curl an ingest from another terminal",
|
|
1081
|
+
draw: "# Open two tabs \u2014 move and click",
|
|
1082
|
+
"deal-room": "# Open two tabs \u2014 propose a decision and ack from both",
|
|
1083
|
+
"fleet-panel": "# Keep this tab open; click Post sample GPS",
|
|
1084
|
+
"game-tick": "# Matchmake + start, then Submit input (not netcode)",
|
|
1085
|
+
"voice-stage": "# Open two tabs \u2014 join speaker/listener (signaling, not WebRTC)",
|
|
1086
|
+
"comments-board": "# Click the canvas to pin a thread",
|
|
1087
|
+
"live-cursors-chat": "# Open two tabs \u2014 move and chat",
|
|
1088
|
+
whiteboard: "# Open two tabs \u2014 draw strokes (Yjs, not a second CRDT)"
|
|
1089
|
+
};
|
|
1090
|
+
const tryHint = tryHints[exampleName] ?? "# Open two tabs \u2014 move the pointer";
|
|
1091
|
+
note(
|
|
1092
|
+
[
|
|
1093
|
+
`cd ${projectName}`,
|
|
1094
|
+
"cp .env.example .env",
|
|
1095
|
+
"# Set VITE_FLUXYCHAT_WORKER_URL + public room ID or member JWT",
|
|
1096
|
+
`${devCmd2} dev`,
|
|
1097
|
+
tryHint
|
|
1098
|
+
].join("\n"),
|
|
1099
|
+
"Next steps"
|
|
1100
|
+
);
|
|
1101
|
+
outro(`${pc.green("Done!")} Visit ${pc.cyan("https://docs.fluxychat.com/llms.txt")}`);
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
995
1104
|
const config = await runPrompts({
|
|
996
1105
|
name: args.name,
|
|
997
1106
|
adapter: args.adapter,
|
|
@@ -1158,7 +1267,8 @@ async function main() {
|
|
|
1158
1267
|
`cd ${config.name}`,
|
|
1159
1268
|
"cp .env.example .env",
|
|
1160
1269
|
"# Set VITE_FLUXYCHAT_WORKER_URL + JWT or public room ID",
|
|
1161
|
-
`${devCmd} dev
|
|
1270
|
+
`${devCmd} dev`,
|
|
1271
|
+
"# In the room, send: @assistant hello"
|
|
1162
1272
|
].join("\n") : [
|
|
1163
1273
|
`cd ${config.name}`,
|
|
1164
1274
|
"cp .env.example .dev.vars",
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -26,6 +26,16 @@ Self-host writes `.fluxy/worker.dev.vars` (Worker URL, Groq key, signing key). M
|
|
|
26
26
|
```bash
|
|
27
27
|
npx @fluxy-chat/create-fluxy-chat@latest my-app --mode hosted -y
|
|
28
28
|
npx @fluxy-chat/create-fluxy-chat@latest my-app --full -y
|
|
29
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-cursors --example live-cursors
|
|
30
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-doc --example tiptap-room
|
|
31
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-war --example war-room
|
|
32
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-iot --example iot-panel
|
|
33
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-draw --example draw
|
|
34
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-deal --example deal-room
|
|
35
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-fleet --example fleet-panel
|
|
36
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-game --example game-tick
|
|
37
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-stage --example voice-stage
|
|
38
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-comments --example comments-board
|
|
29
39
|
npx @fluxy-chat/create-fluxy-chat@latest my-chat --template react -y
|
|
30
40
|
npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack --pm pnpm
|
|
31
41
|
```
|
|
@@ -41,6 +51,7 @@ npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack --pm pnpm
|
|
|
41
51
|
| `--yes` | `-y` | Skip prompts and accept defaults |
|
|
42
52
|
| `--full` | | Full stack template: chat + `@assistant` + `pnpm setup` / `pnpm dev` |
|
|
43
53
|
| `--minimal` | | Chat-only widget (`@fluxy-chat/ui-kit`), no platform modules |
|
|
54
|
+
| `--example <name>` | | Gallery: `live-cursors`, `live-cursors-chat`, `javascript-live-cursors`, `tiptap-room`, `war-room`, `iot-panel`, `draw`, `deal-room`, `fleet-panel`, `game-tick`, `voice-stage`, `comments-board`, `whiteboard` |
|
|
44
55
|
| `--skip-install` | | Skip dependency installation |
|
|
45
56
|
| `--no-git` | | Skip git repository initialization |
|
|
46
57
|
| `--help` | `-h` | Show help |
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# FluxyChat worker URL (hosted or self-hosted)
|
|
2
|
+
VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
|
|
3
|
+
|
|
4
|
+
# Option A — member JWT
|
|
5
|
+
VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
|
|
6
|
+
VITE_FLUXYCHAT_ROOM_ID=demo
|
|
7
|
+
# Optional — room-peer agent on the timeline
|
|
8
|
+
# VITE_FLUXYCHAT_AGENT_ID=agt_...
|
|
9
|
+
|
|
10
|
+
# Option B — public guest room
|
|
11
|
+
# VITE_FLUXYCHAT_PUBLIC_ROOM_ID=your-public-room-id
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# FluxyChat comments board
|
|
2
|
+
|
|
3
|
+
Contextual `useThreads` pins — **not** chat `parentId` replies. Click the canvas to open `FloatingComposer`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-comments --example comments-board
|
|
7
|
+
cp .env.example .env
|
|
8
|
+
npm run dev
|
|
9
|
+
```
|
|
@@ -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 comments</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-comments-board",
|
|
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.4",
|
|
13
|
+
"@fluxy-chat/sdk": "^0.6.4",
|
|
14
|
+
"@fluxy-chat/ui": "^0.1.4",
|
|
15
|
+
"react": "^19.0.0",
|
|
16
|
+
"react-dom": "^19.0.0",
|
|
17
|
+
"zustand": "^5.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,86 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { FluxyRealtimeProvider, useChat, useThreads } from "@fluxy-chat/react";
|
|
3
|
+
import { CommentPin, FloatingComposer, Thread } from "@fluxy-chat/ui";
|
|
4
|
+
import { useFluxySession, workerUrl } from "./session";
|
|
5
|
+
|
|
6
|
+
function CommentsBoard({ roomId }: { roomId: string }) {
|
|
7
|
+
const { threads, createThread, createComment, markThreadAsResolved, reload } = useThreads({ roomId });
|
|
8
|
+
const [draftPin, setDraftPin] = useState<{ x: number; y: number } | null>(null);
|
|
9
|
+
const [openId, setOpenId] = useState<string | null>(null);
|
|
10
|
+
useChat({
|
|
11
|
+
roomId,
|
|
12
|
+
replay: "off",
|
|
13
|
+
onServerEvent: (ev) => {
|
|
14
|
+
if (ev.name.startsWith("comment.")) void reload();
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const openThread = threads.find((t) => t.id === openId) ?? null;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<section className="panel">
|
|
22
|
+
<header className="chat-header">
|
|
23
|
+
<strong>Comments · {roomId}</strong>
|
|
24
|
+
<span className="status">pins + threads, not chat replies</span>
|
|
25
|
+
</header>
|
|
26
|
+
<div
|
|
27
|
+
className="canvas"
|
|
28
|
+
onClick={(event) => {
|
|
29
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
30
|
+
setDraftPin({ x: event.clientX - rect.left, y: event.clientY - rect.top });
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
{threads.map((thread) =>
|
|
34
|
+
thread.metadata.x != null && thread.metadata.y != null ? (
|
|
35
|
+
<CommentPin
|
|
36
|
+
key={thread.id}
|
|
37
|
+
x={thread.metadata.x}
|
|
38
|
+
y={thread.metadata.y}
|
|
39
|
+
count={thread.comments.length}
|
|
40
|
+
resolved={thread.resolved}
|
|
41
|
+
onClick={() => setOpenId(thread.id)}
|
|
42
|
+
/>
|
|
43
|
+
) : null,
|
|
44
|
+
)}
|
|
45
|
+
{draftPin ? (
|
|
46
|
+
<FloatingComposer
|
|
47
|
+
x={draftPin.x}
|
|
48
|
+
y={draftPin.y}
|
|
49
|
+
onCancel={() => setDraftPin(null)}
|
|
50
|
+
onSubmit={async (body) => {
|
|
51
|
+
const created = await createThread({ body, metadata: { x: draftPin.x, y: draftPin.y } });
|
|
52
|
+
setDraftPin(null);
|
|
53
|
+
if (created) setOpenId(created.id);
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
) : null}
|
|
57
|
+
</div>
|
|
58
|
+
{openThread ? (
|
|
59
|
+
<div style={{ padding: 12 }}>
|
|
60
|
+
<Thread
|
|
61
|
+
thread={openThread}
|
|
62
|
+
onReply={(body) => createComment(openThread.id, body)}
|
|
63
|
+
onResolve={(resolved) => markThreadAsResolved(openThread.id, resolved)}
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
) : null}
|
|
67
|
+
</section>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function App() {
|
|
72
|
+
const { session, loading, error } = useFluxySession();
|
|
73
|
+
if (!workerUrl) return <div className="shell">Set <code>VITE_FLUXYCHAT_WORKER_URL</code>.</div>;
|
|
74
|
+
if (loading) return <div className="shell">Starting…</div>;
|
|
75
|
+
if (error) return <div className="shell error">{error}</div>;
|
|
76
|
+
if (!session) return <div className="shell">Set a member JWT or public room id.</div>;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="shell">
|
|
80
|
+
<p className="mode-badge">{session.mode} · click the canvas to pin a thread</p>
|
|
81
|
+
<FluxyRealtimeProvider workerUrl={session.workerUrl} authTokenProvider={session.token} userId={session.userId}>
|
|
82
|
+
<CommentsBoard roomId={session.roomId} />
|
|
83
|
+
</FluxyRealtimeProvider>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
color: #0f172a;
|
|
5
|
+
background: #f8fafc;
|
|
6
|
+
}
|
|
7
|
+
* { box-sizing: border-box; }
|
|
8
|
+
body { margin: 0; }
|
|
9
|
+
.shell { max-width: 720px; margin: 0 auto; padding: 1.5rem; }
|
|
10
|
+
.panel {
|
|
11
|
+
border: 1px solid #e2e8f0;
|
|
12
|
+
border-radius: 12px;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
background: #fff;
|
|
15
|
+
}
|
|
16
|
+
.chat-header {
|
|
17
|
+
display: flex;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
padding: 0.75rem 1rem;
|
|
20
|
+
background: #0f172a;
|
|
21
|
+
color: #fff;
|
|
22
|
+
}
|
|
23
|
+
.status { font-size: 0.75rem; opacity: 0.9; }
|
|
24
|
+
.roster {
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 0.5rem 1rem;
|
|
27
|
+
list-style: none;
|
|
28
|
+
font-size: 0.8rem;
|
|
29
|
+
color: #64748b;
|
|
30
|
+
border-bottom: 1px solid #e2e8f0;
|
|
31
|
+
}
|
|
32
|
+
.log { min-height: 240px; max-height: 50vh; overflow: auto; padding: 1rem; }
|
|
33
|
+
.composer { display: flex; gap: 0.5rem; padding: 0.75rem; border-top: 1px solid #e2e8f0; }
|
|
34
|
+
.composer input { flex: 1; padding: 0.5rem 0.75rem; border: 1px solid #cbd5e1; border-radius: 8px; }
|
|
35
|
+
.composer button, button.primary {
|
|
36
|
+
padding: 0.5rem 0.75rem;
|
|
37
|
+
border: 0;
|
|
38
|
+
border-radius: 8px;
|
|
39
|
+
background: #2563eb;
|
|
40
|
+
color: #fff;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
}
|
|
43
|
+
.mode-badge { font-size: 0.75rem; color: #64748b; margin-bottom: 0.75rem; }
|
|
44
|
+
.error { color: #b91c1c; }
|
|
45
|
+
code { font-size: 0.85em; }
|
|
46
|
+
.hint { font-size: 0.8rem; color: #475569; white-space: pre-wrap; background: #f1f5f9; padding: 0.75rem; border-radius: 8px; }
|
|
47
|
+
.readings { margin: 0; padding: 0.75rem 1rem; list-style: none; font-size: 0.875rem; }
|
|
48
|
+
.canvas {
|
|
49
|
+
position: relative;
|
|
50
|
+
height: min(70vh, 480px);
|
|
51
|
+
background: #f8fafc;
|
|
52
|
+
touch-action: none;
|
|
53
|
+
}
|
|
54
|
+
.dot {
|
|
55
|
+
position: absolute;
|
|
56
|
+
width: 10px;
|
|
57
|
+
height: 10px;
|
|
58
|
+
border-radius: 999px;
|
|
59
|
+
transform: translate(-50%, -50%);
|
|
60
|
+
pointer-events: none;
|
|
61
|
+
}
|
|
62
|
+
.peer-cursor {
|
|
63
|
+
position: absolute;
|
|
64
|
+
pointer-events: none;
|
|
65
|
+
z-index: 2;
|
|
66
|
+
}
|
|
67
|
+
.peer-cursor span {
|
|
68
|
+
display: block;
|
|
69
|
+
margin-top: 2px;
|
|
70
|
+
padding: 1px 6px;
|
|
71
|
+
border-radius: 4px;
|
|
72
|
+
background: currentColor;
|
|
73
|
+
color: #fff;
|
|
74
|
+
font-size: 11px;
|
|
75
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { FluxyChatClient } from "@fluxy-chat/sdk";
|
|
3
|
+
|
|
4
|
+
const workerUrl = import.meta.env.VITE_FLUXYCHAT_WORKER_URL?.trim();
|
|
5
|
+
const memberJwt = import.meta.env.VITE_FLUXYCHAT_MEMBER_JWT?.trim();
|
|
6
|
+
const publicRoomId = import.meta.env.VITE_FLUXYCHAT_PUBLIC_ROOM_ID?.trim();
|
|
7
|
+
const configuredRoomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID?.trim() || "demo";
|
|
8
|
+
|
|
9
|
+
export interface FluxySession {
|
|
10
|
+
workerUrl: string;
|
|
11
|
+
token: string;
|
|
12
|
+
userId: string;
|
|
13
|
+
roomId: string;
|
|
14
|
+
mode: "member" | "guest";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useFluxySession(): {
|
|
18
|
+
session: FluxySession | null;
|
|
19
|
+
loading: boolean;
|
|
20
|
+
error: string | null;
|
|
21
|
+
} {
|
|
22
|
+
const [session, setSession] = useState<FluxySession | null>(null);
|
|
23
|
+
const [loading, setLoading] = useState(Boolean(workerUrl));
|
|
24
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!workerUrl) {
|
|
28
|
+
setLoading(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (memberJwt) {
|
|
32
|
+
setSession({
|
|
33
|
+
workerUrl,
|
|
34
|
+
token: memberJwt,
|
|
35
|
+
userId: "demo-user",
|
|
36
|
+
roomId: configuredRoomId,
|
|
37
|
+
mode: "member",
|
|
38
|
+
});
|
|
39
|
+
setLoading(false);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (publicRoomId) {
|
|
43
|
+
let cancelled = false;
|
|
44
|
+
void FluxyChatClient.joinPublicRoomAsGuest(workerUrl, publicRoomId, {
|
|
45
|
+
displayName: "Guest",
|
|
46
|
+
})
|
|
47
|
+
.then((guest) => {
|
|
48
|
+
if (cancelled) return;
|
|
49
|
+
setSession({
|
|
50
|
+
workerUrl,
|
|
51
|
+
token: guest.token,
|
|
52
|
+
userId: guest.userId,
|
|
53
|
+
roomId: guest.roomId,
|
|
54
|
+
mode: "guest",
|
|
55
|
+
});
|
|
56
|
+
})
|
|
57
|
+
.catch((err) => {
|
|
58
|
+
if (!cancelled) setError(err instanceof Error ? err.message : "Guest session failed");
|
|
59
|
+
})
|
|
60
|
+
.finally(() => {
|
|
61
|
+
if (!cancelled) setLoading(false);
|
|
62
|
+
});
|
|
63
|
+
return () => {
|
|
64
|
+
cancelled = true;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
return { session, loading, error };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { workerUrl };
|
|
@@ -0,0 +1,13 @@
|
|
|
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_PUBLIC_ROOM_ID?: string;
|
|
8
|
+
readonly VITE_FLUXYCHAT_AGENT_ID?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ImportMeta {
|
|
12
|
+
readonly env: ImportMetaEnv;
|
|
13
|
+
}
|
|
@@ -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,11 @@
|
|
|
1
|
+
# FluxyChat worker URL (hosted or self-hosted)
|
|
2
|
+
VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
|
|
3
|
+
|
|
4
|
+
# Option A — member JWT
|
|
5
|
+
VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
|
|
6
|
+
VITE_FLUXYCHAT_ROOM_ID=demo
|
|
7
|
+
# Optional — room-peer agent on the timeline
|
|
8
|
+
# VITE_FLUXYCHAT_AGENT_ID=agt_...
|
|
9
|
+
|
|
10
|
+
# Option B — public guest room
|
|
11
|
+
# VITE_FLUXYCHAT_PUBLIC_ROOM_ID=your-public-room-id
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# FluxyChat deal room
|
|
2
|
+
|
|
3
|
+
Chat + quorum decisions (`createDecision` / `ackDecision`) + Markdown export. Open **two tabs** and ack from both.
|
|
4
|
+
|
|
5
|
+
Cross-org room linking is **server-side** REST (`/cross-org/...`), not MQTT and not this Vite app.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-deal --example deal-room
|
|
9
|
+
cp .env.example .env
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
@@ -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 deal room</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-deal-room",
|
|
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.4",
|
|
13
|
+
"@fluxy-chat/sdk": "^0.6.4",
|
|
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
|
+
}
|