@ai-ecoverse/slicc-spectrum 1.86.1 → 1.86.2
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 +11 -4
- package/dist/slicc-ui.js +202 -33
- package/dist/slicc-ui.js.map +4 -4
- package/dist/types/app/browser.d.ts +2 -1
- package/dist/types/app/files.d.ts +1 -0
- package/dist/types/app/memory.d.ts +2 -4
- package/dist/types/app/messages.d.ts +2 -0
- package/dist/types/app/monitor.d.ts +2 -4
- package/dist/types/app/view-state.d.ts +20 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -219,6 +219,7 @@ The sans stack (`--swc-sans-font-family-stack`, also used by the 1.12 components
|
|
|
219
219
|
|
|
220
220
|
The shell: header with the agent picker, status items, the update status, the network indicator, the tray, the View menu and the theme switch, rails, and a `<slicc-dock>`.
|
|
221
221
|
Saves the dock layout per screen class to `storage` (default `localStorage`) under `layoutKey`.
|
|
222
|
+
The dock builds new panel content whenever it lays panels out again (a switch between phone, tablet and desktop, or a layout reset), so what the user opens in a panel (sections, cones, rows, folders, disclosures) lives outside the element, per model and panel (`view-state.ts`, in memory only): a rebuilt panel shows it again, and a click on the outgoing panel while the new one mounts reaches the new one too.
|
|
222
223
|
While connected it sets `overscroll-behavior` to `none !important` on the page's `html` and `body`, so only scroll areas inside panels rubber-band.
|
|
223
224
|
It sets the page's `color-scheme` to its resolved theme, and when the page has a `theme-color` meta per `prefers-color-scheme`, gives every one of them the content of the meta for that theme, so the page and the installed app's title bar follow the app's setting instead of the OS.
|
|
224
225
|
When the last app on the page is removed, the page's own inline values and `theme-color` contents come back.
|
|
@@ -240,7 +241,7 @@ A tree (`role="tree"`) of cones, with their scoops listed under them.
|
|
|
240
241
|
A cone with scoops starts with a quiet disclosure chevron (`data-action="toggle-cone"`, `swc-icon-chevron-right`, turned down when open and mirrored right to left); it's `aria-hidden` and out of the Tab order, since the row's `aria-expanded` carries the state.
|
|
241
242
|
The chevron only expands and collapses; a click on the row, <kbd>Enter</kbd> or <kbd>Space</kbd> selects the agent and makes it the active one, which also expands its cone.
|
|
242
243
|
<kbd>→</kbd> expands a cone and then moves to its first scoop, <kbd>←</kbd> moves from a scoop to its cone and then collapses it, and neither changes the active agent.
|
|
243
|
-
Several cones can be open at once, and the active agent
|
|
244
|
+
Several cones can be open at once, and they stay open for every view of the panel on the same model, so a layout switch keeps them; when the active agent has changed since the panel last showed it, its cone opens again as the panel mounts, so on a phone a cone you tapped still shows its scoops when you come back to Agents, and a cone you closed stays closed.
|
|
244
245
|
When the active scoop's cone is collapsed, the cone row takes the Tab stop.
|
|
245
246
|
A row's first line starts with one circle: its unread count when there are unread messages (`99+` from a hundred up, at `--swc-font-size-50`, white on the accent fill, or on the negative fill when the cone is in error), otherwise a dot coloured by status.
|
|
246
247
|
Then comes the name; the context fill, when it's high, sits in the top right corner.
|
|
@@ -272,6 +273,7 @@ A command's icon is the favicon of the site it names when an open browser tab al
|
|
|
272
273
|
A running run pulses (still, with a dashed ring, under `prefers-reduced-motion`), a done or cancelled one is grey and a failed one red, each with a corner glyph.
|
|
273
274
|
The card (`.tool[data-tool][data-status][data-kind]`, a `details.run` under a pill and a `div[role=region]` named by its `title` and status under a strip) has no header of its own, no chevron, title, tool name or status: its input (a shell command after `$`), an edit's `diff` shown as a diff view, its output wrapped rather than cut off, an `image`, such as a screenshot, an optional `meta` such as a timeout, a standalone tool message's time ("Ran at 16:12", `.ran`, by the two-minute rule), and its `notes` (`{ severity, text }`, such as "Exited with code 5" or "Full output in /tmp/…") as lines under the output, `li[data-note][data-severity]`: `error` with an alert icon in the negative colour, `warn` with an alert icon and `info` with an info icon, both subdued; media galleries with images, video and audio; action cards for tools, pull requests and status; plans and checklists; inline diffs; question prompts (as decision cards, below); delegations; link previews; errors with a fix-it action, as a grey card (`.error-card[role=alert]`, `data-id` on system errors) inset by a wide margin on each side, a narrower one under 480 px: a red alert icon, the title in bold, the detail subdued and the action on the right, or under the detail below 360 px, announced as "Error: " and the title; a content-filter stop offers **Drop the last turn**, which rewinds that turn and puts its prompt back into the composer; when the turns right before it failed too, the button reads **Drop N failed turns** and rewinds to before the first of them, putting the latest prompt back), system messages (error cards; compactions and notices, rewinds and model changes included, as full-width dividers with a line on each side, like the day: a compaction shows only its title, its `text` kept for screen readers, one that lost messages (`state: 'fallback'`, by default "Compaction failed, transcript truncated", or `discarded`, "Context discarded") an alert icon in the negative colour, and a notice its title and text on one line, `role="note"`; a divider that wraps keeps a short line on each side) and licks, SLICC's events into a cone, on every channel, on the right (`.lick-turn`): a byline with the title and the time by the two-minute rule, then a dashed box (`.lick[data-id][data-channel]`) with the text and the count (×3); emoji pills on its bottom edge (`.reactions .reaction`, `aria-hidden`, each named by its `title`) give the channel (⏰ Schedule, 🪝 Webhook, ✨ Sprinkle, …) and the outcome (👍 Allowed, 👎 Denied, also said in the box's hidden text).
|
|
274
275
|
A lick with a body is a `details.lick` whose head (`summary.lick-head`, with a quiet chevron that turns when open) toggles it with a click, <kbd>Enter</kbd> or <kbd>Space</kbd>; clicking the body doesn't close it.
|
|
276
|
+
Open licks and open tool runs hold for every view of the chat on the same model, so a layout switch keeps them.
|
|
275
277
|
A lick's `severity` (`warn` or `error`) adds an alert icon to its byline, a coloured edge to its box and a spoken prefix.
|
|
276
278
|
Pending permission requests (`sudo-request` licks with `state: 'pending'`) and open questions are decision cards in the centre column (`.decision-turn[data-pending]`, `data-id` the lick's or the question's), inset like a tool cluster (a wide margin on each side, a narrower one under 480 px), with their buttons on the right: **Deny**, then **Allow** (`data-action` `deny` and `allow`), or the question's options.
|
|
277
279
|
A request with `expiresAt` counts down as m:ss; the count is hidden from screen readers, which hear its end time, then "Less than a minute left" and "Timed out", so the log's live region doesn't speak every second; a timed-out request (`data-expired`) loses its buttons.
|
|
@@ -341,6 +343,7 @@ A quiet line under it (`[data-seat-note]`) says who reviews ("The host reviews w
|
|
|
341
343
|
#### `<slicc-files>`
|
|
342
344
|
|
|
343
345
|
The file system as a `<slicc-file-tree>`, with pending changes as git status.
|
|
346
|
+
It starts with the folders of the pending changes open; once the user opens or closes a folder, every view of Files on the same model shows the same folders open, a rebuilt one included.
|
|
344
347
|
Opening a file opens it in a tab.
|
|
345
348
|
With `files.mountFolder`, **Mount a folder…** (`data-action="mount-folder"`) mounts a local folder, then selects and expands it in the tree; with `files.mounts`, a **Mounted** strip lists the mount points (with `files.mountInfo`, each mount with a `source` reads `/mnt/proj · ref-node/proj`, `data-source` on its `.path`), each with **Eject** (`data-action="eject"`, `data-path` the mount point) when the port has `eject`.
|
|
346
349
|
With `files.needsFolder`, a **Needs a folder** section lists the mount points waiting for a folder, each with **Insert folder…** (`data-action="insert-folder"`, `data-id` the mount point), which calls `mountFolder(path)`.
|
|
@@ -382,6 +385,7 @@ It opens on the most recently driven tab (`.focused`), or on the tab overview: c
|
|
|
382
385
|
The focused tab has **All tabs** (`data-action="all-tabs"`), a tab picker (a list of tabs at 720 px and wider), the URL with **Copy** (`data-action="copy-url"`) and **Open** (`data-action="open-tab"`, activates the tab), who's using it (`.driving[data-driving=agent|user|none]`; a controlled tab without an agent reads **In use** and has no Stop) with a small **Stop <agent>** (`data-action="stop-agent"`) in the same row, which keeps its height with or without an agent, a view-only preview (`figure[data-view]`: `live` while watching with a frame under 3 s old, `shot` with "Updated N s ago", `none`) and the recent actions (`li[data-action-id][data-kind][data-status]`, failures with their error inline), the tab's own filtered first, then the latest 50, with **Show more** (`data-action="more-actions"`) adding 50 earlier ones each time up to all the port has ("Showing the latest N of M"); the list scrolls and takes focus.
|
|
383
386
|
Actions with a URL (`open`, `goto`, `request`) expand (`data-action="action-url"`, `aria-expanded`) to show the full URL, or the request line, under the shortened one.
|
|
384
387
|
Below 480 px they fold into **Recent actions (n)**, a chevron disclosure counting all of them, with the last one showing.
|
|
388
|
+
The tab the user opened (or **All tabs**), expanded URLs and an open **Recent actions** hold for every view of the panel on the same model, so a layout switch keeps them; a closed tab lets go, and the panel opens on the most recently driven tab again.
|
|
385
389
|
Labels, URLs and errors are text only.
|
|
386
390
|
With no tabs and `browser.via` `null` in the network status, it says browser control isn't connected (`data-browser="none"`) with the extension link and the commands to install and run slicc (`curl -fsSL https://get.sliccy.ai/slicc | sh`, then `slicc`, or `npx sliccy@next` with Node).
|
|
387
391
|
The Browser rail button carries a dot (`data-driven`) while a tab is in use.
|
|
@@ -419,6 +423,7 @@ Then **Rules SLICC refused** (`[data-refused]`), where the rules live (`[data-po
|
|
|
419
423
|
The section's `data-vault` is `locked`, `unlocked` or `loading`.
|
|
420
424
|
A popup the browser blocked shows how to allow it (`[data-popup=blocked]`).
|
|
421
425
|
Widened-rule notices aren't shown.
|
|
426
|
+
An open **Removed** list (`details[data-removed]`) stays open across a layout switch.
|
|
422
427
|
|
|
423
428
|
#### `<slicc-vault-button>`
|
|
424
429
|
|
|
@@ -433,7 +438,7 @@ It closes, clearing the field, when the request leaves `waiting` or while the va
|
|
|
433
438
|
|
|
434
439
|
#### `<slicc-updates>`
|
|
435
440
|
|
|
436
|
-
Install / Update: component states, version changes, last checks, download and link counts, action buttons, failures with Retry, and collapsible install logs.
|
|
441
|
+
Install / Update: component states, version changes, last checks, download and link counts, action buttons, failures with Retry, and collapsible install logs, which stay open across a layout switch.
|
|
437
442
|
Opens at boot until the agent is ready without failures.
|
|
438
443
|
Background updates stay in the header's update status; new failures reopen the panel, and after a reload into a boot that isn't ready yet such a panel closes once the agent is ready.
|
|
439
444
|
Every automatic close is saved to the layout.
|
|
@@ -447,7 +452,7 @@ What agents remember, grouped by section: search, filter by scope and tag, expan
|
|
|
447
452
|
The scope picker starts on **Everyone** (`*`), which lists every memory with its scope on it (`.scope[data-scope]`); `global` reads **Shared** whatever the port calls it, and the cones and roles follow.
|
|
448
453
|
The count (“N of M memories”) counts the memories in the picked scope.
|
|
449
454
|
With a search or tag filter that hides everything it says “No memories match.”, otherwise “No memories yet.”, “No shared memories yet.” or “No memories for {scope} yet.”
|
|
450
|
-
Each memory is a row (`li.row[data-id]`) that opens in place: open rows (`data-open`) take the accent's subtle background and turn their chevron down, and show the body with **Edit** and **Forget** (`data-action="edit"`, `data-action="forget"`).
|
|
455
|
+
Each memory is a row (`li.row[data-id]`) that opens in place, and stays open across a layout switch: open rows (`data-open`) take the accent's subtle background and turn their chevron down, and show the body with **Edit** and **Forget** (`data-action="edit"`, `data-action="forget"`).
|
|
451
456
|
Under the summary, the tag is a subtle badge (`swc-badge[data-tag]`: User indigo, Feedback purple, Project seafoam), then the scope and, for free text outside an entry, “From MEMORY.md” (`.from`).
|
|
452
457
|
Below 560 px of panel width, the search takes its own line over the two pickers.
|
|
453
458
|
The search field is the shared one from `search.ts` (`sp-search[data-action=search]`), as in Files.
|
|
@@ -458,7 +463,7 @@ The live monitor: vitals (`.vital[data-id]`), each a meter when it has a `ratio`
|
|
|
458
463
|
Each card keeps its place whatever it shows: the value line never wraps (a unit or delta that doesn't fit is left out whole rather than cut, and stays in the line's `title` and its text; a vital's optional `hint` replaces that tooltip, such as the exact reset time behind "resets in 4 days"), a card without a `series` keeps the sparkline's room, a meter keeps its description's line, and a card left alone on the last row spans it, so short and long values, 0% and 100%, and missing data move nothing below them.
|
|
459
464
|
Then alerts (an alert's optional `command` shows after its detail, led by its `commandLabel` or "Run", as unwrapped code with **Copy**, `[data-command]` and `data-action="copy-command"`), and the sections (the agent's Tasks, Processes and Recent problems, then terminals, tabs, pending changes and tray followers; cones and sprinkles have their own panels; any other section an agent sends, such as an older agent's `agents` or `sprinkles`, renders the same way).
|
|
460
465
|
Each section (`li.section[data-id]`) shows its count and an attention badge, and opens its surface with **Open** where one exists; the counts line up in one column whether or not a section has **Open**, and a section without rows keeps its name in line with the others (`agents`, `terminals` and `processes` → terminal, `tabs`, `changes`).
|
|
461
|
-
A section with rows expands into them with its toggle (`.toggle`, `aria-expanded`; `data-open` on the section): collapsed by default, expanded while a row is `warn` or `error`, until the user toggles it.
|
|
466
|
+
A section with rows expands into them with its toggle (`.toggle`, `aria-expanded`; `data-open` on the section): collapsed by default, expanded while a row is `warn` or `error`, until the user toggles it; that choice holds for every view of the monitor on the same model, so a layout switch keeps it.
|
|
462
467
|
Each row (`li.row[data-status]`, `data-id` from the row's `id`) shows a status light (blue active, gray idle, orange warn, red error, with the status spoken), the name, the meta line and its badges, indented by `depth` (`data-depth`, up to 6).
|
|
463
468
|
|
|
464
469
|
#### `<slicc-freezer>`
|
|
@@ -504,6 +509,7 @@ The URL is never stored or shown again: closing the dialog drops it.
|
|
|
504
509
|
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.
|
|
505
510
|
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.
|
|
506
511
|
Offered when the model has a `network` port, a `guests` port, or both.
|
|
512
|
+
**Link another device** and **Other ways to reach the whole web** stay open across a layout switch once opened.
|
|
507
513
|
With `guests` and no `network`, it shows only the Guests section (no connection, Sync, Leaders, Tailscale, Linked devices, Get the whole web or failures), still titled Network with its rail icon, and the header's Network and remote indicators stay hidden; the tray's connection chip is then a plain status.
|
|
508
514
|
|
|
509
515
|
#### `<slicc-tray>`
|
|
@@ -530,6 +536,7 @@ Not for direct use.
|
|
|
530
536
|
|
|
531
537
|
Generic.
|
|
532
538
|
[`@pierre/trees`](https://www.npmjs.com/package/@pierre/trees) through its `web-components` entry: `paths` (folders end in `/`), `gitStatus`, `expanded`, `query` (shows only the paths that match it; empty shows all), `reveal(path, focus)`.
|
|
539
|
+
`expanded` also opens and closes the folders of a mounted tree, a `tree-expand` event (`detail.expanded`) reports the open folders when the user changes them, and the tree keeps them when it's moved, such as into a floating group.
|
|
533
540
|
It has no search field of its own.
|
|
534
541
|
Fires `file-open` on click and on <kbd>Enter</kbd>, and `tree-search` (`detail.key`) when a letter or digit is typed in it, for the panel's search field.
|
|
535
542
|
|
package/dist/slicc-ui.js
CHANGED
|
@@ -10052,6 +10052,87 @@ function search(options) {
|
|
|
10052
10052
|
></sp-search>`;
|
|
10053
10053
|
}
|
|
10054
10054
|
|
|
10055
|
+
// src/app/view-state.ts
|
|
10056
|
+
var stores = /* @__PURE__ */ new WeakMap();
|
|
10057
|
+
var loose = {};
|
|
10058
|
+
function slotOf(model, key2, initial) {
|
|
10059
|
+
const owner = model ?? loose;
|
|
10060
|
+
const slots = stores.get(owner) ?? /* @__PURE__ */ new Map();
|
|
10061
|
+
stores.set(owner, slots);
|
|
10062
|
+
const found = slots.get(key2);
|
|
10063
|
+
if (found) return found;
|
|
10064
|
+
const slot = { value: initial(), listeners: /* @__PURE__ */ new Set() };
|
|
10065
|
+
slots.set(key2, slot);
|
|
10066
|
+
return slot;
|
|
10067
|
+
}
|
|
10068
|
+
var ViewState = class {
|
|
10069
|
+
#host;
|
|
10070
|
+
#key;
|
|
10071
|
+
#initial;
|
|
10072
|
+
#watched = null;
|
|
10073
|
+
#connected = false;
|
|
10074
|
+
#listener = () => this.#host.requestUpdate();
|
|
10075
|
+
constructor(host4, key2, initial) {
|
|
10076
|
+
this.#host = host4;
|
|
10077
|
+
this.#key = key2;
|
|
10078
|
+
this.#initial = initial;
|
|
10079
|
+
host4.addController(this);
|
|
10080
|
+
}
|
|
10081
|
+
#slot() {
|
|
10082
|
+
const model = this.#host.model;
|
|
10083
|
+
return slotOf(model, this.#key, () => this.#initial(model));
|
|
10084
|
+
}
|
|
10085
|
+
get value() {
|
|
10086
|
+
return this.#slot().value;
|
|
10087
|
+
}
|
|
10088
|
+
set value(value) {
|
|
10089
|
+
const slot = this.#slot();
|
|
10090
|
+
if (Object.is(slot.value, value)) return;
|
|
10091
|
+
slot.value = value;
|
|
10092
|
+
for (const listener of [...slot.listeners]) listener();
|
|
10093
|
+
}
|
|
10094
|
+
#watch() {
|
|
10095
|
+
const slot = this.#slot();
|
|
10096
|
+
if (slot === this.#watched) return;
|
|
10097
|
+
this.#unwatch();
|
|
10098
|
+
slot.listeners.add(this.#listener);
|
|
10099
|
+
this.#watched = slot;
|
|
10100
|
+
}
|
|
10101
|
+
#unwatch() {
|
|
10102
|
+
this.#watched?.listeners.delete(this.#listener);
|
|
10103
|
+
this.#watched = null;
|
|
10104
|
+
}
|
|
10105
|
+
hostConnected() {
|
|
10106
|
+
this.#connected = true;
|
|
10107
|
+
this.#watch();
|
|
10108
|
+
}
|
|
10109
|
+
hostUpdate() {
|
|
10110
|
+
if (this.#connected) this.#watch();
|
|
10111
|
+
}
|
|
10112
|
+
hostDisconnected() {
|
|
10113
|
+
this.#connected = false;
|
|
10114
|
+
this.#unwatch();
|
|
10115
|
+
}
|
|
10116
|
+
};
|
|
10117
|
+
var ViewSet = class extends ViewState {
|
|
10118
|
+
constructor(host4, key2, initial = () => []) {
|
|
10119
|
+
super(host4, key2, (model) => new Set(initial(model)));
|
|
10120
|
+
}
|
|
10121
|
+
has(id) {
|
|
10122
|
+
return this.value.has(id);
|
|
10123
|
+
}
|
|
10124
|
+
toggle(id, open = !this.has(id)) {
|
|
10125
|
+
if (open === this.has(id)) return;
|
|
10126
|
+
const next = new Set(this.value);
|
|
10127
|
+
if (open) next.add(id);
|
|
10128
|
+
else next.delete(id);
|
|
10129
|
+
this.value = next;
|
|
10130
|
+
}
|
|
10131
|
+
details(id) {
|
|
10132
|
+
return (event2) => this.toggle(id, event2.currentTarget.open);
|
|
10133
|
+
}
|
|
10134
|
+
};
|
|
10135
|
+
|
|
10055
10136
|
// src/app/files.ts
|
|
10056
10137
|
function relative(path) {
|
|
10057
10138
|
return path.replace(/^\//, "");
|
|
@@ -10203,6 +10284,8 @@ var SliccFiles = class extends ModelElement {
|
|
|
10203
10284
|
#entries = [];
|
|
10204
10285
|
#pending = false;
|
|
10205
10286
|
#reveal = null;
|
|
10287
|
+
#folders = new ViewState(this, "files", () => null);
|
|
10288
|
+
#applied = null;
|
|
10206
10289
|
constructor() {
|
|
10207
10290
|
super();
|
|
10208
10291
|
this.count = 0;
|
|
@@ -10358,7 +10441,8 @@ var SliccFiles = class extends ModelElement {
|
|
|
10358
10441
|
const tree = this.#tree();
|
|
10359
10442
|
const changes2 = this.model ? changesOf(this.model).changes() : [];
|
|
10360
10443
|
if (!tree) return;
|
|
10361
|
-
if (tree.paths.length === 0)
|
|
10444
|
+
if (tree.paths.length === 0)
|
|
10445
|
+
tree.expanded = this.#folders.value ?? ancestors(changes2.map((change) => change.path));
|
|
10362
10446
|
tree.paths = treePaths(this.#entries);
|
|
10363
10447
|
tree.gitStatus = treeStatus(changes2);
|
|
10364
10448
|
this.#show();
|
|
@@ -10528,8 +10612,20 @@ var SliccFiles = class extends ModelElement {
|
|
|
10528
10612
|
@tree-search=${this.#seed}
|
|
10529
10613
|
@tree-matches=${this.#matches}
|
|
10530
10614
|
@tree-search-close=${this.#closed}
|
|
10615
|
+
@tree-expand=${this.#expand}
|
|
10531
10616
|
></slicc-file-tree>`;
|
|
10532
10617
|
}
|
|
10618
|
+
#expand(event2) {
|
|
10619
|
+
this.#applied = event2.detail.expanded;
|
|
10620
|
+
this.#folders.value = event2.detail.expanded;
|
|
10621
|
+
}
|
|
10622
|
+
updated() {
|
|
10623
|
+
const folders = this.#folders.value;
|
|
10624
|
+
const tree = this.#tree();
|
|
10625
|
+
if (!folders || folders === this.#applied || !tree?.paths.length) return;
|
|
10626
|
+
this.#applied = folders;
|
|
10627
|
+
tree.expanded = folders;
|
|
10628
|
+
}
|
|
10533
10629
|
};
|
|
10534
10630
|
var SliccFileView = class extends ThemedElement {
|
|
10535
10631
|
static properties = {
|
|
@@ -10995,14 +11091,19 @@ function attention(section) {
|
|
|
10995
11091
|
var SliccMonitor = class extends ModelElement {
|
|
10996
11092
|
static properties = {
|
|
10997
11093
|
...ModelElement.properties,
|
|
10998
|
-
expanded: { state: true },
|
|
10999
11094
|
copied: { state: true }
|
|
11000
11095
|
};
|
|
11096
|
+
#sections = new ViewState(this, "monitor", () => ({}));
|
|
11001
11097
|
constructor() {
|
|
11002
11098
|
super();
|
|
11003
|
-
this.expanded = {};
|
|
11004
11099
|
this.copied = null;
|
|
11005
11100
|
}
|
|
11101
|
+
get expanded() {
|
|
11102
|
+
return this.#sections.value;
|
|
11103
|
+
}
|
|
11104
|
+
set expanded(value) {
|
|
11105
|
+
this.#sections.value = value;
|
|
11106
|
+
}
|
|
11006
11107
|
static styles = [
|
|
11007
11108
|
shared,
|
|
11008
11109
|
panelCss,
|
|
@@ -11460,7 +11561,8 @@ function fillShown(fill2) {
|
|
|
11460
11561
|
}
|
|
11461
11562
|
var SliccAgents = class extends ModelElement {
|
|
11462
11563
|
static properties = { ...ModelElement.properties, errors: { state: true } };
|
|
11463
|
-
#expanded =
|
|
11564
|
+
#expanded = new ViewSet(this, "agents");
|
|
11565
|
+
#revealed = new ViewState(this, "agents:revealed", () => "");
|
|
11464
11566
|
#pointer = false;
|
|
11465
11567
|
#modality = (event2) => {
|
|
11466
11568
|
this.#pointer = event2.type === "pointerdown";
|
|
@@ -11731,16 +11833,16 @@ var SliccAgents = class extends ModelElement {
|
|
|
11731
11833
|
#reveal(model) {
|
|
11732
11834
|
const focused2 = model.agent.list().find((agent) => agent.id === model.agent.active());
|
|
11733
11835
|
const cone = focused2?.kind === "cone" ? focused2.id : focused2?.parentId;
|
|
11734
|
-
if (cone
|
|
11836
|
+
if (!cone || focused2?.id === this.#revealed.value) return;
|
|
11837
|
+
this.#revealed.value = focused2?.id;
|
|
11838
|
+
this.#expanded.toggle(cone, true);
|
|
11735
11839
|
}
|
|
11736
11840
|
#select(agent) {
|
|
11737
11841
|
this.model?.agent.select(agent.id);
|
|
11738
11842
|
if (agent.kind === "cone") this.#toggle(agent, true);
|
|
11739
11843
|
}
|
|
11740
11844
|
#toggle(agent, open = !this.#expanded.has(agent.id)) {
|
|
11741
|
-
|
|
11742
|
-
else this.#expanded.delete(agent.id);
|
|
11743
|
-
this.requestUpdate();
|
|
11845
|
+
this.#expanded.toggle(agent.id, open);
|
|
11744
11846
|
}
|
|
11745
11847
|
focus() {
|
|
11746
11848
|
this.focusOn('li[tabindex="0"]');
|
|
@@ -13783,6 +13885,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
13783
13885
|
guestError: { state: true },
|
|
13784
13886
|
guestsOff: { state: true }
|
|
13785
13887
|
};
|
|
13888
|
+
#open = new ViewSet(this, "network");
|
|
13786
13889
|
#leaderId = "";
|
|
13787
13890
|
#guestId = "";
|
|
13788
13891
|
constructor() {
|
|
@@ -14500,7 +14603,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
14500
14603
|
</div>
|
|
14501
14604
|
${this.#permission(port, links)}
|
|
14502
14605
|
${links.devices.length ? b`<ul aria-labelledby="links">${links.devices.map((device) => this.#device(port, status2, device))}</ul>
|
|
14503
|
-
<details><summary>Link another device</summary><div>${this.#join(port, links)}</div></details>` : this.#join(port, links)}
|
|
14606
|
+
<details ?open=${this.#open.has("link")} @toggle=${this.#open.details("link")}><summary>Link another device</summary><div>${this.#join(port, links)}</div></details>` : this.#join(port, links)}
|
|
14504
14607
|
${this.linksError ? b`<div class="error" role="alert" data-error="links"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.linksError}</span></div>` : A}
|
|
14505
14608
|
</section>`;
|
|
14506
14609
|
}
|
|
@@ -14699,7 +14802,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
14699
14802
|
}
|
|
14700
14803
|
#whole(status2) {
|
|
14701
14804
|
if (wholeWeb.has(routeOf(status2))) {
|
|
14702
|
-
return b`<details><summary>Other ways to reach the whole web</summary><div>${this.#ways(status2)}</div></details>`;
|
|
14805
|
+
return b`<details ?open=${this.#open.has("whole")} @toggle=${this.#open.details("whole")}><summary>Other ways to reach the whole web</summary><div>${this.#ways(status2)}</div></details>`;
|
|
14703
14806
|
}
|
|
14704
14807
|
return b`<section aria-labelledby="whole">
|
|
14705
14808
|
<h2 id="whole">Get the whole web</h2>
|
|
@@ -31832,7 +31935,7 @@ function lick(message, handlers, stamp = true, pile = flatPile) {
|
|
|
31832
31935
|
b`<div class="lick-frame"><div class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}><div class="lick-head">${head2}</div></div>${reaction}</div>`
|
|
31833
31936
|
);
|
|
31834
31937
|
}
|
|
31835
|
-
const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}>
|
|
31938
|
+
const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A} ?open=${handlers.licks?.has(message.id) ?? false} @toggle=${handlers.licks?.details(message.id) ?? A}>
|
|
31836
31939
|
<summary class="lick-head">${head2}<swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right></summary>
|
|
31837
31940
|
<pre class="output">${message.body}</pre>
|
|
31838
31941
|
</details>`;
|
|
@@ -32865,6 +32968,7 @@ var SliccUpdates = class extends ModelElement {
|
|
|
32865
32968
|
busy: { state: true },
|
|
32866
32969
|
problems: { state: true }
|
|
32867
32970
|
};
|
|
32971
|
+
#logs = new ViewSet(this, "updates");
|
|
32868
32972
|
constructor() {
|
|
32869
32973
|
super();
|
|
32870
32974
|
this.pending = /* @__PURE__ */ new Set();
|
|
@@ -33244,7 +33348,7 @@ var SliccUpdates = class extends ModelElement {
|
|
|
33244
33348
|
<span>${error}</span>
|
|
33245
33349
|
</div>` : A}
|
|
33246
33350
|
${this.#actions(item2)}
|
|
33247
|
-
${item2.log ? b`<details><summary>Install log</summary><pre>${item2.log}</pre></details>` : A}
|
|
33351
|
+
${item2.log ? b`<details ?open=${this.#logs.has(`item:${item2.id}`)} @toggle=${this.#logs.details(`item:${item2.id}`)}><summary>Install log</summary><pre>${item2.log}</pre></details>` : A}
|
|
33248
33352
|
</article>`;
|
|
33249
33353
|
}
|
|
33250
33354
|
#packageMeta(item2, all2) {
|
|
@@ -33318,7 +33422,7 @@ var SliccUpdates = class extends ModelElement {
|
|
|
33318
33422
|
<swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>
|
|
33319
33423
|
<span>${error}</span>
|
|
33320
33424
|
</div>` : A}
|
|
33321
|
-
${item2.log ? b`<details><summary>Install log</summary><pre><span class="package">${item2.package}</span>\n${item2.log}</pre></details>` : A}
|
|
33425
|
+
${item2.log ? b`<details ?open=${this.#logs.has(`package:${item2.id}`)} @toggle=${this.#logs.details(`package:${item2.id}`)}><summary>Install log</summary><pre><span class="package">${item2.package}</span>\n${item2.log}</pre></details>` : A}
|
|
33322
33426
|
</li>`;
|
|
33323
33427
|
}
|
|
33324
33428
|
#list(items, all2, port) {
|
|
@@ -35271,7 +35375,9 @@ var SliccBrowser = class extends ModelElement {
|
|
|
35271
35375
|
#last = null;
|
|
35272
35376
|
#shown = actionPage;
|
|
35273
35377
|
#anchor = null;
|
|
35274
|
-
#expanded =
|
|
35378
|
+
#expanded = new ViewSet(this, "browser");
|
|
35379
|
+
#recent = new ViewState(this, "browser:recent", () => false);
|
|
35380
|
+
#chosen = new ViewState(this, "browser:tab", () => void 0);
|
|
35275
35381
|
constructor() {
|
|
35276
35382
|
super();
|
|
35277
35383
|
this.address = "";
|
|
@@ -35742,7 +35848,6 @@ var SliccBrowser = class extends ModelElement {
|
|
|
35742
35848
|
this.#shots.clear();
|
|
35743
35849
|
this.#frames.clear();
|
|
35744
35850
|
this.#pulse.clear();
|
|
35745
|
-
this.#expanded.clear();
|
|
35746
35851
|
this.#shown = actionPage;
|
|
35747
35852
|
const tabs = model.browser.list();
|
|
35748
35853
|
this.#driven = new Set(tabs.filter((tab) => tab.controlled).map((tab) => tab.id));
|
|
@@ -35805,7 +35910,10 @@ var SliccBrowser = class extends ModelElement {
|
|
|
35805
35910
|
this.#driven = driven;
|
|
35806
35911
|
const ids = new Set(tabs.map((tab) => tab.id));
|
|
35807
35912
|
for (const id of this.#frames.keys()) if (!ids.has(id)) this.#frames.delete(id);
|
|
35808
|
-
if (this.#focus && !ids.has(this.#focus))
|
|
35913
|
+
if (this.#focus && !ids.has(this.#focus)) {
|
|
35914
|
+
this.#show(null);
|
|
35915
|
+
this.#chosen.value = void 0;
|
|
35916
|
+
}
|
|
35809
35917
|
this.#stale();
|
|
35810
35918
|
this.requestUpdate();
|
|
35811
35919
|
}
|
|
@@ -35859,11 +35967,22 @@ var SliccBrowser = class extends ModelElement {
|
|
|
35859
35967
|
return shot ?? null;
|
|
35860
35968
|
}
|
|
35861
35969
|
#show(id) {
|
|
35970
|
+
this.#chosen.value = id;
|
|
35971
|
+
this.#visit(id);
|
|
35972
|
+
this.requestUpdate();
|
|
35973
|
+
}
|
|
35974
|
+
#visit(id) {
|
|
35862
35975
|
this.#focus = id;
|
|
35863
35976
|
this.#copied = null;
|
|
35864
35977
|
this.#shown = actionPage;
|
|
35865
35978
|
if (id) this.#pulse.delete(id);
|
|
35866
|
-
|
|
35979
|
+
}
|
|
35980
|
+
willUpdate(changed) {
|
|
35981
|
+
super.willUpdate(changed);
|
|
35982
|
+
const chosen = this.#chosen.value;
|
|
35983
|
+
if (chosen === void 0 || chosen === this.#focus) return;
|
|
35984
|
+
if (chosen === null || this.model?.browser.list().some((tab) => tab.id === chosen))
|
|
35985
|
+
this.#visit(chosen);
|
|
35867
35986
|
}
|
|
35868
35987
|
#focused() {
|
|
35869
35988
|
return this.model?.browser.list().find((tab) => tab.id === this.#focus);
|
|
@@ -36021,8 +36140,7 @@ var SliccBrowser = class extends ModelElement {
|
|
|
36021
36140
|
this.requestUpdate();
|
|
36022
36141
|
}
|
|
36023
36142
|
#toggle(id) {
|
|
36024
|
-
|
|
36025
|
-
this.requestUpdate();
|
|
36143
|
+
this.#expanded.toggle(id);
|
|
36026
36144
|
}
|
|
36027
36145
|
#what(action) {
|
|
36028
36146
|
const url = linked.has(action.kind) ? action.value : void 0;
|
|
@@ -36052,7 +36170,7 @@ var SliccBrowser = class extends ModelElement {
|
|
|
36052
36170
|
if (this.#width >= narrowWidth)
|
|
36053
36171
|
return b`<h3>Recent actions</h3>${more}<ol class="actions" aria-label="Recent actions" tabindex="0">${actions2.map((action) => this.#row(action))}</ol>`;
|
|
36054
36172
|
const last = actions2.at(-1);
|
|
36055
|
-
return b`<details>
|
|
36173
|
+
return b`<details ?open=${this.#recent.value} @toggle=${(event2) => this.#recent.value = event2.currentTarget.open}>
|
|
36056
36174
|
<summary><swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right>Recent actions (${all2.length})</summary>
|
|
36057
36175
|
${more}
|
|
36058
36176
|
<ol class="actions" aria-label="Earlier actions" tabindex="0">${actions2.slice(0, -1).map((action) => this.#row(action))}</ol>
|
|
@@ -37192,7 +37310,9 @@ var SliccChat = class extends ThemedElement {
|
|
|
37192
37310
|
}
|
|
37193
37311
|
`
|
|
37194
37312
|
];
|
|
37313
|
+
#licks = new ViewSet(this, "chat:licks");
|
|
37195
37314
|
#handlers = {
|
|
37315
|
+
licks: this.#licks,
|
|
37196
37316
|
color: "light",
|
|
37197
37317
|
model: null,
|
|
37198
37318
|
readOnly: false,
|
|
@@ -40051,17 +40171,22 @@ var SliccMemory = class extends ModelElement {
|
|
|
40051
40171
|
query: { state: true },
|
|
40052
40172
|
tag: { state: true },
|
|
40053
40173
|
scope: { state: true },
|
|
40054
|
-
open: { state: true },
|
|
40055
40174
|
editing: { state: true }
|
|
40056
40175
|
};
|
|
40176
|
+
#open = new ViewState(this, "memory", () => null);
|
|
40057
40177
|
constructor() {
|
|
40058
40178
|
super();
|
|
40059
40179
|
this.query = "";
|
|
40060
40180
|
this.tag = "all";
|
|
40061
40181
|
this.scope = everyone;
|
|
40062
|
-
this.open = null;
|
|
40063
40182
|
this.editing = null;
|
|
40064
40183
|
}
|
|
40184
|
+
get open() {
|
|
40185
|
+
return this.#open.value;
|
|
40186
|
+
}
|
|
40187
|
+
set open(value) {
|
|
40188
|
+
this.#open.value = value;
|
|
40189
|
+
}
|
|
40065
40190
|
static styles = [
|
|
40066
40191
|
shared,
|
|
40067
40192
|
panelCss,
|
|
@@ -40708,6 +40833,7 @@ var SliccSecrets = class extends ThemedElement {
|
|
|
40708
40833
|
picked: { state: true },
|
|
40709
40834
|
again: { state: true }
|
|
40710
40835
|
};
|
|
40836
|
+
#removed = new ViewState(this, "settings:removed", () => false);
|
|
40711
40837
|
#tick;
|
|
40712
40838
|
#offline = false;
|
|
40713
40839
|
constructor() {
|
|
@@ -41516,7 +41642,7 @@ var SliccSecrets = class extends ThemedElement {
|
|
|
41516
41642
|
const live = sorted.filter((secret) => secret.state !== "removed");
|
|
41517
41643
|
const removed = sorted.filter((secret) => secret.state === "removed");
|
|
41518
41644
|
return b`${live.length ? b`<ul class="secrets" aria-labelledby="secrets">${live.map(row)}</ul>` : A}
|
|
41519
|
-
${removed.length ? b`<details class="removed" data-removed>
|
|
41645
|
+
${removed.length ? b`<details class="removed" data-removed ?open=${this.#removed.value} @toggle=${(event2) => this.#removed.value = event2.currentTarget.open}>
|
|
41520
41646
|
<summary>Removed (${removed.length})</summary>
|
|
41521
41647
|
<ul class="secrets">${removed.map(row)}</ul>
|
|
41522
41648
|
</details>` : A}`;
|
|
@@ -42758,7 +42884,6 @@ ${data.html}`;
|
|
|
42758
42884
|
};
|
|
42759
42885
|
|
|
42760
42886
|
// src/app/tool-runs.ts
|
|
42761
|
-
var opened = /* @__PURE__ */ new Map();
|
|
42762
42887
|
var caches = /* @__PURE__ */ new WeakMap();
|
|
42763
42888
|
function cache(port) {
|
|
42764
42889
|
const found = caches.get(port);
|
|
@@ -42775,6 +42900,7 @@ var SliccToolRuns = class extends i2 {
|
|
|
42775
42900
|
model: { attribute: false },
|
|
42776
42901
|
owners: { attribute: false }
|
|
42777
42902
|
};
|
|
42903
|
+
#opened = new ViewState(this, "chat:runs", () => /* @__PURE__ */ new Map());
|
|
42778
42904
|
#icons = null;
|
|
42779
42905
|
#count = 0;
|
|
42780
42906
|
#resize = new ResizeObserver(() => this.#end());
|
|
@@ -42804,12 +42930,13 @@ var SliccToolRuns = class extends i2 {
|
|
|
42804
42930
|
return this.calls.map((call) => call.id).join(" ");
|
|
42805
42931
|
}
|
|
42806
42932
|
get #open() {
|
|
42807
|
-
return opened.get(this.#anchor) ?? "";
|
|
42933
|
+
return this.#opened.value.get(this.#anchor) ?? "";
|
|
42808
42934
|
}
|
|
42809
42935
|
#set(value) {
|
|
42810
|
-
|
|
42811
|
-
|
|
42812
|
-
|
|
42936
|
+
const next = new Map(this.#opened.value);
|
|
42937
|
+
if (value) next.set(this.#anchor, value);
|
|
42938
|
+
else next.delete(this.#anchor);
|
|
42939
|
+
this.#opened.value = next;
|
|
42813
42940
|
}
|
|
42814
42941
|
#toggled(call, event2) {
|
|
42815
42942
|
const open = event2.currentTarget.open;
|
|
@@ -85330,11 +85457,13 @@ var SliccFileTree = class extends HTMLElement {
|
|
|
85330
85457
|
#expanded = [];
|
|
85331
85458
|
#mount = null;
|
|
85332
85459
|
#query = "";
|
|
85460
|
+
#chosen = [];
|
|
85461
|
+
#applying = false;
|
|
85333
85462
|
get paths() {
|
|
85334
85463
|
return this.#paths;
|
|
85335
85464
|
}
|
|
85336
85465
|
set paths(value) {
|
|
85337
|
-
const expanded = this.#paths.length > 0 ? this.#
|
|
85466
|
+
const expanded = this.#paths.length > 0 ? this.#held() : this.#expanded;
|
|
85338
85467
|
this.#paths = value;
|
|
85339
85468
|
this.#tree?.resetPaths(value, { initialExpandedPaths: expanded });
|
|
85340
85469
|
this.#research();
|
|
@@ -85352,10 +85481,15 @@ var SliccFileTree = class extends HTMLElement {
|
|
|
85352
85481
|
return this.#query;
|
|
85353
85482
|
}
|
|
85354
85483
|
set query(value) {
|
|
85484
|
+
const was = this.#query;
|
|
85355
85485
|
this.#query = value;
|
|
85356
85486
|
this.#tree?.setSearch(value || null);
|
|
85487
|
+
if (was && !value) this.expanded = this.#chosen;
|
|
85357
85488
|
this.#matched();
|
|
85358
85489
|
}
|
|
85490
|
+
#held() {
|
|
85491
|
+
return this.#query ? this.#chosen : this.#expandedNow();
|
|
85492
|
+
}
|
|
85359
85493
|
get empty() {
|
|
85360
85494
|
return this.hasAttribute("data-empty");
|
|
85361
85495
|
}
|
|
@@ -85375,10 +85509,12 @@ var SliccFileTree = class extends HTMLElement {
|
|
|
85375
85509
|
}
|
|
85376
85510
|
#expandedNow() {
|
|
85377
85511
|
const tree = this.#tree;
|
|
85378
|
-
|
|
85379
|
-
const item2 =
|
|
85380
|
-
return
|
|
85381
|
-
}
|
|
85512
|
+
const open = (path) => {
|
|
85513
|
+
const item2 = tree?.getItem(path);
|
|
85514
|
+
return item2 && "isExpanded" in item2 ? item2.isExpanded() : null;
|
|
85515
|
+
};
|
|
85516
|
+
const shown4 = (path) => path.split("/").slice(0, -2).every((_9, i26, parts2) => open(`${parts2.slice(0, i26 + 1).join("/")}/`) !== false);
|
|
85517
|
+
return this.#paths.filter((path) => path.endsWith("/") && open(path) === true && shown4(path)).map((path) => path.slice(0, -1));
|
|
85382
85518
|
}
|
|
85383
85519
|
get gitStatus() {
|
|
85384
85520
|
return this.#status;
|
|
@@ -85392,6 +85528,36 @@ var SliccFileTree = class extends HTMLElement {
|
|
|
85392
85528
|
}
|
|
85393
85529
|
set expanded(value) {
|
|
85394
85530
|
this.#expanded = value;
|
|
85531
|
+
this.#apply();
|
|
85532
|
+
}
|
|
85533
|
+
#apply() {
|
|
85534
|
+
const tree = this.#tree;
|
|
85535
|
+
if (!tree || this.#paths.length === 0) return;
|
|
85536
|
+
if (this.#query) {
|
|
85537
|
+
this.#chosen = this.#expanded;
|
|
85538
|
+
return;
|
|
85539
|
+
}
|
|
85540
|
+
const wanted = new Set(this.#expanded);
|
|
85541
|
+
this.#applying = true;
|
|
85542
|
+
for (const path of this.#paths) {
|
|
85543
|
+
const item2 = path.endsWith("/") ? tree.getItem(path) : null;
|
|
85544
|
+
if (!item2 || !("isExpanded" in item2)) continue;
|
|
85545
|
+
const open = wanted.has(path.slice(0, -1));
|
|
85546
|
+
if (item2.isExpanded() === open) continue;
|
|
85547
|
+
if (open) item2.expand();
|
|
85548
|
+
else item2.collapse();
|
|
85549
|
+
}
|
|
85550
|
+
this.#applying = false;
|
|
85551
|
+
this.#chosen = this.#expandedNow();
|
|
85552
|
+
}
|
|
85553
|
+
#changed() {
|
|
85554
|
+
if (this.#applying || !this.#tree || this.#query) return;
|
|
85555
|
+
const expanded = this.#expandedNow();
|
|
85556
|
+
if (expanded.join("\n") === this.#chosen.join("\n")) return;
|
|
85557
|
+
this.#chosen = expanded;
|
|
85558
|
+
this.dispatchEvent(
|
|
85559
|
+
new CustomEvent("tree-expand", { detail: { expanded }, bubbles: true, composed: true })
|
|
85560
|
+
);
|
|
85395
85561
|
}
|
|
85396
85562
|
get tree() {
|
|
85397
85563
|
return this.#tree;
|
|
@@ -85418,9 +85584,12 @@ var SliccFileTree = class extends HTMLElement {
|
|
|
85418
85584
|
onSearchChange: (value) => this.#searched(value)
|
|
85419
85585
|
});
|
|
85420
85586
|
this.#tree.render({ containerWrapper: this.#mount });
|
|
85587
|
+
this.#chosen = this.#query ? this.#expanded : this.#expandedNow();
|
|
85588
|
+
this.#tree.subscribe(() => this.#changed());
|
|
85421
85589
|
this.#matched();
|
|
85422
85590
|
}
|
|
85423
85591
|
disconnectedCallback() {
|
|
85592
|
+
if (this.#paths.length > 0) this.#expanded = this.#held();
|
|
85424
85593
|
this.#tree?.cleanUp();
|
|
85425
85594
|
this.#tree = null;
|
|
85426
85595
|
this.#mount?.replaceChildren();
|