cicy-desktop 2.1.77 → 2.1.78
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 +21 -18
- package/build/icon.icns +0 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/build/icon.svg +8 -18
- package/build/icons/icon-1024.png +0 -0
- package/build/icons/icon-128.png +0 -0
- package/build/icons/icon-16.png +0 -0
- package/build/icons/icon-24.png +0 -0
- package/build/icons/icon-256.png +0 -0
- package/build/icons/icon-32.png +0 -0
- package/build/icons/icon-48.png +0 -0
- package/build/icons/icon-512.png +0 -0
- package/build/icons/icon-64.png +0 -0
- package/build/icons/icon-96.png +0 -0
- package/build/icons/trayTemplate-16.png +0 -0
- package/build/icons/trayTemplate-16@2x.png +0 -0
- package/build/icons/trayTemplate-22.png +0 -0
- package/build/icons/trayTemplate-22@2x.png +0 -0
- package/build/icons/trayTemplate-32.png +0 -0
- package/build/icons/trayTemplate-32@2x.png +0 -0
- package/build/trayTemplate.png +0 -0
- package/build/trayTemplate.svg +11 -12
- package/build/trayTemplate@2x.png +0 -0
- package/package.json +6 -6
- package/src/backends/auth-loopback.js +17 -6
- package/src/backends/homepage-react/assets/index-DE9m6JTn.css +1 -0
- package/src/backends/homepage-react/assets/index-DLYMzgf5.js +365 -0
- package/src/backends/homepage-react/favicon-256.png +0 -0
- package/src/backends/homepage-react/favicon.svg +12 -0
- package/src/backends/homepage-react/index.html +4 -2
- package/src/backends/local-teams.js +53 -1
- package/src/backends/login-success.html +96 -0
- package/src/cloud/cloud-client.js +239 -0
- package/src/main.js +62 -1
- package/src/utils/brand-host-electron.js +134 -0
- package/src/utils/window-utils.js +62 -14
- package/workers/render/index.html +2 -0
- package/workers/render/public/favicon-256.png +0 -0
- package/workers/render/public/favicon.svg +12 -0
- package/workers/render/src/App.css +127 -31
- package/workers/render/src/App.jsx +170 -24
- package/src/backends/homepage-react/assets/index-CPH-S8uU.css +0 -1
- package/src/backends/homepage-react/assets/index-DuWX0iug.js +0 -365
|
@@ -16,6 +16,15 @@ const ACCESS_TOKEN_KEY = "cicy_access_token";
|
|
|
16
16
|
const USER_ID_KEY = "cicy_user_id";
|
|
17
17
|
const CLOUD_BASE = "https://cicy-ai.com";
|
|
18
18
|
|
|
19
|
+
// Open a cloud dash page with a CLEAN URL — no token of any kind in the address
|
|
20
|
+
// (主人: 钱包/账单/团队账单 URL 不要带 token,连一次性票据 ?t 也不要). The dash
|
|
21
|
+
// authenticates via the browser's own cicy-ai.com session; if not logged in it
|
|
22
|
+
// bounces through /login and returns. `query` is the part after /dash,
|
|
23
|
+
// e.g. "?view=wallet" or "?team=14".
|
|
24
|
+
async function openCloudPage(query) {
|
|
25
|
+
try { window.cicy?.shell?.openExternal?.(`${CLOUD_BASE}/dash${query}`); } catch {}
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
// ── Toast: lightweight global notifications (bottom-right). Pub/sub store so
|
|
20
29
|
// any component can push without prop-drilling — one <ToastHost/> at the shell
|
|
21
30
|
// root renders them. Used for 更新/启动/重启 progress + result so feedback floats
|
|
@@ -212,6 +221,21 @@ export default function App() {
|
|
|
212
221
|
.catch(() => setTermsOk(true));
|
|
213
222
|
}, []);
|
|
214
223
|
|
|
224
|
+
// Tag <html> with the platform + fullscreen state so CSS can reserve the
|
|
225
|
+
// macOS hiddenInset traffic-light gutter (the topbar's padding-left:84px rule
|
|
226
|
+
// is gated on [data-platform="darwin"][data-fullscreen="0"]). Without this the
|
|
227
|
+
// red/yellow/green buttons overlap the brand — the reported misalignment.
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
const root = document.documentElement;
|
|
230
|
+
try { root.setAttribute("data-platform", window.cicy?.platform || "linux"); } catch {}
|
|
231
|
+
root.setAttribute("data-fullscreen", "0");
|
|
232
|
+
let off;
|
|
233
|
+
try {
|
|
234
|
+
off = window.cicy?.window?.onFullscreen?.((fs) => root.setAttribute("data-fullscreen", fs ? "1" : "0"));
|
|
235
|
+
} catch {}
|
|
236
|
+
return () => { try { off && off(); } catch {} };
|
|
237
|
+
}, []);
|
|
238
|
+
|
|
215
239
|
// sk-xxx (LLM API). Used by /v1/chat/completions etc.
|
|
216
240
|
const [token, setToken] = useState(() => safeGet(TOKEN_KEY));
|
|
217
241
|
// Console-API bearer. Used by /api/user/self, /api/teams, etc.
|
|
@@ -253,22 +277,33 @@ export default function App() {
|
|
|
253
277
|
}
|
|
254
278
|
setProfileLoading(true);
|
|
255
279
|
setProfileError("");
|
|
280
|
+
// Cloud uses its own per-user session token (sk-sess-, from /cb) as the
|
|
281
|
+
// SOLE Bearer for every console call. No New-Api-User header and no
|
|
282
|
+
// access_token — those were the old new-api convention, dropped when owner
|
|
283
|
+
// moved cloud to self-built identity (authM resolves owner from the session).
|
|
256
284
|
const headers = { Authorization: `Bearer ${at}` };
|
|
257
|
-
if (uid) headers["New-Api-User"] = String(uid);
|
|
258
285
|
try {
|
|
259
286
|
const [selfRes, teamsRes] = await Promise.all([
|
|
260
287
|
window.cicy.cloud.fetch(`${CLOUD_BASE}/api/user/self`, { headers }),
|
|
261
288
|
window.cicy.cloud.fetch(`${CLOUD_BASE}/api/teams`, { headers }),
|
|
262
289
|
]);
|
|
263
|
-
|
|
290
|
+
// /api/teams drives the team grid — it is the ONLY critical call here.
|
|
264
291
|
if (!teamsRes?.ok) throw new Error(`/api/teams ${teamsRes?.status || "?"} ${teamsRes?.error || ""}`);
|
|
265
|
-
// /api/user/self is wrapped: { success, message, data }
|
|
266
292
|
// /api/teams is bare: { teams: [...] }
|
|
267
|
-
const selfBody = JSON.parse(selfRes.body || "{}");
|
|
268
293
|
const teamsBody = JSON.parse(teamsRes.body || "{}");
|
|
269
|
-
if (selfBody?.success === false) throw new Error(selfBody.message || "self failed");
|
|
270
|
-
setMe(selfBody?.data || null);
|
|
271
294
|
setTeams(Array.isArray(teamsBody?.teams) ? teamsBody.teams : []);
|
|
295
|
+
// /api/user/self is best-effort: it only fills the profile display name.
|
|
296
|
+
// A 404 / failure here must NOT block login or the team list (the cloud
|
|
297
|
+
// endpoint can lag) — degrade to a null profile instead of throwing.
|
|
298
|
+
// /api/user/self is wrapped: { success, message, data }
|
|
299
|
+
if (selfRes?.ok) {
|
|
300
|
+
try {
|
|
301
|
+
const selfBody = JSON.parse(selfRes.body || "{}");
|
|
302
|
+
setMe(selfBody?.success === false ? null : (selfBody?.data || null));
|
|
303
|
+
} catch { setMe(null); }
|
|
304
|
+
} else {
|
|
305
|
+
setMe(null);
|
|
306
|
+
}
|
|
272
307
|
} catch (e) {
|
|
273
308
|
setProfileError(e.message || String(e));
|
|
274
309
|
} finally {
|
|
@@ -276,10 +311,15 @@ export default function App() {
|
|
|
276
311
|
}
|
|
277
312
|
}, []);
|
|
278
313
|
|
|
279
|
-
// First profile fetch on mount
|
|
314
|
+
// First profile fetch on mount. The cloud console endpoints (/api/user/self,
|
|
315
|
+
// /api/teams) authenticate the owner-bound LOGIN token (the sk-xxx from the
|
|
316
|
+
// /cb callback) — NOT the console access_token (the cloud never mints one;
|
|
317
|
+
// sending it 401s). Prefer the login token; fall back to access_token only if
|
|
318
|
+
// somehow that's all we have.
|
|
280
319
|
useEffect(() => {
|
|
281
|
-
|
|
282
|
-
|
|
320
|
+
const bearer = token || accessToken;
|
|
321
|
+
if (bearer) fetchProfile(bearer, userId);
|
|
322
|
+
}, [token, accessToken, userId, fetchProfile]);
|
|
283
323
|
|
|
284
324
|
// Local teams: probe on mount (independent of cloud login — local team
|
|
285
325
|
// discovery doesn't require a token). Fast-poll every 3s for the first
|
|
@@ -538,7 +578,12 @@ export default function App() {
|
|
|
538
578
|
const customList = (localTeams || []).filter((t) => !isLocalSidecar(t.base_url));
|
|
539
579
|
const localCount = localList.length;
|
|
540
580
|
const customCount = customList.length;
|
|
541
|
-
|
|
581
|
+
// /api/teams returns ALL of this owner's teams — including kind=local ones
|
|
582
|
+
// (this device's AND other devices'). On the desktop the 云端 tab must show
|
|
583
|
+
// ONLY cloud teams; local teams come from the local store (localList) and
|
|
584
|
+
// cross-device local aggregation belongs to the web dash, not here.
|
|
585
|
+
const cloudList = (teams || []).filter((t) => !t.is_local && t.kind !== "local");
|
|
586
|
+
const cloudCount = cloudList.length;
|
|
542
587
|
const showLocal = tab === "all" || tab === "local";
|
|
543
588
|
const showCustom = tab === "all" || tab === "custom";
|
|
544
589
|
const showCloud = tab === "all" || tab === "cloud";
|
|
@@ -547,7 +592,8 @@ export default function App() {
|
|
|
547
592
|
<div className="shell shell--app">
|
|
548
593
|
<div className="glow glow--app" aria-hidden />
|
|
549
594
|
<div className="shell__left">
|
|
550
|
-
<Header me={me} welcome={welcome} onLogout={handleLogout}
|
|
595
|
+
<Header me={me} welcome={welcome} onLogout={handleLogout}
|
|
596
|
+
mitmTeam={localList.length > 0 ? localList[0] : null} />
|
|
551
597
|
<main className="main">
|
|
552
598
|
<div className="app__tabs">
|
|
553
599
|
{[
|
|
@@ -569,12 +615,12 @@ export default function App() {
|
|
|
569
615
|
</div>
|
|
570
616
|
|
|
571
617
|
{/* Docker 安装卡已下线 (主人令): Windows 走原生 cicy-code.exe --helper,不再用 Docker。 */}
|
|
572
|
-
{
|
|
618
|
+
{/* HTTPS 审计 tip(MitmConsentCard)已移入右上角用户菜单(user-chip 下拉)。 */}
|
|
573
619
|
|
|
574
620
|
{profileError && (
|
|
575
621
|
<div className="error" style={{ marginBottom: 12 }}>
|
|
576
622
|
云端: {profileError}
|
|
577
|
-
<button className="btn-ghost" style={{ marginLeft: 8 }} onClick={() => fetchProfile(accessToken, userId)}>
|
|
623
|
+
<button className="btn-ghost" style={{ marginLeft: 8 }} onClick={() => fetchProfile(token || accessToken, userId)}>
|
|
578
624
|
重试
|
|
579
625
|
</button>
|
|
580
626
|
</div>
|
|
@@ -587,7 +633,7 @@ export default function App() {
|
|
|
587
633
|
{showCustom && customList.map((t) => (
|
|
588
634
|
<LocalTeamCard key={"custom:" + t.id} team={t} onOpen={() => openLocalTeam(t.id)} onRename={renameLocalTeam} onRefresh={fetchLocalTeams} />
|
|
589
635
|
))}
|
|
590
|
-
{showCloud &&
|
|
636
|
+
{showCloud && cloudList.map((t) => (
|
|
591
637
|
<TeamCard
|
|
592
638
|
key={"cloud:" + t.id}
|
|
593
639
|
team={t}
|
|
@@ -620,20 +666,58 @@ export default function App() {
|
|
|
620
666
|
);
|
|
621
667
|
}
|
|
622
668
|
|
|
623
|
-
function Header({ me, welcome, onLogout }) {
|
|
669
|
+
function Header({ me, welcome, onLogout, mitmTeam }) {
|
|
624
670
|
const name = me?.display_name || me?.username || "…";
|
|
625
671
|
const initials = (name || "?").slice(0, 1).toUpperCase();
|
|
672
|
+
const [open, setOpen] = useState(false);
|
|
673
|
+
const wrap = useRef(null);
|
|
674
|
+
// Click-outside closes the dropdown (mirrors LocalTeamCard's ⋯ menu).
|
|
675
|
+
useEffect(() => {
|
|
676
|
+
if (!open) return;
|
|
677
|
+
const onDoc = (e) => { if (wrap.current && !wrap.current.contains(e.target)) setOpen(false); };
|
|
678
|
+
document.addEventListener("mousedown", onDoc);
|
|
679
|
+
return () => document.removeEventListener("mousedown", onDoc);
|
|
680
|
+
}, [open]);
|
|
681
|
+
// Cloud dash pages: query-param routed, opened via a one-time handoff ticket
|
|
682
|
+
// (no long-term token in the URL — see openCloudPage).
|
|
683
|
+
const goDash = (query) => { openCloudPage(query); setOpen(false); };
|
|
626
684
|
return (
|
|
627
685
|
<header className="topbar">
|
|
628
686
|
<div className="brand-mini">
|
|
629
687
|
<div className="brand-mark sm"><BrandGlyph /></div>
|
|
630
688
|
<span className="brand-name">CiCy Desktop</span>
|
|
631
689
|
</div>
|
|
632
|
-
<div className="user-chip">
|
|
690
|
+
<div className="user-chip" data-id="UserChip" ref={wrap}>
|
|
633
691
|
{welcome && <span className="welcome">{welcome}</span>}
|
|
634
|
-
<
|
|
635
|
-
|
|
636
|
-
|
|
692
|
+
<button
|
|
693
|
+
type="button"
|
|
694
|
+
data-id="UserChip-trigger"
|
|
695
|
+
className={`user-chip__trigger${open ? " is-open" : ""}`}
|
|
696
|
+
onClick={() => setOpen((v) => !v)}
|
|
697
|
+
>
|
|
698
|
+
<div className="avatar">{initials}</div>
|
|
699
|
+
<span className="user-name">{name}</span>
|
|
700
|
+
<span className="user-chip__caret" aria-hidden>▾</span>
|
|
701
|
+
</button>
|
|
702
|
+
{open && (
|
|
703
|
+
<div className="user-chip__menu" data-id="UserChip-menu" role="menu">
|
|
704
|
+
<button type="button" data-id="UserChip-wallet" className="user-chip__menu-item" onClick={() => goDash("?view=wallet")}>
|
|
705
|
+
我的钱包
|
|
706
|
+
</button>
|
|
707
|
+
<button type="button" data-id="UserChip-billing" className="user-chip__menu-item" onClick={() => goDash("?view=usage")}>
|
|
708
|
+
我的账单
|
|
709
|
+
</button>
|
|
710
|
+
{mitmTeam && (
|
|
711
|
+
<div className="user-chip__menu-mitm" data-id="UserChip-mitm" onClick={(e) => e.stopPropagation()}>
|
|
712
|
+
<MitmConsentCard team={mitmTeam} variant="menu" />
|
|
713
|
+
</div>
|
|
714
|
+
)}
|
|
715
|
+
<div className="user-chip__menu-sep" aria-hidden />
|
|
716
|
+
<button type="button" data-id="UserChip-logout" className="user-chip__menu-item is-danger" onClick={() => { setOpen(false); onLogout(); }}>
|
|
717
|
+
退出
|
|
718
|
+
</button>
|
|
719
|
+
</div>
|
|
720
|
+
)}
|
|
637
721
|
</div>
|
|
638
722
|
</header>
|
|
639
723
|
);
|
|
@@ -730,7 +814,7 @@ function FirstRunTermsGate({ onAgree }) {
|
|
|
730
814
|
// HTTPS 审计 CA 授权卡片 (合规 opt-in)。绝不首启静默装根证书 (Superfish 红线) —
|
|
731
815
|
// 用户在此显式同意后,才由 cicy-code 写入系统根信任库。三态:未授权 / 已授权(可撤销) /
|
|
732
816
|
// 处理中。同意走 POST /api/mitm/consent;need_elevation 回退 exec 自提权 install-ca。
|
|
733
|
-
function MitmConsentCard({ team }) {
|
|
817
|
+
function MitmConsentCard({ team, variant }) {
|
|
734
818
|
const [status, setStatus] = useState(undefined); // undefined=loading, null=endpoint absent, {generated,trusted,consent}
|
|
735
819
|
const [busy, setBusy] = useState(""); // "" | enable | disable
|
|
736
820
|
const [error, setError] = useState("");
|
|
@@ -805,6 +889,34 @@ function MitmConsentCard({ team }) {
|
|
|
805
889
|
const partial = status.consent && !status.trusted; // consented but not (re)installed
|
|
806
890
|
const t = (k, fb) => tr(`mitmConsent.${k}`, fb);
|
|
807
891
|
|
|
892
|
+
// Menu variant: a single flat row matching the user-chip dropdown items —
|
|
893
|
+
// label + state dot on the left, a quiet on/off toggle on the right. No big
|
|
894
|
+
// card, no portal pill. Used inside the user menu (主人: tip 要和 menu 风格统一).
|
|
895
|
+
if (variant === "menu") {
|
|
896
|
+
const toggle = (e) => {
|
|
897
|
+
e?.stopPropagation?.();
|
|
898
|
+
if (busy) return;
|
|
899
|
+
if (granted) { if (window.confirm(t("revokeConfirm", "撤销后将停止解密审计并清除同意标记。确定?"))) disable(); }
|
|
900
|
+
else enable();
|
|
901
|
+
};
|
|
902
|
+
return (
|
|
903
|
+
<div className="user-chip__mitm" data-id="MitmConsentCard">
|
|
904
|
+
<div className="user-chip__menu-item user-chip__mitm-row"
|
|
905
|
+
title={t("scopeNote", "仅解密 AI 厂商域名,数据留本地,随时可关闭。")}>
|
|
906
|
+
<span className="user-chip__mitm-label">{t("rowLabel", "HTTPS 审计")}</span>
|
|
907
|
+
<button type="button" role="switch" aria-checked={granted ? "true" : "false"}
|
|
908
|
+
data-id="MitmConsentCard-toggle"
|
|
909
|
+
className={`mini-switch${granted ? " is-on" : ""}${busy ? " is-busy" : ""}`}
|
|
910
|
+
disabled={!!busy} onClick={toggle}>
|
|
911
|
+
<span className="mini-switch__knob" />
|
|
912
|
+
</button>
|
|
913
|
+
</div>
|
|
914
|
+
{partial && !busy && <div className="user-chip__mitm-note" data-id="MitmConsentCard-note">{t("partialNote", "已同意但未安装,点开关重试")}</div>}
|
|
915
|
+
{error && <div className="user-chip__mitm-err" data-id="MitmConsentCard-error">{error}</div>}
|
|
916
|
+
</div>
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
|
|
808
920
|
// 已启用是稳态状态,不是决策 — 收成一个低调的小 pill(一行 + "关闭"),把显眼的
|
|
809
921
|
// 大卡片只留给首次"同意"那一下,不在首页常驻一个大块。
|
|
810
922
|
if (granted || busy === "disable") {
|
|
@@ -1173,6 +1285,23 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
1173
1285
|
: (upToDateMsg || tr("sidecar.checkUpdate", "检查更新"))}
|
|
1174
1286
|
</button>
|
|
1175
1287
|
)}
|
|
1288
|
+
{team.cloud_team_id && (
|
|
1289
|
+
<button
|
|
1290
|
+
type="button"
|
|
1291
|
+
data-id="LocalTeamCard-billing"
|
|
1292
|
+
className="bcard__menu-item"
|
|
1293
|
+
onClick={(e) => {
|
|
1294
|
+
e.stopPropagation();
|
|
1295
|
+
setMenuOpen(false);
|
|
1296
|
+
// Per-team billing (w-10032): /dash?team=<teamId> + handoff
|
|
1297
|
+
// ticket. teamId = the cloud_team_id we stored on name-sync;
|
|
1298
|
+
// no key in the URL — dash fetches it via session.
|
|
1299
|
+
openCloudPage(`?team=${encodeURIComponent(team.cloud_team_id)}`);
|
|
1300
|
+
}}
|
|
1301
|
+
>
|
|
1302
|
+
{tr("localTeams.billing", "账单")}
|
|
1303
|
+
</button>
|
|
1304
|
+
)}
|
|
1176
1305
|
{isCustom && (
|
|
1177
1306
|
<button
|
|
1178
1307
|
type="button"
|
|
@@ -1267,6 +1396,7 @@ function TeamCard({ team, onOpen }) {
|
|
|
1267
1396
|
const kindLabel = team.team_kind === "personal" ? "个人" : "共享";
|
|
1268
1397
|
const statusOk = team.status === "active";
|
|
1269
1398
|
const hasUrl = !!(team.workspace_url || team.workspace_direct_url);
|
|
1399
|
+
const billTeamId = team.teamId || team.id; // /dash?team=<teamId> (no key in URL)
|
|
1270
1400
|
return (
|
|
1271
1401
|
<div className={`bcard bcard--cloud${statusOk ? " bcard--online" : ""}`}>
|
|
1272
1402
|
<div className="bcard__accent" />
|
|
@@ -1275,7 +1405,20 @@ function TeamCard({ team, onOpen }) {
|
|
|
1275
1405
|
<span className="bcard__dot" data-tone={statusOk ? "ok" : "off"} />
|
|
1276
1406
|
<GlobeIcon />
|
|
1277
1407
|
</div>
|
|
1278
|
-
|
|
1408
|
+
<div className="bcard__top-right">
|
|
1409
|
+
{team.is_trial && <span className="bcard__badge">trial</span>}
|
|
1410
|
+
{billTeamId != null && (
|
|
1411
|
+
<button
|
|
1412
|
+
type="button"
|
|
1413
|
+
data-id="TeamCard-billing"
|
|
1414
|
+
className="bcard__billing-btn"
|
|
1415
|
+
title={tr("localTeams.billing", "账单")}
|
|
1416
|
+
onClick={(e) => { e.stopPropagation(); openCloudPage(`?team=${encodeURIComponent(billTeamId)}`); }}
|
|
1417
|
+
>
|
|
1418
|
+
{tr("localTeams.billing", "账单")}
|
|
1419
|
+
</button>
|
|
1420
|
+
)}
|
|
1421
|
+
</div>
|
|
1279
1422
|
</div>
|
|
1280
1423
|
<div className="bcard__body">
|
|
1281
1424
|
<h3 className="bcard__name" title={team.title}>{team.title}</h3>
|
|
@@ -1323,10 +1466,13 @@ function Brand() {
|
|
|
1323
1466
|
}
|
|
1324
1467
|
|
|
1325
1468
|
function BrandGlyph() {
|
|
1469
|
+
// New CiCy mark (六芒星). Rendered white here because it sits on the brand
|
|
1470
|
+
// chip's blue→violet gradient square; the full-color gradient version is the
|
|
1471
|
+
// app/favicon icon. Path matches build/icon.svg (viewBox 0 0 96 96).
|
|
1326
1472
|
return (
|
|
1327
|
-
<svg width="22" height="22" viewBox="0 0
|
|
1328
|
-
<path d="
|
|
1329
|
-
|
|
1473
|
+
<svg width="22" height="22" viewBox="0 0 96 96" fill="none">
|
|
1474
|
+
<path d="M48 11L39.5 33.3L16 29.5L31 48L16 66.5L39.5 62.7L48 85L56.5 62.7L80 66.5L65 48L80 29.5L56.5 33.3Z"
|
|
1475
|
+
fill="white" stroke="white" strokeWidth="8" strokeLinejoin="round" strokeLinecap="round" />
|
|
1330
1476
|
</svg>
|
|
1331
1477
|
);
|
|
1332
1478
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*{box-sizing:border-box}html,body,#root{margin:0;height:100%}body{font-family:-apple-system,BlinkMacSystemFont,PingFang SC,Segoe UI,Roboto,Helvetica Neue,sans-serif;background:#07080c;color:#e5e7eb;-webkit-font-smoothing:antialiased}.shell{position:relative;min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-app-region:drag}.shell--app{align-items:stretch;justify-content:stretch;flex-direction:row;background:linear-gradient(180deg,#0b0d13,#07080c);overflow:hidden;-webkit-app-region:no-drag}.shell__left{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:auto}.helper-aside{flex:0 0 auto;position:relative;background:#0f1115;border-left:1px solid rgba(255,255,255,.06);display:flex;flex-direction:column;min-width:320px;z-index:1}.helper-aside,.helper-aside *,.shell--app .main{-webkit-app-region:no-drag}.shell--app .topbar{-webkit-app-region:drag}.shell--app .topbar .user-chip,.shell--app .topbar .user-chip *,.shell--app .topbar .btn-ghost{-webkit-app-region:no-drag}.glow{position:absolute;top:-40%;right:-40%;bottom:-40%;left:-40%;z-index:0;pointer-events:none;background:radial-gradient(closest-side,rgba(91,141,247,.2),transparent 60%),radial-gradient(closest-side at 30% 70%,rgba(167,139,250,.13),transparent 65%);filter:blur(30px)}.card{-webkit-app-region:no-drag;position:relative;z-index:1;width:380px;padding:36px 36px 28px;background:linear-gradient(180deg,#141820d9,#0d1016d9);border:1px solid rgba(255,255,255,.06);border-radius:16px;box-shadow:0 1px #ffffff0a inset,0 30px 60px #0006;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);display:flex;flex-direction:column;align-items:center;gap:18px}.brand{display:flex;align-items:center;gap:12px;align-self:stretch}.brand-mark{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);box-shadow:0 8px 20px #5b8df759}.brand-mark.sm{width:28px;height:28px;border-radius:8px;box-shadow:none}.brand-mark.sm svg{width:16px;height:16px}.brand-text{line-height:1.2}.brand-name{font-weight:600;font-size:15px}.brand-sub{font-size:12px;color:#9ca3af;margin-top:2px}.tagline{margin:4px 0 0;color:#c7cdd6;font-size:13.5px;text-align:center;line-height:1.55}.btn-primary{-webkit-app-region:no-drag;margin-top:4px;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:42px;display:inline-flex;align-items:center;justify-content:center;gap:8px;background:linear-gradient(180deg,#5b8df7,#4570d8);color:#fff;border:0;border-radius:10px;font-size:14px;font-weight:500;letter-spacing:.2px;cursor:pointer;box-shadow:0 1px #ffffff2e inset,0 -1px #0000002e inset,0 10px 22px #5b8df747;transition:transform 80ms ease,filter .12s ease}.btn-primary:hover{filter:brightness(1.08)}.btn-primary:active{transform:translateY(1px)}.btn-primary:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.btn-ghost{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:6px 14px;font-size:12.5px;cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.btn-ghost.sm{padding:4px 10px;font-size:11.5px}.btn-ghost:hover{color:#e5e7eb;background:#ffffff0a;border-color:#ffffff24}.btn-ghost:disabled{opacity:.4;cursor:default}.hint{margin:0;font-size:11.5px;color:#6b7280}.spinner-row{display:inline-flex;align-items:center;gap:8px;color:#c7cdd6;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error{width:100%;font-size:12px;color:#fca5a5;background:#ef444414;border:1px solid rgba(239,68,68,.18);padding:8px 12px;border-radius:8px;text-align:center;line-height:1.5}.topbar{-webkit-app-region:drag;position:sticky;top:0;z-index:10;display:flex;align-items:center;justify-content:space-between;padding:14px 24px 12px;background:#08090e99;border-bottom:1px solid rgba(255,255,255,.05);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}[data-platform=darwin][data-fullscreen="0"] .topbar{padding-left:84px}.brand-mini{display:inline-flex;align-items:center;gap:10px}.brand-mini .brand-name{font-size:14px}.user-chip{-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:10px}.welcome{font-size:12px;color:#34d399;padding:4px 10px;border-radius:999px;background:#10b9811a;border:1px solid rgba(16,185,129,.25);animation:fadein .2s ease}@keyframes fadein{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}.avatar{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);font-size:12px;font-weight:600;color:#fff}.user-name{font-size:13px;color:#c7cdd6}.glow--app{inset:-10% -10% auto -10%;height:50vh;background:radial-gradient(closest-side at 75% 0%,rgba(91,141,247,.18),transparent 65%),radial-gradient(closest-side at 20% 10%,rgba(167,139,250,.1),transparent 60%);filter:blur(40px)}.main{position:relative;z-index:1;padding:22px 32px 48px;width:100%;display:flex;flex-direction:column;gap:16px}.app__tabs{display:inline-flex;align-items:center;gap:4px;padding:4px;background:#1418208c;border:1px solid rgba(255,255,255,.06);border-radius:10px;align-self:flex-start;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.app__tab{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:0;border-radius:7px;padding:6px 14px;font-size:12.5px;font-weight:500;letter-spacing:.15px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:color .12s ease,background .12s ease}.app__tab:hover{color:#e5e7eb;background:#ffffff0a}.app__tab.is-active{color:#fff;background:#5b8df72e;box-shadow:0 0 0 1px #5b8df74d inset}.app__tab-count{font-size:10.5px;color:#9ca3af;background:#ffffff0f;padding:1px 6px;border-radius:999px;min-width:18px;text-align:center}.app__tab.is-active .app__tab-count{background:#5b8df74d;color:#fff}.app__grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.add-card{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:200px;height:200px;background:transparent;border:1.5px dashed rgba(255,255,255,.12);border-radius:14px;color:#9ca3af;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;transition:border-color .16s ease,color .16s ease,background .16s ease}.add-card:hover{border-color:#5b8df780;color:#e5e7eb;background:#5b8df70d}.add-card__plus{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;background:#ffffff0a;font-size:22px;font-weight:300;color:inherit}.add-card__label{font-size:13px;font-weight:500}.bcard--helper{background:linear-gradient(160deg,#5b8df729,#a78bfa24 60%,#1418208c);border-color:#a78bfa40}.bcard--helper .bcard__accent{background:linear-gradient(90deg,#5b8df7,#a78bfa);opacity:1}.bcard--helper:hover{border-color:#a78bfa73}.bcard__pill--helper{background:#a78bfa2e;border-color:#a78bfa4d;color:#c4b5fd;font-size:11px;font-weight:600;letter-spacing:.2px}.bcard__helper-icon{font-size:12px;filter:grayscale(.2)}.bcard__badge--free{background:#34d39926;color:#34d399;border:1px solid rgba(52,211,153,.3)}.bcard__badge--trial{background:#fbbf2426;color:#fcd34d;border:1px solid rgba(251,191,36,.3);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard__badge--local{background:#5b8df72e;color:#a5c4ff;border:1px solid rgba(91,141,247,.35);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard--helper .bcard__name{color:#f3f4f6}.bcard__desc{margin:4px 0 0;font-size:11.5px;line-height:1.5;color:#c7cdd6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.bcard__fineprint{margin:4px 0 0;font-size:10.5px;color:#9ca3af;opacity:.8}.bcard__cta--helper{background:linear-gradient(90deg,#5b8df7,#a78bfa);box-shadow:0 6px 18px -4px #a78bfa73}.bcard__cta--helper:hover{filter:brightness(1.1)}.helper-aside__top{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 10px;border-bottom:1px solid rgba(255,255,255,.05)}.helper-aside__title{font-size:12.5px;font-weight:600;color:#c7cdd6}.helper-aside__close{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:#6b7280;font-size:20px;line-height:1;width:26px;height:26px;border-radius:6px;cursor:pointer;transition:color .12s ease,background .12s ease}.helper-aside__close:hover{color:#e5e7eb;background:#ffffff0f}.helper-modal__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:transparent;z-index:20;animation:fadein .14s ease;padding:16px;pointer-events:auto}.helper-modal{width:min(360px,100%);background:linear-gradient(180deg,#1f2733,#1a2029);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:20px 22px 16px;color:#e5e7eb;box-shadow:0 24px 48px -12px #0009,0 1px #ffffff14 inset;animation:fadein .18s ease}.helper-modal__title{font-size:14px;font-weight:600;color:#f3f4f6;margin-bottom:8px}.helper-modal__desc{font-size:12.5px;line-height:1.6;color:#b6bcc6;margin-bottom:18px}.helper-modal__desc code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11.5px;padding:1px 6px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);border-radius:4px;color:#e7eaef}.helper-modal__actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.helper-modal__btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#ffffff0f;color:#d2d6dd;border:1px solid rgba(255,255,255,.1);border-radius:7px;padding:7px 14px;font-size:12.5px;font-weight:500;cursor:pointer;transition:background .12s ease,color .12s ease,transform 80ms ease}.helper-modal__btn:hover{background:#ffffff1a;color:#fff}.helper-modal__btn:active{transform:translateY(1px)}.helper-modal__btn--ghost{background:transparent;border-color:transparent;color:#ffffff8c;margin-right:auto;padding-left:4px;padding-right:4px}.helper-modal__btn--ghost:hover{background:transparent;color:#ffffffd9}.helper-modal__btn--primary{background:linear-gradient(180deg,#5b8df7,#4570d8);border-color:#ffffff2e;color:#fff}.helper-modal__btn--primary:hover{background:linear-gradient(180deg,#6c9af9,#5a82e0)}.helper-modal__btn:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.helper-placeholder{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 40px;text-align:center;color:#9ca3af}.helper-placeholder__mark{font-size:48px;line-height:1;filter:grayscale(.2);opacity:.85}.helper-placeholder__title{margin:0;font-size:16px;font-weight:600;color:#e5e7eb}.helper-placeholder__sub{margin:0;font-size:12.5px;line-height:1.6;color:#9ca3af;max-width:320px}.helper-placeholder__note{margin-top:8px;font-size:11px;color:#6b7280;padding:4px 10px;background:#ffffff08;border:1px solid rgba(255,255,255,.06);border-radius:999px}.helper-placeholder__note code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#c7cdd6}.section{background:linear-gradient(180deg,#141820b8,#0d1016b8);border:1px solid rgba(255,255,255,.06);border-radius:14px;box-shadow:0 1px #ffffff0a inset,0 14px 30px #00000052;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px 18px 16px}.section-head{display:flex;align-items:center;gap:8px;padding:0 4px 14px;border-bottom:1px solid rgba(255,255,255,.04);margin-bottom:14px}.section-icon{font-size:14px;opacity:.8}.section-head h2{margin:0;font-size:13.5px;font-weight:600;color:#e5e7eb;letter-spacing:.2px}.section-sub{font-size:12px;color:#6b7280}.section-body{display:flex;flex-direction:column;gap:10px}.grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.bcard{position:relative;width:200px;height:200px;background:#1418208c;border:1px solid rgba(255,255,255,.07);border-radius:14px;padding:16px;display:flex;flex-direction:column;overflow:hidden;transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease;--brand: #5b8df7;--accent-cloud: #f59e0b;--accent-custom: #8b5cf6}.bcard:hover{transform:translateY(-2px);border-color:#ffffff24;box-shadow:0 12px 32px -10px #00000073,0 1px #ffffff0a inset}.bcard__accent{position:absolute;top:0;left:0;right:0;height:3px;background:var(--brand);opacity:.9}.bcard--cloud .bcard__accent{background:var(--accent-cloud)}.bcard--custom .bcard__accent{background:var(--accent-custom)}.bcard--online:before{content:"";position:absolute;bottom:-40%;right:-20%;width:140%;height:100%;background:radial-gradient(circle at center,rgba(91,141,247,.22) 0%,transparent 60%);pointer-events:none;opacity:.55}.bcard--cloud.bcard--online:before{background:radial-gradient(circle at center,rgba(245,158,11,.22) 0%,transparent 60%)}.bcard--custom.bcard--online:before{background:radial-gradient(circle at center,rgba(139,92,246,.22) 0%,transparent 60%)}.bcard__top{display:flex;align-items:center;justify-content:space-between;position:relative;z-index:5}.bcard__pill{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;background:#08090eb3;border:1px solid rgba(255,255,255,.07);border-radius:999px;color:#9ca3af}.bcard__pill svg{display:block}.bcard__dot{width:7px;height:7px;border-radius:50%;background:#6b7280;flex-shrink:0}.bcard__dot[data-tone=ok]{background:#34d399;box-shadow:0 0 0 2px #34d39940}.bcard__dot[data-tone=off]{background:#6b7280}.bcard__dot[data-tone=warn]{background:#fbbf24;box-shadow:0 0 0 2px #fbbf2440}.bcard__dot[data-tone=err]{background:#f87171;box-shadow:0 0 0 2px #f8717140}.bcard__badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#fbbf241f;color:#fbbf24;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.bcard__body{flex:1;display:flex;flex-direction:column;gap:4px;padding-top:12px;padding-bottom:12px;position:relative;z-index:1;min-width:0}.bcard__name{margin:0;font-size:17px;font-weight:700;color:#e5e7eb;letter-spacing:-.015em;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__host{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#9ca3af;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}.bcard__chip{font-size:10.5px;color:#9ca3af;background:#ffffff08;border:1px solid rgba(255,255,255,.06);padding:2px 7px;border-radius:999px}.bcard__cta{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;height:38px;border:0;border-radius:9px;background:var(--brand);color:#fff;font-size:13px;font-weight:600;letter-spacing:.1px;cursor:pointer;transition:transform 80ms ease,background .16s ease,box-shadow .16s ease,filter .16s ease;box-shadow:0 4px 12px -2px #5b8df773}.bcard--cloud .bcard__cta{background:var(--accent-cloud);box-shadow:0 4px 12px -2px #f59e0b73}.bcard--custom .bcard__cta{background:var(--accent-custom);box-shadow:0 4px 12px -2px #8b5cf673}.bcard__cta:hover{filter:brightness(1.08)}.bcard__cta:active{transform:translateY(1px)}.bcard__cta:disabled{background:#ffffff08;color:#6b7280;cursor:not-allowed;box-shadow:none;border:1px solid rgba(255,255,255,.06)}.empty{font-size:12.5px;color:#6b7280;padding:14px 16px;background:#ffffff05;border:1px dashed rgba(255,255,255,.08);border-radius:10px}.bcard__menuwrap{position:relative}.bcard__kebab{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid transparent;border-radius:8px;background:transparent;color:#8b949e;cursor:pointer;transition:.15s}.bcard__kebab:hover:not(:disabled){color:#e6edf3;background:#ffffff0f;border-color:#ffffff1f}.bcard__kebab:disabled{opacity:.5;cursor:default}.bcard__kebab.has-dot{color:#e6edf3}.bcard__kebab.has-dot:after{content:"";position:absolute;top:3px;right:3px;width:7px;height:7px;border-radius:50%;background:#f59e0b;box-shadow:0 0 0 2px #0f1115}.bcard__menu{position:absolute;top:32px;right:0;z-index:20;min-width:150px;padding:5px;background:#1b2027;border:1px solid rgba(255,255,255,.12);border-radius:10px;box-shadow:0 10px 30px #00000073;display:flex;flex-direction:column;gap:2px}.bcard__menu-item{text-align:left;width:100%;border:none;background:transparent;color:#d1d5db;border-radius:7px;padding:7px 10px;font-size:12.5px;cursor:pointer;transition:.12s}.bcard__menu-item:hover{background:#ffffff12;color:#fff}.bcard__menu-item.is-accent{color:#fbbf24;font-weight:600}.bcard__menu-item.is-accent:hover{background:#f59e0b26;color:#fcd34d}.bcard__menu-item.is-danger{color:#f7a3a3}.bcard__menu-item.is-danger:hover{background:#ef444429;color:#fff}.docker-setup{margin-bottom:14px;padding:14px 16px;background:linear-gradient(180deg,#5b8df714,#14182080);border:1px solid rgba(91,141,247,.22);border-radius:12px}.docker-setup__head{display:flex;flex-direction:column;gap:3px;margin-bottom:12px}.docker-setup__title{font-size:13.5px;font-weight:600;color:#e6edf3}.docker-setup__sub{font-size:11.5px;color:#9ca3af;line-height:1.5}.docker-setup__steps{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.docker-step{display:flex;align-items:center;gap:8px;font-size:12px;color:#9ca3af}.docker-step__dot{width:8px;height:8px;border-radius:50%;flex:none;background:#3a4150;transition:.2s}.docker-step__label{color:#c7cdd6;min-width:96px}.docker-step__msg{color:#8b949e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.docker-step__pct{margin-left:auto;font-variant-numeric:tabular-nums;color:#5b8df7;flex:none}.docker-step.is-running .docker-step__dot{background:#5b8df7;box-shadow:0 0 0 3px #5b8df72e;animation:dpulse 1.2s ease-in-out infinite}.docker-step.is-running .docker-step__label{color:#e6edf3}.docker-step.is-done .docker-step__dot,.docker-step.is-skip .docker-step__dot{background:#10b981}.docker-step.is-skip{opacity:.7}.docker-step.is-retry .docker-step__dot{background:#f59e0b}.docker-step.is-error .docker-step__dot{background:#ef4444;box-shadow:0 0 0 3px #ef444426}.docker-step.is-error .docker-step__msg{color:#f7a3a3}@keyframes dpulse{0%,to{opacity:1}50%{opacity:.45}}.docker-setup__actions{display:flex;gap:8px;align-items:center}.docker-setup__actions .btn-primary{width:auto;padding:7px 16px;font-size:12.5px}.bcard__ver{font-size:11px;color:#8b949e;font-variant-numeric:tabular-nums}.bcard__chip--new{color:#fbbf24;background:#f59e0b1f;border-color:#f59e0b4d}.bcard__opmsg{display:flex;align-items:center;gap:6px;margin-top:9px;font-size:11.5px;color:#9ca3af;word-break:break-word}.toast-host{position:fixed;right:16px;bottom:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;max-width:min(360px,calc(100vw - 32px));pointer-events:none}.toast{pointer-events:auto;position:relative;display:flex;flex-direction:column;gap:6px;padding:10px 30px 10px 12px;font-size:12.5px;line-height:1.4;color:var(--text, #e6eaf0);background:#161a21f5;border:1px solid rgba(125,135,150,.22);border-left:3px solid var(--accent, #3b82f6);border-radius:10px;box-shadow:0 8px 28px #0000006b;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);animation:toast-in .18s ease}@keyframes toast-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.toast[data-status=error]{border-left-color:#f87171}.toast[data-status=error] .toast__msg{color:#f87171}.toast[data-status=done]{border-left-color:#4ade80}.toast[data-status=done] .toast__msg{color:#4ade80}.toast__msg{word-break:break-word}.toast__x{position:absolute;top:6px;right:8px;background:none;border:none;cursor:pointer;padding:0;font-size:15px;line-height:1;color:var(--text-dim, #9da7b3)}.toast__x:hover{color:var(--text, #e6eaf0)}.toast__bar{display:block;height:4px;border-radius:2px;background:#7d879640;overflow:hidden}.toast__bar>span{display:block;height:100%;border-radius:2px;background:var(--accent, #3b82f6);transition:width .25s ease}.drawer-scrim{position:fixed;top:0;right:0;bottom:0;left:0;z-index:10000;display:flex;align-items:flex-end;justify-content:center;background:#06080c80;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:drawer-fade .18s ease}@keyframes drawer-fade{0%{opacity:0}to{opacity:1}}.drawer{width:min(560px,calc(100vw - 24px));max-height:76vh;display:flex;flex-direction:column;margin-bottom:12px;background:#14181ffa;border:1px solid rgba(125,135,150,.2);border-radius:16px 16px 12px 12px;box-shadow:0 -10px 44px #00000080;overflow:hidden;animation:drawer-up .24s cubic-bezier(.22,1,.36,1)}@keyframes drawer-up{0%{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}.drawer__head{display:flex;align-items:center;justify-content:space-between;padding:14px 14px 12px 16px;border-bottom:1px solid rgba(125,135,150,.14)}.drawer__title{display:flex;align-items:center;gap:11px}.drawer__spark{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:8px;font-size:14px;font-weight:700;background:#5b8df729;color:var(--brand, #5b8df7)}.drawer__spark--done{background:#4ade8029;color:#4ade80}.drawer__spark--error{background:#f8717129;color:#f87171}.drawer__h{font-size:13.5px;font-weight:650;color:var(--text, #e6eaf0)}.drawer__sub{font-size:11.5px;color:var(--text-dim, #9da7b3);margin-top:1px}.drawer__x{background:none;border:none;cursor:pointer;padding:2px 6px;font-size:19px;line-height:1;color:var(--text-dim, #9da7b3);border-radius:6px}.drawer__x:hover:not(:disabled){color:var(--text, #e6eaf0);background:#7d87961f}.drawer__x:disabled{opacity:.35;cursor:default}.drawer__steps{display:flex;align-items:center;gap:0;padding:14px 18px 4px}.drawer__step{display:flex;align-items:center;gap:7px;color:var(--text-dim, #9da7b3);font-size:12px}.drawer__step-dot{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;font-size:11px;font-weight:700;border:1.5px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);background:transparent;flex:none}.drawer__step-bar{width:30px;height:1.5px;background:#7d879640;margin:0 8px}.drawer__step.is-active .drawer__step-dot{border-color:var(--brand, #5b8df7);color:var(--brand, #5b8df7);box-shadow:0 0 0 3px #5b8df72e}.drawer__step.is-active .drawer__step-label{color:var(--text, #e6eaf0)}.drawer__step.is-done .drawer__step-dot{border-color:#4ade80;color:#06210f;background:#4ade80}.drawer__step.is-done .drawer__step-bar{background:#4ade8080}.drawer__step.is-error .drawer__step-dot{border-color:#f87171;color:#f87171}.drawer__log{flex:1;min-height:96px;overflow-y:auto;margin:10px 14px;padding:10px 12px;background:#0a0c10b3;border:1px solid rgba(125,135,150,.12);border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.7}.drawer__log-empty{color:var(--text-dim, #9da7b3)}.drawer__line{display:flex;align-items:baseline;gap:8px;padding:1px 0}.drawer__t{color:#6b7686;flex:none;font-variant-numeric:tabular-nums}.drawer__badge{flex:none;padding:0 6px;border-radius:4px;font-size:10px;font-weight:600;background:#5b8df729;color:#8fb0f5}.drawer__badge--swap{background:#f59e0b29;color:#f5b342}.drawer__badge--done{background:#4ade8029;color:#6ee79b}.drawer__linemsg{color:var(--text, #e6eaf0);word-break:break-word}.drawer__line[data-status=error] .drawer__linemsg{color:#f87171}.drawer__line[data-status=done] .drawer__linemsg{color:#6ee79b}.drawer__line[data-status=skip] .drawer__linemsg{color:var(--text-dim, #9da7b3)}.drawer__hint{margin:0 14px 8px;padding:8px 12px;background:#f59e0b1a;border:1px solid rgba(245,158,11,.28);border-radius:8px;color:#f5b342;font-size:11.5px;line-height:1.5}.drawer__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid rgba(125,135,150,.14)}.drawer__foot-status{font-size:12.5px;color:var(--text-dim, #9da7b3);margin-right:auto}.drawer__foot-status.is-error{color:#f87171}.drawer__foot-status.is-done{color:#4ade80}.drawer__btn{padding:7px 16px;border-radius:8px;cursor:pointer;font-size:12.5px;font-weight:550;background:#7d879624;border:1px solid rgba(125,135,150,.2);color:var(--text, #e6eaf0)}.drawer__btn:hover{background:#7d879638}.drawer__btn.is-accent{background:var(--brand, #5b8df7);border-color:var(--brand, #5b8df7);color:#fff}.drawer__btn.is-accent:hover{filter:brightness(1.08)}.mitm-card{border:1px solid rgba(125,135,150,.22);border-radius:12px;padding:14px 16px;margin-bottom:14px;background:#1e242e66}.mitm-card--on{border-color:#4ade8059;background:#16281e66}.mitm-pill{position:fixed;top:64px;right:16px;z-index:9998;-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:8px;padding:4px 6px 4px 11px;border-radius:999px;width:fit-content;max-width:calc(100vw - 32px);background:#141e18d9;border:1px solid rgba(74,222,128,.3);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 4px 14px #0000004d;font-size:12px;line-height:1}.mitm-pill__dot{width:7px;height:7px;border-radius:50%;flex:none;background:#4ade80;box-shadow:0 0 0 3px #4ade8029}.mitm-pill__dot[data-busy="1"]{background:#9da7b3;box-shadow:0 0 0 3px #9da7b329}.mitm-pill__text{color:var(--text-dim, #9da7b3);white-space:nowrap}.mitm-pill__off{background:none;border:none;cursor:pointer;color:#6b7686;font-size:11.5px;padding:3px 7px;border-radius:999px}.mitm-pill__off:hover{color:#f87171;background:#f871711a}.mitm-card__head{display:flex;align-items:center;gap:8px;margin-bottom:6px}.mitm-card__dot{width:8px;height:8px;border-radius:50%;background:#9da7b3;flex:0 0 auto}.mitm-card__dot[data-state=on]{background:#4ade80;box-shadow:0 0 6px #4ade8099}.mitm-card__dot[data-state=warn]{background:#fbbf24}.mitm-card__dot[data-state=off]{background:#60a5fa}.mitm-card__title{font-size:14px;font-weight:600;color:var(--text, #e6edf3)}.mitm-card__desc{font-size:12.5px;line-height:1.5;color:var(--text-dim, #9da7b3);margin:0 0 10px}.mitm-card__note{color:#fbbf24}.mitm-card__error{font-size:12px;color:#f87171;margin-bottom:8px}.mitm-card__actions{display:flex;gap:8px}.mitm-card__btn{font-size:13px;padding:7px 16px;border-radius:8px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:500}.mitm-card__btn:disabled{opacity:.6;cursor:default}.mitm-card__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3)}.mitm-card__sub{color:var(--text-dim, #9da7b3);opacity:.8;font-size:11.5px}.terms-gate{display:flex;align-items:center;justify-content:center;padding:24px}.terms-gate__panel,.terms-gate__panel *{-webkit-app-region:no-drag}.terms-gate__panel{position:relative;z-index:1;width:min(680px,94vw);max-height:90vh;display:flex;flex-direction:column;background:#141921eb;border:1px solid rgba(125,135,150,.22);border-radius:16px;padding:28px 30px;box-shadow:0 24px 60px #00000080}.terms-gate__title{font-size:20px;font-weight:700;margin:0 0 4px;color:var(--text, #e6edf3)}.terms-gate__subtitle{font-size:13px;color:var(--text-dim, #9da7b3);margin:0 0 16px}.terms-gate__body{overflow-y:auto;flex:1 1 auto;min-height:0;padding-right:8px;border-top:1px solid rgba(125,135,150,.15);border-bottom:1px solid rgba(125,135,150,.15);padding-top:14px;padding-bottom:14px}.terms-gate__h2{font-size:14px;font-weight:600;margin:0 0 10px;color:var(--text, #e6edf3)}.terms-gate__summary{margin:0 0 14px;padding-left:20px}.terms-gate__summary li{font-size:13px;line-height:1.6;color:var(--text-dim, #c2cbd6);margin-bottom:8px}.terms-gate__viewfull{background:none;border:none;color:var(--accent, #3b82f6);cursor:pointer;font-size:13px;padding:4px 0;text-decoration:underline}.terms-gate__fulltext{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.6;color:var(--text-dim, #b3bcc8);background:#0003;border-radius:8px;padding:14px;margin:10px 0 0;font-family:inherit}.terms-gate__scrollhint{font-size:12px;color:#fbbf24;text-align:center;margin:12px 0 0}.terms-gate__actions{display:flex;gap:12px;justify-content:flex-end;margin-top:18px}.terms-gate__btn{font-size:14px;padding:10px 22px;border-radius:9px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:600}.terms-gate__btn:disabled{opacity:.45;cursor:not-allowed}.terms-gate__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);font-weight:500}
|