@ai-ecoverse/slicc-spectrum 1.92.0 → 1.94.1

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
@@ -9349,7 +9349,7 @@ function themeOf(node) {
9349
9349
  }
9350
9350
  return document.querySelector("slicc-app")?.shadowRoot?.querySelector("sp-theme") ?? document.body;
9351
9351
  }
9352
- var dialogStyles = i`
9352
+ var dialogFrame = i`
9353
9353
  dialog {
9354
9354
  box-sizing: border-box;
9355
9355
  max-inline-size: calc(100vw - 2 * var(--swc-spacing-300));
@@ -9369,6 +9369,13 @@ var dialogStyles = i`
9369
9369
  color-mix(in srgb, var(--swc-overlay-color) calc(var(--swc-overlay-opacity-dark) * 100%), transparent)
9370
9370
  );
9371
9371
  }
9372
+ @media (forced-colors: active) {
9373
+ dialog {
9374
+ outline: solid;
9375
+ }
9376
+ }
9377
+ `;
9378
+ var alertStyles = i`
9372
9379
  sp-alert-dialog {
9373
9380
  --mod-alert-dialog-min-width: min(
9374
9381
  var(--swc-alert-dialog-minimum-width),
@@ -9390,9 +9397,6 @@ var dialogStyles = i`
9390
9397
  }
9391
9398
  }
9392
9399
  @media (forced-colors: active) {
9393
- dialog {
9394
- outline: solid;
9395
- }
9396
9400
  sp-alert-dialog {
9397
9401
  border: none;
9398
9402
  }
@@ -9409,6 +9413,7 @@ var dialogStyles = i`
9409
9413
  margin-block-end: var(--swc-spacing-75);
9410
9414
  }
9411
9415
  `;
9416
+ var dialogStyles = [dialogFrame, alertStyles];
9412
9417
  var SliccConfirm = class extends i2 {
9413
9418
  static properties = { heading: {}, body: {}, action: {}, variant: {}, cancel: {} };
9414
9419
  trigger = null;
@@ -16209,11 +16214,11 @@ function getVisibleRowContext(state2, index) {
16209
16214
  subtreeEndIndex: getVisibleRowSubtreeEndIndex(state2, selected.cursor, selected.index, totalVisibleCount)
16210
16215
  };
16211
16216
  }
16212
- function getVisibleSlice(state2, start, end) {
16217
+ function getVisibleSlice(state2, start2, end) {
16213
16218
  const instrumentation = state2.instrumentation;
16214
16219
  const totalVisibleCount = getVisibleCount(state2);
16215
- if (totalVisibleCount <= 0 || end < start) return [];
16216
- const normalizedStart = Math.max(0, Math.min(start, totalVisibleCount - 1));
16220
+ if (totalVisibleCount <= 0 || end < start2) return [];
16221
+ const normalizedStart = Math.max(0, Math.min(start2, totalVisibleCount - 1));
16217
16222
  const normalizedEnd = Math.max(normalizedStart, Math.min(end, totalVisibleCount - 1));
16218
16223
  if (instrumentation == null) {
16219
16224
  if (normalizedStart === 0) return collectVisibleRowsDFS(state2, normalizedEnd + 1);
@@ -16832,8 +16837,8 @@ var PathStore = class PathStore2 {
16832
16837
  getVisibleCount() {
16833
16838
  return withBenchmarkPhase(this.#state.instrumentation, "store.getVisibleCount", () => getVisibleCount(this.#state));
16834
16839
  }
16835
- getVisibleSlice(start, end) {
16836
- return withBenchmarkPhase(this.#state.instrumentation, "store.getVisibleSlice", () => getVisibleSlice(this.#state, start, end));
16840
+ getVisibleSlice(start2, end) {
16841
+ return withBenchmarkPhase(this.#state.instrumentation, "store.getVisibleSlice", () => getVisibleSlice(this.#state, start2, end));
16837
16842
  }
16838
16843
  getVisibleRowContext(index) {
16839
16844
  return withBenchmarkPhase(this.#state.instrumentation, "store.getVisibleRowContext", () => getVisibleRowContext(this.#state, index));
@@ -17896,9 +17901,9 @@ var FileTreeController = class FileTreeController2 {
17896
17901
  getVisibleCount() {
17897
17902
  return this.#visibleCount;
17898
17903
  }
17899
- getVisibleRows(start, end) {
17900
- if (end < start || this.#visibleCount === 0) return [];
17901
- const boundedStart = Math.max(0, start);
17904
+ getVisibleRows(start2, end) {
17905
+ if (end < start2 || this.#visibleCount === 0) return [];
17906
+ const boundedStart = Math.max(0, start2);
17902
17907
  const boundedEnd = Math.min(this.#visibleCount - 1, end);
17903
17908
  if (boundedEnd < boundedStart) return [];
17904
17909
  const boundedLength = boundedEnd - boundedStart + 1;
@@ -22203,8 +22208,8 @@ var FileTree = class {
22203
22208
  getVisibleCount() {
22204
22209
  return this.#controller.getVisibleCount();
22205
22210
  }
22206
- getVisibleRows(start, end) {
22207
- return this.#controller.getVisibleRows(start, end);
22211
+ getVisibleRows(start2, end) {
22212
+ return this.#controller.getVisibleRows(start2, end);
22208
22213
  }
22209
22214
  getSelectedPaths() {
22210
22215
  return this.#controller.getSelectedPaths();
@@ -22529,14 +22534,14 @@ function inline2(text2) {
22529
22534
  function cells(line) {
22530
22535
  return line.trim().replace(/^\||\|$/g, "").split("|").map((cell) => cell.trim());
22531
22536
  }
22532
- function fence(lines, start) {
22533
- const lang = lines[start].slice(3).trim();
22534
- let end = start + 1;
22537
+ function fence(lines, start2) {
22538
+ const lang = lines[start2].slice(3).trim();
22539
+ let end = start2 + 1;
22535
22540
  while (end < lines.length && !lines[end].startsWith("```")) end++;
22536
- return [{ type: "code", lang, text: lines.slice(start + 1, end).join("\n") }, end + 1];
22541
+ return [{ type: "code", lang, text: lines.slice(start2 + 1, end).join("\n") }, end + 1];
22537
22542
  }
22538
- function run(lines, start, test) {
22539
- let end = start;
22543
+ function run(lines, start2, test) {
22544
+ let end = start2;
22540
22545
  while (end < lines.length && test(lines[end])) end++;
22541
22546
  return end;
22542
22547
  }
@@ -22649,6 +22654,34 @@ function markdown(text2) {
22649
22654
  return b`${parse(text2).map(render)}`;
22650
22655
  }
22651
22656
 
22657
+ // src/app/sign-in.ts
22658
+ var continuing = "Continue in the sign-in window that opened.";
22659
+ var closed = "Closed the sign-in window? Cancel, then Log in again.";
22660
+ var signInWait = { closedAfter: 2e4 };
22661
+ function signInText(waited) {
22662
+ return waited ? closed : continuing;
22663
+ }
22664
+ var SignInWaits = class {
22665
+ #since = /* @__PURE__ */ new Map();
22666
+ #update;
22667
+ constructor(update) {
22668
+ this.#update = update;
22669
+ }
22670
+ waited(key2) {
22671
+ const now = Date.now();
22672
+ const since3 = this.#since.get(key2);
22673
+ if (since3 === void 0) {
22674
+ this.#since.set(key2, now);
22675
+ setTimeout(this.#update, signInWait.closedAfter);
22676
+ return false;
22677
+ }
22678
+ return now - since3 >= signInWait.closedAfter;
22679
+ }
22680
+ done(key2) {
22681
+ this.#since.delete(key2);
22682
+ }
22683
+ };
22684
+
22652
22685
  // src/app/messages.ts
22653
22686
  var lickLabels = {
22654
22687
  webhook: ["Webhook", "informative"],
@@ -22843,8 +22876,6 @@ function actionLabel(action, handlers, messageId) {
22843
22876
  return count2 > 1 ? `Drop ${count2} failed turns` : actionLabels[action];
22844
22877
  }
22845
22878
  var signedIn = "Signed in. Retry to send it again.";
22846
- var continuing = "Continue in the sign-in window that opened.";
22847
- var closed = "Closed the sign-in window? Cancel, then Log in again.";
22848
22879
  function stopSigningIn(event2, handlers, account, id) {
22849
22880
  const main2 = event2.currentTarget.nextElementSibling;
22850
22881
  main2.focus();
@@ -22863,7 +22894,7 @@ function errorCard(item2, handlers) {
22863
22894
  const login = fix === "login" && item2.account && !handlers.readOnly ? handlers.login?.(item2.account, id) : void 0;
22864
22895
  const resolved = login?.state === "connected";
22865
22896
  const signing = login?.state === "signing-in" && login.window;
22866
- const text2 = resolved ? signedIn : signing ? login.waited ? closed : continuing : item2.text;
22897
+ const text2 = resolved ? signedIn : signing ? signInText(login.waited) : item2.text;
22867
22898
  const title = resolved && login.provider ? `Signed in to ${login.provider} again.` : item2.title;
22868
22899
  const failure2 = login?.state === "idle" ? login.error : void 0;
22869
22900
  return b`<div class="error-card" role=${resolved ? A : "alert"} aria-atomic=${resolved ? A : "false"} data-id=${id || A} data-login=${login?.state ?? A}>
@@ -24701,7 +24732,7 @@ document.addEventListener(
24701
24732
  },
24702
24733
  true
24703
24734
  );
24704
- var SliccChat = class _SliccChat extends ThemedElement {
24735
+ var SliccChat = class extends ThemedElement {
24705
24736
  static properties = {
24706
24737
  ...ThemedElement.properties,
24707
24738
  agent: {},
@@ -25125,19 +25156,14 @@ var SliccChat = class _SliccChat extends ThemedElement {
25125
25156
  if (!restored) return;
25126
25157
  this.renderRoot.querySelector("slicc-composer")?.restore(latest ? { text: latest.text, attachments: latest.attachments } : restored);
25127
25158
  }
25128
- static closedAfter = 2e4;
25129
- #logins = /* @__PURE__ */ new Map();
25130
- #signing = /* @__PURE__ */ new Map();
25131
- #waited(key2) {
25132
- const now = Date.now();
25133
- const since3 = this.#signing.get(key2);
25134
- if (since3 === void 0) {
25135
- this.#signing.set(key2, now);
25136
- setTimeout(() => this.requestUpdate(), _SliccChat.closedAfter);
25137
- return false;
25138
- }
25139
- return now - since3 >= _SliccChat.closedAfter;
25159
+ static get closedAfter() {
25160
+ return signInWait.closedAfter;
25140
25161
  }
25162
+ static set closedAfter(after) {
25163
+ signInWait.closedAfter = after;
25164
+ }
25165
+ #logins = /* @__PURE__ */ new Map();
25166
+ #waits = new SignInWaits(() => this.requestUpdate());
25141
25167
  #loginState(accountId, messageId) {
25142
25168
  const settings = this.model.settings;
25143
25169
  const account = settings.accounts().find((candidate) => candidate.id === accountId);
@@ -25146,8 +25172,8 @@ var SliccChat = class _SliccChat extends ThemedElement {
25146
25172
  const window2 = account?.auth === "oauth";
25147
25173
  const key2 = `${messageId}:${accountId}`;
25148
25174
  if (account?.status === "signing-in" || local2?.pending)
25149
- return { state: "signing-in", cancel, window: window2, waited: window2 && this.#waited(key2) };
25150
- this.#signing.delete(key2);
25175
+ return { state: "signing-in", cancel, window: window2, waited: window2 && this.#waits.waited(key2) };
25176
+ this.#waits.done(key2);
25151
25177
  if (account?.status === "connected") return { state: "connected", provider: account.provider };
25152
25178
  return { state: "idle", error: local2?.error };
25153
25179
  }
@@ -25988,13 +26014,13 @@ var SliccFileView = class extends ThemedElement {
25988
26014
  async save() {
25989
26015
  const model = this.model;
25990
26016
  if (!model || this.draft === null) return;
25991
- const draft = this.draft;
26017
+ const draft2 = this.draft;
25992
26018
  this.saving = true;
25993
26019
  try {
25994
- await model.files.write(this.path, draft);
25995
- this.text = draft;
26020
+ await model.files.write(this.path, draft2);
26021
+ this.text = draft2;
25996
26022
  this.missing = false;
25997
- if (this.draft === draft) this.draft = null;
26023
+ if (this.draft === draft2) this.draft = null;
25998
26024
  } finally {
25999
26025
  this.saving = false;
26000
26026
  }
@@ -31336,8 +31362,8 @@ var Panels = class {
31336
31362
  };
31337
31363
  }
31338
31364
  find(id) {
31339
- const listed = this.list().find((info) => info.id === id);
31340
- if (listed) return listed;
31365
+ const listed2 = this.list().find((info) => info.id === id);
31366
+ if (listed2) return listed2;
31341
31367
  const document2 = id.match(/^(file|diff):(.+)$/);
31342
31368
  if (!document2) return void 0;
31343
31369
  return this.#state({
@@ -36597,9 +36623,9 @@ function scan2(words, cwds, whole) {
36597
36623
  }
36598
36624
  function reach2(command3, cwd) {
36599
36625
  if (command3.length > 4096) return `${command3.slice(0, 40)}\u2026`;
36600
- const start = absolute("/", cwd ?? "/");
36601
- if (protects(start, false)) return start;
36602
- const cwds = /* @__PURE__ */ new Set([start]);
36626
+ const start2 = absolute("/", cwd ?? "/");
36627
+ if (protects(start2, false)) return start2;
36628
+ const cwds = /* @__PURE__ */ new Set([start2]);
36603
36629
  const text2 = unglob(ansi(command3.replace(/\\\n/g, "")));
36604
36630
  const plain = text2.split(separators).map((segment2) => commas(segment2.split(/[\s<>=]+/).map((word) => word.replace(/['"\\]/g, ""))));
36605
36631
  const aware = shield(text2).split(separators).map((segment2) => commas(quoted(unshield(segment2))));
@@ -36610,7 +36636,7 @@ function reach2(command3, cwd) {
36610
36636
  if (hit) return hit;
36611
36637
  }
36612
36638
  const named2 = segments.flat().find((word) => !word.startsWith("/") && /fnox|secrets-policy/.test(word));
36613
- if (named2) return absolute([...cwds].pop() ?? start, named2);
36639
+ if (named2) return absolute([...cwds].pop() ?? start2, named2);
36614
36640
  const home2 = segments.flat().find((word) => /^~(?:[+-](?:\/|$)|.*\/\.\.(?:\/|$))/.test(word));
36615
36641
  if (home2) return home2;
36616
36642
  return segments.flat().some((word) => word.includes("$")) || text2.includes("`") ? text2.match(expansion)[0] : null;
@@ -39792,20 +39818,18 @@ var fieldStyles = i`
39792
39818
  font-size: var(--swc-font-size-75);
39793
39819
  color: var(--swc-neutral-subdued-content-color-default);
39794
39820
  }
39795
- .field input:not([type='checkbox']),
39796
- .field select {
39821
+ .field input:not([type='checkbox']) {
39797
39822
  box-sizing: border-box;
39798
39823
  inline-size: 100%;
39799
39824
  font: inherit;
39800
39825
  min-height: var(--swc-component-height-100);
39801
39826
  padding: 0 var(--swc-spacing-100);
39802
- border: var(--swc-border-width-100) solid var(--swc-gray-400);
39827
+ border: var(--swc-border-width-100) solid var(--swc-neutral-visual-color);
39803
39828
  border-radius: var(--swc-corner-radius-small-default);
39804
39829
  background: var(--swc-background-layer-2-color);
39805
39830
  color: inherit;
39806
39831
  }
39807
- .field input[aria-invalid='true'],
39808
- .field select[aria-invalid='true'] {
39832
+ .field input[aria-invalid='true'] {
39809
39833
  border-color: var(--swc-negative-border-color-default);
39810
39834
  }
39811
39835
  .field input[type='file'] {
@@ -39822,7 +39846,6 @@ var fieldStyles = i`
39822
39846
  color: inherit;
39823
39847
  }
39824
39848
  .field input:focus-visible,
39825
- .field select:focus-visible,
39826
39849
  input[type='checkbox']:focus-visible {
39827
39850
  outline: var(--swc-focus-indicator-thickness) solid var(--swc-focus-indicator-color);
39828
39851
  outline-offset: var(--swc-focus-ring-gap);
@@ -40422,7 +40445,7 @@ var SliccComposer = class extends ThemedElement {
40422
40445
  clearValues(this.renderRoot);
40423
40446
  const held = [
40424
40447
  this.attachments,
40425
- ...[...this.#drafts.values()].map((draft) => draft.attachments)
40448
+ ...[...this.#drafts.values()].map((draft2) => draft2.attachments)
40426
40449
  ];
40427
40450
  if (held.flat().some((attachment2) => attachment2.url?.startsWith("blob:")))
40428
40451
  this.#abandon = setTimeout(() => {
@@ -40478,10 +40501,10 @@ var SliccComposer = class extends ThemedElement {
40478
40501
  #switch(id) {
40479
40502
  if (this.#agent)
40480
40503
  this.#drafts.set(this.#agent, { value: this.value, attachments: this.attachments });
40481
- const draft = this.#drafts.get(id);
40504
+ const draft2 = this.#drafts.get(id);
40482
40505
  this.#agent = id;
40483
- this.value = draft?.value ?? "";
40484
- this.attachments = draft?.attachments ?? [];
40506
+ this.value = draft2?.value ?? "";
40507
+ this.attachments = draft2?.attachments ?? [];
40485
40508
  this.popup = null;
40486
40509
  this.#history = -1;
40487
40510
  }
@@ -40534,8 +40557,8 @@ var SliccComposer = class extends ThemedElement {
40534
40557
  this.failure = null;
40535
40558
  this.#history = -1;
40536
40559
  model.agent.send(this.#agent, text2).catch((error) => {
40537
- const draft = this.value.trim();
40538
- this.value = draft && draft !== text2 ? `${text2}
40560
+ const draft2 = this.value.trim();
40561
+ this.value = draft2 && draft2 !== text2 ? `${text2}
40539
40562
  ${this.value}` : text2;
40540
40563
  this.failure = error instanceof Error ? error.message : String(error);
40541
40564
  });
@@ -40860,8 +40883,8 @@ ${this.value}` : text2;
40860
40883
  }
40861
40884
  async #paste(event2) {
40862
40885
  const data = event2.clipboardData;
40863
- const listed = [...data?.files ?? []];
40864
- const files = listed.length ? listed : [...data?.items ?? []].flatMap((item2) => {
40886
+ const listed2 = [...data?.files ?? []];
40887
+ const files = listed2.length ? listed2 : [...data?.items ?? []].flatMap((item2) => {
40865
40888
  const file = item2.kind === "file" ? item2.getAsFile() : null;
40866
40889
  return file ? [file] : [];
40867
40890
  });
@@ -41828,9 +41851,9 @@ function revealed(name, entries, now = Date.now(), more = false) {
41828
41851
  );
41829
41852
  if (!reveals.length) return null;
41830
41853
  const [last] = reveals;
41831
- const start = startOfDay(now);
41832
- const today = reveals.filter((entry) => entry.at >= start).length;
41833
- const partial = more && (entries[entries.length - 1]?.at ?? 0) >= start;
41854
+ const start2 = startOfDay(now);
41855
+ const today = reveals.filter((entry) => entry.at >= start2).length;
41856
+ const partial = more && (entries[entries.length - 1]?.at ?? 0) >= start2;
41834
41857
  const when2 = today ? `${partial ? "at least " : ""}${today} ${today === 1 ? "time" : "times"} today` : since(last.at, now);
41835
41858
  return b`Revealed to programs ${when2}, last by ${who(last)} with <code>${last.argv}</code>`;
41836
41859
  }
@@ -41839,7 +41862,28 @@ function changes(entry, devices) {
41839
41862
  return entry.changes.map((change) => [change.kind, changeLine(change, devices)]);
41840
41863
  return (entry.widened ?? []).map((change) => [change.kind, widening(change, devices)]);
41841
41864
  }
41842
- function auditLine(entry, devices) {
41865
+ var listed = (items, one2) => items.map((item2, index) => b`${index ? ", " : ""}${one2(item2)}`);
41866
+ function endpointsLine(entry, named2 = (provider) => provider) {
41867
+ const parts2 = [];
41868
+ if (entry.purged?.length)
41869
+ parts2.push(
41870
+ b`<span class="part" data-part="purged">Removed custom model URLs an agent set, automatically: ${listed(entry.purged, (item2) => b`${named2(item2.provider)} at <code>${item2.host ?? item2.url}</code>`)}. Only you can set a model URL now, in Settings › Accounts</span>`
41871
+ );
41872
+ if (entry.adopted?.length)
41873
+ parts2.push(
41874
+ b`<span class="part" data-part="adopted">Kept the MCP servers your tokens use, automatically: ${listed(entry.adopted, (item2) => b`${item2.server} at <code>${item2.host}</code>`)}</span>`
41875
+ );
41876
+ if (entry.pinned)
41877
+ parts2.push(
41878
+ b`<span class="part" data-part="pinned"><code>${entry.pinned.name ?? entry.pinned.server}</code> pinned to <code>${entry.pinned.host}</code> automatically, on first use</span>`
41879
+ );
41880
+ if (entry.unpinned)
41881
+ parts2.push(
41882
+ b`<span class="part" data-part="unpinned"><code>${entry.unpinned.name ?? entry.unpinned.server}</code> unpinned from <code>${entry.unpinned.host}</code>; the next use pins it again</span>`
41883
+ );
41884
+ return b`${parts2.map((part2, index) => b`${index ? ". " : ""}${part2}`)}`;
41885
+ }
41886
+ function auditLine(entry, devices, named2) {
41843
41887
  switch (entry.type) {
41844
41888
  case "inject":
41845
41889
  case "forward":
@@ -41855,7 +41899,7 @@ function auditLine(entry, devices) {
41855
41899
  case "restore":
41856
41900
  return b`Restored`;
41857
41901
  case "endpoints":
41858
- return b`Removed custom model URLs an agent set, automatically: ${entry.purged.map((item2, index) => b`${index ? ", " : ""}${item2.provider} <code>${item2.url}</code>`)}`;
41902
+ return endpointsLine(entry, named2);
41859
41903
  case "refresh":
41860
41904
  return entry.grant ? b`Refreshed automatically with the sign-in <code>${entry.grant}</code>` : b`Refreshed automatically`;
41861
41905
  default:
@@ -41912,6 +41956,9 @@ var SliccSecrets = class extends ThemedElement {
41912
41956
  failure: { state: true },
41913
41957
  form: { state: true },
41914
41958
  busy: { state: true },
41959
+ unpinning: { state: true },
41960
+ unpinned: { state: true },
41961
+ unpinFailure: { state: true },
41915
41962
  problem: { state: true },
41916
41963
  invalid: { state: true },
41917
41964
  said: { state: true },
@@ -41934,6 +41981,9 @@ var SliccSecrets = class extends ThemedElement {
41934
41981
  this.failure = null;
41935
41982
  this.form = null;
41936
41983
  this.busy = false;
41984
+ this.unpinning = null;
41985
+ this.unpinned = /* @__PURE__ */ new Set();
41986
+ this.unpinFailure = null;
41937
41987
  this.problem = null;
41938
41988
  this.invalid = null;
41939
41989
  this.said = null;
@@ -42196,6 +42246,13 @@ var SliccSecrets = class extends ThemedElement {
42196
42246
  .audit .hint {
42197
42247
  display: block;
42198
42248
  }
42249
+ .audit .unpin {
42250
+ display: flex;
42251
+ flex-wrap: wrap;
42252
+ align-items: center;
42253
+ gap: var(--swc-spacing-200);
42254
+ margin-top: var(--swc-spacing-100);
42255
+ }
42199
42256
  .row {
42200
42257
  display: flex;
42201
42258
  flex-wrap: wrap;
@@ -42257,6 +42314,9 @@ var SliccSecrets = class extends ThemedElement {
42257
42314
  this.failure = null;
42258
42315
  this.form = null;
42259
42316
  this.busy = false;
42317
+ this.unpinning = null;
42318
+ this.unpinned = /* @__PURE__ */ new Set();
42319
+ this.unpinFailure = null;
42260
42320
  this.problem = null;
42261
42321
  this.invalid = null;
42262
42322
  this.said = null;
@@ -42741,12 +42801,42 @@ var SliccSecrets = class extends ThemedElement {
42741
42801
  ${refused2.map((item2) => b`<li data-refused-secret=${item2.secret}><span><code>${item2.secret}</code> to ${ruleLine(item2.rule)}</span><span class="muted">${item2.reason}</span></li>`)}
42742
42802
  </ul>`;
42743
42803
  }
42744
- #entry(entry, devices) {
42804
+ #provider(id) {
42805
+ const settings = this.model?.settings;
42806
+ const option2 = settings?.providers?.().find((item2) => item2.id === id);
42807
+ const account = settings?.accounts().find((item2) => item2.id === id);
42808
+ return option2?.name ?? account?.provider ?? id;
42809
+ }
42810
+ #unpin(vault, entry, all2) {
42811
+ const name = entry.pinned?.name;
42812
+ if (!name || !vault.unpin) return A;
42813
+ const key2 = `unpin:${entry.seq}`;
42814
+ const undone = all2.some(
42815
+ (later) => later.type === "endpoints" && later.seq > entry.seq && later.unpinned?.name === name
42816
+ );
42817
+ if (undone || this.unpinned.has(key2)) return A;
42818
+ const busy = this.unpinning === key2;
42819
+ return b`<span class="unpin"><swc-button size="s" variant="secondary" fill-style="outline" data-action="unpin" ?pending=${busy} ?disabled=${!!vault.status()?.locked} @click=${() => this.#unpinNow(vault, name, key2)}>Unpin</swc-button>${this.unpinFailure?.key === key2 ? b`<span class="warning" role="alert" data-error="unpin">${this.unpinFailure.message}</span>` : A}</span>`;
42820
+ }
42821
+ async #unpinNow(vault, name, key2) {
42822
+ if (this.unpinning) return;
42823
+ this.unpinning = key2;
42824
+ this.unpinFailure = null;
42825
+ try {
42826
+ await vault.unpin?.([name]);
42827
+ if (this.#mine(vault)) this.unpinned = /* @__PURE__ */ new Set([...this.unpinned, key2]);
42828
+ } catch (error) {
42829
+ if (this.#mine(vault)) this.unpinFailure = { key: key2, message: failureText(error) };
42830
+ } finally {
42831
+ if (this.#mine(vault)) this.unpinning = null;
42832
+ }
42833
+ }
42834
+ #entry(entry, devices, vault, all2 = []) {
42745
42835
  const date = new Date(entry.at);
42746
42836
  const name = entry.type === "policy" || entry.type === "endpoints" ? null : entry.name;
42747
42837
  return b`<li data-seq=${entry.seq} data-type=${entry.type}>
42748
42838
  <time datetime=${date.toISOString()} title=${date.toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" })}>${since(entry.at)}</time>
42749
- <span class="what">${name ? b`<code>${name}</code> · ` : A}${auditLine(entry, devices)}${entry.type === "policy" || entry.type === "refresh" || entry.type === "endpoints" ? A : b` <span class="by">by ${who(entry)}</span>`}</span>
42839
+ <span class="what">${name ? b`<code>${name}</code> · ` : A}${auditLine(entry, devices, (provider) => this.#provider(provider))}${entry.type === "policy" || entry.type === "refresh" || entry.type === "endpoints" ? A : b` <span class="by">by ${who(entry)}</span>`}${entry.type === "endpoints" && vault ? this.#unpin(vault, entry, all2) : A}</span>
42750
42840
  </li>`;
42751
42841
  }
42752
42842
  #audit(vault, list5, entries, devices) {
@@ -42758,7 +42848,7 @@ var SliccSecrets = class extends ThemedElement {
42758
42848
  this.reveals = event2.target.checked;
42759
42849
  }}>Only reveals to programs</sp-switch>
42760
42850
  </div>
42761
- ${shown4.length ? b`<ol class="audit" aria-labelledby="secrets-audit">${shown4.map((entry) => this.#entry(entry, devices))}</ol>` : b`<p class="muted" data-audit-empty>${this.reveals ? more ? "No reveals in the activity loaded so far." : "No program has been given a secret." : "Nothing has used a secret yet."}</p>`}
42851
+ ${shown4.length ? b`<ol class="audit" aria-labelledby="secrets-audit">${shown4.map((entry) => this.#entry(entry, devices, vault, entries))}</ol>` : b`<p class="muted" data-audit-empty>${this.reveals ? more ? "No reveals in the activity loaded so far." : "No program has been given a secret." : "Nothing has used a secret yet."}</p>`}
42762
42852
  ${more ? b`<div class="row"><swc-button size="s" variant="secondary" fill-style="outline" data-action="older" ?pending=${this.loading} @click=${() => this.showOlder(vault, entries)}>Show older</swc-button></div>` : A}`;
42763
42853
  }
42764
42854
  #feedback() {
@@ -42873,6 +42963,7 @@ function recordKind(account) {
42873
42963
  return account.group === "mcp" ? "mcp" : "model";
42874
42964
  }
42875
42965
  function recordName(account) {
42966
+ if (account.vaultName) return account.vaultName;
42876
42967
  if (account.group === "mcp") return `MCP_${upper(serverOf(account))}_TOKEN`;
42877
42968
  return envNames[account.id] ?? `${upper(account.id)}_API_KEY`;
42878
42969
  }
@@ -42887,7 +42978,51 @@ var accountStatus = {
42887
42978
  "needs-sign-in": ["neutral", "Needs sign-in"],
42888
42979
  "sign-in-expired": ["notice", "Sign-in expired"]
42889
42980
  };
42981
+ var authHints = {
42982
+ "api-key": "API key",
42983
+ oauth: "Sign in",
42984
+ fields: "Endpoint and key",
42985
+ local: "On this device"
42986
+ };
42987
+ var listOrder = {
42988
+ connected: 0,
42989
+ expired: 1,
42990
+ "signing-in": 2,
42991
+ disconnected: 3
42992
+ };
42993
+ var corsNotice = {
42994
+ short: "Needs the SLICC extension or CLI",
42995
+ long: "Needs the SLICC extension or CLI, because the browser blocks direct requests to it."
42996
+ };
42997
+ var removeBodies = {
42998
+ oauth: "Its sign-in is deleted from the vault. Models from it stop working.",
42999
+ local: "Models from it stop working."
43000
+ };
43001
+ function draft(option2) {
43002
+ return {
43003
+ id: option2.id,
43004
+ provider: option2.name,
43005
+ identity: "",
43006
+ status: "disconnected",
43007
+ group: "providers",
43008
+ store: "vault",
43009
+ auth: option2.auth === "fields" ? "api-key" : option2.auth,
43010
+ fields: option2.fields,
43011
+ keyUrl: option2.keyUrl,
43012
+ needs: option2.needs,
43013
+ vaultName: option2.vaultName
43014
+ };
43015
+ }
43016
+ function matchProviders(options, query) {
43017
+ const words = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
43018
+ return options.filter((option2) => {
43019
+ const text2 = `${option2.name} ${option2.id} ${option2.description ?? ""}`.toLowerCase();
43020
+ return words.every((word) => text2.includes(word));
43021
+ });
43022
+ }
42890
43023
  var popup = (account) => account.group === "mcp" && account.auth === "oauth";
43024
+ var start = (options, value) => options.some((option2) => option2.value === value) ? value : options[0]?.value ?? "";
43025
+ var nothingToChoose = "Nothing to choose from yet. You can connect without it.";
42891
43026
  var secretNames = { "api-key": "API key", token: "token" };
42892
43027
  var rejectedNames = { "api-key": "API key rejected", token: "Token rejected" };
42893
43028
  var keptNames = { "api-key": "Key kept in the vault", token: "Token kept in the vault" };
@@ -42906,8 +43041,15 @@ var SliccSettings = class extends ThemedElement {
42906
43041
  entering: { state: true },
42907
43042
  failure: { state: true },
42908
43043
  problem: { state: true },
42909
- blocked: { state: true }
43044
+ blocked: { state: true },
43045
+ picking: { state: true },
43046
+ adding: { state: true },
43047
+ query: { state: true },
43048
+ filled: { state: true }
42910
43049
  };
43050
+ #waits = new SignInWaits(() => this.requestUpdate());
43051
+ #landing = null;
43052
+ #bases = /* @__PURE__ */ new Map();
42911
43053
  #scroll = new ViewScroll(this, "settings:scroll", { scroller: () => this, rows: "[data-id]" });
42912
43054
  constructor() {
42913
43055
  super();
@@ -42916,9 +43058,16 @@ var SliccSettings = class extends ThemedElement {
42916
43058
  this.failure = null;
42917
43059
  this.problem = null;
42918
43060
  this.blocked = false;
43061
+ this.picking = false;
43062
+ this.adding = null;
43063
+ this.query = "";
43064
+ this.filled = /* @__PURE__ */ new Set();
42919
43065
  }
42920
43066
  static styles = [
42921
43067
  fieldStyles,
43068
+ dialogFrame,
43069
+ searchCss,
43070
+ r(link_default),
42922
43071
  i`
42923
43072
  :host {
42924
43073
  display: block;
@@ -43005,6 +43154,22 @@ var SliccSettings = class extends ThemedElement {
43005
43154
  gap: var(--swc-spacing-200);
43006
43155
  max-width: 420px;
43007
43156
  }
43157
+ .key.stacked sp-textfield,
43158
+ .field sp-picker {
43159
+ width: 100%;
43160
+ }
43161
+ .field sp-field-label {
43162
+ margin: 0;
43163
+ padding-block: 0;
43164
+ }
43165
+ .sr {
43166
+ position: absolute;
43167
+ inline-size: 1px;
43168
+ block-size: 1px;
43169
+ overflow: hidden;
43170
+ clip-path: inset(50%);
43171
+ white-space: nowrap;
43172
+ }
43008
43173
  .buttons,
43009
43174
  .locked {
43010
43175
  display: flex;
@@ -43030,6 +43195,144 @@ var SliccSettings = class extends ThemedElement {
43030
43195
  line-height: var(--swc-line-height-200);
43031
43196
  --swc-icon-color: var(--swc-negative-content-color-default);
43032
43197
  }
43198
+ .actions {
43199
+ display: flex;
43200
+ align-items: center;
43201
+ gap: var(--swc-spacing-75);
43202
+ }
43203
+ .notes {
43204
+ grid-column: 1 / -1;
43205
+ display: grid;
43206
+ gap: var(--swc-spacing-75);
43207
+ margin-block-start: calc(-1 * var(--swc-spacing-200));
43208
+ }
43209
+ .notice {
43210
+ display: flex;
43211
+ align-items: flex-start;
43212
+ gap: var(--swc-spacing-75);
43213
+ margin: 0;
43214
+ color: var(--swc-neutral-subdued-content-color-default);
43215
+ font-size: var(--swc-font-size-75);
43216
+ line-height: var(--swc-line-height-200);
43217
+ --swc-icon-color: var(--swc-notice-visual-color);
43218
+ }
43219
+ .notice swc-icon-alert-triangle {
43220
+ flex: none;
43221
+ margin-block-start: var(--swc-spacing-50);
43222
+ }
43223
+ .add {
43224
+ margin-block-start: var(--swc-spacing-200);
43225
+ margin-inline-start: calc(-1 * var(--swc-spacing-100));
43226
+ }
43227
+ dialog.picker {
43228
+ inline-size: min(520px, calc(100vw - 2 * var(--swc-spacing-300)));
43229
+ }
43230
+ .picker .head {
43231
+ display: flex;
43232
+ align-items: center;
43233
+ justify-content: space-between;
43234
+ gap: var(--swc-spacing-200);
43235
+ padding: var(--swc-spacing-400) var(--swc-spacing-300) var(--swc-spacing-200) var(--swc-spacing-500);
43236
+ }
43237
+ .picker h2 {
43238
+ margin: 0;
43239
+ font-size: var(--swc-heading-size-s);
43240
+ overflow-wrap: anywhere;
43241
+ }
43242
+ .picker .find {
43243
+ display: flex;
43244
+ padding: var(--swc-spacing-100) var(--swc-spacing-500) var(--swc-spacing-200);
43245
+ }
43246
+ .picker .results {
43247
+ block-size: min(400px, calc(100dvh - 280px));
43248
+ overflow-y: auto;
43249
+ overscroll-behavior: contain;
43250
+ padding: var(--swc-spacing-75) var(--swc-spacing-400);
43251
+ border-block: var(--swc-border-width-100) solid var(--swc-gray-200);
43252
+ }
43253
+ .options {
43254
+ display: grid;
43255
+ gap: var(--swc-spacing-50);
43256
+ }
43257
+ .option {
43258
+ display: grid;
43259
+ grid-template-columns: minmax(0, 1fr) max-content;
43260
+ align-items: baseline;
43261
+ gap: var(--swc-spacing-50) var(--swc-spacing-300);
43262
+ padding: var(--swc-spacing-100) var(--swc-spacing-100);
43263
+ border-radius: var(--swc-corner-radius-medium-default);
43264
+ cursor: pointer;
43265
+ }
43266
+ .option:hover,
43267
+ .option:focus {
43268
+ background: var(--swc-gray-200);
43269
+ }
43270
+ .option:focus {
43271
+ outline: none;
43272
+ }
43273
+ .option:focus-visible {
43274
+ outline: var(--swc-focus-indicator-thickness) solid var(--swc-focus-indicator-color);
43275
+ outline-offset: calc(-1 * var(--swc-focus-indicator-thickness));
43276
+ }
43277
+ .option[aria-disabled='true'] {
43278
+ cursor: default;
43279
+ }
43280
+ .option[aria-disabled='true']:hover {
43281
+ background: none;
43282
+ }
43283
+ .option .name {
43284
+ font-weight: var(--swc-bold-font-weight);
43285
+ overflow-wrap: anywhere;
43286
+ }
43287
+ .option[aria-disabled='true'] .name {
43288
+ color: var(--swc-neutral-subdued-content-color-default);
43289
+ }
43290
+ .option .tag {
43291
+ justify-self: end;
43292
+ color: var(--swc-neutral-subdued-content-color-default);
43293
+ font-size: var(--swc-font-size-75);
43294
+ }
43295
+ .added {
43296
+ display: inline-flex;
43297
+ align-items: center;
43298
+ gap: var(--swc-spacing-50);
43299
+ }
43300
+ .option .description,
43301
+ .option .notice {
43302
+ grid-column: 1 / -1;
43303
+ }
43304
+ .option .description,
43305
+ .lead,
43306
+ .empty {
43307
+ margin: 0;
43308
+ color: var(--swc-neutral-subdued-content-color-default);
43309
+ }
43310
+ .empty {
43311
+ padding: var(--swc-spacing-200) var(--swc-spacing-100);
43312
+ overflow-wrap: anywhere;
43313
+ }
43314
+ .picker .end {
43315
+ justify-content: flex-end;
43316
+ padding: var(--swc-spacing-300) var(--swc-spacing-500) var(--swc-spacing-400);
43317
+ }
43318
+ .step {
43319
+ display: grid;
43320
+ gap: var(--swc-spacing-200);
43321
+ padding: var(--swc-spacing-100) var(--swc-spacing-500) var(--swc-spacing-500);
43322
+ }
43323
+ .step .key.stacked {
43324
+ max-width: none;
43325
+ }
43326
+ .step sp-textfield {
43327
+ inline-size: 100%;
43328
+ }
43329
+ .step .buttons {
43330
+ margin-block-start: var(--swc-spacing-200);
43331
+ }
43332
+ .get-key {
43333
+ justify-self: start;
43334
+ font-size: var(--swc-font-size-75);
43335
+ }
43033
43336
  @container (max-width: 480px) {
43034
43337
  .page {
43035
43338
  padding-inline: var(--swc-spacing-300);
@@ -43060,7 +43363,8 @@ var SliccSettings = class extends ThemedElement {
43060
43363
  .state {
43061
43364
  grid-area: state;
43062
43365
  }
43063
- .account swc-button {
43366
+ .account swc-button,
43367
+ .account .actions {
43064
43368
  grid-area: action;
43065
43369
  }
43066
43370
  .key sp-textfield {
@@ -43080,6 +43384,7 @@ var SliccSettings = class extends ThemedElement {
43080
43384
  ...super.subscribe(model),
43081
43385
  model.settings.on("settings", settle2),
43082
43386
  model.settings.on("accounts", settle2),
43387
+ model.settings.on("providers", update),
43083
43388
  ...model.vault ? [model.vault.on("status", update)] : []
43084
43389
  ];
43085
43390
  }
@@ -43126,6 +43431,7 @@ var SliccSettings = class extends ThemedElement {
43126
43431
  try {
43127
43432
  await (fields ? settings?.connect(id, secret, fields) : secret === void 0 ? settings?.connect(id) : settings?.connect(id, secret));
43128
43433
  if (this.entering === id) this.entering = null;
43434
+ if (this.adding?.id === id && this.#added(id)?.status === "connected") this.#finish(id);
43129
43435
  } catch (error) {
43130
43436
  if (!this.#cancelled.has(id)) this.failure = { id, message: error.message };
43131
43437
  } finally {
@@ -43161,7 +43467,82 @@ var SliccSettings = class extends ThemedElement {
43161
43467
  this.#busy(id, false);
43162
43468
  return;
43163
43469
  }
43164
- await this.connect(id, void 0, fields);
43470
+ await this.#connectBases(account, void 0, fields, vault);
43471
+ }
43472
+ #baseOf(value) {
43473
+ const trimmed = value?.trim() ?? "";
43474
+ try {
43475
+ return new URL(trimmed).protocol === "https:" ? trimmed : void 0;
43476
+ } catch {
43477
+ return void 0;
43478
+ }
43479
+ }
43480
+ #oldBase(account, field) {
43481
+ return this.#baseOf(field.value) ?? this.#bases.get(`${account.id} ${field.name}`);
43482
+ }
43483
+ #baseChanges(account, fields) {
43484
+ return (account.fields ?? []).filter((field) => field.customBase).map((field) => ({
43485
+ field: field.name,
43486
+ url: this.#baseOf(fields?.[field.name]),
43487
+ old: this.#oldBase(account, field)
43488
+ })).filter((change) => change.url !== change.old);
43489
+ }
43490
+ #modelChange(name, changes2, from, to) {
43491
+ const entries = (key2) => changes2.flatMap((change) => change[key2] ? [{ name, url: change[key2] }] : []);
43492
+ const remove2 = entries(from);
43493
+ const add = entries(to);
43494
+ if (!remove2.length && !add.length) return null;
43495
+ return {
43496
+ ...remove2.length ? { remove: { model: remove2 } } : {},
43497
+ ...add.length ? { add: { model: add } } : {}
43498
+ };
43499
+ }
43500
+ async #connectBases(account, secret, fields, vault = null) {
43501
+ const model = this.model;
43502
+ const live = vault?.endpoints ? vault : null;
43503
+ const { id } = account;
43504
+ const name = recordName(account);
43505
+ const changes2 = this.#baseChanges(account, fields);
43506
+ const forward = live ? this.#modelChange(name, changes2, "old", "url") : null;
43507
+ if (forward) {
43508
+ this.#busy(id, true);
43509
+ try {
43510
+ await live?.endpoints?.(forward);
43511
+ } catch (error) {
43512
+ this.#busy(id, false);
43513
+ const field = changes2.find((change) => change.url) ?? changes2[0];
43514
+ if (this.model === model) this.#refuse(id, field.field, failureText(error));
43515
+ return;
43516
+ }
43517
+ if (this.model !== model) {
43518
+ this.#busy(id, false);
43519
+ return;
43520
+ }
43521
+ }
43522
+ await this.connect(id, secret, fields);
43523
+ if (this.failure?.id === id) {
43524
+ this.#placeFailure(account, fields);
43525
+ const back = forward ? this.#modelChange(name, changes2, "url", "old") : null;
43526
+ if (back) await live?.endpoints?.(back).catch(() => {
43527
+ });
43528
+ return;
43529
+ }
43530
+ for (const change of changes2) {
43531
+ const key2 = `${id} ${change.field}`;
43532
+ if (change.url) this.#bases.set(key2, change.url);
43533
+ else this.#bases.delete(key2);
43534
+ }
43535
+ }
43536
+ #placeFailure(account, fields) {
43537
+ const message = this.failure.message;
43538
+ const field = (account.fields ?? []).find(
43539
+ (candidate) => candidate.customBase && !!fields?.[candidate.name] && [candidate.name, candidate.label, "https", "url", "port", "userinfo"].some(
43540
+ (word) => new RegExp(`\\b${word.replace(/[^a-z0-9 ]/gi, "\\$&")}\\b`, "i").test(message)
43541
+ )
43542
+ );
43543
+ if (!field) return;
43544
+ this.failure = null;
43545
+ this.#refuse(account.id, field.name, message);
43165
43546
  }
43166
43547
  #refuse(id, field, message) {
43167
43548
  this.problem = { id, field, message };
@@ -43179,19 +43560,35 @@ var SliccSettings = class extends ThemedElement {
43179
43560
  const value = input.value.trim();
43180
43561
  if (value) values[field.name] = value;
43181
43562
  else if (field.required && field.options?.length !== 0) {
43182
- this.#refuse(account.id, field.name, `${field.label} can\u2019t be empty`);
43563
+ this.#refuse(
43564
+ account.id,
43565
+ field.name,
43566
+ field.options ? `Select a ${field.label.toLowerCase()}.` : `${field.label} can\u2019t be empty`
43567
+ );
43183
43568
  return null;
43184
43569
  }
43185
43570
  }
43186
43571
  return values;
43187
43572
  }
43573
+ #added(id) {
43574
+ return this.model?.settings.accounts().find((account) => account.id === id);
43575
+ }
43576
+ #offered() {
43577
+ return this.model?.settings.providers?.() ?? null;
43578
+ }
43188
43579
  #start(account) {
43189
43580
  if (account.auth !== "api-key") {
43190
43581
  void this.connect(account.id);
43191
43582
  return;
43192
43583
  }
43584
+ this.#enter(account);
43585
+ }
43586
+ #enter(account) {
43193
43587
  this.failure = null;
43194
43588
  this.problem = null;
43589
+ this.filled = new Set(
43590
+ (account.fields ?? []).filter((field) => field.options || field.value?.trim()).map((field) => field.name)
43591
+ );
43195
43592
  this.entering = account.id;
43196
43593
  void this.updateComplete.then(() => this.focusOn(".key sp-textfield", ".key input"));
43197
43594
  }
@@ -43199,6 +43596,7 @@ var SliccSettings = class extends ThemedElement {
43199
43596
  event2.preventDefault();
43200
43597
  const form = event2.currentTarget;
43201
43598
  this.problem = null;
43599
+ this.filled = new Set([...this.filled].filter((name) => name !== "value"));
43202
43600
  if (this.#vault(account)) {
43203
43601
  const value = take(form.querySelector("input[data-field=value]"));
43204
43602
  const fields2 = this.#fields(form, account);
@@ -43209,7 +43607,7 @@ var SliccSettings = class extends ThemedElement {
43209
43607
  const secret = field.value.trim();
43210
43608
  field.value = "";
43211
43609
  const fields = this.#fields(form, account);
43212
- if (secret && fields !== null) void this.connect(account.id, secret, fields);
43610
+ if (secret && fields !== null) void this.#connectBases(account, secret, fields);
43213
43611
  }
43214
43612
  #submitOnEnter(event2) {
43215
43613
  const target2 = event2.target;
@@ -43220,7 +43618,7 @@ var SliccSettings = class extends ThemedElement {
43220
43618
  #send(event2) {
43221
43619
  event2.currentTarget.closest("form")?.requestSubmit();
43222
43620
  }
43223
- #enter(event2) {
43621
+ #enterKey(event2) {
43224
43622
  if (event2.key === "Enter") this.#send(event2);
43225
43623
  }
43226
43624
  #cancel() {
@@ -43229,11 +43627,11 @@ var SliccSettings = class extends ThemedElement {
43229
43627
  this.failure = null;
43230
43628
  this.problem = null;
43231
43629
  }
43232
- #problem(account, field) {
43630
+ #problem(account, field, icon = true) {
43233
43631
  const problem = this.problem;
43234
43632
  if (problem?.id !== account.id || problem.field !== field) return A;
43235
43633
  return b`<div class="problem" id="key-problem" role="alert">
43236
- <swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>
43634
+ ${icon ? b`<swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>` : A}
43237
43635
  <span>${problem.message}</span>
43238
43636
  </div>`;
43239
43637
  }
@@ -43245,35 +43643,75 @@ var SliccSettings = class extends ThemedElement {
43245
43643
  }
43246
43644
  #extra(account) {
43247
43645
  return (account.fields ?? []).map(
43248
- (field) => b`<label class="field">
43249
- <span>${field.label}</span>${this.#control(account, field)}
43250
- </label>
43251
- ${this.#problem(account, field.name)}`
43646
+ (field) => field.options ? b`<div class="field">
43647
+ <sp-field-label for=${`field-${field.name}`} size="m">${field.label}</sp-field-label>${this.#control(account, field)}
43648
+ ${field.options.length ? A : b`<p class="hint" data-empty=${field.name}>${nothingToChoose}</p>`}
43649
+ ${field.help ? b`<p class="hint" id=${`help-${field.name}`}>${field.help}</p>` : A}
43650
+ ${this.#problem(account, field.name, false)}
43651
+ </div>` : b`<label class="field">
43652
+ <span>${field.label}</span>${this.#control(account, field)}
43653
+ </label>
43654
+ ${field.help ? b`<p class="hint" id=${`help-${field.name}`}>${field.help}</p>` : A}
43655
+ ${this.#problem(account, field.name)}`
43252
43656
  );
43253
43657
  }
43658
+ #describedBy(account, field) {
43659
+ const problem = this.#described(account, field.name);
43660
+ const ids = [problem === A ? "" : problem, field.help ? `help-${field.name}` : ""];
43661
+ return ids.filter(Boolean).join(" ") || A;
43662
+ }
43254
43663
  #control(account, field) {
43255
43664
  if (field.options) {
43256
- const chosen = field.value ?? field.options[0]?.value;
43257
- return b`<select
43665
+ const empty3 = !field.options.length;
43666
+ const described = this.problem?.id === account.id && this.problem.field === field.name;
43667
+ return b`<sp-picker
43668
+ size="m"
43258
43669
  data-account-field=${field.name}
43259
- ?disabled=${!field.options.length}
43670
+ label=${empty3 ? "None available" : field.label}
43671
+ id=${`field-${field.name}`}
43672
+ value=${start(field.options, field.value)}
43673
+ ?disabled=${empty3}
43674
+ ?invalid=${described}
43260
43675
  aria-required=${field.required ? "true" : A}
43261
- aria-invalid=${this.#invalid(account, field.name)}
43262
- aria-describedby=${this.#described(account, field.name)}
43263
43676
  >
43264
- ${field.options.map((option2) => b`<option value=${option2.value} ?selected=${option2.value === chosen}>${option2.label}</option>`)}
43265
- </select>`;
43677
+ ${field.options.map((option2) => b`<sp-menu-item value=${option2.value}>${option2.label}</sp-menu-item>`)}
43678
+ ${described || empty3 || field.help ? b`<span slot="description" class="sr">${described ? this.problem?.message : empty3 ? nothingToChoose : field.help}</span>` : A}
43679
+ </sp-picker>`;
43266
43680
  }
43267
43681
  return b`<input
43268
43682
  type="text"
43269
43683
  autocomplete="off"
43270
43684
  spellcheck="false"
43271
43685
  data-account-field=${field.name}
43686
+ value=${field.value ?? A}
43687
+ placeholder=${field.placeholder ?? A}
43272
43688
  aria-required=${field.required ? "true" : A}
43273
43689
  aria-invalid=${this.#invalid(account, field.name)}
43274
- aria-describedby=${this.#described(account, field.name)}
43690
+ aria-describedby=${this.#describedBy(account, field)}
43275
43691
  />`;
43276
43692
  }
43693
+ #getKey(account) {
43694
+ if (!account.keyUrl) return A;
43695
+ return b`<a class="swc-Link get-key" href=${account.keyUrl} target="_blank" rel="noopener noreferrer" data-action="get-key">Get a key</a>`;
43696
+ }
43697
+ #leave(account) {
43698
+ const adding = this.adding?.id === account.id;
43699
+ return b`<swc-button size="m" variant="secondary" data-action=${adding ? "back" : "cancel"} @click=${() => adding ? this.#back() : this.#cancel()}>${adding ? "Back" : "Cancel"}</swc-button>`;
43700
+ }
43701
+ #saveLabel(account) {
43702
+ return this.adding?.id === account.id ? "Connect" : "Save";
43703
+ }
43704
+ #ready(account) {
43705
+ if (!this.#offered()) return true;
43706
+ const required = (account.fields ?? []).filter((field) => field.required && !field.options);
43707
+ return ["value", ...required.map((field) => field.name)].every((name) => this.filled.has(name));
43708
+ }
43709
+ #fill(event2) {
43710
+ const input = event2.target;
43711
+ const name = input.dataset.accountField ?? "value";
43712
+ const rest = [...this.filled].filter((candidate) => candidate !== name);
43713
+ this.filled = new Set(input.value.trim() ? [...rest, name] : rest);
43714
+ }
43277
43715
  #unlock(vault) {
43278
43716
  return b`<div class="locked" data-locked>
43279
43717
  <span>Unlock the vault first</span>
@@ -43292,7 +43730,7 @@ var SliccSettings = class extends ThemedElement {
43292
43730
  const busy = this.pending.has(account.id);
43293
43731
  const locked = !!vault.status()?.locked;
43294
43732
  const name = secretNames[account.secret ?? "api-key"];
43295
- return b`<form class="key stacked" data-id=${account.id} data-store="vault" @submit=${(event2) => this.#submit(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)}>
43733
+ return b`<form class="key stacked" data-id=${account.id} data-store="vault" @submit=${(event2) => this.#submit(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)} @input=${(event2) => this.#fill(event2)}>
43296
43734
  <label class="field"><span>${account.provider} ${name}</span><input
43297
43735
  type="password"
43298
43736
  autocomplete="off"
@@ -43303,11 +43741,12 @@ var SliccSettings = class extends ThemedElement {
43303
43741
  aria-describedby=${this.#described(account, "value")}
43304
43742
  /></label>
43305
43743
  ${this.#problem(account, "value")}
43306
- ${this.#extra(account)}
43307
43744
  ${locked ? this.#unlock(vault) : b`<p class="hint">Saved in the vault</p>`}
43745
+ ${this.#getKey(account)}
43746
+ ${this.#extra(account)}
43308
43747
  <div class="buttons">
43309
- <swc-button size="m" variant="accent" data-action="save" ?pending=${busy} ?disabled=${locked} @click=${(event2) => this.#send(event2)}>Save</swc-button>
43310
- <swc-button size="m" variant="secondary" data-action="cancel" @click=${() => this.#cancel()}>Cancel</swc-button>
43748
+ <swc-button size="m" variant="accent" data-action="save" ?pending=${busy} ?disabled=${locked || !busy && !this.#ready(account)} @click=${(event2) => this.#send(event2)}>${this.#saveLabel(account)}</swc-button>
43749
+ ${this.#leave(account)}
43311
43750
  </div>
43312
43751
  </form>`;
43313
43752
  }
@@ -43316,23 +43755,31 @@ var SliccSettings = class extends ThemedElement {
43316
43755
  const vault = this.#vault(account);
43317
43756
  if (vault) return this.#kept(account, vault);
43318
43757
  const busy = this.pending.has(account.id);
43319
- const extra = account.fields?.length;
43320
- return b`<form class=${extra ? "key stacked" : "key"} data-id=${account.id} @submit=${(event2) => this.#submit(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)}>
43321
- <sp-textfield
43758
+ const extra = account.fields?.length || account.keyUrl;
43759
+ return b`<form class=${extra ? "key stacked" : "key"} data-id=${account.id} @submit=${(event2) => this.#submit(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)} @input=${(event2) => this.#fill(event2)}>
43760
+ ${extra ? b`<div class="field"><sp-field-label for="key-secret" size="m">${this.#secretLabel(account)}</sp-field-label>${this.#secret(account)}</div>` : this.#secret(account)}
43761
+ ${this.#getKey(account)}
43762
+ ${extra ? this.#extra(account) : A}
43763
+ ${extra ? b`<div class="buttons">${this.#buttons(account, busy)}</div>` : this.#buttons(account, busy)}
43764
+ </form>`;
43765
+ }
43766
+ #secretLabel(account) {
43767
+ return `${account.provider} ${secretNames[account.secret ?? "api-key"]}`;
43768
+ }
43769
+ #secret(account) {
43770
+ return b`<sp-textfield
43322
43771
  size="m"
43323
43772
  type="password"
43324
43773
  autocomplete="off"
43325
- label=${`${account.provider} ${secretNames[account.secret ?? "api-key"]}`}
43774
+ id="key-secret"
43775
+ label=${this.#secretLabel(account)}
43326
43776
  placeholder=${`Paste the ${secretNames[account.secret ?? "api-key"]}`}
43327
- @keydown=${(event2) => this.#enter(event2)}
43328
- ></sp-textfield>
43329
- ${extra ? this.#extra(account) : A}
43330
- ${extra ? b`<div class="buttons">${this.#buttons(busy)}</div>` : this.#buttons(busy)}
43331
- </form>`;
43777
+ @keydown=${(event2) => this.#enterKey(event2)}
43778
+ ></sp-textfield>`;
43332
43779
  }
43333
- #buttons(busy) {
43334
- return b`<swc-button size="m" variant="accent" data-action="save" ?pending=${busy} @click=${(event2) => this.#send(event2)}>Save</swc-button>
43335
- <swc-button size="m" variant="secondary" data-action="cancel" @click=${() => this.#cancel()}>Cancel</swc-button>`;
43780
+ #buttons(account, busy) {
43781
+ return b`<swc-button size="m" variant="accent" data-action="save" ?pending=${busy} ?disabled=${!busy && !this.#ready(account)} @click=${(event2) => this.#send(event2)}>${this.#saveLabel(account)}</swc-button>
43782
+ ${this.#leave(account)}`;
43336
43783
  }
43337
43784
  #inVault(account) {
43338
43785
  const vault = this.model?.vault;
@@ -43384,7 +43831,8 @@ var SliccSettings = class extends ThemedElement {
43384
43831
  get busyReason() {
43385
43832
  const accounts = this.model?.settings.accounts() ?? [];
43386
43833
  if (accounts.some((account) => this.#signingIn(account))) return "signing in";
43387
- return this.entering === null ? null : "a key is being entered";
43834
+ if (this.entering !== null) return "a key is being entered";
43835
+ return this.picking ? "a provider is being added" : null;
43388
43836
  }
43389
43837
  #signingIn(account) {
43390
43838
  return account.status === "signing-in" || account.auth !== "api-key" && account.status !== "connected" && this.pending.has(account.id);
@@ -43441,10 +43889,266 @@ var SliccSettings = class extends ThemedElement {
43441
43889
  <span class="provider">${account.provider}</span>
43442
43890
  <span class="identity">${account.identity}${this.#inVault(account) ? b`<span class="kept" data-store="vault">${account.identity ? " \xB7 " : ""}${kept2(account)}</span>` : A}</span>
43443
43891
  <swc-status-light class="state" variant=${variant}>${label}</swc-status-light>
43444
- ${this.#action(account)}
43892
+ ${this.#removable(account) ? b`<div class="actions">${this.#action(account)}${this.#removeButton(account)}</div>` : this.#action(account)}
43445
43893
  </div>
43446
- ${this.#key(account)}
43447
- ${failure2}`;
43894
+ ${this.#offered() ? this.#rowNotes(account) : A}
43895
+ ${this.adding?.id === account.id ? A : b`${this.#key(account)}${failure2}`}`;
43896
+ }
43897
+ #removable(account) {
43898
+ return !!this.#offered() && !!this.model?.settings.remove && account.group !== "mcp";
43899
+ }
43900
+ #removeButton(account) {
43901
+ const id = `remove-${account.id}`;
43902
+ const label = `Remove ${account.provider}`;
43903
+ return b`<swc-action-button
43904
+ id=${id}
43905
+ size="m"
43906
+ quiet
43907
+ data-action="remove-account"
43908
+ accessible-label=${label}
43909
+ ?disabled=${this.pending.has(account.id)}
43910
+ @click=${() => this.#remove(account)}
43911
+ ><swc-icon-delete slot="icon"></swc-icon-delete></swc-action-button
43912
+ ><swc-tooltip for=${id} placement="top">${label}</swc-tooltip>`;
43913
+ }
43914
+ async #remove(account) {
43915
+ const model = this.model;
43916
+ const body4 = removeBodies[account.auth ?? ""] ?? "Its key is deleted from the vault. Models from it stop working.";
43917
+ const title = `Remove ${account.provider}?`;
43918
+ if (!await confirm({ title, body: body4, action: "Remove" })) return;
43919
+ if (this.model !== model) return;
43920
+ clearValues(this.renderRoot);
43921
+ if (this.entering === account.id) this.entering = null;
43922
+ const vault = model.vault;
43923
+ const keyed = account.store === "vault" && account.auth === "api-key" && !!vault?.remove;
43924
+ if (keyed && !await this.#forget(account, vault)) return;
43925
+ if (this.model !== model) return;
43926
+ this.#busy(account.id, true);
43927
+ this.failure = null;
43928
+ try {
43929
+ await model.settings.remove?.(account.id);
43930
+ } catch (error) {
43931
+ if (this.model === model) this.failure = { id: account.id, message: failureText(error) };
43932
+ return;
43933
+ } finally {
43934
+ this.#busy(account.id, false);
43935
+ }
43936
+ this.focusOn("[data-action=add-provider]");
43937
+ for (const field of account.fields ?? []) this.#bases.delete(`${account.id} ${field.name}`);
43938
+ }
43939
+ #notice(needs, size4 = "long") {
43940
+ if (needs !== "cors-free-transport") return A;
43941
+ return b`<p class="notice" data-notice=${needs}><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${corsNotice[size4]}</span></p>`;
43942
+ }
43943
+ #signingNote(account) {
43944
+ const key2 = `settings:${account.id}`;
43945
+ if (account.auth !== "oauth" || !this.#signingIn(account)) {
43946
+ this.#waits.done(key2);
43947
+ return A;
43948
+ }
43949
+ return b`<p class="hint" role="status" data-signing>${signInText(this.#waits.waited(key2))}</p>`;
43950
+ }
43951
+ #rowNotes(account) {
43952
+ const notice = this.#notice(account.needs);
43953
+ const signing = this.adding?.id === account.id ? A : this.#signingNote(account);
43954
+ if (notice === A && signing === A) return A;
43955
+ return b`<div class="notes">${notice}${signing}</div>`;
43956
+ }
43957
+ #openPicker() {
43958
+ this.query = "";
43959
+ this.adding = null;
43960
+ this.picking = true;
43961
+ }
43962
+ updated(changed) {
43963
+ super.updated(changed);
43964
+ const dialog = this.#dialog;
43965
+ if (!changed.has("picking") || !dialog || dialog.open) return;
43966
+ dialog.showModal();
43967
+ const field = this.renderRoot.querySelector("sp-search");
43968
+ void field.updateComplete.then(() => field.focus());
43969
+ }
43970
+ get #dialog() {
43971
+ return this.renderRoot.querySelector("dialog.picker");
43972
+ }
43973
+ #closePicker() {
43974
+ this.#dialog?.close();
43975
+ }
43976
+ #closed() {
43977
+ const id = this.adding?.id;
43978
+ if (id !== void 0 && this.entering === id) {
43979
+ clearValues(this.renderRoot);
43980
+ this.entering = null;
43981
+ this.problem = null;
43982
+ }
43983
+ if (id !== void 0 && this.failure?.id === id) this.failure = null;
43984
+ this.picking = false;
43985
+ this.adding = null;
43986
+ this.query = "";
43987
+ const landing = this.#landing;
43988
+ this.#landing = null;
43989
+ void this.updateComplete.then(() => {
43990
+ const root2 = this.renderRoot;
43991
+ const row = landing ? root2.querySelector(`.account[data-id="${CSS.escape(landing)}"] swc-button`) : null;
43992
+ (row ?? root2.querySelector("[data-action=add-provider]"))?.focus();
43993
+ });
43994
+ }
43995
+ #finish(id) {
43996
+ this.#landing = id;
43997
+ this.#closePicker();
43998
+ }
43999
+ #choose(option2) {
44000
+ if (this.#added(option2.id)) return;
44001
+ this.adding = option2;
44002
+ this.failure = null;
44003
+ if (option2.auth === "api-key" || option2.auth === "fields") {
44004
+ this.#enter(draft(option2));
44005
+ return;
44006
+ }
44007
+ void this.updateComplete.then(
44008
+ () => this.focusOn("[data-step] [data-action=sign-in]", "[data-step] [data-action=connect]")
44009
+ );
44010
+ }
44011
+ #back() {
44012
+ const id = this.adding.id;
44013
+ clearValues(this.renderRoot);
44014
+ if (this.entering === id) this.entering = null;
44015
+ if (this.failure?.id === id) this.failure = null;
44016
+ this.problem = null;
44017
+ this.adding = null;
44018
+ void this.updateComplete.then(
44019
+ () => this.renderRoot.querySelector(`.option[data-provider="${CSS.escape(id)}"]`)?.focus()
44020
+ );
44021
+ }
44022
+ #options() {
44023
+ return [...this.renderRoot.querySelectorAll(".option")];
44024
+ }
44025
+ #pickerKey(event2) {
44026
+ if (event2.key !== "Escape") return;
44027
+ event2.preventDefault();
44028
+ this.#closePicker();
44029
+ }
44030
+ #searchKey(event2) {
44031
+ if (event2.key === "ArrowDown") {
44032
+ event2.preventDefault();
44033
+ this.#options()[0]?.focus();
44034
+ return;
44035
+ }
44036
+ if (event2.key !== "Enter") return;
44037
+ event2.preventDefault();
44038
+ const first2 = matchProviders(this.#offered() ?? [], this.query).find(
44039
+ (option2) => !this.#added(option2.id)
44040
+ );
44041
+ if (first2) this.#choose(first2);
44042
+ }
44043
+ #optionKey(event2, option2) {
44044
+ const options = this.#options();
44045
+ const at = options.indexOf(event2.currentTarget);
44046
+ const moves = {
44047
+ ArrowDown: Math.min(at + 1, options.length - 1),
44048
+ ArrowUp: at - 1,
44049
+ Home: 0,
44050
+ End: options.length - 1
44051
+ };
44052
+ const typed3 = event2.key.length === 1 && !event2.ctrlKey && !event2.metaKey && !event2.altKey;
44053
+ if (event2.key in moves) {
44054
+ const to = moves[event2.key];
44055
+ if (to < 0) this.focusOn("sp-search");
44056
+ else options[to].focus();
44057
+ } else if (event2.key === "Enter" || event2.key === " ") this.#choose(option2);
44058
+ else if (typed3 || event2.key === "Backspace") {
44059
+ this.query = typed3 ? this.query + event2.key : this.query.slice(0, -1);
44060
+ this.focusOn("sp-search");
44061
+ } else return;
44062
+ event2.preventDefault();
44063
+ }
44064
+ #option(option2) {
44065
+ const added = !!this.#added(option2.id);
44066
+ return b`<div
44067
+ class="option"
44068
+ role="option"
44069
+ tabindex="-1"
44070
+ data-provider=${option2.id}
44071
+ data-auth=${option2.auth}
44072
+ aria-disabled=${added ? "true" : "false"}
44073
+ @click=${() => this.#choose(option2)}
44074
+ @keydown=${(event2) => this.#optionKey(event2, option2)}
44075
+ >
44076
+ <span class="name">${option2.name}</span>
44077
+ <span class="tag">${added ? b`<span class="added" data-added><swc-icon-checkmark size="s" aria-hidden="true"></swc-icon-checkmark>Added</span>` : authHints[option2.auth]}</span>
44078
+ ${option2.description ? b`<span class="description">${option2.description}</span>` : A}
44079
+ ${this.#notice(option2.needs, "short")}
44080
+ </div>`;
44081
+ }
44082
+ #pickStep(offered) {
44083
+ const matches2 = matchProviders(offered, this.query);
44084
+ return b`<div class="find">${search({
44085
+ label: "Search providers",
44086
+ value: this.query,
44087
+ input: (value) => {
44088
+ this.query = value;
44089
+ },
44090
+ keydown: (event2) => this.#searchKey(event2)
44091
+ })}</div>
44092
+ <div class="results">${matches2.length ? b`<div class="options" role="listbox" aria-label="Providers">${matches2.map((option2) => this.#option(option2))}</div>` : b`<p class="empty" role="status">No provider matches “${this.query.trim()}”.</p>`}</div>
44093
+ <div class="buttons end">
44094
+ <swc-button size="m" variant="secondary" data-action="close" @click=${() => this.#closePicker()}>Cancel</swc-button>
44095
+ </div>`;
44096
+ }
44097
+ #signInStep(account) {
44098
+ const signing = this.#signingIn(account);
44099
+ const cancel = signing && !!this.model?.settings.cancel;
44100
+ return b`<p class="hint">Sign in to ${account.provider} in a new window.</p>
44101
+ ${signing ? this.#signingNote(account) : A}
44102
+ <div class="buttons">
44103
+ ${cancel ? b`<swc-button size="m" variant="secondary" data-action="cancel-sign-in" @click=${() => this.#stopSigningIn(account)}>Cancel</swc-button>` : A}
44104
+ <swc-button size="m" variant="accent" data-action="sign-in" ?pending=${signing} @click=${() => this.connect(account.id)}>${signing ? "Signing in\u2026" : "Sign in"}</swc-button>
44105
+ ${signing ? A : this.#leave(account)}
44106
+ </div>`;
44107
+ }
44108
+ #localStep(account) {
44109
+ return b`<p class="hint">SLICC connects to ${account.provider} on this device.</p>
44110
+ <div class="buttons">
44111
+ <swc-button size="m" variant="accent" data-action="connect" ?pending=${this.pending.has(account.id)} @click=${() => this.connect(account.id)}>Connect</swc-button>
44112
+ ${this.#leave(account)}
44113
+ </div>`;
44114
+ }
44115
+ #connectStep(option2) {
44116
+ const account = this.#added(option2.id) ?? draft(option2);
44117
+ const step = option2.auth === "oauth" ? this.#signInStep(account) : option2.auth === "local" ? this.#localStep(account) : this.#key(account);
44118
+ return b`<div class="step" data-step=${option2.auth} data-provider=${option2.id}>
44119
+ ${option2.description ? b`<p class="lead">${option2.description}</p>` : A}
44120
+ ${this.#notice(option2.needs)}
44121
+ ${step}
44122
+ ${this.failure?.id === option2.id ? b`<div class="failure" role="alert"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.failure.message}</span></div>` : A}
44123
+ </div>`;
44124
+ }
44125
+ #picker(offered) {
44126
+ if (!this.picking) return A;
44127
+ const adding = this.adding;
44128
+ return b`<dialog class="picker" aria-labelledby="picker-title" data-picker=${adding ? "connect" : "pick"} @keydown=${(event2) => this.#pickerKey(event2)} @close=${() => this.#closed()}>
44129
+ <div class="head">
44130
+ <h2 id="picker-title">${adding ? `Add ${adding.name}` : "Add a provider"}</h2>
44131
+ <swc-close-button size="m" data-action="close" accessible-label="Close" @click=${() => this.#closePicker()}></swc-close-button>
44132
+ </div>
44133
+ ${adding ? this.#connectStep(adding) : this.#pickStep(offered)}
44134
+ </dialog>`;
44135
+ }
44136
+ #providerList(accounts) {
44137
+ const offered = this.#offered();
44138
+ if (!offered)
44139
+ return b`<div class="accounts">${accounts.map((account) => this.#account(account))}</div>`;
44140
+ const sorted = [...accounts].sort((a23, b13) => listOrder[a23.status] - listOrder[b13.status]);
44141
+ return b`<div class="accounts" data-group="providers">${c(
44142
+ sorted,
44143
+ (account) => account.id,
44144
+ (account) => this.#account(account)
44145
+ )}</div>
44146
+ <div class="add">
44147
+ <swc-action-button size="m" quiet data-action="add-provider" @click=${() => this.#openPicker()}>
44148
+ <swc-icon-add slot="icon"></swc-icon-add>Add a provider
44149
+ </swc-action-button>
44150
+ </div>
44151
+ ${this.#picker(offered)}`;
43448
44152
  }
43449
44153
  render() {
43450
44154
  const settings = this.model?.settings;
@@ -43500,7 +44204,7 @@ var SliccSettings = class extends ThemedElement {
43500
44204
  </div>
43501
44205
  </div>
43502
44206
  <h2 data-section="accounts">Accounts</h2>
43503
- <div class="accounts">${providers.map((account) => this.#account(account))}</div>
44207
+ ${this.#providerList(providers)}
43504
44208
  ${servers.length ? b`<h2>MCP servers</h2>
43505
44209
  <div class="accounts" data-group="mcp">${servers.map((account) => this.#account(account))}</div>` : A}
43506
44210
  ${this.model?.vault ? b`<slicc-secrets .model=${this.model}></slicc-secrets>` : A}
@@ -43536,11 +44240,11 @@ function closure(found, source) {
43536
44240
  }
43537
44241
  return used;
43538
44242
  }
43539
- function args(value, start) {
44243
+ function args(value, start2) {
43540
44244
  const parts2 = [];
43541
44245
  let depth = 0;
43542
- let from = start;
43543
- let at = start;
44246
+ let from = start2;
44247
+ let at = start2;
43544
44248
  for (; at < value.length; at++) {
43545
44249
  const char = value[at];
43546
44250
  if (char === "(") depth++;
@@ -52924,14 +53628,14 @@ function splitToken(token2, offsets) {
52924
53628
  return tokens;
52925
53629
  }
52926
53630
  function findFirstBreakpointAfter(breakpoints, offset3) {
52927
- let start = 0;
53631
+ let start2 = 0;
52928
53632
  let end = breakpoints.length;
52929
- while (start < end) {
52930
- const middle = start + (end - start >> 1);
52931
- if (breakpoints[middle] <= offset3) start = middle + 1;
53633
+ while (start2 < end) {
53634
+ const middle = start2 + (end - start2 >> 1);
53635
+ if (breakpoints[middle] <= offset3) start2 = middle + 1;
52932
53636
  else end = middle;
52933
53637
  }
52934
- return start;
53638
+ return start2;
52935
53639
  }
52936
53640
  function splitTokens(tokens, breakpoints) {
52937
53641
  const sorted = [...breakpoints instanceof Set ? breakpoints : new Set(breakpoints)].sort((a23, b13) => a23 - b13);
@@ -52939,11 +53643,11 @@ function splitTokens(tokens, breakpoints) {
52939
53643
  return tokens.map((line) => {
52940
53644
  return line.flatMap((token2) => {
52941
53645
  const tokenEnd = token2.offset + token2.content.length;
52942
- const start = findFirstBreakpointAfter(sorted, token2.offset);
52943
- let end = start;
53646
+ const start2 = findFirstBreakpointAfter(sorted, token2.offset);
53647
+ let end = start2;
52944
53648
  while (end < sorted.length && sorted[end] < tokenEnd) end++;
52945
- if (start === end) return token2;
52946
- const breakpointsInToken = sorted.slice(start, end);
53649
+ if (start2 === end) return token2;
53650
+ const breakpointsInToken = sorted.slice(start2, end);
52947
53651
  for (let index = 0; index < breakpointsInToken.length; index++) breakpointsInToken[index] -= token2.offset;
52948
53652
  return splitToken(token2, breakpointsInToken);
52949
53653
  });
@@ -53049,17 +53753,17 @@ function transformerDecorations() {
53049
53753
  const ctx = getContext(this);
53050
53754
  const lines = [...codeEl.children].filter((i26) => i26.type === "element" && i26.tagName === "span");
53051
53755
  if (lines.length !== ctx.converter.lines.length) throw new ShikiError2(`Number of lines in code element (${lines.length}) does not match the number of lines in the source (${ctx.converter.lines.length}). Failed to apply decorations.`);
53052
- function applyLineSection(line, start, end, decoration) {
53756
+ function applyLineSection(line, start2, end, decoration) {
53053
53757
  const lineEl = lines[line];
53054
53758
  let text2 = "";
53055
53759
  let startIndex = -1;
53056
53760
  let endIndex = -1;
53057
- if (start === 0) startIndex = 0;
53761
+ if (start2 === 0) startIndex = 0;
53058
53762
  if (end === 0) endIndex = 0;
53059
53763
  if (end === Number.POSITIVE_INFINITY) endIndex = lineEl.children.length;
53060
53764
  if (startIndex === -1 || endIndex === -1) for (let i26 = 0; i26 < lineEl.children.length; i26++) {
53061
53765
  text2 += stringify3(lineEl.children[i26]);
53062
- if (startIndex === -1 && text2.length === start) startIndex = i26 + 1;
53766
+ if (startIndex === -1 && text2.length === start2) startIndex = i26 + 1;
53063
53767
  if (endIndex === -1 && text2.length === end) endIndex = i26 + 1;
53064
53768
  }
53065
53769
  if (startIndex === -1) throw new ShikiError2(`Failed to find start index for decoration ${JSON.stringify(decoration.start)}`);
@@ -53097,11 +53801,11 @@ function transformerDecorations() {
53097
53801
  const lineApplies = [];
53098
53802
  const sorted = ctx.decorations.sort((a23, b13) => b13.start.offset - a23.start.offset || a23.end.offset - b13.end.offset);
53099
53803
  for (const decoration of sorted) {
53100
- const { start, end } = decoration;
53101
- if (start.line === end.line) applyLineSection(start.line, start.character, end.character, decoration);
53102
- else if (start.line < end.line) {
53103
- applyLineSection(start.line, start.character, Number.POSITIVE_INFINITY, decoration);
53104
- for (let i26 = start.line + 1; i26 < end.line; i26++) lineApplies.unshift(() => applyLine(i26, decoration));
53804
+ const { start: start2, end } = decoration;
53805
+ if (start2.line === end.line) applyLineSection(start2.line, start2.character, end.character, decoration);
53806
+ else if (start2.line < end.line) {
53807
+ applyLineSection(start2.line, start2.character, Number.POSITIVE_INFINITY, decoration);
53808
+ for (let i26 = start2.line + 1; i26 < end.line; i26++) lineApplies.unshift(() => applyLine(i26, decoration));
53105
53809
  applyLineSection(end.line, 0, end.character, decoration);
53106
53810
  }
53107
53811
  }
@@ -55587,9 +56291,9 @@ var PosixClassMap = /* @__PURE__ */ new Map([
55587
56291
  ["word", r39`[\p{Alpha}\p{M}\p{Nd}\p{Pc}]`],
55588
56292
  ["xdigit", r39`\p{AHex}`]
55589
56293
  ]);
55590
- function range(start, end) {
56294
+ function range(start2, end) {
55591
56295
  const range22 = [];
55592
- for (let i26 = start; i26 <= end; i26++) {
56296
+ for (let i26 = start2; i26 <= end; i26++) {
55593
56297
  range22.push(i26);
55594
56298
  }
55595
56299
  return range22;
@@ -55598,8 +56302,8 @@ function titleEntry(codePoint) {
55598
56302
  const char = cp(codePoint);
55599
56303
  return [char.toLowerCase(), char];
55600
56304
  }
55601
- function titleRange(start, end) {
55602
- return range(start, end).map((codePoint) => titleEntry(codePoint));
56305
+ function titleRange(start2, end) {
56306
+ return range(start2, end).map((codePoint) => titleEntry(codePoint));
55603
56307
  }
55604
56308
  var UnicodePropertiesWithSpecificCase = /* @__PURE__ */ new Set([
55605
56309
  "Lower",
@@ -56829,15 +57533,15 @@ function getCharEscape(codePoint, { escDigit, inCharClass, useFlagV }) {
56829
57533
  function getCodePointRangesFromChars(chars) {
56830
57534
  const codePoints = chars.map((char) => char.codePointAt(0)).sort((a23, b13) => a23 - b13);
56831
57535
  const values = [];
56832
- let start = null;
57536
+ let start2 = null;
56833
57537
  for (let i26 = 0; i26 < codePoints.length; i26++) {
56834
57538
  if (codePoints[i26 + 1] === codePoints[i26] + 1) {
56835
- start ??= codePoints[i26];
56836
- } else if (start === null) {
57539
+ start2 ??= codePoints[i26];
57540
+ } else if (start2 === null) {
56837
57541
  values.push(codePoints[i26]);
56838
57542
  } else {
56839
- values.push([start, codePoints[i26]]);
56840
- start = null;
57543
+ values.push([start2, codePoints[i26]]);
57544
+ start2 = null;
56841
57545
  }
56842
57546
  }
56843
57547
  return values;
@@ -58481,7 +59185,7 @@ var InteractionManager = class {
58481
59185
  return range3 == null ? void 0 : this.selectionEnds(range3);
58482
59186
  }
58483
59187
  selectionEnds(range3) {
58484
- const start = {
59188
+ const start2 = {
58485
59189
  lineNumber: range3.start,
58486
59190
  side: range3.side
58487
59191
  };
@@ -58489,14 +59193,14 @@ var InteractionManager = class {
58489
59193
  lineNumber: range3.end,
58490
59194
  side: range3.endSide ?? range3.side
58491
59195
  };
58492
- const startIndex = this.selectionPointRowIndex(start);
59196
+ const startIndex = this.selectionPointRowIndex(start2);
58493
59197
  const endIndex = this.selectionPointRowIndex(end);
58494
59198
  if (startIndex == null || endIndex == null) return;
58495
59199
  return startIndex > endIndex ? {
58496
59200
  top: end,
58497
- bottom: start
59201
+ bottom: start2
58498
59202
  } : {
58499
- top: start,
59203
+ top: start2,
58500
59204
  bottom: end
58501
59205
  };
58502
59206
  }
@@ -58814,9 +59518,9 @@ var InteractionManager = class {
58814
59518
  buildSelectedLineRange(anchor, current) {
58815
59519
  return this.buildSelectionRange(anchor.lineNumber, current.lineNumber, anchor.side, current.side);
58816
59520
  }
58817
- buildSelectionRange(start, end, side, endSide) {
59521
+ buildSelectionRange(start2, end, side, endSide) {
58818
59522
  return {
58819
- start,
59523
+ start: start2,
58820
59524
  end,
58821
59525
  ...side != null ? { side } : {},
58822
59526
  ...side !== endSide && endSide != null ? { endSide } : {}
@@ -60401,9 +61105,9 @@ function computeLineOffsets(contents) {
60401
61105
  function linesFromFileContents(contents) {
60402
61106
  const offsets = computeLineOffsets(contents);
60403
61107
  return Array.from({ length: offsets.length }, (_9, i26) => {
60404
- const start = offsets[i26];
61108
+ const start2 = offsets[i26];
60405
61109
  const end = offsets[i26 + 1] ?? contents.length;
60406
- return contents.slice(start, end);
61110
+ return contents.slice(start2, end);
60407
61111
  });
60408
61112
  }
60409
61113
 
@@ -62692,11 +63396,11 @@ function cloneFileDiffMetadata(fileDiff) {
62692
63396
  }
62693
63397
 
62694
63398
  // node_modules/@pierre/diffs/dist/utils/getHunkSideBoundaries.js
62695
- function getHunkSideStartBoundary(start, count2) {
62696
- return start - (count2 === 0 ? 0 : 1);
63399
+ function getHunkSideStartBoundary(start2, count2) {
63400
+ return start2 - (count2 === 0 ? 0 : 1);
62697
63401
  }
62698
- function getHunkSideEndBoundary(start, count2) {
62699
- return getHunkSideStartBoundary(start, count2) + count2;
63402
+ function getHunkSideEndBoundary(start2, count2) {
63403
+ return getHunkSideStartBoundary(start2, count2) + count2;
62700
63404
  }
62701
63405
 
62702
63406
  // node_modules/@pierre/diffs/dist/utils/composeCacheKey.js
@@ -64615,12 +65319,12 @@ function getNearestRenderableAdditionLine({ fileDiff, lineNumber, direction, exp
64615
65319
  }
64616
65320
  if (lineNumber >= modeledEnd) return lineNumber;
64617
65321
  if (direction === "down") {
64618
- for (const [start, end] of ranges) if (end > lineNumber) return Math.max(start, lineNumber);
65322
+ for (const [start2, end] of ranges) if (end > lineNumber) return Math.max(start2, lineNumber);
64619
65323
  return;
64620
65324
  }
64621
65325
  for (let index = ranges.length - 1; index >= 0; index--) {
64622
- const [start, end] = ranges[index];
64623
- if (start <= lineNumber) return Math.min(end - 1, lineNumber);
65326
+ const [start2, end] = ranges[index];
65327
+ if (start2 <= lineNumber) return Math.min(end - 1, lineNumber);
64624
65328
  }
64625
65329
  }
64626
65330
 
@@ -64983,21 +65687,21 @@ function getContextLineIterationBounds(state2, count2, diffStyle) {
64983
65687
  if (!state2.isWindowedHighlight || count2 <= 0) return [0, count2];
64984
65688
  const ranges = [];
64985
65689
  function pushRange(currentCount) {
64986
- const start2 = Math.max(0, state2.viewportStart - currentCount);
65690
+ const start3 = Math.max(0, state2.viewportStart - currentCount);
64987
65691
  const end2 = Math.min(count2, state2.viewportEnd - currentCount);
64988
- if (end2 > start2) ranges.push([start2, end2]);
65692
+ if (end2 > start3) ranges.push([start3, end2]);
64989
65693
  }
64990
65694
  if (diffStyle !== "split") pushRange(state2.unifiedCount);
64991
65695
  if (diffStyle !== "unified") pushRange(state2.splitCount);
64992
65696
  if (ranges.length === 0) return [0, 0];
64993
- let start = ranges[0][0];
65697
+ let start2 = ranges[0][0];
64994
65698
  let end = ranges[0][1];
64995
65699
  for (let index = 1; index < ranges.length; index++) {
64996
65700
  const range3 = ranges[index];
64997
- start = Math.min(start, range3[0]);
65701
+ start2 = Math.min(start2, range3[0]);
64998
65702
  end = Math.max(end, range3[1]);
64999
65703
  }
65000
- return [start, end];
65704
+ return [start2, end];
65001
65705
  }
65002
65706
  function walkContextLines(state2, count2, diffStyle, callback, onSkippedStart, shouldBreak) {
65003
65707
  const [startIndex, endIndex] = getContextLineIterationBounds(state2, count2, diffStyle);
@@ -65025,10 +65729,10 @@ function getChangeIterationRanges(state2, content, diffStyle) {
65025
65729
  const useSplit = diffStyle !== "unified";
65026
65730
  const iterationSpace = diffStyle === "unified" ? "unified" : "split";
65027
65731
  const iterationRanges = [];
65028
- function getVisibleRange(start, count2) {
65029
- if (start + count2 <= state2.viewportStart || start >= state2.viewportEnd) return;
65030
- const visibleStart = Math.max(0, state2.viewportStart - start);
65031
- const visibleEnd = Math.min(count2, state2.viewportEnd - start);
65732
+ function getVisibleRange(start2, count2) {
65733
+ if (start2 + count2 <= state2.viewportStart || start2 >= state2.viewportEnd) return;
65734
+ const visibleStart = Math.max(0, state2.viewportStart - start2);
65735
+ const visibleEnd = Math.min(count2, state2.viewportEnd - start2);
65032
65736
  return visibleEnd > visibleStart ? [visibleStart, visibleEnd] : void 0;
65033
65737
  }
65034
65738
  function mapRangeToIteration(range3, kind) {
@@ -65037,8 +65741,8 @@ function getChangeIterationRanges(state2, content, diffStyle) {
65037
65741
  }
65038
65742
  function pushRange(range3, kind) {
65039
65743
  if (range3 == null) return;
65040
- const [start, end] = mapRangeToIteration(range3, kind);
65041
- if (end > start) iterationRanges.push([start, end]);
65744
+ const [start2, end] = mapRangeToIteration(range3, kind);
65745
+ if (end > start2) iterationRanges.push([start2, end]);
65042
65746
  }
65043
65747
  if (useUnified) {
65044
65748
  pushRange(getVisibleRange(state2.unifiedCount, content.deletions), "deletions");
@@ -65051,10 +65755,10 @@ function getChangeIterationRanges(state2, content, diffStyle) {
65051
65755
  if (iterationRanges.length === 0) return iterationRanges;
65052
65756
  iterationRanges.sort((a23, b13) => a23[0] - b13[0]);
65053
65757
  const merged2 = [iterationRanges[0]];
65054
- for (const [start, end] of iterationRanges.slice(1)) {
65758
+ for (const [start2, end] of iterationRanges.slice(1)) {
65055
65759
  const last = merged2[merged2.length - 1];
65056
- if (start <= last[1]) last[1] = Math.max(last[1], end);
65057
- else merged2.push([start, end]);
65760
+ if (start2 <= last[1]) last[1] = Math.max(last[1], end);
65761
+ else merged2.push([start2, end]);
65058
65762
  }
65059
65763
  return merged2;
65060
65764
  }
@@ -65638,17 +66342,17 @@ function normalizeEditorLines(lines) {
65638
66342
  }
65639
66343
  function findDivergenceCore(deletionLines, additionLines) {
65640
66344
  const maxStart = Math.min(deletionLines.length, additionLines.length);
65641
- let start = 0;
65642
- while (start < maxStart && deletionLines[start] === additionLines[start]) start++;
66345
+ let start2 = 0;
66346
+ while (start2 < maxStart && deletionLines[start2] === additionLines[start2]) start2++;
65643
66347
  let deletionEnd = deletionLines.length;
65644
66348
  let additionEnd = additionLines.length;
65645
- while (deletionEnd > start && additionEnd > start && deletionLines[deletionEnd - 1] === additionLines[additionEnd - 1]) {
66349
+ while (deletionEnd > start2 && additionEnd > start2 && deletionLines[deletionEnd - 1] === additionLines[additionEnd - 1]) {
65646
66350
  deletionEnd--;
65647
66351
  additionEnd--;
65648
66352
  }
65649
- if (start === deletionEnd && start === additionEnd) return;
66353
+ if (start2 === deletionEnd && start2 === additionEnd) return;
65650
66354
  return {
65651
- start,
66355
+ start: start2,
65652
66356
  deletionEnd,
65653
66357
  additionEnd
65654
66358
  };
@@ -65686,8 +66390,8 @@ function applySessionChangedLines(diff, changedAdditionLineIndexes, parseDiffOpt
65686
66390
  hunkIndex++;
65687
66391
  }
65688
66392
  const hunk = hunks[hunkIndex];
65689
- const start = hunk == null ? void 0 : getHunkAdditionStart(hunk);
65690
- if (start == null || line < start || regionIndex != null && regionIndex !== hunkIndex) return rebuildSessionHunks(diff, parseDiffOptions, getPreviousAdditionLine);
66393
+ const start2 = hunk == null ? void 0 : getHunkAdditionStart(hunk);
66394
+ if (start2 == null || line < start2 || regionIndex != null && regionIndex !== hunkIndex) return rebuildSessionHunks(diff, parseDiffOptions, getPreviousAdditionLine);
65691
66395
  regionIndex = hunkIndex;
65692
66396
  }
65693
66397
  if (regionIndex == null) return;
@@ -65783,10 +66487,10 @@ function rebuildExpansionFromAnchors(diff, anchors) {
65783
66487
  if (gapEnd <= gapStart) return;
65784
66488
  let fromStart = 0;
65785
66489
  let fromEnd = 0;
65786
- for (const [start, end] of anchors) {
65787
- if (end <= gapStart || start >= gapEnd) continue;
65788
- if (start <= gapStart) fromStart = Math.max(fromStart, Math.min(end, gapEnd) - gapStart);
65789
- if (end >= gapEnd) fromEnd = Math.max(fromEnd, gapEnd - Math.max(start, gapStart));
66490
+ for (const [start2, end] of anchors) {
66491
+ if (end <= gapStart || start2 >= gapEnd) continue;
66492
+ if (start2 <= gapStart) fromStart = Math.max(fromStart, Math.min(end, gapEnd) - gapStart);
66493
+ if (end >= gapEnd) fromEnd = Math.max(fromEnd, gapEnd - Math.max(start2, gapStart));
65790
66494
  }
65791
66495
  if (fromStart > 0 || fromEnd > 0) rebuilt.set(key2, {
65792
66496
  fromStart,
@@ -70757,7 +71461,7 @@ var Splitview = class {
70757
71461
  for (const item2 of this.viewItems) item2.enabled = false;
70758
71462
  const doc = (_sash$ownerDocument = sash.ownerDocument) !== null && _sash$ownerDocument !== void 0 ? _sash$ownerDocument : document;
70759
71463
  const iframes = disableIframePointEvents(doc);
70760
- const start = this._orientation === "HORIZONTAL" ? event2.clientX : event2.clientY;
71464
+ const start2 = this._orientation === "HORIZONTAL" ? event2.clientX : event2.clientY;
70761
71465
  const sashIndex = firstIndex(this.sashes, (s19) => s19.container === sash);
70762
71466
  const sizes = this.viewItems.map((x7) => x7.size);
70763
71467
  let snapBefore;
@@ -70791,7 +71495,7 @@ var Splitview = class {
70791
71495
  };
70792
71496
  }
70793
71497
  const onPointerMove = (event3) => {
70794
- const delta = (this._orientation === "HORIZONTAL" ? event3.clientX : event3.clientY) - start;
71498
+ const delta = (this._orientation === "HORIZONTAL" ? event3.clientX : event3.clientY) - start2;
70795
71499
  this.resize(sashIndex, delta, sizes, void 0, void 0, minDelta, maxDelta, snapBefore, snapAfter);
70796
71500
  this.distributeEmptySpace();
70797
71501
  this.layoutViews();
@@ -75389,11 +76093,11 @@ var BaseTabGroupIndicator = class {
75389
76093
  */
75390
76094
  trackUnderlines() {
75391
76095
  if (this._rafId !== null) cancelAnimationFrame(this._rafId);
75392
- const start = performance.now();
76096
+ const start2 = performance.now();
75393
76097
  const duration = 250;
75394
76098
  const tick = () => {
75395
76099
  this._positionUnderlinesSync();
75396
- if (performance.now() - start < duration) this._rafId = requestAnimationFrame(tick);
76100
+ if (performance.now() - start2 < duration) this._rafId = requestAnimationFrame(tick);
75397
76101
  else this._rafId = null;
75398
76102
  };
75399
76103
  this._rafId = requestAnimationFrame(tick);
@@ -77658,12 +78362,12 @@ var Tabs = class extends CompositeDisposable {
77658
78362
  _scrollTabIntoView(element2, isVertical) {
77659
78363
  const parentElement = element2.parentElement;
77660
78364
  if (!parentElement) return;
77661
- const start = isVertical ? element2.offsetTop : element2.offsetLeft;
78365
+ const start2 = isVertical ? element2.offsetTop : element2.offsetLeft;
77662
78366
  const size4 = isVertical ? element2.offsetHeight : element2.offsetWidth;
77663
78367
  const scrollStart = isVertical ? parentElement.scrollTop : parentElement.scrollLeft;
77664
78368
  const clientSize = isVertical ? parentElement.clientHeight : parentElement.clientWidth;
77665
- if (start < scrollStart || start + size4 > scrollStart + clientSize) if (isVertical) parentElement.scrollTop = start;
77666
- else parentElement.scrollLeft = start;
78369
+ if (start2 < scrollStart || start2 + size4 > scrollStart + clientSize) if (isVertical) parentElement.scrollTop = start2;
78370
+ else parentElement.scrollLeft = start2;
77667
78371
  }
77668
78372
  /**
77669
78373
  * Activate a tab in response to a plain (non-shift) left pointerdown.