@ai-ecoverse/slicc-spectrum 1.78.0 → 1.79.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/README.md +4 -2
- package/dist/slicc-dummy.js +131 -40
- package/dist/slicc-dummy.js.map +4 -4
- package/dist/slicc-ui.js +414 -8
- package/dist/slicc-ui.js.map +4 -4
- package/dist/types/app/confirm.d.ts +1 -0
- package/dist/types/app/guests.d.ts +98 -0
- package/dist/types/app/network.d.ts +10 -1
- package/dist/types/dummy/guests.d.ts +20 -0
- package/dist/types/dummy/index.d.ts +4 -1
- package/dist/types/dummy.d.ts +2 -0
- package/dist/types/model/types.d.ts +34 -0
- package/dist/types/ui.d.ts +2 -1
- package/package.json +2 -2
package/dist/slicc-ui.js
CHANGED
|
@@ -12314,6 +12314,326 @@ e25("swc-icon-visibility-off", o60);
|
|
|
12314
12314
|
// node_modules/@adobe/spectrum-wc/dist/link.css
|
|
12315
12315
|
var link_default = '/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Standalone link presentation for native `<a href>` elements.\n *\n * Default anchor appearance inside `.swc-Typography--prose` and\n * `.swc-Typography--links` ships with `typography.css` (generated). Import this\n * stylesheet when authors need explicit BEM modifiers on anchors.\n *\n */\n\n.swc-Link {\n inline-size: -moz-fit-content;\n inline-size: fit-content;\n font-family: var(--swc-sans-font-family-stack);\n font-size: var(--swc-font-size-100);\n font-style: normal;\n font-weight: 400;\n line-height: 1.30;\n color: var(--swc-link-text-color-default, var(--swc-accent-color-900));\n -webkit-text-decoration: underline;\n text-decoration: underline;\n text-decoration-thickness: 1px;\n text-underline-offset: 1px;\n transition: color 130ms ease-in-out;\n\n &:hover {\n color: var(--swc-link-text-color-hover, var(--swc-accent-color-1000));\n }\n\n &:active {\n color: var(--swc-link-text-color-down, var(--swc-accent-color-1000));\n }\n\n &:focus-visible {\n color: var(--swc-link-text-color-focus, var(--swc-accent-color-1000));\n border-radius: 4px;\n outline: 2px solid var(--swc-link-focus-indicator-color, var(--swc-blue-800));\n outline-offset: 2px;\n }\n\n &:lang(ja),\n &:lang(zh),\n &:lang(ko) {\n line-height: 1.50;\n }\n}\n\n.swc-Link--standalone {\n font-weight: 500;\n}\n\n.swc-Link--secondary {\n --swc-link-text-color-default: var(--swc-gray-800);\n --swc-link-text-color-hover: var(--swc-gray-900);\n --swc-link-text-color-down: var(--swc-gray-900);\n --swc-link-text-color-focus: var(--swc-gray-900);\n}\n\n.swc-Link--staticWhite {\n --swc-link-text-color-default: var(--swc-white);\n --swc-link-text-color-hover: var(--swc-white);\n --swc-link-text-color-down: var(--swc-white);\n --swc-link-text-color-focus: var(--swc-white);\n --swc-link-focus-indicator-color: var(--swc-white);\n}\n\n.swc-Link--staticBlack {\n --swc-link-text-color-default: var(--swc-black);\n --swc-link-text-color-hover: var(--swc-black);\n --swc-link-text-color-down: var(--swc-black);\n --swc-link-text-color-focus: var(--swc-black);\n --swc-link-focus-indicator-color: var(--swc-black);\n}\n\n.swc-Link--quiet.swc-Link--standalone {\n -webkit-text-decoration: none;\n text-decoration: none;\n}\n\n.swc-Link--quiet.swc-Link--standalone:hover {\n -webkit-text-decoration: underline;\n text-decoration: underline;\n}\n';
|
|
12316
12316
|
|
|
12317
|
+
// src/app/guests.ts
|
|
12318
|
+
var hour = 36e5;
|
|
12319
|
+
var day = 24 * hour;
|
|
12320
|
+
var guestExpiries = [
|
|
12321
|
+
["1h", "1 hour", hour],
|
|
12322
|
+
["1d", "1 day", day],
|
|
12323
|
+
["7d", "7 days", 7 * day],
|
|
12324
|
+
["30d", "30 days", 30 * day]
|
|
12325
|
+
];
|
|
12326
|
+
var guestNameLimit = 40;
|
|
12327
|
+
var approve = "A person with Control approves each one.";
|
|
12328
|
+
function gateOptions(field, cone) {
|
|
12329
|
+
const scoops = cone.scoops.map((name) => ({
|
|
12330
|
+
value: `scoop:${name}`,
|
|
12331
|
+
label: `${name} decides`,
|
|
12332
|
+
description: `The scoop ${name} decides.`
|
|
12333
|
+
}));
|
|
12334
|
+
const user2 = { value: "user", label: "You approve each one", description: approve };
|
|
12335
|
+
const agent = {
|
|
12336
|
+
value: "agent",
|
|
12337
|
+
label: "The automatic check decides",
|
|
12338
|
+
description: "It decides by your intent, and asks a person when unsure."
|
|
12339
|
+
};
|
|
12340
|
+
if (field === "tools") return [user2, agent, ...scoops];
|
|
12341
|
+
return [
|
|
12342
|
+
user2,
|
|
12343
|
+
agent,
|
|
12344
|
+
{
|
|
12345
|
+
value: "cone",
|
|
12346
|
+
label: `${cone.name} decides`,
|
|
12347
|
+
description: "The shared cone itself decides."
|
|
12348
|
+
},
|
|
12349
|
+
...scoops,
|
|
12350
|
+
{ value: "off", label: "No review", description: "Messages go straight in as follow-ups." }
|
|
12351
|
+
];
|
|
12352
|
+
}
|
|
12353
|
+
function guestCones(agents) {
|
|
12354
|
+
return agents.filter((agent) => agent.kind === "cone" && !agent.frozen).map((cone) => ({
|
|
12355
|
+
id: cone.id,
|
|
12356
|
+
name: cone.name,
|
|
12357
|
+
scoops: agents.filter((agent) => agent.kind === "scoop" && agent.parentId === cone.id).map((scoop) => scoop.name)
|
|
12358
|
+
}));
|
|
12359
|
+
}
|
|
12360
|
+
function activeCone(agents, active) {
|
|
12361
|
+
const agent = agents.find((item2) => item2.id === active);
|
|
12362
|
+
return (agent?.kind === "scoop" ? agent.parentId : agent?.id) ?? "";
|
|
12363
|
+
}
|
|
12364
|
+
function guestNameProblem(label) {
|
|
12365
|
+
if (!label) return "Enter a name for the guest.";
|
|
12366
|
+
if (label.length > guestNameLimit)
|
|
12367
|
+
return `Keep the name to ${guestNameLimit} characters or fewer.`;
|
|
12368
|
+
return null;
|
|
12369
|
+
}
|
|
12370
|
+
function guestExpiry(seat, now = Date.now()) {
|
|
12371
|
+
if (seat.expiresAt === null) return "No expiry";
|
|
12372
|
+
const hours = Math.max(1, Math.ceil((seat.expiresAt - now) / hour));
|
|
12373
|
+
if (hours < 24) return `Expires in ${hours} ${hours === 1 ? "hour" : "hours"}`;
|
|
12374
|
+
const days = Math.round(hours / 24);
|
|
12375
|
+
return `Expires in ${days} ${days === 1 ? "day" : "days"}`;
|
|
12376
|
+
}
|
|
12377
|
+
function pendingLabel(count) {
|
|
12378
|
+
return `${count} waiting for review`;
|
|
12379
|
+
}
|
|
12380
|
+
var SliccGuestInvite = class extends i2 {
|
|
12381
|
+
static properties = {
|
|
12382
|
+
cone: { state: true },
|
|
12383
|
+
label: { state: true },
|
|
12384
|
+
expiry: { state: true },
|
|
12385
|
+
messages: { state: true },
|
|
12386
|
+
tools: { state: true },
|
|
12387
|
+
pending: { state: true },
|
|
12388
|
+
error: { state: true },
|
|
12389
|
+
problem: { state: true },
|
|
12390
|
+
url: { state: true },
|
|
12391
|
+
copied: { state: true },
|
|
12392
|
+
uncopied: { state: true }
|
|
12393
|
+
};
|
|
12394
|
+
port = null;
|
|
12395
|
+
cones = [];
|
|
12396
|
+
trigger = null;
|
|
12397
|
+
#result = Promise.withResolvers();
|
|
12398
|
+
#seat = null;
|
|
12399
|
+
constructor() {
|
|
12400
|
+
super();
|
|
12401
|
+
this.cone = "";
|
|
12402
|
+
this.label = "";
|
|
12403
|
+
this.expiry = "7d";
|
|
12404
|
+
this.messages = "user";
|
|
12405
|
+
this.tools = "user";
|
|
12406
|
+
this.pending = false;
|
|
12407
|
+
this.error = "";
|
|
12408
|
+
this.problem = "";
|
|
12409
|
+
this.url = "";
|
|
12410
|
+
this.copied = false;
|
|
12411
|
+
this.uncopied = false;
|
|
12412
|
+
}
|
|
12413
|
+
static styles = [
|
|
12414
|
+
dialogStyles,
|
|
12415
|
+
i`
|
|
12416
|
+
.fields,
|
|
12417
|
+
.link,
|
|
12418
|
+
.once {
|
|
12419
|
+
inline-size: calc(var(--mod-alert-dialog-max-width) - 2 * var(--mod-alert-dialog-padding));
|
|
12420
|
+
box-sizing: border-box;
|
|
12421
|
+
}
|
|
12422
|
+
.fields {
|
|
12423
|
+
display: grid;
|
|
12424
|
+
gap: var(--swc-spacing-300);
|
|
12425
|
+
padding: var(--swc-spacing-75);
|
|
12426
|
+
}
|
|
12427
|
+
.field {
|
|
12428
|
+
display: grid;
|
|
12429
|
+
gap: var(--swc-spacing-75);
|
|
12430
|
+
min-width: 0;
|
|
12431
|
+
}
|
|
12432
|
+
.fields sp-field-label,
|
|
12433
|
+
.fields sp-textfield,
|
|
12434
|
+
.link sp-textfield {
|
|
12435
|
+
margin: 0;
|
|
12436
|
+
inline-size: 100%;
|
|
12437
|
+
}
|
|
12438
|
+
sp-picker {
|
|
12439
|
+
inline-size: 100%;
|
|
12440
|
+
}
|
|
12441
|
+
.link {
|
|
12442
|
+
display: flex;
|
|
12443
|
+
flex-wrap: wrap;
|
|
12444
|
+
align-items: center;
|
|
12445
|
+
gap: var(--swc-spacing-100);
|
|
12446
|
+
padding: var(--swc-spacing-75);
|
|
12447
|
+
}
|
|
12448
|
+
.link sp-textfield {
|
|
12449
|
+
flex: 1 1 200px;
|
|
12450
|
+
inline-size: auto;
|
|
12451
|
+
}
|
|
12452
|
+
.once {
|
|
12453
|
+
margin-block-start: var(--swc-spacing-200);
|
|
12454
|
+
padding-inline: var(--swc-spacing-75);
|
|
12455
|
+
}
|
|
12456
|
+
.problem {
|
|
12457
|
+
display: flex;
|
|
12458
|
+
align-items: flex-start;
|
|
12459
|
+
gap: var(--swc-spacing-75);
|
|
12460
|
+
margin: var(--swc-spacing-200) var(--swc-spacing-75) 0;
|
|
12461
|
+
color: var(--swc-negative-content-color-default);
|
|
12462
|
+
overflow-wrap: anywhere;
|
|
12463
|
+
--swc-icon-color: var(--swc-negative-content-color-default);
|
|
12464
|
+
}
|
|
12465
|
+
.problem swc-icon-alert-triangle {
|
|
12466
|
+
flex: none;
|
|
12467
|
+
margin-block-start: var(--swc-spacing-50);
|
|
12468
|
+
}
|
|
12469
|
+
`
|
|
12470
|
+
];
|
|
12471
|
+
get result() {
|
|
12472
|
+
return this.#result.promise;
|
|
12473
|
+
}
|
|
12474
|
+
get #dialog() {
|
|
12475
|
+
return this.renderRoot.querySelector("dialog");
|
|
12476
|
+
}
|
|
12477
|
+
#focus(selector) {
|
|
12478
|
+
const target2 = this.renderRoot.querySelector(selector);
|
|
12479
|
+
void target2.updateComplete.then(() => target2.focus());
|
|
12480
|
+
}
|
|
12481
|
+
firstUpdated() {
|
|
12482
|
+
this.#dialog.showModal();
|
|
12483
|
+
this.#focus("#guest-name");
|
|
12484
|
+
}
|
|
12485
|
+
disconnectedCallback() {
|
|
12486
|
+
super.disconnectedCallback();
|
|
12487
|
+
this.url = "";
|
|
12488
|
+
this.#result.resolve(this.#seat);
|
|
12489
|
+
}
|
|
12490
|
+
get #current() {
|
|
12491
|
+
return this.cones.find((cone) => cone.id === this.cone) ?? this.cones[0];
|
|
12492
|
+
}
|
|
12493
|
+
#keep(gate, field, cone) {
|
|
12494
|
+
return gateOptions(field, cone).some((option2) => option2.value === gate) ? gate : "user";
|
|
12495
|
+
}
|
|
12496
|
+
#pick(event2) {
|
|
12497
|
+
return event2.target.value;
|
|
12498
|
+
}
|
|
12499
|
+
#setCone(event2) {
|
|
12500
|
+
this.cone = this.#pick(event2);
|
|
12501
|
+
this.messages = this.#keep(this.messages, "messages", this.#current);
|
|
12502
|
+
this.tools = this.#keep(this.tools, "tools", this.#current);
|
|
12503
|
+
}
|
|
12504
|
+
#input(event2) {
|
|
12505
|
+
this.label = event2.currentTarget.value;
|
|
12506
|
+
this.problem = "";
|
|
12507
|
+
}
|
|
12508
|
+
#key(event2) {
|
|
12509
|
+
if (event2.key !== "Enter") return;
|
|
12510
|
+
event2.preventDefault();
|
|
12511
|
+
void this.create();
|
|
12512
|
+
}
|
|
12513
|
+
async create() {
|
|
12514
|
+
if (this.pending) return;
|
|
12515
|
+
const label = this.label.trim();
|
|
12516
|
+
const problem = guestNameProblem(label);
|
|
12517
|
+
this.problem = problem ?? "";
|
|
12518
|
+
this.error = "";
|
|
12519
|
+
if (problem) {
|
|
12520
|
+
this.#focus("#guest-name");
|
|
12521
|
+
return;
|
|
12522
|
+
}
|
|
12523
|
+
const ms = guestExpiries.find(([key2]) => key2 === this.expiry)[2];
|
|
12524
|
+
this.pending = true;
|
|
12525
|
+
try {
|
|
12526
|
+
const { seat, url } = await this.port.mint(this.#current.id, {
|
|
12527
|
+
label,
|
|
12528
|
+
expiresAt: Date.now() + ms,
|
|
12529
|
+
gates: { messages: this.messages, tools: this.tools }
|
|
12530
|
+
});
|
|
12531
|
+
this.#seat = seat;
|
|
12532
|
+
this.url = url;
|
|
12533
|
+
this.pending = false;
|
|
12534
|
+
await this.updateComplete;
|
|
12535
|
+
this.#focus("[data-action=copy-guest-url]");
|
|
12536
|
+
} catch (error) {
|
|
12537
|
+
this.pending = false;
|
|
12538
|
+
this.error = failure(error);
|
|
12539
|
+
}
|
|
12540
|
+
}
|
|
12541
|
+
async copy() {
|
|
12542
|
+
try {
|
|
12543
|
+
await globalThis.navigator.clipboard.writeText(this.url);
|
|
12544
|
+
this.copied = true;
|
|
12545
|
+
this.uncopied = false;
|
|
12546
|
+
} catch {
|
|
12547
|
+
this.copied = false;
|
|
12548
|
+
this.uncopied = true;
|
|
12549
|
+
}
|
|
12550
|
+
}
|
|
12551
|
+
#cancel(event2) {
|
|
12552
|
+
if (this.pending) event2.preventDefault();
|
|
12553
|
+
}
|
|
12554
|
+
#closed() {
|
|
12555
|
+
this.url = "";
|
|
12556
|
+
this.remove();
|
|
12557
|
+
if (this.trigger?.isConnected) this.trigger.focus();
|
|
12558
|
+
}
|
|
12559
|
+
#gate(field, title, value) {
|
|
12560
|
+
const options = gateOptions(field, this.#current);
|
|
12561
|
+
const chosen = options.find((option2) => option2.value === value);
|
|
12562
|
+
const id = `guest-${field}`;
|
|
12563
|
+
return b`<div class="field">
|
|
12564
|
+
<sp-field-label for=${id} size="m">${title}</sp-field-label>
|
|
12565
|
+
<sp-picker id=${id} size="m" label=${title} data-action=${`guest-${field}`} .value=${l(value)} ?disabled=${this.pending} @change=${(event2) => {
|
|
12566
|
+
this[field] = this.#pick(event2);
|
|
12567
|
+
}}>
|
|
12568
|
+
${options.map((option2) => b`<sp-menu-item value=${option2.value}>${option2.label}<span slot="description">${option2.description}</span></sp-menu-item>`)}
|
|
12569
|
+
</sp-picker>
|
|
12570
|
+
<sp-help-text size="m" data-help=${field}>${chosen.description}</sp-help-text>
|
|
12571
|
+
</div>`;
|
|
12572
|
+
}
|
|
12573
|
+
#form() {
|
|
12574
|
+
const cone = this.#current;
|
|
12575
|
+
return b`<h2 id="title" slot="heading">Invite a guest</h2>
|
|
12576
|
+
<div class="fields">
|
|
12577
|
+
<div class="field">
|
|
12578
|
+
<sp-field-label for="guest-cone" size="m">Cone</sp-field-label>
|
|
12579
|
+
<sp-picker id="guest-cone" size="m" label="Cone" data-action="guest-cone" .value=${l(cone.id)} ?disabled=${this.pending} @change=${this.#setCone}>
|
|
12580
|
+
${this.cones.map((item2) => b`<sp-menu-item value=${item2.id}>${item2.name}</sp-menu-item>`)}
|
|
12581
|
+
</sp-picker>
|
|
12582
|
+
</div>
|
|
12583
|
+
<div class="field">
|
|
12584
|
+
<sp-field-label for="guest-name" size="m">Name</sp-field-label>
|
|
12585
|
+
<sp-textfield id="guest-name" size="m" data-action="guest-name" autocomplete="off" .value=${l(this.label)} ?invalid=${Boolean(this.problem)} ?readonly=${this.pending} @input=${this.#input} @keydown=${this.#key}>
|
|
12586
|
+
<sp-help-text slot="help-text">Shown to you and in the cone, never set by the guest.</sp-help-text>
|
|
12587
|
+
${this.problem ? b`<sp-help-text slot="negative-help-text" icon>${this.problem}</sp-help-text>` : A}
|
|
12588
|
+
</sp-textfield>
|
|
12589
|
+
</div>
|
|
12590
|
+
<div class="field">
|
|
12591
|
+
<sp-field-label for="guest-expiry" size="m">Link expires</sp-field-label>
|
|
12592
|
+
<sp-picker id="guest-expiry" size="m" label="Link expires" data-action="guest-expiry" .value=${l(this.expiry)} ?disabled=${this.pending} @change=${(event2) => {
|
|
12593
|
+
this.expiry = this.#pick(event2);
|
|
12594
|
+
}}>
|
|
12595
|
+
${guestExpiries.map(([key2, label]) => b`<sp-menu-item value=${key2}>${label}</sp-menu-item>`)}
|
|
12596
|
+
</sp-picker>
|
|
12597
|
+
</div>
|
|
12598
|
+
${this.#gate("messages", "Their messages", this.messages)}
|
|
12599
|
+
${this.#gate("tools", "Their tool calls", this.tools)}
|
|
12600
|
+
</div>
|
|
12601
|
+
${this.error ? b`<p class="problem" role="alert" data-error="guest"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.error}</span></p>` : A}
|
|
12602
|
+
<swc-button slot="button" variant="secondary" fill-style="outline" data-cancel data-action="cancel-guest" ?disabled=${this.pending} @click=${() => this.#dialog.close()}>Cancel</swc-button>
|
|
12603
|
+
<swc-button slot="button" variant="accent" data-action="create-guest-link" ?pending=${this.pending} @click=${() => this.create()}>Create link</swc-button>`;
|
|
12604
|
+
}
|
|
12605
|
+
#link() {
|
|
12606
|
+
const label = this.#seat?.label ?? "";
|
|
12607
|
+
return b`<h2 id="title" slot="heading">Link for ${label}</h2>
|
|
12608
|
+
<div class="link" data-guest-link>
|
|
12609
|
+
<sp-textfield size="m" readonly label="Guest link" aria-label="Guest link" data-copy="guest-url" .value=${this.url}></sp-textfield>
|
|
12610
|
+
<swc-action-button size="m" data-action="copy-guest-url" accessible-label=${this.copied ? "Copied" : "Copy guest link"} @click=${() => this.copy()}>
|
|
12611
|
+
<swc-icon-copy slot="icon"></swc-icon-copy>${this.copied ? "Copied" : "Copy"}
|
|
12612
|
+
</swc-action-button>
|
|
12613
|
+
</div>
|
|
12614
|
+
<p class="once" data-once>This link is shown only once. Copy it now.</p>
|
|
12615
|
+
${this.uncopied ? b`<p class="problem" role="alert" data-error="guest-copy"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>Couldn’t copy. Select the link and copy it.</span></p>` : A}
|
|
12616
|
+
<swc-button slot="button" variant="accent" data-action="done-guest" @click=${() => this.#dialog.close()}>Done</swc-button>`;
|
|
12617
|
+
}
|
|
12618
|
+
render() {
|
|
12619
|
+
return b`<dialog aria-labelledby="title" data-guest-invite=${this.url ? "link" : "form"} @cancel=${this.#cancel} @close=${this.#closed}>
|
|
12620
|
+
<sp-alert-dialog variant="confirmation">${this.url ? this.#link() : this.#form()}</sp-alert-dialog>
|
|
12621
|
+
</dialog>`;
|
|
12622
|
+
}
|
|
12623
|
+
};
|
|
12624
|
+
function invite(options) {
|
|
12625
|
+
const trigger2 = options.trigger ?? focused();
|
|
12626
|
+
const element2 = document.createElement("slicc-guest-invite");
|
|
12627
|
+
const cones = guestCones(options.agents);
|
|
12628
|
+
const active = activeCone(options.agents, options.active);
|
|
12629
|
+
element2.port = options.port;
|
|
12630
|
+
element2.cones = cones;
|
|
12631
|
+
element2.cone = cones.some((cone) => cone.id === active) ? active : cones[0]?.id ?? "";
|
|
12632
|
+
element2.trigger = trigger2;
|
|
12633
|
+
themeOf(trigger2).append(element2);
|
|
12634
|
+
return element2.result;
|
|
12635
|
+
}
|
|
12636
|
+
|
|
12317
12637
|
// node_modules/uqr/dist/index.mjs
|
|
12318
12638
|
var QrCodeDataType = /* @__PURE__ */ ((QrCodeDataType2) => {
|
|
12319
12639
|
QrCodeDataType2[QrCodeDataType2["Border"] = -1] = "Border";
|
|
@@ -13269,6 +13589,14 @@ function leaderConfirm(leader) {
|
|
|
13269
13589
|
variant: "destructive"
|
|
13270
13590
|
};
|
|
13271
13591
|
}
|
|
13592
|
+
function guestConfirm(seat) {
|
|
13593
|
+
return {
|
|
13594
|
+
title: `Revoke ${seat.label}?`,
|
|
13595
|
+
body: "Their link stops working at once. To let them back in, invite them again.",
|
|
13596
|
+
action: "Revoke",
|
|
13597
|
+
variant: "destructive"
|
|
13598
|
+
};
|
|
13599
|
+
}
|
|
13272
13600
|
function remoteLight(status2) {
|
|
13273
13601
|
const devices = status2?.links?.devices ?? [];
|
|
13274
13602
|
const asking3 = devices.filter((device) => device.asking);
|
|
@@ -13413,9 +13741,12 @@ var SliccNetwork = class extends ModelElement {
|
|
|
13413
13741
|
revealed: { state: true },
|
|
13414
13742
|
linksError: { state: true },
|
|
13415
13743
|
syncError: { state: true },
|
|
13416
|
-
leaderError: { state: true }
|
|
13744
|
+
leaderError: { state: true },
|
|
13745
|
+
guestError: { state: true },
|
|
13746
|
+
guestsOff: { state: true }
|
|
13417
13747
|
};
|
|
13418
13748
|
#leaderId = "";
|
|
13749
|
+
#guestId = "";
|
|
13419
13750
|
constructor() {
|
|
13420
13751
|
super();
|
|
13421
13752
|
this.checking = false;
|
|
@@ -13429,6 +13760,8 @@ var SliccNetwork = class extends ModelElement {
|
|
|
13429
13760
|
this.linksError = "";
|
|
13430
13761
|
this.syncError = "";
|
|
13431
13762
|
this.leaderError = "";
|
|
13763
|
+
this.guestError = "";
|
|
13764
|
+
this.guestsOff = null;
|
|
13432
13765
|
}
|
|
13433
13766
|
static styles = [
|
|
13434
13767
|
r(link_default),
|
|
@@ -13734,6 +14067,17 @@ var SliccNetwork = class extends ModelElement {
|
|
|
13734
14067
|
grid-column: 1 / -1;
|
|
13735
14068
|
font-size: var(--swc-font-size-75);
|
|
13736
14069
|
}
|
|
14070
|
+
.sr {
|
|
14071
|
+
position: absolute;
|
|
14072
|
+
width: 1px;
|
|
14073
|
+
height: 1px;
|
|
14074
|
+
margin: -1px;
|
|
14075
|
+
padding: 0;
|
|
14076
|
+
overflow: hidden;
|
|
14077
|
+
clip-path: inset(50%);
|
|
14078
|
+
white-space: nowrap;
|
|
14079
|
+
border: 0;
|
|
14080
|
+
}
|
|
13737
14081
|
time {
|
|
13738
14082
|
color: var(--swc-neutral-subdued-content-color-default);
|
|
13739
14083
|
font-size: var(--swc-font-size-75);
|
|
@@ -13753,10 +14097,19 @@ var SliccNetwork = class extends ModelElement {
|
|
|
13753
14097
|
this.linksError = "";
|
|
13754
14098
|
this.syncError = "";
|
|
13755
14099
|
this.leaderError = "";
|
|
14100
|
+
this.guestError = "";
|
|
14101
|
+
this.guestsOff = null;
|
|
13756
14102
|
const update = () => this.requestUpdate();
|
|
13757
14103
|
return [
|
|
13758
14104
|
model.tray.on("status", update),
|
|
13759
|
-
...model.network ? [model.network.on("network", update)] : []
|
|
14105
|
+
...model.network ? [model.network.on("network", update)] : [],
|
|
14106
|
+
...model.guests ? [
|
|
14107
|
+
model.guests.on("seats", update),
|
|
14108
|
+
model.agent.on("agents", update),
|
|
14109
|
+
model.guests.on("unavailable", (text2) => {
|
|
14110
|
+
this.guestsOff = text2;
|
|
14111
|
+
})
|
|
14112
|
+
] : []
|
|
13760
14113
|
];
|
|
13761
14114
|
}
|
|
13762
14115
|
reveal(section) {
|
|
@@ -14167,6 +14520,56 @@ var SliccNetwork = class extends ModelElement {
|
|
|
14167
14520
|
<ul aria-labelledby="leaders">${status2.leaders.map((leader) => this.#leader(port, leader))}</ul>
|
|
14168
14521
|
</section>`;
|
|
14169
14522
|
}
|
|
14523
|
+
async #revoke(event2, port, seat) {
|
|
14524
|
+
const trigger2 = event2.currentTarget;
|
|
14525
|
+
if (!await confirm({ ...guestConfirm(seat), trigger: trigger2 })) return;
|
|
14526
|
+
await this.#act(
|
|
14527
|
+
`guest:${seat.id}`,
|
|
14528
|
+
() => {
|
|
14529
|
+
this.#guestId = seat.id;
|
|
14530
|
+
return port.revoke(seat.id);
|
|
14531
|
+
},
|
|
14532
|
+
"guestError"
|
|
14533
|
+
);
|
|
14534
|
+
}
|
|
14535
|
+
#invite(event2, port) {
|
|
14536
|
+
const agent = this.model.agent;
|
|
14537
|
+
void invite({
|
|
14538
|
+
port,
|
|
14539
|
+
agents: agent.list(),
|
|
14540
|
+
active: agent.active(),
|
|
14541
|
+
trigger: event2.currentTarget
|
|
14542
|
+
});
|
|
14543
|
+
}
|
|
14544
|
+
#seat(port, seat, now) {
|
|
14545
|
+
const action = `guest:${seat.id}`;
|
|
14546
|
+
const cone = this.model.agent.list().find((item2) => item2.id === seat.agentId);
|
|
14547
|
+
return b`<li class="device guest" data-seat=${seat.id} data-connected=${seat.connected ? "true" : "false"}>
|
|
14548
|
+
<div class="who">
|
|
14549
|
+
<span class="name" title=${seat.label}>${seat.label}</span>
|
|
14550
|
+
<swc-status-light size="s" variant=${seat.connected ? "positive" : "neutral"}>${seat.connected ? "Connected" : "Not connected"}</swc-status-light>
|
|
14551
|
+
${seat.pending > 0 ? b`<swc-badge size="s" variant="notice" subtle data-pending=${seat.pending}><span aria-hidden="true">${seat.pending} waiting</span><span class="sr">${pendingLabel(seat.pending)}</span></swc-badge>` : A}
|
|
14552
|
+
</div>
|
|
14553
|
+
<swc-button size="s" variant="secondary" fill-style="outline" data-action="revoke-guest" accessible-label=${`Revoke ${seat.label}`} ?pending=${this.busy === action} ?disabled=${this.#waiting(action)} @click=${(event2) => this.#revoke(event2, port, seat)}>Revoke</swc-button>
|
|
14554
|
+
<span class="where"><span data-seat-cone>${cone?.name ?? seat.agentId}</span><span data-expiry>${guestExpiry(seat, now)}</span></span>
|
|
14555
|
+
${this.guestError && this.#guestId === seat.id ? b`<div class="error" role="alert" data-error="guest"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.guestError}</span></div>` : A}
|
|
14556
|
+
</li>`;
|
|
14557
|
+
}
|
|
14558
|
+
#guestsSection() {
|
|
14559
|
+
const port = this.model?.guests;
|
|
14560
|
+
if (!port) return A;
|
|
14561
|
+
const seats = port.list();
|
|
14562
|
+
const off = port.unavailable ? port.unavailable() : this.guestsOff;
|
|
14563
|
+
const cones = guestCones(this.model.agent.list()).length > 0;
|
|
14564
|
+
const now = Date.now();
|
|
14565
|
+
return b`<section aria-labelledby="guests" data-guests=${seats.length}>
|
|
14566
|
+
<div class="head">
|
|
14567
|
+
<h2 id="guests">Guests</h2>
|
|
14568
|
+
${off ? b`<p class="muted" data-guests-unavailable>${off}</p>` : b`<swc-button size="m" variant="accent" data-action="invite-guest" ?disabled=${!cones} @click=${(event2) => this.#invite(event2, port)}>Invite a guest</swc-button>`}
|
|
14569
|
+
</div>
|
|
14570
|
+
${seats.length ? b`<ul aria-labelledby="guests">${seats.map((seat) => this.#seat(port, seat, now))}</ul>` : b`<p class="muted" data-guests-empty>No guests yet.</p>`}
|
|
14571
|
+
</section>`;
|
|
14572
|
+
}
|
|
14170
14573
|
#exit(event2, port) {
|
|
14171
14574
|
const value = event2.target.value;
|
|
14172
14575
|
void this.#tailnet("exit", () => port.setExitNode?.(value === "none" ? null : value));
|
|
@@ -14289,6 +14692,7 @@ var SliccNetwork = class extends ModelElement {
|
|
|
14289
14692
|
${this.#state(port, status2)}
|
|
14290
14693
|
${this.#sync(this.model.tray)}
|
|
14291
14694
|
${this.#leadersSection(port, status2)}
|
|
14695
|
+
${this.#guestsSection()}
|
|
14292
14696
|
${this.#tailnetSection(port, status2)}
|
|
14293
14697
|
${this.#linksSection(port, status2)}
|
|
14294
14698
|
${this.#whole(status2)}
|
|
@@ -30836,7 +31240,7 @@ function errorCard(item2, handlers) {
|
|
|
30836
31240
|
function time(at) {
|
|
30837
31241
|
return new Date(at).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
|
|
30838
31242
|
}
|
|
30839
|
-
function
|
|
31243
|
+
function day2(at) {
|
|
30840
31244
|
return new Date(at).toLocaleDateString("en-GB", {
|
|
30841
31245
|
weekday: "short",
|
|
30842
31246
|
day: "numeric",
|
|
@@ -30849,7 +31253,7 @@ function stamps(messages) {
|
|
|
30849
31253
|
let last = -Infinity;
|
|
30850
31254
|
let seen = "";
|
|
30851
31255
|
for (const message of messages) {
|
|
30852
|
-
const label =
|
|
31256
|
+
const label = day2(message.createdAt);
|
|
30853
31257
|
if (label !== seen) {
|
|
30854
31258
|
seen = label;
|
|
30855
31259
|
last = -Infinity;
|
|
@@ -37005,7 +37409,7 @@ var SliccChat = class extends ThemedElement {
|
|
|
37005
37409
|
let anchor = null;
|
|
37006
37410
|
for (const message of messages) {
|
|
37007
37411
|
const calls = toolOnly(message, showThinking);
|
|
37008
|
-
if (anchor && calls &&
|
|
37412
|
+
if (anchor && calls && day2(message.createdAt) === day2(anchor.createdAt)) {
|
|
37009
37413
|
extra.set(anchor.id, [...extra.get(anchor.id) ?? [], ...calls]);
|
|
37010
37414
|
owners.set(calls[0].id, {
|
|
37011
37415
|
id: message.id,
|
|
@@ -37020,7 +37424,7 @@ var SliccChat = class extends ThemedElement {
|
|
|
37020
37424
|
const target2 = last && (into.get(last.id) ?? last.id);
|
|
37021
37425
|
for (const message of messages) {
|
|
37022
37426
|
if (into.has(message.id)) continue;
|
|
37023
|
-
const label =
|
|
37427
|
+
const label = day2(message.createdAt);
|
|
37024
37428
|
if (label !== previous) {
|
|
37025
37429
|
out.push([
|
|
37026
37430
|
`day:${message.id}`,
|
|
@@ -40040,8 +40444,8 @@ function replaceable(secret) {
|
|
|
40040
40444
|
}
|
|
40041
40445
|
function filename(now = Date.now()) {
|
|
40042
40446
|
const date = new Date(now);
|
|
40043
|
-
const
|
|
40044
|
-
return `slicc-secrets-${
|
|
40447
|
+
const day3 = [date.getFullYear(), date.getMonth() + 1, date.getDate()].map((part2) => String(part2).padStart(2, "0")).join("-");
|
|
40448
|
+
return `slicc-secrets-${day3}.slicc-vault`;
|
|
40045
40449
|
}
|
|
40046
40450
|
function download(bytes, name, root2) {
|
|
40047
40451
|
const url = URL.createObjectURL(
|
|
@@ -84883,6 +85287,7 @@ var elements = {
|
|
|
84883
85287
|
"slicc-value-request": SliccValueRequest,
|
|
84884
85288
|
"slicc-confirm": SliccConfirm,
|
|
84885
85289
|
"slicc-prompt": SliccPrompt,
|
|
85290
|
+
"slicc-guest-invite": SliccGuestInvite,
|
|
84886
85291
|
"slicc-app": SliccApp
|
|
84887
85292
|
};
|
|
84888
85293
|
function define2() {
|
|
@@ -84914,6 +85319,7 @@ export {
|
|
|
84914
85319
|
SliccFileView,
|
|
84915
85320
|
SliccFiles,
|
|
84916
85321
|
SliccFollow,
|
|
85322
|
+
SliccGuestInvite,
|
|
84917
85323
|
SliccNetwork,
|
|
84918
85324
|
SliccNotices,
|
|
84919
85325
|
SliccPrompt,
|