@ai-ecoverse/slicc-spectrum 2.4.1 → 2.5.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;
@@ -50309,6 +50487,7 @@ var SliccSecrets = class extends ThemedElement {
50309
50487
  problem: { state: true },
50310
50488
  invalid: { state: true },
50311
50489
  said: { state: true },
50490
+ entry: { state: true },
50312
50491
  acting: { state: true },
50313
50492
  importKind: { state: true },
50314
50493
  candidates: { state: true },
@@ -50339,6 +50518,7 @@ var SliccSecrets = class extends ThemedElement {
50339
50518
  this.candidates = null;
50340
50519
  this.picked = /* @__PURE__ */ new Set();
50341
50520
  this.again = false;
50521
+ this.entry = null;
50342
50522
  }
50343
50523
  static styles = [
50344
50524
  fieldStyles,
@@ -50647,6 +50827,7 @@ var SliccSecrets = class extends ThemedElement {
50647
50827
  super.disconnectedCallback();
50648
50828
  clearTimeout(this.#tick);
50649
50829
  clearValues(this.renderRoot);
50830
+ this.#leave();
50650
50831
  this.older = [];
50651
50832
  this.more = null;
50652
50833
  }
@@ -50660,6 +50841,7 @@ var SliccSecrets = class extends ThemedElement {
50660
50841
  this.loading = false;
50661
50842
  this.popup = null;
50662
50843
  this.failure = null;
50844
+ this.#leave();
50663
50845
  this.form = null;
50664
50846
  this.busy = false;
50665
50847
  this.unpinning = null;
@@ -50722,6 +50904,7 @@ var SliccSecrets = class extends ThemedElement {
50722
50904
  open(form) {
50723
50905
  if (form && this.busy) return;
50724
50906
  clearValues(this.renderRoot);
50907
+ this.#leave();
50725
50908
  this.form = form;
50726
50909
  this.problem = null;
50727
50910
  this.invalid = null;
@@ -50730,6 +50913,88 @@ var SliccSecrets = class extends ThemedElement {
50730
50913
  this.again = false;
50731
50914
  if (form?.kind === "import") this.importKind = "bundle";
50732
50915
  if (form) void this.updateComplete.then(() => this.#focus());
50916
+ const vault = this.model?.vault;
50917
+ if (form?.kind === "replace" && enters(vault))
50918
+ void this.#begin(vault, form, form.name, true);
50919
+ }
50920
+ #leave() {
50921
+ const entry = this.entry;
50922
+ if (!entry) return;
50923
+ this.entry = null;
50924
+ drop(entry.vault, entry.request);
50925
+ }
50926
+ async #begin(vault, form, name, replace2) {
50927
+ const options2 = form.kind === "replace" ? form.options : void 0;
50928
+ this.busy = true;
50929
+ try {
50930
+ const { request: request2 } = await vault.enter({
50931
+ name,
50932
+ ...options2?.kind ? { kind: options2.kind } : {},
50933
+ ...options2?.provider ? { provider: options2.provider } : {},
50934
+ ...options2?.server ? { server: options2.server } : {},
50935
+ ...replace2 ? { replace: true } : {}
50936
+ });
50937
+ if (!this.#mine(vault, form)) {
50938
+ drop(vault, request2);
50939
+ return;
50940
+ }
50941
+ this.#refuse(null);
50942
+ this.entry = { request: request2, form, vault, name };
50943
+ void this.updateComplete.then(
50944
+ () => this.renderRoot.querySelector("[data-form] slicc-vault-value")?.focus()
50945
+ );
50946
+ } catch (error) {
50947
+ if (this.#mine(vault, form)) this.#refuse(failureText(error), fieldOf(error, "name"), true);
50948
+ } finally {
50949
+ if (this.model?.vault === vault) this.busy = false;
50950
+ }
50951
+ }
50952
+ async next(vault) {
50953
+ const form = this.form;
50954
+ if (this.busy) return;
50955
+ const name = this.#field("name").value.trim();
50956
+ const bad = nameProblem(name);
50957
+ if (bad) {
50958
+ this.#refuse(bad, "name", true);
50959
+ return;
50960
+ }
50961
+ const exists = !!vault.list()?.secrets.some(
50962
+ (secret) => secret.name === name && secret.kind === "secret" && secret.state !== "removed"
50963
+ );
50964
+ if (exists) {
50965
+ this.busy = true;
50966
+ const sure = await confirm({
50967
+ title: `Replace ${name}?`,
50968
+ body: "A secret with this name exists. Saving replaces its value, and its placeholder stays.",
50969
+ action: "Replace",
50970
+ variant: "confirmation"
50971
+ });
50972
+ if (this.model?.vault === vault) this.busy = false;
50973
+ if (!sure || !this.#mine(vault, form)) return;
50974
+ }
50975
+ await this.#begin(vault, form, name, exists);
50976
+ }
50977
+ #entered(detail, request2) {
50978
+ const entry = this.entry;
50979
+ if (!entry || !ours(detail, request2, entry.request)) return;
50980
+ if (detail.event === "error") {
50981
+ this.#refuse(detail.message);
50982
+ return;
50983
+ }
50984
+ if (detail.event === "escape") {
50985
+ this.open(null);
50986
+ return;
50987
+ }
50988
+ this.entry = null;
50989
+ if (detail.event === "gone") {
50990
+ this.#refuse(goneText(detail.state));
50991
+ return;
50992
+ }
50993
+ this.form = null;
50994
+ this.said = {
50995
+ kind: "saved",
50996
+ name: (detail.state === "sealed" ? detail.name : void 0) ?? entry.name
50997
+ };
50733
50998
  }
50734
50999
  #refuse(text4, field3 = null, focus = false) {
50735
51000
  this.problem = text4;
@@ -50993,7 +51258,33 @@ var SliccSecrets = class extends ThemedElement {
50993
51258
  #locked(locked) {
50994
51259
  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
51260
  }
51261
+ #entryForm(vault, form) {
51262
+ const add = form.kind === "add";
51263
+ const entry = this.entry?.form === form ? this.entry : null;
51264
+ const next = () => this.next(vault);
51265
+ const keys = (event2) => {
51266
+ if (event2.key === "Escape") {
51267
+ event2.preventDefault();
51268
+ this.open(null);
51269
+ } else if (event2.key === "Enter" && event2.target.localName === "input") {
51270
+ event2.preventDefault();
51271
+ void next();
51272
+ }
51273
+ };
51274
+ const name = form.kind === "replace" ? form.name : "";
51275
+ return b`<div class="form" data-form=${form.kind} data-entry=${entry ? "" : A} @input=${() => this.#typed()} @keydown=${keys}>
51276
+ <strong>${add ? "Add a secret" : b`Replace the value of <code>${name}</code>`}</strong>
51277
+ ${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}
51278
+ ${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>`}
51279
+ ${this.problem ? this.#error(this.problem, "form") : A}
51280
+ <div class="buttons">
51281
+ ${add && !entry ? b`<swc-button size="m" variant="accent" data-action="enter-value" ?pending=${this.busy} @click=${next}>Next</swc-button>` : A}
51282
+ <swc-button size="m" variant="secondary" fill-style="outline" data-action="cancel" @click=${() => this.open(null)}>Cancel</swc-button>
51283
+ </div>
51284
+ </div>`;
51285
+ }
50996
51286
  #valueForm(vault, form, locked) {
51287
+ if (enters(vault)) return this.#entryForm(vault, form);
50997
51288
  const save = () => this.save(vault);
50998
51289
  const add = form.kind === "add";
50999
51290
  return b`<div class="form" data-form=${form.kind} @input=${() => this.#typed()} @keydown=${(event2) => this.#enter(event2, save, locked)}>
@@ -51911,12 +52202,7 @@ var SliccApprovals = class extends ModelElement {
51911
52202
  #config() {
51912
52203
  const doc = this.ownerDocument;
51913
52204
  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
- };
52205
+ return valueConfig(doc, resolveColor(this.model?.settings?.get().color ?? "system", dark));
51920
52206
  }
51921
52207
  #frame(approval) {
51922
52208
  const vault = this.model?.vault;
@@ -54933,6 +55219,7 @@ var SliccSettings = class extends ThemedElement {
54933
55219
  ...ThemedElement.properties,
54934
55220
  pending: { state: true },
54935
55221
  entering: { state: true },
55222
+ keyEntry: { state: true },
54936
55223
  failure: { state: true },
54937
55224
  problem: { state: true },
54938
55225
  blocked: { state: true },
@@ -54949,6 +55236,8 @@ var SliccSettings = class extends ThemedElement {
54949
55236
  refusals: { state: true },
54950
55237
  removing: { state: true }
54951
55238
  };
55239
+ #auto = null;
55240
+ #wants = null;
54952
55241
  #waits = new SignInWaits(() => this.requestUpdate());
54953
55242
  #copies = new SignInCopies(() => this.requestUpdate());
54954
55243
  #modelFilter = new ModelFilter(this);
@@ -54964,6 +55253,7 @@ var SliccSettings = class extends ThemedElement {
54964
55253
  super();
54965
55254
  this.pending = /* @__PURE__ */ new Set();
54966
55255
  this.entering = null;
55256
+ this.keyEntry = null;
54967
55257
  this.failure = null;
54968
55258
  this.problem = null;
54969
55259
  this.blocked = false;
@@ -55077,6 +55367,17 @@ var SliccSettings = class extends ThemedElement {
55077
55367
  .field sp-picker {
55078
55368
  width: 100%;
55079
55369
  }
55370
+ .entry-fields {
55371
+ display: grid;
55372
+ gap: var(--swc-spacing-200);
55373
+ }
55374
+ .entry-fields[hidden] {
55375
+ display: none;
55376
+ }
55377
+ .entry-label {
55378
+ font-size: var(--swc-font-size-75);
55379
+ color: var(--swc-neutral-subdued-content-color-default);
55380
+ }
55080
55381
  .field sp-field-label {
55081
55382
  margin: 0;
55082
55383
  padding-block: 0;
@@ -55493,7 +55794,7 @@ var SliccSettings = class extends ThemedElement {
55493
55794
  #vault(account) {
55494
55795
  const vault = this.model?.vault;
55495
55796
  const keyed2 = account.auth === "api-key" || dual(account);
55496
- return keyed2 && account.store === "vault" && vault?.put ? vault : null;
55797
+ return keyed2 && account.store === "vault" && vault && (vault.put || enters(vault)) ? vault : null;
55497
55798
  }
55498
55799
  async keep(account, value, fields) {
55499
55800
  const model = this.model;
@@ -55515,15 +55816,7 @@ var SliccSettings = class extends ThemedElement {
55515
55816
  try {
55516
55817
  if (keyed2) await vault.put?.(recordName(account), value, void 0, options2);
55517
55818
  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
- }
55819
+ if (url) await this.#endpoint(account, vault, url);
55527
55820
  } catch (error) {
55528
55821
  this.#busy(id, false);
55529
55822
  if (this.model === model) this.#refused(account, failureText(error), keyed2);
@@ -55539,6 +55832,114 @@ var SliccSettings = class extends ThemedElement {
55539
55832
  await vault.settings?.(recordName(account), "model", before ?? {}).catch(() => {
55540
55833
  });
55541
55834
  }
55835
+ async #endpoint(account, vault, url) {
55836
+ const server = serverOf(account);
55837
+ const old = registrations2()[server];
55838
+ await vault.endpoints?.({
55839
+ ...old && old !== url ? { remove: { mcp: [{ server, url: old }] } } : {},
55840
+ add: { mcp: [{ server, url }] }
55841
+ });
55842
+ register(server, url);
55843
+ }
55844
+ #held(account) {
55845
+ const name = recordName(account);
55846
+ const kind = recordKind(account);
55847
+ return !!this.model?.vault?.list()?.secrets.some(
55848
+ (secret) => secret.name === name && secret.kind === kind && secret.state !== "removed"
55849
+ );
55850
+ }
55851
+ async #startKey(account, fields) {
55852
+ const model = this.model;
55853
+ const vault = this.#vault(account);
55854
+ const { id } = account;
55855
+ const name = recordName(account);
55856
+ const settings = this.#settingsOf(account, fields);
55857
+ const options2 = account.group === "mcp" ? { name, kind: "mcp", server: serverOf(account) } : { name, kind: "model", provider: account.id, ...settings ? { settings } : {} };
55858
+ if (this.#held(account)) options2.replace = true;
55859
+ this.#busy(id, true);
55860
+ this.failure = null;
55861
+ this.problem = null;
55862
+ try {
55863
+ const { request: request2 } = await vault.enter(options2);
55864
+ if (this.model !== model || this.entering !== id) {
55865
+ drop(vault, request2);
55866
+ return;
55867
+ }
55868
+ this.keyEntry = { id, request: request2, fields, vault };
55869
+ void this.updateComplete.then(
55870
+ () => this.renderRoot.querySelector("[data-entry] slicc-vault-value")?.focus()
55871
+ );
55872
+ } catch (error) {
55873
+ if (this.model === model) this.#refused(account, failureText(error), true);
55874
+ } finally {
55875
+ this.#busy(id, false);
55876
+ }
55877
+ }
55878
+ async #nextKey(event2, account) {
55879
+ event2.preventDefault();
55880
+ if (account.blocked || this.pending.has(account.id)) return;
55881
+ const form = event2.currentTarget.closest("form");
55882
+ const fields = this.#fields(form, account);
55883
+ if (fields === null) return;
55884
+ const url = this.#mcpUrl(account);
55885
+ const model = this.model;
55886
+ if (url && !await this.#confirmUrl(url)) return;
55887
+ if (this.model !== model || this.entering !== account.id) return;
55888
+ if (away(this.#vault(account))) return;
55889
+ await this.#startKey(account, fields);
55890
+ }
55891
+ async #keyed(account, detail, request2) {
55892
+ const entry = this.keyEntry;
55893
+ if (!entry || entry.id !== account.id || !ours(detail, request2, entry.request)) return;
55894
+ const { id } = account;
55895
+ if (detail.event === "error") {
55896
+ this.#refuse(id, "value", detail.message);
55897
+ return;
55898
+ }
55899
+ if (detail.event === "escape") {
55900
+ this.#leaveKey(account);
55901
+ return;
55902
+ }
55903
+ this.keyEntry = null;
55904
+ if (detail.event === "gone") {
55905
+ this.#refuse(id, "value", goneText(detail.state));
55906
+ return;
55907
+ }
55908
+ const model = this.model;
55909
+ const vault = this.#vault(account);
55910
+ const url = this.#mcpUrl(account);
55911
+ this.#busy(id, true);
55912
+ if (url) {
55913
+ try {
55914
+ await this.#endpoint(account, vault, url);
55915
+ } catch (error) {
55916
+ this.#busy(id, false);
55917
+ if (this.model === model) this.#refused(account, failureText(error), false);
55918
+ return;
55919
+ }
55920
+ }
55921
+ if (this.model !== model || this.entering !== id) {
55922
+ this.#busy(id, false);
55923
+ return;
55924
+ }
55925
+ await this.#connectBases(account, void 0, entry.fields, vault);
55926
+ }
55927
+ #leaveKey(account) {
55928
+ this.#dropKey();
55929
+ if (this.#auto === account.id) {
55930
+ if (this.adding?.id === account.id) this.#back();
55931
+ else this.#cancel();
55932
+ }
55933
+ }
55934
+ #unmounted() {
55935
+ if (this.keyEntry && !this.renderRoot.querySelector("form.key[data-entry]")) this.#dropKey();
55936
+ }
55937
+ #dropKey() {
55938
+ const entry = this.keyEntry;
55939
+ if (!entry) return;
55940
+ this.keyEntry = null;
55941
+ drop(entry.vault, entry.request);
55942
+ }
55542
55943
  #settingsOf(account, fields) {
55543
55944
  const settable = (account.fields ?? []).filter((field3) => field3.vaultSetting);
55544
55945
  if (account.group === "mcp" || !settable.length) return void 0;
@@ -55919,7 +56320,40 @@ var SliccSettings = class extends ThemedElement {
55919
56320
  </div>
55920
56321
  ${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
56322
  }
56323
+ #entryKept(account, vault) {
56324
+ const { id } = account;
56325
+ const busy = this.pending.has(id);
56326
+ const name = secretNames[account.secret ?? "api-key"];
56327
+ const entry = this.keyEntry?.id === id ? this.keyEntry : null;
56328
+ const fields = account.fields ?? [];
56329
+ const waiting3 = away(vault);
56330
+ if (!entry && !waiting3 && !fields.length && !this.#mcpUrl(account) && this.#auto !== id && !busy)
56331
+ this.#wants = account;
56332
+ const required = fields.filter((field3) => field3.required && !field3.options);
56333
+ const ready = !waiting3 && !account.blocked && required.every((field3) => !this.#offered() || this.filled.has(field3.name));
56334
+ const optional = this.#keyOptional(account);
56335
+ 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." : ""}`;
56336
+ 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)}>
56337
+ <span class="entry-label">${account.provider} ${name}</span>
56338
+ ${entry ? valueField(vault, entry.request, valueConfig(this.ownerDocument, this.color), (detail, request2) => void this.#keyed(account, detail, request2)) : A}
56339
+ ${this.#problem(account, "value")}
56340
+ <p class="hint">${hint2}</p>
56341
+ ${this.#getKey(account)}
56342
+ <div class="entry-fields" ?hidden=${!!entry}>${this.#extra(account)}</div>
56343
+ <div class="buttons">
56344
+ ${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>
56345
+ ${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}
56346
+ ${this.#leave(account)}`}
56347
+ </div>
56348
+ </form>`;
56349
+ }
56350
+ #keepSettings(event2, account) {
56351
+ const form = event2.currentTarget.closest("form");
56352
+ const fields = this.#fields(form, account);
56353
+ if (fields !== null && !this.pending.has(account.id)) void this.keep(account, "", fields);
56354
+ }
55922
56355
  #kept(account, vault) {
56356
+ if (enters(vault)) return this.#entryKept(account, vault);
55923
56357
  const busy = this.pending.has(account.id);
55924
56358
  const locked = !!vault.status()?.locked;
55925
56359
  const name = secretNames[account.secret ?? "api-key"];
@@ -56431,8 +56865,25 @@ var SliccSettings = class extends ThemedElement {
56431
56865
  this.adding = null;
56432
56866
  this.picking = true;
56433
56867
  }
56868
+ disconnectedCallback() {
56869
+ super.disconnectedCallback();
56870
+ this.#dropKey();
56871
+ }
56872
+ willUpdate(changed2) {
56873
+ super.willUpdate(changed2);
56874
+ const entry = this.keyEntry;
56875
+ if (entry && (this.entering !== entry.id || entry.vault !== this.model?.vault)) this.#dropKey();
56876
+ if (this.entering === null) this.#auto = null;
56877
+ }
56434
56878
  updated(changed2) {
56435
56879
  super.updated(changed2);
56880
+ if (this.keyEntry) void this.updateComplete.then(() => this.#unmounted());
56881
+ const wants = this.#wants;
56882
+ this.#wants = null;
56883
+ if (wants && this.#auto !== wants.id) {
56884
+ this.#auto = wants.id;
56885
+ void this.#startKey(wants, void 0);
56886
+ }
56436
56887
  const dialog = this.#dialog;
56437
56888
  if (!changed2.has("picking") || !dialog || dialog.open) return;
56438
56889
  dialog.showModal();
@@ -65145,12 +65596,12 @@ var TabReorderController = class extends CompositeDisposable {
65145
65596
  commitPointerReorder(event2) {
65146
65597
  const animState = this._animState;
65147
65598
  if ((animState === null || animState === void 0 ? void 0 : animState.currentInsertionIndex) == null) return;
65148
- const drop = this.resolveTabDrop(animState);
65599
+ const drop2 = this.resolveTabDrop(animState);
65149
65600
  this._animState = null;
65150
65601
  this.clearWrapDropIndicator();
65151
65602
  this.uncollapseSourceTab(animState.sourceTabId);
65152
- if (!drop) return;
65153
- this.host.fireDrop(_objectSpread2({ event: event2 }, drop));
65603
+ if (!drop2) return;
65604
+ this.host.fireDrop(_objectSpread2({ event: event2 }, drop2));
65154
65605
  }
65155
65606
  /**
65156
65607
  * Shared body of the dragover entry point. Refreshes stale anim state
@@ -66194,19 +66645,19 @@ var Tabs = class extends CompositeDisposable {
66194
66645
  this._commitGroupMove(animState.sourceTabGroupId, animState.currentInsertionIndex);
66195
66646
  return;
66196
66647
  }
66197
- const drop = this._reorder.resolveTabDrop(animState);
66648
+ const drop2 = this._reorder.resolveTabDrop(animState);
66198
66649
  this._reorder.uncollapseSourceTab(animState.sourceTabId);
66199
- if (!drop) {
66650
+ if (!drop2) {
66200
66651
  this.resetTabTransforms();
66201
66652
  return;
66202
66653
  }
66203
66654
  const sourceIndex = animState.sourceIndex;
66204
66655
  const firstPositions = this.snapshotTabPositions();
66205
66656
  this.resetTabTransforms();
66206
- this._onDrop.fire(_objectSpread2({ event: event2 }, drop));
66657
+ this._onDrop.fire(_objectSpread2({ event: event2 }, drop2));
66207
66658
  this.runFlipAnimation(firstPositions, animState.sourceTabId, sourceIndex === -1, {
66208
- from: Math.min(sourceIndex, drop.index),
66209
- to: Math.max(sourceIndex, drop.index)
66659
+ from: Math.min(sourceIndex, drop2.index),
66660
+ to: Math.max(sourceIndex, drop2.index)
66210
66661
  });
66211
66662
  }, true), Disposable.from(() => {
66212
66663
  var _this$_voidContainerL2;
@@ -85300,6 +85751,7 @@ function defineFileTree() {
85300
85751
  }
85301
85752
 
85302
85753
  // src/ui.ts
85754
+ var valueEntry = true;
85303
85755
  var elements = {
85304
85756
  "slicc-agents": SliccAgents,
85305
85757
  "slicc-chat": SliccChat,
@@ -85381,7 +85833,8 @@ export {
85381
85833
  panelCommand,
85382
85834
  prompt,
85383
85835
  resolveColor,
85384
- surfaces2 as surfaces
85836
+ surfaces2 as surfaces,
85837
+ valueEntry
85385
85838
  };
85386
85839
  /*! Bundled license information:
85387
85840