create-fluxstack 1.14.0 → 1.16.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/LLMD/INDEX.md +4 -3
- package/LLMD/resources/live-binary-delta.md +507 -0
- package/LLMD/resources/live-components.md +208 -12
- package/LLMD/resources/live-rooms.md +731 -333
- package/app/client/.live-stubs/LiveAdminPanel.js +5 -0
- package/app/client/.live-stubs/LiveCounter.js +9 -0
- package/app/client/.live-stubs/LiveForm.js +11 -0
- package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
- package/app/client/.live-stubs/LivePingPong.js +10 -0
- package/app/client/.live-stubs/LiveRoomChat.js +11 -0
- package/app/client/.live-stubs/LiveSharedCounter.js +10 -0
- package/app/client/.live-stubs/LiveUpload.js +15 -0
- package/app/client/src/App.tsx +19 -7
- package/app/client/src/components/AppLayout.tsx +18 -10
- package/app/client/src/live/PingPongDemo.tsx +199 -0
- package/app/client/src/live/RoomChatDemo.tsx +187 -22
- package/app/client/src/live/SharedCounterDemo.tsx +142 -0
- package/app/server/auth/DevAuthProvider.ts +2 -2
- package/app/server/auth/JWTAuthProvider.example.ts +2 -2
- package/app/server/index.ts +2 -2
- package/app/server/live/LiveAdminPanel.ts +1 -1
- package/app/server/live/LivePingPong.ts +61 -0
- package/app/server/live/LiveProtectedChat.ts +1 -1
- package/app/server/live/LiveRoomChat.ts +106 -38
- package/app/server/live/LiveSharedCounter.ts +73 -0
- package/app/server/live/rooms/ChatRoom.ts +68 -0
- package/app/server/live/rooms/CounterRoom.ts +51 -0
- package/app/server/live/rooms/DirectoryRoom.ts +42 -0
- package/app/server/live/rooms/PingRoom.ts +40 -0
- package/app/server/routes/room.routes.ts +1 -2
- package/core/build/live-components-generator.ts +11 -2
- package/core/build/vite-plugins.ts +28 -0
- package/core/client/hooks/useLiveUpload.ts +3 -4
- package/core/client/index.ts +25 -35
- package/core/framework/server.ts +1 -1
- package/core/server/index.ts +1 -2
- package/core/server/live/auto-generated-components.ts +5 -8
- package/core/server/live/index.ts +90 -21
- package/core/server/live/websocket-plugin.ts +54 -1079
- package/core/types/types.ts +76 -1025
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +1 -1
- package/package.json +100 -95
- package/plugins/crypto-auth/index.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +2 -2
- package/tsconfig.json +4 -1
- package/vite.config.ts +40 -12
- package/app/client/src/live/ChatDemo.tsx +0 -107
- package/app/client/src/live/LiveDebuggerPanel.tsx +0 -779
- package/app/server/live/LiveChat.ts +0 -78
- package/core/client/LiveComponentsProvider.tsx +0 -531
- package/core/client/components/Live.tsx +0 -111
- package/core/client/components/LiveDebugger.tsx +0 -1324
- package/core/client/hooks/AdaptiveChunkSizer.ts +0 -215
- package/core/client/hooks/state-validator.ts +0 -130
- package/core/client/hooks/useChunkedUpload.ts +0 -359
- package/core/client/hooks/useLiveChunkedUpload.ts +0 -87
- package/core/client/hooks/useLiveComponent.ts +0 -853
- package/core/client/hooks/useLiveDebugger.ts +0 -392
- package/core/client/hooks/useRoom.ts +0 -409
- package/core/client/hooks/useRoomProxy.ts +0 -382
- package/core/server/live/ComponentRegistry.ts +0 -1128
- package/core/server/live/FileUploadManager.ts +0 -446
- package/core/server/live/LiveComponentPerformanceMonitor.ts +0 -931
- package/core/server/live/LiveDebugger.ts +0 -462
- package/core/server/live/LiveLogger.ts +0 -144
- package/core/server/live/LiveRoomManager.ts +0 -278
- package/core/server/live/RoomEventBus.ts +0 -234
- package/core/server/live/RoomStateManager.ts +0 -172
- package/core/server/live/SingleConnectionManager.ts +0 -0
- package/core/server/live/StateSignature.ts +0 -705
- package/core/server/live/WebSocketConnectionManager.ts +0 -710
- package/core/server/live/auth/LiveAuthContext.ts +0 -71
- package/core/server/live/auth/LiveAuthManager.ts +0 -304
- package/core/server/live/auth/index.ts +0 -19
- package/core/server/live/auth/types.ts +0 -179
package/core/utils/version.ts
CHANGED
package/create-fluxstack.ts
CHANGED
|
@@ -344,7 +344,7 @@ bun.lockb
|
|
|
344
344
|
|
|
345
345
|
// Customize package.json with project name
|
|
346
346
|
const packageJsonPath = join(projectPath, 'package.json')
|
|
347
|
-
const actualProjectName =
|
|
347
|
+
const actualProjectName = basename(projectPath)
|
|
348
348
|
|
|
349
349
|
if (existsSync(packageJsonPath)) {
|
|
350
350
|
const packageContent = readFileSync(packageJsonPath, 'utf-8')
|
package/package.json
CHANGED
|
@@ -1,95 +1,100 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "create-fluxstack",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "⚡ Revolutionary full-stack TypeScript framework with Declarative Config System, Elysia + React + Bun",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"framework",
|
|
7
|
-
"full-stack",
|
|
8
|
-
"typescript",
|
|
9
|
-
"elysia",
|
|
10
|
-
"react",
|
|
11
|
-
"bun",
|
|
12
|
-
"vite"
|
|
13
|
-
],
|
|
14
|
-
"author": "FluxStack Team",
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"homepage": "https://github.com/MarcosBrendonDePaula/FluxStack",
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/MarcosBrendonDePaula/FluxStack.git"
|
|
20
|
-
},
|
|
21
|
-
"module": "app/server/index.ts",
|
|
22
|
-
"type": "module",
|
|
23
|
-
"bin": {
|
|
24
|
-
"create-fluxstack": "create-fluxstack.ts"
|
|
25
|
-
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"dev": "bun run core/cli/index.ts dev",
|
|
28
|
-
"dev:frontend": "bun run core/cli/index.ts dev --frontend-only",
|
|
29
|
-
"dev:backend": "bun run core/cli/index.ts dev --backend-only",
|
|
30
|
-
"build": "cross-env NODE_ENV=production bun run core/cli/index.ts build",
|
|
31
|
-
"build:frontend": "cross-env NODE_ENV=production bun run core/cli/index.ts build --frontend-only",
|
|
32
|
-
"build:backend": "cross-env NODE_ENV=production bun run core/cli/index.ts build --backend-only",
|
|
33
|
-
"build:exe": "cross-env NODE_ENV=production && bun run core/cli/index.ts build && bun run core/cli/index.ts build:exe",
|
|
34
|
-
"start": "NODE_ENV=production bun dist/index.js",
|
|
35
|
-
"create": "bun run core/cli/index.ts create",
|
|
36
|
-
"cli": "bun run core/cli/index.ts",
|
|
37
|
-
"make:component": "bun run core/cli/index.ts make:component",
|
|
38
|
-
"sync-version": "bun run core/utils/sync-version.ts",
|
|
39
|
-
"test": "vitest",
|
|
40
|
-
"test:ui": "vitest --ui",
|
|
41
|
-
"test:coverage": "vitest run --coverage",
|
|
42
|
-
"typecheck:api": "tsc --noEmit -p tsconfig.api-strict.json"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@eslint/js": "^9.30.1",
|
|
46
|
-
"@noble/curves": "1.2.0",
|
|
47
|
-
"@noble/hashes": "1.3.2",
|
|
48
|
-
"@tailwindcss/vite": "^4.1.13",
|
|
49
|
-
"@testing-library/jest-dom": "^6.6.4",
|
|
50
|
-
"@testing-library/react": "^16.3.0",
|
|
51
|
-
"@testing-library/user-event": "^14.6.1",
|
|
52
|
-
"@types/bun": "latest",
|
|
53
|
-
"@types/node": "^24.5.2",
|
|
54
|
-
"@types/react": "^19.1.8",
|
|
55
|
-
"@types/react-dom": "^19.1.6",
|
|
56
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
57
|
-
"@vitest/ui": "^3.2.4",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"eslint
|
|
61
|
-
"eslint-plugin-react-
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"typescript
|
|
68
|
-
"
|
|
69
|
-
"vite-
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"@
|
|
75
|
-
"@
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "create-fluxstack",
|
|
3
|
+
"version": "1.16.0",
|
|
4
|
+
"description": "⚡ Revolutionary full-stack TypeScript framework with Declarative Config System, Elysia + React + Bun",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"framework",
|
|
7
|
+
"full-stack",
|
|
8
|
+
"typescript",
|
|
9
|
+
"elysia",
|
|
10
|
+
"react",
|
|
11
|
+
"bun",
|
|
12
|
+
"vite"
|
|
13
|
+
],
|
|
14
|
+
"author": "FluxStack Team",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://github.com/MarcosBrendonDePaula/FluxStack",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/MarcosBrendonDePaula/FluxStack.git"
|
|
20
|
+
},
|
|
21
|
+
"module": "app/server/index.ts",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"bin": {
|
|
24
|
+
"create-fluxstack": "create-fluxstack.ts"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"dev": "bun run core/cli/index.ts dev",
|
|
28
|
+
"dev:frontend": "bun run core/cli/index.ts dev --frontend-only",
|
|
29
|
+
"dev:backend": "bun run core/cli/index.ts dev --backend-only",
|
|
30
|
+
"build": "cross-env NODE_ENV=production bun run core/cli/index.ts build",
|
|
31
|
+
"build:frontend": "cross-env NODE_ENV=production bun run core/cli/index.ts build --frontend-only",
|
|
32
|
+
"build:backend": "cross-env NODE_ENV=production bun run core/cli/index.ts build --backend-only",
|
|
33
|
+
"build:exe": "cross-env NODE_ENV=production && bun run core/cli/index.ts build && bun run core/cli/index.ts build:exe",
|
|
34
|
+
"start": "NODE_ENV=production bun dist/index.js",
|
|
35
|
+
"create": "bun run core/cli/index.ts create",
|
|
36
|
+
"cli": "bun run core/cli/index.ts",
|
|
37
|
+
"make:component": "bun run core/cli/index.ts make:component",
|
|
38
|
+
"sync-version": "bun run core/utils/sync-version.ts",
|
|
39
|
+
"test": "vitest",
|
|
40
|
+
"test:ui": "vitest --ui",
|
|
41
|
+
"test:coverage": "vitest run --coverage",
|
|
42
|
+
"typecheck:api": "tsc --noEmit -p tsconfig.api-strict.json"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^9.30.1",
|
|
46
|
+
"@noble/curves": "1.2.0",
|
|
47
|
+
"@noble/hashes": "1.3.2",
|
|
48
|
+
"@tailwindcss/vite": "^4.1.13",
|
|
49
|
+
"@testing-library/jest-dom": "^6.6.4",
|
|
50
|
+
"@testing-library/react": "^16.3.0",
|
|
51
|
+
"@testing-library/user-event": "^14.6.1",
|
|
52
|
+
"@types/bun": "latest",
|
|
53
|
+
"@types/node": "^24.5.2",
|
|
54
|
+
"@types/react": "^19.1.8",
|
|
55
|
+
"@types/react-dom": "^19.1.6",
|
|
56
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
57
|
+
"@vitest/ui": "^3.2.4",
|
|
58
|
+
"baseline-browser-mapping": "^2.10.7",
|
|
59
|
+
"cross-env": "^10.1.0",
|
|
60
|
+
"eslint": "^9.30.1",
|
|
61
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
62
|
+
"eslint-plugin-react-refresh": "^0.4.20",
|
|
63
|
+
"globals": "^16.3.0",
|
|
64
|
+
"jsdom": "^26.1.0",
|
|
65
|
+
"rollup": "4.20.0",
|
|
66
|
+
"tailwindcss": "^4.1.13",
|
|
67
|
+
"typescript": "^5.8.3",
|
|
68
|
+
"typescript-eslint": "^8.35.1",
|
|
69
|
+
"vite-plugin-checker": "^0.12.0",
|
|
70
|
+
"vite-tsconfig-paths": "^6.0.5",
|
|
71
|
+
"vitest": "^3.2.4"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@fluxstack/live": "^0.2.0",
|
|
75
|
+
"@fluxstack/live-elysia": "^0.2.0",
|
|
76
|
+
"@fluxstack/live-client": "^0.2.0",
|
|
77
|
+
"@fluxstack/live-react": "^0.2.0",
|
|
78
|
+
"@elysiajs/eden": "^1.3.2",
|
|
79
|
+
"@elysiajs/swagger": "^1.3.1",
|
|
80
|
+
"@vitejs/plugin-react": "^4.6.0",
|
|
81
|
+
"chalk": "^5.3.0",
|
|
82
|
+
"commander": "^12.1.0",
|
|
83
|
+
"elysia": "^1.4.6",
|
|
84
|
+
"lightningcss": "^1.30.1",
|
|
85
|
+
"ora": "^8.1.0",
|
|
86
|
+
"react": "^19.1.0",
|
|
87
|
+
"react-dom": "^19.1.0",
|
|
88
|
+
"react-icons": "^5.5.0",
|
|
89
|
+
"react-router": "^7.9.3",
|
|
90
|
+
"uuid": "^13.0.0",
|
|
91
|
+
"vite": "^7.1.7",
|
|
92
|
+
"winston": "^3.18.3",
|
|
93
|
+
"winston-daily-rotate-file": "^5.0.0",
|
|
94
|
+
"zustand": "^5.0.8"
|
|
95
|
+
},
|
|
96
|
+
"engines": {
|
|
97
|
+
"bun": ">=1.2.0"
|
|
98
|
+
},
|
|
99
|
+
"preferredPackageManager": "bun"
|
|
100
|
+
}
|
|
@@ -9,7 +9,7 @@ type Plugin = FluxStack.Plugin
|
|
|
9
9
|
import { Elysia, t } from "elysia"
|
|
10
10
|
import { CryptoAuthService, AuthMiddleware } from "./server"
|
|
11
11
|
import { CryptoAuthLiveProvider } from "./server/CryptoAuthLiveProvider"
|
|
12
|
-
import { liveAuthManager } from "@core/server/live
|
|
12
|
+
import { liveAuthManager } from "@core/server/live"
|
|
13
13
|
import { makeProtectedRouteCommand } from "./cli/make-protected-route.command"
|
|
14
14
|
|
|
15
15
|
// ✅ Plugin carrega sua própria configuração (da pasta config/ do plugin)
|
|
@@ -14,8 +14,8 @@ import type {
|
|
|
14
14
|
LiveAuthProvider,
|
|
15
15
|
LiveAuthCredentials,
|
|
16
16
|
LiveAuthContext,
|
|
17
|
-
} from '@
|
|
18
|
-
import { AuthenticatedContext, ANONYMOUS_CONTEXT } from '@
|
|
17
|
+
} from '@fluxstack/live'
|
|
18
|
+
import { AuthenticatedContext, ANONYMOUS_CONTEXT } from '@fluxstack/live'
|
|
19
19
|
|
|
20
20
|
export class CryptoAuthLiveProvider implements LiveAuthProvider {
|
|
21
21
|
readonly name = 'crypto-auth'
|
package/tsconfig.json
CHANGED
|
@@ -28,7 +28,10 @@
|
|
|
28
28
|
"@config/*": ["./config/*"],
|
|
29
29
|
"@app/*": ["./app/*"],
|
|
30
30
|
"@shared/*": ["./app/shared/*"],
|
|
31
|
-
"@plugins/*": ["./plugins/*"]
|
|
31
|
+
"@plugins/*": ["./plugins/*"],
|
|
32
|
+
"@fluxstack/live": ["../fluxstack-live/packages/core/src/index.ts"],
|
|
33
|
+
"@fluxstack/live-client": ["../fluxstack-live/packages/client/src/index.ts"],
|
|
34
|
+
"@fluxstack/live-react": ["../fluxstack-live/packages/react/src/index.ts"]
|
|
32
35
|
},
|
|
33
36
|
|
|
34
37
|
// Best practices
|
package/vite.config.ts
CHANGED
|
@@ -1,34 +1,54 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
2
|
import react from '@vitejs/plugin-react'
|
|
3
3
|
import tailwindcss from '@tailwindcss/vite'
|
|
4
|
-
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
5
|
-
import checker from 'vite-plugin-checker'
|
|
6
4
|
import { resolve } from 'path'
|
|
5
|
+
import { existsSync } from 'fs'
|
|
7
6
|
import { clientConfig } from './config/system/client.config'
|
|
8
|
-
import {
|
|
7
|
+
import { fluxstackVitePlugins } from './core/build/vite-plugins'
|
|
9
8
|
|
|
10
9
|
// Root directory (vite.config.ts is in project root)
|
|
11
10
|
const rootDir = import.meta.dirname
|
|
12
11
|
|
|
12
|
+
// When using bun-linked @fluxstack/live-* packages locally, point Vite at the
|
|
13
|
+
// TypeScript source instead of pre-built dist. This ensures a single React
|
|
14
|
+
// context (no dual-instance problem) and gives us HMR for the library code.
|
|
15
|
+
// In CI or when the sibling repo doesn't exist, resolve from node_modules.
|
|
16
|
+
const liveMonorepoRoot = resolve(rootDir, '../fluxstack-live/packages')
|
|
17
|
+
const hasLocalLiveMonorepo = existsSync(resolve(liveMonorepoRoot, 'core/src/index.ts'))
|
|
18
|
+
|
|
19
|
+
const liveAliases: Record<string, string> = hasLocalLiveMonorepo
|
|
20
|
+
? {
|
|
21
|
+
'@fluxstack/live-react': resolve(liveMonorepoRoot, 'react/src/index.ts'),
|
|
22
|
+
'@fluxstack/live-client': resolve(liveMonorepoRoot, 'client/src/index.ts'),
|
|
23
|
+
'@fluxstack/live': resolve(liveMonorepoRoot, 'core/src/index.ts'),
|
|
24
|
+
}
|
|
25
|
+
: {}
|
|
26
|
+
|
|
13
27
|
// https://vite.dev/config/
|
|
14
28
|
export default defineConfig({
|
|
15
29
|
plugins: [
|
|
30
|
+
// FluxStack internal plugins (live-strip, tsconfig-paths, type-checker)
|
|
31
|
+
...fluxstackVitePlugins(),
|
|
16
32
|
react(),
|
|
17
33
|
tailwindcss(),
|
|
18
|
-
|
|
19
|
-
projects: [resolve(rootDir, 'tsconfig.json')]
|
|
20
|
-
}),
|
|
21
|
-
// Only run type checker in development (saves ~5+ minutes in Docker builds)
|
|
22
|
-
helpers.isDevelopment() && checker({
|
|
23
|
-
typescript: true,
|
|
24
|
-
overlay: true
|
|
25
|
-
})
|
|
26
|
-
].filter(Boolean),
|
|
34
|
+
],
|
|
27
35
|
|
|
28
36
|
root: resolve(rootDir, 'app/client'),
|
|
29
37
|
|
|
30
38
|
// Aliases são lidos do tsconfig.json pelo plugin vite-tsconfig-paths
|
|
31
39
|
|
|
40
|
+
resolve: {
|
|
41
|
+
dedupe: ['react', 'react-dom', 'react/jsx-runtime'],
|
|
42
|
+
alias: liveAliases,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Exclude linked packages from dep optimization when aliased to source
|
|
46
|
+
optimizeDeps: {
|
|
47
|
+
exclude: hasLocalLiveMonorepo
|
|
48
|
+
? ['@fluxstack/live', '@fluxstack/live-client', '@fluxstack/live-react']
|
|
49
|
+
: [],
|
|
50
|
+
},
|
|
51
|
+
|
|
32
52
|
server: {
|
|
33
53
|
port: clientConfig.vite.port, // ✅ From config
|
|
34
54
|
host: clientConfig.vite.host, // ✅ From config
|
|
@@ -36,6 +56,14 @@ export default defineConfig({
|
|
|
36
56
|
open: clientConfig.vite.open, // ✅ From config
|
|
37
57
|
allowedHosts: clientConfig.vite.allowedHosts, // ✅ From config (VITE_ALLOWED_HOSTS)
|
|
38
58
|
|
|
59
|
+
// Allow Vite to serve files outside the client root (needed for monorepo aliases)
|
|
60
|
+
fs: {
|
|
61
|
+
allow: [
|
|
62
|
+
rootDir,
|
|
63
|
+
...(hasLocalLiveMonorepo ? [liveMonorepoRoot] : []),
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
|
|
39
67
|
hmr: {
|
|
40
68
|
protocol: 'ws',
|
|
41
69
|
host: clientConfig.vite.host,
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
-
import { Live } from '@/core/client'
|
|
3
|
-
import { LiveChat } from '@server/live/LiveChat'
|
|
4
|
-
|
|
5
|
-
export function ChatDemo() {
|
|
6
|
-
const [text, setText] = useState('')
|
|
7
|
-
const [user, setUser] = useState('')
|
|
8
|
-
const containerRef = useRef<HTMLDivElement | null>(null)
|
|
9
|
-
const wasNearBottomRef = useRef(true)
|
|
10
|
-
const defaultUser = useMemo(() => {
|
|
11
|
-
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
|
12
|
-
return `user-${crypto.randomUUID().slice(0, 6)}`
|
|
13
|
-
}
|
|
14
|
-
return `user-${Math.random().toString(36).slice(2, 8)}`
|
|
15
|
-
}, [])
|
|
16
|
-
|
|
17
|
-
const chat = Live.use(LiveChat, {
|
|
18
|
-
room: 'global-chat',
|
|
19
|
-
initialState: LiveChat.defaultState,
|
|
20
|
-
persistState: false
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const handleSend = async () => {
|
|
24
|
-
if (!text.trim()) return
|
|
25
|
-
const finalUser = user.trim() || defaultUser
|
|
26
|
-
await chat.sendMessage({ user: finalUser, text })
|
|
27
|
-
setText('')
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const el = containerRef.current
|
|
32
|
-
if (!el) return
|
|
33
|
-
|
|
34
|
-
if (wasNearBottomRef.current) {
|
|
35
|
-
el.scrollTop = el.scrollHeight
|
|
36
|
-
}
|
|
37
|
-
}, [chat.$state.messages.length])
|
|
38
|
-
|
|
39
|
-
const handleScroll = () => {
|
|
40
|
-
const el = containerRef.current
|
|
41
|
-
if (!el) return
|
|
42
|
-
const distance = el.scrollHeight - (el.scrollTop + el.clientHeight)
|
|
43
|
-
wasNearBottomRef.current = distance < 80
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<div className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl p-4 sm:p-6 md:p-8 w-full max-w-2xl mx-auto">
|
|
48
|
-
<h2 className="text-2xl font-bold text-white mb-2 text-center">Chat Compartilhado</h2>
|
|
49
|
-
<p className="text-gray-400 text-sm text-center mb-4">
|
|
50
|
-
Sala global em tempo real. Abra em várias abas para testar.
|
|
51
|
-
</p>
|
|
52
|
-
|
|
53
|
-
<div className="flex flex-wrap items-center gap-2 mb-4 text-xs">
|
|
54
|
-
<span className={`px-3 py-1 rounded-full ${chat.$connected ? 'bg-emerald-500/20 text-emerald-300' : 'bg-red-500/20 text-red-300'}`}>
|
|
55
|
-
{chat.$connected ? 'Conectado' : 'Desconectado'}
|
|
56
|
-
</span>
|
|
57
|
-
<span className="text-gray-400">Você: {user.trim() || defaultUser}</span>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<div className="mb-4">
|
|
61
|
-
<label className="block text-xs text-gray-400 mb-2">Seu nome</label>
|
|
62
|
-
<input
|
|
63
|
-
value={user}
|
|
64
|
-
onChange={(e) => setUser(e.target.value)}
|
|
65
|
-
placeholder={`Ex: ${defaultUser}`}
|
|
66
|
-
className="w-full px-4 py-2 rounded-lg bg-white/10 border border-white/20 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500/50"
|
|
67
|
-
/>
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
<div
|
|
71
|
-
ref={containerRef}
|
|
72
|
-
onScroll={handleScroll}
|
|
73
|
-
className="bg-black/40 border border-white/10 rounded-xl p-3 sm:p-5 h-72 sm:h-96 md:h-[28rem] overflow-auto space-y-3"
|
|
74
|
-
>
|
|
75
|
-
{chat.$state.messages.length === 0 && (
|
|
76
|
-
<div className="text-gray-500 text-sm text-center">Nenhuma mensagem ainda</div>
|
|
77
|
-
)}
|
|
78
|
-
{chat.$state.messages.map((m) => (
|
|
79
|
-
<div key={m.id} className="text-sm">
|
|
80
|
-
<span className="text-purple-300 font-semibold">{m.user}</span>
|
|
81
|
-
<span className="text-gray-500 text-xs ml-2">{new Date(m.timestamp).toLocaleTimeString()}</span>
|
|
82
|
-
<div className="text-gray-200">{m.text}</div>
|
|
83
|
-
</div>
|
|
84
|
-
))}
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
<div className="mt-4 flex gap-2">
|
|
88
|
-
<input
|
|
89
|
-
value={text}
|
|
90
|
-
onChange={(e) => setText(e.target.value)}
|
|
91
|
-
onKeyDown={(e) => {
|
|
92
|
-
if (e.key === 'Enter') void handleSend()
|
|
93
|
-
}}
|
|
94
|
-
placeholder="Digite uma mensagem..."
|
|
95
|
-
className="flex-1 px-4 py-2 rounded-lg bg-white/10 border border-white/20 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500/50"
|
|
96
|
-
/>
|
|
97
|
-
<button
|
|
98
|
-
onClick={handleSend}
|
|
99
|
-
disabled={!chat.$connected}
|
|
100
|
-
className="px-4 py-2 rounded-lg bg-purple-500/20 border border-purple-500/30 text-purple-200 hover:bg-purple-500/30 transition-all disabled:opacity-50"
|
|
101
|
-
>
|
|
102
|
-
Enviar
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
)
|
|
107
|
-
}
|