@ai-ecoverse/slicc-spectrum 1.12.0 → 1.14.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/dist/slicc-ui.js CHANGED
@@ -6597,6 +6597,14 @@ var surfaces = [
6597
6597
  icon: "sp-icon-settings",
6598
6598
  side: "center",
6599
6599
  open: []
6600
+ },
6601
+ {
6602
+ id: "updates",
6603
+ title: "Install / Update",
6604
+ tag: "slicc-updates",
6605
+ icon: "sp-icon-refresh",
6606
+ side: "center",
6607
+ open: []
6600
6608
  }
6601
6609
  ];
6602
6610
  var documents = {
@@ -6757,6 +6765,248 @@ function openSprinkle(dock, sprinkle, screen, place = null) {
6757
6765
  return id;
6758
6766
  }
6759
6767
 
6768
+ // src/app/updates.ts
6769
+ var states = {
6770
+ current: ["neutral", "Up to date"],
6771
+ checking: ["informative", "Checking"],
6772
+ downloading: ["informative", "Downloading"],
6773
+ linking: ["informative", "Linking"],
6774
+ installed: ["positive", "Installed"],
6775
+ available: ["notice", "Update available"],
6776
+ ready: ["notice", "Ready to apply"],
6777
+ failed: ["negative", "Failed"]
6778
+ };
6779
+ var actions = {
6780
+ "restart-agent": "Restart agent",
6781
+ reload: "Reload",
6782
+ "update-now": "Update now",
6783
+ retry: "Retry"
6784
+ };
6785
+ function updatesStatus(items) {
6786
+ const failed = items.filter((item2) => item2.state === "failed").length;
6787
+ if (failed) return `${failed} failed`;
6788
+ const running = items.filter(
6789
+ (item2) => ["checking", "downloading", "linking"].includes(item2.state)
6790
+ ).length;
6791
+ if (running) return `updating ${running}`;
6792
+ if (items.some((item2) => item2.state === "ready" || item2.state === "available")) {
6793
+ return "update ready";
6794
+ }
6795
+ return null;
6796
+ }
6797
+ var UpdateVisibility = class {
6798
+ #boot;
6799
+ #initial = true;
6800
+ #failed = /* @__PURE__ */ new Set();
6801
+ constructor(ready) {
6802
+ this.#boot = !ready;
6803
+ }
6804
+ change(port, layout = false) {
6805
+ const failed = new Set(
6806
+ port.list().filter((item2) => item2.state === "failed").map((item2) => item2.id)
6807
+ );
6808
+ const open = (this.#initial || layout) && this.#boot || [...failed].some((id) => !this.#failed.has(id));
6809
+ this.#initial = false;
6810
+ this.#failed = failed;
6811
+ if (this.#boot && port.ready() && failed.size === 0) {
6812
+ this.#boot = false;
6813
+ return "close";
6814
+ }
6815
+ return open ? "open" : null;
6816
+ }
6817
+ };
6818
+ var SliccUpdates = class extends ModelElement {
6819
+ static properties = {
6820
+ ...ModelElement.properties,
6821
+ pending: { state: true },
6822
+ failures: { state: true }
6823
+ };
6824
+ constructor() {
6825
+ super();
6826
+ this.pending = /* @__PURE__ */ new Set();
6827
+ this.failures = /* @__PURE__ */ new Map();
6828
+ }
6829
+ static styles = [
6830
+ shared,
6831
+ i`
6832
+ :host {
6833
+ display: block;
6834
+ height: 100%;
6835
+ overflow: auto;
6836
+ container-type: inline-size;
6837
+ background: var(--spectrum-background-layer-2-color);
6838
+ color: var(--spectrum-neutral-content-color-default);
6839
+ font-size: var(--spectrum-font-size-100);
6840
+ }
6841
+ .page {
6842
+ max-width: 800px;
6843
+ padding: 24px;
6844
+ margin: 0 auto;
6845
+ }
6846
+ h1 {
6847
+ font-size: var(--spectrum-font-size-400);
6848
+ margin: 0 0 8px;
6849
+ }
6850
+ .hint, .meta, summary {
6851
+ color: var(--spectrum-neutral-subdued-content-color-default);
6852
+ font-size: var(--spectrum-font-size-75);
6853
+ }
6854
+ .hint {
6855
+ margin: 0 0 24px;
6856
+ line-height: 1.5;
6857
+ }
6858
+ article {
6859
+ padding: 16px 0;
6860
+ border-top: 1px solid var(--spectrum-gray-200);
6861
+ }
6862
+ .head, .meta, .actions {
6863
+ display: flex;
6864
+ align-items: center;
6865
+ flex-wrap: wrap;
6866
+ gap: 8px 12px;
6867
+ }
6868
+ .head {
6869
+ justify-content: space-between;
6870
+ margin-bottom: 8px;
6871
+ }
6872
+ .meta {
6873
+ justify-content: space-between;
6874
+ line-height: 1.5;
6875
+ }
6876
+ .version {
6877
+ font-family: var(--spectrum-code-font-family);
6878
+ }
6879
+ progress {
6880
+ display: block;
6881
+ width: 100%;
6882
+ height: 4px;
6883
+ margin: 12px 0;
6884
+ accent-color: var(--spectrum-accent-visual-color);
6885
+ }
6886
+ .actions {
6887
+ margin-top: 12px;
6888
+ }
6889
+ .error {
6890
+ margin: 12px 0 0;
6891
+ padding: 12px;
6892
+ border-left: 3px solid var(--spectrum-negative-visual-color);
6893
+ background: var(--spectrum-background-layer-1-color);
6894
+ color: var(--spectrum-negative-content-color-default);
6895
+ overflow-wrap: anywhere;
6896
+ line-height: 1.5;
6897
+ }
6898
+ details {
6899
+ margin-top: 12px;
6900
+ }
6901
+ summary {
6902
+ cursor: pointer;
6903
+ width: fit-content;
6904
+ }
6905
+ summary:focus-visible {
6906
+ outline: 2px solid var(--spectrum-focus-indicator-color);
6907
+ outline-offset: 3px;
6908
+ }
6909
+ pre {
6910
+ padding: 12px;
6911
+ max-height: 180px;
6912
+ overflow: auto;
6913
+ background: var(--spectrum-background-layer-1-color);
6914
+ border: 1px solid var(--spectrum-gray-200);
6915
+ border-radius: 4px;
6916
+ font-family: var(--spectrum-code-font-family);
6917
+ font-size: var(--spectrum-font-size-75);
6918
+ line-height: 1.5;
6919
+ white-space: pre-wrap;
6920
+ overflow-wrap: anywhere;
6921
+ }
6922
+ @container (max-width: 480px) {
6923
+ .page {
6924
+ padding: 16px;
6925
+ }
6926
+ .head, .meta {
6927
+ align-items: flex-start;
6928
+ flex-direction: column;
6929
+ }
6930
+ }
6931
+ `
6932
+ ];
6933
+ subscribe(model) {
6934
+ this.pending = /* @__PURE__ */ new Set();
6935
+ this.failures = /* @__PURE__ */ new Map();
6936
+ const port = model.updates;
6937
+ if (!port) return [];
6938
+ let previous = new Map(port.list().map(({ id, state, error }) => [id, { state, error }]));
6939
+ return [
6940
+ port.on("items", (items) => {
6941
+ const next = new Map(items.map(({ id, state, error }) => [id, { state, error }]));
6942
+ this.failures = new Map(
6943
+ [...this.failures].filter(([id]) => {
6944
+ const before = previous.get(id);
6945
+ const after = next.get(id);
6946
+ return before && after && before.state === after.state && before.error === after.error;
6947
+ })
6948
+ );
6949
+ previous = next;
6950
+ this.requestUpdate();
6951
+ })
6952
+ ];
6953
+ }
6954
+ focus() {
6955
+ this.focusOn("sp-button, summary");
6956
+ }
6957
+ async act(id, action) {
6958
+ const port = this.model?.updates;
6959
+ if (!port || this.pending.has(id)) return;
6960
+ this.pending = /* @__PURE__ */ new Set([...this.pending, id]);
6961
+ this.failures = new Map([...this.failures].filter(([key2]) => key2 !== id));
6962
+ try {
6963
+ await port.act(id, action);
6964
+ } catch (error) {
6965
+ if (this.model?.updates === port) {
6966
+ this.failures = new Map([
6967
+ ...this.failures,
6968
+ [id, error instanceof Error ? error.message : String(error)]
6969
+ ]);
6970
+ }
6971
+ } finally {
6972
+ if (this.model?.updates === port) {
6973
+ this.pending = new Set([...this.pending].filter((key2) => key2 !== id));
6974
+ }
6975
+ }
6976
+ }
6977
+ #item(item2) {
6978
+ const [variant, label] = states[item2.state];
6979
+ const progress = item2.progress;
6980
+ const error = this.failures.get(item2.id) ?? item2.error;
6981
+ return b`<article data-id=${item2.id} data-state=${item2.state} aria-label=${item2.label}>
6982
+ <div class="head">
6983
+ <strong>${item2.label}</strong>
6984
+ <sp-badge size="s" variant=${variant}>${label}${progress ? ` ${progress.done}/${progress.total}` : ""}</sp-badge>
6985
+ </div>
6986
+ <div class="meta">
6987
+ <span class="version">${item2.from ?? "Not installed"}${item2.to && item2.to !== item2.from ? b` → ${item2.to}` : A}</span>
6988
+ ${item2.checkedAt === null ? b`<span>Not checked yet</span>` : b`<span>Checked <time datetime=${new Date(item2.checkedAt).toISOString()}>${new Date(item2.checkedAt).toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" })}</time></span>`}
6989
+ </div>
6990
+ ${progress ? b`<progress aria-label=${`${item2.label}: ${progress.phase === "download" ? "downloading" : "linking"}`} value=${progress.done} max=${progress.total}></progress>` : A}
6991
+ ${error ? b`<div class="error" role="alert">${error}</div>` : A}
6992
+ ${item2.actions.length ? b`<div class="actions">${item2.actions.map((action) => b`<sp-button size="s" variant="secondary" treatment="outline" ?disabled=${this.pending.has(item2.id)} ?pending=${this.pending.has(item2.id)} data-action=${action} @click=${() => void this.act(item2.id, action)}>${actions[action]}</sp-button>`)}</div>` : A}
6993
+ ${item2.log ? b`<details><summary>Install log</summary><pre>${item2.log}</pre></details>` : A}
6994
+ </article>`;
6995
+ }
6996
+ render() {
6997
+ const port = this.model?.updates;
6998
+ return b`<div class="page">
6999
+ <h1>Install / Update</h1>
7000
+ <p class="hint">${!port ? "Update information is not connected." : port.ready() ? "Your agent is ready. Background updates appear here; apply them when it suits you." : "Preparing your agent. You can keep working; this panel closes when the agent is ready."}</p>
7001
+ ${c(
7002
+ port?.list() ?? [],
7003
+ (item2) => item2.id,
7004
+ (item2) => this.#item(item2)
7005
+ )}
7006
+ </div>`;
7007
+ }
7008
+ };
7009
+
6760
7010
  // src/app/app.ts
6761
7011
  function railIcon(icon) {
6762
7012
  if (icon.startsWith("sp-icon-")) {
@@ -6777,6 +7027,8 @@ var SliccApp = class extends ThemedElement {
6777
7027
  #resize = null;
6778
7028
  #started = null;
6779
7029
  #saving = false;
7030
+ #updatesPort;
7031
+ #updatesVisibility = null;
6780
7032
  constructor() {
6781
7033
  super();
6782
7034
  this.screen = screenClass(globalThis.innerWidth ?? 1280);
@@ -6797,6 +7049,7 @@ var SliccApp = class extends ThemedElement {
6797
7049
  }
6798
7050
  .shell {
6799
7051
  display: grid;
7052
+ grid-template-columns: minmax(0, 1fr);
6800
7053
  grid-template-rows: 40px minmax(0, 1fr) auto 24px;
6801
7054
  height: 100%;
6802
7055
  }
@@ -6834,11 +7087,12 @@ var SliccApp = class extends ThemedElement {
6834
7087
  }
6835
7088
  .rail.bottom {
6836
7089
  flex-direction: row;
6837
- justify-content: center;
7090
+ justify-content: safe center;
6838
7091
  width: auto;
6839
7092
  height: 44px;
6840
7093
  padding: 0 8px;
6841
7094
  border-top: 1px solid var(--spectrum-gray-200);
7095
+ overflow-x: auto;
6842
7096
  }
6843
7097
  .rail:empty {
6844
7098
  display: none;
@@ -6846,6 +7100,14 @@ var SliccApp = class extends ThemedElement {
6846
7100
  :host([screen='phone']) footer .hints {
6847
7101
  display: none;
6848
7102
  }
7103
+ :host([screen='phone']) header sp-picker {
7104
+ min-width: 0;
7105
+ width: 0;
7106
+ flex: 1;
7107
+ }
7108
+ :host([screen='phone']) header .spacer {
7109
+ display: none;
7110
+ }
6849
7111
  header {
6850
7112
  display: flex;
6851
7113
  align-items: center;
@@ -6941,7 +7203,8 @@ var SliccApp = class extends ThemedElement {
6941
7203
  }),
6942
7204
  model.files.on("changes", update),
6943
7205
  model.sprinkles.on("sprinkles", update),
6944
- this.dips.on("change", update)
7206
+ this.dips.on("change", update),
7207
+ ...model.updates ? [model.updates.on("items", update)] : []
6945
7208
  ];
6946
7209
  }
6947
7210
  connectedCallback() {
@@ -6978,8 +7241,28 @@ var SliccApp = class extends ThemedElement {
6978
7241
  }
6979
7242
  updated(changed) {
6980
7243
  if (!this.model) return;
6981
- if (this.#started !== this.screen) this.#start(this.model);
7244
+ const layout = this.#started !== this.screen;
7245
+ if (layout) this.#start(this.model);
6982
7246
  else if (changed.has("model")) this.#rebind(this.model);
7247
+ this.#updates(layout);
7248
+ }
7249
+ #updates(layout) {
7250
+ const port = this.model?.updates;
7251
+ if (port !== this.#updatesPort) {
7252
+ this.#updatesPort = port;
7253
+ this.#updatesVisibility = port ? new UpdateVisibility(port.ready()) : null;
7254
+ }
7255
+ if (!port || !this.#offers("updates")) return;
7256
+ const change = this.#updatesVisibility?.change(port, layout);
7257
+ if (change === "open") {
7258
+ if (this.dock.has("updates")) this.dock.reveal("updates");
7259
+ else {
7260
+ this.show("updates");
7261
+ if (!port.ready()) this.dock.api.getPanel("updates")?.api.updateParameters({ boot: true });
7262
+ }
7263
+ } else if (this.dock.api.getPanel("updates")?.params?.boot && port.ready() && !port.list().some((item2) => item2.state === "failed")) {
7264
+ this.dock.close("updates");
7265
+ }
6983
7266
  }
6984
7267
  #factories(model) {
6985
7268
  return {
@@ -7072,6 +7355,7 @@ var SliccApp = class extends ThemedElement {
7072
7355
  const item2 = surface(id, this.surfaces);
7073
7356
  if (!item2) return;
7074
7357
  openSurface(this.dock, item2, this.screen);
7358
+ if (id === "updates") this.dock.api.getPanel(id)?.api.updateParameters({ boot: false });
7075
7359
  this.dock.focusPanel(id);
7076
7360
  }
7077
7361
  #sprinkle(id) {
@@ -7150,7 +7434,7 @@ var SliccApp = class extends ThemedElement {
7150
7434
  }
7151
7435
  #menuItem(surface2, index) {
7152
7436
  return b`<sp-menu-item value=${surface2.id}>
7153
- ${surface2.title}<kbd slot="value">Alt+${index + 1}</kbd>
7437
+ ${surface2.title}${index < 9 ? b`<kbd slot="value">Alt+${index + 1}</kbd>` : A}
7154
7438
  </sp-menu-item>`;
7155
7439
  }
7156
7440
  #offers(id) {
@@ -7174,7 +7458,9 @@ var SliccApp = class extends ThemedElement {
7174
7458
  return b`<button class="link" @click=${() => this.show("changes")}>${changes} ${changes === 1 ? "change" : "changes"}</button>`;
7175
7459
  }
7176
7460
  #status() {
7461
+ const updates = this.#offers("updates") ? updatesStatus(this.model?.updates?.list() ?? []) : null;
7177
7462
  return b`<footer role="status" aria-label="Status">
7463
+ ${updates ? b`<button class="link" aria-label=${`Install / Update: ${updates}`} data-updates @click=${() => this.show("updates")}>${updates}</button>` : A}
7178
7464
  ${this.#agentStatus()}
7179
7465
  ${this.#changes()}
7180
7466
  <slot name="status"></slot>
@@ -7196,7 +7482,7 @@ var SliccApp = class extends ThemedElement {
7196
7482
  )}
7197
7483
  </sp-picker>` : A}
7198
7484
  <span class="spacer"></span>
7199
- ${this.#offers("chat") ? b`<slicc-tray .model=${this.model}></slicc-tray>` : A}
7485
+ ${this.#offers("chat") ? b`<slicc-tray .model=${this.model} ?compact=${this.screen === "phone"}></slicc-tray>` : A}
7200
7486
  <sp-action-menu size="s" quiet label="View" @change=${this.#view}>
7201
7487
  <sp-icon-view-grid slot="icon"></sp-icon-view-grid>
7202
7488
  <span slot="label">View</span>
@@ -7781,10 +8067,10 @@ function render(entry) {
7781
8067
  return entry.ordered ? b`<ol>${items}</ol>` : b`<ul>${items}</ul>`;
7782
8068
  }
7783
8069
  case "table": {
7784
- const [head2, ...body3] = entry.rows;
8070
+ const [head2, ...body4] = entry.rows;
7785
8071
  return b`<div class="table"><table>
7786
8072
  <thead><tr>${head2.map((cell) => b`<th>${inline(cell)}</th>`)}</tr></thead>
7787
- <tbody>${body3.map((row) => b`<tr>${row.map((cell) => b`<td>${inline(cell)}</td>`)}</tr>`)}</tbody>
8073
+ <tbody>${body4.map((row) => b`<tr>${row.map((cell) => b`<td>${inline(cell)}</td>`)}</tr>`)}</tbody>
7788
8074
  </table></div>`;
7789
8075
  }
7790
8076
  case "quote":
@@ -7843,21 +8129,28 @@ function size(bytes) {
7843
8129
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
7844
8130
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
7845
8131
  }
7846
- function tool(call) {
8132
+ function body(call, color) {
8133
+ if (call.diff) {
8134
+ return b`<slicc-diff-view class="input" path=${call.paths[0] ?? call.title} color=${color} .oldText=${call.diff.before} .newText=${call.diff.after}></slicc-diff-view>`;
8135
+ }
8136
+ return b`<pre class="input">${call.name === "bash" ? `$ ${call.input}` : call.input}</pre>`;
8137
+ }
8138
+ function tool(call, color = "light") {
7847
8139
  const state = call.status === "running" ? b`<sp-progress-circle size="s" indeterminate label="Running"></sp-progress-circle>` : b`<span class="state">${toolStates[call.status]}</span>`;
7848
8140
  return b`<details class="tool" data-status=${call.status} data-tool=${call.name}>
7849
8141
  <summary>
7850
8142
  <span class="chevron" aria-hidden="true">▸</span>
7851
8143
  <span class="name">${call.name}</span>
7852
8144
  <span class="title">${call.title}</span>
8145
+ ${call.meta ? b`<span class="tool-meta">${call.meta}</span>` : A}
7853
8146
  ${state}
7854
8147
  </summary>
7855
- <pre class="input">${call.name === "bash" ? `$ ${call.input}` : call.input}</pre>
8148
+ ${body(call, color)}
7856
8149
  ${call.output ? b`<pre class="output">${call.output}</pre>` : A}
7857
8150
  ${call.image ? b`<img class="shot" src=${call.image} alt=${call.title} />` : A}
7858
8151
  </details>`;
7859
8152
  }
7860
- function cluster(calls, open) {
8153
+ function cluster(calls, open, color) {
7861
8154
  const failed = calls.filter((call) => call.status === "error").length;
7862
8155
  const running = calls.some((call) => call.status === "running");
7863
8156
  return b`<details class="cluster" ?open=${open || running}>
@@ -7867,7 +8160,7 @@ function cluster(calls, open) {
7867
8160
  <span class="names">${[...new Set(calls.map((call) => call.name))].join(" \xB7 ")}</span>
7868
8161
  ${failed ? b`<span class="state failed">${failed} failed</span>` : A}
7869
8162
  </summary>
7870
- ${calls.map(tool)}
8163
+ ${calls.map((call) => tool(call, color))}
7871
8164
  </details>`;
7872
8165
  }
7873
8166
  function media(items) {
@@ -7907,14 +8200,14 @@ function check(items) {
7907
8200
  }
7908
8201
  function question(item2, handlers) {
7909
8202
  const answer = (value) => handlers.answer(item2.id, value);
7910
- let body3;
8203
+ let body4;
7911
8204
  if (item2.state === "answered") {
7912
- body3 = b`<div class="answer">✓ ${item2.answer}</div>`;
8205
+ body4 = b`<div class="answer">✓ ${item2.answer}</div>`;
7913
8206
  } else if (item2.state === "inert") {
7914
- body3 = b`<div class="hint">Only the latest question can be answered here.</div>`;
8207
+ body4 = b`<div class="hint">Only the latest question can be answered here.</div>`;
7915
8208
  } else if (item2.kind === "yes-no" || item2.kind === "choice") {
7916
8209
  const options = item2.kind === "yes-no" ? ["Yes", "No"] : item2.options;
7917
- body3 = b`<div class="options">${options.map(
8210
+ body4 = b`<div class="options">${options.map(
7918
8211
  (option2) => b`<sp-button size="s" variant="secondary" treatment="outline" @click=${() => answer(option2)}>${option2}</sp-button>`
7919
8212
  )}</div>`;
7920
8213
  } else {
@@ -7923,14 +8216,14 @@ function question(item2, handlers) {
7923
8216
  const field = event.currentTarget.querySelector("input");
7924
8217
  if (field.value.trim()) answer(field.value.trim());
7925
8218
  };
7926
- body3 = b`<form class="options" @submit=${submit}>
8219
+ body4 = b`<form class="options" @submit=${submit}>
7927
8220
  <input class="input" type=${item2.kind === "email" ? "email" : item2.kind === "number" ? "number" : item2.kind === "date" ? "date" : "text"} aria-label=${item2.question} />
7928
8221
  <sp-button size="s" variant="accent" @click=${(event) => event.currentTarget.closest("form")?.requestSubmit()}>Answer</sp-button>
7929
8222
  </form>`;
7930
8223
  }
7931
8224
  return b`<div class="question" data-state=${item2.state} data-kind=${item2.kind}>
7932
8225
  <div class="q">${item2.question}</div>
7933
- ${body3}
8226
+ ${body4}
7934
8227
  </div>`;
7935
8228
  }
7936
8229
  var delegationVerbs = {
@@ -7946,7 +8239,7 @@ function part(entry, handlers, showThinking) {
7946
8239
  case "thinking":
7947
8240
  return showThinking ? b`<div class="thinking">${entry.text}</div>` : A;
7948
8241
  case "tool":
7949
- return tool(entry.tool);
8242
+ return tool(entry.tool, handlers.color);
7950
8243
  case "media":
7951
8244
  return media(entry.items);
7952
8245
  case "card":
@@ -7985,7 +8278,7 @@ function parts(entries, handlers, showThinking, streaming) {
7985
8278
  while (end < entries.length && entries[end].type === "tool") end++;
7986
8279
  if (end - i10 >= 3) {
7987
8280
  const calls = entries.slice(i10, end).map((entry) => entry.tool);
7988
- out.push(cluster(calls, streaming && end === entries.length));
8281
+ out.push(cluster(calls, streaming && end === entries.length, handlers.color));
7989
8282
  i10 = end;
7990
8283
  } else {
7991
8284
  out.push(part(entries[i10], handlers, showThinking));
@@ -8036,10 +8329,10 @@ function assistant(message, name, model, handlers, showThinking) {
8036
8329
  ${message.status === "streaming" ? b`<span class="caret" aria-hidden="true"></span>` : A}
8037
8330
  </article>`;
8038
8331
  }
8039
- function toolMessage(message) {
8332
+ function toolMessage(message, color = "light") {
8040
8333
  return b`<article class="message tool-message" data-id=${message.id}>
8041
8334
  <div class="meta"><span class="who">Tool</span><span>${time(message.createdAt)}</span></div>
8042
- ${tool(message.tool)}
8335
+ ${tool(message.tool, color)}
8043
8336
  </article>`;
8044
8337
  }
8045
8338
  function system(message, handlers) {
@@ -8068,18 +8361,18 @@ function lick(message, handlers) {
8068
8361
  ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}
8069
8362
  ${outcome ? b`<span class="tag">${outcome}</span>` : A}
8070
8363
  <span class="when">${time(message.createdAt)}</span>`;
8071
- const actions = decide ? b`<div class="decide">
8364
+ const actions2 = decide ? b`<div class="decide">
8072
8365
  <sp-button size="s" variant="accent" @click=${() => handlers.resolve(message.id, "confirmed")}>Allow</sp-button>
8073
8366
  <sp-button size="s" variant="secondary" treatment="outline" @click=${() => handlers.resolve(message.id, "dismissed")}>Deny</sp-button>
8074
8367
  </div>` : A;
8075
8368
  if (!message.body) {
8076
- return b`<div class="lick" data-id=${message.id} data-channel=${message.channel}><div class="lick-head">${head2}</div>${actions}</div>`;
8369
+ return b`<div class="lick" data-id=${message.id} data-channel=${message.channel}><div class="lick-head">${head2}</div>${actions2}</div>`;
8077
8370
  }
8078
8371
  const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel}>
8079
8372
  <summary class="lick-head"><span class="chevron" aria-hidden="true">▸</span>${head2}</summary>
8080
8373
  <pre class="output">${message.body}</pre>
8081
8374
  </details>`;
8082
- return decide ? b`<div class="pending">${details}${actions}</div>` : details;
8375
+ return decide ? b`<div class="pending">${details}${actions2}</div>` : details;
8083
8376
  }
8084
8377
  var messageCss = i`
8085
8378
  .message {
@@ -8332,6 +8625,17 @@ var messageCss = i`
8332
8625
  .state.failed {
8333
8626
  color: var(--spectrum-negative-visual-color);
8334
8627
  }
8628
+ .tool-meta {
8629
+ flex: none;
8630
+ color: var(--spectrum-neutral-subdued-content-color-default);
8631
+ font-size: var(--spectrum-font-size-50);
8632
+ }
8633
+ .tool > slicc-diff-view {
8634
+ display: block;
8635
+ margin: 0 8px 8px;
8636
+ max-height: 240px;
8637
+ overflow: auto;
8638
+ }
8335
8639
  .tool pre,
8336
8640
  .lick pre {
8337
8641
  margin: 0 8px 8px;
@@ -8793,7 +9097,7 @@ var SliccChat = class extends ThemedElement {
8793
9097
  case "user":
8794
9098
  return user(message);
8795
9099
  case "tool":
8796
- return toolMessage(message);
9100
+ return toolMessage(message, this.#handlers.color);
8797
9101
  case "system":
8798
9102
  return system(message, this.#handlers);
8799
9103
  default:
@@ -10871,6 +11175,9 @@ var SliccTray = class extends ModelElement {
10871
11175
  .chip:hover {
10872
11176
  background: var(--spectrum-gray-100);
10873
11177
  }
11178
+ :host([compact]) .chip > span:not(.dot) {
11179
+ display: none;
11180
+ }
10874
11181
  .chip:focus-visible {
10875
11182
  outline: 2px solid var(--spectrum-focus-indicator-color);
10876
11183
  }
@@ -19048,7 +19355,7 @@ function omission(handlers) {
19048
19355
 
19049
19356
  // node_modules/hast-util-to-html/lib/omission/closing.js
19050
19357
  var closing = omission({
19051
- body,
19358
+ body: body2,
19052
19359
  caption: headOrColgroupOrCaption,
19053
19360
  colgroup: headOrColgroupOrCaption,
19054
19361
  dd,
@@ -19076,7 +19383,7 @@ function html3(_6, index, parent) {
19076
19383
  const next = siblingAfter(parent, index);
19077
19384
  return !next || next.type !== "comment";
19078
19385
  }
19079
- function body(_6, index, parent) {
19386
+ function body2(_6, index, parent) {
19080
19387
  const next = siblingAfter(parent, index);
19081
19388
  return !next || next.type !== "comment";
19082
19389
  }
@@ -19135,7 +19442,7 @@ function cells2(_6, index, parent) {
19135
19442
 
19136
19443
  // node_modules/hast-util-to-html/lib/omission/opening.js
19137
19444
  var opening = omission({
19138
- body: body2,
19445
+ body: body3,
19139
19446
  colgroup,
19140
19447
  head,
19141
19448
  html: html4,
@@ -19156,7 +19463,7 @@ function head(node) {
19156
19463
  const child = node.children[0];
19157
19464
  return !child || child.type === "element";
19158
19465
  }
19159
- function body2(node) {
19466
+ function body3(node) {
19160
19467
  const head2 = siblingAfter(node, -1, true);
19161
19468
  return !head2 || head2.type !== "comment" && !(head2.type === "text" && whitespace(head2.value.charAt(0))) && !(head2.type === "element" && (head2.tagName === "meta" || head2.tagName === "link" || head2.tagName === "script" || head2.tagName === "style" || head2.tagName === "template"));
19162
19469
  }
@@ -22244,12 +22551,12 @@ function transform(ast, options) {
22244
22551
  }
22245
22552
  var FirstPassVisitor = {
22246
22553
  AbsenceFunction({ node, parent, replaceWith }) {
22247
- const { body: body3, kind } = node;
22554
+ const { body: body4, kind } = node;
22248
22555
  if (kind === "repeater") {
22249
22556
  const innerGroup = A4();
22250
22557
  innerGroup.body[0].body.push(
22251
22558
  // Insert own alts as `body`
22252
- K2({ negate: true, body: body3 }),
22559
+ K2({ negate: true, body: body4 }),
22253
22560
  Q2("Any")
22254
22561
  );
22255
22562
  const outerGroup = A4();
@@ -22834,8 +23141,8 @@ function getKids(node) {
22834
23141
  if (!node) {
22835
23142
  throw new Error("Node expected");
22836
23143
  }
22837
- const { body: body3 } = node;
22838
- return Array.isArray(body3) ? body3 : body3 ? [body3] : null;
23144
+ const { body: body4 } = node;
23145
+ return Array.isArray(body4) ? body4 : body4 ? [body4] : null;
22839
23146
  }
22840
23147
  function getLeadingG(els) {
22841
23148
  const firstToConsider = els.find((el) => el.kind === "search_start" || isLoneGLookaround(el, { negate: false }) || !isAlwaysZeroLength(el));
@@ -23044,8 +23351,8 @@ var generator = {
23044
23351
  /**
23045
23352
  @param {AlternativeNode} node
23046
23353
  */
23047
- Alternative({ body: body3 }, _6, gen) {
23048
- return body3.map(gen).join("");
23354
+ Alternative({ body: body4 }, _6, gen) {
23355
+ return body4.map(gen).join("");
23049
23356
  },
23050
23357
  /**
23051
23358
  @param {AssertionNode} node
@@ -23078,7 +23385,7 @@ var generator = {
23078
23385
  @param {CapturingGroupNode} node
23079
23386
  */
23080
23387
  CapturingGroup(node, state, gen) {
23081
- const { body: body3, name, number: number2 } = node;
23388
+ const { body: body4, name, number: number2 } = node;
23082
23389
  const data = { ignoreCase: state.currentFlags.ignoreCase };
23083
23390
  const origin = state.originMap.get(node);
23084
23391
  if (origin) {
@@ -23088,7 +23395,7 @@ var generator = {
23088
23395
  }
23089
23396
  }
23090
23397
  state.captureMap.set(number2, data);
23091
- return `(${name ? `?<${name}>` : ""}${body3.map(gen).join("|")})`;
23398
+ return `(${name ? `?<${name}>` : ""}${body4.map(gen).join("|")})`;
23092
23399
  },
23093
23400
  /**
23094
23401
  @param {CharacterNode} node
@@ -23114,20 +23421,20 @@ var generator = {
23114
23421
  */
23115
23422
  CharacterClass(node, state, gen) {
23116
23423
  const { kind, negate, parent } = node;
23117
- let { body: body3 } = node;
23424
+ let { body: body4 } = node;
23118
23425
  if (kind === "intersection" && !state.useFlagV) {
23119
23426
  throw new Error("Use of character class intersection requires min target ES2024");
23120
23427
  }
23121
- if (envFlags.bugFlagVLiteralHyphenIsRange && state.useFlagV && body3.some(isLiteralHyphen)) {
23122
- body3 = [m4(45), ...body3.filter((kid) => !isLiteralHyphen(kid))];
23428
+ if (envFlags.bugFlagVLiteralHyphenIsRange && state.useFlagV && body4.some(isLiteralHyphen)) {
23429
+ body4 = [m4(45), ...body4.filter((kid) => !isLiteralHyphen(kid))];
23123
23430
  }
23124
- const genClass = () => `[${negate ? "^" : ""}${body3.map(gen).join(kind === "intersection" ? "&&" : "")}]`;
23431
+ const genClass = () => `[${negate ? "^" : ""}${body4.map(gen).join(kind === "intersection" ? "&&" : "")}]`;
23125
23432
  if (!state.inCharClass) {
23126
23433
  if (
23127
23434
  // Already established `kind !== 'intersection'` if `!state.useFlagV`; don't check again
23128
23435
  (!state.useFlagV || envFlags.bugNestedClassIgnoresNegation) && !negate
23129
23436
  ) {
23130
- const negatedChildClasses = body3.filter(
23437
+ const negatedChildClasses = body4.filter(
23131
23438
  (kid) => kid.type === "CharacterClass" && kid.kind === "union" && kid.negate
23132
23439
  );
23133
23440
  if (negatedChildClasses.length) {
@@ -23135,9 +23442,9 @@ var generator = {
23135
23442
  const groupFirstAlt = group.body[0];
23136
23443
  group.parent = parent;
23137
23444
  groupFirstAlt.parent = group;
23138
- body3 = body3.filter((kid) => !negatedChildClasses.includes(kid));
23139
- node.body = body3;
23140
- if (body3.length) {
23445
+ body4 = body4.filter((kid) => !negatedChildClasses.includes(kid));
23446
+ node.body = body4;
23447
+ if (body4.length) {
23141
23448
  node.parent = groupFirstAlt;
23142
23449
  groupFirstAlt.body.push(node);
23143
23450
  } else {
@@ -23157,14 +23464,14 @@ var generator = {
23157
23464
  state.inCharClass = false;
23158
23465
  return result;
23159
23466
  }
23160
- const firstEl = body3[0];
23467
+ const firstEl = body4[0];
23161
23468
  if (
23162
23469
  // Already established that the parent is a char class via `inCharClass`; don't check again
23163
23470
  kind === "union" && !negate && firstEl && // Allows many nested classes to work with `target` ES2018 which doesn't support nesting
23164
23471
  ((!state.useFlagV || !state.verbose) && parent.kind === "union" && !(envFlags.bugFlagVLiteralHyphenIsRange && state.useFlagV) || !state.verbose && parent.kind === "intersection" && // JS doesn't allow intersection with union or ranges
23165
- body3.length === 1 && firstEl.type !== "CharacterClassRange")
23472
+ body4.length === 1 && firstEl.type !== "CharacterClassRange")
23166
23473
  ) {
23167
- return body3.map(gen).join("");
23474
+ return body4.map(gen).join("");
23168
23475
  }
23169
23476
  if (!state.useFlagV && parent.type === "CharacterClass") {
23170
23477
  throw new Error("Uses nested character class in a way that requires min target ES2024");
@@ -23236,13 +23543,13 @@ var generator = {
23236
23543
  /**
23237
23544
  @param {GroupNode} node
23238
23545
  */
23239
- Group({ atomic: atomic2, body: body3, flags, parent }, state, gen) {
23546
+ Group({ atomic: atomic2, body: body4, flags, parent }, state, gen) {
23240
23547
  const currentFlags = state.currentFlags;
23241
23548
  if (flags) {
23242
23549
  state.currentFlags = getNewCurrentFlags(currentFlags, flags);
23243
23550
  }
23244
- const contents = body3.map(gen).join("|");
23245
- const result = !state.verbose && body3.length === 1 && // Single alt
23551
+ const contents = body4.map(gen).join("|");
23552
+ const result = !state.verbose && body4.length === 1 && // Single alt
23246
23553
  parent.type !== "Quantifier" && !atomic2 && (!state.useFlagMods || !flags) ? contents : `(?${getGroupPrefix(atomic2, flags, state.useFlagMods)}${contents})`;
23247
23554
  state.currentFlags = currentFlags;
23248
23555
  return result;
@@ -23250,9 +23557,9 @@ var generator = {
23250
23557
  /**
23251
23558
  @param {LookaroundAssertionNode} node
23252
23559
  */
23253
- LookaroundAssertion({ body: body3, kind, negate }, _6, gen) {
23560
+ LookaroundAssertion({ body: body4, kind, negate }, _6, gen) {
23254
23561
  const prefix = `${kind === "lookahead" ? "" : "<"}${negate ? "!" : "="}`;
23255
- return `(?${prefix}${body3.map(gen).join("|")})`;
23562
+ return `(?${prefix}${body4.map(gen).join("|")})`;
23256
23563
  },
23257
23564
  /**
23258
23565
  @param {QuantifierNode} node
@@ -44910,8 +45217,8 @@ var TabsContainer = class extends CompositeDisposable {
44910
45217
  }
44911
45218
  };
44912
45219
  },
44913
- open: (body3) => {
44914
- popup().openPopover(body3, anchor);
45220
+ open: (body4) => {
45221
+ popup().openPopover(body4, anchor);
44915
45222
  },
44916
45223
  close: () => popup().close(),
44917
45224
  focusTrigger: () => {
@@ -62230,6 +62537,7 @@ var elements = {
62230
62537
  "slicc-sprinkle": SliccSprinkle,
62231
62538
  "slicc-lucide": SliccLucide,
62232
62539
  "slicc-tray": SliccTray,
62540
+ "slicc-updates": SliccUpdates,
62233
62541
  "slicc-app": SliccApp
62234
62542
  };
62235
62543
  function define2() {
@@ -62257,6 +62565,7 @@ export {
62257
62565
  SliccFileTree,
62258
62566
  SliccFileView,
62259
62567
  SliccFiles,
62568
+ SliccUpdates,
62260
62569
  define2 as define,
62261
62570
  grammarBase,
62262
62571
  resolveColor,