fluxy-bot 0.8.9 → 0.9.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/README.md +53 -84
- package/bin/cli.js +1717 -6
- package/components.json +18 -18
- package/package.json +98 -109
- package/postcss.config.js +3 -3
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/postinstall.js +47 -58
- package/shared/ai.ts +118 -202
- package/shared/config.ts +37 -39
- package/shared/logger.ts +6 -14
- package/shared/paths.ts +10 -16
- package/shared/relay.ts +89 -98
- package/supervisor/backend.ts +77 -89
- package/supervisor/chat/ARCHITECTURE.md +13 -14
- package/supervisor/chat/OnboardWizard.tsx +2194 -3419
- package/supervisor/chat/fluxy-main.tsx +495 -633
- package/supervisor/chat/fluxy.html +24 -36
- package/supervisor/chat/onboard-main.tsx +8 -8
- package/supervisor/chat/onboard.html +10 -13
- package/supervisor/chat/src/components/Chat/AudioBubble.tsx +94 -108
- package/supervisor/chat/src/components/Chat/ChatView.tsx +14 -36
- package/supervisor/chat/src/components/Chat/ImageLightbox.tsx +71 -89
- package/supervisor/chat/src/components/Chat/InputBar.tsx +498 -728
- package/supervisor/chat/src/components/Chat/MessageBubble.tsx +154 -246
- package/supervisor/chat/src/components/Chat/MessageList.tsx +114 -138
- package/supervisor/chat/src/components/Chat/TypingIndicator.tsx +38 -50
- package/supervisor/chat/src/components/LoginScreen.tsx +245 -296
- package/supervisor/chat/src/hooks/useChat.ts +210 -259
- package/supervisor/chat/src/hooks/useFluxyChat.ts +232 -322
- package/supervisor/chat/src/hooks/useSpeechRecognition.ts +126 -148
- package/supervisor/chat/src/lib/auth.ts +20 -23
- package/supervisor/chat/src/lib/ws-client.ts +113 -125
- package/supervisor/chat/src/styles/globals.css +76 -85
- package/supervisor/file-saver.ts +39 -52
- package/supervisor/fluxy-agent.ts +213 -292
- package/supervisor/index.ts +895 -1263
- package/supervisor/scheduler.ts +237 -299
- package/supervisor/tunnel.ts +113 -167
- package/supervisor/vite-dev.ts +51 -61
- package/supervisor/widget.js +135 -147
- package/supervisor/worker.ts +45 -49
- package/tsconfig.json +18 -24
- package/vite.config.ts +48 -55
- package/vite.fluxy.config.ts +34 -34
- package/worker/claude-auth.ts +202 -265
- package/worker/codex-auth.ts +144 -181
- package/worker/db.ts +63 -191
- package/worker/index.ts +601 -842
- package/workspace/MYSELF.md +1 -1
- package/workspace/PULSE.json +6 -6
- package/workspace/backend/index.ts +16 -19
- package/workspace/client/index.html +47 -60
- package/workspace/client/public/manifest.json +21 -21
- package/workspace/client/public/sw.js +29 -31
- package/workspace/client/src/App.tsx +91 -136
- package/workspace/client/src/components/Dashboard/DashboardPage.tsx +68 -72
- package/workspace/client/src/components/ErrorBoundary.tsx +11 -11
- package/workspace/client/src/components/Layout/DashboardLayout.tsx +35 -35
- package/workspace/client/src/components/Layout/Footer.tsx +12 -12
- package/workspace/client/src/components/Layout/MobileNav.tsx +23 -23
- package/workspace/client/src/components/Layout/Sidebar.tsx +81 -81
- package/workspace/client/src/components/ui/avatar.tsx +85 -85
- package/workspace/client/src/components/ui/badge.tsx +40 -41
- package/workspace/client/src/components/ui/button.tsx +56 -57
- package/workspace/client/src/components/ui/card.tsx +75 -75
- package/workspace/client/src/components/ui/dialog.tsx +114 -117
- package/workspace/client/src/components/ui/dropdown-menu.tsx +191 -199
- package/workspace/client/src/components/ui/input.tsx +17 -17
- package/workspace/client/src/components/ui/scroll-area.tsx +47 -47
- package/workspace/client/src/components/ui/select.tsx +145 -151
- package/workspace/client/src/components/ui/separator.tsx +21 -21
- package/workspace/client/src/components/ui/sheet.tsx +102 -102
- package/workspace/client/src/components/ui/skeleton.tsx +10 -10
- package/workspace/client/src/components/ui/switch.tsx +26 -26
- package/workspace/client/src/components/ui/tabs.tsx +69 -69
- package/workspace/client/src/components/ui/textarea.tsx +14 -14
- package/workspace/client/src/components/ui/tooltip.tsx +37 -37
- package/workspace/client/src/lib/utils.ts +1 -1
- package/workspace/client/src/main.tsx +3 -3
- package/workspace/client/src/styles/globals.css +76 -85
- package/workspace/skills/code-reviewer/.claude-plugin/plugin.json +3 -3
- package/workspace/skills/code-reviewer/skills/code-reviewer/SKILL.md +0 -6
- package/workspace/skills/daily-standup/.claude-plugin/plugin.json +3 -3
- package/workspace/skills/daily-standup/skills/daily-standup/SKILL.md +0 -7
- package/workspace/skills/workspace-helper/.claude-plugin/plugin.json +3 -3
- package/workspace/skills/workspace-helper/skills/workspace-helper/SKILL.md +0 -2
- package/bin/cli.backup.js +0 -2138
- package/cli/commands/daemon.ts +0 -42
- package/cli/commands/init.ts +0 -32
- package/cli/commands/start.ts +0 -113
- package/cli/commands/tunnel.temp.ts +0 -206
- package/cli/commands/tunnel.ts +0 -227
- package/cli/commands/update.ts +0 -163
- package/cli/core/base-adapter.ts +0 -156
- package/cli/core/cloudflared.ts +0 -113
- package/cli/core/config.ts +0 -73
- package/cli/core/os-detector.ts +0 -43
- package/cli/core/server.ts +0 -109
- package/cli/core/types.ts +0 -15
- package/cli/index.ts +0 -72
- package/cli/platforms/darwin.ts +0 -146
- package/cli/platforms/index.ts +0 -21
- package/cli/platforms/linux.ts +0 -156
- package/cli/platforms/win32.ts +0 -34
- package/cli/utils/ui.ts +0 -37
package/workspace/MYSELF.md
CHANGED
|
@@ -17,4 +17,4 @@ My human might be non-technical. I match their level.
|
|
|
17
17
|
|
|
18
18
|
_Everything else I need to know — routing, architecture, coding rules, memory system, personality — is in my system prompt. This file is just me._
|
|
19
19
|
|
|
20
|
-
_This file is mine to change. As I figure out who I am and how I work best — I update it._
|
|
20
|
+
_This file is mine to change. As I figure out who I am and how I work best — I update it._
|
package/workspace/PULSE.json
CHANGED
|
@@ -9,18 +9,15 @@ const WORKSPACE = path.resolve(import.meta.dirname, '..');
|
|
|
9
9
|
// Load workspace/.env manually (no dotenv dep needed)
|
|
10
10
|
const envPath = path.join(WORKSPACE, '.env');
|
|
11
11
|
if (fs.existsSync(envPath)) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.replace(/^["']|["']$/g, '');
|
|
22
|
-
if (!process.env[key]) process.env[key] = val;
|
|
23
|
-
}
|
|
12
|
+
for (const line of fs.readFileSync(envPath, 'utf-8').split('\n')) {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
15
|
+
const eq = trimmed.indexOf('=');
|
|
16
|
+
if (eq === -1) continue;
|
|
17
|
+
const key = trimmed.slice(0, eq).trim();
|
|
18
|
+
const val = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, '');
|
|
19
|
+
if (!process.env[key]) process.env[key] = val;
|
|
20
|
+
}
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
// Open SQLite database
|
|
@@ -32,24 +29,24 @@ app.use(express.json());
|
|
|
32
29
|
|
|
33
30
|
// Health check
|
|
34
31
|
app.get('/health', (_req, res) => {
|
|
35
|
-
|
|
32
|
+
res.json({ status: 'ok' });
|
|
36
33
|
});
|
|
37
34
|
|
|
38
35
|
// 404 catch-all
|
|
39
36
|
app.use((_req, res) => {
|
|
40
|
-
|
|
37
|
+
res.status(404).json({ error: 'Not found' });
|
|
41
38
|
});
|
|
42
39
|
|
|
43
40
|
const server = app.listen(PORT, () => {
|
|
44
|
-
|
|
41
|
+
console.log(`[backend] Listening on port ${PORT}`);
|
|
45
42
|
});
|
|
46
43
|
|
|
47
44
|
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
console.error(`[backend] Server error: ${err.message}`);
|
|
46
|
+
process.exit(1);
|
|
50
47
|
});
|
|
51
48
|
|
|
52
49
|
// Keep process alive — prevent exit on unhandled rejection
|
|
53
|
-
process.on('unhandledRejection', err => {
|
|
54
|
-
|
|
50
|
+
process.on('unhandledRejection', (err) => {
|
|
51
|
+
console.error('[backend] Unhandled rejection:', err);
|
|
55
52
|
});
|
|
@@ -1,62 +1,49 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
w.addEventListener('statechange', function () {
|
|
50
|
-
if (
|
|
51
|
-
w.state === 'installed' &&
|
|
52
|
-
navigator.serviceWorker.controller
|
|
53
|
-
)
|
|
54
|
-
w.postMessage({ type: 'SKIP_WAITING' });
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
</script>
|
|
60
|
-
<script src="/fluxy/widget.js"></script>
|
|
61
|
-
</body>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content" />
|
|
6
|
+
<meta name="theme-color" content="#212121" />
|
|
7
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
8
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
9
|
+
<meta name="apple-mobile-web-app-title" content="Fluxy" />
|
|
10
|
+
<link rel="apple-touch-icon" href="/fluxy-icon-192.png" />
|
|
11
|
+
<link rel="manifest" href="/manifest.json" />
|
|
12
|
+
<title>Fluxy</title>
|
|
13
|
+
</head>
|
|
14
|
+
<body class="bg-background text-foreground">
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
<script>
|
|
17
|
+
// Global error handler — catches errors outside React's Error Boundary
|
|
18
|
+
// (e.g., Vite compilation errors, module loading failures)
|
|
19
|
+
window.addEventListener('error', function (e) {
|
|
20
|
+
// Only show if root is empty (React didn't mount or crashed before mounting)
|
|
21
|
+
var root = document.getElementById('root');
|
|
22
|
+
if (root && root.children.length === 0) {
|
|
23
|
+
root.innerHTML =
|
|
24
|
+
'<div style="background:#222122;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100dvh;width:100vw;position:fixed;inset:0;z-index:50;font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:24px">' +
|
|
25
|
+
'<img src="/fluxy_frame1.png" style="height:120px;width:120px;border-radius:50%;object-fit:cover;margin-bottom:32px" alt="Fluxy" />' +
|
|
26
|
+
'<h1 style="font-size:20px;font-weight:600;margin-bottom:8px">Your app crashed</h1>' +
|
|
27
|
+
'<p style="font-size:14px;color:rgba(255,255,255,0.5);max-width:320px;line-height:1.5">Ask the agent to fix it using the chat.</p>' +
|
|
28
|
+
'</div>';
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
33
|
+
<script>
|
|
34
|
+
if('serviceWorker' in navigator){
|
|
35
|
+
navigator.serviceWorker.register('/sw.js').then(function(r){
|
|
36
|
+
r.update();
|
|
37
|
+
if(r.waiting){r.waiting.postMessage({type:'SKIP_WAITING'})}
|
|
38
|
+
r.addEventListener('updatefound',function(){
|
|
39
|
+
var w=r.installing;
|
|
40
|
+
if(w)w.addEventListener('statechange',function(){
|
|
41
|
+
if(w.state==='installed'&&navigator.serviceWorker.controller)w.postMessage({type:'SKIP_WAITING'});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
<script src="/fluxy/widget.js"></script>
|
|
48
|
+
</body>
|
|
62
49
|
</html>
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
"name": "Fluxy",
|
|
3
|
+
"short_name": "Fluxy",
|
|
4
|
+
"description": "Your AI assistant",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#212121",
|
|
8
|
+
"theme_color": "#212121",
|
|
9
|
+
"icons": [
|
|
10
|
+
{
|
|
11
|
+
"src": "/fluxy-icon-192.png",
|
|
12
|
+
"sizes": "192x192",
|
|
13
|
+
"type": "image/png",
|
|
14
|
+
"purpose": "any maskable"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"src": "/fluxy-icon-512.png",
|
|
18
|
+
"sizes": "512x512",
|
|
19
|
+
"type": "image/png",
|
|
20
|
+
"purpose": "any maskable"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
23
|
}
|
|
@@ -1,42 +1,40 @@
|
|
|
1
1
|
// Service worker — PWA installability + push notifications
|
|
2
2
|
self.addEventListener('install', () => self.skipWaiting());
|
|
3
|
-
self.addEventListener('activate', e => e.waitUntil(self.clients.claim()));
|
|
3
|
+
self.addEventListener('activate', (e) => e.waitUntil(self.clients.claim()));
|
|
4
4
|
self.addEventListener('fetch', () => {});
|
|
5
5
|
|
|
6
6
|
// Push notification
|
|
7
|
-
self.addEventListener('push', event => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
self.addEventListener('push', (event) => {
|
|
8
|
+
let data = { title: 'Fluxy', body: 'New message' };
|
|
9
|
+
try {
|
|
10
|
+
data = event.data.json();
|
|
11
|
+
} catch {}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
event.waitUntil(
|
|
14
|
+
self.registration.showNotification(data.title || 'Fluxy', {
|
|
15
|
+
body: data.body || '',
|
|
16
|
+
icon: '/fluxy-icon-192.png',
|
|
17
|
+
badge: '/fluxy-badge.png',
|
|
18
|
+
vibrate: [100, 50, 100],
|
|
19
|
+
tag: data.tag || 'fluxy-default',
|
|
20
|
+
data: { url: data.url || '/' },
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
// Notification click — focus or open app
|
|
26
|
-
self.addEventListener('notificationclick', event => {
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
self.addEventListener('notificationclick', (event) => {
|
|
27
|
+
event.notification.close();
|
|
28
|
+
const url = event.notification.data?.url || '/';
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
41
|
-
);
|
|
30
|
+
event.waitUntil(
|
|
31
|
+
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clients) => {
|
|
32
|
+
for (const client of clients) {
|
|
33
|
+
if (client.url.includes('/fluxy') && 'focus' in client) {
|
|
34
|
+
return client.focus();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return self.clients.openWindow(url);
|
|
38
|
+
})
|
|
39
|
+
);
|
|
42
40
|
});
|
|
@@ -4,148 +4,103 @@ import DashboardLayout from './components/Layout/DashboardLayout';
|
|
|
4
4
|
import DashboardPage from './components/Dashboard/DashboardPage';
|
|
5
5
|
|
|
6
6
|
function DashboardError() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
padding: '24px'
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
<video
|
|
27
|
-
src="/fluxy_say_hi.webm"
|
|
28
|
-
autoPlay
|
|
29
|
-
loop
|
|
30
|
-
muted
|
|
31
|
-
playsInline
|
|
32
|
-
style={{
|
|
33
|
-
height: 120,
|
|
34
|
-
width: 120,
|
|
35
|
-
borderRadius: '50%',
|
|
36
|
-
objectFit: 'cover',
|
|
37
|
-
marginBottom: 32
|
|
38
|
-
}}
|
|
39
|
-
/>
|
|
40
|
-
<h1 style={{ fontSize: 20, fontWeight: 600, marginBottom: 8 }}>
|
|
41
|
-
Your app crashed
|
|
42
|
-
</h1>
|
|
43
|
-
<p
|
|
44
|
-
style={{
|
|
45
|
-
fontSize: 14,
|
|
46
|
-
color: 'rgba(255,255,255,0.5)',
|
|
47
|
-
maxWidth: 320,
|
|
48
|
-
lineHeight: 1.5
|
|
49
|
-
}}
|
|
50
|
-
>
|
|
51
|
-
Ask the agent to fix it using the chat.
|
|
52
|
-
</p>
|
|
53
|
-
</div>
|
|
54
|
-
);
|
|
7
|
+
return (
|
|
8
|
+
<div style={{ background: '#222122', color: '#fff', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100dvh', width: '100vw', position: 'fixed', inset: 0, zIndex: 50, fontFamily: 'system-ui, -apple-system, sans-serif', textAlign: 'center', padding: '24px' }}>
|
|
9
|
+
<video
|
|
10
|
+
src="/fluxy_say_hi.webm"
|
|
11
|
+
autoPlay
|
|
12
|
+
loop
|
|
13
|
+
muted
|
|
14
|
+
playsInline
|
|
15
|
+
style={{ height: 120, width: 120, borderRadius: '50%', objectFit: 'cover', marginBottom: 32 }}
|
|
16
|
+
/>
|
|
17
|
+
<h1 style={{ fontSize: 20, fontWeight: 600, marginBottom: 8 }}>Your app crashed</h1>
|
|
18
|
+
<p style={{ fontSize: 14, color: 'rgba(255,255,255,0.5)', maxWidth: 320, lineHeight: 1.5 }}>
|
|
19
|
+
Ask the agent to fix it using the chat.
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
55
23
|
}
|
|
56
24
|
|
|
57
25
|
export default function App() {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
>('idle');
|
|
62
|
-
const [buildError, setBuildError] = useState('');
|
|
26
|
+
const [showOnboard, setShowOnboard] = useState(false);
|
|
27
|
+
const [rebuildState, setRebuildState] = useState<'idle' | 'rebuilding' | 'error'>('idle');
|
|
28
|
+
const [buildError, setBuildError] = useState('');
|
|
63
29
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
fetch('/api/settings')
|
|
32
|
+
.then((r) => r.json())
|
|
33
|
+
.then((s) => {
|
|
34
|
+
if (s.onboard_complete !== 'true') setShowOnboard(true);
|
|
35
|
+
})
|
|
36
|
+
.catch(() => {});
|
|
37
|
+
}, []);
|
|
72
38
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
};
|
|
108
|
-
}, []);
|
|
39
|
+
// Listen for rebuild events from Fluxy iframe via postMessage
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
let safetyTimer: ReturnType<typeof setTimeout>;
|
|
42
|
+
const handler = (e: MessageEvent) => {
|
|
43
|
+
if (e.data?.type === 'fluxy:rebuilding') {
|
|
44
|
+
setRebuildState('rebuilding');
|
|
45
|
+
setBuildError('');
|
|
46
|
+
// Safety: auto-reload after 60s in case app:rebuilt message is lost
|
|
47
|
+
clearTimeout(safetyTimer);
|
|
48
|
+
safetyTimer = setTimeout(() => location.reload(), 60_000);
|
|
49
|
+
} else if (e.data?.type === 'fluxy:rebuilt') {
|
|
50
|
+
clearTimeout(safetyTimer);
|
|
51
|
+
setRebuildState('idle');
|
|
52
|
+
location.reload();
|
|
53
|
+
} else if (e.data?.type === 'fluxy:build-error') {
|
|
54
|
+
clearTimeout(safetyTimer);
|
|
55
|
+
setRebuildState('error');
|
|
56
|
+
setBuildError(e.data.error || 'Build failed');
|
|
57
|
+
setTimeout(() => setRebuildState('idle'), 5000);
|
|
58
|
+
} else if (e.data?.type === 'fluxy:onboard-complete') {
|
|
59
|
+
setShowOnboard(false);
|
|
60
|
+
} else if (e.data?.type === 'fluxy:hmr-update') {
|
|
61
|
+
// Vite HMR handles hot updates natively — no manual reload needed.
|
|
62
|
+
// Manual location.reload() here was causing unnecessary full-page refreshes
|
|
63
|
+
// that killed the chat iframe's WebSocket connection.
|
|
64
|
+
console.log('[dashboard] File changed — Vite HMR will handle it');
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
window.addEventListener('message', handler);
|
|
68
|
+
return () => {
|
|
69
|
+
window.removeEventListener('message', handler);
|
|
70
|
+
clearTimeout(safetyTimer);
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
109
73
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<ErrorBoundary fallback={<DashboardError />}>
|
|
77
|
+
<DashboardLayout>
|
|
78
|
+
<DashboardPage />
|
|
79
|
+
</DashboardLayout>
|
|
80
|
+
</ErrorBoundary>
|
|
117
81
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
width: '100vw',
|
|
125
|
-
height: '100dvh',
|
|
126
|
-
border: 'none',
|
|
127
|
-
zIndex: 200
|
|
128
|
-
}}
|
|
129
|
-
/>
|
|
130
|
-
)}
|
|
82
|
+
{showOnboard && (
|
|
83
|
+
<iframe
|
|
84
|
+
src="/fluxy/onboard.html"
|
|
85
|
+
style={{ position: 'fixed', inset: 0, width: '100vw', height: '100dvh', border: 'none', zIndex: 200 }}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
131
88
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
</>
|
|
150
|
-
);
|
|
89
|
+
{rebuildState !== 'idle' && (
|
|
90
|
+
<div className="fixed inset-0 z-[49] flex flex-col items-center justify-center bg-background/90">
|
|
91
|
+
<video
|
|
92
|
+
src="/fluxy_tilts.webm"
|
|
93
|
+
autoPlay
|
|
94
|
+
loop
|
|
95
|
+
muted
|
|
96
|
+
playsInline
|
|
97
|
+
className="h-24 w-24 rounded-full object-cover"
|
|
98
|
+
/>
|
|
99
|
+
<p className="mt-4 text-sm text-muted-foreground">
|
|
100
|
+
{rebuildState === 'rebuilding' ? 'Rebuilding app...' : buildError}
|
|
101
|
+
</p>
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
</>
|
|
105
|
+
);
|
|
151
106
|
}
|