@ai-ecoverse/slicc-spectrum 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/slicc-ui.js CHANGED
@@ -35131,10 +35131,44 @@ var SliccVaultButton = class extends i2 {
35131
35131
  }
35132
35132
  }
35133
35133
  };
35134
+ function valueConfig(doc, theme3) {
35135
+ const lang = doc.documentElement.lang;
35136
+ return {
35137
+ theme: theme3,
35138
+ dir: doc.documentElement.dir === "rtl" ? "rtl" : "ltr",
35139
+ ...lang ? { lang } : {}
35140
+ };
35141
+ }
35142
+ function enters(vault) {
35143
+ return !!(vault?.enter && vault.valueFrame);
35144
+ }
35145
+ function ours(detail, request2, open) {
35146
+ if (request2 !== open) return false;
35147
+ return !("request" in detail) || detail.request === void 0 || detail.request === request2;
35148
+ }
35149
+ function drop(vault, request2) {
35150
+ void vault?.cancel?.(request2).catch(() => {
35151
+ });
35152
+ }
35153
+ function goneText(state2) {
35154
+ return state2 === "expired" ? "The vault\u2019s field timed out. Try again." : "The vault\u2019s field closed. Try again.";
35155
+ }
35156
+ function valueField(vault, request2, config, on2) {
35157
+ return b`<div class="value-field" data-value-frame><slicc-vault-value .vault=${vault} .request=${request2} .config=${config} @vault-value=${(event2) => on2(event2.detail, request2)}></slicc-vault-value></div>`;
35158
+ }
35134
35159
  var frameHeight = 480;
35135
35160
  var frameInset = 4;
35136
35161
  var between = (value, most) => typeof value === "number" && Number.isFinite(value) ? Math.min(Math.max(value, 0), most) : 0;
35137
- var picked = ["event", "request", "state", "code", "message"];
35162
+ var picked = [
35163
+ "event",
35164
+ "request",
35165
+ "state",
35166
+ "code",
35167
+ "message",
35168
+ "name",
35169
+ "placeholder",
35170
+ "record"
35171
+ ];
35138
35172
  function pick(event2) {
35139
35173
  const fields = event2;
35140
35174
  return Object.fromEntries(
@@ -35148,6 +35182,7 @@ var SliccVaultValue = class extends i2 {
35148
35182
  config: { attribute: false }
35149
35183
  };
35150
35184
  #frame = null;
35185
+ #focusing = false;
35151
35186
  #off = null;
35152
35187
  #ready = false;
35153
35188
  #sent = "";
@@ -35182,7 +35217,8 @@ var SliccVaultValue = class extends i2 {
35182
35217
  this.#drop();
35183
35218
  }
35184
35219
  focus() {
35185
- this.#frame?.post({ type: "slicc-vault", v: 1, op: "focus" });
35220
+ this.#focusing = !this.#ready;
35221
+ if (this.#ready) this.#frame?.post({ type: "slicc-vault", v: 1, op: "focus" });
35186
35222
  }
35187
35223
  updated(changed2) {
35188
35224
  if (changed2.has("vault") || changed2.has("request")) this.#drop();
@@ -35223,6 +35259,7 @@ var SliccVaultValue = class extends i2 {
35223
35259
  if (event2.event === "ready") {
35224
35260
  this.#ready = true;
35225
35261
  this.#send();
35262
+ if (this.#focusing) this.focus();
35226
35263
  } else if (event2.event === "size") {
35227
35264
  const inset = between(event2.inset, frameInset);
35228
35265
  element2.style.blockSize = `${between(event2.height, frameHeight)}px`;
@@ -35602,6 +35639,8 @@ var SliccComposer = class extends ThemedElement {
35602
35639
  sharing: { state: true },
35603
35640
  secretError: { state: true },
35604
35641
  secretField: { state: true },
35642
+ secretName: { state: true },
35643
+ entry: { state: true },
35605
35644
  failure: { state: true },
35606
35645
  reattach: { state: true },
35607
35646
  agent: {}
@@ -35635,6 +35674,8 @@ var SliccComposer = class extends ThemedElement {
35635
35674
  this.sharing = false;
35636
35675
  this.secretError = null;
35637
35676
  this.secretField = null;
35677
+ this.secretName = "";
35678
+ this.entry = null;
35638
35679
  this.failure = null;
35639
35680
  this.reattach = null;
35640
35681
  this.agent = "";
@@ -35921,6 +35962,9 @@ var SliccComposer = class extends ThemedElement {
35921
35962
  flex-wrap: wrap;
35922
35963
  gap: var(--swc-spacing-100);
35923
35964
  }
35965
+ .secret .value-field {
35966
+ min-inline-size: min(22rem, 70vw);
35967
+ }
35924
35968
  .secret-error {
35925
35969
  display: flex;
35926
35970
  align-items: flex-start;
@@ -35939,7 +35983,11 @@ var SliccComposer = class extends ThemedElement {
35939
35983
  ];
35940
35984
  willUpdate(changed2) {
35941
35985
  super.willUpdate(changed2);
35942
- if (changed2.has("popup") && this.popup?.kind !== "secret") clearValues(this.renderRoot);
35986
+ if (changed2.has("popup") && this.popup?.kind !== "secret") {
35987
+ clearValues(this.renderRoot);
35988
+ this.#leave();
35989
+ this.secretName = "";
35990
+ }
35943
35991
  if (changed2.has("agent") && this.agent && this.model && this.agent !== this.#agent) {
35944
35992
  this.#switch(this.agent);
35945
35993
  }
@@ -35969,6 +36017,7 @@ var SliccComposer = class extends ThemedElement {
35969
36017
  globalThis.removeEventListener("blur", this.#release);
35970
36018
  globalThis.removeEventListener("pointerdown", this.#outside, true);
35971
36019
  clearValues(this.renderRoot);
36020
+ this.#leave();
35972
36021
  const kept3 = this.#waiting.draft?.attachments;
35973
36022
  const held2 = [
35974
36023
  this.attachments === kept3 ? [] : this.attachments,
@@ -36223,6 +36272,88 @@ ${this.value}` : text4;
36223
36272
  ];
36224
36273
  this.popup = null;
36225
36274
  }
36275
+ #clean(name) {
36276
+ return name.trim().toUpperCase().replace(/[^A-Z0-9_]/g, "_");
36277
+ }
36278
+ #exists(vault, name) {
36279
+ return !!vault?.list()?.secrets.some(
36280
+ (secret) => secret.name === name && secret.kind === "secret" && secret.state !== "removed"
36281
+ );
36282
+ }
36283
+ #attach(name) {
36284
+ this.#refuse(null);
36285
+ this.attachments = [
36286
+ ...this.attachments,
36287
+ { id: this.#id(), name, kind: "secret", mimeType: "application/octet-stream", size: 0 }
36288
+ ];
36289
+ this.popup = null;
36290
+ }
36291
+ #leave() {
36292
+ const entry = this.entry;
36293
+ if (!entry) return;
36294
+ this.entry = null;
36295
+ drop(entry.vault, entry.request);
36296
+ }
36297
+ async enterSecret(name, replace2 = false) {
36298
+ const vault = this.model?.vault;
36299
+ const clean = this.#clean(name);
36300
+ if (this.sharing) return;
36301
+ const bad = clean ? nameProblem(clean) : "Enter a name";
36302
+ if (bad) {
36303
+ this.#refuse(bad, "name", true);
36304
+ return;
36305
+ }
36306
+ if (!replace2 && this.#exists(vault, clean)) {
36307
+ this.#attach(clean);
36308
+ return;
36309
+ }
36310
+ const agent = this.#agent;
36311
+ const mine2 = () => this.#agent === agent && this.model?.vault === vault;
36312
+ this.sharing = true;
36313
+ try {
36314
+ if (replace2) {
36315
+ const sure = await confirm({
36316
+ title: `Replace ${clean}?`,
36317
+ body: "A secret with this name exists. Sharing replaces its value, and its placeholder stays.",
36318
+ action: "Replace",
36319
+ variant: "confirmation"
36320
+ });
36321
+ if (!sure || !mine2()) return;
36322
+ }
36323
+ const { request: request2 } = await vault.enter({
36324
+ name: clean,
36325
+ ...replace2 ? { replace: true } : {}
36326
+ });
36327
+ if (!mine2() || this.popup?.kind !== "secret") {
36328
+ drop(vault, request2);
36329
+ return;
36330
+ }
36331
+ this.#refuse(null);
36332
+ this.entry = { request: request2, name: clean, vault };
36333
+ void this.updateComplete.then(
36334
+ () => this.renderRoot.querySelector("slicc-vault-value")?.focus()
36335
+ );
36336
+ } catch (error) {
36337
+ if (mine2()) this.#refuse(failureText(error), fieldOf(error, "name"), true);
36338
+ } finally {
36339
+ this.sharing = false;
36340
+ }
36341
+ }
36342
+ #entered(detail, request2) {
36343
+ const entry = this.entry;
36344
+ if (!entry || !ours(detail, request2, entry.request)) return;
36345
+ if (detail.event === "error") {
36346
+ this.#refuse(detail.message);
36347
+ return;
36348
+ }
36349
+ if (detail.event === "escape") {
36350
+ this.closeSecret();
36351
+ return;
36352
+ }
36353
+ this.entry = null;
36354
+ if (detail.event === "done") this.#attach(entry.name);
36355
+ else this.#refuse(goneText(detail.state));
36356
+ }
36226
36357
  #refuse(text4, field3 = null, focus = false) {
36227
36358
  this.secretError = text4;
36228
36359
  this.secretField = text4 ? field3 : null;
@@ -36316,6 +36447,7 @@ ${this.value}` : text4;
36316
36447
  }
36317
36448
  closeSecret() {
36318
36449
  clearValues(this.renderRoot);
36450
+ this.#leave();
36319
36451
  this.popup = null;
36320
36452
  }
36321
36453
  openPopup(kind) {
@@ -36547,9 +36679,11 @@ ${this.value}` : text4;
36547
36679
  #popup() {
36548
36680
  const popup2 = this.popup;
36549
36681
  if (!popup2) return A;
36682
+ if (popup2.kind === "secret" && this.#framed())
36683
+ return this.#entering(this.model?.vault);
36550
36684
  if (popup2.kind === "secret") {
36551
36685
  const vault = this.model?.vault;
36552
- const valued = !!vault?.put && !blocked(vault);
36686
+ const valued = !!vault?.put && !vault.enter && !blocked(vault);
36553
36687
  const waiting3 = vault?.put ? blocked(vault) : null;
36554
36688
  if (!valued) clearValues(this.renderRoot);
36555
36689
  const submit = (event2) => {
@@ -36610,6 +36744,50 @@ ${this.value}` : text4;
36610
36744
  ) : b`<div class="empty">Nothing matches.</div>`}
36611
36745
  </div>`;
36612
36746
  }
36747
+ #framed() {
36748
+ const vault = this.model?.vault;
36749
+ return enters(vault) && (!!this.entry || !away(vault));
36750
+ }
36751
+ #entering(vault) {
36752
+ const entry = this.entry;
36753
+ const close = (event2) => {
36754
+ if (event2.key === "Escape") this.closeSecret();
36755
+ };
36756
+ const cancel = b`<swc-button size="s" variant="secondary" fill-style="outline" data-action="cancel-secret" ?disabled=${this.sharing} @click=${() => this.closeSecret()}>Cancel</swc-button>`;
36757
+ const error = this.secretError ? b`<span class="secret-error" role="alert" id="secret-error" data-error><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.secretError}</span></span>` : A;
36758
+ if (entry)
36759
+ return b`<form class="popup secret" data-vault="on" data-entry @submit=${(event2) => event2.preventDefault()} @keydown=${close}>
36760
+ <strong>Share a secret</strong>
36761
+ <span class="lead">Type the value of <code>${entry.name}</code> in the vault’s own field. The agent only sees its name.</span>
36762
+ ${valueField(vault, entry.request, valueConfig(this.ownerDocument, this.color), (detail, request2) => this.#entered(detail, request2))}
36763
+ ${error}
36764
+ <span class="buttons">
36765
+ <swc-button size="s" variant="secondary" fill-style="outline" data-action="back-secret" @click=${() => this.#leave()}>Back</swc-button>
36766
+ ${cancel}
36767
+ </span>
36768
+ </form>`;
36769
+ const name = this.#clean(this.secretName);
36770
+ const exists = !!name && this.#exists(vault, name);
36771
+ const bad = this.secretField === "name" ? "true" : A;
36772
+ const submit = (event2) => {
36773
+ event2.preventDefault();
36774
+ void this.enterSecret(this.secretName);
36775
+ };
36776
+ return b`<form class="popup secret" data-vault="on" @submit=${submit} @keydown=${close} @input=${(event2) => {
36777
+ this.secretField = null;
36778
+ this.secretName = event2.target.value;
36779
+ }}>
36780
+ <strong>Share a secret</strong>
36781
+ <span class="lead">${exists ? b`<code>${name}</code> is in the vault. Share it as it is, or replace its value first.` : "You type the value in the vault\u2019s own field next; the agent only sees its name."}</span>
36782
+ <label class="field"><span>Name</span><input name="name" placeholder="API_TOKEN" autocomplete="off" spellcheck="false" .value=${this.secretName} aria-invalid=${bad} aria-describedby=${this.secretField === "name" ? "secret-error" : A} /></label>
36783
+ ${error}
36784
+ <span class="buttons">
36785
+ <swc-button size="s" variant="accent" data-action="share-secret" ?pending=${this.sharing} @click=${(event2) => event2.currentTarget.closest("form")?.requestSubmit()}>Share</swc-button>
36786
+ ${exists ? b`<swc-button size="s" variant="secondary" fill-style="outline" data-action="replace-secret" ?disabled=${this.sharing} @click=${() => void this.enterSecret(this.secretName, true)}>Replace value</swc-button>` : A}
36787
+ ${cancel}
36788
+ </span>
36789
+ </form>`;
36790
+ }
36613
36791
  #hold() {
36614
36792
  const hold2 = this.model?.agent.hold?.();
36615
36793
  if (!hold2) return A;
@@ -42525,7 +42703,8 @@ var controlBadges = {
42525
42703
  var accessLabels = {
42526
42704
  view: "View",
42527
42705
  control: "Control",
42528
- terminal: "Terminal"
42706
+ terminal: "Terminal",
42707
+ browser: "Browser"
42529
42708
  };
42530
42709
  var accessKeys = Object.keys(accessLabels);
42531
42710
  function accessShown(access) {
@@ -42553,6 +42732,10 @@ function accessConfirm(device, key) {
42553
42732
  terminal: [
42554
42733
  `Give ${name} a root shell?`,
42555
42734
  "Terminal lets it open shells on this SLICC as root: it can run any command and read or change every file."
42735
+ ],
42736
+ browser: [
42737
+ `Let ${name} drive this browser?`,
42738
+ "It can open, read and control the tabs in this browser, as you, with your signed-in sites. This SLICC\u2019s own pages and the vault stay out of its reach."
42556
42739
  ]
42557
42740
  }[key];
42558
42741
  return { title: title2, body: `${body2} ${again}`, action: "Allow", variant: "confirmation" };
@@ -50309,6 +50492,7 @@ var SliccSecrets = class extends ThemedElement {
50309
50492
  problem: { state: true },
50310
50493
  invalid: { state: true },
50311
50494
  said: { state: true },
50495
+ entry: { state: true },
50312
50496
  acting: { state: true },
50313
50497
  importKind: { state: true },
50314
50498
  candidates: { state: true },
@@ -50339,6 +50523,7 @@ var SliccSecrets = class extends ThemedElement {
50339
50523
  this.candidates = null;
50340
50524
  this.picked = /* @__PURE__ */ new Set();
50341
50525
  this.again = false;
50526
+ this.entry = null;
50342
50527
  }
50343
50528
  static styles = [
50344
50529
  fieldStyles,
@@ -50647,6 +50832,7 @@ var SliccSecrets = class extends ThemedElement {
50647
50832
  super.disconnectedCallback();
50648
50833
  clearTimeout(this.#tick);
50649
50834
  clearValues(this.renderRoot);
50835
+ this.#leave();
50650
50836
  this.older = [];
50651
50837
  this.more = null;
50652
50838
  }
@@ -50660,6 +50846,7 @@ var SliccSecrets = class extends ThemedElement {
50660
50846
  this.loading = false;
50661
50847
  this.popup = null;
50662
50848
  this.failure = null;
50849
+ this.#leave();
50663
50850
  this.form = null;
50664
50851
  this.busy = false;
50665
50852
  this.unpinning = null;
@@ -50722,6 +50909,7 @@ var SliccSecrets = class extends ThemedElement {
50722
50909
  open(form) {
50723
50910
  if (form && this.busy) return;
50724
50911
  clearValues(this.renderRoot);
50912
+ this.#leave();
50725
50913
  this.form = form;
50726
50914
  this.problem = null;
50727
50915
  this.invalid = null;
@@ -50730,6 +50918,88 @@ var SliccSecrets = class extends ThemedElement {
50730
50918
  this.again = false;
50731
50919
  if (form?.kind === "import") this.importKind = "bundle";
50732
50920
  if (form) void this.updateComplete.then(() => this.#focus());
50921
+ const vault = this.model?.vault;
50922
+ if (form?.kind === "replace" && enters(vault))
50923
+ void this.#begin(vault, form, form.name, true);
50924
+ }
50925
+ #leave() {
50926
+ const entry = this.entry;
50927
+ if (!entry) return;
50928
+ this.entry = null;
50929
+ drop(entry.vault, entry.request);
50930
+ }
50931
+ async #begin(vault, form, name, replace2) {
50932
+ const options2 = form.kind === "replace" ? form.options : void 0;
50933
+ this.busy = true;
50934
+ try {
50935
+ const { request: request2 } = await vault.enter({
50936
+ name,
50937
+ ...options2?.kind ? { kind: options2.kind } : {},
50938
+ ...options2?.provider ? { provider: options2.provider } : {},
50939
+ ...options2?.server ? { server: options2.server } : {},
50940
+ ...replace2 ? { replace: true } : {}
50941
+ });
50942
+ if (!this.#mine(vault, form)) {
50943
+ drop(vault, request2);
50944
+ return;
50945
+ }
50946
+ this.#refuse(null);
50947
+ this.entry = { request: request2, form, vault, name };
50948
+ void this.updateComplete.then(
50949
+ () => this.renderRoot.querySelector("[data-form] slicc-vault-value")?.focus()
50950
+ );
50951
+ } catch (error) {
50952
+ if (this.#mine(vault, form)) this.#refuse(failureText(error), fieldOf(error, "name"), true);
50953
+ } finally {
50954
+ if (this.model?.vault === vault) this.busy = false;
50955
+ }
50956
+ }
50957
+ async next(vault) {
50958
+ const form = this.form;
50959
+ if (this.busy) return;
50960
+ const name = this.#field("name").value.trim();
50961
+ const bad = nameProblem(name);
50962
+ if (bad) {
50963
+ this.#refuse(bad, "name", true);
50964
+ return;
50965
+ }
50966
+ const exists = !!vault.list()?.secrets.some(
50967
+ (secret) => secret.name === name && secret.kind === "secret" && secret.state !== "removed"
50968
+ );
50969
+ if (exists) {
50970
+ this.busy = true;
50971
+ const sure = await confirm({
50972
+ title: `Replace ${name}?`,
50973
+ body: "A secret with this name exists. Saving replaces its value, and its placeholder stays.",
50974
+ action: "Replace",
50975
+ variant: "confirmation"
50976
+ });
50977
+ if (this.model?.vault === vault) this.busy = false;
50978
+ if (!sure || !this.#mine(vault, form)) return;
50979
+ }
50980
+ await this.#begin(vault, form, name, exists);
50981
+ }
50982
+ #entered(detail, request2) {
50983
+ const entry = this.entry;
50984
+ if (!entry || !ours(detail, request2, entry.request)) return;
50985
+ if (detail.event === "error") {
50986
+ this.#refuse(detail.message);
50987
+ return;
50988
+ }
50989
+ if (detail.event === "escape") {
50990
+ this.open(null);
50991
+ return;
50992
+ }
50993
+ this.entry = null;
50994
+ if (detail.event === "gone") {
50995
+ this.#refuse(goneText(detail.state));
50996
+ return;
50997
+ }
50998
+ this.form = null;
50999
+ this.said = {
51000
+ kind: "saved",
51001
+ name: (detail.state === "sealed" ? detail.name : void 0) ?? entry.name
51002
+ };
50733
51003
  }
50734
51004
  #refuse(text4, field3 = null, focus = false) {
50735
51005
  this.problem = text4;
@@ -50993,7 +51263,33 @@ var SliccSecrets = class extends ThemedElement {
50993
51263
  #locked(locked) {
50994
51264
  return locked ? b`<div class="locked"><p class="muted" data-locked>Unlock the vault first</p>${this.#button({ intent: { kind: "unlock" }, label: "Unlock", size: "s", variant: "accent" }, "unlock")}</div>` : A;
50995
51265
  }
51266
+ #entryForm(vault, form) {
51267
+ const add = form.kind === "add";
51268
+ const entry = this.entry?.form === form ? this.entry : null;
51269
+ const next = () => this.next(vault);
51270
+ const keys = (event2) => {
51271
+ if (event2.key === "Escape") {
51272
+ event2.preventDefault();
51273
+ this.open(null);
51274
+ } else if (event2.key === "Enter" && event2.target.localName === "input") {
51275
+ event2.preventDefault();
51276
+ void next();
51277
+ }
51278
+ };
51279
+ const name = form.kind === "replace" ? form.name : "";
51280
+ return b`<div class="form" data-form=${form.kind} data-entry=${entry ? "" : A} @input=${() => this.#typed()} @keydown=${keys}>
51281
+ <strong>${add ? "Add a secret" : b`Replace the value of <code>${name}</code>`}</strong>
51282
+ ${add && !entry ? b`<label class="field"><span>Name</span><input type="text" autocomplete="off" spellcheck="false" autocapitalize="characters" data-field="name" placeholder="API_TOKEN" aria-invalid=${this.#bad("name")} aria-describedby=${this.#described("name")} @input=${(event2) => this.#upper(event2)} /></label>` : A}
51283
+ ${entry ? b`<p class="muted">Type the value in the vault’s own field. It never passes through this page.</p>${valueField(vault, entry.request, valueConfig(this.ownerDocument, this.color), (detail, request2) => this.#entered(detail, request2))}` : add ? b`<p class="muted">You type the value in the vault’s own field next.</p>` : b`<p class="muted" data-opening>${this.busy ? "Opening the vault\u2019s field\u2026" : ""}</p>`}
51284
+ ${this.problem ? this.#error(this.problem, "form") : A}
51285
+ <div class="buttons">
51286
+ ${add && !entry ? b`<swc-button size="m" variant="accent" data-action="enter-value" ?pending=${this.busy} @click=${next}>Next</swc-button>` : A}
51287
+ <swc-button size="m" variant="secondary" fill-style="outline" data-action="cancel" @click=${() => this.open(null)}>Cancel</swc-button>
51288
+ </div>
51289
+ </div>`;
51290
+ }
50996
51291
  #valueForm(vault, form, locked) {
51292
+ if (enters(vault)) return this.#entryForm(vault, form);
50997
51293
  const save = () => this.save(vault);
50998
51294
  const add = form.kind === "add";
50999
51295
  return b`<div class="form" data-form=${form.kind} @input=${() => this.#typed()} @keydown=${(event2) => this.#enter(event2, save, locked)}>
@@ -51911,12 +52207,7 @@ var SliccApprovals = class extends ModelElement {
51911
52207
  #config() {
51912
52208
  const doc = this.ownerDocument;
51913
52209
  const dark = doc.defaultView?.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
51914
- const lang = doc.documentElement.lang;
51915
- return {
51916
- theme: resolveColor(this.model?.settings?.get().color ?? "system", dark),
51917
- dir: doc.documentElement.dir === "rtl" ? "rtl" : "ltr",
51918
- ...lang ? { lang } : {}
51919
- };
52210
+ return valueConfig(doc, resolveColor(this.model?.settings?.get().color ?? "system", dark));
51920
52211
  }
51921
52212
  #frame(approval) {
51922
52213
  const vault = this.model?.vault;
@@ -54933,6 +55224,7 @@ var SliccSettings = class extends ThemedElement {
54933
55224
  ...ThemedElement.properties,
54934
55225
  pending: { state: true },
54935
55226
  entering: { state: true },
55227
+ keyEntry: { state: true },
54936
55228
  failure: { state: true },
54937
55229
  problem: { state: true },
54938
55230
  blocked: { state: true },
@@ -54949,6 +55241,8 @@ var SliccSettings = class extends ThemedElement {
54949
55241
  refusals: { state: true },
54950
55242
  removing: { state: true }
54951
55243
  };
55244
+ #auto = null;
55245
+ #wants = null;
54952
55246
  #waits = new SignInWaits(() => this.requestUpdate());
54953
55247
  #copies = new SignInCopies(() => this.requestUpdate());
54954
55248
  #modelFilter = new ModelFilter(this);
@@ -54964,6 +55258,7 @@ var SliccSettings = class extends ThemedElement {
54964
55258
  super();
54965
55259
  this.pending = /* @__PURE__ */ new Set();
54966
55260
  this.entering = null;
55261
+ this.keyEntry = null;
54967
55262
  this.failure = null;
54968
55263
  this.problem = null;
54969
55264
  this.blocked = false;
@@ -55077,6 +55372,17 @@ var SliccSettings = class extends ThemedElement {
55077
55372
  .field sp-picker {
55078
55373
  width: 100%;
55079
55374
  }
55375
+ .entry-fields {
55376
+ display: grid;
55377
+ gap: var(--swc-spacing-200);
55378
+ }
55379
+ .entry-fields[hidden] {
55380
+ display: none;
55381
+ }
55382
+ .entry-label {
55383
+ font-size: var(--swc-font-size-75);
55384
+ color: var(--swc-neutral-subdued-content-color-default);
55385
+ }
55080
55386
  .field sp-field-label {
55081
55387
  margin: 0;
55082
55388
  padding-block: 0;
@@ -55493,7 +55799,7 @@ var SliccSettings = class extends ThemedElement {
55493
55799
  #vault(account) {
55494
55800
  const vault = this.model?.vault;
55495
55801
  const keyed2 = account.auth === "api-key" || dual(account);
55496
- return keyed2 && account.store === "vault" && vault?.put ? vault : null;
55802
+ return keyed2 && account.store === "vault" && vault && (vault.put || enters(vault)) ? vault : null;
55497
55803
  }
55498
55804
  async keep(account, value, fields) {
55499
55805
  const model = this.model;
@@ -55515,15 +55821,7 @@ var SliccSettings = class extends ThemedElement {
55515
55821
  try {
55516
55822
  if (keyed2) await vault.put?.(recordName(account), value, void 0, options2);
55517
55823
  else if (settings) await vault.settings?.(recordName(account), "model", settings);
55518
- if (url) {
55519
- const server = serverOf(account);
55520
- const old = registrations2()[server];
55521
- await vault.endpoints?.({
55522
- ...old && old !== url ? { remove: { mcp: [{ server, url: old }] } } : {},
55523
- add: { mcp: [{ server, url }] }
55524
- });
55525
- register(server, url);
55526
- }
55824
+ if (url) await this.#endpoint(account, vault, url);
55527
55825
  } catch (error) {
55528
55826
  this.#busy(id, false);
55529
55827
  if (this.model === model) this.#refused(account, failureText(error), keyed2);
@@ -55539,6 +55837,114 @@ var SliccSettings = class extends ThemedElement {
55539
55837
  await vault.settings?.(recordName(account), "model", before ?? {}).catch(() => {
55540
55838
  });
55541
55839
  }
55840
+ async #endpoint(account, vault, url) {
55841
+ const server = serverOf(account);
55842
+ const old = registrations2()[server];
55843
+ await vault.endpoints?.({
55844
+ ...old && old !== url ? { remove: { mcp: [{ server, url: old }] } } : {},
55845
+ add: { mcp: [{ server, url }] }
55846
+ });
55847
+ register(server, url);
55848
+ }
55849
+ #held(account) {
55850
+ const name = recordName(account);
55851
+ const kind = recordKind(account);
55852
+ return !!this.model?.vault?.list()?.secrets.some(
55853
+ (secret) => secret.name === name && secret.kind === kind && secret.state !== "removed"
55854
+ );
55855
+ }
55856
+ async #startKey(account, fields) {
55857
+ const model = this.model;
55858
+ const vault = this.#vault(account);
55859
+ const { id } = account;
55860
+ const name = recordName(account);
55861
+ const settings = this.#settingsOf(account, fields);
55862
+ const options2 = account.group === "mcp" ? { name, kind: "mcp", server: serverOf(account) } : { name, kind: "model", provider: account.id, ...settings ? { settings } : {} };
55863
+ if (this.#held(account)) options2.replace = true;
55864
+ this.#busy(id, true);
55865
+ this.failure = null;
55866
+ this.problem = null;
55867
+ try {
55868
+ const { request: request2 } = await vault.enter(options2);
55869
+ if (this.model !== model || this.entering !== id) {
55870
+ drop(vault, request2);
55871
+ return;
55872
+ }
55873
+ this.keyEntry = { id, request: request2, fields, vault };
55874
+ void this.updateComplete.then(
55875
+ () => this.renderRoot.querySelector("[data-entry] slicc-vault-value")?.focus()
55876
+ );
55877
+ } catch (error) {
55878
+ if (this.model === model) this.#refused(account, failureText(error), true);
55879
+ } finally {
55880
+ this.#busy(id, false);
55881
+ }
55882
+ }
55883
+ async #nextKey(event2, account) {
55884
+ event2.preventDefault();
55885
+ if (account.blocked || this.pending.has(account.id)) return;
55886
+ const form = event2.currentTarget.closest("form");
55887
+ const fields = this.#fields(form, account);
55888
+ if (fields === null) return;
55889
+ const url = this.#mcpUrl(account);
55890
+ const model = this.model;
55891
+ if (url && !await this.#confirmUrl(url)) return;
55892
+ if (this.model !== model || this.entering !== account.id) return;
55893
+ if (away(this.#vault(account))) return;
55894
+ await this.#startKey(account, fields);
55895
+ }
55896
+ async #keyed(account, detail, request2) {
55897
+ const entry = this.keyEntry;
55898
+ if (!entry || entry.id !== account.id || !ours(detail, request2, entry.request)) return;
55899
+ const { id } = account;
55900
+ if (detail.event === "error") {
55901
+ this.#refuse(id, "value", detail.message);
55902
+ return;
55903
+ }
55904
+ if (detail.event === "escape") {
55905
+ this.#leaveKey(account);
55906
+ return;
55907
+ }
55908
+ this.keyEntry = null;
55909
+ if (detail.event === "gone") {
55910
+ this.#refuse(id, "value", goneText(detail.state));
55911
+ return;
55912
+ }
55913
+ const model = this.model;
55914
+ const vault = this.#vault(account);
55915
+ const url = this.#mcpUrl(account);
55916
+ this.#busy(id, true);
55917
+ if (url) {
55918
+ try {
55919
+ await this.#endpoint(account, vault, url);
55920
+ } catch (error) {
55921
+ this.#busy(id, false);
55922
+ if (this.model === model) this.#refused(account, failureText(error), false);
55923
+ return;
55924
+ }
55925
+ }
55926
+ if (this.model !== model || this.entering !== id) {
55927
+ this.#busy(id, false);
55928
+ return;
55929
+ }
55930
+ await this.#connectBases(account, void 0, entry.fields, vault);
55931
+ }
55932
+ #leaveKey(account) {
55933
+ this.#dropKey();
55934
+ if (this.#auto === account.id) {
55935
+ if (this.adding?.id === account.id) this.#back();
55936
+ else this.#cancel();
55937
+ }
55938
+ }
55939
+ #unmounted() {
55940
+ if (this.keyEntry && !this.renderRoot.querySelector("form.key[data-entry]")) this.#dropKey();
55941
+ }
55942
+ #dropKey() {
55943
+ const entry = this.keyEntry;
55944
+ if (!entry) return;
55945
+ this.keyEntry = null;
55946
+ drop(entry.vault, entry.request);
55947
+ }
55542
55948
  #settingsOf(account, fields) {
55543
55949
  const settable = (account.fields ?? []).filter((field3) => field3.vaultSetting);
55544
55950
  if (account.group === "mcp" || !settable.length) return void 0;
@@ -55919,7 +56325,40 @@ var SliccSettings = class extends ThemedElement {
55919
56325
  </div>
55920
56326
  ${this.blocked ? b`<div class="problem" role="alert" data-popup="blocked"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>The browser blocked the vault window. Allow pop-ups for this site, then try again.</span></div>` : A}`;
55921
56327
  }
56328
+ #entryKept(account, vault) {
56329
+ const { id } = account;
56330
+ const busy = this.pending.has(id);
56331
+ const name = secretNames[account.secret ?? "api-key"];
56332
+ const entry = this.keyEntry?.id === id ? this.keyEntry : null;
56333
+ const fields = account.fields ?? [];
56334
+ const waiting3 = away(vault);
56335
+ if (!entry && !waiting3 && !fields.length && !this.#mcpUrl(account) && this.#auto !== id && !busy)
56336
+ this.#wants = account;
56337
+ const required = fields.filter((field3) => field3.required && !field3.options);
56338
+ const ready = !waiting3 && !account.blocked && required.every((field3) => !this.#offered() || this.filled.has(field3.name));
56339
+ const optional = this.#keyOptional(account);
56340
+ const hint2 = entry ? "Type it in the vault\u2019s own field. It never passes through this page." : waiting3 ? "Waiting for the vault" : busy ? "Opening the vault\u2019s field\u2026" : `You type the ${name} in the vault\u2019s own field next.${optional ? " Or save these settings and keep the key you have." : ""}`;
56341
+ return b`<form class="key stacked" data-id=${id} data-store="vault" data-entry=${entry ? "" : A} @submit=${(event2) => void this.#nextKey(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)} @input=${(event2) => this.#fill(event2)}>
56342
+ <span class="entry-label">${account.provider} ${name}</span>
56343
+ ${entry ? valueField(vault, entry.request, valueConfig(this.ownerDocument, this.color), (detail, request2) => void this.#keyed(account, detail, request2)) : A}
56344
+ ${this.#problem(account, "value")}
56345
+ <p class="hint">${hint2}</p>
56346
+ ${this.#getKey(account)}
56347
+ <div class="entry-fields" ?hidden=${!!entry}>${this.#extra(account)}</div>
56348
+ <div class="buttons">
56349
+ ${entry ? b`<swc-button size="m" variant="secondary" data-action="back-key" @click=${() => this.#leaveKey(account)}>Back</swc-button>` : b`<swc-button size="m" variant="accent" data-action="enter-key" ?pending=${busy} ?disabled=${!busy && !ready} @click=${(event2) => void this.#nextKey(event2, account)}>Next</swc-button>
56350
+ ${optional ? b`<swc-button size="m" variant="secondary" data-action="save" ?disabled=${busy || !ready} @click=${(event2) => this.#keepSettings(event2, account)}>Save settings</swc-button>` : A}
56351
+ ${this.#leave(account)}`}
56352
+ </div>
56353
+ </form>`;
56354
+ }
56355
+ #keepSettings(event2, account) {
56356
+ const form = event2.currentTarget.closest("form");
56357
+ const fields = this.#fields(form, account);
56358
+ if (fields !== null && !this.pending.has(account.id)) void this.keep(account, "", fields);
56359
+ }
55922
56360
  #kept(account, vault) {
56361
+ if (enters(vault)) return this.#entryKept(account, vault);
55923
56362
  const busy = this.pending.has(account.id);
55924
56363
  const locked = !!vault.status()?.locked;
55925
56364
  const name = secretNames[account.secret ?? "api-key"];
@@ -56431,8 +56870,25 @@ var SliccSettings = class extends ThemedElement {
56431
56870
  this.adding = null;
56432
56871
  this.picking = true;
56433
56872
  }
56873
+ disconnectedCallback() {
56874
+ super.disconnectedCallback();
56875
+ this.#dropKey();
56876
+ }
56877
+ willUpdate(changed2) {
56878
+ super.willUpdate(changed2);
56879
+ const entry = this.keyEntry;
56880
+ if (entry && (this.entering !== entry.id || entry.vault !== this.model?.vault)) this.#dropKey();
56881
+ if (this.entering === null) this.#auto = null;
56882
+ }
56434
56883
  updated(changed2) {
56435
56884
  super.updated(changed2);
56885
+ if (this.keyEntry) void this.updateComplete.then(() => this.#unmounted());
56886
+ const wants = this.#wants;
56887
+ this.#wants = null;
56888
+ if (wants && this.#auto !== wants.id) {
56889
+ this.#auto = wants.id;
56890
+ void this.#startKey(wants, void 0);
56891
+ }
56436
56892
  const dialog = this.#dialog;
56437
56893
  if (!changed2.has("picking") || !dialog || dialog.open) return;
56438
56894
  dialog.showModal();
@@ -65145,12 +65601,12 @@ var TabReorderController = class extends CompositeDisposable {
65145
65601
  commitPointerReorder(event2) {
65146
65602
  const animState = this._animState;
65147
65603
  if ((animState === null || animState === void 0 ? void 0 : animState.currentInsertionIndex) == null) return;
65148
- const drop = this.resolveTabDrop(animState);
65604
+ const drop2 = this.resolveTabDrop(animState);
65149
65605
  this._animState = null;
65150
65606
  this.clearWrapDropIndicator();
65151
65607
  this.uncollapseSourceTab(animState.sourceTabId);
65152
- if (!drop) return;
65153
- this.host.fireDrop(_objectSpread2({ event: event2 }, drop));
65608
+ if (!drop2) return;
65609
+ this.host.fireDrop(_objectSpread2({ event: event2 }, drop2));
65154
65610
  }
65155
65611
  /**
65156
65612
  * Shared body of the dragover entry point. Refreshes stale anim state
@@ -66194,19 +66650,19 @@ var Tabs = class extends CompositeDisposable {
66194
66650
  this._commitGroupMove(animState.sourceTabGroupId, animState.currentInsertionIndex);
66195
66651
  return;
66196
66652
  }
66197
- const drop = this._reorder.resolveTabDrop(animState);
66653
+ const drop2 = this._reorder.resolveTabDrop(animState);
66198
66654
  this._reorder.uncollapseSourceTab(animState.sourceTabId);
66199
- if (!drop) {
66655
+ if (!drop2) {
66200
66656
  this.resetTabTransforms();
66201
66657
  return;
66202
66658
  }
66203
66659
  const sourceIndex = animState.sourceIndex;
66204
66660
  const firstPositions = this.snapshotTabPositions();
66205
66661
  this.resetTabTransforms();
66206
- this._onDrop.fire(_objectSpread2({ event: event2 }, drop));
66662
+ this._onDrop.fire(_objectSpread2({ event: event2 }, drop2));
66207
66663
  this.runFlipAnimation(firstPositions, animState.sourceTabId, sourceIndex === -1, {
66208
- from: Math.min(sourceIndex, drop.index),
66209
- to: Math.max(sourceIndex, drop.index)
66664
+ from: Math.min(sourceIndex, drop2.index),
66665
+ to: Math.max(sourceIndex, drop2.index)
66210
66666
  });
66211
66667
  }, true), Disposable.from(() => {
66212
66668
  var _this$_voidContainerL2;
@@ -85300,6 +85756,7 @@ function defineFileTree() {
85300
85756
  }
85301
85757
 
85302
85758
  // src/ui.ts
85759
+ var valueEntry = true;
85303
85760
  var elements = {
85304
85761
  "slicc-agents": SliccAgents,
85305
85762
  "slicc-chat": SliccChat,
@@ -85381,7 +85838,8 @@ export {
85381
85838
  panelCommand,
85382
85839
  prompt,
85383
85840
  resolveColor,
85384
- surfaces2 as surfaces
85841
+ surfaces2 as surfaces,
85842
+ valueEntry
85385
85843
  };
85386
85844
  /*! Bundled license information:
85387
85845