upfynai-code 3.0.0 → 3.0.2
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/client/dist/assets/{AppContent-CapWOZnI.js → AppContent-Bvg0CPCO.js} +31 -31
- package/client/dist/assets/{CanvasFullScreen-WOseUyrD.js → CanvasFullScreen-BdiJ35aq.js} +1 -1
- package/client/dist/assets/{CanvasWorkspace-DksIgrfl.js → CanvasWorkspace-Bk9R9_e0.js} +1 -1
- package/client/dist/assets/DashboardPanel-CblJfTGi.js +1 -0
- package/client/dist/assets/{FileTree-CbJF7m5P.js → FileTree-BDUnBheV.js} +1 -1
- package/client/dist/assets/{GitPanel-avzqm9Zj.js → GitPanel-RtyZUIWS.js} +1 -1
- package/client/dist/assets/{LoginModal-Bk9mxZsi.js → LoginModal-BWep8a6g.js} +3 -3
- package/client/dist/assets/Onboarding-Drnlt75a.js +1 -0
- package/client/dist/assets/{SetupForm-DuIDpVGw.js → SetupForm-CtCKitZG.js} +1 -1
- package/client/dist/assets/{WorkflowsPanel-BOaVEpdu.js → WorkflowsPanel-B2mIXDvD.js} +1 -1
- package/client/dist/assets/{index-DtIQCXd6.js → index-C5ptjuTl.js} +29 -29
- package/client/dist/index.html +1 -1
- package/package.json +2 -2
- package/server/cli-ui.js +15 -2
- package/server/cli.js +150 -1
- package/server/database/db.js +53 -3
- package/server/index.js +8 -1
- package/client/dist/assets/DashboardPanel-DmHbSyTP.js +0 -1
- package/client/dist/assets/Onboarding-CaiBrbYB.js +0 -1
package/client/dist/index.html
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
40% { transform: translateY(-8px); }
|
|
63
63
|
}
|
|
64
64
|
</style>
|
|
65
|
-
<script type="module" crossorigin src="/assets/index-
|
|
65
|
+
<script type="module" crossorigin src="/assets/index-C5ptjuTl.js"></script>
|
|
66
66
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-96lCPsRK.js">
|
|
67
67
|
<link rel="modulepreload" crossorigin href="/assets/vendor-markdown-CimbIo6Y.js">
|
|
68
68
|
<link rel="modulepreload" crossorigin href="/assets/vendor-syntax-DuHI9Ok6.js">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "upfynai-code",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Visual AI coding interface for Claude Code, Cursor & Codex. Canvas whiteboard, multi-agent chat, terminal, git, voice assistant. Self-host locally or connect to cli.upfyn.com for remote access.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"@anthropic-ai/claude-agent-sdk": "^0.1.71",
|
|
57
57
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
58
58
|
"@iarna/toml": "^2.2.5",
|
|
59
|
-
"@libsql/client": "^0.14.0",
|
|
60
59
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
61
60
|
"@openai/codex-sdk": "^0.101.0",
|
|
62
61
|
"bcryptjs": "^3.0.3",
|
|
@@ -76,6 +75,7 @@
|
|
|
76
75
|
"zod": "^3.25.76"
|
|
77
76
|
},
|
|
78
77
|
"optionalDependencies": {
|
|
78
|
+
"libsql": "^0.5.22",
|
|
79
79
|
"node-pty": "^1.1.0-beta34"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
package/server/cli-ui.js
CHANGED
|
@@ -520,13 +520,16 @@ function showStyledHelp(version) {
|
|
|
520
520
|
lines.push(` ${c.bBright('Commands:')}`);
|
|
521
521
|
const cmds = [
|
|
522
522
|
['(default)', 'Launch Claude Code with Upfyn branding'],
|
|
523
|
-
['start', 'Start the local server
|
|
523
|
+
['start', 'Start the local server (web UI)'],
|
|
524
|
+
['stop', 'Stop a running server'],
|
|
524
525
|
['connect', 'Connect local machine to hosted server'],
|
|
525
526
|
['config', 'View/set configuration (API key, etc.)'],
|
|
526
527
|
['status', 'Show configuration and data locations'],
|
|
527
528
|
['install-commands', 'Install /upfynai-* slash commands'],
|
|
528
529
|
['uninstall-commands', 'Remove /upfynai-* slash commands'],
|
|
529
530
|
['update', 'Update to the latest version'],
|
|
531
|
+
['reset', 'Clear local database (fresh start)'],
|
|
532
|
+
['uninstall', 'Remove all data, config, and slash commands'],
|
|
530
533
|
['help', 'Show this help information'],
|
|
531
534
|
['version', 'Show version information'],
|
|
532
535
|
];
|
|
@@ -607,6 +610,14 @@ function showStyledStatus(info) {
|
|
|
607
610
|
}
|
|
608
611
|
lines.push('');
|
|
609
612
|
|
|
613
|
+
// Server status
|
|
614
|
+
if (info.serverRunning) {
|
|
615
|
+
lines.push(` ${c.bright('Server')} ${c.green('Running')} ${c.dim(`(PID ${info.serverRunning}, port ${info.port})`)}`);
|
|
616
|
+
} else {
|
|
617
|
+
lines.push(` ${c.bright('Server')} ${c.dim('Stopped')}`);
|
|
618
|
+
}
|
|
619
|
+
lines.push('');
|
|
620
|
+
|
|
610
621
|
// Configuration
|
|
611
622
|
lines.push(` ${c.bright('Config')}`);
|
|
612
623
|
lines.push(` ${c.gray('PORT')} ${c.cyan(info.port)} ${info.portDefault ? c.dim('(default)') : ''}`);
|
|
@@ -621,9 +632,11 @@ function showStyledStatus(info) {
|
|
|
621
632
|
lines.push(` ${c.dark('='.repeat(50))}`);
|
|
622
633
|
lines.push('');
|
|
623
634
|
lines.push(` ${c.bCyan('Tips:')}`);
|
|
624
|
-
lines.push(` ${c.dim('>')} Use ${c.bright('uc')} to
|
|
635
|
+
lines.push(` ${c.dim('>')} Use ${c.bright('uc start')} to start the local web UI`);
|
|
636
|
+
lines.push(` ${c.dim('>')} Use ${c.bright('uc stop')} to stop the server`);
|
|
625
637
|
lines.push(` ${c.dim('>')} Use ${c.bright('uc connect --key upfyn_xxx')} to bridge to web UI`);
|
|
626
638
|
lines.push(` ${c.dim('>')} Use ${c.bright('uc config --api-key sk-ant-xxx')} to save your API key`);
|
|
639
|
+
lines.push(` ${c.dim('>')} Use ${c.bright('uc help')} for all commands`);
|
|
627
640
|
lines.push('');
|
|
628
641
|
|
|
629
642
|
process.stdout.write(lines.join('\n') + '\n');
|
package/server/cli.js
CHANGED
|
@@ -7,10 +7,14 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Commands:
|
|
9
9
|
* (no args) - Launch Claude Code with Upfyn branding (default)
|
|
10
|
-
* start - Start the server
|
|
10
|
+
* start - Start the local server (web UI)
|
|
11
|
+
* stop - Stop a running server
|
|
11
12
|
* connect - Connect to hosted server (relay bridge)
|
|
12
13
|
* config - View/set configuration (API key, server, etc.)
|
|
13
14
|
* status - Show configuration and data locations
|
|
15
|
+
* update - Update to the latest version
|
|
16
|
+
* reset - Clear local database
|
|
17
|
+
* uninstall - Remove all data, config, and slash commands
|
|
14
18
|
* help - Show help information
|
|
15
19
|
* version - Show version information
|
|
16
20
|
*/
|
|
@@ -45,6 +49,7 @@ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
|
45
49
|
|
|
46
50
|
const CONFIG_DIR = path.join(os.homedir(), '.upfynai');
|
|
47
51
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
52
|
+
const PID_FILE = path.join(CONFIG_DIR, 'server.pid');
|
|
48
53
|
|
|
49
54
|
/** Mask internal Railway URL for display */
|
|
50
55
|
function displayServerUrl(url) {
|
|
@@ -122,6 +127,20 @@ function findClaudeBinary() {
|
|
|
122
127
|
return null;
|
|
123
128
|
}
|
|
124
129
|
|
|
130
|
+
// Check if a server is running via PID file
|
|
131
|
+
function isServerRunning() {
|
|
132
|
+
if (!fs.existsSync(PID_FILE)) return false;
|
|
133
|
+
try {
|
|
134
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
135
|
+
process.kill(pid, 0); // signal 0 = check if alive
|
|
136
|
+
return pid;
|
|
137
|
+
} catch {
|
|
138
|
+
// Process not running, clean up stale PID file
|
|
139
|
+
try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
125
144
|
// --- Show status command ---
|
|
126
145
|
function showStatus() {
|
|
127
146
|
const config = loadConfig();
|
|
@@ -134,6 +153,8 @@ function showStatus() {
|
|
|
134
153
|
dbSize = (stats.size / 1024).toFixed(2) + ' KB';
|
|
135
154
|
}
|
|
136
155
|
|
|
156
|
+
const serverPid = isServerRunning();
|
|
157
|
+
|
|
137
158
|
showStyledStatus({
|
|
138
159
|
version: packageJson.version,
|
|
139
160
|
installDir,
|
|
@@ -144,6 +165,7 @@ function showStatus() {
|
|
|
144
165
|
portDefault: !process.env.PORT,
|
|
145
166
|
claudeCli: findClaudeBinary() || null,
|
|
146
167
|
apiKey: config.anthropicApiKey || null,
|
|
168
|
+
serverRunning: serverPid,
|
|
147
169
|
});
|
|
148
170
|
}
|
|
149
171
|
|
|
@@ -444,6 +466,118 @@ function startBackgroundRelay(config) {
|
|
|
444
466
|
});
|
|
445
467
|
}
|
|
446
468
|
|
|
469
|
+
// Write PID file so `uc stop` can find and kill the server
|
|
470
|
+
function writePidFile() {
|
|
471
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
472
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
473
|
+
}
|
|
474
|
+
fs.writeFileSync(PID_FILE, String(process.pid));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Remove PID file on exit
|
|
478
|
+
function cleanupPidFile() {
|
|
479
|
+
try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Stop a running server
|
|
483
|
+
function stopServer() {
|
|
484
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
485
|
+
console.log(`\n ${c.yellow('!')} No running server found.`);
|
|
486
|
+
console.log(` ${c.dim('Start one with:')} ${c.bright('uc start')}\n`);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
492
|
+
process.kill(pid, 'SIGTERM');
|
|
493
|
+
fs.unlinkSync(PID_FILE);
|
|
494
|
+
console.log(`\n ${c.green('OK')} Server stopped (PID ${pid})\n`);
|
|
495
|
+
} catch (e) {
|
|
496
|
+
// Process might already be dead
|
|
497
|
+
try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
|
|
498
|
+
if (e.code === 'ESRCH') {
|
|
499
|
+
console.log(`\n ${c.yellow('!')} Server was not running (stale PID file removed)\n`);
|
|
500
|
+
} else {
|
|
501
|
+
console.log(`\n ${c.red('FAIL')} Could not stop server: ${e.message}\n`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Uninstall: remove config, database, slash commands
|
|
507
|
+
function uninstallApp() {
|
|
508
|
+
console.log(`\n ${c.bBright('Upfyn-Code — Uninstall')}`);
|
|
509
|
+
console.log(` ${c.dark('='.repeat(40))}\n`);
|
|
510
|
+
|
|
511
|
+
// 1. Stop any running server
|
|
512
|
+
if (fs.existsSync(PID_FILE)) {
|
|
513
|
+
try {
|
|
514
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
515
|
+
process.kill(pid, 'SIGTERM');
|
|
516
|
+
console.log(` ${c.green('OK')} Stopped running server (PID ${pid})`);
|
|
517
|
+
} catch { /* ignore */ }
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// 2. Remove slash commands
|
|
521
|
+
const commandsDest = path.join(os.homedir(), '.claude', 'commands');
|
|
522
|
+
const cmdsRemoved = [];
|
|
523
|
+
if (fs.existsSync(commandsDest)) {
|
|
524
|
+
const files = fs.readdirSync(commandsDest).filter(f => f.startsWith('upfynai'));
|
|
525
|
+
for (const file of files) {
|
|
526
|
+
fs.unlinkSync(path.join(commandsDest, file));
|
|
527
|
+
cmdsRemoved.push(file);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (cmdsRemoved.length > 0) {
|
|
531
|
+
console.log(` ${c.green('OK')} Removed ${cmdsRemoved.length} slash commands`);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// 3. Remove config directory (~/.upfynai/)
|
|
535
|
+
if (fs.existsSync(CONFIG_DIR)) {
|
|
536
|
+
fs.rmSync(CONFIG_DIR, { recursive: true, force: true });
|
|
537
|
+
console.log(` ${c.green('OK')} Removed config directory: ${c.dim(CONFIG_DIR)}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// 4. Remove local database (in install dir)
|
|
541
|
+
const dbPath = path.join(__dirname, 'database', 'auth.db');
|
|
542
|
+
const dbFiles = [dbPath, dbPath + '-wal', dbPath + '-shm'];
|
|
543
|
+
for (const f of dbFiles) {
|
|
544
|
+
if (fs.existsSync(f)) {
|
|
545
|
+
fs.unlinkSync(f);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (fs.existsSync(dbPath + '-wal') === false) {
|
|
549
|
+
console.log(` ${c.green('OK')} Removed local database`);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
console.log('');
|
|
553
|
+
console.log(` ${c.bBright('Almost done!')} Run this to finish:`);
|
|
554
|
+
console.log(` ${c.bright('npm uninstall -g upfynai-code')}`);
|
|
555
|
+
console.log('');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Reset: clear database only
|
|
559
|
+
function resetApp() {
|
|
560
|
+
console.log(`\n ${c.bBright('Upfyn-Code — Reset')}`);
|
|
561
|
+
console.log(` ${c.dark('='.repeat(40))}\n`);
|
|
562
|
+
|
|
563
|
+
const dbPath = getDatabasePath();
|
|
564
|
+
const dbFiles = [dbPath, dbPath + '-wal', dbPath + '-shm'];
|
|
565
|
+
let removed = false;
|
|
566
|
+
for (const f of dbFiles) {
|
|
567
|
+
if (fs.existsSync(f)) {
|
|
568
|
+
fs.unlinkSync(f);
|
|
569
|
+
removed = true;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (removed) {
|
|
574
|
+
console.log(` ${c.green('OK')} Database cleared: ${c.dim(dbPath)}`);
|
|
575
|
+
console.log(` ${c.dim('A fresh database will be created on next server start.')}\n`);
|
|
576
|
+
} else {
|
|
577
|
+
console.log(` ${c.yellow('!')} No database found at ${c.dim(dbPath)}\n`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
447
581
|
// Start the server (self-hosted local mode)
|
|
448
582
|
async function startServer() {
|
|
449
583
|
// Check for updates silently on startup
|
|
@@ -456,6 +590,12 @@ async function startServer() {
|
|
|
456
590
|
process.env.VITE_IS_PLATFORM = 'true';
|
|
457
591
|
}
|
|
458
592
|
|
|
593
|
+
// Write PID file so `uc stop` can kill this process
|
|
594
|
+
writePidFile();
|
|
595
|
+
process.on('exit', cleanupPidFile);
|
|
596
|
+
process.on('SIGINT', () => { cleanupPidFile(); process.exit(0); });
|
|
597
|
+
process.on('SIGTERM', () => { cleanupPidFile(); process.exit(0); });
|
|
598
|
+
|
|
459
599
|
// Show server banner
|
|
460
600
|
showServerBanner(port, packageJson.version);
|
|
461
601
|
|
|
@@ -580,6 +720,15 @@ async function main() {
|
|
|
580
720
|
case 'uninstall-commands':
|
|
581
721
|
await uninstallCommands();
|
|
582
722
|
break;
|
|
723
|
+
case 'stop':
|
|
724
|
+
stopServer();
|
|
725
|
+
break;
|
|
726
|
+
case 'uninstall':
|
|
727
|
+
uninstallApp();
|
|
728
|
+
break;
|
|
729
|
+
case 'reset':
|
|
730
|
+
resetApp();
|
|
731
|
+
break;
|
|
583
732
|
case 'connect': {
|
|
584
733
|
const { connectToServer } = await import('./relay-client.js');
|
|
585
734
|
await connectToServer({
|
package/server/database/db.js
CHANGED
|
@@ -1,9 +1,59 @@
|
|
|
1
|
+
// ─── Database Client Resolution ──────────────────────────────────────────────
|
|
2
|
+
// Cloud (Railway): uses @libsql/client for Turso remote databases
|
|
3
|
+
// Local (npm install): uses libsql (native SQLite binding) with async adapter
|
|
4
|
+
// This avoids shipping deprecated transitive deps (node-domexception) to npm users
|
|
5
|
+
|
|
1
6
|
let createClient;
|
|
7
|
+
|
|
8
|
+
// 1. Try @libsql/client (cloud — installed on Railway via nixpacks)
|
|
2
9
|
try {
|
|
3
10
|
createClient = (await import('@libsql/client')).createClient;
|
|
4
|
-
} catch
|
|
5
|
-
|
|
11
|
+
} catch {
|
|
12
|
+
// 2. Fallback: use libsql native binding with async adapter (local installs)
|
|
13
|
+
try {
|
|
14
|
+
const DatabaseMod = await import('libsql');
|
|
15
|
+
const Database = DatabaseMod.default || DatabaseMod;
|
|
16
|
+
createClient = function createLocalClient({ url }) {
|
|
17
|
+
const dbPath = url.replace(/^file:/, '');
|
|
18
|
+
const sqliteDb = new Database(dbPath);
|
|
19
|
+
sqliteDb.pragma('journal_mode = WAL');
|
|
20
|
+
return {
|
|
21
|
+
execute: async (query) => {
|
|
22
|
+
const sql = typeof query === 'string' ? query : query.sql;
|
|
23
|
+
const args = typeof query === 'string' ? [] : (query.args || []);
|
|
24
|
+
const stmt = sqliteDb.prepare(sql);
|
|
25
|
+
if (sql.trim().match(/^(SELECT|PRAGMA|EXPLAIN)/i)) {
|
|
26
|
+
const rows = args.length ? stmt.all(...args) : stmt.all();
|
|
27
|
+
return { rows, columns: rows.length > 0 ? Object.keys(rows[0]) : [] };
|
|
28
|
+
}
|
|
29
|
+
const info = args.length ? stmt.run(...args) : stmt.run();
|
|
30
|
+
return { rows: [], columns: [], rowsAffected: info.changes, lastInsertRowid: info.lastInsertRowid };
|
|
31
|
+
},
|
|
32
|
+
batch: async (queries) => {
|
|
33
|
+
const results = [];
|
|
34
|
+
for (const q of queries) {
|
|
35
|
+
// Re-use the adapter's own execute method
|
|
36
|
+
const sql = typeof q === 'string' ? q : q.sql;
|
|
37
|
+
const args = typeof q === 'string' ? [] : (q.args || []);
|
|
38
|
+
const stmt = sqliteDb.prepare(sql);
|
|
39
|
+
if (sql.trim().match(/^(SELECT|PRAGMA|EXPLAIN)/i)) {
|
|
40
|
+
const rows = args.length ? stmt.all(...args) : stmt.all();
|
|
41
|
+
results.push({ rows, columns: rows.length > 0 ? Object.keys(rows[0]) : [] });
|
|
42
|
+
} else {
|
|
43
|
+
const info = args.length ? stmt.run(...args) : stmt.run();
|
|
44
|
+
results.push({ rows: [], columns: [], rowsAffected: info.changes, lastInsertRowid: info.lastInsertRowid });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return results;
|
|
48
|
+
},
|
|
49
|
+
close: () => sqliteDb.close(),
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
} catch {
|
|
53
|
+
// Neither available
|
|
54
|
+
}
|
|
6
55
|
}
|
|
56
|
+
|
|
7
57
|
import path from 'path';
|
|
8
58
|
import fs from 'fs';
|
|
9
59
|
import crypto from 'crypto';
|
|
@@ -50,7 +100,7 @@ function resolveDbConfig() {
|
|
|
50
100
|
function getDb() {
|
|
51
101
|
if (!_db) {
|
|
52
102
|
if (!createClient) {
|
|
53
|
-
throw new Error('[DB]
|
|
103
|
+
throw new Error('[DB] No database driver available. Install libsql or @libsql/client.');
|
|
54
104
|
}
|
|
55
105
|
resolveDbConfig();
|
|
56
106
|
_db = createClient({ url: _dbUrl, ...(_dbAuthToken ? { authToken: _dbAuthToken } : {}) });
|
package/server/index.js
CHANGED
|
@@ -3646,7 +3646,14 @@ app.get('*', (req, res) => {
|
|
|
3646
3646
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
3647
3647
|
res.setHeader('Pragma', 'no-cache');
|
|
3648
3648
|
res.setHeader('Expires', '0');
|
|
3649
|
-
|
|
3649
|
+
|
|
3650
|
+
// Inject runtime config so the frontend knows the server mode
|
|
3651
|
+
// (VITE_IS_PLATFORM is a build-time var that may not match the runtime)
|
|
3652
|
+
let html = fs.readFileSync(indexPath, 'utf8');
|
|
3653
|
+
const runtimeConfig = JSON.stringify({ isPlatform: IS_PLATFORM, isLocal: IS_LOCAL });
|
|
3654
|
+
html = html.replace('</head>', `<script>window.__UPFYN_CONFIG__=${runtimeConfig}</script>\n</head>`);
|
|
3655
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
3656
|
+
res.send(html);
|
|
3650
3657
|
} else {
|
|
3651
3658
|
// In development, redirect to Vite dev server only if dist doesn't exist
|
|
3652
3659
|
res.redirect(`http://localhost:${process.env.VITE_PORT || 5173}`);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as a,j as e}from"./vendor-react-96lCPsRK.js";import{d as S,k as v}from"./index-DtIQCXd6.js";import{u as I,a as R}from"./AppContent-CapWOZnI.js";import{ab as M,R as T,M as A,Z as w,bi as L,t as $,at as D,aB as E,v as B,bj as O}from"./vendor-icons-BaD0x9SL.js";import"./vendor-syntax-DuHI9Ok6.js";import"./vendor-markdown-CimbIo6Y.js";import"./vendor-i18n-DCFGyhQR.js";import"./LoginModal-Bk9mxZsi.js";import"./vendor-xterm-CZq1hqo1.js";import"./vendor-canvas-D39yWul6.js";import"./vendor-mermaid-CH7SGc99.js";import"./vendor-codemirror-CbtmxxaB.js";function U({onRefresh:s,threshold:t=80,maxPull:l=120,enabled:r=!0}){const i=a.useRef(0),[n,d]=a.useState(0),[c,h]=a.useState(!1),u=a.useRef(!1),f=a.useCallback(o=>{!r||c||(i.current=o.touches[0].clientY,u.current=!1)},[r,c]),x=a.useCallback(o=>{if(!r||c||o.currentTarget.scrollTop>0)return;const g=o.touches[0].clientY-i.current;g>0&&(u.current=!0,d(Math.min(g*.5,l)))},[r,c,l]),m=a.useCallback(async()=>{if(u.current){if(n>=t){h(!0);try{await s()}finally{h(!1)}}d(0),u.current=!1}},[n,t,s]);return{pullDistance:n,isRefreshing:c,handlers:{onTouchStart:f,onTouchMove:x,onTouchEnd:m}}}function y({className:s}){return e.jsx("div",{className:`animate-pulse rounded-md bg-muted/60 ${s||""}`})}function K({selectedProject:s}){const[t,l]=a.useState(null),[r,i]=a.useState(null),[n,d]=a.useState(!0),[c,h]=a.useState(null),{canPrompt:u,promptInstall:f}=I(),{isMobile:x}=R({trackPWA:!1}),m=a.useCallback(async()=>{d(!0),h(null);try{const j=await S("/api/dashboard/stats");if(j.ok){const C=await j.json();l(C)}}catch{}d(!1)},[]),{pullDistance:o,isRefreshing:b,handlers:g}=U({onRefresh:m,enabled:x});return a.useEffect(()=>{m()},[m]),e.jsxs("div",{className:"h-full overflow-y-auto p-4 sm:p-6 space-y-6","data-pull-refresh":!0,...x?g:{},style:o>0?{transform:`translateY(${o*.3}px)`}:void 0,children:[x&&(o>0||b)&&e.jsx("div",{className:"flex items-center justify-center py-2 -mt-2",children:b?e.jsx("div",{className:"w-5 h-5 border-2 border-primary/30 border-t-primary rounded-full animate-spin"}):e.jsx("svg",{className:"w-5 h-5 text-muted-foreground transition-transform",style:{transform:`rotate(${Math.min(o/80*180,180)}deg)`},fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 14l-7 7m0 0l-7-7m7 7V3"})})}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsxs("h2",{className:"text-lg font-semibold text-foreground flex items-center gap-2",children:[e.jsx(M,{className:"w-5 h-5 text-blue-500"}),"Dashboard"]}),e.jsx("p",{className:"text-sm text-muted-foreground mt-0.5",children:s?`Project: ${s.displayName||s.name}`:"Overview"})]}),e.jsx("button",{onClick:m,disabled:n,className:"p-2 rounded-lg hover:bg-muted/60 text-muted-foreground hover:text-foreground transition-colors",title:"Refresh stats",children:e.jsx(T,{className:`w-4 h-4 ${n?"animate-spin":""}`})})]}),e.jsxs("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-3",children:[e.jsx(k,{icon:A,label:"Sessions",value:(t==null?void 0:t.total)??0,subtext:t!=null&&t.today?`${t.today} today`:void 0,color:"blue",loading:n}),e.jsx(k,{icon:w,label:"AI Providers",value:t!=null&&t.providers?Object.keys(t.providers).length:0,subtext:t!=null&&t.providers?Object.keys(t.providers).join(", "):void 0,color:"purple",loading:n}),v]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium text-muted-foreground mb-3 uppercase tracking-wider",children:"Quick Actions"}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2",children:[e.jsx(N,{label:"Install CLI",description:"npm install -g upfynai-code",onClick:()=>navigator.clipboard.writeText("npm install -g upfynai-code")}),e.jsx(N,{label:"Connect Machine",description:"uc connect",onClick:()=>navigator.clipboard.writeText("uc connect")}),v,u&&e.jsx(N,{label:"Install App",description:"Add Upfyn to your home screen",onClick:f})]})]}),v,s&&e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium text-muted-foreground mb-3 uppercase tracking-wider",children:"Project Info"}),e.jsxs("div",{className:"rounded-lg bg-muted/30 border border-border/50 p-4 space-y-2",children:[e.jsx(p,{label:"Name",value:s.displayName||s.name}),e.jsx(p,{label:"Path",value:s.fullPath||s.path||"—"}),s.sessions&&e.jsx(p,{label:"Claude Sessions",value:String(s.sessions.length)}),s.cursorSessions&&e.jsx(p,{label:"Cursor Sessions",value:String(s.cursorSessions.length)}),s.codexSessions&&e.jsx(p,{label:"Codex Sessions",value:String(s.codexSessions.length)})]})]}),!s&&e.jsxs("div",{children:[e.jsxs("h3",{className:"text-sm font-medium text-muted-foreground mb-3 uppercase tracking-wider flex items-center gap-2",children:[e.jsx(L,{className:"w-3.5 h-3.5"}),"Getting Started"]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-3",children:[e.jsxs("div",{className:"rounded-lg border border-border/50 bg-card/50 p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx($,{className:"w-4 h-4 text-blue-500"}),e.jsx("span",{className:"text-sm font-medium text-foreground",children:"1. Install the CLI"})]}),e.jsxs("p",{className:"text-xs text-muted-foreground mb-2",children:["Install globally, then run ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"uc"})," to launch."]}),e.jsx("code",{className:"text-xs bg-muted/50 px-2 py-1 rounded block text-foreground/80",children:"npm install -g upfynai-code"})]}),e.jsxs("div",{className:"rounded-lg border border-border/50 bg-card/50 p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(w,{className:"w-4 h-4 text-yellow-500"}),e.jsx("span",{className:"text-sm font-medium text-foreground",children:"2. Connect Your Machine"})]}),e.jsx("p",{className:"text-xs text-muted-foreground mb-2",children:"Bridge your local dev environment to this web UI."}),e.jsx("code",{className:"text-xs bg-muted/50 px-2 py-1 rounded block text-foreground/80",children:"uc connect --key your_relay_token"})]}),e.jsxs("div",{className:"rounded-lg border border-border/50 bg-card/50 p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(D,{className:"w-4 h-4 text-indigo-500"}),e.jsx("span",{className:"text-sm font-medium text-foreground",children:"3. Use the Canvas"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Switch to the Canvas tab to create visual workspaces with code blocks, diagrams, and notes."})]}),e.jsxs("div",{className:"rounded-lg border border-border/50 bg-card/50 p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(E,{className:"w-4 h-4 text-emerald-500"}),e.jsx("span",{className:"text-sm font-medium text-foreground",children:"4. Chat with AI"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Use the Chat tab to talk to Claude, Cursor, or Codex. Bring your own API keys in Settings."})]})]})]})]})}function k({icon:s,label:t,value:l,subtext:r,color:i,loading:n}){const d={blue:"text-blue-500 bg-blue-500/10",purple:"text-purple-500 bg-purple-500/10",amber:"text-amber-500 bg-amber-500/10",emerald:"text-emerald-500 bg-emerald-500/10"};return e.jsxs("div",{className:"rounded-lg border border-border/50 bg-card/50 p-3 sm:p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx("div",{className:`w-7 h-7 rounded-md flex items-center justify-center ${d[i]}`,children:e.jsx(s,{className:"w-3.5 h-3.5"})}),e.jsx("span",{className:"text-xs text-muted-foreground",children:t})]}),n?e.jsxs(e.Fragment,{children:[e.jsx(y,{className:"h-6 w-16 mb-1"}),e.jsx(y,{className:"h-3 w-20"})]}):e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-xl font-semibold text-foreground",children:l}),r&&e.jsx("p",{className:"text-xs text-muted-foreground mt-0.5",children:r})]})]})}function N({label:s,description:t,onClick:l,href:r}){const i=r?"a":"button",n=r?{href:r,target:"_blank",rel:"noopener noreferrer"}:{onClick:l};return e.jsxs(i,{...n,className:"flex items-center justify-between p-3 rounded-lg border border-border/50 bg-card/50 hover:bg-muted/60 transition-colors text-left group",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-foreground",children:s}),e.jsx("p",{className:"text-xs text-muted-foreground",children:t})]}),r?e.jsx(B,{className:"w-3.5 h-3.5 text-muted-foreground group-hover:text-foreground"}):e.jsx(O,{className:"w-3.5 h-3.5 text-muted-foreground group-hover:text-foreground"})]})}function p({label:s,value:t}){return e.jsxs("div",{className:"flex items-center justify-between text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:s}),e.jsx("span",{className:"text-foreground font-mono text-xs truncate max-w-[60%] text-right",children:t})]})}export{K as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r,j as e}from"./vendor-react-96lCPsRK.js";import{L as X,b as ee,C as te,a as se}from"./LoginModal-Bk9mxZsi.js";import{h as ae,d as c}from"./index-DtIQCXd6.js";import{b as re,t as ie,at as ne,Z as oe,G as le,au as de,av as ce,j as ue,r as me,L as R,a4 as q,q as xe}from"./vendor-icons-BaD0x9SL.js";import"./vendor-xterm-CZq1hqo1.js";import"./vendor-canvas-D39yWul6.js";import"./vendor-mermaid-CH7SGc99.js";import"./vendor-syntax-DuHI9Ok6.js";import"./vendor-markdown-CimbIo6Y.js";import"./vendor-i18n-DCFGyhQR.js";const Ce=({onComplete:y})=>{const[a,G]=r.useState(0),[m,N]=r.useState(""),[d,v]=r.useState(""),[o,x]=r.useState(!1),[w,l]=r.useState(""),[O,C]=r.useState(!1),[M,T]=r.useState("right"),[i,g]=r.useState(null),[B]=r.useState({name:"default",fullPath:""}),[k,p]=r.useState({authenticated:!1,email:null,loading:!0,error:null}),[S,b]=r.useState({authenticated:!1,email:null,loading:!0,error:null}),[A,j]=r.useState({authenticated:!1,email:null,loading:!0,error:null}),{user:h}=ae(),L=r.useRef(void 0);r.useEffect(()=>{D()},[]);const D=async()=>{try{const t=await c("/api/user/git-config");if(t.ok){const s=await t.json();s.gitName&&N(s.gitName),s.gitEmail&&v(s.gitEmail)}}catch{}};r.useEffect(()=>{const t=L.current;L.current=i,(t===void 0||t!==null&&i===null)&&(F(),E(),I())},[i]);const F=async()=>{try{const t=await c("/api/cli/claude/status");if(t.ok){const s=await t.json();p({authenticated:s.authenticated,email:s.email,loading:!1,error:s.error||null})}else p({authenticated:!1,email:null,loading:!1,error:"Failed to check status"})}catch(t){p({authenticated:!1,email:null,loading:!1,error:t.message})}},E=async()=>{try{const t=await c("/api/cli/cursor/status");if(t.ok){const s=await t.json();b({authenticated:s.authenticated,email:s.email,loading:!1,error:s.error||null})}else b({authenticated:!1,email:null,loading:!1,error:"Failed to check status"})}catch(t){b({authenticated:!1,email:null,loading:!1,error:t.message})}},I=async()=>{try{const t=await c("/api/cli/codex/status");if(t.ok){const s=await t.json();j({authenticated:s.authenticated,email:s.email,loading:!1,error:s.error||null})}else j({authenticated:!1,email:null,loading:!1,error:"Failed to check status"})}catch(t){j({authenticated:!1,email:null,loading:!1,error:t.message})}},U=()=>g("claude"),Y=()=>g("cursor"),z=()=>g("codex"),J=t=>{t===0&&(i==="claude"?F():i==="cursor"?E():i==="codex"&&I())},u=(t,s="right")=>{T(s),C(!0),setTimeout(()=>{G(t),C(!1)},200)},V=async()=>{if(l(""),a===0){u(1,"right");return}if(a===1){if(!m.trim()||!d.trim()){l("Both git name and email are required");return}if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(d)){l("Please enter a valid email address");return}x(!0);try{const s=await c("/api/user/git-config",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({gitName:m,gitEmail:d})});if(!s.ok){const n=await s.json();throw new Error(n.error||"Failed to save git configuration")}u(2,"right")}catch(s){l(s.message)}finally{x(!1)}return}u(a+1,"right")},W=()=>{l(""),u(a-1,"left")},Z=async()=>{x(!0),l("");try{const t=await c("/api/user/complete-onboarding",{method:"POST"});if(!t.ok){const s=await t.json();throw new Error(s.error||"Failed to complete onboarding")}y&&y()}catch(t){l(t.message)}finally{x(!1)}},f=[{title:"Welcome",required:!1},{title:"Git Identity",required:!0},{title:"Agents",required:!1}],_=()=>a===0?!0:a===1?m.trim()&&d.trim()&&/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(d):!0,P=[k,S,A].filter(t=>t.authenticated).length,Q=[{icon:ie,title:"AI Agents",desc:"Claude, Cursor & Codex — unified in one interface",gradient:"from-blue-500/10 to-blue-600/5",iconColor:"text-blue-500"},{icon:ne,title:"Canvas",desc:"Visual workspace with code blocks, diagrams & notes",gradient:"from-violet-500/10 to-violet-600/5",iconColor:"text-violet-500"},{icon:oe,title:"Relay",desc:"Bridge your local machine to the web UI seamlessly",gradient:"from-amber-500/10 to-amber-600/5",iconColor:"text-amber-500"}],H=[{name:"Claude Code",status:k,onLogin:U,logo:e.jsx(ee,{size:22}),accent:"blue"},{name:"Cursor",status:S,onLogin:Y,logo:e.jsx(te,{size:22}),accent:"violet"},{name:"OpenAI Codex",status:A,onLogin:z,logo:e.jsx(se,{className:"w-5 h-5"}),accent:"neutral"}],K=O?`opacity-0 ${M==="right"?"translate-x-4":"-translate-x-4"}`:"opacity-100 translate-x-0";return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"min-h-screen bg-background flex items-center justify-center p-4 sm:p-8",children:e.jsxs("div",{className:"w-full max-w-xl",children:[e.jsxs("div",{className:"mb-10",children:[e.jsx("div",{className:"flex items-center justify-between mb-3 px-1",children:f.map((t,s)=>e.jsxs("button",{onClick:()=>{s<a&&u(s,"left")},disabled:s>a,className:`text-xs font-medium tracking-wide uppercase transition-colors duration-300 ${s===a?"text-foreground":s<a?"text-primary cursor-pointer hover:text-primary/80":"text-muted-foreground/50"}`,children:[t.title,t.required&&s===a&&e.jsx("span",{className:"text-destructive ml-1",children:"*"})]},s))}),e.jsx("div",{className:"h-1 bg-muted/60 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full bg-primary rounded-full transition-all duration-500 ease-out",style:{width:`${a/(f.length-1)*100}%`}})})]}),e.jsxs("div",{className:"bg-card rounded-2xl border border-border/60 shadow-xl shadow-black/[0.04] dark:shadow-black/[0.2] overflow-hidden",children:[e.jsxs("div",{className:`p-8 sm:p-10 transition-all duration-200 ease-out ${K}`,children:[a===0&&e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"text-center",children:[e.jsxs("div",{className:"inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium mb-6",children:[e.jsx(re,{className:"w-3.5 h-3.5"}),"Quick setup — under a minute"]}),e.jsxs("h1",{className:"text-3xl sm:text-4xl font-bold text-foreground tracking-tight leading-tight",children:["Welcome to Upfyn",h!=null&&h.first_name?e.jsxs("span",{className:"text-primary",children:[", ",h.first_name]}):null]}),e.jsx("p",{className:"text-muted-foreground mt-3 text-base max-w-md mx-auto leading-relaxed",children:"Your visual AI coding interface. Connect your favorite agents, manage projects, and build faster."})]}),e.jsx("div",{className:"grid gap-3",children:Q.map((t,s)=>e.jsxs("div",{className:`group flex items-start gap-4 p-4 rounded-xl bg-gradient-to-r ${t.gradient} border border-border/40 hover:border-border/80 transition-all duration-200`,children:[e.jsx("div",{className:`mt-0.5 p-2 rounded-lg bg-background/80 ${t.iconColor} flex-shrink-0`,children:e.jsx(t.icon,{className:"w-5 h-5"})}),e.jsxs("div",{children:[e.jsx("p",{className:"font-semibold text-foreground text-sm",children:t.title}),e.jsx("p",{className:"text-muted-foreground text-sm mt-0.5",children:t.desc})]})]},s))})]}),a===1&&e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"w-14 h-14 rounded-2xl bg-primary/10 flex items-center justify-center mx-auto mb-5",children:e.jsx(le,{className:"w-7 h-7 text-primary"})}),e.jsx("h2",{className:"text-2xl sm:text-3xl font-bold text-foreground tracking-tight",children:"Git Identity"}),e.jsx("p",{className:"text-muted-foreground mt-2 text-sm max-w-sm mx-auto",children:"Set your name and email for commit attribution"})]}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"gitName",className:"flex items-center gap-2 text-sm font-medium text-foreground mb-2",children:[e.jsx(de,{className:"w-3.5 h-3.5 text-muted-foreground"}),"Name"]}),e.jsx("input",{type:"text",id:"gitName",value:m,onChange:t=>N(t.target.value),className:"w-full px-4 py-3 border border-border/60 rounded-xl bg-background text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary/40 transition-all duration-200",placeholder:"John Doe",required:!0,disabled:o})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"gitEmail",className:"flex items-center gap-2 text-sm font-medium text-foreground mb-2",children:[e.jsx(ce,{className:"w-3.5 h-3.5 text-muted-foreground"}),"Email"]}),e.jsx("input",{type:"email",id:"gitEmail",value:d,onChange:t=>v(t.target.value),className:"w-full px-4 py-3 border border-border/60 rounded-xl bg-background text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary/40 transition-all duration-200",placeholder:"john@example.com",required:!0,disabled:o})]}),e.jsxs("p",{className:"text-xs text-muted-foreground/70 text-center",children:["Applied as ",e.jsx("code",{className:"px-1 py-0.5 rounded bg-muted/60 text-xs",children:"git config --global"})," on your machine"]})]})]}),a===2&&e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("h2",{className:"text-2xl sm:text-3xl font-bold text-foreground tracking-tight",children:"Connect Your Agents"}),e.jsxs("p",{className:"text-muted-foreground mt-2 text-sm max-w-sm mx-auto",children:["Optional — login to one or more AI assistants.",P>0&&e.jsxs("span",{className:"text-primary font-medium",children:[" ",P," connected"]})]})]}),e.jsx("div",{className:"space-y-3",children:H.map((t,s)=>{const n=t.status.authenticated,$=t.status.loading;return e.jsxs("div",{className:`group flex items-center justify-between p-4 rounded-xl border transition-all duration-200 ${n?"bg-primary/5 border-primary/20":"bg-card border-border/50 hover:border-border"}`,children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`w-10 h-10 rounded-xl flex items-center justify-center flex-shrink-0 ${n?"bg-primary/10":"bg-muted/60"}`,children:t.logo}),e.jsxs("div",{children:[e.jsxs("div",{className:"font-medium text-sm text-foreground flex items-center gap-2",children:[t.name,n&&e.jsxs("span",{className:"inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md bg-green-500/10 text-green-600 dark:text-green-400 text-[10px] font-semibold uppercase tracking-wider",children:[e.jsx(ue,{className:"w-3 h-3"}),"Connected"]})]}),e.jsx("p",{className:"text-xs text-muted-foreground mt-0.5",children:$?"Checking...":n?t.status.email||"Ready to use":"Not connected"})]})]}),!n&&!$&&e.jsx("button",{onClick:t.onLogin,className:"px-4 py-2 text-sm font-medium rounded-lg bg-foreground text-background hover:opacity-90 transition-opacity flex-shrink-0",children:"Connect"})]},s)})}),e.jsx("p",{className:"text-xs text-muted-foreground/60 text-center",children:"You can always add or change these in Settings later."})]})]}),w&&e.jsx("div",{className:"mx-8 sm:mx-10 mb-2 p-3 rounded-xl bg-destructive/10 border border-destructive/20",children:e.jsx("p",{className:"text-sm text-destructive",children:w})}),e.jsxs("div",{className:"flex items-center justify-between px-8 sm:px-10 py-5 border-t border-border/40 bg-muted/20",children:[e.jsxs("button",{onClick:W,disabled:a===0||o,className:"flex items-center gap-1.5 text-sm font-medium text-muted-foreground hover:text-foreground disabled:opacity-0 disabled:pointer-events-none transition-all duration-200",children:[e.jsx(me,{className:"w-4 h-4"}),"Back"]}),a<f.length-1?e.jsx("button",{onClick:V,disabled:!_()||o,className:"flex items-center gap-2 px-6 py-2.5 bg-primary text-primary-foreground font-medium text-sm rounded-xl hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm",children:o?e.jsxs(e.Fragment,{children:[e.jsx(R,{className:"w-4 h-4 animate-spin"}),"Saving..."]}):a===0?e.jsxs(e.Fragment,{children:["Get Started",e.jsx(q,{className:"w-4 h-4"})]}):e.jsxs(e.Fragment,{children:["Continue",e.jsx(xe,{className:"w-4 h-4"})]})}):e.jsx("button",{onClick:Z,disabled:o,className:"flex items-center gap-2 px-6 py-2.5 bg-primary text-primary-foreground font-medium text-sm rounded-xl hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm",children:o?e.jsxs(e.Fragment,{children:[e.jsx(R,{className:"w-4 h-4 animate-spin"}),"Finishing..."]}):e.jsxs(e.Fragment,{children:["Start Building",e.jsx(q,{className:"w-4 h-4"})]})})]})]}),e.jsxs("p",{className:"text-center text-xs text-muted-foreground/50 mt-6",children:["Step ",a+1," of ",f.length]})]})}),i&&e.jsx(X,{isOpen:!!i,onClose:()=>g(null),provider:i,project:B,onComplete:J,isOnboarding:!0})]})};export{Ce as default};
|