@webmcpui/core 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -24
- package/dist/index.d.ts +446 -13
- package/dist/index.js +1653 -71
- package/dist/testing.d.ts +1 -1
- package/dist/{webmcp-JAn7I2xj.d.ts → webmcp-DbmbtX6x.d.ts} +1 -1
- package/dist/webmcpui.global.js +701 -47
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4,12 +4,11 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/elements/form-control.ts
|
|
6
6
|
import {
|
|
7
|
-
LitElement,
|
|
8
7
|
html,
|
|
9
8
|
css,
|
|
10
9
|
nothing
|
|
11
10
|
} from "lit";
|
|
12
|
-
import { property, state } from "lit/decorators.js";
|
|
11
|
+
import { property as property2, state } from "lit/decorators.js";
|
|
13
12
|
|
|
14
13
|
// src/standard-schema.ts
|
|
15
14
|
async function validateStandard(schema, value) {
|
|
@@ -23,6 +22,10 @@ function isStandardSchema(value) {
|
|
|
23
22
|
return typeof value === "object" && value !== null && "~standard" in value && typeof value["~standard"]?.validate === "function";
|
|
24
23
|
}
|
|
25
24
|
|
|
25
|
+
// src/elements/exposable.ts
|
|
26
|
+
import { LitElement } from "lit";
|
|
27
|
+
import { property } from "lit/decorators.js";
|
|
28
|
+
|
|
26
29
|
// src/webmcp.ts
|
|
27
30
|
function getModelContext() {
|
|
28
31
|
const fromDocument = typeof document !== "undefined" ? document.modelContext : void 0;
|
|
@@ -73,6 +76,67 @@ function exposeTool(definition) {
|
|
|
73
76
|
};
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
// src/elements/exposable.ts
|
|
80
|
+
var WmcpExposable = class extends LitElement {
|
|
81
|
+
constructor() {
|
|
82
|
+
super(...arguments);
|
|
83
|
+
this.expose = false;
|
|
84
|
+
this.toolName = "";
|
|
85
|
+
this.toolDescription = "";
|
|
86
|
+
this.toolDisposer = () => {
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** JSON Schema for the tool's args. Defaults to a no-argument tool. */
|
|
90
|
+
toolInputSchema() {
|
|
91
|
+
return { type: "object", properties: {} };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Property changes that alter the tool's *identity* (name/description) and so
|
|
95
|
+
* require re-registration. State read live inside {@link executeTool} does
|
|
96
|
+
* not belong here. Subclasses extend this with their own naming inputs.
|
|
97
|
+
*/
|
|
98
|
+
get toolReactiveProps() {
|
|
99
|
+
return ["expose", "toolName", "toolDescription"];
|
|
100
|
+
}
|
|
101
|
+
connectedCallback() {
|
|
102
|
+
super.connectedCallback();
|
|
103
|
+
if (this.expose) this.registerTool();
|
|
104
|
+
}
|
|
105
|
+
disconnectedCallback() {
|
|
106
|
+
super.disconnectedCallback();
|
|
107
|
+
this.toolDisposer();
|
|
108
|
+
this.toolDisposer = () => {
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
updated(changed) {
|
|
112
|
+
if (this.expose && this.toolReactiveProps.some((p) => changed.has(p))) {
|
|
113
|
+
this.registerTool();
|
|
114
|
+
} else if (!this.expose && changed.has("expose")) {
|
|
115
|
+
this.toolDisposer();
|
|
116
|
+
this.toolDisposer = () => {
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
registerTool() {
|
|
121
|
+
this.toolDisposer();
|
|
122
|
+
this.toolDisposer = exposeTool({
|
|
123
|
+
name: this.resolvedToolName,
|
|
124
|
+
description: this.toolDescription || this.defaultToolDescription,
|
|
125
|
+
inputSchema: this.toolInputSchema(),
|
|
126
|
+
execute: (args) => this.executeTool(args)
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
__decorateClass([
|
|
131
|
+
property({ type: Boolean })
|
|
132
|
+
], WmcpExposable.prototype, "expose", 2);
|
|
133
|
+
__decorateClass([
|
|
134
|
+
property({ attribute: "tool-name" })
|
|
135
|
+
], WmcpExposable.prototype, "toolName", 2);
|
|
136
|
+
__decorateClass([
|
|
137
|
+
property({ attribute: "tool-description" })
|
|
138
|
+
], WmcpExposable.prototype, "toolDescription", 2);
|
|
139
|
+
|
|
76
140
|
// src/elements/form-control.ts
|
|
77
141
|
var textFieldStyles = css`
|
|
78
142
|
.control {
|
|
@@ -116,7 +180,7 @@ var textFieldStyles = css`
|
|
|
116
180
|
);
|
|
117
181
|
}
|
|
118
182
|
`;
|
|
119
|
-
var WmcpFormControl = class extends
|
|
183
|
+
var WmcpFormControl = class extends WmcpExposable {
|
|
120
184
|
constructor() {
|
|
121
185
|
super(...arguments);
|
|
122
186
|
this.label = "";
|
|
@@ -127,13 +191,8 @@ var WmcpFormControl = class extends LitElement {
|
|
|
127
191
|
this.disabled = false;
|
|
128
192
|
this.helperText = "";
|
|
129
193
|
this.requiredMessage = "";
|
|
130
|
-
this.expose = false;
|
|
131
|
-
this.toolName = "";
|
|
132
|
-
this.toolDescription = "";
|
|
133
194
|
this.error = "";
|
|
134
195
|
this.internals = this.attachInternals();
|
|
135
|
-
this.toolDisposer = () => {
|
|
136
|
-
};
|
|
137
196
|
/** Reveal the validation message when the form reports validity (submit). */
|
|
138
197
|
this.onInvalid = () => {
|
|
139
198
|
this.error = this.internals.validationMessage;
|
|
@@ -198,7 +257,6 @@ var WmcpFormControl = class extends LitElement {
|
|
|
198
257
|
this.syncFormValue();
|
|
199
258
|
this.addEventListener("invalid", this.onInvalid);
|
|
200
259
|
void this.validate(false);
|
|
201
|
-
if (this.expose) this.registerTool();
|
|
202
260
|
}
|
|
203
261
|
/**
|
|
204
262
|
* The value contributed to the containing form. Defaults to the string
|
|
@@ -233,40 +291,32 @@ var WmcpFormControl = class extends LitElement {
|
|
|
233
291
|
disconnectedCallback() {
|
|
234
292
|
super.disconnectedCallback();
|
|
235
293
|
this.removeEventListener("invalid", this.onInvalid);
|
|
236
|
-
this.toolDisposer();
|
|
237
|
-
this.toolDisposer = () => {
|
|
238
|
-
};
|
|
239
294
|
}
|
|
240
295
|
updated(changed) {
|
|
296
|
+
super.updated(changed);
|
|
241
297
|
this.syncFormValue();
|
|
242
|
-
if (this.expose && (changed.has("expose") || changed.has("name") || changed.has("toolName") || changed.has("toolDescription"))) {
|
|
243
|
-
this.registerTool();
|
|
244
|
-
}
|
|
245
298
|
}
|
|
246
|
-
/** The resolved WebMCP tool name. */
|
|
247
299
|
get resolvedToolName() {
|
|
248
300
|
return this.toolName || `fill_${this.name || this.controlNoun}`;
|
|
249
301
|
}
|
|
250
|
-
|
|
251
|
-
this.
|
|
302
|
+
get defaultToolDescription() {
|
|
303
|
+
return `Set the value of the "${this.label || this.name || this.controlNoun}" field.`;
|
|
304
|
+
}
|
|
305
|
+
get toolReactiveProps() {
|
|
306
|
+
return [...super.toolReactiveProps, "name"];
|
|
307
|
+
}
|
|
308
|
+
async executeTool(args) {
|
|
252
309
|
const noun = this.label || this.name || this.controlNoun;
|
|
253
|
-
this.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
text: this.error ? `Set "${noun}" but validation failed: ${this.error}` : `Set "${noun}" to "${this.stateDescription()}".`
|
|
264
|
-
}
|
|
265
|
-
],
|
|
266
|
-
isError: Boolean(this.error)
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
});
|
|
310
|
+
await this.applyAgentValue(args);
|
|
311
|
+
return {
|
|
312
|
+
content: [
|
|
313
|
+
{
|
|
314
|
+
type: "text",
|
|
315
|
+
text: this.error ? `Set "${noun}" but validation failed: ${this.error}` : `Set "${noun}" to "${this.stateDescription()}".`
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
isError: Boolean(this.error)
|
|
319
|
+
};
|
|
270
320
|
}
|
|
271
321
|
/**
|
|
272
322
|
* Apply the agent's tool arguments to component state. Defaults to treating
|
|
@@ -370,40 +420,31 @@ var WmcpFormControl = class extends LitElement {
|
|
|
370
420
|
}
|
|
371
421
|
};
|
|
372
422
|
__decorateClass([
|
|
373
|
-
|
|
423
|
+
property2()
|
|
374
424
|
], WmcpFormControl.prototype, "label", 2);
|
|
375
425
|
__decorateClass([
|
|
376
|
-
|
|
426
|
+
property2()
|
|
377
427
|
], WmcpFormControl.prototype, "name", 2);
|
|
378
428
|
__decorateClass([
|
|
379
|
-
|
|
429
|
+
property2()
|
|
380
430
|
], WmcpFormControl.prototype, "value", 2);
|
|
381
431
|
__decorateClass([
|
|
382
|
-
|
|
432
|
+
property2()
|
|
383
433
|
], WmcpFormControl.prototype, "placeholder", 2);
|
|
384
434
|
__decorateClass([
|
|
385
|
-
|
|
435
|
+
property2({ type: Boolean, reflect: true })
|
|
386
436
|
], WmcpFormControl.prototype, "required", 2);
|
|
387
437
|
__decorateClass([
|
|
388
|
-
|
|
438
|
+
property2({ type: Boolean, reflect: true })
|
|
389
439
|
], WmcpFormControl.prototype, "disabled", 2);
|
|
390
440
|
__decorateClass([
|
|
391
|
-
|
|
441
|
+
property2({ attribute: "helper-text" })
|
|
392
442
|
], WmcpFormControl.prototype, "helperText", 2);
|
|
393
443
|
__decorateClass([
|
|
394
|
-
|
|
444
|
+
property2({ attribute: "required-message" })
|
|
395
445
|
], WmcpFormControl.prototype, "requiredMessage", 2);
|
|
396
446
|
__decorateClass([
|
|
397
|
-
|
|
398
|
-
], WmcpFormControl.prototype, "expose", 2);
|
|
399
|
-
__decorateClass([
|
|
400
|
-
property({ attribute: "tool-name" })
|
|
401
|
-
], WmcpFormControl.prototype, "toolName", 2);
|
|
402
|
-
__decorateClass([
|
|
403
|
-
property({ attribute: "tool-description" })
|
|
404
|
-
], WmcpFormControl.prototype, "toolDescription", 2);
|
|
405
|
-
__decorateClass([
|
|
406
|
-
property({ attribute: false })
|
|
447
|
+
property2({ attribute: false })
|
|
407
448
|
], WmcpFormControl.prototype, "schema", 2);
|
|
408
449
|
__decorateClass([
|
|
409
450
|
state()
|
|
@@ -415,7 +456,7 @@ import {
|
|
|
415
456
|
css as css2,
|
|
416
457
|
nothing as nothing2
|
|
417
458
|
} from "lit";
|
|
418
|
-
import { property as
|
|
459
|
+
import { property as property3 } from "lit/decorators.js";
|
|
419
460
|
var WmcpInput = class extends WmcpFormControl {
|
|
420
461
|
constructor() {
|
|
421
462
|
super(...arguments);
|
|
@@ -470,7 +511,7 @@ var WmcpInput = class extends WmcpFormControl {
|
|
|
470
511
|
}
|
|
471
512
|
};
|
|
472
513
|
__decorateClass([
|
|
473
|
-
|
|
514
|
+
property3()
|
|
474
515
|
], WmcpInput.prototype, "type", 2);
|
|
475
516
|
|
|
476
517
|
// src/elements/textarea.ts
|
|
@@ -479,7 +520,7 @@ import {
|
|
|
479
520
|
css as css3,
|
|
480
521
|
nothing as nothing3
|
|
481
522
|
} from "lit";
|
|
482
|
-
import { property as
|
|
523
|
+
import { property as property4 } from "lit/decorators.js";
|
|
483
524
|
var WmcpTextarea = class extends WmcpFormControl {
|
|
484
525
|
constructor() {
|
|
485
526
|
super(...arguments);
|
|
@@ -522,7 +563,7 @@ var WmcpTextarea = class extends WmcpFormControl {
|
|
|
522
563
|
}
|
|
523
564
|
};
|
|
524
565
|
__decorateClass([
|
|
525
|
-
|
|
566
|
+
property4({ type: Number })
|
|
526
567
|
], WmcpTextarea.prototype, "rows", 2);
|
|
527
568
|
|
|
528
569
|
// src/elements/select.ts
|
|
@@ -531,7 +572,7 @@ import {
|
|
|
531
572
|
css as css4,
|
|
532
573
|
nothing as nothing4
|
|
533
574
|
} from "lit";
|
|
534
|
-
import { property as
|
|
575
|
+
import { property as property5, state as state2 } from "lit/decorators.js";
|
|
535
576
|
import { repeat } from "lit/directives/repeat.js";
|
|
536
577
|
function isGroup(item) {
|
|
537
578
|
return Array.isArray(item.options);
|
|
@@ -662,7 +703,7 @@ var WmcpSelect = class extends WmcpFormControl {
|
|
|
662
703
|
}
|
|
663
704
|
};
|
|
664
705
|
__decorateClass([
|
|
665
|
-
|
|
706
|
+
property5({ attribute: false })
|
|
666
707
|
], WmcpSelect.prototype, "options", 2);
|
|
667
708
|
__decorateClass([
|
|
668
709
|
state2()
|
|
@@ -674,7 +715,7 @@ import {
|
|
|
674
715
|
css as css5,
|
|
675
716
|
nothing as nothing5
|
|
676
717
|
} from "lit";
|
|
677
|
-
import { property as
|
|
718
|
+
import { property as property6 } from "lit/decorators.js";
|
|
678
719
|
var WmcpCheckbox = class extends WmcpFormControl {
|
|
679
720
|
constructor() {
|
|
680
721
|
super();
|
|
@@ -801,7 +842,7 @@ var WmcpCheckbox = class extends WmcpFormControl {
|
|
|
801
842
|
}
|
|
802
843
|
};
|
|
803
844
|
__decorateClass([
|
|
804
|
-
|
|
845
|
+
property6({ type: Boolean, reflect: true })
|
|
805
846
|
], WmcpCheckbox.prototype, "checked", 2);
|
|
806
847
|
|
|
807
848
|
// src/elements/radio.ts
|
|
@@ -810,7 +851,7 @@ import {
|
|
|
810
851
|
css as css6,
|
|
811
852
|
nothing as nothing6
|
|
812
853
|
} from "lit";
|
|
813
|
-
import { property as
|
|
854
|
+
import { property as property7, state as state3 } from "lit/decorators.js";
|
|
814
855
|
import { repeat as repeat2 } from "lit/directives/repeat.js";
|
|
815
856
|
var groupCounter = 0;
|
|
816
857
|
var WmcpRadio = class extends HTMLElement {
|
|
@@ -982,20 +1023,1553 @@ var WmcpRadioGroup = class extends WmcpFormControl {
|
|
|
982
1023
|
}
|
|
983
1024
|
};
|
|
984
1025
|
__decorateClass([
|
|
985
|
-
|
|
1026
|
+
property7({ attribute: false })
|
|
986
1027
|
], WmcpRadioGroup.prototype, "options", 2);
|
|
987
1028
|
__decorateClass([
|
|
988
1029
|
state3()
|
|
989
1030
|
], WmcpRadioGroup.prototype, "resolvedOptions", 2);
|
|
990
1031
|
|
|
991
|
-
// src/
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1032
|
+
// src/elements/action.ts
|
|
1033
|
+
import { property as property8 } from "lit/decorators.js";
|
|
1034
|
+
var WmcpAction = class extends WmcpExposable {
|
|
1035
|
+
constructor() {
|
|
1036
|
+
super(...arguments);
|
|
1037
|
+
this.name = "";
|
|
1038
|
+
this.label = "";
|
|
1039
|
+
}
|
|
1040
|
+
/** Suffix for the default tool name when `name` is empty. */
|
|
1041
|
+
get defaultNameSuffix() {
|
|
1042
|
+
return "element";
|
|
1043
|
+
}
|
|
1044
|
+
/** Human-readable noun for the thing being acted on. */
|
|
1045
|
+
get actionNoun() {
|
|
1046
|
+
return this.label || this.name || this.defaultNameSuffix;
|
|
1047
|
+
}
|
|
1048
|
+
get resolvedToolName() {
|
|
1049
|
+
return this.toolName || `${this.actionVerb}_${this.name || this.defaultNameSuffix}`;
|
|
1050
|
+
}
|
|
1051
|
+
get defaultToolDescription() {
|
|
1052
|
+
return `Activate the "${this.actionNoun}".`;
|
|
1053
|
+
}
|
|
1054
|
+
get toolReactiveProps() {
|
|
1055
|
+
return [...super.toolReactiveProps, "name", "label"];
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
__decorateClass([
|
|
1059
|
+
property8()
|
|
1060
|
+
], WmcpAction.prototype, "name", 2);
|
|
1061
|
+
__decorateClass([
|
|
1062
|
+
property8()
|
|
1063
|
+
], WmcpAction.prototype, "label", 2);
|
|
1064
|
+
|
|
1065
|
+
// src/elements/button.ts
|
|
1066
|
+
import {
|
|
1067
|
+
LitElement as LitElement2,
|
|
1068
|
+
html as html7,
|
|
1069
|
+
css as css7,
|
|
1070
|
+
nothing as nothing7
|
|
1071
|
+
} from "lit";
|
|
1072
|
+
import { property as property9 } from "lit/decorators.js";
|
|
1073
|
+
var WmcpButton = class extends WmcpAction {
|
|
1074
|
+
constructor() {
|
|
1075
|
+
super(...arguments);
|
|
1076
|
+
this.variant = "primary";
|
|
1077
|
+
this.size = "md";
|
|
1078
|
+
this.type = "button";
|
|
1079
|
+
this.disabled = false;
|
|
1080
|
+
this.internals = this.attachInternals();
|
|
1081
|
+
}
|
|
1082
|
+
static {
|
|
1083
|
+
this.tagName = "wmcp-button";
|
|
1084
|
+
}
|
|
1085
|
+
static {
|
|
1086
|
+
this.formAssociated = true;
|
|
1087
|
+
}
|
|
1088
|
+
static {
|
|
1089
|
+
// Delegate focus to the inner native button so the host is keyboard- and
|
|
1090
|
+
// agent-focusable and `:focus-visible` lands on the real control.
|
|
1091
|
+
this.shadowRootOptions = {
|
|
1092
|
+
...LitElement2.shadowRootOptions,
|
|
1093
|
+
delegatesFocus: true
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
static {
|
|
1097
|
+
this.styles = css7`
|
|
1098
|
+
:host {
|
|
1099
|
+
display: inline-block;
|
|
1100
|
+
font-family: var(
|
|
1101
|
+
--button-font-family,
|
|
1102
|
+
var(--input-font-family, ui-sans-serif, system-ui, sans-serif)
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
:host([hidden]) {
|
|
1106
|
+
display: none;
|
|
1107
|
+
}
|
|
1108
|
+
.control {
|
|
1109
|
+
box-sizing: border-box;
|
|
1110
|
+
display: inline-flex;
|
|
1111
|
+
align-items: center;
|
|
1112
|
+
justify-content: center;
|
|
1113
|
+
gap: var(--button-gap, 0.5rem);
|
|
1114
|
+
width: 100%;
|
|
1115
|
+
font-family: inherit;
|
|
1116
|
+
font-size: var(--button-font-size, 0.875rem);
|
|
1117
|
+
font-weight: var(--button-font-weight, 500);
|
|
1118
|
+
line-height: 1;
|
|
1119
|
+
white-space: nowrap;
|
|
1120
|
+
cursor: pointer;
|
|
1121
|
+
user-select: none;
|
|
1122
|
+
border: var(--button-border-width, 1px) solid transparent;
|
|
1123
|
+
border-radius: var(--button-radius, var(--radius, 0.625rem));
|
|
1124
|
+
transition: background-color var(--button-transition-duration, 150ms)
|
|
1125
|
+
var(--button-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
1126
|
+
color var(--button-transition-duration, 150ms)
|
|
1127
|
+
var(--button-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
1128
|
+
border-color var(--button-transition-duration, 150ms)
|
|
1129
|
+
var(--button-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
1130
|
+
box-shadow var(--button-transition-duration, 150ms)
|
|
1131
|
+
var(--button-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
1132
|
+
}
|
|
1133
|
+
/* Sizes */
|
|
1134
|
+
:host([size='sm']) .control {
|
|
1135
|
+
height: var(--button-height-sm, 2rem);
|
|
1136
|
+
padding-inline: var(--button-padding-x-sm, 0.75rem);
|
|
1137
|
+
}
|
|
1138
|
+
.control {
|
|
1139
|
+
height: var(--button-height-md, 2.25rem);
|
|
1140
|
+
padding-inline: var(--button-padding-x-md, 1rem);
|
|
1141
|
+
}
|
|
1142
|
+
:host([size='lg']) .control {
|
|
1143
|
+
height: var(--button-height-lg, 2.5rem);
|
|
1144
|
+
padding-inline: var(--button-padding-x-lg, 1.5rem);
|
|
1145
|
+
}
|
|
1146
|
+
/* Primary (default) */
|
|
1147
|
+
.control {
|
|
1148
|
+
color: var(--button-primary-text, var(--primary-foreground, oklch(0.985 0 0)));
|
|
1149
|
+
background: var(--button-primary-bg, var(--primary, oklch(0.205 0 0)));
|
|
1150
|
+
}
|
|
1151
|
+
.control:hover:not(:disabled) {
|
|
1152
|
+
background: var(
|
|
1153
|
+
--button-primary-bg-hover,
|
|
1154
|
+
color-mix(in oklch, var(--primary, oklch(0.205 0 0)) 90%, transparent)
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
/* Secondary */
|
|
1158
|
+
:host([variant='secondary']) .control {
|
|
1159
|
+
color: var(--button-secondary-text, var(--secondary-foreground, oklch(0.205 0 0)));
|
|
1160
|
+
background: var(--button-secondary-bg, var(--secondary, oklch(0.97 0 0)));
|
|
1161
|
+
}
|
|
1162
|
+
:host([variant='secondary']) .control:hover:not(:disabled) {
|
|
1163
|
+
background: var(
|
|
1164
|
+
--button-secondary-bg-hover,
|
|
1165
|
+
color-mix(in oklch, var(--secondary, oklch(0.97 0 0)) 80%, var(--foreground, oklch(0.145 0 0)))
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
/* Destructive */
|
|
1169
|
+
:host([variant='destructive']) .control {
|
|
1170
|
+
color: var(--button-destructive-text, var(--destructive-foreground, oklch(0.985 0 0)));
|
|
1171
|
+
background: var(--button-destructive-bg, var(--destructive, oklch(0.577 0.245 27.325)));
|
|
1172
|
+
}
|
|
1173
|
+
:host([variant='destructive']) .control:hover:not(:disabled) {
|
|
1174
|
+
background: var(
|
|
1175
|
+
--button-destructive-bg-hover,
|
|
1176
|
+
color-mix(in oklch, var(--destructive, oklch(0.577 0.245 27.325)) 90%, transparent)
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
/* Outline */
|
|
1180
|
+
:host([variant='outline']) .control {
|
|
1181
|
+
color: var(--button-outline-text, var(--foreground, oklch(0.145 0 0)));
|
|
1182
|
+
background: var(--button-outline-bg, var(--background, oklch(1 0 0)));
|
|
1183
|
+
border-color: var(--button-outline-border, var(--border, oklch(0.922 0 0)));
|
|
1184
|
+
}
|
|
1185
|
+
:host([variant='outline']) .control:hover:not(:disabled) {
|
|
1186
|
+
background: var(--button-outline-bg-hover, var(--accent, oklch(0.97 0 0)));
|
|
1187
|
+
color: var(--button-outline-text-hover, var(--accent-foreground, oklch(0.205 0 0)));
|
|
1188
|
+
}
|
|
1189
|
+
/* Ghost */
|
|
1190
|
+
:host([variant='ghost']) .control {
|
|
1191
|
+
color: var(--button-ghost-text, var(--foreground, oklch(0.145 0 0)));
|
|
1192
|
+
background: transparent;
|
|
1193
|
+
}
|
|
1194
|
+
:host([variant='ghost']) .control:hover:not(:disabled) {
|
|
1195
|
+
background: var(--button-ghost-bg-hover, var(--accent, oklch(0.97 0 0)));
|
|
1196
|
+
color: var(--button-ghost-text-hover, var(--accent-foreground, oklch(0.205 0 0)));
|
|
1197
|
+
}
|
|
1198
|
+
/* Focus + disabled (shared across variants) */
|
|
1199
|
+
.control:focus-visible {
|
|
1200
|
+
outline: none;
|
|
1201
|
+
box-shadow: 0 0 0 var(--ring-width, 3px)
|
|
1202
|
+
color-mix(in oklch, var(--ring, oklch(0.708 0 0)) 40%, transparent);
|
|
1203
|
+
}
|
|
1204
|
+
.control:disabled {
|
|
1205
|
+
opacity: var(--button-disabled-opacity, 0.5);
|
|
1206
|
+
cursor: not-allowed;
|
|
1207
|
+
}
|
|
1208
|
+
`;
|
|
1209
|
+
}
|
|
1210
|
+
/** The inner native `<button>` that owns activation, focus, and a11y. */
|
|
1211
|
+
get button() {
|
|
1212
|
+
return this.renderRoot?.querySelector("button") ?? null;
|
|
1213
|
+
}
|
|
1214
|
+
// --- WmcpAction hooks ---------------------------------------------------
|
|
1215
|
+
get actionVerb() {
|
|
1216
|
+
return "click";
|
|
1217
|
+
}
|
|
1218
|
+
get defaultNameSuffix() {
|
|
1219
|
+
return "button";
|
|
1220
|
+
}
|
|
1221
|
+
// A button's visible name is its slotted text, so fall back to that.
|
|
1222
|
+
get actionNoun() {
|
|
1223
|
+
return this.label || this.textContent?.trim() || this.name || "button";
|
|
1224
|
+
}
|
|
1225
|
+
get defaultToolDescription() {
|
|
1226
|
+
return `Click the "${this.actionNoun}" button.`;
|
|
1227
|
+
}
|
|
1228
|
+
executeTool() {
|
|
1229
|
+
const noun = this.actionNoun;
|
|
1230
|
+
if (this.disabled) {
|
|
1231
|
+
return {
|
|
1232
|
+
content: [
|
|
1233
|
+
{
|
|
1234
|
+
type: "text",
|
|
1235
|
+
text: `The "${noun}" button is disabled and can't be activated.`
|
|
1236
|
+
}
|
|
1237
|
+
],
|
|
1238
|
+
isError: true
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
this.activate();
|
|
1242
|
+
const effect = this.type === "submit" ? " (submitted the form)" : this.type === "reset" ? " (reset the form)" : "";
|
|
1243
|
+
return {
|
|
1244
|
+
content: [
|
|
1245
|
+
{ type: "text", text: `Clicked the "${noun}" button${effect}.` }
|
|
1246
|
+
]
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
// --- Activation ---------------------------------------------------------
|
|
1250
|
+
/**
|
|
1251
|
+
* Activate the button as if a human clicked it: routes through the inner
|
|
1252
|
+
* native button so light-DOM `click` handlers fire and `type`-driven form
|
|
1253
|
+
* submission/reset happens. A no-op when disabled.
|
|
1254
|
+
*/
|
|
1255
|
+
activate() {
|
|
1256
|
+
this.button?.click();
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Inner-button click handler. The native click already bubbles (composed) to
|
|
1260
|
+
* the host, so light-DOM listeners fire without help; this only adds the
|
|
1261
|
+
* cross-shadow form behavior a native submit/reset button can't do from here.
|
|
1262
|
+
*/
|
|
1263
|
+
onInnerClick() {
|
|
1264
|
+
if (this.disabled) return;
|
|
1265
|
+
if (this.type === "submit") {
|
|
1266
|
+
this.internals.form?.requestSubmit();
|
|
1267
|
+
} else if (this.type === "reset") {
|
|
1268
|
+
this.internals.form?.reset();
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
render() {
|
|
1272
|
+
return html7`
|
|
1273
|
+
<button
|
|
1274
|
+
class="control"
|
|
1275
|
+
part="control"
|
|
1276
|
+
type="button"
|
|
1277
|
+
?disabled=${this.disabled}
|
|
1278
|
+
aria-label=${this.label || nothing7}
|
|
1279
|
+
@click=${this.onInnerClick}
|
|
1280
|
+
>
|
|
1281
|
+
<slot></slot>
|
|
1282
|
+
</button>
|
|
1283
|
+
`;
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
__decorateClass([
|
|
1287
|
+
property9({ reflect: true })
|
|
1288
|
+
], WmcpButton.prototype, "variant", 2);
|
|
1289
|
+
__decorateClass([
|
|
1290
|
+
property9({ reflect: true })
|
|
1291
|
+
], WmcpButton.prototype, "size", 2);
|
|
1292
|
+
__decorateClass([
|
|
1293
|
+
property9()
|
|
1294
|
+
], WmcpButton.prototype, "type", 2);
|
|
1295
|
+
__decorateClass([
|
|
1296
|
+
property9({ type: Boolean, reflect: true })
|
|
1297
|
+
], WmcpButton.prototype, "disabled", 2);
|
|
1298
|
+
|
|
1299
|
+
// src/elements/dialog.ts
|
|
1300
|
+
import {
|
|
1301
|
+
html as html8,
|
|
1302
|
+
css as css8
|
|
1303
|
+
} from "lit";
|
|
1304
|
+
import { property as property10 } from "lit/decorators.js";
|
|
1305
|
+
var WmcpDialog = class extends WmcpAction {
|
|
1306
|
+
constructor() {
|
|
1307
|
+
super(...arguments);
|
|
1308
|
+
this.open = false;
|
|
1309
|
+
this.modal = true;
|
|
1310
|
+
this.staticBackdrop = false;
|
|
1311
|
+
/** Last `returnValue` the dialog closed with (mirrors native `<dialog>`). */
|
|
1312
|
+
this.returnValue = "";
|
|
1313
|
+
}
|
|
1314
|
+
static {
|
|
1315
|
+
this.tagName = "wmcp-dialog";
|
|
1316
|
+
}
|
|
1317
|
+
static {
|
|
1318
|
+
this.styles = css8`
|
|
1319
|
+
:host {
|
|
1320
|
+
display: contents;
|
|
1321
|
+
}
|
|
1322
|
+
dialog {
|
|
1323
|
+
box-sizing: border-box;
|
|
1324
|
+
width: var(--dialog-width, min(92vw, 32rem));
|
|
1325
|
+
max-width: var(--dialog-max-width, 32rem);
|
|
1326
|
+
max-height: var(--dialog-max-height, 85dvh);
|
|
1327
|
+
color: var(--dialog-text, var(--popover-foreground, oklch(0.145 0 0)));
|
|
1328
|
+
background: var(--dialog-bg, var(--popover, oklch(1 0 0)));
|
|
1329
|
+
border: var(--dialog-border-width, 1px) solid
|
|
1330
|
+
var(--dialog-border, var(--border, oklch(0.922 0 0)));
|
|
1331
|
+
border-radius: var(--dialog-radius, var(--radius, 0.625rem));
|
|
1332
|
+
box-shadow: var(
|
|
1333
|
+
--dialog-shadow,
|
|
1334
|
+
0 10px 38px -10px color-mix(in oklch, oklch(0 0 0) 35%, transparent),
|
|
1335
|
+
0 10px 20px -15px color-mix(in oklch, oklch(0 0 0) 20%, transparent)
|
|
1336
|
+
);
|
|
1337
|
+
font-family: var(
|
|
1338
|
+
--dialog-font-family,
|
|
1339
|
+
ui-sans-serif,
|
|
1340
|
+
system-ui,
|
|
1341
|
+
sans-serif
|
|
1342
|
+
);
|
|
1343
|
+
overflow: auto;
|
|
1344
|
+
}
|
|
1345
|
+
dialog::backdrop {
|
|
1346
|
+
background: var(
|
|
1347
|
+
--dialog-backdrop,
|
|
1348
|
+
color-mix(in oklch, oklch(0 0 0) 50%, transparent)
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
/* Padding lives on the panel, not the dialog, so the dialog's box is the
|
|
1352
|
+
panel's box — a click whose target is the <dialog> is unambiguously the
|
|
1353
|
+
backdrop, never the dialog's own visible frame. */
|
|
1354
|
+
.panel {
|
|
1355
|
+
padding: var(--dialog-padding, 1.5rem);
|
|
1356
|
+
}
|
|
1357
|
+
/* Modern-CSS entrance: fade + lift, honoring reduced-motion. */
|
|
1358
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1359
|
+
dialog,
|
|
1360
|
+
dialog::backdrop {
|
|
1361
|
+
transition: opacity var(--dialog-transition-duration, 180ms)
|
|
1362
|
+
var(--dialog-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
1363
|
+
translate var(--dialog-transition-duration, 180ms)
|
|
1364
|
+
var(--dialog-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
1365
|
+
overlay var(--dialog-transition-duration, 180ms) allow-discrete,
|
|
1366
|
+
display var(--dialog-transition-duration, 180ms) allow-discrete;
|
|
1367
|
+
}
|
|
1368
|
+
dialog:not([open]),
|
|
1369
|
+
dialog:not([open])::backdrop {
|
|
1370
|
+
opacity: 0;
|
|
1371
|
+
}
|
|
1372
|
+
dialog:not([open]) {
|
|
1373
|
+
translate: 0 0.5rem;
|
|
1374
|
+
}
|
|
1375
|
+
@starting-style {
|
|
1376
|
+
dialog[open],
|
|
1377
|
+
dialog[open]::backdrop {
|
|
1378
|
+
opacity: 0;
|
|
1379
|
+
}
|
|
1380
|
+
dialog[open] {
|
|
1381
|
+
translate: 0 0.5rem;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
`;
|
|
1386
|
+
}
|
|
1387
|
+
get dialogEl() {
|
|
1388
|
+
return this.renderRoot?.querySelector("dialog") ?? null;
|
|
1389
|
+
}
|
|
1390
|
+
// --- WmcpAction hooks ---------------------------------------------------
|
|
1391
|
+
get actionVerb() {
|
|
1392
|
+
return "open";
|
|
1393
|
+
}
|
|
1394
|
+
get defaultNameSuffix() {
|
|
1395
|
+
return "dialog";
|
|
1396
|
+
}
|
|
1397
|
+
get defaultToolDescription() {
|
|
1398
|
+
return `Open the "${this.actionNoun}" dialog for the user to review.`;
|
|
1399
|
+
}
|
|
1400
|
+
executeTool() {
|
|
1401
|
+
const noun = this.actionNoun;
|
|
1402
|
+
if (this.open) {
|
|
1403
|
+
return {
|
|
1404
|
+
content: [
|
|
1405
|
+
{ type: "text", text: `The "${noun}" dialog is already open.` }
|
|
1406
|
+
]
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
this.show();
|
|
1410
|
+
return {
|
|
1411
|
+
content: [
|
|
1412
|
+
{
|
|
1413
|
+
type: "text",
|
|
1414
|
+
text: `Opened the "${noun}" dialog for the user to review.`
|
|
1415
|
+
}
|
|
1416
|
+
]
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
// --- Open / close -------------------------------------------------------
|
|
1420
|
+
updated(changed) {
|
|
1421
|
+
super.updated(changed);
|
|
1422
|
+
if (changed.has("open")) this.syncNativeOpen();
|
|
1423
|
+
}
|
|
1424
|
+
/** Open the dialog (modal unless `modal` is false). */
|
|
1425
|
+
show() {
|
|
1426
|
+
this.open = true;
|
|
1427
|
+
}
|
|
1428
|
+
/** Close the dialog, optionally recording a `returnValue`. */
|
|
1429
|
+
close(returnValue) {
|
|
1430
|
+
if (returnValue !== void 0) this.returnValue = returnValue;
|
|
1431
|
+
this.open = false;
|
|
1432
|
+
}
|
|
1433
|
+
/** Reconcile our `open` state with the native dialog's imperative API. */
|
|
1434
|
+
syncNativeOpen() {
|
|
1435
|
+
const d = this.dialogEl;
|
|
1436
|
+
if (!d) return;
|
|
1437
|
+
if (this.open && !d.open) {
|
|
1438
|
+
if (this.modal) d.showModal();
|
|
1439
|
+
else d.show();
|
|
1440
|
+
this.dispatchEvent(new Event("open", { bubbles: true, composed: true }));
|
|
1441
|
+
} else if (!this.open && d.open) {
|
|
1442
|
+
d.close(this.returnValue);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/** Native `close` fires for Escape, backdrop, or our own `close()`. */
|
|
1446
|
+
onNativeClose() {
|
|
1447
|
+
this.returnValue = this.dialogEl?.returnValue ?? this.returnValue;
|
|
1448
|
+
if (this.open) this.open = false;
|
|
1449
|
+
this.dispatchEvent(new Event("close", { bubbles: true, composed: true }));
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Close on a backdrop click. A click whose target is the `<dialog>` itself
|
|
1453
|
+
* is the backdrop — content clicks retarget to `.panel` or its children — so
|
|
1454
|
+
* this never fires on the dialog's own frame or a text drag-select.
|
|
1455
|
+
*/
|
|
1456
|
+
onDialogClick(event) {
|
|
1457
|
+
if (this.staticBackdrop) return;
|
|
1458
|
+
if (event.target === this.dialogEl) this.close();
|
|
1459
|
+
}
|
|
1460
|
+
render() {
|
|
1461
|
+
return html8`
|
|
1462
|
+
<dialog
|
|
1463
|
+
part="dialog"
|
|
1464
|
+
aria-label=${this.label || this.actionNoun}
|
|
1465
|
+
@close=${this.onNativeClose}
|
|
1466
|
+
@click=${this.onDialogClick}
|
|
1467
|
+
>
|
|
1468
|
+
<div class="panel" part="panel">
|
|
1469
|
+
<slot></slot>
|
|
1470
|
+
</div>
|
|
1471
|
+
</dialog>
|
|
1472
|
+
`;
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
__decorateClass([
|
|
1476
|
+
property10({ type: Boolean, reflect: true })
|
|
1477
|
+
], WmcpDialog.prototype, "open", 2);
|
|
1478
|
+
__decorateClass([
|
|
1479
|
+
property10({ type: Boolean })
|
|
1480
|
+
], WmcpDialog.prototype, "modal", 2);
|
|
1481
|
+
__decorateClass([
|
|
1482
|
+
property10({ type: Boolean, attribute: "static-backdrop" })
|
|
1483
|
+
], WmcpDialog.prototype, "staticBackdrop", 2);
|
|
1484
|
+
|
|
1485
|
+
// src/elements/menu.ts
|
|
1486
|
+
import {
|
|
1487
|
+
html as html9,
|
|
1488
|
+
css as css9,
|
|
1489
|
+
nothing as nothing8
|
|
1490
|
+
} from "lit";
|
|
1491
|
+
import { property as property11, state as state4 } from "lit/decorators.js";
|
|
1492
|
+
import { repeat as repeat3 } from "lit/directives/repeat.js";
|
|
1493
|
+
var WmcpMenu = class extends WmcpAction {
|
|
1494
|
+
constructor() {
|
|
1495
|
+
super(...arguments);
|
|
1496
|
+
this.label = "";
|
|
1497
|
+
this.items = [];
|
|
1498
|
+
this.open = false;
|
|
1499
|
+
this.resolvedItems = [];
|
|
1500
|
+
}
|
|
1501
|
+
static {
|
|
1502
|
+
this.tagName = "wmcp-menu";
|
|
1503
|
+
}
|
|
1504
|
+
static {
|
|
1505
|
+
this.styles = css9`
|
|
1506
|
+
:host {
|
|
1507
|
+
display: inline-block;
|
|
1508
|
+
font-family: var(
|
|
1509
|
+
--menu-font-family,
|
|
1510
|
+
var(--input-font-family, ui-sans-serif, system-ui, sans-serif)
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
:host([hidden]) {
|
|
1514
|
+
display: none;
|
|
1515
|
+
}
|
|
1516
|
+
.trigger {
|
|
1517
|
+
box-sizing: border-box;
|
|
1518
|
+
display: inline-flex;
|
|
1519
|
+
align-items: center;
|
|
1520
|
+
gap: var(--menu-trigger-gap, 0.375rem);
|
|
1521
|
+
height: var(--menu-trigger-height, 2.25rem);
|
|
1522
|
+
padding-inline: var(--menu-trigger-padding-x, 1rem);
|
|
1523
|
+
font: inherit;
|
|
1524
|
+
font-size: var(--menu-font-size, 0.875rem);
|
|
1525
|
+
font-weight: var(--menu-trigger-font-weight, 500);
|
|
1526
|
+
color: var(--menu-trigger-text, var(--foreground, oklch(0.145 0 0)));
|
|
1527
|
+
background: var(--menu-trigger-bg, var(--background, oklch(1 0 0)));
|
|
1528
|
+
border: 1px solid var(--menu-trigger-border, var(--border, oklch(0.922 0 0)));
|
|
1529
|
+
border-radius: var(--menu-radius, var(--radius, 0.625rem));
|
|
1530
|
+
cursor: pointer;
|
|
1531
|
+
anchor-name: --wmcp-menu-anchor;
|
|
1532
|
+
}
|
|
1533
|
+
.trigger:hover {
|
|
1534
|
+
background: var(--menu-trigger-bg-hover, var(--accent, oklch(0.97 0 0)));
|
|
1535
|
+
}
|
|
1536
|
+
.trigger:focus-visible {
|
|
1537
|
+
outline: none;
|
|
1538
|
+
box-shadow: 0 0 0 var(--ring-width, 3px)
|
|
1539
|
+
color-mix(in oklch, var(--ring, oklch(0.708 0 0)) 40%, transparent);
|
|
1540
|
+
}
|
|
1541
|
+
.caret {
|
|
1542
|
+
width: 0.75rem;
|
|
1543
|
+
height: 0.75rem;
|
|
1544
|
+
transition: rotate 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
1545
|
+
}
|
|
1546
|
+
.trigger[aria-expanded='true'] .caret {
|
|
1547
|
+
rotate: 180deg;
|
|
1548
|
+
}
|
|
1549
|
+
.menu {
|
|
1550
|
+
box-sizing: border-box;
|
|
1551
|
+
min-width: var(--menu-min-width, 11rem);
|
|
1552
|
+
margin: 0;
|
|
1553
|
+
padding: var(--menu-padding, 0.25rem);
|
|
1554
|
+
color: var(--menu-text, var(--popover-foreground, oklch(0.145 0 0)));
|
|
1555
|
+
background: var(--menu-bg, var(--popover, oklch(1 0 0)));
|
|
1556
|
+
border: 1px solid var(--menu-border, var(--border, oklch(0.922 0 0)));
|
|
1557
|
+
border-radius: var(--menu-radius, var(--radius, 0.625rem));
|
|
1558
|
+
box-shadow: var(
|
|
1559
|
+
--menu-shadow,
|
|
1560
|
+
0 10px 38px -10px color-mix(in oklch, oklch(0 0 0) 35%, transparent)
|
|
1561
|
+
);
|
|
1562
|
+
/* Anchor the popover under the trigger; margin is the no-anchor fallback. */
|
|
1563
|
+
position-anchor: --wmcp-menu-anchor;
|
|
1564
|
+
inset: auto;
|
|
1565
|
+
top: anchor(bottom);
|
|
1566
|
+
left: anchor(left);
|
|
1567
|
+
margin-top: var(--menu-offset, 0.25rem);
|
|
1568
|
+
}
|
|
1569
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1570
|
+
.menu {
|
|
1571
|
+
transition: opacity 130ms ease, translate 130ms ease,
|
|
1572
|
+
overlay 130ms allow-discrete, display 130ms allow-discrete;
|
|
1573
|
+
}
|
|
1574
|
+
.menu:not(:popover-open) {
|
|
1575
|
+
opacity: 0;
|
|
1576
|
+
translate: 0 -0.25rem;
|
|
1577
|
+
}
|
|
1578
|
+
@starting-style {
|
|
1579
|
+
.menu:popover-open {
|
|
1580
|
+
opacity: 0;
|
|
1581
|
+
translate: 0 -0.25rem;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
.item {
|
|
1586
|
+
display: flex;
|
|
1587
|
+
width: 100%;
|
|
1588
|
+
align-items: center;
|
|
1589
|
+
gap: var(--menu-item-gap, 0.5rem);
|
|
1590
|
+
padding: var(--menu-item-padding, 0.4rem 0.6rem);
|
|
1591
|
+
font: inherit;
|
|
1592
|
+
font-size: var(--menu-font-size, 0.875rem);
|
|
1593
|
+
color: inherit;
|
|
1594
|
+
text-align: start;
|
|
1595
|
+
background: transparent;
|
|
1596
|
+
border: 0;
|
|
1597
|
+
border-radius: var(--menu-item-radius, 0.4rem);
|
|
1598
|
+
cursor: pointer;
|
|
1599
|
+
}
|
|
1600
|
+
.item:hover:not(:disabled),
|
|
1601
|
+
.item:focus-visible {
|
|
1602
|
+
outline: none;
|
|
1603
|
+
background: var(--menu-item-bg-hover, var(--accent, oklch(0.97 0 0)));
|
|
1604
|
+
color: var(--menu-item-text-hover, var(--accent-foreground, oklch(0.205 0 0)));
|
|
1605
|
+
}
|
|
1606
|
+
.item:disabled {
|
|
1607
|
+
opacity: var(--menu-item-disabled-opacity, 0.5);
|
|
1608
|
+
cursor: not-allowed;
|
|
1609
|
+
}
|
|
1610
|
+
`;
|
|
1611
|
+
}
|
|
1612
|
+
get menuEl() {
|
|
1613
|
+
return this.renderRoot?.querySelector(".menu") ?? null;
|
|
1614
|
+
}
|
|
1615
|
+
get itemButtons() {
|
|
1616
|
+
return Array.from(
|
|
1617
|
+
this.renderRoot?.querySelectorAll(".item") ?? []
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
// --- WmcpAction hooks ---------------------------------------------------
|
|
1621
|
+
get actionVerb() {
|
|
1622
|
+
return "select";
|
|
1623
|
+
}
|
|
1624
|
+
get defaultNameSuffix() {
|
|
1625
|
+
return "menu";
|
|
1626
|
+
}
|
|
1627
|
+
get actionNoun() {
|
|
1628
|
+
return this.label || this.name || "menu";
|
|
1629
|
+
}
|
|
1630
|
+
get defaultToolDescription() {
|
|
1631
|
+
return `Select an item from the "${this.actionNoun}" menu.`;
|
|
1632
|
+
}
|
|
1633
|
+
// Re-register the tool when the item set changes so its enum stays in sync.
|
|
1634
|
+
get toolReactiveProps() {
|
|
1635
|
+
return [...super.toolReactiveProps, "resolvedItems"];
|
|
1636
|
+
}
|
|
1637
|
+
toolInputSchema() {
|
|
1638
|
+
return {
|
|
1639
|
+
type: "object",
|
|
1640
|
+
properties: {
|
|
1641
|
+
item: {
|
|
1642
|
+
type: "string",
|
|
1643
|
+
enum: this.resolvedItems.filter((i) => !i.disabled).map((i) => i.value),
|
|
1644
|
+
description: `Which item to select from the "${this.actionNoun}" menu.`
|
|
1645
|
+
}
|
|
1646
|
+
},
|
|
1647
|
+
required: ["item"]
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
executeTool(args) {
|
|
1651
|
+
const value = args.item == null ? "" : String(args.item);
|
|
1652
|
+
const item = this.resolvedItems.find((i) => i.value === value);
|
|
1653
|
+
if (!item) {
|
|
1654
|
+
return {
|
|
1655
|
+
content: [
|
|
1656
|
+
{
|
|
1657
|
+
type: "text",
|
|
1658
|
+
text: `No item "${value}" in the "${this.actionNoun}" menu.`
|
|
1659
|
+
}
|
|
1660
|
+
],
|
|
1661
|
+
isError: true
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
if (item.disabled) {
|
|
1665
|
+
return {
|
|
1666
|
+
content: [
|
|
1667
|
+
{ type: "text", text: `The "${item.label}" item is disabled.` }
|
|
1668
|
+
],
|
|
1669
|
+
isError: true
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
this.selectItem(item);
|
|
1673
|
+
return {
|
|
1674
|
+
content: [
|
|
1675
|
+
{
|
|
1676
|
+
type: "text",
|
|
1677
|
+
text: `Selected "${item.label}" from the "${this.actionNoun}" menu.`
|
|
1678
|
+
}
|
|
1679
|
+
]
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
// --- Item model ---------------------------------------------------------
|
|
1683
|
+
connectedCallback() {
|
|
1684
|
+
this.syncItems();
|
|
1685
|
+
super.connectedCallback();
|
|
1686
|
+
this.itemObserver = new MutationObserver(() => this.syncItems());
|
|
1687
|
+
this.itemObserver.observe(this, { childList: true });
|
|
1688
|
+
}
|
|
1689
|
+
disconnectedCallback() {
|
|
1690
|
+
super.disconnectedCallback();
|
|
1691
|
+
this.itemObserver?.disconnect();
|
|
1692
|
+
this.itemObserver = void 0;
|
|
1693
|
+
}
|
|
1694
|
+
willUpdate(changed) {
|
|
1695
|
+
if (changed.has("items")) this.syncItems();
|
|
1696
|
+
}
|
|
1697
|
+
syncItems() {
|
|
1698
|
+
this.resolvedItems = this.items.length > 0 ? this.items : this.readDeclarativeItems();
|
|
1699
|
+
}
|
|
1700
|
+
readDeclarativeItems() {
|
|
1701
|
+
return Array.from(this.querySelectorAll("option")).map((o) => ({
|
|
1702
|
+
value: o.value,
|
|
1703
|
+
label: o.textContent?.trim() || o.value,
|
|
1704
|
+
disabled: o.disabled
|
|
1705
|
+
}));
|
|
1706
|
+
}
|
|
1707
|
+
// --- Selection + keyboard ----------------------------------------------
|
|
1708
|
+
/** Dispatch the selection and close the menu. */
|
|
1709
|
+
selectItem(item) {
|
|
1710
|
+
this.dispatchEvent(
|
|
1711
|
+
new CustomEvent("select", {
|
|
1712
|
+
detail: { value: item.value, label: item.label },
|
|
1713
|
+
bubbles: true,
|
|
1714
|
+
composed: true
|
|
1715
|
+
})
|
|
1716
|
+
);
|
|
1717
|
+
this.menuEl?.hidePopover();
|
|
1718
|
+
}
|
|
1719
|
+
onItemClick(item) {
|
|
1720
|
+
if (item.disabled) return;
|
|
1721
|
+
this.selectItem(item);
|
|
1722
|
+
}
|
|
1723
|
+
/** Sync `open`/`aria-expanded` and move focus into the menu when it opens. */
|
|
1724
|
+
onToggle(event) {
|
|
1725
|
+
this.open = event.newState === "open";
|
|
1726
|
+
if (this.open) {
|
|
1727
|
+
this.itemButtons.find((b) => !b.disabled)?.focus();
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
/** Roving focus among items with the arrow / Home / End keys. */
|
|
1731
|
+
onMenuKeydown(event) {
|
|
1732
|
+
const buttons = this.itemButtons.filter((b) => !b.disabled);
|
|
1733
|
+
if (buttons.length === 0) return;
|
|
1734
|
+
const current = buttons.indexOf(
|
|
1735
|
+
this.shadowRoot?.activeElement
|
|
1736
|
+
);
|
|
1737
|
+
let next = -1;
|
|
1738
|
+
switch (event.key) {
|
|
1739
|
+
case "ArrowDown":
|
|
1740
|
+
next = current < 0 ? 0 : (current + 1) % buttons.length;
|
|
1741
|
+
break;
|
|
1742
|
+
case "ArrowUp":
|
|
1743
|
+
next = current <= 0 ? buttons.length - 1 : current - 1;
|
|
1744
|
+
break;
|
|
1745
|
+
case "Home":
|
|
1746
|
+
next = 0;
|
|
1747
|
+
break;
|
|
1748
|
+
case "End":
|
|
1749
|
+
next = buttons.length - 1;
|
|
1750
|
+
break;
|
|
1751
|
+
default:
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
event.preventDefault();
|
|
1755
|
+
buttons[next]?.focus();
|
|
1756
|
+
}
|
|
1757
|
+
render() {
|
|
1758
|
+
return html9`
|
|
1759
|
+
<button
|
|
1760
|
+
type="button"
|
|
1761
|
+
class="trigger"
|
|
1762
|
+
part="trigger"
|
|
1763
|
+
popovertarget="menu-pop"
|
|
1764
|
+
aria-haspopup="menu"
|
|
1765
|
+
aria-expanded=${this.open ? "true" : "false"}
|
|
1766
|
+
>
|
|
1767
|
+
<span>${this.label || "Menu"}</span>
|
|
1768
|
+
<svg class="caret" viewBox="0 0 16 16" aria-hidden="true">
|
|
1769
|
+
<path
|
|
1770
|
+
d="M4 6l4 4 4-4"
|
|
1771
|
+
fill="none"
|
|
1772
|
+
stroke="currentColor"
|
|
1773
|
+
stroke-width="1.5"
|
|
1774
|
+
stroke-linecap="round"
|
|
1775
|
+
stroke-linejoin="round"
|
|
1776
|
+
/>
|
|
1777
|
+
</svg>
|
|
1778
|
+
</button>
|
|
1779
|
+
<div
|
|
1780
|
+
id="menu-pop"
|
|
1781
|
+
class="menu"
|
|
1782
|
+
part="menu"
|
|
1783
|
+
popover="auto"
|
|
1784
|
+
role="menu"
|
|
1785
|
+
aria-label=${this.label || "Menu"}
|
|
1786
|
+
@toggle=${this.onToggle}
|
|
1787
|
+
@keydown=${this.onMenuKeydown}
|
|
1788
|
+
>
|
|
1789
|
+
${this.resolvedItems.length === 0 ? nothing8 : repeat3(
|
|
1790
|
+
this.resolvedItems,
|
|
1791
|
+
(item) => item.value,
|
|
1792
|
+
(item) => html9`
|
|
1793
|
+
<button
|
|
1794
|
+
type="button"
|
|
1795
|
+
class="item"
|
|
1796
|
+
role="menuitem"
|
|
1797
|
+
part="item"
|
|
1798
|
+
?disabled=${item.disabled ?? false}
|
|
1799
|
+
@click=${() => this.onItemClick(item)}
|
|
1800
|
+
>
|
|
1801
|
+
${item.label}
|
|
1802
|
+
</button>
|
|
1803
|
+
`
|
|
1804
|
+
)}
|
|
1805
|
+
</div>
|
|
1806
|
+
`;
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
__decorateClass([
|
|
1810
|
+
property11()
|
|
1811
|
+
], WmcpMenu.prototype, "label", 2);
|
|
1812
|
+
__decorateClass([
|
|
1813
|
+
property11({ attribute: false })
|
|
1814
|
+
], WmcpMenu.prototype, "items", 2);
|
|
1815
|
+
__decorateClass([
|
|
1816
|
+
property11({ type: Boolean, reflect: true })
|
|
1817
|
+
], WmcpMenu.prototype, "open", 2);
|
|
1818
|
+
__decorateClass([
|
|
1819
|
+
state4()
|
|
1820
|
+
], WmcpMenu.prototype, "resolvedItems", 2);
|
|
1821
|
+
|
|
1822
|
+
// src/elements/tabs.ts
|
|
1823
|
+
import {
|
|
1824
|
+
html as html10,
|
|
1825
|
+
css as css10,
|
|
1826
|
+
nothing as nothing9
|
|
1827
|
+
} from "lit";
|
|
1828
|
+
import { property as property12, state as state5 } from "lit/decorators.js";
|
|
1829
|
+
import { repeat as repeat4 } from "lit/directives/repeat.js";
|
|
1830
|
+
var WmcpTabs = class extends WmcpAction {
|
|
1831
|
+
constructor() {
|
|
1832
|
+
super(...arguments);
|
|
1833
|
+
this.active = "";
|
|
1834
|
+
this.label = "";
|
|
1835
|
+
this.tabs = [];
|
|
1836
|
+
}
|
|
1837
|
+
static {
|
|
1838
|
+
this.tagName = "wmcp-tabs";
|
|
1839
|
+
}
|
|
1840
|
+
static {
|
|
1841
|
+
this.styles = css10`
|
|
1842
|
+
:host {
|
|
1843
|
+
display: block;
|
|
1844
|
+
font-family: var(
|
|
1845
|
+
--tabs-font-family,
|
|
1846
|
+
var(--input-font-family, ui-sans-serif, system-ui, sans-serif)
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
:host([hidden]) {
|
|
1850
|
+
display: none;
|
|
1851
|
+
}
|
|
1852
|
+
[role='tablist'] {
|
|
1853
|
+
display: flex;
|
|
1854
|
+
gap: var(--tabs-gap, 0.25rem);
|
|
1855
|
+
border-bottom: 1px solid var(--tabs-border, var(--border, oklch(0.922 0 0)));
|
|
1856
|
+
}
|
|
1857
|
+
.tab {
|
|
1858
|
+
position: relative;
|
|
1859
|
+
appearance: none;
|
|
1860
|
+
border: 0;
|
|
1861
|
+
background: transparent;
|
|
1862
|
+
padding: var(--tabs-tab-padding, 0.5rem 0.875rem);
|
|
1863
|
+
margin-bottom: -1px;
|
|
1864
|
+
font: inherit;
|
|
1865
|
+
font-size: var(--tabs-font-size, 0.875rem);
|
|
1866
|
+
font-weight: var(--tabs-font-weight, 500);
|
|
1867
|
+
color: var(--tabs-tab-text, var(--muted-foreground, oklch(0.556 0 0)));
|
|
1868
|
+
cursor: pointer;
|
|
1869
|
+
border-bottom: 2px solid transparent;
|
|
1870
|
+
transition: color 150ms ease, border-color 150ms ease;
|
|
1871
|
+
}
|
|
1872
|
+
.tab:hover:not(:disabled) {
|
|
1873
|
+
color: var(--tabs-tab-text-hover, var(--foreground, oklch(0.145 0 0)));
|
|
1874
|
+
}
|
|
1875
|
+
.tab[aria-selected='true'] {
|
|
1876
|
+
color: var(--tabs-tab-text-active, var(--foreground, oklch(0.145 0 0)));
|
|
1877
|
+
border-bottom-color: var(--tabs-indicator, var(--primary, oklch(0.205 0 0)));
|
|
1878
|
+
}
|
|
1879
|
+
.tab:focus-visible {
|
|
1880
|
+
outline: none;
|
|
1881
|
+
border-radius: 0.375rem;
|
|
1882
|
+
box-shadow: 0 0 0 var(--ring-width, 3px)
|
|
1883
|
+
color-mix(in oklch, var(--ring, oklch(0.708 0 0)) 40%, transparent);
|
|
1884
|
+
}
|
|
1885
|
+
.tab:disabled {
|
|
1886
|
+
opacity: var(--tabs-tab-disabled-opacity, 0.5);
|
|
1887
|
+
cursor: not-allowed;
|
|
1888
|
+
}
|
|
1889
|
+
.panels {
|
|
1890
|
+
padding-top: var(--tabs-panel-padding-top, 1rem);
|
|
1891
|
+
}
|
|
1892
|
+
`;
|
|
1893
|
+
}
|
|
1894
|
+
// --- WmcpAction hooks ---------------------------------------------------
|
|
1895
|
+
get actionVerb() {
|
|
1896
|
+
return "switch";
|
|
1897
|
+
}
|
|
1898
|
+
get defaultNameSuffix() {
|
|
1899
|
+
return "tabs";
|
|
1900
|
+
}
|
|
1901
|
+
get actionNoun() {
|
|
1902
|
+
return this.label || this.name || "tabs";
|
|
1903
|
+
}
|
|
1904
|
+
get defaultToolDescription() {
|
|
1905
|
+
return `Switch the active tab in the "${this.actionNoun}" tabs.`;
|
|
1906
|
+
}
|
|
1907
|
+
// The enum tracks the live tab set; the description need not track `active`.
|
|
1908
|
+
get toolReactiveProps() {
|
|
1909
|
+
return [...super.toolReactiveProps, "tabs"];
|
|
1910
|
+
}
|
|
1911
|
+
toolInputSchema() {
|
|
1912
|
+
return {
|
|
1913
|
+
type: "object",
|
|
1914
|
+
properties: {
|
|
1915
|
+
tab: {
|
|
1916
|
+
type: "string",
|
|
1917
|
+
enum: this.tabs.filter((t) => !t.disabled).map((t) => t.value),
|
|
1918
|
+
description: `Which tab to switch to in the "${this.actionNoun}" tabs.`
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
required: ["tab"]
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
executeTool(args) {
|
|
1925
|
+
const value = args.tab == null ? "" : String(args.tab);
|
|
1926
|
+
const tab = this.tabs.find((t) => t.value === value);
|
|
1927
|
+
if (!tab) {
|
|
1928
|
+
return {
|
|
1929
|
+
content: [
|
|
1930
|
+
{ type: "text", text: `No tab "${value}" in the "${this.actionNoun}" tabs.` }
|
|
1931
|
+
],
|
|
1932
|
+
isError: true
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
if (tab.disabled) {
|
|
1936
|
+
return {
|
|
1937
|
+
content: [{ type: "text", text: `The "${tab.label}" tab is disabled.` }],
|
|
1938
|
+
isError: true
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
this.switchTo(tab.value);
|
|
1942
|
+
return {
|
|
1943
|
+
content: [
|
|
1944
|
+
{
|
|
1945
|
+
type: "text",
|
|
1946
|
+
text: `Switched to the "${tab.label}" tab in the "${this.actionNoun}" tabs.`
|
|
1947
|
+
}
|
|
1948
|
+
]
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
// --- Tab model + state --------------------------------------------------
|
|
1952
|
+
connectedCallback() {
|
|
1953
|
+
this.syncTabs();
|
|
1954
|
+
this.ensureActive();
|
|
1955
|
+
super.connectedCallback();
|
|
1956
|
+
this.panelObserver = new MutationObserver(() => {
|
|
1957
|
+
this.syncTabs();
|
|
1958
|
+
this.ensureActive();
|
|
1959
|
+
});
|
|
1960
|
+
this.panelObserver.observe(this, { childList: true });
|
|
1961
|
+
}
|
|
1962
|
+
disconnectedCallback() {
|
|
1963
|
+
super.disconnectedCallback();
|
|
1964
|
+
this.panelObserver?.disconnect();
|
|
1965
|
+
this.panelObserver = void 0;
|
|
1966
|
+
}
|
|
1967
|
+
updated(changed) {
|
|
1968
|
+
super.updated(changed);
|
|
1969
|
+
if (changed.has("active") || changed.has("tabs")) this.applyPanels();
|
|
1970
|
+
}
|
|
1971
|
+
/** Light-DOM children that declare a tab via the `tab` attribute. */
|
|
1972
|
+
get panelEls() {
|
|
1973
|
+
return Array.from(this.querySelectorAll("[tab]"));
|
|
1974
|
+
}
|
|
1975
|
+
syncTabs() {
|
|
1976
|
+
this.tabs = this.panelEls.map((el) => ({
|
|
1977
|
+
value: el.getAttribute("tab") ?? "",
|
|
1978
|
+
label: el.getAttribute("tab-label") || el.getAttribute("tab") || "",
|
|
1979
|
+
disabled: el.hasAttribute("tab-disabled")
|
|
1980
|
+
}));
|
|
1981
|
+
}
|
|
1982
|
+
/** Fall back to the first enabled tab when `active` is unset or invalid. */
|
|
1983
|
+
ensureActive() {
|
|
1984
|
+
const valid = this.tabs.some((t) => t.value === this.active && !t.disabled);
|
|
1985
|
+
if (!valid) this.active = this.tabs.find((t) => !t.disabled)?.value ?? "";
|
|
1986
|
+
}
|
|
1987
|
+
/** Reflect the active tab onto the slotted panels (roles, labels, hidden). */
|
|
1988
|
+
applyPanels() {
|
|
1989
|
+
for (const el of this.panelEls) {
|
|
1990
|
+
const value = el.getAttribute("tab") ?? "";
|
|
1991
|
+
const tab = this.tabs.find((t) => t.value === value);
|
|
1992
|
+
el.setAttribute("role", "tabpanel");
|
|
1993
|
+
el.setAttribute("tabindex", "0");
|
|
1994
|
+
if (tab?.label) el.setAttribute("aria-label", tab.label);
|
|
1995
|
+
el.toggleAttribute("hidden", value !== this.active);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
/** Switch the active tab, firing `change` when it actually moves. */
|
|
1999
|
+
switchTo(value) {
|
|
2000
|
+
if (value === this.active) return;
|
|
2001
|
+
this.active = value;
|
|
2002
|
+
this.dispatchEvent(
|
|
2003
|
+
new CustomEvent("change", {
|
|
2004
|
+
detail: { value },
|
|
2005
|
+
bubbles: true,
|
|
2006
|
+
composed: true
|
|
2007
|
+
})
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
get tabButtons() {
|
|
2011
|
+
return Array.from(this.renderRoot?.querySelectorAll(".tab") ?? []);
|
|
2012
|
+
}
|
|
2013
|
+
/** Arrow / Home / End roving with automatic activation (ARIA tabs pattern). */
|
|
2014
|
+
onKeydown(event) {
|
|
2015
|
+
const enabled = this.tabs.filter((t) => !t.disabled);
|
|
2016
|
+
if (enabled.length === 0) return;
|
|
2017
|
+
const order = enabled.map((t) => t.value);
|
|
2018
|
+
const current = order.indexOf(this.active);
|
|
2019
|
+
let next = -1;
|
|
2020
|
+
switch (event.key) {
|
|
2021
|
+
case "ArrowRight":
|
|
2022
|
+
case "ArrowDown":
|
|
2023
|
+
next = (current + 1) % order.length;
|
|
2024
|
+
break;
|
|
2025
|
+
case "ArrowLeft":
|
|
2026
|
+
case "ArrowUp":
|
|
2027
|
+
next = current <= 0 ? order.length - 1 : current - 1;
|
|
2028
|
+
break;
|
|
2029
|
+
case "Home":
|
|
2030
|
+
next = 0;
|
|
2031
|
+
break;
|
|
2032
|
+
case "End":
|
|
2033
|
+
next = order.length - 1;
|
|
2034
|
+
break;
|
|
2035
|
+
default:
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
event.preventDefault();
|
|
2039
|
+
const value = order[next];
|
|
2040
|
+
this.switchTo(value);
|
|
2041
|
+
void this.updateComplete.then(() => {
|
|
2042
|
+
this.tabButtons.find((b) => b.dataset.value === value)?.focus();
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
render() {
|
|
2046
|
+
return html10`
|
|
2047
|
+
<div role="tablist" aria-label=${this.label || nothing9} @keydown=${this.onKeydown}>
|
|
2048
|
+
${repeat4(
|
|
2049
|
+
this.tabs,
|
|
2050
|
+
(tab) => tab.value,
|
|
2051
|
+
(tab) => {
|
|
2052
|
+
const selected = tab.value === this.active;
|
|
2053
|
+
return html10`<button
|
|
2054
|
+
type="button"
|
|
2055
|
+
class="tab"
|
|
2056
|
+
part="tab"
|
|
2057
|
+
role="tab"
|
|
2058
|
+
data-value=${tab.value}
|
|
2059
|
+
aria-selected=${selected ? "true" : "false"}
|
|
2060
|
+
tabindex=${selected ? "0" : "-1"}
|
|
2061
|
+
?disabled=${tab.disabled ?? false}
|
|
2062
|
+
@click=${() => this.switchTo(tab.value)}
|
|
2063
|
+
>
|
|
2064
|
+
${tab.label}
|
|
2065
|
+
</button>`;
|
|
2066
|
+
}
|
|
2067
|
+
)}
|
|
2068
|
+
</div>
|
|
2069
|
+
<div class="panels" part="panels"><slot></slot></div>
|
|
2070
|
+
`;
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
__decorateClass([
|
|
2074
|
+
property12({ reflect: true })
|
|
2075
|
+
], WmcpTabs.prototype, "active", 2);
|
|
2076
|
+
__decorateClass([
|
|
2077
|
+
property12()
|
|
2078
|
+
], WmcpTabs.prototype, "label", 2);
|
|
2079
|
+
__decorateClass([
|
|
2080
|
+
state5()
|
|
2081
|
+
], WmcpTabs.prototype, "tabs", 2);
|
|
2082
|
+
|
|
2083
|
+
// src/elements/popover.ts
|
|
2084
|
+
import {
|
|
2085
|
+
html as html11,
|
|
2086
|
+
css as css11,
|
|
2087
|
+
nothing as nothing10
|
|
2088
|
+
} from "lit";
|
|
2089
|
+
import { property as property13 } from "lit/decorators.js";
|
|
2090
|
+
var WmcpPopover = class extends WmcpAction {
|
|
2091
|
+
constructor() {
|
|
2092
|
+
super(...arguments);
|
|
2093
|
+
this.open = false;
|
|
2094
|
+
this.label = "";
|
|
2095
|
+
this.placement = "bottom";
|
|
2096
|
+
this.trigger = "click";
|
|
2097
|
+
}
|
|
2098
|
+
static {
|
|
2099
|
+
this.tagName = "wmcp-popover";
|
|
2100
|
+
}
|
|
2101
|
+
static {
|
|
2102
|
+
this.styles = css11`
|
|
2103
|
+
:host {
|
|
2104
|
+
display: inline-block;
|
|
2105
|
+
font-family: var(
|
|
2106
|
+
--popover-font-family,
|
|
2107
|
+
var(--input-font-family, ui-sans-serif, system-ui, sans-serif)
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
:host([hidden]) {
|
|
2111
|
+
display: none;
|
|
2112
|
+
}
|
|
2113
|
+
.trigger {
|
|
2114
|
+
appearance: none;
|
|
2115
|
+
font: inherit;
|
|
2116
|
+
cursor: pointer;
|
|
2117
|
+
anchor-name: --wmcp-pop-anchor;
|
|
2118
|
+
/* Unstyled by default so any trigger content (text, a button, an icon)
|
|
2119
|
+
sits flush; consumers theme via ::part(trigger). */
|
|
2120
|
+
border: 0;
|
|
2121
|
+
background: none;
|
|
2122
|
+
padding: 0;
|
|
2123
|
+
color: inherit;
|
|
2124
|
+
}
|
|
2125
|
+
.panel {
|
|
2126
|
+
box-sizing: border-box;
|
|
2127
|
+
width: max-content;
|
|
2128
|
+
max-width: var(--popover-max-width, 18rem);
|
|
2129
|
+
margin: 0;
|
|
2130
|
+
padding: var(--popover-padding, 0.75rem 0.875rem);
|
|
2131
|
+
color: var(--popover-text, var(--popover-foreground, oklch(0.145 0 0)));
|
|
2132
|
+
background: var(--popover-bg, var(--popover, oklch(1 0 0)));
|
|
2133
|
+
border: 1px solid var(--popover-border, var(--border, oklch(0.922 0 0)));
|
|
2134
|
+
border-radius: var(--popover-radius, var(--radius, 0.625rem));
|
|
2135
|
+
box-shadow: var(
|
|
2136
|
+
--popover-shadow,
|
|
2137
|
+
0 10px 38px -10px color-mix(in oklch, oklch(0 0 0) 35%, transparent)
|
|
2138
|
+
);
|
|
2139
|
+
font-size: var(--popover-font-size, 0.875rem);
|
|
2140
|
+
position-anchor: --wmcp-pop-anchor;
|
|
2141
|
+
inset: auto;
|
|
2142
|
+
}
|
|
2143
|
+
:host([placement='bottom']) .panel {
|
|
2144
|
+
top: anchor(bottom);
|
|
2145
|
+
left: anchor(left);
|
|
2146
|
+
margin-top: var(--popover-offset, 0.4rem);
|
|
2147
|
+
}
|
|
2148
|
+
:host([placement='top']) .panel {
|
|
2149
|
+
bottom: anchor(top);
|
|
2150
|
+
left: anchor(left);
|
|
2151
|
+
margin-bottom: var(--popover-offset, 0.4rem);
|
|
2152
|
+
}
|
|
2153
|
+
:host([placement='right']) .panel {
|
|
2154
|
+
left: anchor(right);
|
|
2155
|
+
top: anchor(top);
|
|
2156
|
+
margin-left: var(--popover-offset, 0.4rem);
|
|
2157
|
+
}
|
|
2158
|
+
:host([placement='left']) .panel {
|
|
2159
|
+
right: anchor(left);
|
|
2160
|
+
top: anchor(top);
|
|
2161
|
+
margin-right: var(--popover-offset, 0.4rem);
|
|
2162
|
+
}
|
|
2163
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2164
|
+
.panel {
|
|
2165
|
+
transition: opacity 130ms ease, translate 130ms ease,
|
|
2166
|
+
overlay 130ms allow-discrete, display 130ms allow-discrete;
|
|
2167
|
+
}
|
|
2168
|
+
.panel:not(:popover-open) {
|
|
2169
|
+
opacity: 0;
|
|
2170
|
+
translate: 0 -0.25rem;
|
|
2171
|
+
}
|
|
2172
|
+
@starting-style {
|
|
2173
|
+
.panel:popover-open {
|
|
2174
|
+
opacity: 0;
|
|
2175
|
+
translate: 0 -0.25rem;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
`;
|
|
2180
|
+
}
|
|
2181
|
+
get panelEl() {
|
|
2182
|
+
return this.renderRoot?.querySelector(".panel") ?? null;
|
|
2183
|
+
}
|
|
2184
|
+
// --- WmcpAction hooks ---------------------------------------------------
|
|
2185
|
+
get actionVerb() {
|
|
2186
|
+
return "open";
|
|
2187
|
+
}
|
|
2188
|
+
get defaultNameSuffix() {
|
|
2189
|
+
return "popover";
|
|
2190
|
+
}
|
|
2191
|
+
get defaultToolDescription() {
|
|
2192
|
+
return `Open the "${this.actionNoun}" popover.`;
|
|
2193
|
+
}
|
|
2194
|
+
executeTool() {
|
|
2195
|
+
const noun = this.actionNoun;
|
|
2196
|
+
if (this.open) {
|
|
2197
|
+
return { content: [{ type: "text", text: `The "${noun}" popover is already open.` }] };
|
|
2198
|
+
}
|
|
2199
|
+
this.show();
|
|
2200
|
+
return { content: [{ type: "text", text: `Opened the "${noun}" popover.` }] };
|
|
2201
|
+
}
|
|
2202
|
+
// --- Open / close -------------------------------------------------------
|
|
2203
|
+
updated(changed) {
|
|
2204
|
+
super.updated(changed);
|
|
2205
|
+
if (changed.has("open")) this.syncNativeOpen();
|
|
2206
|
+
}
|
|
2207
|
+
/** Open the popover. */
|
|
2208
|
+
show() {
|
|
2209
|
+
this.open = true;
|
|
2210
|
+
}
|
|
2211
|
+
/** Close the popover. */
|
|
2212
|
+
close() {
|
|
2213
|
+
this.open = false;
|
|
2214
|
+
}
|
|
2215
|
+
syncNativeOpen() {
|
|
2216
|
+
const p = this.panelEl;
|
|
2217
|
+
if (!p) return;
|
|
2218
|
+
const isOpen = p.matches(":popover-open");
|
|
2219
|
+
if (this.open && !isOpen) p.showPopover();
|
|
2220
|
+
else if (!this.open && isOpen) p.hidePopover();
|
|
2221
|
+
}
|
|
2222
|
+
/** Native toggle fires for clicks, light-dismiss, Escape, and our own calls. */
|
|
2223
|
+
onToggle(event) {
|
|
2224
|
+
const opened = event.newState === "open";
|
|
2225
|
+
this.open = opened;
|
|
2226
|
+
this.dispatchEvent(
|
|
2227
|
+
new Event(opened ? "open" : "close", { bubbles: true, composed: true })
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
disconnectedCallback() {
|
|
2231
|
+
super.disconnectedCallback();
|
|
2232
|
+
this.cancelScheduledClose();
|
|
2233
|
+
}
|
|
2234
|
+
/** Open on hover/focus of the trigger *or* the panel. */
|
|
2235
|
+
openOnHover() {
|
|
2236
|
+
if (this.trigger !== "hover") return;
|
|
2237
|
+
this.cancelScheduledClose();
|
|
2238
|
+
this.show();
|
|
2239
|
+
}
|
|
2240
|
+
/**
|
|
2241
|
+
* Close on leave/blur — but on a short delay, so the pointer can cross the
|
|
2242
|
+
* gap from the trigger to the panel (and reach interactive content inside it)
|
|
2243
|
+
* without the popover vanishing. Re-entering either cancels the close.
|
|
2244
|
+
*/
|
|
2245
|
+
scheduleHoverClose() {
|
|
2246
|
+
if (this.trigger !== "hover") return;
|
|
2247
|
+
this.cancelScheduledClose();
|
|
2248
|
+
this.closeTimer = setTimeout(() => this.close(), 120);
|
|
2249
|
+
}
|
|
2250
|
+
cancelScheduledClose() {
|
|
2251
|
+
if (this.closeTimer) {
|
|
2252
|
+
clearTimeout(this.closeTimer);
|
|
2253
|
+
this.closeTimer = void 0;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
render() {
|
|
2257
|
+
const isTooltip = this.trigger === "hover";
|
|
2258
|
+
return html11`
|
|
2259
|
+
<button
|
|
2260
|
+
type="button"
|
|
2261
|
+
class="trigger"
|
|
2262
|
+
part="trigger"
|
|
2263
|
+
popovertarget=${this.trigger === "click" ? "wmcp-pop" : nothing10}
|
|
2264
|
+
aria-haspopup=${isTooltip ? nothing10 : "dialog"}
|
|
2265
|
+
aria-expanded=${isTooltip ? nothing10 : this.open ? "true" : "false"}
|
|
2266
|
+
aria-describedby=${isTooltip ? "wmcp-pop" : nothing10}
|
|
2267
|
+
@pointerenter=${this.openOnHover}
|
|
2268
|
+
@pointerleave=${this.scheduleHoverClose}
|
|
2269
|
+
@focusin=${this.openOnHover}
|
|
2270
|
+
@focusout=${this.scheduleHoverClose}
|
|
2271
|
+
>
|
|
2272
|
+
<slot name="trigger">${this.label}</slot>
|
|
2273
|
+
</button>
|
|
2274
|
+
<div
|
|
2275
|
+
id="wmcp-pop"
|
|
2276
|
+
class="panel"
|
|
2277
|
+
part="panel"
|
|
2278
|
+
popover=${isTooltip ? "manual" : "auto"}
|
|
2279
|
+
role=${isTooltip ? "tooltip" : "dialog"}
|
|
2280
|
+
aria-label=${this.label || this.actionNoun}
|
|
2281
|
+
@toggle=${this.onToggle}
|
|
2282
|
+
@pointerenter=${this.openOnHover}
|
|
2283
|
+
@pointerleave=${this.scheduleHoverClose}
|
|
2284
|
+
@focusin=${this.openOnHover}
|
|
2285
|
+
@focusout=${this.scheduleHoverClose}
|
|
2286
|
+
>
|
|
2287
|
+
<slot></slot>
|
|
2288
|
+
</div>
|
|
2289
|
+
`;
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
__decorateClass([
|
|
2293
|
+
property13({ type: Boolean, reflect: true })
|
|
2294
|
+
], WmcpPopover.prototype, "open", 2);
|
|
2295
|
+
__decorateClass([
|
|
2296
|
+
property13()
|
|
2297
|
+
], WmcpPopover.prototype, "label", 2);
|
|
2298
|
+
__decorateClass([
|
|
2299
|
+
property13({ reflect: true })
|
|
2300
|
+
], WmcpPopover.prototype, "placement", 2);
|
|
2301
|
+
__decorateClass([
|
|
2302
|
+
property13()
|
|
2303
|
+
], WmcpPopover.prototype, "trigger", 2);
|
|
2304
|
+
|
|
2305
|
+
// src/elements/toast.ts
|
|
2306
|
+
import {
|
|
2307
|
+
html as html12,
|
|
2308
|
+
css as css12,
|
|
2309
|
+
nothing as nothing11
|
|
2310
|
+
} from "lit";
|
|
2311
|
+
import { property as property14, state as state6 } from "lit/decorators.js";
|
|
2312
|
+
import { repeat as repeat5 } from "lit/directives/repeat.js";
|
|
2313
|
+
var RECENT_WINDOW_MS = 3e4;
|
|
2314
|
+
var WmcpToast = class extends WmcpExposable {
|
|
2315
|
+
constructor() {
|
|
2316
|
+
super(...arguments);
|
|
2317
|
+
this.name = "";
|
|
2318
|
+
this.label = "";
|
|
2319
|
+
this.placement = "bottom-right";
|
|
2320
|
+
this.duration = 5e3;
|
|
2321
|
+
this.toasts = [];
|
|
2322
|
+
// A short history so an agent that reads *after* a toast auto-dismisses still
|
|
2323
|
+
// learns what happened (async outcomes vanish before the agent polls).
|
|
2324
|
+
this.recent = [];
|
|
2325
|
+
this.nextId = 0;
|
|
2326
|
+
this.timers = /* @__PURE__ */ new Map();
|
|
2327
|
+
}
|
|
2328
|
+
static {
|
|
2329
|
+
this.tagName = "wmcp-toast";
|
|
2330
|
+
}
|
|
2331
|
+
static {
|
|
2332
|
+
this.styles = css12`
|
|
2333
|
+
:host {
|
|
2334
|
+
position: fixed;
|
|
2335
|
+
z-index: var(--toast-z, 1000);
|
|
2336
|
+
display: flex;
|
|
2337
|
+
flex-direction: column;
|
|
2338
|
+
gap: var(--toast-gap, 0.5rem);
|
|
2339
|
+
width: max-content;
|
|
2340
|
+
max-width: var(--toast-max-width, min(92vw, 22rem));
|
|
2341
|
+
pointer-events: none;
|
|
2342
|
+
font-family: var(
|
|
2343
|
+
--toast-font-family,
|
|
2344
|
+
var(--input-font-family, ui-sans-serif, system-ui, sans-serif)
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
:host([placement='bottom-right']) {
|
|
2348
|
+
right: var(--toast-inset, 1rem);
|
|
2349
|
+
bottom: var(--toast-inset, 1rem);
|
|
2350
|
+
}
|
|
2351
|
+
:host([placement='bottom-left']) {
|
|
2352
|
+
left: var(--toast-inset, 1rem);
|
|
2353
|
+
bottom: var(--toast-inset, 1rem);
|
|
2354
|
+
}
|
|
2355
|
+
:host([placement='top-right']) {
|
|
2356
|
+
right: var(--toast-inset, 1rem);
|
|
2357
|
+
top: var(--toast-inset, 1rem);
|
|
2358
|
+
}
|
|
2359
|
+
:host([placement='top-left']) {
|
|
2360
|
+
left: var(--toast-inset, 1rem);
|
|
2361
|
+
top: var(--toast-inset, 1rem);
|
|
2362
|
+
}
|
|
2363
|
+
.toast {
|
|
2364
|
+
pointer-events: auto;
|
|
2365
|
+
display: flex;
|
|
2366
|
+
align-items: start;
|
|
2367
|
+
gap: var(--toast-item-gap, 0.625rem);
|
|
2368
|
+
padding: var(--toast-padding, 0.75rem 0.875rem);
|
|
2369
|
+
color: var(--toast-text, var(--popover-foreground, oklch(0.145 0 0)));
|
|
2370
|
+
background: var(--toast-bg, var(--popover, oklch(1 0 0)));
|
|
2371
|
+
border: 1px solid var(--toast-border, var(--border, oklch(0.922 0 0)));
|
|
2372
|
+
border-left: 3px solid var(--toast-accent, var(--border, oklch(0.922 0 0)));
|
|
2373
|
+
border-radius: var(--toast-radius, var(--radius, 0.625rem));
|
|
2374
|
+
box-shadow: var(
|
|
2375
|
+
--toast-shadow,
|
|
2376
|
+
0 10px 38px -10px color-mix(in oklch, oklch(0 0 0) 35%, transparent)
|
|
2377
|
+
);
|
|
2378
|
+
font-size: var(--toast-font-size, 0.875rem);
|
|
2379
|
+
}
|
|
2380
|
+
.toast[data-variant='success'] {
|
|
2381
|
+
--toast-accent: var(--toast-accent-success, oklch(0.627 0.13 160));
|
|
2382
|
+
}
|
|
2383
|
+
.toast[data-variant='warning'] {
|
|
2384
|
+
--toast-accent: var(--toast-accent-warning, oklch(0.7 0.16 75));
|
|
2385
|
+
}
|
|
2386
|
+
.toast[data-variant='error'] {
|
|
2387
|
+
--toast-accent: var(--toast-accent-error, var(--destructive, oklch(0.577 0.245 27.325)));
|
|
2388
|
+
}
|
|
2389
|
+
.toast[data-variant='info'] {
|
|
2390
|
+
--toast-accent: var(--toast-accent-info, var(--primary, oklch(0.205 0 0)));
|
|
2391
|
+
}
|
|
2392
|
+
.body {
|
|
2393
|
+
flex: 1;
|
|
2394
|
+
min-width: 0;
|
|
2395
|
+
}
|
|
2396
|
+
.title {
|
|
2397
|
+
font-weight: 600;
|
|
2398
|
+
}
|
|
2399
|
+
.message {
|
|
2400
|
+
color: var(--toast-message, var(--muted-foreground, oklch(0.556 0 0)));
|
|
2401
|
+
}
|
|
2402
|
+
.title + .message {
|
|
2403
|
+
margin-top: 0.125rem;
|
|
2404
|
+
}
|
|
2405
|
+
.close {
|
|
2406
|
+
appearance: none;
|
|
2407
|
+
flex-shrink: 0;
|
|
2408
|
+
width: 1.25rem;
|
|
2409
|
+
height: 1.25rem;
|
|
2410
|
+
padding: 0;
|
|
2411
|
+
border: 0;
|
|
2412
|
+
border-radius: 0.25rem;
|
|
2413
|
+
background: transparent;
|
|
2414
|
+
color: var(--toast-message, var(--muted-foreground, oklch(0.556 0 0)));
|
|
2415
|
+
cursor: pointer;
|
|
2416
|
+
line-height: 1;
|
|
2417
|
+
}
|
|
2418
|
+
.close:hover {
|
|
2419
|
+
color: var(--toast-text, var(--foreground, oklch(0.145 0 0)));
|
|
2420
|
+
}
|
|
2421
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2422
|
+
.toast {
|
|
2423
|
+
animation: toast-in 180ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
2424
|
+
}
|
|
2425
|
+
@keyframes toast-in {
|
|
2426
|
+
from {
|
|
2427
|
+
opacity: 0;
|
|
2428
|
+
translate: 0 0.5rem;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
`;
|
|
2433
|
+
}
|
|
2434
|
+
// --- Imperative API (what page code calls) ------------------------------
|
|
2435
|
+
/** Show a toast. Returns its id (pass to {@link dismiss}). */
|
|
2436
|
+
show(options) {
|
|
2437
|
+
const record = {
|
|
2438
|
+
...options,
|
|
2439
|
+
id: this.nextId++,
|
|
2440
|
+
variant: options.variant ?? "info",
|
|
2441
|
+
shownAt: Date.now()
|
|
2442
|
+
};
|
|
2443
|
+
this.toasts = [...this.toasts, record];
|
|
2444
|
+
this.recent = [record, ...this.recent].slice(0, 10);
|
|
2445
|
+
const ms = options.duration ?? this.duration;
|
|
2446
|
+
if (ms > 0) {
|
|
2447
|
+
this.timers.set(
|
|
2448
|
+
record.id,
|
|
2449
|
+
setTimeout(() => this.dismiss(record.id), ms)
|
|
2450
|
+
);
|
|
2451
|
+
}
|
|
2452
|
+
return record.id;
|
|
2453
|
+
}
|
|
2454
|
+
/** Dismiss a toast by id. */
|
|
2455
|
+
dismiss(id) {
|
|
2456
|
+
this.toasts = this.toasts.filter((t) => t.id !== id);
|
|
2457
|
+
const timer = this.timers.get(id);
|
|
2458
|
+
if (timer) {
|
|
2459
|
+
clearTimeout(timer);
|
|
2460
|
+
this.timers.delete(id);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
/** Dismiss all visible toasts. */
|
|
2464
|
+
clear() {
|
|
2465
|
+
for (const t of [...this.toasts]) this.dismiss(t.id);
|
|
2466
|
+
}
|
|
2467
|
+
disconnectedCallback() {
|
|
2468
|
+
super.disconnectedCallback();
|
|
2469
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
2470
|
+
this.timers.clear();
|
|
2471
|
+
}
|
|
2472
|
+
// --- WmcpExposable hooks (the agent's *read* surface) -------------------
|
|
2473
|
+
get resolvedToolName() {
|
|
2474
|
+
return this.toolName || `read_${this.name || "notifications"}`;
|
|
2475
|
+
}
|
|
2476
|
+
get defaultToolDescription() {
|
|
2477
|
+
return "Read the notifications currently shown to the user (with any that appeared in the last few seconds), so you can tell what just happened on the page.";
|
|
2478
|
+
}
|
|
2479
|
+
get toolReactiveProps() {
|
|
2480
|
+
return [...super.toolReactiveProps, "name"];
|
|
2481
|
+
}
|
|
2482
|
+
executeTool() {
|
|
2483
|
+
const describe = (t) => `[${t.variant}] ${t.title ? `${t.title}: ` : ""}${t.message}`;
|
|
2484
|
+
if (this.toasts.length > 0) {
|
|
2485
|
+
return {
|
|
2486
|
+
content: [
|
|
2487
|
+
{
|
|
2488
|
+
type: "text",
|
|
2489
|
+
text: `${this.toasts.length} notification(s) showing:
|
|
2490
|
+
` + this.toasts.map(describe).join("\n")
|
|
2491
|
+
}
|
|
2492
|
+
]
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
const cutoff = Date.now() - RECENT_WINDOW_MS;
|
|
2496
|
+
const recent = this.recent.filter((t) => t.shownAt >= cutoff);
|
|
2497
|
+
if (recent.length > 0) {
|
|
2498
|
+
return {
|
|
2499
|
+
content: [
|
|
2500
|
+
{
|
|
2501
|
+
type: "text",
|
|
2502
|
+
text: "No notifications are showing now. Recently shown:\n" + recent.map(describe).join("\n")
|
|
2503
|
+
}
|
|
2504
|
+
]
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
return { content: [{ type: "text", text: "No notifications." }] };
|
|
2508
|
+
}
|
|
2509
|
+
// --- Render -------------------------------------------------------------
|
|
2510
|
+
render() {
|
|
2511
|
+
return html12`
|
|
2512
|
+
<div role="region" aria-label=${this.label || "Notifications"} aria-live="polite">
|
|
2513
|
+
${repeat5(
|
|
2514
|
+
this.toasts,
|
|
2515
|
+
(t) => t.id,
|
|
2516
|
+
(t) => html12`
|
|
2517
|
+
<div
|
|
2518
|
+
class="toast"
|
|
2519
|
+
part="toast"
|
|
2520
|
+
data-variant=${t.variant}
|
|
2521
|
+
role=${t.variant === "error" ? "alert" : "status"}
|
|
2522
|
+
>
|
|
2523
|
+
<div class="body">
|
|
2524
|
+
${t.title ? html12`<div class="title">${t.title}</div>` : nothing11}
|
|
2525
|
+
<div class="message">${t.message}</div>
|
|
2526
|
+
</div>
|
|
2527
|
+
<button
|
|
2528
|
+
class="close"
|
|
2529
|
+
part="close"
|
|
2530
|
+
type="button"
|
|
2531
|
+
aria-label="Dismiss notification"
|
|
2532
|
+
@click=${() => this.dismiss(t.id)}
|
|
2533
|
+
>
|
|
2534
|
+
✕
|
|
2535
|
+
</button>
|
|
2536
|
+
</div>
|
|
2537
|
+
`
|
|
2538
|
+
)}
|
|
2539
|
+
</div>
|
|
2540
|
+
`;
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
__decorateClass([
|
|
2544
|
+
property14()
|
|
2545
|
+
], WmcpToast.prototype, "name", 2);
|
|
2546
|
+
__decorateClass([
|
|
2547
|
+
property14()
|
|
2548
|
+
], WmcpToast.prototype, "label", 2);
|
|
2549
|
+
__decorateClass([
|
|
2550
|
+
property14({ reflect: true })
|
|
2551
|
+
], WmcpToast.prototype, "placement", 2);
|
|
2552
|
+
__decorateClass([
|
|
2553
|
+
property14({ type: Number })
|
|
2554
|
+
], WmcpToast.prototype, "duration", 2);
|
|
2555
|
+
__decorateClass([
|
|
2556
|
+
state6()
|
|
2557
|
+
], WmcpToast.prototype, "toasts", 2);
|
|
2558
|
+
|
|
2559
|
+
// src/register.ts
|
|
2560
|
+
var elements = [
|
|
2561
|
+
WmcpInput,
|
|
2562
|
+
WmcpTextarea,
|
|
2563
|
+
WmcpSelect,
|
|
2564
|
+
WmcpCheckbox,
|
|
2565
|
+
WmcpRadio,
|
|
2566
|
+
WmcpRadioGroup,
|
|
2567
|
+
WmcpButton,
|
|
2568
|
+
WmcpDialog,
|
|
2569
|
+
WmcpMenu,
|
|
2570
|
+
WmcpTabs,
|
|
2571
|
+
WmcpPopover,
|
|
2572
|
+
WmcpToast
|
|
999
2573
|
];
|
|
1000
2574
|
function defineComponents() {
|
|
1001
2575
|
if (typeof customElements === "undefined") return;
|
|
@@ -1006,13 +2580,21 @@ function defineComponents() {
|
|
|
1006
2580
|
}
|
|
1007
2581
|
}
|
|
1008
2582
|
export {
|
|
2583
|
+
WmcpAction,
|
|
2584
|
+
WmcpButton,
|
|
1009
2585
|
WmcpCheckbox,
|
|
2586
|
+
WmcpDialog,
|
|
2587
|
+
WmcpExposable,
|
|
1010
2588
|
WmcpFormControl,
|
|
1011
2589
|
WmcpInput,
|
|
2590
|
+
WmcpMenu,
|
|
2591
|
+
WmcpPopover,
|
|
1012
2592
|
WmcpRadio,
|
|
1013
2593
|
WmcpRadioGroup,
|
|
1014
2594
|
WmcpSelect,
|
|
2595
|
+
WmcpTabs,
|
|
1015
2596
|
WmcpTextarea,
|
|
2597
|
+
WmcpToast,
|
|
1016
2598
|
defineComponents,
|
|
1017
2599
|
exposeTool,
|
|
1018
2600
|
isStandardSchema,
|