@ai-ecoverse/slicc-spectrum 1.52.0 → 1.53.0
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 +4 -4
- package/dist/slicc-ui.js +207 -189
- package/dist/slicc-ui.js.map +4 -4
- package/dist/types/app/app.d.ts +1 -1
- package/dist/types/app/network.d.ts +5 -0
- package/dist/types/app/panels.d.ts +1 -1
- package/dist/types/app/tray.d.ts +6 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ Things found while building this against wterm 0.5.4, recorded here, not pushed
|
|
|
119
119
|
|
|
120
120
|
### Layouts and rails
|
|
121
121
|
|
|
122
|
-
The layout follows the width of `<slicc-app>`, which it reflects as its `screen` attribute: `phone` (under 640 px) has one column, `tablet` (under 1200 px) two, `desktop` three, like SLICC v6. Each screen class starts with few panels open and saves its own layout (`slicc-ui.layout.v2.<screen>`). Every surface has a home side, left, center or right.
|
|
122
|
+
The layout follows the width of `<slicc-app>`, which it reflects as its `screen` attribute: `phone` (under 640 px) has one column, `tablet` (under 1200 px) two, `desktop` three, like SLICC v6. Each screen class starts with few panels open and saves its own layout (`slicc-ui.layout.v2.<screen>`). Every surface has a home side, left, center or right. Every panel has an icon in the left or right rail (on a phone, in one bar at the bottom, with the rest in its More menu). The icon stays when its panel is open; while the panel is visible it shows selected (`.selected`, `data-visible`, labelled "{Title}, shown"). One click puts a closed panel back on its side, next to its neighbours, or brings an open one to the front. Each thread, a cone's or a scoop's conversation, opens in its own chat panel (`chat:<agent id>`), as a tab in the same group as the other chats. Selecting a thread opens or reveals its tab, activating a chat tab selects its thread, and a thread's chat closes when the thread goes away (frozen or deleted).
|
|
123
123
|
|
|
124
124
|
`surfaces` on `<slicc-app>` (default: every surface, exported as `surfaces`) is what the app offers: the panels it can open, the View menu, the rails and `Alt+1…9`. A saved layout with a panel it doesn't offer is dropped for the default. An embedder can pass a subset with its own `open` per screen class. The agent picker and the tray show only when `chat` is offered, the update status only with `updates`, the network indicator only with `network` and a `network` port, and the settings button only with `settings`. Elements with `slot="status"` show in the header as compact single-line items, before the update status, and are cut with an ellipsis when long; a `hidden` one takes no space. There are no strips under the header. On a phone the status items are hidden.
|
|
125
125
|
|
|
@@ -151,8 +151,8 @@ Text is Adobe Clean Spectrum VF, falling back to Adobe Clean, and code is Spectr
|
|
|
151
151
|
| `<slicc-monitor>` | The live monitor: vitals with sparklines (active agents, spend, budget, fullest context), alerts, and the topology (cones and scoops, terminals, tabs, pending changes, sprinkles, tray followers). |
|
|
152
152
|
| `<slicc-freezer>` | Every conversation in the session, from `frozen()`: cones, scoops and agent runs, each with a kind badge, live ones first with a Live light, then the newest first. A live row offers **Open**, which selects that agent; a stopped one offers **Thaw** and **Remove**, which deletes its working folders after a confirmation. A thawed scoop or agent run keeps its row, which says what it was thawed as. **Delete cone**, in the agents list (or <kbd>Delete</kbd> on a cone's row) and the header's cone picker, stops a cone after a confirmation; `/freeze` does it at once. Either way its conversation stays here, and a row without a `kind` counts as a cone. |
|
|
153
153
|
| `<slicc-sprinkle>` | A SLICC sprinkle (`.shtml`), as a panel or, with `inline`, as a dip in the chat that grows to fit. A dip has a tab-like handle: drag it into the dock (or onto a rail, or use its open-as-panel button) and it lives on as a sprinkle panel, with a note in the chat where it was; which dips are out is saved in `slicc-ui.dips`. It runs in a sandboxed frame (`allow-scripts` only) with SLICC's sprinkle theme (the `--s2-*` tokens and `.sprinkle-*` classes), the app's fonts, and Lucide icons (`<i data-lucide>`, `LucideIcons.render()`). It talks back through `slicc.lick({ action, data })`, which reaches the owning cone as a `sprinkle` lick, and, when the port has `call`, reads files and keeps state (see [Sprinkle bridge](#sprinkle-bridge)). Panel sprinkles sit at the top of the right rail with their Lucide icon. |
|
|
154
|
-
| `<slicc-network>` | Network: the route and its health in plain words with the bios `detail`, a line on browser automation (`data-browser`: through the SLICC extension, through slicc-node, through a linked device's browser, that slicc-node has no browser attached when `route` is `proxy` and `via` is `null`, or not available with how to get it) when the status has `browser`, **Check again** (`data-action="check"`) when the port has `check`, an **Internet traffic** picker (`data-action="exit"`, in `[data-exit-picker]` with the exit's kind) when the port has `setExit`, the route line saying where traffic goes, and under the picker a warning that traffic waits while the chosen device isn't connected (`data-exit="device"`, `data-held`, with **Send directly**, `data-action="exit-direct"`, which calls `setExit(null)`) or, when `route` isn't `tailnet`, the Tailscale exit node in use (`data-exit="node"`), a **Tailscale** section (`data-tailnet` with the state) when the status has `tailnet`, a **Linked devices** section (`data-links`: `linked`, `connecting`, `reconnecting`, `offline`, `none` or `permission`; each device row has a **Prompting** or **Watching** badge, `data-controlling`, while it controls the agent, and **Allow remote control**, `sp-switch[data-action="remote-control"]`, behind a confirm when turned on, or `data-control="fixed"` for a device on this computer; with `access`, an **Allow** group of **View**, **Control** and **Terminal** switches, `data-action="access-view"`, `access-control` and `access-terminal`, each confirmed when turned on, Terminal's saying it gives a root shell, and Control disabled while View is off; a device that asks to follow shows **Asks to follow** and **Allow**, `data-action="allow-follow"`; a **Terminal open** badge, `data-terminals`, counts its terminals; with `followUrl`, the join details add **Open on another device** with a QR code and the link, `data-join="follow"` and `data-copy="follow-url"`) when the status has `links`, **Get the whole web** with the Chrome extension link (`data-action="install-extension"`, when `extensionUrl` is set) and `npx @ai-ecoverse/slicc-node` with a copy button (`data-action="copy-command"`), folded away when the route already reaches the whole web (`proxy`, `extension` or `tailnet`, whose line names `tailnet.exitNode`) or a linked device is the exit (`exit.kind` `link`: the route line names the device and its `policy`, or says it's waiting for it, and `data-route` is `link`, whatever `route` says), and recent failures with host, error and time. The header's **Network** indicator (`data-network`, `data-health`) opens it: a status light, green for `ok`, yellow for `limited` and red for `failing`, with the state in its text and label and `detail` as its tooltip, which adds "Tailscale is connected." while the tailnet runs; on a phone, a cloud icon for the state. While a linked device asks to follow, controls the agent, watches it or has a terminal open, an indicator (`data-remote`, `ask`, `prompt`, `terminal` or `watch`) sits before it, **{name} asks to follow**, **Controlled by**, **Terminal open from** or **Watched by** the device's name (or the number of devices; in that order of precedence), says who does what in its tooltip and opens the panel too. Offered only when the model has a `network` port. |
|
|
155
|
-
| `<slicc-tray>` | The tray
|
|
154
|
+
| `<slicc-network>` | Network: the route and its health in plain words with the bios `detail`, a line on browser automation (`data-browser`: through the SLICC extension, through slicc-node, through a linked device's browser, that slicc-node has no browser attached when `route` is `proxy` and `via` is `null`, or not available with how to get it) when the status has `browser`, **Check again** (`data-action="check"`) when the port has `check`, an **Internet traffic** picker (`data-action="exit"`, in `[data-exit-picker]` with the exit's kind) when the port has `setExit`, the route line saying where traffic goes, and under the picker a warning that traffic waits while the chosen device isn't connected (`data-exit="device"`, `data-held`, with **Send directly**, `data-action="exit-direct"`, which calls `setExit(null)`) or, when `route` isn't `tailnet`, the Tailscale exit node in use (`data-exit="node"`), a **Tailscale** section (`data-tailnet` with the state) when the status has `tailnet`, a **Linked devices** section (`data-links`: `linked`, `connecting`, `reconnecting`, `offline`, `none` or `permission`; each device row has a **Prompting** or **Watching** badge, `data-controlling`, while it controls the agent, and **Allow remote control**, `sp-switch[data-action="remote-control"]`, behind a confirm when turned on, or `data-control="fixed"` for a device on this computer; with `access`, an **Allow** group of **View**, **Control** and **Terminal** switches, `data-action="access-view"`, `access-control` and `access-terminal`, each confirmed when turned on, Terminal's saying it gives a root shell, and Control disabled while View is off; a device that asks to follow shows **Asks to follow** and **Allow**, `data-action="allow-follow"`; a **Terminal open** badge, `data-terminals`, counts its terminals; with `followUrl`, the join details add **Open on another device** with a QR code and the link, `data-join="follow"` and `data-copy="follow-url"`) when the status has `links`, **Get the whole web** with the Chrome extension link (`data-action="install-extension"`, when `extensionUrl` is set) and `npx @ai-ecoverse/slicc-node` with a copy button (`data-action="copy-command"`), folded away when the route already reaches the whole web (`proxy`, `extension` or `tailnet`, whose line names `tailnet.exitNode`) or a linked device is the exit (`exit.kind` `link`: the route line names the device and its `policy`, or says it's waiting for it, and `data-route` is `link`, whatever `route` says), and recent failures with host, error and time. When the model has a `tray` port, a **Sync** section (`data-sync` with the connection) follows the connection: the tray's state, the float's name, role and runtime, the join link with **Copy** (`data-action="copy-tray-join"`), the followers (`li[data-follower]`), and **Disconnect** (`data-action="tray-disconnect"`, confirmed) or **Reconnect** (`data-action="tray-reconnect"`). The header's **Network** indicator (`data-network`, `data-health`) opens it: a status light, green for `ok`, yellow for `limited` and red for `failing`, with the state in its text and label and `detail` as its tooltip, which adds "Tailscale is connected." while the tailnet runs; on a phone, a cloud icon for the state. While a linked device asks to follow, controls the agent, watches it or has a terminal open, an indicator (`data-remote`, `ask`, `prompt`, `terminal` or `watch`) sits before it, **{name} asks to follow**, **Controlled by**, **Terminal open from** or **Watched by** the device's name (or the number of devices; in that order of precedence), says who does what in its tooltip and opens the panel too. Offered only when the model has a `network` port. |
|
|
155
|
+
| `<slicc-tray>` | The tray items in the header, each opening a panel when that panel is in `panels`: the connection (`#chip`, a status light with the float's name and followers, `data-surface="network"`) opens Network at its **Sync** section, and the budget (`#usage`, `data-surface="monitor"`) opens the Monitor. Without the panel an item is a plain status with its label. |
|
|
156
156
|
| `<slicc-confirm>` | The confirmation dialog `confirm()` opens. Not for direct use. |
|
|
157
157
|
| `<slicc-prompt>` | The name dialog `prompt()` opens. Not for direct use. |
|
|
158
158
|
| `<slicc-file-tree>` | Generic. [`@pierre/trees`](https://www.npmjs.com/package/@pierre/trees) through its `web-components` entry: `paths` (folders end in `/`), `gitStatus`, `expanded`, `reveal(path, focus)`. Fires `file-open` on click and on <kbd>Enter</kbd>. |
|
|
@@ -221,7 +221,7 @@ The dummy supports `createDummyModel({ updates: 'boot' | 'starting' | 'current'
|
|
|
221
221
|
|
|
222
222
|
`createDummyModel({ approvals: 'agent' | 'user' | 'many' | 'follower' | 'fail' | 'soon' | 'elsewhere' | 'withdrawn' })` (`?approvals=` on `/ui/`) adds waiting sudo requests: one from an agent, one from a terminal user, three in a row (the last as `www-data`), one without `decide`, one whose `decide` fails, one that expires in 24 seconds, or two whose first is answered in another tab or withdrawn after a few seconds. Decisions are recorded in `model.approvals.decisions`, and `model.approvals.end(id, reason)` ends a request.
|
|
223
223
|
|
|
224
|
-
On phones the update status and the tray show only their status light and the network indicator a cloud icon, with the text in their labels and tooltips; the agent picker keeps room for the cone's name, cut with an ellipsis when long, and the bottom rail scrolls when needed. The update status comes first so progress and failures remain reachable on narrow screens.
|
|
224
|
+
On phones the update status and the tray connection show only their status light (the tray's budget is hidden) and the network indicator a cloud icon, with the text in their labels and tooltips; the agent picker keeps room for the cone's name, cut with an ellipsis when long, and the bottom rail scrolls when needed. The update status comes first so progress and failures remain reachable on narrow screens.
|
|
225
225
|
|
|
226
226
|
### Kernel model
|
|
227
227
|
|
package/dist/slicc-ui.js
CHANGED
|
@@ -11895,6 +11895,122 @@ async function copyText(text2) {
|
|
|
11895
11895
|
}
|
|
11896
11896
|
}
|
|
11897
11897
|
|
|
11898
|
+
// src/app/tray.ts
|
|
11899
|
+
var connectionVariant = {
|
|
11900
|
+
live: "positive",
|
|
11901
|
+
connecting: "info",
|
|
11902
|
+
reconnecting: "info",
|
|
11903
|
+
stalled: "notice",
|
|
11904
|
+
offline: "neutral",
|
|
11905
|
+
error: "negative"
|
|
11906
|
+
};
|
|
11907
|
+
var connectionLabel = {
|
|
11908
|
+
live: "Live",
|
|
11909
|
+
connecting: "Connecting",
|
|
11910
|
+
reconnecting: "Reconnecting",
|
|
11911
|
+
stalled: "Stalled",
|
|
11912
|
+
offline: "Offline",
|
|
11913
|
+
error: "Error"
|
|
11914
|
+
};
|
|
11915
|
+
function budgetVariant(percent2) {
|
|
11916
|
+
const variant = meterVariant(percent2);
|
|
11917
|
+
return variant === "informative" ? "neutral" : variant;
|
|
11918
|
+
}
|
|
11919
|
+
function budgetLine(status2) {
|
|
11920
|
+
return `Spent $${status2.spent.toFixed(2)} \xB7 $${status2.rate.toFixed(2)}/h. ${status2.budget.percent}% of the ${status2.budget.window} budget, ${status2.budget.resets}.`;
|
|
11921
|
+
}
|
|
11922
|
+
var SliccTray = class extends ModelElement {
|
|
11923
|
+
static properties = {
|
|
11924
|
+
...ModelElement.properties,
|
|
11925
|
+
panels: { attribute: false }
|
|
11926
|
+
};
|
|
11927
|
+
constructor() {
|
|
11928
|
+
super();
|
|
11929
|
+
this.panels = ["network", "monitor"];
|
|
11930
|
+
}
|
|
11931
|
+
static styles = [
|
|
11932
|
+
shared,
|
|
11933
|
+
i`
|
|
11934
|
+
:host {
|
|
11935
|
+
display: inline-flex;
|
|
11936
|
+
align-items: center;
|
|
11937
|
+
gap: var(--swc-spacing-75);
|
|
11938
|
+
}
|
|
11939
|
+
.chip > span {
|
|
11940
|
+
display: inline-flex;
|
|
11941
|
+
align-items: center;
|
|
11942
|
+
gap: var(--swc-spacing-75);
|
|
11943
|
+
}
|
|
11944
|
+
span.chip,
|
|
11945
|
+
span.usage {
|
|
11946
|
+
display: inline-flex;
|
|
11947
|
+
align-items: center;
|
|
11948
|
+
padding-inline: var(--swc-spacing-100);
|
|
11949
|
+
}
|
|
11950
|
+
swc-status-light {
|
|
11951
|
+
align-self: center;
|
|
11952
|
+
}
|
|
11953
|
+
.chip .followers {
|
|
11954
|
+
display: inline-flex;
|
|
11955
|
+
align-items: center;
|
|
11956
|
+
gap: var(--swc-spacing-50);
|
|
11957
|
+
}
|
|
11958
|
+
:host([compact]) .chip .detail,
|
|
11959
|
+
:host([compact]) .usage {
|
|
11960
|
+
display: none;
|
|
11961
|
+
}
|
|
11962
|
+
.muted {
|
|
11963
|
+
color: var(--swc-neutral-subdued-content-color-default);
|
|
11964
|
+
}
|
|
11965
|
+
`
|
|
11966
|
+
];
|
|
11967
|
+
subscribe(model) {
|
|
11968
|
+
return [model.tray.on("status", () => this.requestUpdate())];
|
|
11969
|
+
}
|
|
11970
|
+
#open(id, section) {
|
|
11971
|
+
this.dispatchEvent(
|
|
11972
|
+
new CustomEvent("show-surface", {
|
|
11973
|
+
detail: section ? { id, section } : { id },
|
|
11974
|
+
bubbles: true,
|
|
11975
|
+
composed: true
|
|
11976
|
+
})
|
|
11977
|
+
);
|
|
11978
|
+
}
|
|
11979
|
+
#item(id, surface2, label, content, open) {
|
|
11980
|
+
return this.panels.includes(surface2) ? b`<swc-action-button id=${id} class=${id} size="s" quiet data-surface=${surface2} accessible-label=${label} @click=${open}>${content}</swc-action-button>` : b`<span id=${id} class=${id} tabindex="0" role="img" aria-label=${label}>${content}</span>`;
|
|
11981
|
+
}
|
|
11982
|
+
render() {
|
|
11983
|
+
const model = this.model;
|
|
11984
|
+
if (!model) return b``;
|
|
11985
|
+
const status2 = model.tray.status();
|
|
11986
|
+
const connection = connectionLabel[status2.connection];
|
|
11987
|
+
const followers = `${status2.followers.length} ${status2.followers.length === 1 ? "follower" : "followers"}`;
|
|
11988
|
+
const light = status2.connection === "live" ? status2.name : `${status2.name}, ${connection}`;
|
|
11989
|
+
const budget = `Budget ${status2.budget.percent}%`;
|
|
11990
|
+
return b`${this.#item(
|
|
11991
|
+
"chip",
|
|
11992
|
+
"network",
|
|
11993
|
+
`Tray ${status2.name}: ${connection}, ${followers}`,
|
|
11994
|
+
b`<span>
|
|
11995
|
+
<swc-status-light size="s" variant=${connectionVariant[status2.connection]}>
|
|
11996
|
+
<span class="detail">${light}</span>
|
|
11997
|
+
</swc-status-light>
|
|
11998
|
+
<span class="detail muted followers"><swc-icon-user-group size="s"></swc-icon-user-group>${status2.followers.length}</span>
|
|
11999
|
+
</span>`,
|
|
12000
|
+
() => this.#open("network", "sync")
|
|
12001
|
+
)}
|
|
12002
|
+
<swc-tooltip for="chip" placement="bottom">${`${connection}, ${followers}`}</swc-tooltip>
|
|
12003
|
+
${this.#item(
|
|
12004
|
+
"usage",
|
|
12005
|
+
"monitor",
|
|
12006
|
+
`${budget}. ${budgetLine(status2)}`,
|
|
12007
|
+
b`<swc-status-light size="s" variant=${budgetVariant(status2.budget.percent)}>${budget}</swc-status-light>`,
|
|
12008
|
+
() => this.#open("monitor")
|
|
12009
|
+
)}
|
|
12010
|
+
<swc-tooltip for="usage" placement="bottom">${budgetLine(status2)}</swc-tooltip>`;
|
|
12011
|
+
}
|
|
12012
|
+
};
|
|
12013
|
+
|
|
11898
12014
|
// src/app/network.ts
|
|
11899
12015
|
var networkHealth = {
|
|
11900
12016
|
ok: ["positive", "Full access"],
|
|
@@ -12139,7 +12255,8 @@ var SliccNetwork = class extends ModelElement {
|
|
|
12139
12255
|
busy: { state: true },
|
|
12140
12256
|
tailnetError: { state: true },
|
|
12141
12257
|
tailnetCopied: { state: true },
|
|
12142
|
-
linksError: { state: true }
|
|
12258
|
+
linksError: { state: true },
|
|
12259
|
+
syncError: { state: true }
|
|
12143
12260
|
};
|
|
12144
12261
|
constructor() {
|
|
12145
12262
|
super();
|
|
@@ -12151,6 +12268,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
12151
12268
|
this.tailnetError = "";
|
|
12152
12269
|
this.tailnetCopied = "";
|
|
12153
12270
|
this.linksError = "";
|
|
12271
|
+
this.syncError = "";
|
|
12154
12272
|
}
|
|
12155
12273
|
static styles = [
|
|
12156
12274
|
r(link_default),
|
|
@@ -12181,6 +12299,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
12181
12299
|
display: grid;
|
|
12182
12300
|
gap: var(--swc-spacing-200);
|
|
12183
12301
|
align-content: start;
|
|
12302
|
+
scroll-margin-top: var(--swc-spacing-300);
|
|
12184
12303
|
}
|
|
12185
12304
|
h2 {
|
|
12186
12305
|
margin: 0;
|
|
@@ -12424,7 +12543,21 @@ var SliccNetwork = class extends ModelElement {
|
|
|
12424
12543
|
this.error = "";
|
|
12425
12544
|
this.tailnetError = "";
|
|
12426
12545
|
this.linksError = "";
|
|
12427
|
-
|
|
12546
|
+
this.syncError = "";
|
|
12547
|
+
const update = () => this.requestUpdate();
|
|
12548
|
+
return [
|
|
12549
|
+
model.tray.on("status", update),
|
|
12550
|
+
...model.network ? [model.network.on("network", update)] : []
|
|
12551
|
+
];
|
|
12552
|
+
}
|
|
12553
|
+
reveal(section) {
|
|
12554
|
+
void this.updateComplete.then(() => {
|
|
12555
|
+
const target = this.renderRoot.querySelector(
|
|
12556
|
+
`section[aria-labelledby="${section}"]`
|
|
12557
|
+
);
|
|
12558
|
+
target?.querySelector("swc-button, swc-action-button")?.focus({ focusVisible: false });
|
|
12559
|
+
target?.scrollIntoView({ block: "start" });
|
|
12560
|
+
});
|
|
12428
12561
|
}
|
|
12429
12562
|
focus() {
|
|
12430
12563
|
this.focusWith(
|
|
@@ -12787,6 +12920,35 @@ var SliccNetwork = class extends ModelElement {
|
|
|
12787
12920
|
${this.error ? b`<div class="error" role="alert"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.error}</span></div>` : A}
|
|
12788
12921
|
</section>`;
|
|
12789
12922
|
}
|
|
12923
|
+
async #disconnect(tray, name, trigger2) {
|
|
12924
|
+
const ok = await confirm({
|
|
12925
|
+
title: `Disconnect from ${name}?`,
|
|
12926
|
+
body: "This SLICC stops syncing with the tray until you reconnect.",
|
|
12927
|
+
action: "Disconnect",
|
|
12928
|
+
variant: "confirmation",
|
|
12929
|
+
trigger: trigger2
|
|
12930
|
+
});
|
|
12931
|
+
if (ok) tray.disconnect();
|
|
12932
|
+
}
|
|
12933
|
+
#sync(tray) {
|
|
12934
|
+
const status2 = tray.status();
|
|
12935
|
+
return b`<section aria-labelledby="sync" data-sync=${status2.connection}>
|
|
12936
|
+
<div class="head">
|
|
12937
|
+
<h2 id="sync">Sync</h2>
|
|
12938
|
+
<swc-status-light variant=${connectionVariant[status2.connection]}>${connectionLabel[status2.connection]}</swc-status-light>
|
|
12939
|
+
</div>
|
|
12940
|
+
<dl class="node">
|
|
12941
|
+
<dt>Name</dt><dd>${status2.name}</dd>
|
|
12942
|
+
<dt>Role</dt><dd>${status2.role}</dd>
|
|
12943
|
+
<dt>Runs in</dt><dd>${status2.kind}</dd>
|
|
12944
|
+
${status2.joinUrl ? this.#copyRow("tray-join", "Join link", status2.joinUrl, "syncError", "join link") : A}
|
|
12945
|
+
</dl>
|
|
12946
|
+
<h3 id="sync-followers">Followers</h3>
|
|
12947
|
+
${status2.followers.length ? b`<ul aria-labelledby="sync-followers" data-followers>${status2.followers.map((follower) => b`<li data-follower=${follower.id}>${follower.name} <span class="muted">${follower.device}</span></li>`)}</ul>` : b`<p class="muted" data-followers>No one else is connected.</p>`}
|
|
12948
|
+
<div class="actions">${status2.connection === "offline" ? b`<swc-button size="m" variant="secondary" data-action="tray-reconnect" @click=${() => tray.reconnect()}>Reconnect</swc-button>` : b`<swc-button size="m" variant="secondary" data-action="tray-disconnect" @click=${(event2) => this.#disconnect(tray, status2.name, event2.currentTarget)}>Disconnect</swc-button>`}</div>
|
|
12949
|
+
${this.syncError ? b`<div class="error" role="alert" data-error="sync"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.syncError}</span></div>` : A}
|
|
12950
|
+
</section>`;
|
|
12951
|
+
}
|
|
12790
12952
|
#ways(status2) {
|
|
12791
12953
|
return reach({
|
|
12792
12954
|
intro: "Install the SLICC Chrome extension, or run slicc-node on this computer. Either one reaches every site.",
|
|
@@ -12827,6 +12989,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
12827
12989
|
const status2 = networkStatus(port);
|
|
12828
12990
|
return b`<div class="page" tabindex="-1">
|
|
12829
12991
|
${this.#state(port, status2)}
|
|
12992
|
+
${this.#sync(this.model.tray)}
|
|
12830
12993
|
${this.#tailnetSection(port, status2)}
|
|
12831
12994
|
${this.#linksSection(port, status2)}
|
|
12832
12995
|
${this.#whole(status2)}
|
|
@@ -13087,14 +13250,9 @@ function defaultLayout(dock, screen, agent, items = surfaces, terminals = []) {
|
|
|
13087
13250
|
const chat = chats(dock)[0];
|
|
13088
13251
|
if (chat) dock.api.getPanel(chat)?.api.setActive();
|
|
13089
13252
|
}
|
|
13090
|
-
function
|
|
13091
|
-
const
|
|
13092
|
-
|
|
13093
|
-
terminal: () => terminalPanels(dock)
|
|
13094
|
-
};
|
|
13095
|
-
return items.filter(
|
|
13096
|
-
(item2) => item2.id in grouped ? grouped[item2.id]().length === 0 : !dock.has(item2.id)
|
|
13097
|
-
);
|
|
13253
|
+
function shown(dock, id) {
|
|
13254
|
+
const ids = id === "chat" ? chats(dock) : id === "terminal" ? terminalPanels(dock) : [id];
|
|
13255
|
+
return ids.some((panel) => dock.api.getPanel(panel)?.api.isVisible === true);
|
|
13098
13256
|
}
|
|
13099
13257
|
function sprinkleSurface(sprinkle) {
|
|
13100
13258
|
return {
|
|
@@ -21150,8 +21308,8 @@ var SliccUpdates = class extends ModelElement {
|
|
|
21150
21308
|
#checked(at) {
|
|
21151
21309
|
if (at === null) return b`<span>Not checked yet</span>`;
|
|
21152
21310
|
const date = new Date(at);
|
|
21153
|
-
const
|
|
21154
|
-
return b`<span>Checked <time datetime=${date.toISOString()}>${
|
|
21311
|
+
const shown2 = date.toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" });
|
|
21312
|
+
return b`<span>Checked <time datetime=${date.toISOString()}>${shown2}</time></span>`;
|
|
21155
21313
|
}
|
|
21156
21314
|
#item(item2) {
|
|
21157
21315
|
const [variant, state2] = states[item2.state];
|
|
@@ -21208,14 +21366,14 @@ var SliccUpdates = class extends ModelElement {
|
|
|
21208
21366
|
#package(item2, all2, act) {
|
|
21209
21367
|
const light = packageStates[item2.state];
|
|
21210
21368
|
const busy = this.busy.get(item2.id);
|
|
21211
|
-
const
|
|
21369
|
+
const shown2 = busy ? [busy] : act ? item2.actions : [];
|
|
21212
21370
|
const error = this.problems.get(item2.id) ?? item2.error;
|
|
21213
21371
|
return b`<li data-id=${item2.id} data-state=${item2.state}>
|
|
21214
21372
|
<div class="name">
|
|
21215
21373
|
<h3 title=${item2.package}>${item2.label}</h3>
|
|
21216
21374
|
${light ? b`<swc-status-light size="s" variant=${light[0]}>${light[1]}</swc-status-light>` : A}
|
|
21217
21375
|
</div>
|
|
21218
|
-
<div class="buttons">${
|
|
21376
|
+
<div class="buttons">${shown2.map(
|
|
21219
21377
|
(action) => b`<swc-button
|
|
21220
21378
|
size="s"
|
|
21221
21379
|
variant="secondary"
|
|
@@ -21286,7 +21444,7 @@ var allAgents = "slicc:all-agents";
|
|
|
21286
21444
|
var newCone = "slicc:new-cone";
|
|
21287
21445
|
var deleteActiveCone = "slicc:delete-cone";
|
|
21288
21446
|
var railPitch = 52;
|
|
21289
|
-
var phoneRail = ["agents", "files", "changes", "terminal", "browser"];
|
|
21447
|
+
var phoneRail = ["chat", "agents", "files", "changes", "terminal", "browser"];
|
|
21290
21448
|
function railCapacity(width, count) {
|
|
21291
21449
|
if (!width) return count;
|
|
21292
21450
|
const slots = Math.max(1, Math.floor((width - 16 + 4) / railPitch));
|
|
@@ -21461,6 +21619,16 @@ var SliccApp = class extends ThemedElement {
|
|
|
21461
21619
|
.rail:empty {
|
|
21462
21620
|
display: none;
|
|
21463
21621
|
}
|
|
21622
|
+
.rail swc-action-button.selected {
|
|
21623
|
+
--swc-action-button-background-color-default: var(--swc-neutral-background-color-selected-default);
|
|
21624
|
+
--swc-action-button-background-color-hover: var(--swc-neutral-background-color-selected-hover);
|
|
21625
|
+
--swc-action-button-background-color-down: var(--swc-neutral-background-color-selected-down);
|
|
21626
|
+
--swc-action-button-background-color-focus: var(--swc-neutral-background-color-selected-key-focus);
|
|
21627
|
+
--swc-action-button-content-color-default: var(--swc-gray-25);
|
|
21628
|
+
--swc-action-button-content-color-hover: var(--swc-gray-25);
|
|
21629
|
+
--swc-action-button-content-color-down: var(--swc-gray-25);
|
|
21630
|
+
--swc-action-button-content-color-focus: var(--swc-gray-25);
|
|
21631
|
+
}
|
|
21464
21632
|
.badged {
|
|
21465
21633
|
position: relative;
|
|
21466
21634
|
display: inline-flex;
|
|
@@ -21779,6 +21947,7 @@ var SliccApp = class extends ThemedElement {
|
|
|
21779
21947
|
}
|
|
21780
21948
|
#activated(event2) {
|
|
21781
21949
|
const { id } = event2.detail;
|
|
21950
|
+
this.requestUpdate();
|
|
21782
21951
|
this.terminalPanels.activated(id);
|
|
21783
21952
|
const agent = chatAgent(id);
|
|
21784
21953
|
if (agent && agent !== this.model?.agent.active()) this.model?.agent.select(agent);
|
|
@@ -21788,7 +21957,11 @@ var SliccApp = class extends ThemedElement {
|
|
|
21788
21957
|
defaultLayout(this.dock, this.screen, this.#active(), this.#available(), terminals);
|
|
21789
21958
|
this.#save();
|
|
21790
21959
|
}
|
|
21791
|
-
|
|
21960
|
+
#showSurface(event2) {
|
|
21961
|
+
const { id, section } = event2.detail;
|
|
21962
|
+
this.show(id, section);
|
|
21963
|
+
}
|
|
21964
|
+
show(id, section) {
|
|
21792
21965
|
const agent = this.#active();
|
|
21793
21966
|
if (id === "chat" && agent && this.#offers("chat")) {
|
|
21794
21967
|
openChat(this.dock, agent, this.screen);
|
|
@@ -21808,6 +21981,8 @@ var SliccApp = class extends ThemedElement {
|
|
|
21808
21981
|
openSurface(this.dock, item2, this.screen);
|
|
21809
21982
|
if (id === "updates") this.dock.api.getPanel(id)?.api.updateParameters({ boot: false });
|
|
21810
21983
|
this.dock.focusPanel(id);
|
|
21984
|
+
if (section)
|
|
21985
|
+
this.dock.content(id)?.reveal?.(section);
|
|
21811
21986
|
}
|
|
21812
21987
|
#sprinkle(id) {
|
|
21813
21988
|
return this.model?.sprinkles.list().find((candidate) => candidate.id === id);
|
|
@@ -21840,8 +22015,9 @@ var SliccApp = class extends ThemedElement {
|
|
|
21840
22015
|
#railButton(item2, side) {
|
|
21841
22016
|
const changes = item2.id === "changes" ? this.#changes() : 0;
|
|
21842
22017
|
const driven = item2.id === "browser" && !!this.model?.browser.list().some((tab) => tab.controlled);
|
|
21843
|
-
const
|
|
21844
|
-
const
|
|
22018
|
+
const visible2 = shown(this.dock, item2.id);
|
|
22019
|
+
const label = `${visible2 ? `${item2.title}, shown` : `Open ${item2.title}`}${changes ? `, ${changeCount(changes)}` : driven ? ", a tab is in use" : ""}`;
|
|
22020
|
+
const button = u16`<swc-action-button id=${`rail-${item2.id}`} class=${visible2 ? "selected" : ""} quiet size="m" accessible-label=${label} data-surface=${item2.id} ?data-visible=${visible2} @click=${() => this.show(item2.id)}>${railIcon(item2.icon)}</swc-action-button>`;
|
|
21845
22021
|
const tooltip = b`<swc-tooltip for=${`rail-${item2.id}`} placement=${{ left: "end", right: "start", bottom: "top" }[side]}>${item2.title}</swc-tooltip>`;
|
|
21846
22022
|
return changes ? b`<span class="badged">${button}<swc-badge size="s" variant="neutral" aria-hidden="true">${changes}</swc-badge></span>${tooltip}` : driven ? b`<span class="badged">${button}<span class="dot" data-driven aria-hidden="true"></span></span>${tooltip}` : b`${button}${tooltip}`;
|
|
21847
22023
|
}
|
|
@@ -21862,20 +22038,20 @@ var SliccApp = class extends ThemedElement {
|
|
|
21862
22038
|
#rail(items, side) {
|
|
21863
22039
|
const primary = side === "bottom" ? items.filter((item2) => phoneRail.includes(item2.id)) : items;
|
|
21864
22040
|
const fit = primary.length < items.length ? Math.min(primary.length, railCapacity(this.#width, Infinity)) : railCapacity(this.#width, items.length);
|
|
21865
|
-
const
|
|
21866
|
-
return b`<nav class=${`rail ${side}`} aria-label=${`
|
|
22041
|
+
const shown2 = side === "bottom" ? primary.slice(0, fit) : items;
|
|
22042
|
+
return b`<nav class=${`rail ${side}`} aria-label=${`Panels, ${side}`} @dragover=${this.#railOver} @dragleave=${this.#railLeave} @drop=${this.#railDrop}>${shown2.map(
|
|
21867
22043
|
(item2) => this.#railButton(item2, side)
|
|
21868
|
-
)}${this.#more(items.filter((item2) => !
|
|
22044
|
+
)}${this.#more(items.filter((item2) => !shown2.includes(item2)))}</nav>`;
|
|
21869
22045
|
}
|
|
21870
22046
|
#rails() {
|
|
21871
22047
|
const dock = this.renderRoot.querySelector("slicc-dock");
|
|
21872
|
-
const sprinkles = (this.model?.sprinkles.list() ?? []).filter((sprinkle) => !sprinkle.inline || this.dips.has(sprinkle.id)).map(sprinkleSurface)
|
|
21873
|
-
const
|
|
22048
|
+
const sprinkles = (this.model?.sprinkles.list() ?? []).filter((sprinkle) => !sprinkle.inline || this.dips.has(sprinkle.id)).map(sprinkleSurface);
|
|
22049
|
+
const all2 = dock && this.#started ? [...sprinkles, ...this.#available()] : [];
|
|
21874
22050
|
const phone = this.screen === "phone";
|
|
21875
22051
|
return {
|
|
21876
|
-
left: this.#rail(phone ? [] :
|
|
21877
|
-
right: this.#rail(phone ? [] :
|
|
21878
|
-
bottom: phone ? this.#rail(
|
|
22052
|
+
left: this.#rail(phone ? [] : all2.filter((item2) => item2.side !== "right"), "left"),
|
|
22053
|
+
right: this.#rail(phone ? [] : all2.filter((item2) => item2.side === "right"), "right"),
|
|
22054
|
+
bottom: phone ? this.#rail(all2, "bottom") : A
|
|
21879
22055
|
};
|
|
21880
22056
|
}
|
|
21881
22057
|
newTerminal() {
|
|
@@ -22047,7 +22223,7 @@ var SliccApp = class extends ThemedElement {
|
|
|
22047
22223
|
${this.#updatesIndicator()}
|
|
22048
22224
|
${this.#remoteIndicator()}
|
|
22049
22225
|
${this.#networkIndicator()}
|
|
22050
|
-
${this.#offers("chat") ? b`<slicc-tray .model=${this.model} ?compact=${this.screen === "phone"}></slicc-tray>` : A}
|
|
22226
|
+
${this.#offers("chat") ? b`<slicc-tray .model=${this.model} .panels=${["network", "monitor"].filter((id) => this.#offers(id))} ?compact=${this.screen === "phone"} @show-surface=${this.#showSurface}></slicc-tray>` : A}
|
|
22051
22227
|
<sp-action-menu size="s" quiet label="View" @change=${this.#view}>
|
|
22052
22228
|
<swc-icon-view-grid slot="icon"></swc-icon-view-grid>
|
|
22053
22229
|
<span slot="label" class="view-label">View</span>
|
|
@@ -22081,7 +22257,7 @@ var SliccApp = class extends ThemedElement {
|
|
|
22081
22257
|
@open-file=${(event2) => this.#request("file", event2)}
|
|
22082
22258
|
@open-diff=${(event2) => this.#request("diff", event2)}
|
|
22083
22259
|
@new-terminal=${() => this.newTerminal()}
|
|
22084
|
-
@show-surface=${
|
|
22260
|
+
@show-surface=${this.#showSurface}
|
|
22085
22261
|
></slicc-dock>
|
|
22086
22262
|
${rails.right}
|
|
22087
22263
|
</main>
|
|
@@ -26463,8 +26639,8 @@ var SliccMemory = class extends ModelElement {
|
|
|
26463
26639
|
const scopes = this.#scopes;
|
|
26464
26640
|
const scope = this.scope;
|
|
26465
26641
|
const option2 = (item2) => b`<sp-menu-item value=${item2.id}>${item2.label}</sp-menu-item>`;
|
|
26466
|
-
const
|
|
26467
|
-
return b`<div class="bar"><span>${
|
|
26642
|
+
const shown2 = visible(all2, scope, this.tag, this.query);
|
|
26643
|
+
return b`<div class="bar"><span>${shown2.length} of ${all2.filter((memory) => memory.scope === scope).length} memories</span><span class="spacer"></span>
|
|
26468
26644
|
<swc-action-button size="s" quiet @click=${() => this.editing = "new"}><swc-icon-add slot="icon"></swc-icon-add>Remember</swc-action-button>
|
|
26469
26645
|
</div>
|
|
26470
26646
|
<div class="tools">
|
|
@@ -26486,7 +26662,7 @@ var SliccMemory = class extends ModelElement {
|
|
|
26486
26662
|
</div>
|
|
26487
26663
|
<div class="list">
|
|
26488
26664
|
${this.editing === "new" ? b`<div class="row">${this.#form({ scope })}</div>` : A}
|
|
26489
|
-
${
|
|
26665
|
+
${shown2.length ? sections(shown2).map(
|
|
26490
26666
|
([section, items]) => b`<h3>${section}</h3>${items.map((memory) => this.#row(memory))}`
|
|
26491
26667
|
) : b`<div class="note">${all2.length ? "No memories match." : "No memories yet."}</div>`}
|
|
26492
26668
|
</div>`;
|
|
@@ -27659,164 +27835,6 @@ ${data.html}`;
|
|
|
27659
27835
|
}
|
|
27660
27836
|
};
|
|
27661
27837
|
|
|
27662
|
-
// src/app/tray.ts
|
|
27663
|
-
var connectionVariant = {
|
|
27664
|
-
live: "positive",
|
|
27665
|
-
connecting: "info",
|
|
27666
|
-
reconnecting: "info",
|
|
27667
|
-
stalled: "notice",
|
|
27668
|
-
offline: "neutral",
|
|
27669
|
-
error: "negative"
|
|
27670
|
-
};
|
|
27671
|
-
var connectionLabel = {
|
|
27672
|
-
live: "Live",
|
|
27673
|
-
connecting: "Connecting",
|
|
27674
|
-
reconnecting: "Reconnecting",
|
|
27675
|
-
stalled: "Stalled",
|
|
27676
|
-
offline: "Offline",
|
|
27677
|
-
error: "Error"
|
|
27678
|
-
};
|
|
27679
|
-
var SliccTray = class extends ModelElement {
|
|
27680
|
-
static properties = {
|
|
27681
|
-
...ModelElement.properties,
|
|
27682
|
-
copied: { state: true }
|
|
27683
|
-
};
|
|
27684
|
-
constructor() {
|
|
27685
|
-
super();
|
|
27686
|
-
this.copied = false;
|
|
27687
|
-
}
|
|
27688
|
-
static styles = [
|
|
27689
|
-
shared,
|
|
27690
|
-
i`
|
|
27691
|
-
:host {
|
|
27692
|
-
display: inline-block;
|
|
27693
|
-
}
|
|
27694
|
-
.chip > span {
|
|
27695
|
-
display: inline-flex;
|
|
27696
|
-
align-items: center;
|
|
27697
|
-
gap: var(--swc-spacing-75);
|
|
27698
|
-
}
|
|
27699
|
-
.chip swc-status-light {
|
|
27700
|
-
align-self: center;
|
|
27701
|
-
}
|
|
27702
|
-
.chip .followers {
|
|
27703
|
-
display: inline-flex;
|
|
27704
|
-
align-items: center;
|
|
27705
|
-
gap: var(--swc-spacing-50);
|
|
27706
|
-
}
|
|
27707
|
-
:host([compact]) .chip .detail {
|
|
27708
|
-
display: none;
|
|
27709
|
-
}
|
|
27710
|
-
.muted {
|
|
27711
|
-
color: var(--swc-neutral-subdued-content-color-default);
|
|
27712
|
-
}
|
|
27713
|
-
.panel {
|
|
27714
|
-
display: grid;
|
|
27715
|
-
gap: var(--swc-spacing-200);
|
|
27716
|
-
width: calc(var(--swc-spacing-400) * 12);
|
|
27717
|
-
max-width: 100%;
|
|
27718
|
-
padding: var(--swc-spacing-300);
|
|
27719
|
-
box-sizing: border-box;
|
|
27720
|
-
font-size: var(--swc-font-size-100);
|
|
27721
|
-
color: var(--swc-neutral-content-color-default);
|
|
27722
|
-
}
|
|
27723
|
-
dl {
|
|
27724
|
-
display: grid;
|
|
27725
|
-
grid-template-columns: auto 1fr;
|
|
27726
|
-
gap: var(--swc-spacing-75) var(--swc-spacing-200);
|
|
27727
|
-
margin: 0;
|
|
27728
|
-
}
|
|
27729
|
-
dt {
|
|
27730
|
-
color: var(--swc-neutral-subdued-content-color-default);
|
|
27731
|
-
}
|
|
27732
|
-
dd {
|
|
27733
|
-
margin: 0;
|
|
27734
|
-
}
|
|
27735
|
-
h3 {
|
|
27736
|
-
margin: 0 0 var(--swc-spacing-75);
|
|
27737
|
-
font-size: inherit;
|
|
27738
|
-
font-weight: var(--swc-bold-font-weight);
|
|
27739
|
-
}
|
|
27740
|
-
ul {
|
|
27741
|
-
list-style: none;
|
|
27742
|
-
padding: 0;
|
|
27743
|
-
margin: 0;
|
|
27744
|
-
}
|
|
27745
|
-
swc-meter {
|
|
27746
|
-
width: 100%;
|
|
27747
|
-
}
|
|
27748
|
-
.actions {
|
|
27749
|
-
display: flex;
|
|
27750
|
-
flex-wrap: wrap;
|
|
27751
|
-
gap: var(--swc-spacing-100);
|
|
27752
|
-
}
|
|
27753
|
-
`
|
|
27754
|
-
];
|
|
27755
|
-
subscribe(model) {
|
|
27756
|
-
return [model.tray.on("status", () => this.requestUpdate())];
|
|
27757
|
-
}
|
|
27758
|
-
async #disconnect(name, trigger2) {
|
|
27759
|
-
const body4 = "This SLICC stops syncing with the tray until you reconnect.";
|
|
27760
|
-
const title = `Disconnect from ${name}?`;
|
|
27761
|
-
if (await confirm({ title, body: body4, action: "Disconnect", variant: "confirmation", trigger: trigger2 }))
|
|
27762
|
-
this.model?.tray.disconnect();
|
|
27763
|
-
}
|
|
27764
|
-
async copy() {
|
|
27765
|
-
const url = this.model?.tray.status().joinUrl ?? "";
|
|
27766
|
-
await globalThis.navigator?.clipboard?.writeText(url).catch(() => {
|
|
27767
|
-
});
|
|
27768
|
-
this.copied = true;
|
|
27769
|
-
}
|
|
27770
|
-
render() {
|
|
27771
|
-
const model = this.model;
|
|
27772
|
-
if (!model) return b``;
|
|
27773
|
-
const status2 = model.tray.status();
|
|
27774
|
-
const connection = connectionLabel[status2.connection];
|
|
27775
|
-
const followers = `${status2.followers.length} ${status2.followers.length === 1 ? "follower" : "followers"}`;
|
|
27776
|
-
const light = status2.connection === "live" ? status2.name : `${status2.name}, ${connection}`;
|
|
27777
|
-
return b`<swc-action-button
|
|
27778
|
-
id="chip"
|
|
27779
|
-
class="chip"
|
|
27780
|
-
size="s"
|
|
27781
|
-
quiet
|
|
27782
|
-
accessible-label=${`Tray ${status2.name}: ${connection}, ${followers}`}
|
|
27783
|
-
>
|
|
27784
|
-
<span>
|
|
27785
|
-
<swc-status-light size="s" variant=${connectionVariant[status2.connection]}>
|
|
27786
|
-
<span class="detail">${light}</span>
|
|
27787
|
-
</swc-status-light>
|
|
27788
|
-
<span class="detail muted followers"><swc-icon-user-group size="s"></swc-icon-user-group>${status2.followers.length}</span>
|
|
27789
|
-
</span>
|
|
27790
|
-
</swc-action-button>
|
|
27791
|
-
<swc-tooltip for="chip" placement="bottom">${`${connection}, ${followers}`}</swc-tooltip>
|
|
27792
|
-
<swc-popover for="chip" placement="bottom-end" accessible-label="Tray" @swc-open=${() => {
|
|
27793
|
-
this.copied = false;
|
|
27794
|
-
}}>
|
|
27795
|
-
<div class="panel">
|
|
27796
|
-
<dl>
|
|
27797
|
-
<dt>Name</dt><dd>${status2.name}</dd>
|
|
27798
|
-
<dt>Connection</dt><dd>${connection}</dd>
|
|
27799
|
-
<dt>Role</dt><dd>${status2.role}</dd>
|
|
27800
|
-
<dt>Runs in</dt><dd>${status2.kind}</dd>
|
|
27801
|
-
<dt>Spent</dt><dd>$${status2.spent.toFixed(2)} · $${status2.rate.toFixed(2)}/h</dd>
|
|
27802
|
-
</dl>
|
|
27803
|
-
<swc-meter size="s" value=${status2.budget.percent} variant=${meterVariant(status2.budget.percent)}>
|
|
27804
|
-
<span slot="label">Budget (${status2.budget.window})</span>
|
|
27805
|
-
<span slot="description">${status2.budget.resets}</span>
|
|
27806
|
-
</swc-meter>
|
|
27807
|
-
<div>
|
|
27808
|
-
<h3>Followers</h3>
|
|
27809
|
-
${status2.followers.length ? b`<ul>${status2.followers.map((follower) => b`<li>${follower.name} <span class="muted">${follower.device}</span></li>`)}</ul>` : b`<div class="muted">No one else is connected.</div>`}
|
|
27810
|
-
</div>
|
|
27811
|
-
<div class="actions">
|
|
27812
|
-
<swc-action-button size="s" @click=${() => this.copy()}>${this.copied ? "Copied" : "Copy join link"}</swc-action-button>
|
|
27813
|
-
${status2.connection === "offline" ? b`<swc-action-button size="s" @click=${() => model.tray.reconnect()}>Reconnect</swc-action-button>` : b`<swc-action-button size="s" @click=${(event2) => this.#disconnect(status2.name, event2.currentTarget)}>Disconnect</swc-action-button>`}
|
|
27814
|
-
</div>
|
|
27815
|
-
</div>
|
|
27816
|
-
</swc-popover>`;
|
|
27817
|
-
}
|
|
27818
|
-
};
|
|
27819
|
-
|
|
27820
27838
|
// node_modules/@pierre/diffs/dist/constants.js
|
|
27821
27839
|
var DIFFS_TAG_NAME = "diffs-container";
|
|
27822
27840
|
var DIFFS_DEVELOPMENT_BUILD = (() => {
|