@ai-ecoverse/slicc-spectrum 1.48.0 → 1.49.1

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/dist/slicc-ui.js CHANGED
@@ -10906,20 +10906,43 @@ function exitNodeValue(tailnet) {
10906
10906
  var linkStates = {
10907
10907
  connecting: ["info", "Connecting"],
10908
10908
  connected: ["positive", "Connected"],
10909
- reconnecting: ["notice", "Reconnecting"]
10910
- };
10909
+ reconnecting: ["notice", "Reconnecting"],
10910
+ offline: ["neutral", "Offline"]
10911
+ };
10912
+ var controlBadges = {
10913
+ prompt: "Prompting",
10914
+ watch: "Watching"
10915
+ };
10916
+ function remoteLight(status2) {
10917
+ const active = (status2?.links?.devices ?? []).filter((device) => device.controlling);
10918
+ if (!active.length) return null;
10919
+ const prompting = active.filter((device) => device.controlling === "prompt");
10920
+ const watching = active.filter((device) => device.controlling === "watch");
10921
+ const say = (devices, verb) => {
10922
+ if (!devices.length) return [];
10923
+ if (devices.length === 1) return [`${devices[0].name} is ${verb} the agent.`];
10924
+ return [`${devices.length} linked devices are ${verb} the agent.`];
10925
+ };
10926
+ const tooltip = [...say(prompting, "sending prompts to"), ...say(watching, "watching")].join(" ");
10927
+ if (prompting.length) return { mode: "prompt", text: "Remote control", tooltip };
10928
+ return { mode: "watch", text: "Watched remotely", tooltip };
10929
+ }
10911
10930
  function linksSummary(links) {
10912
10931
  if (links.permission) return "permission";
10913
10932
  if (!links.devices.length) return "none";
10914
10933
  if (links.devices.some((device) => device.state === "reconnecting")) return "reconnecting";
10915
10934
  if (links.devices.some((device) => device.state === "connecting")) return "connecting";
10935
+ if (links.devices.every((device) => device.state === "offline")) return "offline";
10916
10936
  return "linked";
10917
10937
  }
10918
10938
  function linksLight(links) {
10919
10939
  const summary = linksSummary(links);
10920
10940
  if (summary === "permission") return ["notice", "Needs permission"];
10921
10941
  if (summary === "none") return ["neutral", "None"];
10922
- if (summary === "linked") return ["positive", `${links.devices.length} linked`];
10942
+ if (summary === "linked") {
10943
+ const online = links.devices.filter((device) => device.state !== "offline");
10944
+ return ["positive", `${online.length} linked`];
10945
+ }
10923
10946
  return linkStates[summary];
10924
10947
  }
10925
10948
  function exitValue(exit) {
@@ -11406,6 +11429,36 @@ var SliccNetwork = class extends ModelElement {
11406
11429
  const value = event2.target.value;
11407
11430
  void this.#act("exit", () => port.setExit?.(parseExit(value)), "error");
11408
11431
  }
11432
+ #control(port, device) {
11433
+ const control = device.control;
11434
+ if (!control) return A;
11435
+ if (control.fixed)
11436
+ return b`<span class="muted" data-control="fixed">Remote control is always allowed from this computer.</span>`;
11437
+ if (!port.setRemoteControl) {
11438
+ return b`<span class="muted" data-control=${control.allowed ? "allowed" : "off"}>${control.allowed ? "Remote control allowed." : "Remote control not allowed."}</span>`;
11439
+ }
11440
+ return b`<sp-switch class="control" data-action="remote-control" .checked=${l(control.allowed)} ?disabled=${this.busy !== ""} @change=${(event2) => this.#setControl(port, device, event2.target)}>Allow remote control</sp-switch>`;
11441
+ }
11442
+ async #setControl(port, device, toggle) {
11443
+ const allowed = toggle.checked;
11444
+ if (allowed) {
11445
+ const ok = await confirm({
11446
+ title: `Allow remote control from ${device.name}?`,
11447
+ body: "It can send prompts to the agent, watch its replies and stop it. Unlinking it, or a new certificate on it, turns this off again.",
11448
+ action: "Allow",
11449
+ variant: "confirmation"
11450
+ });
11451
+ if (!ok) {
11452
+ toggle.checked = false;
11453
+ return;
11454
+ }
11455
+ }
11456
+ await this.#act(
11457
+ `control:${device.id}`,
11458
+ () => port.setRemoteControl?.(device.id, allowed),
11459
+ "linksError"
11460
+ );
11461
+ }
11409
11462
  async #unlink(port, status2, device) {
11410
11463
  const isExit = status2.exit?.kind === "link" && status2.exit.id === device.id;
11411
11464
  const ok = await confirm({
@@ -11434,11 +11487,13 @@ var SliccNetwork = class extends ModelElement {
11434
11487
  <span class="name" title=${device.name}>${device.name}</span>
11435
11488
  <swc-status-light size="s" variant=${variant}>${label}</swc-status-light>
11436
11489
  ${isExit ? b`<swc-badge size="s" variant="accent" subtle data-exit-badge>Exit</swc-badge>` : A}
11490
+ ${device.controlling ? b`<swc-badge size="s" variant="informative" subtle data-controlling=${device.controlling}>${controlBadges[device.controlling]}</swc-badge>` : A}
11437
11491
  </div>
11438
11492
  ${port.unlink ? b`<swc-button size="s" variant="secondary" fill-style="outline" data-action="unlink" ?pending=${this.busy === action} ?disabled=${this.#waiting(action)} @click=${() => this.#unlink(port, status2, device)}>Unlink</swc-button>` : A}
11439
11493
  <span class="where"><code>${device.host}</code><span><code>${device.address}</code> · ${device.mode === "local" ? "This computer" : "Remote"}</span></span>
11440
11494
  ${device.policy ? b`<span class="muted" data-policy>Reaches ${device.policy}.</span>` : A}
11441
11495
  ${device.browsers?.length ? b`<span class="muted" data-browsers>${device.browsers.length === 1 ? "Browser" : "Browsers"}: ${device.browsers.map((item2, index) => b`${index ? ", " : ""}${item2.name} (<code>${item2.runtime}</code>)`)}</span>` : A}
11496
+ ${this.#control(port, device)}
11442
11497
  ${device.offers.includes("ssh") ? b`<div class="command" data-copy="ssh"><code>ssh ${device.host}</code>${this.#copyButton(`ssh:${device.id}`, "ssh", "ssh command", `ssh ${device.host}`, "linksError")}</div>` : A}
11443
11498
  </li>`;
11444
11499
  }
@@ -19584,16 +19639,13 @@ function requiresText(item2, all2) {
19584
19639
  const found = all2.find((other) => other.id === id);
19585
19640
  return { label: found?.label ?? id, missing: found?.version === null };
19586
19641
  });
19587
- if (!needs.length) return null;
19588
- if (item2.version === null) {
19589
- const present = needs.filter((need) => !need.missing).map((need) => need.label);
19590
- const missing = needs.filter((need) => need.missing).map((need) => need.label);
19591
- return [
19592
- present.length ? `Needs ${list2(present)}` : "",
19593
- missing.length ? `Installs ${list2(missing)} too` : ""
19594
- ].filter(Boolean).join("; ");
19595
- }
19596
- return `Needs ${list2(needs.map((need) => need.missing ? `${need.label} (not installed)` : need.label))}`;
19642
+ const present = needs.filter((need) => !need.missing).map((need) => need.label);
19643
+ const missing = needs.filter((need) => need.missing).map((need) => need.label);
19644
+ const parts2 = [
19645
+ present.length ? `Uses ${list2(present)}` : "",
19646
+ item2.version === null && missing.length ? `Installs ${list2(missing)} too` : ""
19647
+ ].filter(Boolean);
19648
+ return parts2.length ? parts2.join("; ") : null;
19597
19649
  }
19598
19650
  var snapshot = (items) => new Map(items.map(({ id, state: state2, error }) => [id, { state: state2, error }]));
19599
19651
  function settled(failures, before, after) {
@@ -19830,6 +19882,20 @@ var SliccUpdates = class extends ModelElement {
19830
19882
  line-height: var(--swc-line-height-200);
19831
19883
  overflow-wrap: anywhere;
19832
19884
  }
19885
+ .warnings {
19886
+ display: flex;
19887
+ flex-direction: column;
19888
+ gap: var(--swc-spacing-75);
19889
+ font-size: var(--swc-font-size-75);
19890
+ line-height: var(--swc-line-height-200);
19891
+ overflow-wrap: anywhere;
19892
+ --swc-icon-color: var(--swc-notice-visual-color);
19893
+ }
19894
+ .warnings span {
19895
+ display: flex;
19896
+ align-items: flex-start;
19897
+ gap: var(--swc-spacing-75);
19898
+ }
19833
19899
  .commands {
19834
19900
  display: flex;
19835
19901
  flex-wrap: wrap;
@@ -20066,6 +20132,9 @@ var SliccUpdates = class extends ModelElement {
20066
20132
  <p class="description">${item2.description}</p>
20067
20133
  ${this.#packageMeta(item2, all2)}
20068
20134
  ${item2.notes?.length ? b`<p class="notes" data-notes>${item2.notes.map((note) => b`<span>${note}</span>`)}</p>` : A}
20135
+ ${item2.warnings?.length ? b`<p class="warnings" data-warnings>${item2.warnings.map(
20136
+ (warning) => b`<span><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>${warning}</span>`
20137
+ )}</p>` : A}
20069
20138
  ${item2.commands.length ? b`<p class="commands"><span class="hidden">Commands: </span>${item2.commands.map(
20070
20139
  (command2) => b`<code>${command2}</code>`
20071
20140
  )}</p>` : A}
@@ -20817,6 +20886,16 @@ var SliccApp = class extends ThemedElement {
20817
20886
  </span>
20818
20887
  <swc-tooltip for="tabs" placement="bottom">${light.tooltip}</swc-tooltip>`;
20819
20888
  }
20889
+ #remoteIndicator() {
20890
+ const port = this.model?.network;
20891
+ if (!port || !this.#offers("network")) return A;
20892
+ const light = remoteLight(port.status());
20893
+ if (!light) return A;
20894
+ return b`<swc-action-button id="remote" quiet size="s" data-remote=${light.mode} accessible-label=${`${light.text}. ${light.tooltip}`} @click=${() => this.show("network")}>
20895
+ <swc-status-light size="s" variant="info">${this.screen === "phone" ? A : light.text}</swc-status-light>
20896
+ </swc-action-button>
20897
+ <swc-tooltip for="remote" placement="bottom">${light.tooltip}</swc-tooltip>`;
20898
+ }
20820
20899
  #networkIndicator() {
20821
20900
  const port = this.model?.network;
20822
20901
  if (!port || !this.#offers("network")) return A;
@@ -20853,6 +20932,7 @@ var SliccApp = class extends ThemedElement {
20853
20932
  <div class="status" role="status"><slot name="status"></slot></div>
20854
20933
  ${this.#tabsIndicator()}
20855
20934
  ${this.#updatesIndicator()}
20935
+ ${this.#remoteIndicator()}
20856
20936
  ${this.#networkIndicator()}
20857
20937
  ${this.#offers("chat") ? b`<slicc-tray .model=${this.model} ?compact=${this.screen === "phone"}></slicc-tray>` : A}
20858
20938
  <sp-action-menu size="s" quiet label="View" @change=${this.#view}>