fluxy-bot 0.3.5 → 0.3.6
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/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useRef, useEffect, type KeyboardEvent } from 'react';
|
|
2
|
-
import { ArrowRight, LoaderCircle, ExternalLink, ClipboardPaste, RefreshCw, Check, ChevronDown, Mic } from 'lucide-react';
|
|
2
|
+
import { ArrowRight, LoaderCircle, ExternalLink, ClipboardPaste, RefreshCw, Check, ChevronDown, Mic, Eye, EyeOff } from 'lucide-react';
|
|
3
3
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
4
4
|
|
|
5
5
|
/* ── Provider config ── */
|
|
@@ -25,7 +25,7 @@ const MODELS: Record<string, { id: string; label: string }[]> = {
|
|
|
25
25
|
],
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// TOTAL_STEPS is dynamic — set inside the component based on isInitialSetup
|
|
29
29
|
|
|
30
30
|
const HANDLES = [
|
|
31
31
|
{ tier: 'premium', label: (n: string) => `${n}.fluxy.bot`, badge: '$5', badgeCls: 'bg-[#AF27E3]/15 text-[#AF27E3] border-[#AF27E3]/20', highlight: true },
|
|
@@ -86,9 +86,12 @@ function ModelDropdown({ models, value, onChange }: { models: { id: string; labe
|
|
|
86
86
|
|
|
87
87
|
interface Props {
|
|
88
88
|
onComplete: () => void;
|
|
89
|
+
isInitialSetup?: boolean;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
export default function OnboardWizard({ onComplete }: Props) {
|
|
92
|
+
export default function OnboardWizard({ onComplete, isInitialSetup = false }: Props) {
|
|
93
|
+
const TOTAL_STEPS = isInitialSetup ? 7 : 6; // 0..5 normal, +step 6 "All Set" for initial
|
|
94
|
+
|
|
92
95
|
const [step, setStep] = useState(0);
|
|
93
96
|
const [userName, setUserName] = useState('');
|
|
94
97
|
const [provider, setProvider] = useState('anthropic');
|
|
@@ -129,11 +132,12 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
129
132
|
const [changingHandle, setChangingHandle] = useState(false);
|
|
130
133
|
|
|
131
134
|
// Portal credentials (step 3)
|
|
132
|
-
const [portalUser, setPortalUser] = useState('');
|
|
135
|
+
const [portalUser, setPortalUser] = useState('admin');
|
|
133
136
|
const [portalPass, setPortalPass] = useState('');
|
|
134
137
|
const [portalPassConfirm, setPortalPassConfirm] = useState('');
|
|
135
138
|
const [portalCopied, setPortalCopied] = useState(false);
|
|
136
139
|
const [portalExists, setPortalExists] = useState(false);
|
|
140
|
+
const [showPassAllSet, setShowPassAllSet] = useState(false);
|
|
137
141
|
|
|
138
142
|
// Portal old password (for changing existing credentials)
|
|
139
143
|
const [portalOldPass, setPortalOldPass] = useState('');
|
|
@@ -428,12 +432,12 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
428
432
|
|
|
429
433
|
/* ── Navigation ── */
|
|
430
434
|
|
|
431
|
-
// Steps: 0=Welcome, 1=Name, 2=Bot name + Handle, 3=
|
|
435
|
+
// Steps: 0=Welcome, 1=Name, 2=Bot name + Handle, 3=Password, 4=Provider, 5=Whisper+Complete, 6=All Set (initial only)
|
|
432
436
|
const portalPassMatch = portalPass === portalPassConfirm;
|
|
433
|
-
const portalValid =
|
|
437
|
+
const portalValid = portalPass.length >= 6 && portalPassMatch;
|
|
434
438
|
// When portal exists: can continue if no password fields touched, or old pass verified + new pass valid
|
|
435
439
|
const portalCanContinue = portalExists
|
|
436
|
-
? (
|
|
440
|
+
? (portalPass.length === 0 || (portalOldPassVerified && portalValid))
|
|
437
441
|
: portalValid;
|
|
438
442
|
|
|
439
443
|
const canNext = (() => {
|
|
@@ -473,7 +477,12 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
473
477
|
portalPass,
|
|
474
478
|
}),
|
|
475
479
|
});
|
|
476
|
-
|
|
480
|
+
if (isInitialSetup) {
|
|
481
|
+
setSaving(false);
|
|
482
|
+
setStep(6);
|
|
483
|
+
} else {
|
|
484
|
+
onComplete();
|
|
485
|
+
}
|
|
477
486
|
} catch (err) {
|
|
478
487
|
console.error('Onboard failed:', err);
|
|
479
488
|
setSaving(false);
|
|
@@ -762,67 +771,26 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
762
771
|
</div>
|
|
763
772
|
)}
|
|
764
773
|
|
|
765
|
-
{/* ── Step 3:
|
|
774
|
+
{/* ── Step 3: Password ── */}
|
|
766
775
|
{step === 3 && (
|
|
767
776
|
<div>
|
|
768
777
|
<h1 className="text-xl font-bold text-white tracking-tight">
|
|
769
|
-
|
|
778
|
+
Set a password
|
|
770
779
|
</h1>
|
|
771
780
|
<p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
|
|
772
|
-
|
|
781
|
+
You'll need this password to access your agent's chat. Keep it safe — anyone with your URL will need it to log in.
|
|
773
782
|
</p>
|
|
774
783
|
|
|
775
|
-
{/* Relay URL display + copy */}
|
|
776
|
-
{(registered && registeredUrl) && (
|
|
777
|
-
<div className="mt-4 flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
|
|
778
|
-
<span className="font-mono text-[13px] text-white/60 truncate flex-1">{registeredUrl}</span>
|
|
779
|
-
<button
|
|
780
|
-
onClick={() => {
|
|
781
|
-
navigator.clipboard.writeText(registeredUrl);
|
|
782
|
-
setPortalCopied(true);
|
|
783
|
-
setTimeout(() => setPortalCopied(false), 2000);
|
|
784
|
-
}}
|
|
785
|
-
className="shrink-0 text-white/30 hover:text-white/60 transition-colors"
|
|
786
|
-
>
|
|
787
|
-
{portalCopied ? (
|
|
788
|
-
<Check className="h-4 w-4 text-emerald-400" />
|
|
789
|
-
) : (
|
|
790
|
-
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
791
|
-
<rect x="9" y="9" width="13" height="13" rx="2" />
|
|
792
|
-
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
|
|
793
|
-
</svg>
|
|
794
|
-
)}
|
|
795
|
-
</button>
|
|
796
|
-
</div>
|
|
797
|
-
)}
|
|
798
|
-
|
|
799
784
|
{/* Already configured hint */}
|
|
800
785
|
{portalExists && (
|
|
801
786
|
<div className="mt-4 bg-white/[0.02] border border-white/[0.06] rounded-xl px-4 py-2.5">
|
|
802
|
-
<p className="text-white/40 text-[12px]">
|
|
787
|
+
<p className="text-white/40 text-[12px]">Password already set. Leave fields empty to keep your current password, or enter your current password to change it.</p>
|
|
803
788
|
</div>
|
|
804
789
|
)}
|
|
805
790
|
|
|
806
|
-
{/* Username */}
|
|
807
|
-
<div className="mt-5">
|
|
808
|
-
<label className="text-[12px] text-white/40 font-medium mb-1.5 block">Username</label>
|
|
809
|
-
<input
|
|
810
|
-
type="text"
|
|
811
|
-
value={portalUser}
|
|
812
|
-
onChange={(e) => setPortalUser(e.target.value.replace(/\s/g, '').toLowerCase())}
|
|
813
|
-
placeholder="admin"
|
|
814
|
-
autoFocus
|
|
815
|
-
autoComplete="username"
|
|
816
|
-
className={inputCls + ' font-mono'}
|
|
817
|
-
/>
|
|
818
|
-
{portalUser.length > 0 && portalUser.trim().length < 3 && (
|
|
819
|
-
<p className="text-amber-400/70 text-[11px] mt-1">At least 3 characters</p>
|
|
820
|
-
)}
|
|
821
|
-
</div>
|
|
822
|
-
|
|
823
791
|
{/* Current password (only when changing existing credentials) */}
|
|
824
792
|
{portalExists && (
|
|
825
|
-
<div className="mt-
|
|
793
|
+
<div className="mt-5">
|
|
826
794
|
<label className="text-[12px] text-white/40 font-medium mb-1.5 block">Current password</label>
|
|
827
795
|
<div className="flex items-center gap-2">
|
|
828
796
|
<input
|
|
@@ -831,6 +799,7 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
831
799
|
onChange={(e) => { setPortalOldPass(e.target.value); setPortalOldPassError(''); setPortalOldPassVerified(false); }}
|
|
832
800
|
placeholder="Enter current password to change it"
|
|
833
801
|
autoComplete="current-password"
|
|
802
|
+
autoFocus
|
|
834
803
|
className={inputCls + ' flex-1'}
|
|
835
804
|
/>
|
|
836
805
|
{portalOldPass.length > 0 && !portalOldPassVerified && (
|
|
@@ -877,7 +846,7 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
877
846
|
{/* New password + confirm (shown when no existing credentials, or when old password verified) */}
|
|
878
847
|
{(!portalExists || portalOldPassVerified) && (
|
|
879
848
|
<>
|
|
880
|
-
<div className=
|
|
849
|
+
<div className={portalExists ? 'mt-3' : 'mt-5'}>
|
|
881
850
|
<label className="text-[12px] text-white/40 font-medium mb-1.5 block">
|
|
882
851
|
{portalExists ? 'New password' : 'Password'}
|
|
883
852
|
</label>
|
|
@@ -887,6 +856,7 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
887
856
|
onChange={(e) => setPortalPass(e.target.value)}
|
|
888
857
|
placeholder="••••••••"
|
|
889
858
|
autoComplete="new-password"
|
|
859
|
+
autoFocus={!portalExists}
|
|
890
860
|
onKeyDown={handleKeyDown}
|
|
891
861
|
className={inputCls}
|
|
892
862
|
/>
|
|
@@ -1226,11 +1196,81 @@ export default function OnboardWizard({ onComplete }: Props) {
|
|
|
1226
1196
|
)}
|
|
1227
1197
|
</div>
|
|
1228
1198
|
)}
|
|
1199
|
+
|
|
1200
|
+
{/* ── Step 6: All Set (initial onboard only) ── */}
|
|
1201
|
+
{step === 6 && isInitialSetup && (
|
|
1202
|
+
<div className="flex flex-col items-center text-center">
|
|
1203
|
+
<div className="w-16 h-16 rounded-full bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center mb-5">
|
|
1204
|
+
<Check className="h-8 w-8 text-emerald-400" />
|
|
1205
|
+
</div>
|
|
1206
|
+
<h1 className="text-2xl font-bold text-white tracking-tight">
|
|
1207
|
+
All Set!
|
|
1208
|
+
</h1>
|
|
1209
|
+
<p className="text-white/40 text-[13px] mt-2 leading-relaxed max-w-[340px]">
|
|
1210
|
+
Your agent is live and ready. From now on, access it using your custom URL below.
|
|
1211
|
+
</p>
|
|
1212
|
+
|
|
1213
|
+
{/* Custom URL */}
|
|
1214
|
+
<div className="w-full mt-6 flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
|
|
1215
|
+
<span className="font-mono text-[13px] text-white/70 truncate flex-1 text-left">{registeredUrl}</span>
|
|
1216
|
+
<button
|
|
1217
|
+
onClick={() => {
|
|
1218
|
+
const url = registeredUrl.startsWith('http') ? registeredUrl : `https://${registeredUrl}`;
|
|
1219
|
+
navigator.clipboard.writeText(url);
|
|
1220
|
+
setPortalCopied(true);
|
|
1221
|
+
setTimeout(() => setPortalCopied(false), 2000);
|
|
1222
|
+
}}
|
|
1223
|
+
className="shrink-0 text-white/30 hover:text-white/60 transition-colors"
|
|
1224
|
+
>
|
|
1225
|
+
{portalCopied ? (
|
|
1226
|
+
<Check className="h-4 w-4 text-emerald-400" />
|
|
1227
|
+
) : (
|
|
1228
|
+
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
1229
|
+
<rect x="9" y="9" width="13" height="13" rx="2" />
|
|
1230
|
+
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
|
|
1231
|
+
</svg>
|
|
1232
|
+
)}
|
|
1233
|
+
</button>
|
|
1234
|
+
</div>
|
|
1235
|
+
|
|
1236
|
+
{/* Password display with eye toggle */}
|
|
1237
|
+
<div className="w-full mt-3">
|
|
1238
|
+
<label className="text-[12px] text-white/40 font-medium mb-1.5 block text-left">Your password</label>
|
|
1239
|
+
<div className="flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
|
|
1240
|
+
<span className="font-mono text-[13px] text-white/70 truncate flex-1 text-left">
|
|
1241
|
+
{showPassAllSet ? portalPass : '\u2022'.repeat(Math.max(portalPass.length, 8))}
|
|
1242
|
+
</span>
|
|
1243
|
+
<button
|
|
1244
|
+
onClick={() => setShowPassAllSet((v) => !v)}
|
|
1245
|
+
className="shrink-0 text-white/30 hover:text-white/60 transition-colors"
|
|
1246
|
+
>
|
|
1247
|
+
{showPassAllSet ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
1248
|
+
</button>
|
|
1249
|
+
</div>
|
|
1250
|
+
</div>
|
|
1251
|
+
|
|
1252
|
+
{/* Redirect button */}
|
|
1253
|
+
<button
|
|
1254
|
+
onClick={() => {
|
|
1255
|
+
const url = registeredUrl.startsWith('http') ? registeredUrl : `https://${registeredUrl}`;
|
|
1256
|
+
window.location.href = url;
|
|
1257
|
+
}}
|
|
1258
|
+
className="w-full mt-6 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2"
|
|
1259
|
+
>
|
|
1260
|
+
Go to your agent
|
|
1261
|
+
<ExternalLink className="h-4 w-4" />
|
|
1262
|
+
</button>
|
|
1263
|
+
|
|
1264
|
+
<p className="text-white/20 text-[11px] mt-3 leading-relaxed">
|
|
1265
|
+
You'll be redirected to your custom URL.
|
|
1266
|
+
</p>
|
|
1267
|
+
</div>
|
|
1268
|
+
)}
|
|
1229
1269
|
</motion.div>
|
|
1230
1270
|
</AnimatePresence>
|
|
1231
1271
|
|
|
1232
|
-
{/* Back button */}
|
|
1233
|
-
{step > 0 && (
|
|
1272
|
+
{/* Back button (hidden on All Set step) */}
|
|
1273
|
+
{step > 0 && step < TOTAL_STEPS - 1 && !(step === 6 && isInitialSetup) && (
|
|
1234
1274
|
<div className="px-8 pb-5 -mt-3">
|
|
1235
1275
|
<button
|
|
1236
1276
|
onClick={back}
|
|
@@ -9,7 +9,7 @@ function App() {
|
|
|
9
9
|
window.parent?.postMessage({ type: 'fluxy:onboard-complete' }, '*');
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
return <OnboardWizard onComplete={handleComplete} />;
|
|
12
|
+
return <OnboardWizard onComplete={handleComplete} isInitialSetup />;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|