@ai-ecoverse/slicc-spectrum 1.5.0 → 1.6.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 +3 -3
- package/dist/slicc-ui.js +342 -29
- package/dist/slicc-ui.js.map +4 -4
- package/dist/types/app/app.d.ts +2 -0
- package/dist/types/app/chat.d.ts +8 -0
- package/dist/types/app/composer.d.ts +4 -1
- package/dist/types/app/dips.d.ts +13 -0
- package/dist/types/app/panels.d.ts +13 -2
- package/dist/types/app/spectrum.d.ts +1 -0
- package/dist/types/components/dock.d.ts +2 -0
- package/package.json +2 -2
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.<screen>`). Every surface has a home side, left, center or right. Closed panels wait as icons in the left and right rails (on a phone, in one bar at the bottom); one click puts a panel back on its side, next to its neighbours.
|
|
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. Closed panels wait as icons in the left and right rails (on a phone, in one bar at the bottom); one click puts a panel back on its side, next to its neighbours. 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, the tray and the agent part of the status bar show only when `chat` is offered, the change count only with `changes`, and the settings button only with `settings`. Elements with `slot="status"` go into the status bar, after the built-in items.
|
|
125
125
|
|
|
@@ -134,7 +134,7 @@ Text is Adobe Clean and code Spectrum's Source Code Pro. Source Code Pro (OFL) i
|
|
|
134
134
|
| `<slicc-app>` | The shell: header with the agent picker, the View menu and the theme switch, rails, a `<slicc-dock>`, and a status bar. Saves the dock layout per screen class to `storage` (default `localStorage`) under `layoutKey`. |
|
|
135
135
|
| `<slicc-dock>` | Generic. [dockview-core](https://dockview.dev/) 8 with a theme mapped to Spectrum tokens, in its own shadow root. Panels come from `factories` (`name → (params) => HTMLElement`). `open`, `close`, `has`, `content`, `focusPanel`, `cycleGroup`, `cycleTab`, `toJSON`, `restore`, `clear`, and the full `api`. Tabs have a context menu (close, close others, float, maximize). Events: `layout-change`, `active-panel-change`, `panel-close`. |
|
|
136
136
|
| `<slicc-agents>` | Cones with their scoops, status, context fill and unread count. Selecting one makes it the active agent. |
|
|
137
|
-
| `<slicc-chat>` |
|
|
137
|
+
| `<slicc-chat>` | One thread's conversation (its `agent`, or the active agent if unset), by day: user messages (with attachments, steered, or delegated from another cone), assistant messages (Markdown with tables, task lists, quotes, code and images; thinking; tool calls, grouped into a collapsible cluster from three in a row; media galleries with images, video and audio; action cards for tools, pull requests and status; plans and checklists; inline diffs; question prompts; delegations; link previews; errors with a fix-it action), standalone tool results, system messages (compaction markers, error cards, notices) and licks, SLICC's events into a cone, on every channel. Permission requests are allowed or denied in place. |
|
|
138
138
|
| `<slicc-composer>` | The message box. <kbd>Enter</kbd> sends, or queues behind a running reply; <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>Enter</kbd> steers (interrupts); <kbd>Shift</kbd>+<kbd>Enter</kbd> adds a line; <kbd>Tab</kbd> takes the suggested follow-up; <kbd>↑</kbd>/<kbd>↓</kbd> at the start or end walk this agent's history; <kbd>Esc</kbd> stops. `/` completes commands (`/clear`, `/compact`, `/model`, `/thinking`, `/scoop`, `/stop`, `/theme`) and their arguments, `@` mentions agents and files. Attach from the computer, from SLICC's files, a browser screenshot or a shared secret, or paste and drop files (25 MB each). Queued messages wait above it, to send now or drop. Model and thinking pickers, and dictation where the browser has speech recognition. Drafts are kept per agent. |
|
|
139
139
|
| `<slicc-files>` | The file system as a `<slicc-file-tree>`, with pending changes as git status. Opening a file opens it in a tab. |
|
|
140
140
|
| `<slicc-file-view>` | One file (`path`), highlighted, with its pending change and a link to the diff. |
|
|
@@ -146,7 +146,7 @@ Text is Adobe Clean and code Spectrum's Source Code Pro. Source Code Pro (OFL) i
|
|
|
146
146
|
| `<slicc-memory>` | What agents remember, globally or per cone, grouped by section: search, filter by tag, expand, edit, add and forget. |
|
|
147
147
|
| `<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). |
|
|
148
148
|
| `<slicc-freezer>` | Frozen cones, archived with their scoops: thaw to bring one back, delete, or freeze the active cone (also `/freeze`). |
|
|
149
|
-
| `<slicc-sprinkle>` | A SLICC sprinkle (`.shtml`), as a panel or, with `inline`, as a dip in the chat that grows to fit. 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()`). Its only way back is `slicc.lick({ action, data })`, which reaches the owning cone as a `sprinkle` lick. Panel sprinkles sit at the top of the right rail with their Lucide icon. |
|
|
149
|
+
| `<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()`). Its only way back is `slicc.lick({ action, data })`, which reaches the owning cone as a `sprinkle` lick. Panel sprinkles sit at the top of the right rail with their Lucide icon. |
|
|
150
150
|
| `<slicc-tray>` | The tray indicator in the header: connection, the float's name, followers and budget; its panel adds role, runtime, spend, the follower list, the join link, and disconnect or reconnect. |
|
|
151
151
|
| `<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>. |
|
|
152
152
|
| `<slicc-code-view>` | Generic. A highlighted file through [`@pierre/diffs`](https://www.npmjs.com/package/@pierre/diffs): `path`, `contents`, `color`. |
|
package/dist/slicc-ui.js
CHANGED
|
@@ -5332,6 +5332,56 @@ var IconSaveFloppy = class extends IconBase {
|
|
|
5332
5332
|
// node_modules/@spectrum-web-components/icons-workflow/icons/sp-icon-save-floppy.js
|
|
5333
5333
|
defineElement("sp-icon-save-floppy", IconSaveFloppy);
|
|
5334
5334
|
|
|
5335
|
+
// node_modules/@spectrum-web-components/icons-workflow/src/icons-s2/OpenIn.js
|
|
5336
|
+
var OpenInIcon = ({ width: e14 = 24, height: t14 = 24, hidden: l10 = false, title: r13 = "Open In" } = {}) => tag2`<svg
|
|
5337
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5338
|
+
width="${e14}"
|
|
5339
|
+
height="${t14}"
|
|
5340
|
+
viewBox="0 0 20 20"
|
|
5341
|
+
aria-hidden=${l10 ? "true" : "false"}
|
|
5342
|
+
role="img"
|
|
5343
|
+
fill="currentColor"
|
|
5344
|
+
aria-label="${r13}"
|
|
5345
|
+
>
|
|
5346
|
+
<path
|
|
5347
|
+
d="m18,15.75V4.25c0-1.24072-1.00928-2.25-2.25-2.25H4.25c-1.24072,0-2.25,1.00928-2.25,2.25v3.71777c0,.41406.33594.75.75.75s.75-.33594.75-.75v-3.71777c0-.41357.33643-.75.75-.75h11.5c.41357,0,.75.33643.75.75v11.5c0,.41357-.33643.75-.75.75h-3.81104c-.41406,0-.75.33594-.75.75s.33594.75.75.75h3.81104c1.24072,0,2.25-1.00928,2.25-2.25Z"
|
|
5348
|
+
fill="currentColor"
|
|
5349
|
+
/>
|
|
5350
|
+
<path
|
|
5351
|
+
d="m11,9.75v4.24268c0,.41406-.33594.75-.75.75s-.75-.33594-.75-.75v-2.43213l-6.46973,6.46973c-.14648.14648-.33838.21973-.53027.21973s-.38379-.07324-.53027-.21973c-.29297-.29297-.29297-.76758,0-1.06055l6.46973-6.46973h-2.43213c-.41406,0-.75-.33594-.75-.75s.33594-.75.75-.75h4.24268c.41406,0,.75.33594.75.75Z"
|
|
5352
|
+
fill="currentColor"
|
|
5353
|
+
/>
|
|
5354
|
+
</svg>`;
|
|
5355
|
+
|
|
5356
|
+
// node_modules/@spectrum-web-components/icons-workflow/src/icons/OpenIn.js
|
|
5357
|
+
var OpenInIcon2 = ({ width: a11 = 24, height: e14 = 24, hidden: t14 = false, title: l10 = "Open In" } = {}) => tag2`<svg
|
|
5358
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5359
|
+
width="${a11}"
|
|
5360
|
+
height="${e14}"
|
|
5361
|
+
viewBox="0 0 36 36"
|
|
5362
|
+
aria-hidden=${t14 ? "true" : "false"}
|
|
5363
|
+
role="img"
|
|
5364
|
+
fill="currentColor"
|
|
5365
|
+
aria-label="${l10}"
|
|
5366
|
+
>
|
|
5367
|
+
<path
|
|
5368
|
+
d="M33 2H3a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V6h24v24H19a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1Z"
|
|
5369
|
+
/>
|
|
5370
|
+
<path
|
|
5371
|
+
d="M18.636 27.764a.784.784 0 0 0 .56.236.803.803 0 0 0 .804-.754v-10.75a.497.497 0 0 0-.496-.496H8.754a.803.803 0 0 0-.754.804.785.785 0 0 0 .235.56l3.786 3.786-9.042 9.042a1 1 0 0 0 0 1.415l1.414 1.414a1 1 0 0 0 1.414 0l9.043-9.042Z"
|
|
5372
|
+
/>
|
|
5373
|
+
</svg>`;
|
|
5374
|
+
|
|
5375
|
+
// node_modules/@spectrum-web-components/icons-workflow/src/elements/IconOpenIn.js
|
|
5376
|
+
var IconOpenIn = class extends IconBase {
|
|
5377
|
+
render() {
|
|
5378
|
+
return setCustomTemplateLiteralTag2(b), this.spectrumVersion === 2 ? OpenInIcon({ hidden: !this.label, title: this.label }) : OpenInIcon2({ hidden: !this.label, title: this.label });
|
|
5379
|
+
}
|
|
5380
|
+
};
|
|
5381
|
+
|
|
5382
|
+
// node_modules/@spectrum-web-components/icons-workflow/icons/sp-icon-open-in.js
|
|
5383
|
+
defineElement("sp-icon-open-in", IconOpenIn);
|
|
5384
|
+
|
|
5335
5385
|
// node_modules/@spectrum-web-components/icons-workflow/src/icons-s2/GlobeGrid.js
|
|
5336
5386
|
var GlobeGridIcon = ({ width: e14 = 24, height: t14 = 24, hidden: r13 = false, title: l10 = "Globe Grid" } = {}) => tag2`<svg
|
|
5337
5387
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -6068,6 +6118,45 @@ var u8 = n7(b);
|
|
|
6068
6118
|
var c9 = n7(w);
|
|
6069
6119
|
var $2 = n7(T);
|
|
6070
6120
|
|
|
6121
|
+
// src/app/dips.ts
|
|
6122
|
+
var dipType = "application/x-slicc-sprinkle";
|
|
6123
|
+
var Dips = class extends Emitter {
|
|
6124
|
+
key = "slicc-ui.dips";
|
|
6125
|
+
#out = /* @__PURE__ */ new Set();
|
|
6126
|
+
#storage = null;
|
|
6127
|
+
load(storage) {
|
|
6128
|
+
this.#storage = storage;
|
|
6129
|
+
try {
|
|
6130
|
+
const saved = JSON.parse(storage?.getItem(this.key) ?? "[]");
|
|
6131
|
+
this.#out = new Set(Array.isArray(saved) ? saved.filter((id) => typeof id === "string") : []);
|
|
6132
|
+
} catch {
|
|
6133
|
+
this.#out = /* @__PURE__ */ new Set();
|
|
6134
|
+
}
|
|
6135
|
+
this.emit("change", this.list());
|
|
6136
|
+
}
|
|
6137
|
+
list() {
|
|
6138
|
+
return [...this.#out];
|
|
6139
|
+
}
|
|
6140
|
+
has(id) {
|
|
6141
|
+
return this.#out.has(id);
|
|
6142
|
+
}
|
|
6143
|
+
detach(id) {
|
|
6144
|
+
if (this.#out.has(id)) return;
|
|
6145
|
+
this.#out.add(id);
|
|
6146
|
+
this.#storage?.setItem(this.key, JSON.stringify(this.list()));
|
|
6147
|
+
this.emit("change", this.list());
|
|
6148
|
+
}
|
|
6149
|
+
};
|
|
6150
|
+
function dipsOf(node) {
|
|
6151
|
+
let current = node;
|
|
6152
|
+
while (current) {
|
|
6153
|
+
const found = current.dips;
|
|
6154
|
+
if (found instanceof Dips) return found;
|
|
6155
|
+
current = current.parentNode ?? current.host ?? null;
|
|
6156
|
+
}
|
|
6157
|
+
return null;
|
|
6158
|
+
}
|
|
6159
|
+
|
|
6071
6160
|
// src/app/fonts.ts
|
|
6072
6161
|
var adobeClean = [
|
|
6073
6162
|
["Regular", 400],
|
|
@@ -6571,11 +6660,39 @@ function openSurface(dock, item2, screen) {
|
|
|
6571
6660
|
...fresh && item2.width && screen !== "phone" ? { initialWidth: item2.width } : {}
|
|
6572
6661
|
});
|
|
6573
6662
|
}
|
|
6663
|
+
function chatId(agentId) {
|
|
6664
|
+
return `chat:${agentId}`;
|
|
6665
|
+
}
|
|
6666
|
+
function chatAgent(id) {
|
|
6667
|
+
return id?.startsWith("chat:") ? id.slice(5) : null;
|
|
6668
|
+
}
|
|
6669
|
+
function chats(dock) {
|
|
6670
|
+
return dock.api.panels.map((panel) => panel.id).filter((id) => chatAgent(id) !== null);
|
|
6671
|
+
}
|
|
6672
|
+
function openChat(dock, agent, screen, focus = true) {
|
|
6673
|
+
const id = chatId(agent.id);
|
|
6674
|
+
if (!dock.has(id)) {
|
|
6675
|
+
const sibling = chats(dock)[0];
|
|
6676
|
+
const place = sibling ? { position: { referencePanel: sibling, direction: "within" } } : placement(dock, "center", screen);
|
|
6677
|
+
dock.open({
|
|
6678
|
+
id,
|
|
6679
|
+
component: "chat",
|
|
6680
|
+
title: agent.name,
|
|
6681
|
+
params: { agent: agent.id },
|
|
6682
|
+
inactive: !focus,
|
|
6683
|
+
...place
|
|
6684
|
+
});
|
|
6685
|
+
}
|
|
6686
|
+
if (focus) dock.focusPanel(id);
|
|
6687
|
+
else dock.reveal(id);
|
|
6688
|
+
return id;
|
|
6689
|
+
}
|
|
6574
6690
|
function openDocument(dock, kind, path, screen) {
|
|
6575
6691
|
const id = `${kind}:${path}`;
|
|
6576
6692
|
if (!dock.has(id)) {
|
|
6577
6693
|
const sibling = dock.api.panels.find((panel) => /^(file|diff):/.test(panel.id));
|
|
6578
|
-
const
|
|
6694
|
+
const chat = chats(dock)[0];
|
|
6695
|
+
const place = sibling ? { position: { referencePanel: sibling.id, direction: "within" } } : chat && screen !== "phone" ? { position: { referencePanel: chat, direction: "right" } } : placement(dock, "center", screen);
|
|
6579
6696
|
dock.open({
|
|
6580
6697
|
id,
|
|
6581
6698
|
component: kind,
|
|
@@ -6587,18 +6704,23 @@ function openDocument(dock, kind, path, screen) {
|
|
|
6587
6704
|
dock.focusPanel(id);
|
|
6588
6705
|
return id;
|
|
6589
6706
|
}
|
|
6590
|
-
function defaultLayout(dock, screen, items = surfaces) {
|
|
6707
|
+
function defaultLayout(dock, screen, agent, items = surfaces) {
|
|
6591
6708
|
dock.clear();
|
|
6592
6709
|
const order = ["center", "left", "right"];
|
|
6593
6710
|
for (const side of order) {
|
|
6594
6711
|
for (const item2 of items) {
|
|
6595
|
-
if (item2.side
|
|
6712
|
+
if (item2.side !== side || !item2.open.includes(screen)) continue;
|
|
6713
|
+
if (item2.id !== "chat") openSurface(dock, item2, screen);
|
|
6714
|
+
else if (agent) openChat(dock, agent, screen, false);
|
|
6596
6715
|
}
|
|
6597
6716
|
}
|
|
6598
|
-
|
|
6717
|
+
const chat = chats(dock)[0];
|
|
6718
|
+
if (chat) dock.api.getPanel(chat)?.api.setActive();
|
|
6599
6719
|
}
|
|
6600
6720
|
function closed(dock, items = surfaces) {
|
|
6601
|
-
return items.filter(
|
|
6721
|
+
return items.filter(
|
|
6722
|
+
(item2) => item2.id === "chat" ? chats(dock).length === 0 : !dock.has(item2.id)
|
|
6723
|
+
);
|
|
6602
6724
|
}
|
|
6603
6725
|
function sprinkleSurface(sprinkle) {
|
|
6604
6726
|
return {
|
|
@@ -6610,7 +6732,17 @@ function sprinkleSurface(sprinkle) {
|
|
|
6610
6732
|
open: []
|
|
6611
6733
|
};
|
|
6612
6734
|
}
|
|
6613
|
-
|
|
6735
|
+
var directions = {
|
|
6736
|
+
left: "left",
|
|
6737
|
+
right: "right",
|
|
6738
|
+
top: "above",
|
|
6739
|
+
bottom: "below",
|
|
6740
|
+
center: "within"
|
|
6741
|
+
};
|
|
6742
|
+
function dropPlacement(panel, position) {
|
|
6743
|
+
return panel ? { position: { referencePanel: panel, direction: directions[position] ?? "within" } } : null;
|
|
6744
|
+
}
|
|
6745
|
+
function openSprinkle(dock, sprinkle, screen, place = null) {
|
|
6614
6746
|
const id = `sprinkle:${sprinkle.id}`;
|
|
6615
6747
|
if (!dock.has(id)) {
|
|
6616
6748
|
dock.open({
|
|
@@ -6618,7 +6750,7 @@ function openSprinkle(dock, sprinkle, screen) {
|
|
|
6618
6750
|
component: "sprinkle",
|
|
6619
6751
|
title: sprinkle.title,
|
|
6620
6752
|
params: { sprinkle: sprinkle.id },
|
|
6621
|
-
...placement(dock, "right", screen)
|
|
6753
|
+
...place ?? placement(dock, "right", screen)
|
|
6622
6754
|
});
|
|
6623
6755
|
}
|
|
6624
6756
|
dock.focusPanel(id);
|
|
@@ -6639,7 +6771,8 @@ var SliccApp = class extends ThemedElement {
|
|
|
6639
6771
|
surfaces: { attribute: false }
|
|
6640
6772
|
};
|
|
6641
6773
|
storage = globalThis.localStorage ?? null;
|
|
6642
|
-
layoutKey = "slicc-ui.layout";
|
|
6774
|
+
layoutKey = "slicc-ui.layout.v2";
|
|
6775
|
+
dips = new Dips();
|
|
6643
6776
|
#fontBase = defaultFontBase;
|
|
6644
6777
|
#resize = null;
|
|
6645
6778
|
#started = null;
|
|
@@ -6682,6 +6815,11 @@ var SliccApp = class extends ThemedElement {
|
|
|
6682
6815
|
box-sizing: border-box;
|
|
6683
6816
|
background: var(--spectrum-background-layer-1-color);
|
|
6684
6817
|
}
|
|
6818
|
+
.rail.drop {
|
|
6819
|
+
background: var(--spectrum-accent-background-color-default);
|
|
6820
|
+
outline: 2px solid var(--spectrum-focus-indicator-color);
|
|
6821
|
+
outline-offset: -2px;
|
|
6822
|
+
}
|
|
6685
6823
|
.rail.left {
|
|
6686
6824
|
grid-column: 1;
|
|
6687
6825
|
border-right: 1px solid var(--spectrum-gray-200);
|
|
@@ -6790,10 +6928,20 @@ var SliccApp = class extends ThemedElement {
|
|
|
6790
6928
|
const update = () => this.requestUpdate();
|
|
6791
6929
|
return [
|
|
6792
6930
|
...super.subscribe(model),
|
|
6793
|
-
model.agent.on("agents",
|
|
6794
|
-
|
|
6931
|
+
model.agent.on("agents", () => {
|
|
6932
|
+
this.#prune();
|
|
6933
|
+
update();
|
|
6934
|
+
}),
|
|
6935
|
+
model.agent.on("active", () => {
|
|
6936
|
+
const agent = this.#active();
|
|
6937
|
+
if (this.#started && agent && this.#offers("chat")) {
|
|
6938
|
+
openChat(this.dock, agent, this.screen, false);
|
|
6939
|
+
}
|
|
6940
|
+
update();
|
|
6941
|
+
}),
|
|
6795
6942
|
model.files.on("changes", update),
|
|
6796
|
-
model.sprinkles.on("sprinkles", update)
|
|
6943
|
+
model.sprinkles.on("sprinkles", update),
|
|
6944
|
+
this.dips.on("change", update)
|
|
6797
6945
|
];
|
|
6798
6946
|
}
|
|
6799
6947
|
connectedCallback() {
|
|
@@ -6842,12 +6990,16 @@ var SliccApp = class extends ThemedElement {
|
|
|
6842
6990
|
(params) => create(document2.tag, model, params)
|
|
6843
6991
|
])
|
|
6844
6992
|
),
|
|
6993
|
+
...this.#offers("chat") ? { chat: (params) => create("slicc-chat", model, params) } : {},
|
|
6845
6994
|
sprinkle: (params) => create("slicc-sprinkle", model, params)
|
|
6846
6995
|
};
|
|
6996
|
+
dock.accepts = [dipType];
|
|
6997
|
+
this.dips.load(this.storage);
|
|
6847
6998
|
const saved = this.#saved();
|
|
6848
6999
|
const restored = this.#offered(saved, dock.factories) && dock.restore(saved);
|
|
6849
|
-
if (!restored) defaultLayout(dock, this.screen, this.surfaces);
|
|
7000
|
+
if (!restored) defaultLayout(dock, this.screen, this.#active(), this.surfaces);
|
|
6850
7001
|
this.#started = this.screen;
|
|
7002
|
+
this.#prune();
|
|
6851
7003
|
this.#saving = true;
|
|
6852
7004
|
this.#save();
|
|
6853
7005
|
}
|
|
@@ -6870,11 +7022,32 @@ var SliccApp = class extends ThemedElement {
|
|
|
6870
7022
|
if (this.#saving) this.storage?.setItem(this.storageKey, JSON.stringify(this.dock.toJSON()));
|
|
6871
7023
|
this.requestUpdate();
|
|
6872
7024
|
}
|
|
7025
|
+
#active() {
|
|
7026
|
+
const model = this.model;
|
|
7027
|
+
return model?.agent.list().find((agent) => agent.id === model.agent.active()) ?? null;
|
|
7028
|
+
}
|
|
7029
|
+
#prune() {
|
|
7030
|
+
const dock = this.renderRoot.querySelector("slicc-dock");
|
|
7031
|
+
if (!dock || !this.#started) return;
|
|
7032
|
+
const known = new Set(this.model?.agent.list().map((agent) => agent.id));
|
|
7033
|
+
for (const id of chats(dock)) {
|
|
7034
|
+
if (!known.has(chatAgent(id))) dock.close(id);
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
#activated(event) {
|
|
7038
|
+
const agent = chatAgent(event.detail.id);
|
|
7039
|
+
if (agent && agent !== this.model?.agent.active()) this.model?.agent.select(agent);
|
|
7040
|
+
}
|
|
6873
7041
|
resetLayout() {
|
|
6874
|
-
defaultLayout(this.dock, this.screen, this.surfaces);
|
|
7042
|
+
defaultLayout(this.dock, this.screen, this.#active(), this.surfaces);
|
|
6875
7043
|
this.#save();
|
|
6876
7044
|
}
|
|
6877
7045
|
show(id) {
|
|
7046
|
+
const agent = this.#active();
|
|
7047
|
+
if (id === "chat" && agent && this.#offers("chat")) {
|
|
7048
|
+
openChat(this.dock, agent, this.screen);
|
|
7049
|
+
return;
|
|
7050
|
+
}
|
|
6878
7051
|
const sprinkle = this.model?.sprinkles.list().find((candidate) => `sprinkle:${candidate.id}` === id);
|
|
6879
7052
|
if (sprinkle) {
|
|
6880
7053
|
openSprinkle(this.dock, sprinkle, this.screen);
|
|
@@ -6885,14 +7058,42 @@ var SliccApp = class extends ThemedElement {
|
|
|
6885
7058
|
openSurface(this.dock, item2, this.screen);
|
|
6886
7059
|
this.dock.focusPanel(id);
|
|
6887
7060
|
}
|
|
7061
|
+
#sprinkle(id) {
|
|
7062
|
+
return this.model?.sprinkles.list().find((candidate) => candidate.id === id);
|
|
7063
|
+
}
|
|
7064
|
+
#detach(id, place, open) {
|
|
7065
|
+
const sprinkle = this.#sprinkle(id);
|
|
7066
|
+
if (!sprinkle) return;
|
|
7067
|
+
this.dips.detach(id);
|
|
7068
|
+
if (open) openSprinkle(this.dock, sprinkle, this.screen, place);
|
|
7069
|
+
}
|
|
7070
|
+
#dropped(event) {
|
|
7071
|
+
const { type, data, panel, position } = event.detail;
|
|
7072
|
+
if (type === dipType) this.#detach(data, dropPlacement(panel, position), true);
|
|
7073
|
+
}
|
|
7074
|
+
#railOver(event) {
|
|
7075
|
+
if (!event.dataTransfer?.types.includes(dipType)) return;
|
|
7076
|
+
event.preventDefault();
|
|
7077
|
+
event.currentTarget.classList.add("drop");
|
|
7078
|
+
}
|
|
7079
|
+
#railLeave(event) {
|
|
7080
|
+
event.currentTarget.classList.remove("drop");
|
|
7081
|
+
}
|
|
7082
|
+
#railDrop(event) {
|
|
7083
|
+
event.currentTarget.classList.remove("drop");
|
|
7084
|
+
const id = event.dataTransfer?.getData(dipType);
|
|
7085
|
+
if (!id) return;
|
|
7086
|
+
event.preventDefault();
|
|
7087
|
+
this.#detach(id, null, false);
|
|
7088
|
+
}
|
|
6888
7089
|
#rail(items, side) {
|
|
6889
|
-
return b`<nav class=${`rail ${side}`} aria-label=${`Closed panels, ${side}`}>${items.map(
|
|
7090
|
+
return b`<nav class=${`rail ${side}`} aria-label=${`Closed panels, ${side}`} @dragover=${this.#railOver} @dragleave=${this.#railLeave} @drop=${this.#railDrop}>${items.map(
|
|
6890
7091
|
(item2) => u8`<sp-action-button quiet size="m" label=${`Open ${item2.title}`} title=${item2.title} data-surface=${item2.id} @click=${() => this.show(item2.id)}>${railIcon(item2.icon)}</sp-action-button>`
|
|
6891
7092
|
)}</nav>`;
|
|
6892
7093
|
}
|
|
6893
7094
|
#rails() {
|
|
6894
7095
|
const dock = this.renderRoot.querySelector("slicc-dock");
|
|
6895
|
-
const sprinkles = (this.model?.sprinkles.list() ?? []).filter((sprinkle) => !sprinkle.inline).map(sprinkleSurface).filter((item2) => !dock?.has(item2.id));
|
|
7096
|
+
const sprinkles = (this.model?.sprinkles.list() ?? []).filter((sprinkle) => !sprinkle.inline || this.dips.has(sprinkle.id)).map(sprinkleSurface).filter((item2) => !dock?.has(item2.id));
|
|
6896
7097
|
const shut = dock && this.#started ? [...sprinkles, ...closed(dock, this.surfaces)] : [];
|
|
6897
7098
|
const phone = this.screen === "phone";
|
|
6898
7099
|
return {
|
|
@@ -7002,6 +7203,9 @@ var SliccApp = class extends ThemedElement {
|
|
|
7002
7203
|
<main>
|
|
7003
7204
|
${rails.left}
|
|
7004
7205
|
<slicc-dock empty-text="All panels are closed. Open one from a rail or View." @layout-change=${this.#save}
|
|
7206
|
+
@active-panel-change=${this.#activated}
|
|
7207
|
+
@external-drop=${this.#dropped}
|
|
7208
|
+
@detach-sprinkle=${(event) => this.#detach(event.detail.id, null, true)}
|
|
7005
7209
|
@open-file=${(event) => this.#request("file", event)}
|
|
7006
7210
|
@open-diff=${(event) => this.#request("diff", event)}
|
|
7007
7211
|
@show-surface=${(event) => this.show(event.detail.id)}
|
|
@@ -8410,6 +8614,11 @@ var messageCss = i`
|
|
|
8410
8614
|
|
|
8411
8615
|
// src/app/chat.ts
|
|
8412
8616
|
var SliccChat = class extends ThemedElement {
|
|
8617
|
+
static properties = { ...ThemedElement.properties, agent: {} };
|
|
8618
|
+
constructor() {
|
|
8619
|
+
super();
|
|
8620
|
+
this.agent = "";
|
|
8621
|
+
}
|
|
8413
8622
|
static styles = [
|
|
8414
8623
|
shared,
|
|
8415
8624
|
messageCss,
|
|
@@ -8462,8 +8671,8 @@ var SliccChat = class extends ThemedElement {
|
|
|
8462
8671
|
#handlers = {
|
|
8463
8672
|
color: "light",
|
|
8464
8673
|
model: null,
|
|
8465
|
-
answer: (questionId, answer) => this.model?.agent.answer(this
|
|
8466
|
-
resolve: (messageId, state) => this.model?.agent.resolveLick(this
|
|
8674
|
+
answer: (questionId, answer) => this.model?.agent.answer(this.#id(), questionId, answer),
|
|
8675
|
+
resolve: (messageId, state) => this.model?.agent.resolveLick(this.#id(), messageId, state),
|
|
8467
8676
|
action: (action) => this.#action(action),
|
|
8468
8677
|
open: (path) => this.dispatchEvent(
|
|
8469
8678
|
new CustomEvent("open-file", { detail: { path }, bubbles: true, composed: true })
|
|
@@ -8472,13 +8681,15 @@ var SliccChat = class extends ThemedElement {
|
|
|
8472
8681
|
subscribe(model) {
|
|
8473
8682
|
return [
|
|
8474
8683
|
...super.subscribe(model),
|
|
8475
|
-
model.agent.on("active", () =>
|
|
8684
|
+
model.agent.on("active", () => {
|
|
8685
|
+
if (!this.agent) this.#refresh(true);
|
|
8686
|
+
}),
|
|
8476
8687
|
model.agent.on("agents", () => this.requestUpdate()),
|
|
8477
8688
|
model.agent.on("messages", (agentId) => {
|
|
8478
|
-
if (agentId ===
|
|
8689
|
+
if (agentId === this.#id()) this.#refresh(true);
|
|
8479
8690
|
}),
|
|
8480
8691
|
model.agent.on("message", ({ agentId }) => {
|
|
8481
|
-
if (agentId ===
|
|
8692
|
+
if (agentId === this.#id()) this.#refresh(false);
|
|
8482
8693
|
})
|
|
8483
8694
|
];
|
|
8484
8695
|
}
|
|
@@ -8497,14 +8708,17 @@ var SliccChat = class extends ThemedElement {
|
|
|
8497
8708
|
focus() {
|
|
8498
8709
|
this.focusOn("slicc-composer");
|
|
8499
8710
|
}
|
|
8711
|
+
#id() {
|
|
8712
|
+
return this.agent || (this.model?.agent.active() ?? "");
|
|
8713
|
+
}
|
|
8500
8714
|
#agent() {
|
|
8501
|
-
const
|
|
8502
|
-
return model?.agent.list().find((agent) => agent.id ===
|
|
8715
|
+
const id = this.#id();
|
|
8716
|
+
return this.model?.agent.list().find((agent) => agent.id === id);
|
|
8503
8717
|
}
|
|
8504
8718
|
#action(action) {
|
|
8505
8719
|
const model = this.model;
|
|
8506
8720
|
if (!model) return;
|
|
8507
|
-
const id =
|
|
8721
|
+
const id = this.#id();
|
|
8508
8722
|
if (action === "retry") {
|
|
8509
8723
|
const last = model.agent.messages(id).filter((message) => message.role === "user").at(-1);
|
|
8510
8724
|
if (last) void model.agent.send(id, { text: last.text, attachments: last.attachments });
|
|
@@ -8568,7 +8782,7 @@ var SliccChat = class extends ThemedElement {
|
|
|
8568
8782
|
${agent && messages.length > 0 ? this.#items(messages, agent) : b`<div class="empty">No messages yet. Ask ${agent?.name ?? "the agent"} something.</div>`}
|
|
8569
8783
|
</div>
|
|
8570
8784
|
</div>
|
|
8571
|
-
${this.model ? b`<slicc-composer .model=${this.model}></slicc-composer>` : A}
|
|
8785
|
+
${this.model ? b`<slicc-composer .model=${this.model} .agent=${this.agent}></slicc-composer>` : A}
|
|
8572
8786
|
`;
|
|
8573
8787
|
}
|
|
8574
8788
|
};
|
|
@@ -8660,7 +8874,8 @@ var SliccComposer = class extends ModelElement {
|
|
|
8660
8874
|
value: { state: true },
|
|
8661
8875
|
attachments: { state: true },
|
|
8662
8876
|
popup: { state: true },
|
|
8663
|
-
dictating: { state: true }
|
|
8877
|
+
dictating: { state: true },
|
|
8878
|
+
agent: {}
|
|
8664
8879
|
};
|
|
8665
8880
|
#drafts = /* @__PURE__ */ new Map();
|
|
8666
8881
|
#history = -1;
|
|
@@ -8674,6 +8889,7 @@ var SliccComposer = class extends ModelElement {
|
|
|
8674
8889
|
this.attachments = [];
|
|
8675
8890
|
this.popup = null;
|
|
8676
8891
|
this.dictating = false;
|
|
8892
|
+
this.agent = "";
|
|
8677
8893
|
}
|
|
8678
8894
|
static styles = [
|
|
8679
8895
|
shared,
|
|
@@ -8872,14 +9088,22 @@ var SliccComposer = class extends ModelElement {
|
|
|
8872
9088
|
}
|
|
8873
9089
|
`
|
|
8874
9090
|
];
|
|
9091
|
+
willUpdate(changed) {
|
|
9092
|
+
super.willUpdate(changed);
|
|
9093
|
+
if (changed.has("agent") && this.agent && this.model && this.agent !== this.#agent) {
|
|
9094
|
+
this.#switch(this.agent);
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
8875
9097
|
subscribe(model) {
|
|
8876
|
-
this.#switch(model.agent.active());
|
|
9098
|
+
this.#switch(this.agent || model.agent.active());
|
|
8877
9099
|
void model.files.list().then((entries) => {
|
|
8878
9100
|
this.#paths = entries.filter((entry) => entry.kind === "file").map((entry) => entry.path);
|
|
8879
9101
|
});
|
|
8880
9102
|
const update = () => this.requestUpdate();
|
|
8881
9103
|
return [
|
|
8882
|
-
model.agent.on("active", (id) =>
|
|
9104
|
+
model.agent.on("active", (id) => {
|
|
9105
|
+
if (!this.agent) this.#switch(id);
|
|
9106
|
+
}),
|
|
8883
9107
|
model.agent.on("agents", update),
|
|
8884
9108
|
model.agent.on("messages", update),
|
|
8885
9109
|
model.agent.on("message", update),
|
|
@@ -10131,6 +10355,7 @@ async function frameFonts(files, base) {
|
|
|
10131
10355
|
(font) => Boolean(font.data)
|
|
10132
10356
|
);
|
|
10133
10357
|
}
|
|
10358
|
+
var grip = w`<svg class="grip" viewBox="0 0 8 14" width="8" height="14" aria-hidden="true">${[2, 7, 12].flatMap((y5) => [2, 6].map((x5) => w`<circle cx=${x5} cy=${y5} r="1.25"></circle>`))}</svg>`;
|
|
10134
10359
|
var SliccSprinkle = class extends ThemedElement {
|
|
10135
10360
|
static properties = {
|
|
10136
10361
|
...ThemedElement.properties,
|
|
@@ -10160,6 +10385,43 @@ var SliccSprinkle = class extends ThemedElement {
|
|
|
10160
10385
|
height: 100%;
|
|
10161
10386
|
border: 0;
|
|
10162
10387
|
}
|
|
10388
|
+
.handle {
|
|
10389
|
+
display: flex;
|
|
10390
|
+
align-items: center;
|
|
10391
|
+
gap: 6px;
|
|
10392
|
+
width: max-content;
|
|
10393
|
+
max-width: 100%;
|
|
10394
|
+
box-sizing: border-box;
|
|
10395
|
+
height: 28px;
|
|
10396
|
+
padding: 0 4px 0 6px;
|
|
10397
|
+
border: 1px solid var(--spectrum-gray-200);
|
|
10398
|
+
border-bottom: 0;
|
|
10399
|
+
border-radius: 6px 6px 0 0;
|
|
10400
|
+
background: var(--spectrum-background-layer-1-color);
|
|
10401
|
+
color: var(--spectrum-neutral-subdued-content-color-default);
|
|
10402
|
+
font-size: var(--spectrum-font-size-75);
|
|
10403
|
+
cursor: grab;
|
|
10404
|
+
user-select: none;
|
|
10405
|
+
}
|
|
10406
|
+
.handle:active {
|
|
10407
|
+
cursor: grabbing;
|
|
10408
|
+
}
|
|
10409
|
+
.handle .grip {
|
|
10410
|
+
fill: currentColor;
|
|
10411
|
+
}
|
|
10412
|
+
.handle .name {
|
|
10413
|
+
color: var(--spectrum-neutral-content-color-default);
|
|
10414
|
+
}
|
|
10415
|
+
.moved {
|
|
10416
|
+
display: flex;
|
|
10417
|
+
align-items: center;
|
|
10418
|
+
gap: 8px;
|
|
10419
|
+
padding: 8px 12px;
|
|
10420
|
+
border: 1px dashed var(--spectrum-gray-300);
|
|
10421
|
+
border-radius: 8px;
|
|
10422
|
+
color: var(--spectrum-neutral-subdued-content-color-default);
|
|
10423
|
+
font-size: var(--spectrum-font-size-75);
|
|
10424
|
+
}
|
|
10163
10425
|
.note {
|
|
10164
10426
|
padding: 24px;
|
|
10165
10427
|
color: var(--spectrum-neutral-subdued-content-color-default);
|
|
@@ -10185,22 +10447,50 @@ var SliccSprinkle = class extends ThemedElement {
|
|
|
10185
10447
|
this.height = data.height;
|
|
10186
10448
|
}
|
|
10187
10449
|
};
|
|
10450
|
+
#dips = new Dips();
|
|
10188
10451
|
subscribe(model) {
|
|
10452
|
+
this.#dips = dipsOf(this) ?? this.#dips;
|
|
10189
10453
|
globalThis.addEventListener("message", this.#message);
|
|
10190
10454
|
return [
|
|
10191
10455
|
...super.subscribe(model),
|
|
10192
10456
|
() => globalThis.removeEventListener("message", this.#message),
|
|
10193
|
-
model.sprinkles.on("sprinkles", () => this.requestUpdate())
|
|
10457
|
+
model.sprinkles.on("sprinkles", () => this.requestUpdate()),
|
|
10458
|
+
this.#dips.on("change", () => this.requestUpdate())
|
|
10194
10459
|
];
|
|
10195
10460
|
}
|
|
10461
|
+
#drag = (event) => {
|
|
10462
|
+
const data = this.#data;
|
|
10463
|
+
event.dataTransfer?.setData(dipType, this.sprinkle);
|
|
10464
|
+
event.dataTransfer?.setData("text/plain", data?.title ?? this.sprinkle);
|
|
10465
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
|
10466
|
+
};
|
|
10467
|
+
#send(type, id) {
|
|
10468
|
+
this.dispatchEvent(new CustomEvent(type, { detail: { id }, bubbles: true, composed: true }));
|
|
10469
|
+
}
|
|
10196
10470
|
get #data() {
|
|
10197
10471
|
return this.model?.sprinkles.list().find((candidate) => candidate.id === this.sprinkle);
|
|
10198
10472
|
}
|
|
10199
10473
|
render() {
|
|
10200
10474
|
const data = this.#data;
|
|
10201
10475
|
if (!data) return b`<div class="note">This sprinkle is gone.</div>`;
|
|
10476
|
+
if (this.inline && this.#dips.has(data.id)) {
|
|
10477
|
+
return b`<div class="moved">
|
|
10478
|
+
<sp-icon-open-in size="s"></sp-icon-open-in>
|
|
10479
|
+
<span>${data.title} is open as a panel.</span>
|
|
10480
|
+
<sp-action-button size="s" quiet @click=${() => this.#send("show-surface", `sprinkle:${data.id}`)}>Show</sp-action-button>
|
|
10481
|
+
</div>`;
|
|
10482
|
+
}
|
|
10202
10483
|
const style = `color-scheme:${this.color};${this.inline ? `height:${this.height}px;` : ""}`;
|
|
10203
|
-
|
|
10484
|
+
const frame = b`<iframe title=${data.title} sandbox="allow-scripts" style=${style} .srcdoc=${frameDocument(data.html, this.color, data.name)}></iframe>`;
|
|
10485
|
+
if (!this.inline) return frame;
|
|
10486
|
+
return b`<div class="handle" draggable="true" title="Drag into the dock or a rail to open as a panel" @dragstart=${this.#drag}>
|
|
10487
|
+
${grip}
|
|
10488
|
+
<span class="name">${data.title}</span>
|
|
10489
|
+
<sp-action-button size="xs" quiet label=${`Open ${data.title} as a panel`} title="Open as a panel" @click=${() => this.#send("detach-sprinkle", data.id)}>
|
|
10490
|
+
<sp-icon-open-in slot="icon"></sp-icon-open-in>
|
|
10491
|
+
</sp-action-button>
|
|
10492
|
+
</div>
|
|
10493
|
+
${frame}`;
|
|
10204
10494
|
}
|
|
10205
10495
|
};
|
|
10206
10496
|
|
|
@@ -52320,6 +52610,7 @@ var theme = {
|
|
|
52320
52610
|
};
|
|
52321
52611
|
var SliccDock = class extends HTMLElement {
|
|
52322
52612
|
factories = {};
|
|
52613
|
+
accepts = [];
|
|
52323
52614
|
#api = null;
|
|
52324
52615
|
#contents = /* @__PURE__ */ new Map();
|
|
52325
52616
|
get api() {
|
|
@@ -52367,12 +52658,30 @@ var SliccDock = class extends HTMLElement {
|
|
|
52367
52658
|
api.onDidActivePanelChange(
|
|
52368
52659
|
({ panel }) => this.#dispatch("active-panel-change", { id: panel?.id ?? null })
|
|
52369
52660
|
);
|
|
52661
|
+
api.onUnhandledDragOver((event) => {
|
|
52662
|
+
if (this.#accepted(event.nativeEvent)) event.accept();
|
|
52663
|
+
});
|
|
52664
|
+
api.onDidDrop((event) => {
|
|
52665
|
+
const type = this.#accepted(event.nativeEvent);
|
|
52666
|
+
if (!type) return;
|
|
52667
|
+
const transfer = event.nativeEvent.dataTransfer;
|
|
52668
|
+
this.#dispatch("external-drop", {
|
|
52669
|
+
type,
|
|
52670
|
+
data: transfer.getData(type),
|
|
52671
|
+
panel: event.group?.activePanel?.id ?? null,
|
|
52672
|
+
position: event.position
|
|
52673
|
+
});
|
|
52674
|
+
});
|
|
52370
52675
|
api.onDidRemovePanel((panel) => {
|
|
52371
52676
|
this.#contents.delete(panel.id);
|
|
52372
52677
|
this.#dispatch("panel-close", { id: panel.id });
|
|
52373
52678
|
});
|
|
52374
52679
|
this.#api = api;
|
|
52375
52680
|
}
|
|
52681
|
+
#accepted(native) {
|
|
52682
|
+
const types = native instanceof DragEvent ? [...native.dataTransfer?.types ?? []] : [];
|
|
52683
|
+
return this.accepts.find((type) => types.includes(type));
|
|
52684
|
+
}
|
|
52376
52685
|
#create(id, name) {
|
|
52377
52686
|
const element2 = document.createElement("div");
|
|
52378
52687
|
element2.className = "panel";
|
|
@@ -52404,6 +52713,10 @@ var SliccDock = class extends HTMLElement {
|
|
|
52404
52713
|
}
|
|
52405
52714
|
return this.api.addPanel(options);
|
|
52406
52715
|
}
|
|
52716
|
+
reveal(id) {
|
|
52717
|
+
const panel = this.api.getPanel(id);
|
|
52718
|
+
panel?.group.model.openPanel(panel, { skipSetGroupActive: true });
|
|
52719
|
+
}
|
|
52407
52720
|
close(id) {
|
|
52408
52721
|
this.api.getPanel(id)?.api.close();
|
|
52409
52722
|
}
|