cicy-desktop 2.1.49 → 2.1.51

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.
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>CiCy Desktop</title>
7
- <script type="module" crossorigin src="./assets/index-CLpXv2cd.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/index-BowhPJHl.css">
7
+ <script type="module" crossorigin src="./assets/index-BPk6QOob.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-BN_rniiJ.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -671,6 +671,12 @@ body {
671
671
  .bcard__menu-item.is-accent:hover { background: rgba(245,158,11,.15); color: #fcd34d; }
672
672
  .bcard__menu-item.is-danger { color: #f7a3a3; }
673
673
  .bcard__menu-item.is-danger:hover { background: rgba(239,68,68,.16); color: #fff; }
674
+ /* version from /api/health 探活 */
675
+ .bcard__ver {
676
+ font-size: 11px;
677
+ color: #8b949e;
678
+ font-variant-numeric: tabular-nums;
679
+ }
674
680
  /* "新版 vX.Y.Z" chip on the card face */
675
681
  .bcard__chip--new {
676
682
  color: #fbbf24;
@@ -320,9 +320,16 @@ export default function App() {
320
320
 
321
321
  // Logged in: unified tabs + cards grid on the left, full-height webview
322
322
  // drawer on the right.
323
- const localCount = (localTeams || []).length;
323
+ // Split the cicyDesktopNodes list into 本地 (the localhost:8008 sidecar the
324
+ // desktop owns — full lifecycle) vs 自定义 (deeplink-added nodes, usually
325
+ // remote — probe-only, no restart/stop/update, just 打开).
326
+ const localList = (localTeams || []).filter((t) => isLocalSidecar(t.base_url));
327
+ const customList = (localTeams || []).filter((t) => !isLocalSidecar(t.base_url));
328
+ const localCount = localList.length;
329
+ const customCount = customList.length;
324
330
  const cloudCount = (teams || []).length;
325
331
  const showLocal = tab === "all" || tab === "local";
332
+ const showCustom = tab === "all" || tab === "custom";
326
333
  const showCloud = tab === "all" || tab === "cloud";
327
334
 
328
335
  return (
@@ -333,9 +340,10 @@ export default function App() {
333
340
  <main className="main">
334
341
  <div className="app__tabs">
335
342
  {[
336
- { k: "all", label: "全部", n: localCount + cloudCount },
337
- { k: "local", label: "本地", n: localCount },
338
- { k: "cloud", label: "云端", n: cloudCount },
343
+ { k: "all", label: "全部", n: localCount + customCount + cloudCount },
344
+ { k: "local", label: "本地", n: localCount },
345
+ { k: "cloud", label: "云端", n: cloudCount },
346
+ { k: "custom", label: "自定义", n: customCount },
339
347
  ].map(({ k, label, n }) => (
340
348
  <button
341
349
  key={k}
@@ -359,9 +367,12 @@ export default function App() {
359
367
  )}
360
368
 
361
369
  <div className="app__grid">
362
- {showLocal && localTeams && localTeams.map((t) => (
370
+ {showLocal && localList.map((t) => (
363
371
  <LocalTeamCard key={"local:" + t.id} team={t} onOpen={() => openLocalTeam(t.id)} onRename={renameLocalTeam} onRefresh={fetchLocalTeams} />
364
372
  ))}
373
+ {showCustom && customList.map((t) => (
374
+ <LocalTeamCard key={"custom:" + t.id} team={t} onOpen={() => openLocalTeam(t.id)} onRename={renameLocalTeam} onRefresh={fetchLocalTeams} />
375
+ ))}
365
376
  {showCloud && teams && teams.map((t) => (
366
377
  <TeamCard
367
378
  key={"cloud:" + t.id}
@@ -439,11 +450,13 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
439
450
  if (onRename && next && next !== team.name) await onRename(team.id, next);
440
451
  };
441
452
 
442
- // The local cicy-code daemon (the :8008 sidecar) that backs this team:
443
- // 启动 / 重启 / 更新 / 停止. 打开 stays the one primary action — daemon
444
- // maintenance lives in a menu so it never competes for attention. Only on
445
- // a desktop build whose bridge owns the daemon.
446
- const hasOps = !!window.cicy?.sidecar?.restart;
453
+ // Lifecycle (启动 / 重启 / 更新 / 停止) acts on the daemon the desktop OWNS —
454
+ // localhost on the sidecar port (:8008). A remote node or a non-8008 port
455
+ // can't be controlled from here (sidecar.* would hit the wrong, local :8008),
456
+ // so those cards get 打开 only no ⋯ menu, no update prompt. 打开 stays the
457
+ // one primary action; maintenance lives in the ⋯ menu.
458
+ const hasBridge = !!window.cicy?.sidecar?.restart;
459
+ const local = hasBridge && isLocalSidecar(team.base_url);
447
460
  const running = team.status === "running";
448
461
  const [busy, setBusy] = useState(""); // "" | start | restart | update | stop
449
462
  const [opMsg, setOpMsg] = useState("");
@@ -455,17 +468,17 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
455
468
  // exists (no nagging when current). Renderer-side via cloud.fetch — main
456
469
  // proxies it, dodging CORS; no extra IPC needed.
457
470
  useEffect(() => {
458
- if (!hasOps || !window.cicy?.cloud?.fetch) return;
471
+ if (!local || !window.cicy?.cloud?.fetch) return;
459
472
  let alive = true;
460
473
  window.cicy.cloud
461
474
  .fetch("https://registry.npmmirror.com/cicy-code/latest")
462
475
  .then((r) => { if (alive && r?.ok) { try { setLatest(JSON.parse(r.body)?.version || null); } catch {} } })
463
476
  .catch(() => {});
464
477
  return () => { alive = false; };
465
- }, [hasOps]);
478
+ }, [local]);
466
479
 
467
- const updateAvailable = !!(latest && team.version && cmpVer(latest, team.version) > 0);
468
- const showMenu = hasOps && (running || updateAvailable);
480
+ const updateAvailable = !!(local && latest && team.version && cmpVer(latest, team.version) > 0);
481
+ const showMenu = local && (running || updateAvailable);
469
482
 
470
483
  useEffect(() => {
471
484
  if (!menuOpen) return;
@@ -492,18 +505,13 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
492
505
  };
493
506
  const BUSY_LABEL = { start: "启动中…", restart: "重启中…", update: "更新中…", stop: "停止中…" };
494
507
 
495
- // Can we bring this daemon up locally? Only the 127.0.0.1:8008 team is the
496
- // sidecar we own — a remote node (or a non-8008 local port) can't be started
497
- // from the desktop, so for those 打开 just opens the window and lets the
498
- // loaded page show its own connecting/login/error UI.
499
- const localSidecar = hasOps && isLocalSidecar(team.base_url);
500
-
501
508
  // 打开 is NEVER gated on /api/health — openTeam() in main doesn't check it,
502
- // it just opens the window. health is an indicator, not a gate. When a local
503
- // daemon is down we start it first; otherwise we open and let the page cope.
509
+ // it just opens the window. health is an indicator, not a gate. When the
510
+ // LOCAL daemon is down we start it first; remote/other-port teams just open
511
+ // and let the loaded page show its own connecting/login/error UI.
504
512
  const handleOpen = async () => {
505
513
  if (busy) return;
506
- if (!running && localSidecar && window.cicy?.sidecar?.start) {
514
+ if (!running && local && window.cicy?.sidecar?.start) {
507
515
  setBusy("start"); setOpMsg("");
508
516
  const r = await window.cicy.sidecar.start().catch((e) => ({ ok: false, error: e?.message || String(e) }));
509
517
  setBusy(""); onRefresh?.();
@@ -516,9 +524,9 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
516
524
  };
517
525
  const openLabel = running
518
526
  ? tr("localTeams.open", "打开")
519
- : localSidecar
520
- ? tr("localTeams.startOpen", "启动并打开")
521
- : tr("localTeams.open", "打开");
527
+ : local
528
+ ? tr("localTeams.startOpen", "启动并打开") // only the local sidecar can be started from here
529
+ : tr("localTeams.open", "打开"); // custom/remote: 探活-only, just open
522
530
  return (
523
531
  <div data-id="LocalTeamCard" className={`bcard bcard--local${tone === "ok" ? " bcard--online" : ""}`}>
524
532
  <div className="bcard__accent" />
@@ -604,8 +612,9 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
604
612
  {team.base_url || "—"}
605
613
  </div>
606
614
  <div className="bcard__meta">
607
- <span className="bcard__chip">{statusInfo.label}</span>
608
- {team.version && <span className="bcard__chip">v{team.version}</span>}
615
+ {team.version && (
616
+ <span className="bcard__ver" data-id="LocalTeamCard-version">v{team.version}</span>
617
+ )}
609
618
  {updateAvailable && (
610
619
  <span
611
620
  className="bcard__chip bcard__chip--new"
@@ -616,11 +625,6 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
616
625
  </span>
617
626
  )}
618
627
  </div>
619
- {(busy || opMsg) && (
620
- <div className="bcard__opmsg" data-id="LocalTeamCard-opmsg">
621
- {busy ? <><Spinner />{BUSY_LABEL[busy] || tr("sidecar.working", "处理中…")}</> : opMsg}
622
- </div>
623
- )}
624
628
  </div>
625
629
  <button
626
630
  type="button"