@ai-ecoverse/slicc-spectrum 1.34.0 → 1.36.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
@@ -8636,6 +8636,9 @@ var ModelElement = class extends i2 {
8636
8636
  this.#off = [];
8637
8637
  }
8638
8638
  };
8639
+ function changesOf(model) {
8640
+ return model.changes ?? model.files;
8641
+ }
8639
8642
  function resolveColor(scheme, prefersDark) {
8640
8643
  if (scheme === "system") return prefersDark ? "dark" : "light";
8641
8644
  return scheme;
@@ -9240,6 +9243,11 @@ var dialogStyles = i`
9240
9243
  --mod-alert-dialog-description-to-buttons: var(--swc-spacing-500);
9241
9244
  --system-alert-dialog-divider-background-color: transparent;
9242
9245
  }
9246
+ @media (max-width: 512px) {
9247
+ sp-alert-dialog {
9248
+ --mod-alert-dialog-min-width: calc(100vw - 2 * var(--swc-spacing-300));
9249
+ }
9250
+ }
9243
9251
  p {
9244
9252
  margin: 0;
9245
9253
  }
@@ -9253,7 +9261,7 @@ var dialogStyles = i`
9253
9261
  }
9254
9262
  `;
9255
9263
  var SliccConfirm = class extends i2 {
9256
- static properties = { heading: {}, body: {}, action: {}, variant: {} };
9264
+ static properties = { heading: {}, body: {}, action: {}, variant: {}, cancel: {} };
9257
9265
  trigger = null;
9258
9266
  #result = Promise.withResolvers();
9259
9267
  constructor() {
@@ -9262,6 +9270,7 @@ var SliccConfirm = class extends i2 {
9262
9270
  this.body = "";
9263
9271
  this.action = "";
9264
9272
  this.variant = "destructive";
9273
+ this.cancel = "Cancel";
9265
9274
  }
9266
9275
  static styles = dialogStyles;
9267
9276
  get result() {
@@ -9290,7 +9299,7 @@ var SliccConfirm = class extends i2 {
9290
9299
  <sp-alert-dialog variant=${this.variant}>
9291
9300
  <h2 id="title" slot="heading">${this.heading}</h2>
9292
9301
  <p id="body">${this.body}</p>
9293
- <swc-button slot="button" variant="secondary" fill-style="outline" data-cancel @click=${() => this.#dialog.close()}>Cancel</swc-button>
9302
+ <swc-button slot="button" variant="secondary" fill-style="outline" data-cancel @click=${() => this.#dialog.close()}>${this.cancel}</swc-button>
9294
9303
  <swc-button
9295
9304
  slot="button"
9296
9305
  variant=${this.variant === "destructive" ? "negative" : "accent"}
@@ -9309,6 +9318,7 @@ function confirm(options) {
9309
9318
  element2.body = options.body;
9310
9319
  element2.action = options.action;
9311
9320
  element2.variant = options.variant ?? "destructive";
9321
+ element2.cancel = options.cancel ?? "Cancel";
9312
9322
  element2.trigger = trigger2;
9313
9323
  themeOf(trigger2).append(element2);
9314
9324
  return element2.result;
@@ -9992,6 +10002,9 @@ function agentName(model, id) {
9992
10002
  if (!id) return "you";
9993
10003
  return model?.agent.list().find((agent) => agent.id === id)?.name ?? id;
9994
10004
  }
10005
+ function author(model, change) {
10006
+ return change.agentId === null && change.repo ? null : agentName(model, change.agentId);
10007
+ }
9995
10008
  function request(target, type, path) {
9996
10009
  target.dispatchEvent(new CustomEvent(type, { detail: { path }, bubbles: true, composed: true }));
9997
10010
  }
@@ -10074,6 +10087,25 @@ function statusMark(status2, announce = true) {
10074
10087
  function failure(error) {
10075
10088
  return error instanceof Error ? error.message : String(error);
10076
10089
  }
10090
+ var errorCss = i`
10091
+ .error {
10092
+ display: flex;
10093
+ align-items: flex-start;
10094
+ gap: var(--swc-spacing-75);
10095
+ flex: none;
10096
+ color: var(--swc-negative-content-color-default);
10097
+ background: var(--swc-background-layer-1-color);
10098
+ white-space: normal;
10099
+ }
10100
+ .error:not(:empty) {
10101
+ padding: var(--swc-spacing-75) var(--swc-spacing-100) var(--swc-spacing-75) var(--swc-spacing-200);
10102
+ border-bottom: var(--swc-border-width-100) solid var(--swc-gray-200);
10103
+ }
10104
+ .error swc-icon-alert-diamond {
10105
+ flex: none;
10106
+ color: var(--swc-negative-content-color-default);
10107
+ }
10108
+ `;
10077
10109
  var SliccFiles = class extends ModelElement {
10078
10110
  static properties = {
10079
10111
  ...ModelElement.properties,
@@ -10101,6 +10133,7 @@ var SliccFiles = class extends ModelElement {
10101
10133
  static styles = [
10102
10134
  shared,
10103
10135
  panelCss,
10136
+ errorCss,
10104
10137
  i`
10105
10138
  :host {
10106
10139
  container-type: inline-size;
@@ -10117,23 +10150,6 @@ var SliccFiles = class extends ModelElement {
10117
10150
  .mount {
10118
10151
  flex: none;
10119
10152
  }
10120
- .error {
10121
- display: flex;
10122
- align-items: flex-start;
10123
- gap: var(--swc-spacing-75);
10124
- flex: none;
10125
- color: var(--swc-negative-content-color-default);
10126
- background: var(--swc-background-layer-1-color);
10127
- white-space: normal;
10128
- }
10129
- .error:not(:empty) {
10130
- padding: var(--swc-spacing-75) var(--swc-spacing-100) var(--swc-spacing-75) var(--swc-spacing-200);
10131
- border-bottom: var(--swc-border-width-100) solid var(--swc-gray-200);
10132
- }
10133
- .error swc-icon-alert-diamond {
10134
- flex: none;
10135
- color: var(--swc-negative-content-color-default);
10136
- }
10137
10153
  .mounts {
10138
10154
  flex: none;
10139
10155
  display: flex;
@@ -10190,7 +10206,7 @@ var SliccFiles = class extends ModelElement {
10190
10206
  this.#mounts(model.files);
10191
10207
  return [
10192
10208
  model.files.on("files", (entries) => this.#load(entries)),
10193
- model.files.on("changes", () => this.#sync()),
10209
+ changesOf(model).on("changes", () => this.#sync()),
10194
10210
  model.files.on("mounts", () => this.#mounts(model.files))
10195
10211
  ];
10196
10212
  }
@@ -10212,7 +10228,7 @@ var SliccFiles = class extends ModelElement {
10212
10228
  }
10213
10229
  #sync() {
10214
10230
  const tree = this.#tree();
10215
- const changes = this.model?.files.changes() ?? [];
10231
+ const changes = this.model ? changesOf(this.model).changes() : [];
10216
10232
  if (!tree) return;
10217
10233
  if (tree.paths.length === 0) tree.expanded = ancestors(changes.map((change) => change.path));
10218
10234
  tree.paths = treePaths(this.#entries);
@@ -10457,7 +10473,7 @@ var SliccFileView = class extends ThemedElement {
10457
10473
  model.files.on("file", (path) => {
10458
10474
  if (path === this.path) void this.#read(model);
10459
10475
  }),
10460
- model.files.on("changes", () => this.requestUpdate())
10476
+ changesOf(model).on("changes", () => this.requestUpdate())
10461
10477
  ];
10462
10478
  }
10463
10479
  async #read(model) {
@@ -10470,12 +10486,14 @@ var SliccFileView = class extends ThemedElement {
10470
10486
  }
10471
10487
  }
10472
10488
  render() {
10473
- const change = this.model?.files.changes().find((candidate) => candidate.path === this.path);
10489
+ const port = this.model ? changesOf(this.model) : null;
10490
+ const change = port && !port.unavailable?.() ? port.changes().find((candidate) => candidate.path === this.path) : void 0;
10491
+ const by = change && author(this.model, change);
10474
10492
  return b`<div class="bar">
10475
10493
  <span class="path" title=${this.path}>${this.path}</span>
10476
10494
  <span class="spacer"></span>
10477
10495
  ${change ? b`${statusMark(change.status, false)}
10478
- <span>${statusLabels[change.status]} by ${agentName(this.model, change.agentId)}</span>
10496
+ <span>${by ? `${statusLabels[change.status]} by ${by}` : statusLabels[change.status]}</span>
10479
10497
  <swc-action-button size="s" quiet @click=${() => request(this, "open-diff", this.path)}>
10480
10498
  <swc-icon-compare slot="icon"></swc-icon-compare>Diff
10481
10499
  </swc-action-button>` : A}
@@ -10596,6 +10614,15 @@ var routes = {
10596
10614
  page: "Through this page\u2019s own fetch. Sites that block cross-origin requests are out of reach.",
10597
10615
  none: "No route to the network."
10598
10616
  };
10617
+ var automation = {
10618
+ extension: "Browser automation: through the SLICC extension.",
10619
+ proxy: "Browser automation: through slicc-node.",
10620
+ none: "Browser automation: not available. Install the extension or run slicc-node.",
10621
+ declined: "Browser automation: off for this session. You declined it; reload to be asked again."
10622
+ };
10623
+ function automationOf(browser) {
10624
+ return browser.declined ? "declined" : browser.via ?? "none";
10625
+ }
10599
10626
  function networkLabel(status2) {
10600
10627
  return `Network: ${networkHealth[status2.health][1].toLowerCase()}`;
10601
10628
  }
@@ -10826,6 +10853,7 @@ var SliccNetwork = class extends ModelElement {
10826
10853
  </div>
10827
10854
  <p data-route=${status2.route ?? "none"}>${routes[status2.route ?? "none"]}</p>
10828
10855
  ${status2.detail ? b`<p class="detail">${status2.detail}</p>` : A}
10856
+ ${status2.browser ? b`<p data-browser=${automationOf(status2.browser)} class=${status2.browser.declined ? "muted" : ""}>${automation[automationOf(status2.browser)]}${status2.browser.detail ? b` <span class="detail">${status2.browser.detail}</span>` : A}</p>` : A}
10829
10857
  ${this.model?.network?.check ? b`<div class="actions"><swc-button size="m" variant="secondary" data-action="check" ?pending=${this.checking} @click=${() => this.check()}>Check again</swc-button></div>` : A}
10830
10858
  ${this.error ? b`<div class="error" role="alert"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.error}</span></div>` : A}
10831
10859
  </section>`;
@@ -14975,7 +15003,7 @@ var SliccApp = class extends ThemedElement {
14975
15003
  margin-inline-end: var(--swc-spacing-200);
14976
15004
  }
14977
15005
  :host([screen='phone']) header sp-picker {
14978
- min-width: 0;
15006
+ min-width: calc(var(--swc-spacing-400) * 4);
14979
15007
  width: 0;
14980
15008
  flex: 1;
14981
15009
  }
@@ -15027,7 +15055,7 @@ var SliccApp = class extends ThemedElement {
15027
15055
  }
15028
15056
  update();
15029
15057
  }),
15030
- model.files.on("changes", update),
15058
+ changesOf(model).on("changes", update),
15031
15059
  model.terminals.on("terminals", () => {
15032
15060
  this.#prune();
15033
15061
  update();
@@ -15114,10 +15142,12 @@ var SliccApp = class extends ThemedElement {
15114
15142
  if (!port || !this.#offers("updates")) return;
15115
15143
  const change = this.#updatesVisibility?.change(port, layout);
15116
15144
  if (change === "open") {
15117
- if (this.dock.has("updates")) this.dock.reveal("updates");
15118
- else {
15119
- this.show("updates");
15120
- if (!port.ready()) this.dock.api.getPanel("updates")?.api.updateParameters({ boot: true });
15145
+ const restored = this.dock.api.getPanel("updates");
15146
+ if (restored) this.dock.reveal("updates");
15147
+ else this.show("updates");
15148
+ if (!port.ready() && restored?.params?.boot !== false) {
15149
+ this.dock.api.getPanel("updates")?.api.updateParameters({ boot: true });
15150
+ this.#save();
15121
15151
  }
15122
15152
  } else if (this.dock.api.getPanel("updates")?.params?.boot && port.ready() && !port.list().some((item2) => item2.state === "failed")) {
15123
15153
  this.dock.close("updates");
@@ -15275,7 +15305,8 @@ var SliccApp = class extends ThemedElement {
15275
15305
  return changes ? b`<span class="badged">${button}<swc-badge size="s" variant="neutral" aria-hidden="true">${changes}</swc-badge></span>${tooltip}` : b`${button}${tooltip}`;
15276
15306
  }
15277
15307
  #changes() {
15278
- return this.model?.files.changes().length ?? 0;
15308
+ const port = this.model ? changesOf(this.model) : null;
15309
+ return port && !port.unavailable?.() ? port.changes().length : 0;
15279
15310
  }
15280
15311
  #more(items) {
15281
15312
  if (!items.length) return A;
@@ -15393,8 +15424,9 @@ var SliccApp = class extends ThemedElement {
15393
15424
  if (!updates) return A;
15394
15425
  const text2 = updates[0].toUpperCase() + updates.slice(1);
15395
15426
  return b`<swc-action-button id="updates" quiet size="s" data-updates accessible-label=${`Install / Update: ${updates}`} @click=${() => this.show("updates")}>
15396
- <swc-status-light size="s" variant=${updatesVariant(updates)}>${text2}</swc-status-light>
15397
- </swc-action-button>`;
15427
+ <swc-status-light size="s" variant=${updatesVariant(updates)}>${this.screen === "phone" ? A : text2}</swc-status-light>
15428
+ </swc-action-button>
15429
+ <swc-tooltip for="updates" placement="bottom">${text2}</swc-tooltip>`;
15398
15430
  }
15399
15431
  #networkIndicator() {
15400
15432
  const port = this.model?.network;
@@ -16740,15 +16772,50 @@ var layouts = [
16740
16772
  function folder(path) {
16741
16773
  return path.slice(0, path.lastIndexOf("/")) || "/";
16742
16774
  }
16743
- var confirmRevert = (path) => confirm({
16744
- title: `Revert ${basename(path)}?`,
16745
- body: `This discards the agent\u2019s edits to ${path}. You can\u2019t undo this.`,
16746
- action: "Revert"
16747
- });
16775
+ function where(change) {
16776
+ const at = folder(change.path);
16777
+ if (!change.repo) return at;
16778
+ if (at === change.repo) return "";
16779
+ return at.startsWith(`${change.repo}/`) ? at.slice(change.repo.length + 1) : at;
16780
+ }
16781
+ function lost(changes) {
16782
+ const kind = changes.every((change) => change.repo) ? "uncommitted" : "pending";
16783
+ return `${changes.length === 1 ? "Its" : "Their"} ${kind} changes are discarded and can\u2019t be undone.`;
16784
+ }
16785
+ var confirmRevert = (change) => confirm({ title: `Revert ${basename(change.path)}?`, body: lost([change]), action: "Revert" });
16786
+ async function revertPaths(port, paths) {
16787
+ const errors = [];
16788
+ for (const path of paths) {
16789
+ try {
16790
+ await port.revert(path);
16791
+ } catch (error) {
16792
+ errors.push(failure(error));
16793
+ }
16794
+ }
16795
+ if (errors.length > 1) return `Couldn\u2019t revert ${errors.length} files. ${errors[0]}`;
16796
+ return errors[0] ?? "";
16797
+ }
16798
+ function groups(changes) {
16799
+ const byRepo = /* @__PURE__ */ new Map();
16800
+ for (const change of changes) {
16801
+ const key2 = change.repo ?? "";
16802
+ byRepo.set(key2, [...byRepo.get(key2) ?? [], change]);
16803
+ }
16804
+ return [...byRepo];
16805
+ }
16806
+ function errorLine(error) {
16807
+ return b`<div class="error" role="status" aria-live="polite">${error ? b`<swc-icon-alert-diamond size="s" aria-hidden="true"></swc-icon-alert-diamond><span>${error}</span>` : A}</div>`;
16808
+ }
16748
16809
  var SliccChanges = class extends ModelElement {
16810
+ static properties = { ...ModelElement.properties, error: { state: true } };
16811
+ constructor() {
16812
+ super();
16813
+ this.error = "";
16814
+ }
16749
16815
  static styles = [
16750
16816
  shared,
16751
16817
  panelCss,
16818
+ errorCss,
16752
16819
  i`
16753
16820
  ul {
16754
16821
  list-style: none;
@@ -16758,7 +16825,24 @@ var SliccChanges = class extends ModelElement {
16758
16825
  flex: 1;
16759
16826
  min-height: 0;
16760
16827
  }
16761
- li {
16828
+ ul ul {
16829
+ padding: 0;
16830
+ overflow: visible;
16831
+ }
16832
+ .repo {
16833
+ display: flex;
16834
+ gap: var(--swc-spacing-100);
16835
+ padding: var(--swc-spacing-100) var(--swc-spacing-200) var(--swc-spacing-50);
16836
+ font-size: var(--swc-font-size-75);
16837
+ font-weight: var(--swc-bold-font-weight);
16838
+ color: var(--swc-neutral-subdued-content-color-default);
16839
+ }
16840
+ .repo span:first-child {
16841
+ overflow: hidden;
16842
+ text-overflow: ellipsis;
16843
+ white-space: nowrap;
16844
+ }
16845
+ li[data-path] {
16762
16846
  display: grid;
16763
16847
  grid-template-columns: auto minmax(0, auto) minmax(0, 1fr) auto;
16764
16848
  align-items: center;
@@ -16767,11 +16851,11 @@ var SliccChanges = class extends ModelElement {
16767
16851
  padding: 0 var(--swc-spacing-75) 0 var(--swc-spacing-200);
16768
16852
  cursor: pointer;
16769
16853
  }
16770
- li:hover,
16771
- li:focus-within {
16854
+ li[data-path]:hover,
16855
+ li[data-path]:focus-within {
16772
16856
  background: var(--swc-gray-100);
16773
16857
  }
16774
- li:focus-visible {
16858
+ li[data-path]:focus-visible {
16775
16859
  outline: var(--swc-focus-indicator-thickness) solid var(--swc-focus-indicator-color);
16776
16860
  outline-offset: calc(-1 * var(--swc-focus-indicator-thickness));
16777
16861
  }
@@ -16791,12 +16875,12 @@ var SliccChanges = class extends ModelElement {
16791
16875
  display: flex;
16792
16876
  visibility: hidden;
16793
16877
  }
16794
- li:hover .actions,
16795
- li:focus-within .actions {
16878
+ li[data-path]:hover .actions,
16879
+ li[data-path]:focus-within .actions {
16796
16880
  visibility: visible;
16797
16881
  }
16798
16882
  @media (hover: none), (pointer: coarse) {
16799
- li {
16883
+ li[data-path] {
16800
16884
  min-height: var(--swc-component-height-200);
16801
16885
  }
16802
16886
  .actions {
@@ -16806,33 +16890,41 @@ var SliccChanges = class extends ModelElement {
16806
16890
  `
16807
16891
  ];
16808
16892
  subscribe(model) {
16809
- return [model.files.on("changes", () => this.requestUpdate())];
16893
+ return [changesOf(model).on("changes", () => this.requestUpdate())];
16810
16894
  }
16811
16895
  focus() {
16812
- this.focusOn("li");
16896
+ this.focusOn("li[data-path]");
16897
+ }
16898
+ #port() {
16899
+ return changesOf(this.model);
16813
16900
  }
16814
16901
  #accept(event2, path) {
16815
16902
  event2.stopPropagation();
16816
- this.model?.files.accept(path);
16903
+ this.#port().accept(path);
16817
16904
  }
16818
- async #revert(event2, path) {
16905
+ async #revert(event2, change) {
16819
16906
  event2.stopPropagation();
16820
- if (await confirmRevert(path)) await this.model?.files.revert(path);
16907
+ this.error = "";
16908
+ if (await confirmRevert(change)) this.error = await revertPaths(this.#port(), [change.path]);
16821
16909
  }
16822
16910
  async #all(action) {
16823
- const files = this.model?.files;
16824
- const changes = files?.changes() ?? [];
16911
+ const port = this.#port();
16912
+ const changes = port.changes();
16913
+ this.error = "";
16914
+ if (action === "accept") {
16915
+ for (const change of changes) port.accept(change.path);
16916
+ return;
16917
+ }
16825
16918
  const count = changes.length;
16826
16919
  const title = `Revert ${count} ${count === 1 ? "change" : "changes"}?`;
16827
- const body4 = "This discards the agents\u2019 edits and puts every file back. You can\u2019t undo this.";
16828
- if (action === "revert" && !await confirm({ title, body: body4, action: "Revert all" })) return;
16829
- for (const change of changes) {
16830
- if (action === "accept") files?.accept(change.path);
16831
- else void files?.revert(change.path);
16832
- }
16920
+ if (await confirm({ title, body: lost(changes), action: "Revert all" }))
16921
+ this.error = await revertPaths(
16922
+ port,
16923
+ changes.map((change) => change.path)
16924
+ );
16833
16925
  }
16834
16926
  #keydown(event2) {
16835
- const items = [...this.renderRoot.querySelectorAll("li")];
16927
+ const items = [...this.renderRoot.querySelectorAll("li[data-path]")];
16836
16928
  const index = items.indexOf(this.renderRoot.activeElement);
16837
16929
  if (index < 0) return;
16838
16930
  const moves = {
@@ -16850,15 +16942,17 @@ var SliccChanges = class extends ModelElement {
16850
16942
  }
16851
16943
  }
16852
16944
  #row(change) {
16945
+ const by = author(this.model, change);
16946
+ const at = where(change);
16853
16947
  return b`<li
16854
16948
  tabindex="0"
16855
16949
  data-path=${change.path}
16856
- title=${`${change.path}, ${change.status} by ${agentName(this.model, change.agentId)}`}
16950
+ title=${`${change.path}, ${change.status}${by ? ` by ${by}` : ""}`}
16857
16951
  @click=${() => request(this, "open-diff", change.path)}
16858
16952
  >
16859
16953
  ${statusMark(change.status)}
16860
16954
  <span class="name">${basename(change.path)}</span>
16861
- <span class="where"><bdi>${folder(change.path)} · ${agentName(this.model, change.agentId)}</bdi></span>
16955
+ <span class="where"><bdi>${[at, by].filter(Boolean).join(" \xB7 ")}</bdi></span>
16862
16956
  <span class="actions">
16863
16957
  <swc-action-button
16864
16958
  size="xs"
@@ -16874,33 +16968,53 @@ var SliccChanges = class extends ModelElement {
16874
16968
  quiet
16875
16969
  accessible-label=${`Revert ${basename(change.path)}`}
16876
16970
  title="Revert"
16877
- @click=${(event2) => this.#revert(event2, change.path)}
16971
+ @click=${(event2) => this.#revert(event2, change)}
16878
16972
  >
16879
16973
  <swc-icon-revert slot="icon"></swc-icon-revert>
16880
16974
  </swc-action-button>
16881
16975
  </span>
16882
16976
  </li>`;
16883
16977
  }
16978
+ #list(changes) {
16979
+ const grouped = groups(changes);
16980
+ if (grouped.length < 2)
16981
+ return b`<ul aria-label="Pending changes" @keydown=${this.#keydown}>${changes.map((change) => this.#row(change))}</ul>`;
16982
+ return b`<ul aria-label="Pending changes" @keydown=${this.#keydown}>${grouped.map(
16983
+ ([repo, items]) => b`<li data-repo=${repo}>
16984
+ <div class="repo" title=${repo}>
16985
+ <span>${repo || "Outside a repository"}</span>
16986
+ <span>${items.length}</span>
16987
+ </div>
16988
+ <ul aria-label=${repo || "Outside a repository"}>${items.map((change) => this.#row(change))}</ul>
16989
+ </li>`
16990
+ )}</ul>`;
16991
+ }
16884
16992
  render() {
16885
- const changes = this.model?.files.changes() ?? [];
16993
+ const port = this.model ? changesOf(this.model) : null;
16994
+ const unavailable = port?.unavailable?.() ?? null;
16995
+ if (unavailable) return b`<div class="note" data-unavailable>${unavailable}</div>`;
16996
+ const changes = port?.changes() ?? [];
16886
16997
  return b`<div class="bar">
16887
16998
  <span>${changes.length} pending ${changes.length === 1 ? "change" : "changes"}</span>
16888
16999
  <span class="spacer"></span>
16889
17000
  ${changes.length ? b`<swc-action-button size="s" quiet @click=${() => this.#all("accept")}>Accept all</swc-action-button>
16890
17001
  <swc-action-button size="s" quiet @click=${() => this.#all("revert")}>Revert all</swc-action-button>` : A}
16891
17002
  </div>
16892
- ${changes.length ? b`<ul aria-label="Pending changes" @keydown=${this.#keydown}>${changes.map((change) => this.#row(change))}</ul>` : b`<div class="note">No pending changes. Edits by agents show up here for review.</div>`}`;
17003
+ ${errorLine(this.error)}
17004
+ ${changes.length ? this.#list(changes) : b`<div class="note">No pending changes. Edits by agents show up here for review.</div>`}`;
16893
17005
  }
16894
17006
  };
16895
17007
  var SliccDiffPanel = class extends ThemedElement {
16896
- static properties = { ...ThemedElement.properties, path: {} };
17008
+ static properties = { ...ThemedElement.properties, path: {}, error: { state: true } };
16897
17009
  constructor() {
16898
17010
  super();
16899
17011
  this.path = "";
17012
+ this.error = "";
16900
17013
  }
16901
17014
  static styles = [
16902
17015
  shared,
16903
17016
  panelCss,
17017
+ errorCss,
16904
17018
  i`
16905
17019
  slicc-diff-view {
16906
17020
  flex: 1;
@@ -16922,17 +17036,34 @@ var SliccDiffPanel = class extends ThemedElement {
16922
17036
  `
16923
17037
  ];
16924
17038
  subscribe(model) {
16925
- return [...super.subscribe(model), model.files.on("changes", () => this.requestUpdate())];
17039
+ return [...super.subscribe(model), changesOf(model).on("changes", () => this.requestUpdate())];
16926
17040
  }
16927
- async #revert() {
16928
- if (await confirmRevert(this.path)) await this.model?.files.revert(this.path);
17041
+ async #revert(port, change) {
17042
+ this.error = "";
17043
+ if (await confirmRevert(change)) this.error = await revertPaths(port, [this.path]);
17044
+ }
17045
+ #body(change, style) {
17046
+ if (change.before === null && change.after === null) {
17047
+ return b`<div class="note" data-diff="none">
17048
+ No diff for this file: it’s binary or over 1 MB.
17049
+ ${change.status === "deleted" ? A : b`<swc-action-button size="s" quiet @click=${() => request(this, "open-file", this.path)}>Open the file</swc-action-button>`}
17050
+ </div>`;
17051
+ }
17052
+ return b`<slicc-diff-view
17053
+ path=${this.path}
17054
+ color=${this.color}
17055
+ diff-style=${style}
17056
+ .oldText=${change.before}
17057
+ .newText=${change.after}
17058
+ ></slicc-diff-view>`;
16929
17059
  }
16930
17060
  #style(style) {
16931
17061
  this.model?.settings.update({ diffStyle: style });
16932
17062
  }
16933
17063
  render() {
16934
17064
  const model = this.model;
16935
- const change = model?.files.changes().find((candidate) => candidate.path === this.path);
17065
+ const port = model ? changesOf(model) : null;
17066
+ const change = port?.changes().find((candidate) => candidate.path === this.path);
16936
17067
  const style = model?.settings.get().diffStyle ?? "unified";
16937
17068
  if (!change) {
16938
17069
  return b`<div class="bar"><span class="path" title=${this.path}>${this.path}</span></div>
@@ -16941,12 +17072,13 @@ var SliccDiffPanel = class extends ThemedElement {
16941
17072
  <swc-action-button size="s" quiet @click=${() => request(this, "open-file", this.path)}>Open the file</swc-action-button>
16942
17073
  </div>`;
16943
17074
  }
17075
+ const by = author(model, change);
16944
17076
  return b`<div class="bar">
16945
17077
  ${statusMark(change.status, false)}
16946
17078
  <span class="path" title=${this.path}>${this.path}</span>
16947
- <span>${statusLabels[change.status]} by ${agentName(model, change.agentId)}</span>
17079
+ <span>${statusLabels[change.status]}${by ? ` by ${by}` : ""}</span>
16948
17080
  <span class="spacer"></span>
16949
- <swc-action-group size="s" compact accessible-label="Diff layout">
17081
+ ${change.before === null && change.after === null ? A : b`<swc-action-group size="s" compact accessible-label="Diff layout">
16950
17082
  ${layouts.map(
16951
17083
  ([value, label]) => b`<swc-action-button
16952
17084
  class=${value === style ? "selected" : ""}
@@ -16956,21 +17088,16 @@ var SliccDiffPanel = class extends ThemedElement {
16956
17088
  >${label}</swc-action-button
16957
17089
  >`
16958
17090
  )}
16959
- </swc-action-group>
16960
- <swc-action-button size="s" quiet @click=${() => model?.files.accept(this.path)}>
17091
+ </swc-action-group>`}
17092
+ <swc-action-button size="s" quiet @click=${() => port?.accept(this.path)}>
16961
17093
  <swc-icon-checkmark slot="icon"></swc-icon-checkmark>Accept
16962
17094
  </swc-action-button>
16963
- <swc-action-button size="s" quiet @click=${() => this.#revert()}>
17095
+ <swc-action-button size="s" quiet @click=${() => this.#revert(port, change)}>
16964
17096
  <swc-icon-revert slot="icon"></swc-icon-revert>Revert
16965
17097
  </swc-action-button>
16966
17098
  </div>
16967
- <slicc-diff-view
16968
- path=${this.path}
16969
- color=${this.color}
16970
- diff-style=${style}
16971
- .oldText=${change.before}
16972
- .newText=${change.after}
16973
- ></slicc-diff-view>`;
17099
+ ${errorLine(this.error)}
17100
+ ${this.#body(change, style)}`;
16974
17101
  }
16975
17102
  };
16976
17103
 
@@ -20898,7 +21025,8 @@ var SliccChat = class extends ThemedElement {
20898
21025
  <sp-menu-item value="high">Think hard</sp-menu-item>
20899
21026
  </sp-picker>
20900
21027
  ${meter}
20901
- <swc-action-button size="s" quiet data-action="new-chat" ?disabled=${empty2} @click=${() => this.#newChat(agent)}><swc-icon-new slot="icon"></swc-icon-new>New chat</swc-action-button>`;
21028
+ <swc-action-button id="new-chat" size="s" quiet data-action="new-chat" ?disabled=${empty2} @click=${() => this.#newChat(agent)}><swc-icon-new slot="icon"></swc-icon-new>New chat</swc-action-button>
21029
+ <swc-tooltip for="new-chat" placement="bottom">${agent.kind === "cone" && model.agent.freeze ? "Freeze this chat and start a new one" : "Start a new chat"}</swc-tooltip>`;
20902
21030
  }
20903
21031
  render() {
20904
21032
  this.#handlers.color = this.color;
@@ -21783,7 +21911,7 @@ function available(model) {
21783
21911
  var commands = [
21784
21912
  {
21785
21913
  name: "clear",
21786
- detail: "Start the conversation over",
21914
+ detail: "Start a new chat",
21787
21915
  run: (model, id) => model.agent.clear(id)
21788
21916
  },
21789
21917
  {
@@ -22673,10 +22801,10 @@ var tagLabels = {
22673
22801
  project: "Project"
22674
22802
  };
22675
22803
  function sections(memories) {
22676
- const groups = /* @__PURE__ */ new Map();
22804
+ const groups2 = /* @__PURE__ */ new Map();
22677
22805
  for (const memory of memories)
22678
- groups.set(memory.section, [...groups.get(memory.section) ?? [], memory]);
22679
- return [...groups];
22806
+ groups2.set(memory.section, [...groups2.get(memory.section) ?? [], memory]);
22807
+ return [...groups2];
22680
22808
  }
22681
22809
  var SliccMemory = class extends ModelElement {
22682
22810
  static properties = {
@@ -23926,13 +24054,9 @@ var SliccTray = class extends ModelElement {
23926
24054
  align-items: center;
23927
24055
  gap: var(--swc-spacing-50);
23928
24056
  }
23929
- .chip .short,
23930
24057
  :host([compact]) .chip .detail {
23931
24058
  display: none;
23932
24059
  }
23933
- :host([compact]) .chip .short {
23934
- display: inline;
23935
- }
23936
24060
  .muted {
23937
24061
  color: var(--swc-neutral-subdued-content-color-default);
23938
24062
  }
@@ -24009,7 +24133,7 @@ var SliccTray = class extends ModelElement {
24009
24133
  >
24010
24134
  <span>
24011
24135
  <swc-status-light size="s" variant=${connectionVariant[status2.connection]}>
24012
- <span class="detail">${light}</span><span class="short">${connection}</span>
24136
+ <span class="detail">${light}</span>
24013
24137
  </swc-status-light>
24014
24138
  <span class="detail muted followers"><swc-icon-user-group size="s"></swc-icon-user-group>${status2.followers.length}</span>
24015
24139
  </span>
@@ -31559,12 +31683,12 @@ function charactersToExpressionCached(subset) {
31559
31683
  return cached;
31560
31684
  }
31561
31685
  function charactersToExpression(subset) {
31562
- const groups = [];
31686
+ const groups2 = [];
31563
31687
  let index = -1;
31564
31688
  while (++index < subset.length) {
31565
- groups.push(subset[index].replace(regexEscapeRegex, "\\$&"));
31689
+ groups2.push(subset[index].replace(regexEscapeRegex, "\\$&"));
31566
31690
  }
31567
- return new RegExp("(?:" + groups.join("|") + ")", "g");
31691
+ return new RegExp("(?:" + groups2.join("|") + ")", "g");
31568
31692
  }
31569
31693
 
31570
31694
  // node_modules/stringify-entities/lib/util/to-hexadecimal.js
@@ -51631,8 +51755,8 @@ var BaseGrid = class extends Resizable {
51631
51755
  this._onDidRemove.fire(group);
51632
51756
  }
51633
51757
  if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
51634
- const groups = Array.from(this._groups.values());
51635
- this.doSetGroupActive(groups.length > 0 ? groups[0].value : void 0);
51758
+ const groups2 = Array.from(this._groups.values());
51759
+ this.doSetGroupActive(groups2.length > 0 ? groups2[0].value : void 0);
51636
51760
  }
51637
51761
  return view;
51638
51762
  }
@@ -61960,7 +62084,7 @@ var StrictEventsSequencing = class extends CompositeDisposable {
61960
62084
  }
61961
62085
  init() {
61962
62086
  const panels = /* @__PURE__ */ new Set();
61963
- const groups = /* @__PURE__ */ new Set();
62087
+ const groups2 = /* @__PURE__ */ new Set();
61964
62088
  this.addDisposables(this.accessor.onDidAddPanel((panel) => {
61965
62089
  if (panels.has(panel.api.id)) throw new Error(`dockview: Invalid event sequence. [onDidAddPanel] called for panel ${panel.api.id} but panel already exists`);
61966
62090
  else panels.add(panel.api.id);
@@ -61968,10 +62092,10 @@ var StrictEventsSequencing = class extends CompositeDisposable {
61968
62092
  if (panels.has(panel.api.id)) panels.delete(panel.api.id);
61969
62093
  else throw new Error(`dockview: Invalid event sequence. [onDidRemovePanel] called for panel ${panel.api.id} but panel does not exists`);
61970
62094
  }), this.accessor.onDidAddGroup((group) => {
61971
- if (groups.has(group.api.id)) throw new Error(`dockview: Invalid event sequence. [onDidAddGroup] called for group ${group.api.id} but group already exists`);
61972
- else groups.add(group.api.id);
62095
+ if (groups2.has(group.api.id)) throw new Error(`dockview: Invalid event sequence. [onDidAddGroup] called for group ${group.api.id} but group already exists`);
62096
+ else groups2.add(group.api.id);
61973
62097
  }), this.accessor.onDidRemoveGroup((group) => {
61974
- if (groups.has(group.api.id)) groups.delete(group.api.id);
62098
+ if (groups2.has(group.api.id)) groups2.delete(group.api.id);
61975
62099
  else throw new Error(`dockview: Invalid event sequence. [onDidRemoveGroup] called for group ${group.api.id} but group does not exists`);
61976
62100
  }));
61977
62101
  }
@@ -64765,9 +64889,9 @@ var DockviewComponent = class extends BaseGrid {
64765
64889
  this.mutation("clear", () => this._doClear());
64766
64890
  }
64767
64891
  _doClear() {
64768
- const groups = Array.from(this._groups.values()).map((_9) => _9.value);
64892
+ const groups2 = Array.from(this._groups.values()).map((_9) => _9.value);
64769
64893
  const hasActiveGroup = !!this.activeGroup;
64770
- for (const group of groups) {
64894
+ for (const group of groups2) {
64771
64895
  var _this$_edgeGroupServi9;
64772
64896
  if ((_this$_edgeGroupServi9 = this._edgeGroupService) === null || _this$_edgeGroupServi9 === void 0 ? void 0 : _this$_edgeGroupServi9.includes(group)) {
64773
64897
  const panels = [...group.panels];
@@ -65020,8 +65144,8 @@ var DockviewComponent = class extends BaseGrid {
65020
65144
  */
65021
65145
  activateFallbackGroupIfRemoved(removed, skipActive) {
65022
65146
  if (!skipActive && this._activeGroup === removed) {
65023
- const groups = Array.from(this._groups.values());
65024
- this.doSetGroupAndPanelActive(groups.length > 0 ? groups[0].value : void 0);
65147
+ const groups2 = Array.from(this._groups.values());
65148
+ this.doSetGroupAndPanelActive(groups2.length > 0 ? groups2[0].value : void 0);
65025
65149
  }
65026
65150
  }
65027
65151
  doRemoveGroup(group, options) {
@@ -65965,10 +66089,10 @@ var SliccDock = class extends HTMLElement {
65965
66089
  this.#contents.get(id)?.focus();
65966
66090
  }
65967
66091
  cycleGroup(delta) {
65968
- const groups = this.api.groups.filter((group) => group.activePanel);
65969
- if (groups.length === 0) return null;
65970
- const current = groups.indexOf(this.api.activeGroup);
65971
- const next = groups[(current + delta + groups.length) % groups.length];
66092
+ const groups2 = this.api.groups.filter((group) => group.activePanel);
66093
+ if (groups2.length === 0) return null;
66094
+ const current = groups2.indexOf(this.api.activeGroup);
66095
+ const next = groups2[(current + delta + groups2.length) % groups2.length];
65972
66096
  const id = next.activePanel?.id;
65973
66097
  this.focusPanel(id);
65974
66098
  return id;