@syncended/dsh-automations 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +14 -0
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -3
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/lib/client.js +858 -61
- package/package.json +4 -2
package/lib/client.js
CHANGED
|
@@ -11,6 +11,7 @@ window.__ModuleLoader__.load({
|
|
|
11
11
|
// the same-origin HTTP API at /api/automations; every mutation carries the
|
|
12
12
|
// x-dsh-automation-client fence header plus an application/json body.
|
|
13
13
|
const React = require("react");
|
|
14
|
+
const { createPortal } = require("react-dom");
|
|
14
15
|
const h = React.createElement;
|
|
15
16
|
const { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } = React;
|
|
16
17
|
const {
|
|
@@ -23,10 +24,12 @@ window.__ModuleLoader__.load({
|
|
|
23
24
|
IconChevronDownOutline14,
|
|
24
25
|
IconChevronLeftOutline14,
|
|
25
26
|
IconEditOutline16,
|
|
27
|
+
IconGlobeOutline14,
|
|
26
28
|
IconPlayOutline16,
|
|
27
29
|
IconPlusOutline16,
|
|
28
30
|
IconRefreshOutline16,
|
|
29
31
|
IconSettingsOutline16,
|
|
32
|
+
IconThinkOutline14,
|
|
30
33
|
IconTrashOutline16,
|
|
31
34
|
IconWarningOutline16,
|
|
32
35
|
} = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
@@ -50,26 +53,77 @@ window.__ModuleLoader__.load({
|
|
|
50
53
|
}
|
|
51
54
|
})();
|
|
52
55
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
const POPULAR_TIMEZONE_CANDIDATES = [
|
|
57
|
+
BROWSER_TIMEZONE,
|
|
58
|
+
"UTC",
|
|
59
|
+
"America/New_York",
|
|
60
|
+
"America/Los_Angeles",
|
|
61
|
+
"America/Sao_Paulo",
|
|
62
|
+
"Europe/London",
|
|
63
|
+
"Europe/Berlin",
|
|
64
|
+
"Europe/Moscow",
|
|
65
|
+
"Asia/Dubai",
|
|
66
|
+
"Asia/Kolkata",
|
|
67
|
+
"Asia/Shanghai",
|
|
68
|
+
"Asia/Tokyo",
|
|
69
|
+
"Australia/Sydney",
|
|
70
|
+
];
|
|
71
|
+
const PREFERRED_TIMEZONE_ALIASES = [
|
|
72
|
+
"Africa/Asmara",
|
|
73
|
+
"America/Argentina/Buenos_Aires",
|
|
74
|
+
"America/Atikokan",
|
|
75
|
+
"America/Indiana/Indianapolis",
|
|
76
|
+
"America/Kentucky/Louisville",
|
|
77
|
+
"America/Nuuk",
|
|
78
|
+
"Asia/Ho_Chi_Minh",
|
|
79
|
+
"Asia/Kathmandu",
|
|
80
|
+
"Asia/Kolkata",
|
|
81
|
+
"Asia/Yangon",
|
|
82
|
+
"Atlantic/Faroe",
|
|
83
|
+
"Europe/Kyiv",
|
|
84
|
+
"Pacific/Chuuk",
|
|
85
|
+
"Pacific/Kanton",
|
|
86
|
+
"Pacific/Pohnpei",
|
|
87
|
+
];
|
|
88
|
+
const TIMEZONE_ALIASES_BY_ZONE = new Map();
|
|
89
|
+
for (const alias of PREFERRED_TIMEZONE_ALIASES) {
|
|
90
|
+
const zone = canonicalTimezone(alias);
|
|
91
|
+
if (!zone || zone === alias) continue;
|
|
92
|
+
const aliases = TIMEZONE_ALIASES_BY_ZONE.get(zone) || [];
|
|
93
|
+
aliases.push(alias);
|
|
94
|
+
TIMEZONE_ALIASES_BY_ZONE.set(zone, aliases);
|
|
95
|
+
}
|
|
96
|
+
const POPULAR_TIMEZONES = Array.from(
|
|
97
|
+
new Set(POPULAR_TIMEZONE_CANDIDATES.map(canonicalTimezone).filter(Boolean)),
|
|
70
98
|
);
|
|
99
|
+
const SUPPORTED_TIMEZONES = (() => {
|
|
100
|
+
let discovered = [];
|
|
101
|
+
try {
|
|
102
|
+
discovered = typeof Intl.supportedValuesOf === "function"
|
|
103
|
+
? Intl.supportedValuesOf("timeZone")
|
|
104
|
+
: [];
|
|
105
|
+
} catch {
|
|
106
|
+
discovered = [];
|
|
107
|
+
}
|
|
108
|
+
return Array.from(
|
|
109
|
+
new Set([
|
|
110
|
+
...POPULAR_TIMEZONES,
|
|
111
|
+
...TIMEZONE_ALIASES_BY_ZONE.keys(),
|
|
112
|
+
...discovered.filter((zone) => typeof zone === "string" && zone !== ""),
|
|
113
|
+
]),
|
|
114
|
+
).sort((left, right) => left.localeCompare(right));
|
|
115
|
+
})();
|
|
116
|
+
const TIMEZONE_SEARCH_INDEX = SUPPORTED_TIMEZONES.map((zone) => {
|
|
117
|
+
const aliases = TIMEZONE_ALIASES_BY_ZONE.get(zone) || [];
|
|
118
|
+
const preferred = aliases[0] || zone;
|
|
119
|
+
return {
|
|
120
|
+
zone,
|
|
121
|
+
city: timezoneCityLabel(preferred),
|
|
122
|
+
search: timezoneSearchKey([zone, ...aliases].join(" ")),
|
|
123
|
+
};
|
|
124
|
+
});
|
|
71
125
|
|
|
72
|
-
const
|
|
126
|
+
const MODEL_METADATA_CACHE = new Map();
|
|
73
127
|
const OVERLAP_OPTIONS = [
|
|
74
128
|
{ value: "skip", label: "skip", hint: "Skip the run if a previous run is still active." },
|
|
75
129
|
{ value: "queue", label: "queue", hint: "Defer the run until the previous run finishes." },
|
|
@@ -147,6 +201,138 @@ window.__ModuleLoader__.load({
|
|
|
147
201
|
});
|
|
148
202
|
}
|
|
149
203
|
|
|
204
|
+
function canonicalTimezone(value) {
|
|
205
|
+
const timezone = String(value || "").trim();
|
|
206
|
+
if (timezone === "") return null;
|
|
207
|
+
try {
|
|
208
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: timezone }).resolvedOptions().timeZone;
|
|
209
|
+
} catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function timezoneCityLabel(zone) {
|
|
215
|
+
if (zone === "UTC") return "UTC";
|
|
216
|
+
const segments = zone.split("/");
|
|
217
|
+
return (segments[segments.length - 1] || zone).replace(/_/g, " ");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function timezonePreferredValue(zone) {
|
|
221
|
+
return TIMEZONE_ALIASES_BY_ZONE.get(zone)?.[0] || zone;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function timezoneSearchKey(zone) {
|
|
225
|
+
const literal = zone.toLowerCase();
|
|
226
|
+
const expanded = literal
|
|
227
|
+
.normalize("NFKD")
|
|
228
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
229
|
+
.replace(/[\/_\-.]+/g, " ")
|
|
230
|
+
.replace(/\s+/g, " ")
|
|
231
|
+
.trim();
|
|
232
|
+
return expanded;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function timezoneOffsetLabel(zone, at = new Date()) {
|
|
236
|
+
try {
|
|
237
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
238
|
+
timeZone: zone,
|
|
239
|
+
timeZoneName: "longOffset",
|
|
240
|
+
hour: "2-digit",
|
|
241
|
+
});
|
|
242
|
+
const value = formatter.formatToParts(at).find((part) => part.type === "timeZoneName")?.value;
|
|
243
|
+
if (!value) return "UTC offset unavailable";
|
|
244
|
+
if (value === "GMT") return "UTC+00:00";
|
|
245
|
+
return value.replace(/^GMT/, "UTC");
|
|
246
|
+
} catch {
|
|
247
|
+
return "UTC offset unavailable";
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function timezoneSearchResults(query, currentValue, limit = 30) {
|
|
252
|
+
const rawQuery = String(query || "").trim().toLowerCase();
|
|
253
|
+
const current = canonicalTimezone(currentValue);
|
|
254
|
+
if (rawQuery === "") {
|
|
255
|
+
return Array.from(
|
|
256
|
+
new Set([current, BROWSER_TIMEZONE, ...POPULAR_TIMEZONES].filter(Boolean)),
|
|
257
|
+
).slice(0, 12);
|
|
258
|
+
}
|
|
259
|
+
const normalizedQuery = timezoneSearchKey(rawQuery);
|
|
260
|
+
if (normalizedQuery === "") return [];
|
|
261
|
+
const terms = normalizedQuery.split(" ").filter(Boolean);
|
|
262
|
+
return TIMEZONE_SEARCH_INDEX
|
|
263
|
+
.filter((entry) => terms.every((term) => entry.search.includes(term)))
|
|
264
|
+
.map((entry) => {
|
|
265
|
+
const canonical = entry.zone.toLowerCase();
|
|
266
|
+
const preferred = timezonePreferredValue(entry.zone).toLowerCase();
|
|
267
|
+
const city = timezoneSearchKey(entry.city);
|
|
268
|
+
let score = 5;
|
|
269
|
+
if (canonical === rawQuery || preferred === rawQuery) score = 0;
|
|
270
|
+
else if (city === normalizedQuery) score = 1;
|
|
271
|
+
else if (city.startsWith(normalizedQuery)) score = 2;
|
|
272
|
+
else if (canonical.split("/").some((segment) => timezoneSearchKey(segment).startsWith(normalizedQuery))) score = 3;
|
|
273
|
+
else if (entry.search.startsWith(normalizedQuery)) score = 4;
|
|
274
|
+
return { ...entry, score };
|
|
275
|
+
})
|
|
276
|
+
.sort((left, right) => left.score - right.score || left.city.localeCompare(right.city) || left.zone.localeCompare(right.zone))
|
|
277
|
+
.slice(0, limit)
|
|
278
|
+
.map((entry) => entry.zone);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function comboboxSearchResults(options, query, limit = 30) {
|
|
282
|
+
const rawQuery = String(query || "").trim().toLowerCase();
|
|
283
|
+
if (rawQuery === "") return options.slice(0, limit);
|
|
284
|
+
const normalizedQuery = timezoneSearchKey(rawQuery);
|
|
285
|
+
if (normalizedQuery === "") return [];
|
|
286
|
+
const terms = normalizedQuery.split(" ").filter(Boolean);
|
|
287
|
+
return options
|
|
288
|
+
.map((option, order) => {
|
|
289
|
+
const identifier = String(option.value ?? option.id).toLowerCase();
|
|
290
|
+
const label = timezoneSearchKey(option.label || "");
|
|
291
|
+
const search = timezoneSearchKey(
|
|
292
|
+
[option.label, option.detail, option.value, ...(option.aliases || [])].filter(Boolean).join(" "),
|
|
293
|
+
);
|
|
294
|
+
if (!terms.every((term) => search.includes(term))) return null;
|
|
295
|
+
let score = option.custom ? 6 : 5;
|
|
296
|
+
if (!option.custom && identifier === rawQuery) score = 0;
|
|
297
|
+
else if (!option.custom && label === normalizedQuery) score = 1;
|
|
298
|
+
else if (!option.custom && label.startsWith(normalizedQuery)) score = 2;
|
|
299
|
+
else if (!option.custom && identifier.split(/[\/:._-]+/).some((segment) => segment.startsWith(rawQuery))) score = 3;
|
|
300
|
+
else if (!option.custom && search.startsWith(normalizedQuery)) score = 4;
|
|
301
|
+
return { option, order, score };
|
|
302
|
+
})
|
|
303
|
+
.filter(Boolean)
|
|
304
|
+
.sort((left, right) => left.score - right.score || left.order - right.order)
|
|
305
|
+
.slice(0, limit)
|
|
306
|
+
.map((entry) => entry.option);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function timezoneNavigationIndex(key, current, resultCount, reopening) {
|
|
310
|
+
if (resultCount === 0) return -1;
|
|
311
|
+
if (reopening) return key === "ArrowDown" ? 0 : resultCount - 1;
|
|
312
|
+
if (key === "ArrowDown") return current < 0 || current >= resultCount - 1 ? 0 : current + 1;
|
|
313
|
+
return current <= 0 ? resultCount - 1 : current - 1;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function timezoneOptionPresentation(zone, offsetCache) {
|
|
317
|
+
let offset = offsetCache?.get(zone);
|
|
318
|
+
if (!offset) {
|
|
319
|
+
offset = timezoneOffsetLabel(zone);
|
|
320
|
+
offsetCache?.set(zone, offset);
|
|
321
|
+
}
|
|
322
|
+
const current = BROWSER_TIMEZONE === zone;
|
|
323
|
+
const preferred = timezonePreferredValue(zone);
|
|
324
|
+
if (zone === "UTC") {
|
|
325
|
+
return {
|
|
326
|
+
label: "UTC",
|
|
327
|
+
detail: offset + " now · Coordinated Universal Time" + (current ? " · Your browser time zone" : ""),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
label: timezoneCityLabel(preferred),
|
|
332
|
+
detail: preferred + " · " + offset + " now" + (current ? " · Your browser time zone" : ""),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
150
336
|
function browserTimezone() {
|
|
151
337
|
return BROWSER_TIMEZONE;
|
|
152
338
|
}
|
|
@@ -203,6 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
203
389
|
try {
|
|
204
390
|
response = await fetch(API_PREFIX + path, Object.assign({}, options, { headers }));
|
|
205
391
|
} catch (error) {
|
|
392
|
+
if (error && error.name === "AbortError") throw error;
|
|
206
393
|
throw new Error("Network error: " + errMessage(error));
|
|
207
394
|
}
|
|
208
395
|
const text = await response.text();
|
|
@@ -446,6 +633,588 @@ window.__ModuleLoader__.load({
|
|
|
446
633
|
);
|
|
447
634
|
}
|
|
448
635
|
|
|
636
|
+
function useComboboxPopoverLayout(open, anchorRef, panelRef) {
|
|
637
|
+
const [layout, setLayout] = useState(null);
|
|
638
|
+
useEffect(() => {
|
|
639
|
+
if (!open) {
|
|
640
|
+
setLayout(null);
|
|
641
|
+
return undefined;
|
|
642
|
+
}
|
|
643
|
+
const place = () => {
|
|
644
|
+
const anchor = anchorRef.current;
|
|
645
|
+
if (!anchor) return;
|
|
646
|
+
const rect = anchor.getBoundingClientRect();
|
|
647
|
+
const panel = panelRef.current;
|
|
648
|
+
const margin = 12;
|
|
649
|
+
const gap = 4;
|
|
650
|
+
const width = Math.min(Math.max(rect.width, 280), window.innerWidth - margin * 2);
|
|
651
|
+
const left = Math.min(Math.max(rect.left, margin), window.innerWidth - width - margin);
|
|
652
|
+
const desiredHeight = Math.min(360, panel?.scrollHeight || 360);
|
|
653
|
+
const below = window.innerHeight - rect.bottom - gap - margin;
|
|
654
|
+
const above = rect.top - gap - margin;
|
|
655
|
+
const side = below >= Math.min(desiredHeight, 220) || below >= above ? "bottom" : "top";
|
|
656
|
+
const available = Math.max(80, side === "bottom" ? below : above);
|
|
657
|
+
const maxHeight = Math.min(360, available);
|
|
658
|
+
const renderedHeight = Math.min(panel?.offsetHeight || desiredHeight, maxHeight);
|
|
659
|
+
const top = side === "bottom"
|
|
660
|
+
? rect.bottom + gap
|
|
661
|
+
: Math.max(margin, rect.top - gap - renderedHeight);
|
|
662
|
+
setLayout((current) => {
|
|
663
|
+
const next = { left, top, width, maxHeight, side };
|
|
664
|
+
return current
|
|
665
|
+
&& current.left === next.left
|
|
666
|
+
&& current.top === next.top
|
|
667
|
+
&& current.width === next.width
|
|
668
|
+
&& current.maxHeight === next.maxHeight
|
|
669
|
+
&& current.side === next.side
|
|
670
|
+
? current
|
|
671
|
+
: next;
|
|
672
|
+
});
|
|
673
|
+
};
|
|
674
|
+
const frame = requestAnimationFrame(place);
|
|
675
|
+
window.addEventListener("scroll", place, true);
|
|
676
|
+
window.addEventListener("resize", place);
|
|
677
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(place);
|
|
678
|
+
if (anchorRef.current) observer?.observe(anchorRef.current);
|
|
679
|
+
if (panelRef.current) observer?.observe(panelRef.current);
|
|
680
|
+
return () => {
|
|
681
|
+
cancelAnimationFrame(frame);
|
|
682
|
+
observer?.disconnect();
|
|
683
|
+
window.removeEventListener("scroll", place, true);
|
|
684
|
+
window.removeEventListener("resize", place);
|
|
685
|
+
};
|
|
686
|
+
}, [open, anchorRef, panelRef]);
|
|
687
|
+
return layout;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function EditableCombobox(props) {
|
|
691
|
+
const {
|
|
692
|
+
id,
|
|
693
|
+
value,
|
|
694
|
+
onChange,
|
|
695
|
+
optionsForQuery,
|
|
696
|
+
selectedId,
|
|
697
|
+
commitExactValue,
|
|
698
|
+
placeholder,
|
|
699
|
+
Icon,
|
|
700
|
+
listboxLabel,
|
|
701
|
+
initialTitle,
|
|
702
|
+
searchTitle,
|
|
703
|
+
emptyText,
|
|
704
|
+
hintText,
|
|
705
|
+
resultNoun,
|
|
706
|
+
invalidMessage,
|
|
707
|
+
invalid = false,
|
|
708
|
+
disabled = false,
|
|
709
|
+
resumeSearch: resumeSearchProp,
|
|
710
|
+
panelNotice = null,
|
|
711
|
+
onOpen,
|
|
712
|
+
} = props;
|
|
713
|
+
const [open, setOpen] = useState(false);
|
|
714
|
+
const [searching, setSearching] = useState(false);
|
|
715
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
716
|
+
const [announcement, setAnnouncement] = useState("");
|
|
717
|
+
const anchorRef = useRef(null);
|
|
718
|
+
const inputRef = useRef(null);
|
|
719
|
+
const panelRef = useRef(null);
|
|
720
|
+
const listboxId = id + "-options";
|
|
721
|
+
const query = searching ? value : "";
|
|
722
|
+
const results = optionsForQuery(query);
|
|
723
|
+
const active = activeIndex >= 0 && activeIndex < results.length ? activeIndex : -1;
|
|
724
|
+
const resumeSearch = resumeSearchProp === undefined
|
|
725
|
+
? value.trim() !== "" && selectedId === null
|
|
726
|
+
: resumeSearchProp;
|
|
727
|
+
const popoverLayout = useComboboxPopoverLayout(open, anchorRef, panelRef);
|
|
728
|
+
const close = () => {
|
|
729
|
+
setOpen(false);
|
|
730
|
+
setSearching(false);
|
|
731
|
+
setActiveIndex(-1);
|
|
732
|
+
setAnnouncement("");
|
|
733
|
+
};
|
|
734
|
+
const show = () => {
|
|
735
|
+
setAnnouncement("");
|
|
736
|
+
setOpen(true);
|
|
737
|
+
onOpen?.();
|
|
738
|
+
};
|
|
739
|
+
const choose = (option) => {
|
|
740
|
+
if (option.disabled) return;
|
|
741
|
+
if (option.value !== value) onChange(option.value);
|
|
742
|
+
close();
|
|
743
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
useEffect(() => {
|
|
747
|
+
if (!open) return undefined;
|
|
748
|
+
const dismiss = (event) => {
|
|
749
|
+
const target = event.target;
|
|
750
|
+
if (!(target instanceof Node)) return;
|
|
751
|
+
if (anchorRef.current?.contains(target) || panelRef.current?.contains(target)) return;
|
|
752
|
+
close();
|
|
753
|
+
};
|
|
754
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
755
|
+
return () => document.removeEventListener("pointerdown", dismiss, true);
|
|
756
|
+
}, [open]);
|
|
757
|
+
|
|
758
|
+
useEffect(() => {
|
|
759
|
+
if (!open) return undefined;
|
|
760
|
+
const timer = window.setTimeout(() => {
|
|
761
|
+
setAnnouncement(
|
|
762
|
+
results.length === 0
|
|
763
|
+
? "No matching " + resultNoun + "s."
|
|
764
|
+
: results.length + " " + resultNoun + (results.length === 1 ? " suggestion available." : " suggestions available."),
|
|
765
|
+
);
|
|
766
|
+
}, 250);
|
|
767
|
+
return () => window.clearTimeout(timer);
|
|
768
|
+
}, [open, query, results.length]);
|
|
769
|
+
|
|
770
|
+
useEffect(() => {
|
|
771
|
+
if (!open || active < 0) return;
|
|
772
|
+
document.getElementById(listboxId + "-option-" + active)?.scrollIntoView({ block: "nearest" });
|
|
773
|
+
}, [open, active, listboxId]);
|
|
774
|
+
|
|
775
|
+
const onInputKeyDown = (event) => {
|
|
776
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
777
|
+
event.preventDefault();
|
|
778
|
+
const navigationResults = !open && resumeSearch
|
|
779
|
+
? optionsForQuery(value)
|
|
780
|
+
: results;
|
|
781
|
+
if (!open) {
|
|
782
|
+
setSearching(resumeSearch);
|
|
783
|
+
show();
|
|
784
|
+
}
|
|
785
|
+
if (navigationResults.length === 0) {
|
|
786
|
+
setActiveIndex(-1);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
setActiveIndex((current) =>
|
|
790
|
+
timezoneNavigationIndex(event.key, current, navigationResults.length, !open),
|
|
791
|
+
);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (event.key === "Enter" && open) {
|
|
795
|
+
event.preventDefault();
|
|
796
|
+
if (active >= 0) {
|
|
797
|
+
choose(results[active]);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const exact = commitExactValue(value);
|
|
801
|
+
if (exact !== null) choose({ id: exact, value: exact });
|
|
802
|
+
else setAnnouncement(invalidMessage);
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (event.key === "Escape" && open) {
|
|
806
|
+
event.preventDefault();
|
|
807
|
+
event.stopPropagation();
|
|
808
|
+
(event.nativeEvent || event).__dshAutomationsNested = true;
|
|
809
|
+
close();
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
if (event.key === "Tab" && open) close();
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const panel = open
|
|
816
|
+
? h(
|
|
817
|
+
"div",
|
|
818
|
+
{
|
|
819
|
+
ref: panelRef,
|
|
820
|
+
className: "dsh-auto-combobox-popover",
|
|
821
|
+
style: popoverLayout
|
|
822
|
+
? {
|
|
823
|
+
left: popoverLayout.left,
|
|
824
|
+
top: popoverLayout.top,
|
|
825
|
+
width: popoverLayout.width,
|
|
826
|
+
maxHeight: popoverLayout.maxHeight,
|
|
827
|
+
}
|
|
828
|
+
: { left: 12, top: 12, width: 320, visibility: "hidden" },
|
|
829
|
+
"data-side": popoverLayout?.side,
|
|
830
|
+
"data-dsh-auto-combobox-popover": "true",
|
|
831
|
+
},
|
|
832
|
+
h(
|
|
833
|
+
"div",
|
|
834
|
+
{ className: "dsh-auto-combobox-popover-title" },
|
|
835
|
+
searching && query.trim() !== "" ? searchTitle : initialTitle,
|
|
836
|
+
),
|
|
837
|
+
panelNotice
|
|
838
|
+
? h("div", { className: "dsh-auto-combobox-notice", role: "status" }, panelNotice)
|
|
839
|
+
: null,
|
|
840
|
+
h(
|
|
841
|
+
"div",
|
|
842
|
+
{
|
|
843
|
+
id: listboxId,
|
|
844
|
+
className: "dsh-auto-combobox-list",
|
|
845
|
+
role: "listbox",
|
|
846
|
+
"aria-label": listboxLabel,
|
|
847
|
+
},
|
|
848
|
+
results.length > 0
|
|
849
|
+
? results.map((option, index) => {
|
|
850
|
+
const optionId = listboxId + "-option-" + index;
|
|
851
|
+
const OptionIcon = option.Icon || Icon;
|
|
852
|
+
const isSelected = selectedId === option.id;
|
|
853
|
+
return h(
|
|
854
|
+
"div",
|
|
855
|
+
{
|
|
856
|
+
key: option.id,
|
|
857
|
+
id: optionId,
|
|
858
|
+
className: "dsh-auto-combobox-option",
|
|
859
|
+
role: "option",
|
|
860
|
+
"aria-selected": isSelected,
|
|
861
|
+
"aria-disabled": option.disabled ? "true" : undefined,
|
|
862
|
+
"data-active": active === index ? "true" : undefined,
|
|
863
|
+
"data-disabled": option.disabled ? "true" : undefined,
|
|
864
|
+
onPointerMove: () => {
|
|
865
|
+
if (!option.disabled) setActiveIndex(index);
|
|
866
|
+
},
|
|
867
|
+
onPointerDown: (event) => event.preventDefault(),
|
|
868
|
+
onClick: () => choose(option),
|
|
869
|
+
},
|
|
870
|
+
OptionIcon
|
|
871
|
+
? h("span", { className: "dsh-auto-combobox-option-icon", "aria-hidden": "true" }, h(OptionIcon))
|
|
872
|
+
: null,
|
|
873
|
+
h(
|
|
874
|
+
"span",
|
|
875
|
+
{ className: "dsh-auto-picker-item-copy" },
|
|
876
|
+
h("span", { className: "dsh-auto-picker-item-label" }, option.label),
|
|
877
|
+
option.detail
|
|
878
|
+
? h("span", { className: "dsh-auto-picker-item-detail" }, option.detail)
|
|
879
|
+
: null,
|
|
880
|
+
),
|
|
881
|
+
isSelected
|
|
882
|
+
? h("span", { className: "dsh-auto-combobox-option-check", "aria-hidden": "true" }, h(IconCheckOutline16))
|
|
883
|
+
: null,
|
|
884
|
+
);
|
|
885
|
+
})
|
|
886
|
+
: h(
|
|
887
|
+
"div",
|
|
888
|
+
{
|
|
889
|
+
className: "dsh-auto-combobox-empty",
|
|
890
|
+
role: "option",
|
|
891
|
+
"aria-disabled": "true",
|
|
892
|
+
"aria-selected": false,
|
|
893
|
+
},
|
|
894
|
+
emptyText,
|
|
895
|
+
),
|
|
896
|
+
),
|
|
897
|
+
hintText ? h("p", { className: "dsh-auto-combobox-popover-hint" }, hintText) : null,
|
|
898
|
+
)
|
|
899
|
+
: null;
|
|
900
|
+
|
|
901
|
+
return h(
|
|
902
|
+
React.Fragment,
|
|
903
|
+
null,
|
|
904
|
+
h(
|
|
905
|
+
"span",
|
|
906
|
+
{
|
|
907
|
+
ref: anchorRef,
|
|
908
|
+
className: "dsh-auto-combobox-control" + (Icon ? "" : " dsh-auto-combobox-control-no-icon"),
|
|
909
|
+
"data-open": open ? "true" : undefined,
|
|
910
|
+
onPointerDown: (event) => {
|
|
911
|
+
if (disabled) return;
|
|
912
|
+
if (event.target === inputRef.current) return;
|
|
913
|
+
event.preventDefault();
|
|
914
|
+
if (open) close();
|
|
915
|
+
else {
|
|
916
|
+
setSearching(resumeSearch);
|
|
917
|
+
show();
|
|
918
|
+
requestAnimationFrame(() => {
|
|
919
|
+
inputRef.current?.focus();
|
|
920
|
+
inputRef.current?.select();
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
},
|
|
925
|
+
Icon
|
|
926
|
+
? h("span", { className: "dsh-auto-combobox-control-icon", "aria-hidden": "true" }, h(Icon))
|
|
927
|
+
: null,
|
|
928
|
+
h("input", {
|
|
929
|
+
ref: inputRef,
|
|
930
|
+
id,
|
|
931
|
+
className: "dsh-auto-combobox-input",
|
|
932
|
+
type: "text",
|
|
933
|
+
role: "combobox",
|
|
934
|
+
value,
|
|
935
|
+
placeholder,
|
|
936
|
+
disabled,
|
|
937
|
+
autoComplete: "off",
|
|
938
|
+
spellCheck: false,
|
|
939
|
+
"aria-autocomplete": "list",
|
|
940
|
+
"aria-haspopup": "listbox",
|
|
941
|
+
"aria-expanded": open,
|
|
942
|
+
"aria-controls": open ? listboxId : undefined,
|
|
943
|
+
"aria-activedescendant": open && active >= 0 ? listboxId + "-option-" + active : undefined,
|
|
944
|
+
"aria-invalid": !open && invalid ? "true" : undefined,
|
|
945
|
+
onFocus: (event) => {
|
|
946
|
+
setSearching(resumeSearch);
|
|
947
|
+
setActiveIndex(-1);
|
|
948
|
+
show();
|
|
949
|
+
event.currentTarget.select();
|
|
950
|
+
},
|
|
951
|
+
onClick: () => {
|
|
952
|
+
if (open) return;
|
|
953
|
+
setSearching(resumeSearch);
|
|
954
|
+
setActiveIndex(-1);
|
|
955
|
+
show();
|
|
956
|
+
},
|
|
957
|
+
onBlur: (event) => {
|
|
958
|
+
const next = event.relatedTarget;
|
|
959
|
+
if (next instanceof Node && (anchorRef.current?.contains(next) || panelRef.current?.contains(next))) return;
|
|
960
|
+
close();
|
|
961
|
+
},
|
|
962
|
+
onChange: (event) => {
|
|
963
|
+
setSearching(true);
|
|
964
|
+
setActiveIndex(0);
|
|
965
|
+
show();
|
|
966
|
+
onChange(event.target.value);
|
|
967
|
+
},
|
|
968
|
+
onKeyDown: onInputKeyDown,
|
|
969
|
+
}),
|
|
970
|
+
h(
|
|
971
|
+
"span",
|
|
972
|
+
{
|
|
973
|
+
className: "dsh-auto-combobox-chevron" + (open ? " dsh-auto-combobox-chevron-open" : ""),
|
|
974
|
+
"aria-hidden": "true",
|
|
975
|
+
},
|
|
976
|
+
h(IconChevronDownOutline14),
|
|
977
|
+
),
|
|
978
|
+
),
|
|
979
|
+
open && typeof document !== "undefined" ? createPortal(panel, document.body) : null,
|
|
980
|
+
h("span", { className: "dsh-auto-sr-only", role: "status", "aria-live": "polite" }, announcement),
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
function TimeZonePicker({ id, value, onChange }) {
|
|
985
|
+
const offsetCacheRef = useRef(new Map());
|
|
986
|
+
const selected = canonicalTimezone(value);
|
|
987
|
+
const optionsForQuery = (query) => timezoneSearchResults(query, value).map((zone) => {
|
|
988
|
+
const copy = timezoneOptionPresentation(zone, offsetCacheRef.current);
|
|
989
|
+
return {
|
|
990
|
+
id: zone,
|
|
991
|
+
value: timezonePreferredValue(zone),
|
|
992
|
+
label: copy.label,
|
|
993
|
+
detail: copy.detail,
|
|
994
|
+
};
|
|
995
|
+
});
|
|
996
|
+
return h(EditableCombobox, {
|
|
997
|
+
id,
|
|
998
|
+
value,
|
|
999
|
+
onChange,
|
|
1000
|
+
optionsForQuery,
|
|
1001
|
+
selectedId: selected,
|
|
1002
|
+
commitExactValue: (raw) => {
|
|
1003
|
+
const canonical = canonicalTimezone(raw);
|
|
1004
|
+
return canonical ? timezonePreferredValue(canonical) : null;
|
|
1005
|
+
},
|
|
1006
|
+
placeholder: "Search city or time zone",
|
|
1007
|
+
Icon: IconGlobeOutline14,
|
|
1008
|
+
listboxLabel: "Time zones",
|
|
1009
|
+
initialTitle: "Suggested time zones",
|
|
1010
|
+
searchTitle: "Matching time zones",
|
|
1011
|
+
emptyText: "No matching time zones. You can still enter a recognized IANA time zone, such as Europe/Berlin.",
|
|
1012
|
+
hintText: "Type a city, region, or IANA time zone.",
|
|
1013
|
+
resultNoun: "time zone",
|
|
1014
|
+
invalidMessage: "Enter a valid IANA time zone, such as Europe/Berlin.",
|
|
1015
|
+
invalid: value.trim() !== "" && selected === null,
|
|
1016
|
+
resumeSearch: value.trim() !== "" && selected === null,
|
|
1017
|
+
onOpen: () => offsetCacheRef.current.clear(),
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function normalizedProviders(providers) {
|
|
1022
|
+
return (Array.isArray(providers) ? providers : [])
|
|
1023
|
+
.filter((provider) => provider && typeof provider.id === "string" && provider.id !== "")
|
|
1024
|
+
.map((provider) => ({
|
|
1025
|
+
id: provider.id,
|
|
1026
|
+
name: typeof provider.name === "string" && provider.name !== "" ? provider.name : provider.id,
|
|
1027
|
+
models: (Array.isArray(provider.models) ? provider.models : [])
|
|
1028
|
+
.map((model) => typeof model === "string"
|
|
1029
|
+
? { id: model, name: model }
|
|
1030
|
+
: model && typeof model.id === "string"
|
|
1031
|
+
? {
|
|
1032
|
+
id: model.id,
|
|
1033
|
+
name: typeof model.name === "string" && model.name !== "" ? model.name : model.id,
|
|
1034
|
+
...(typeof model.description === "string" ? { description: model.description } : {}),
|
|
1035
|
+
}
|
|
1036
|
+
: null)
|
|
1037
|
+
.filter(Boolean),
|
|
1038
|
+
}));
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function ProviderPicker({ id, value, providers, defaultModel, onChange }) {
|
|
1042
|
+
const catalog = normalizedProviders(providers);
|
|
1043
|
+
const known = value === "" || catalog.some((provider) => provider.id === value);
|
|
1044
|
+
const options = [
|
|
1045
|
+
{
|
|
1046
|
+
id: "",
|
|
1047
|
+
value: "",
|
|
1048
|
+
label: "Harness default",
|
|
1049
|
+
detail: defaultModel && defaultModel.provider && defaultModel.model
|
|
1050
|
+
? "Use " + defaultModel.provider + " / " + defaultModel.model + "."
|
|
1051
|
+
: "Resolve the current Harness provider at run time.",
|
|
1052
|
+
Icon: IconSettingsOutline16,
|
|
1053
|
+
},
|
|
1054
|
+
...catalog.map((provider) => ({
|
|
1055
|
+
id: provider.id,
|
|
1056
|
+
value: provider.id,
|
|
1057
|
+
label: provider.name,
|
|
1058
|
+
detail: (provider.name === provider.id ? "Provider route" : provider.id)
|
|
1059
|
+
+ " · " + provider.models.length + (provider.models.length === 1 ? " model" : " models"),
|
|
1060
|
+
aliases: [provider.id],
|
|
1061
|
+
Icon: IconSettingsOutline16,
|
|
1062
|
+
})),
|
|
1063
|
+
...(value.trim() !== "" && !known
|
|
1064
|
+
? [{
|
|
1065
|
+
id: "custom-provider:" + value,
|
|
1066
|
+
value,
|
|
1067
|
+
label: value,
|
|
1068
|
+
detail: "Custom provider route · Press Enter to keep this value.",
|
|
1069
|
+
custom: true,
|
|
1070
|
+
Icon: IconSettingsOutline16,
|
|
1071
|
+
}]
|
|
1072
|
+
: []),
|
|
1073
|
+
];
|
|
1074
|
+
return h(EditableCombobox, {
|
|
1075
|
+
id,
|
|
1076
|
+
value,
|
|
1077
|
+
onChange,
|
|
1078
|
+
optionsForQuery: (query) => comboboxSearchResults(options, query),
|
|
1079
|
+
selectedId: value === "" ? "" : known ? value : "custom-provider:" + value,
|
|
1080
|
+
commitExactValue: (raw) => raw.trim(),
|
|
1081
|
+
placeholder: "Harness default",
|
|
1082
|
+
Icon: IconSettingsOutline16,
|
|
1083
|
+
listboxLabel: "Providers",
|
|
1084
|
+
initialTitle: "Available providers",
|
|
1085
|
+
searchTitle: "Matching providers",
|
|
1086
|
+
emptyText: "No matching providers. Press Enter to keep this custom provider route.",
|
|
1087
|
+
hintText: "Choose a configured provider or enter an adapter-supported route.",
|
|
1088
|
+
resultNoun: "provider",
|
|
1089
|
+
invalidMessage: "Enter a provider route.",
|
|
1090
|
+
resumeSearch: value.trim() !== "" && !known,
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function useExactModelMetadata(provider, model) {
|
|
1095
|
+
const cacheKey = provider && model ? provider + "\u0000" + model : "";
|
|
1096
|
+
const [state, setState] = useState(() => {
|
|
1097
|
+
const cached = cacheKey ? MODEL_METADATA_CACHE.get(cacheKey) : null;
|
|
1098
|
+
return cached
|
|
1099
|
+
? { key: cacheKey, status: "ready", data: cached, error: null }
|
|
1100
|
+
: { key: cacheKey, status: cacheKey ? "loading" : "idle", data: null, error: null };
|
|
1101
|
+
});
|
|
1102
|
+
useEffect(() => {
|
|
1103
|
+
if (!cacheKey) {
|
|
1104
|
+
setState({ key: "", status: "idle", data: null, error: null });
|
|
1105
|
+
return undefined;
|
|
1106
|
+
}
|
|
1107
|
+
const cached = MODEL_METADATA_CACHE.get(cacheKey);
|
|
1108
|
+
if (cached) {
|
|
1109
|
+
setState({ key: cacheKey, status: "ready", data: cached, error: null });
|
|
1110
|
+
return undefined;
|
|
1111
|
+
}
|
|
1112
|
+
const controller = new AbortController();
|
|
1113
|
+
let active = true;
|
|
1114
|
+
setState({ key: cacheKey, status: "loading", data: null, error: null });
|
|
1115
|
+
const timer = window.setTimeout(() => {
|
|
1116
|
+
apiFetch(
|
|
1117
|
+
"/meta/model?provider=" + encodeURIComponent(provider) + "&model=" + encodeURIComponent(model),
|
|
1118
|
+
{ signal: controller.signal },
|
|
1119
|
+
).then((data) => {
|
|
1120
|
+
MODEL_METADATA_CACHE.set(cacheKey, data);
|
|
1121
|
+
if (active) setState({ key: cacheKey, status: "ready", data, error: null });
|
|
1122
|
+
}).catch((error) => {
|
|
1123
|
+
if (!active || (error && error.name === "AbortError")) return;
|
|
1124
|
+
setState({ key: cacheKey, status: "error", data: null, error: errMessage(error) });
|
|
1125
|
+
});
|
|
1126
|
+
}, 180);
|
|
1127
|
+
return () => {
|
|
1128
|
+
active = false;
|
|
1129
|
+
window.clearTimeout(timer);
|
|
1130
|
+
controller.abort();
|
|
1131
|
+
};
|
|
1132
|
+
}, [cacheKey, provider, model]);
|
|
1133
|
+
if (state.key === cacheKey) return state;
|
|
1134
|
+
const cached = cacheKey ? MODEL_METADATA_CACHE.get(cacheKey) : null;
|
|
1135
|
+
return cached
|
|
1136
|
+
? { key: cacheKey, status: "ready", data: cached, error: null }
|
|
1137
|
+
: { key: cacheKey, status: cacheKey ? "loading" : "idle", data: null, error: null };
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function ReasoningEffortPicker({ id, value, provider, model, defaultModel, onChange }) {
|
|
1141
|
+
const inherited = provider.trim() === "" && model.trim() === "";
|
|
1142
|
+
const exactProvider = inherited ? String(defaultModel?.provider || "") : provider.trim();
|
|
1143
|
+
const exactModel = inherited ? String(defaultModel?.model || "") : model.trim();
|
|
1144
|
+
const modelState = useExactModelMetadata(exactProvider, exactModel);
|
|
1145
|
+
const reasoning = modelState.status === "ready" && modelState.data
|
|
1146
|
+
? modelState.data.reasoning
|
|
1147
|
+
: undefined;
|
|
1148
|
+
const efforts = reasoning && Array.isArray(reasoning.efforts) ? reasoning.efforts : [];
|
|
1149
|
+
const known = value === "" || efforts.some((effort) => effort.id === value);
|
|
1150
|
+
const advertisedDefault = inherited
|
|
1151
|
+
? defaultModel?.reasoningEffort
|
|
1152
|
+
: reasoning?.defaultEffort;
|
|
1153
|
+
const defaultEffort = efforts.find((effort) => effort.id === advertisedDefault);
|
|
1154
|
+
const defaultDetail = advertisedDefault
|
|
1155
|
+
? "Use " + (defaultEffort?.name || advertisedDefault) + (inherited ? " from the current Harness selection." : " as this model's default.")
|
|
1156
|
+
: inherited
|
|
1157
|
+
? "Use the current Harness reasoning setting at run time."
|
|
1158
|
+
: "Let the selected model or provider choose.";
|
|
1159
|
+
const options = [
|
|
1160
|
+
{
|
|
1161
|
+
id: "",
|
|
1162
|
+
value: "",
|
|
1163
|
+
label: "Default",
|
|
1164
|
+
detail: defaultDetail,
|
|
1165
|
+
Icon: IconThinkOutline14,
|
|
1166
|
+
},
|
|
1167
|
+
...efforts.map((effort) => ({
|
|
1168
|
+
id: effort.id,
|
|
1169
|
+
value: effort.id,
|
|
1170
|
+
label: effort.name || effort.id,
|
|
1171
|
+
detail: effort.description || (effort.name === effort.id ? "Adapter-owned effort level." : effort.id),
|
|
1172
|
+
aliases: [effort.id],
|
|
1173
|
+
Icon: IconThinkOutline14,
|
|
1174
|
+
})),
|
|
1175
|
+
...(value.trim() !== "" && !known
|
|
1176
|
+
? [{
|
|
1177
|
+
id: "custom-effort:" + value,
|
|
1178
|
+
value,
|
|
1179
|
+
label: value,
|
|
1180
|
+
detail: "Custom effort ID · Availability is checked when the automation runs.",
|
|
1181
|
+
custom: true,
|
|
1182
|
+
Icon: IconThinkOutline14,
|
|
1183
|
+
}]
|
|
1184
|
+
: []),
|
|
1185
|
+
];
|
|
1186
|
+
let panelNotice = null;
|
|
1187
|
+
if (!exactProvider || !exactModel) {
|
|
1188
|
+
panelNotice = "Choose a model to load its effort levels.";
|
|
1189
|
+
} else if (modelState.status === "loading") {
|
|
1190
|
+
panelNotice = "Loading effort levels for " + exactProvider + " / " + exactModel + "…";
|
|
1191
|
+
} else if (modelState.status === "error") {
|
|
1192
|
+
panelNotice = "Effort metadata is unavailable. You can still enter an adapter-supported ID.";
|
|
1193
|
+
} else if (modelState.status === "ready" && !reasoning) {
|
|
1194
|
+
panelNotice = "This model does not advertise selectable effort levels. Custom IDs remain available.";
|
|
1195
|
+
}
|
|
1196
|
+
return h(EditableCombobox, {
|
|
1197
|
+
id,
|
|
1198
|
+
value,
|
|
1199
|
+
onChange,
|
|
1200
|
+
optionsForQuery: (query) => comboboxSearchResults(options, query),
|
|
1201
|
+
selectedId: value === "" ? "" : known ? value : "custom-effort:" + value,
|
|
1202
|
+
commitExactValue: (raw) => raw.trim(),
|
|
1203
|
+
placeholder: "Default",
|
|
1204
|
+
Icon: IconThinkOutline14,
|
|
1205
|
+
listboxLabel: "Reasoning effort levels",
|
|
1206
|
+
initialTitle: "Reasoning effort",
|
|
1207
|
+
searchTitle: "Matching effort levels",
|
|
1208
|
+
emptyText: "No matching effort levels. Press Enter to keep this custom effort ID.",
|
|
1209
|
+
hintText: "Options come from the exact model; custom adapter-owned IDs remain accepted.",
|
|
1210
|
+
resultNoun: "effort level",
|
|
1211
|
+
invalidMessage: "Enter an effort ID or choose Default.",
|
|
1212
|
+
resumeSearch: value.trim() !== "" && !known,
|
|
1213
|
+
panelNotice,
|
|
1214
|
+
disabled: provider.trim() !== "" && model.trim() === "" && value.trim() === "",
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
|
|
449
1218
|
function AgentPresetPicker({ id, value, presets, onChange }) {
|
|
450
1219
|
const [open, setOpen] = useState(false);
|
|
451
1220
|
const ownerId = useId();
|
|
@@ -608,7 +1377,7 @@ window.__ModuleLoader__.load({
|
|
|
608
1377
|
|
|
609
1378
|
function JobForm(props) {
|
|
610
1379
|
const { meta, draft, editing, saving, error, onChange, onIdTouched, onSubmit, onCancel } = props;
|
|
611
|
-
const providers = meta && Array.isArray(meta.providers) ? meta.providers : [];
|
|
1380
|
+
const providers = normalizedProviders(meta && Array.isArray(meta.providers) ? meta.providers : []);
|
|
612
1381
|
const providerModels = providers.find((provider) => provider.id === draft.provider);
|
|
613
1382
|
const models = providerModels ? providerModels.models : [];
|
|
614
1383
|
const permissionPresets =
|
|
@@ -692,21 +1461,17 @@ window.__ModuleLoader__.load({
|
|
|
692
1461
|
),
|
|
693
1462
|
h(
|
|
694
1463
|
Field,
|
|
695
|
-
{
|
|
696
|
-
|
|
1464
|
+
{
|
|
1465
|
+
label: "Time zone",
|
|
1466
|
+
htmlFor: fieldId("timezone"),
|
|
1467
|
+
required: true,
|
|
1468
|
+
hint: "Enter an IANA time zone. Current UTC offsets may change with daylight saving time.",
|
|
1469
|
+
},
|
|
1470
|
+
h(TimeZonePicker, {
|
|
697
1471
|
id: fieldId("timezone"),
|
|
698
|
-
className: "dsh-auto-input",
|
|
699
|
-
type: "text",
|
|
700
|
-
list: fieldId("timezone-list"),
|
|
701
1472
|
value: draft.timezone,
|
|
702
|
-
|
|
703
|
-
onChange: (event) => onChange("timezone", event.target.value),
|
|
1473
|
+
onChange: (value) => onChange("timezone", value),
|
|
704
1474
|
}),
|
|
705
|
-
h(
|
|
706
|
-
"datalist",
|
|
707
|
-
{ id: fieldId("timezone-list") },
|
|
708
|
-
TIMEZONES.map((zone) => h("option", { key: zone, value: zone })),
|
|
709
|
-
),
|
|
710
1475
|
),
|
|
711
1476
|
h(
|
|
712
1477
|
Field,
|
|
@@ -740,21 +1505,13 @@ window.__ModuleLoader__.load({
|
|
|
740
1505
|
h(
|
|
741
1506
|
Field,
|
|
742
1507
|
{ label: "Provider", htmlFor: fieldId("provider"), hint: "Blank uses the current Harness default. You may type an unlisted provider route." },
|
|
743
|
-
h(
|
|
1508
|
+
h(ProviderPicker, {
|
|
744
1509
|
id: fieldId("provider"),
|
|
745
|
-
className: "dsh-auto-input",
|
|
746
|
-
type: "text",
|
|
747
|
-
list: fieldId("provider-list"),
|
|
748
1510
|
value: draft.provider,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
onChange: (
|
|
1511
|
+
providers,
|
|
1512
|
+
defaultModel: meta?.defaultModel,
|
|
1513
|
+
onChange: (value) => onChange("provider", value),
|
|
752
1514
|
}),
|
|
753
|
-
h(
|
|
754
|
-
"datalist",
|
|
755
|
-
{ id: fieldId("provider-list") },
|
|
756
|
-
providers.map((provider) => h("option", { key: provider.id, value: provider.id })),
|
|
757
|
-
),
|
|
758
1515
|
),
|
|
759
1516
|
h(
|
|
760
1517
|
Field,
|
|
@@ -780,27 +1537,24 @@ window.__ModuleLoader__.load({
|
|
|
780
1537
|
h(
|
|
781
1538
|
"datalist",
|
|
782
1539
|
{ id: fieldId("model-list") },
|
|
783
|
-
models.map((model) => h("option", {
|
|
1540
|
+
models.map((model) => h("option", {
|
|
1541
|
+
key: model.id,
|
|
1542
|
+
value: model.id,
|
|
1543
|
+
label: model.name === model.id ? undefined : model.name,
|
|
1544
|
+
})),
|
|
784
1545
|
),
|
|
785
1546
|
),
|
|
786
1547
|
h(
|
|
787
1548
|
Field,
|
|
788
|
-
{ label: "Reasoning effort", htmlFor: fieldId("effort"), hint: "
|
|
789
|
-
h(
|
|
1549
|
+
{ label: "Reasoning effort", htmlFor: fieldId("effort"), hint: "Default follows the selected model. Custom adapter-owned IDs remain supported." },
|
|
1550
|
+
h(ReasoningEffortPicker, {
|
|
790
1551
|
id: fieldId("effort"),
|
|
791
|
-
className: "dsh-auto-input",
|
|
792
|
-
type: "text",
|
|
793
|
-
list: fieldId("effort-list"),
|
|
794
1552
|
value: draft.reasoningEffort,
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1553
|
+
provider: draft.provider,
|
|
1554
|
+
model: draft.model,
|
|
1555
|
+
defaultModel: meta?.defaultModel,
|
|
1556
|
+
onChange: (value) => onChange("reasoningEffort", value),
|
|
798
1557
|
}),
|
|
799
|
-
h(
|
|
800
|
-
"datalist",
|
|
801
|
-
{ id: fieldId("effort-list") },
|
|
802
|
-
REASONING_EFFORTS.map((effort) => h("option", { key: effort, value: effort })),
|
|
803
|
-
),
|
|
804
1558
|
),
|
|
805
1559
|
h(
|
|
806
1560
|
Field,
|
|
@@ -1348,7 +2102,9 @@ window.__ModuleLoader__.load({
|
|
|
1348
2102
|
next = Object.assign({}, next, { id: slugifyJobId(value) });
|
|
1349
2103
|
}
|
|
1350
2104
|
if (key === "provider") {
|
|
1351
|
-
next = Object.assign({}, next, { model: "" });
|
|
2105
|
+
next = Object.assign({}, next, { model: "", reasoningEffort: "" });
|
|
2106
|
+
} else if (key === "model") {
|
|
2107
|
+
next = Object.assign({}, next, { reasoningEffort: "" });
|
|
1352
2108
|
}
|
|
1353
2109
|
return next;
|
|
1354
2110
|
});
|
|
@@ -1657,7 +2413,7 @@ window.__ModuleLoader__.load({
|
|
|
1657
2413
|
const onKeyDown = (event) => {
|
|
1658
2414
|
if (event.key !== "Escape" || event.defaultPrevented || event.__dshAutomationsNested) return;
|
|
1659
2415
|
if (event.target?.closest?.('[role="dialog"],[role="menu"]')) return;
|
|
1660
|
-
if (document.querySelector('.dsh-auto-picker-trigger[aria-expanded="true"]')) return;
|
|
2416
|
+
if (document.querySelector('.dsh-auto-picker-trigger[aria-expanded="true"],.dsh-auto-combobox-input[aria-expanded="true"]')) return;
|
|
1661
2417
|
event.preventDefault();
|
|
1662
2418
|
disclosure.close();
|
|
1663
2419
|
};
|
|
@@ -1745,7 +2501,34 @@ window.__ModuleLoader__.load({
|
|
|
1745
2501
|
".dsh-auto-picker-chevron-open{transform:rotate(180deg);}",
|
|
1746
2502
|
".dsh-auto-picker-item-copy{min-width:0;display:flex;flex-direction:column;white-space:normal;}",
|
|
1747
2503
|
".dsh-auto-picker-item-label{color:inherit;font-size:14px;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
1748
|
-
".dsh-auto-picker-item-detail{color:var(--dsh-auto-muted);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
2504
|
+
".dsh-auto-picker-item-detail{color:var(--dsh-auto-muted,var(--dsw-alias-label-tertiary,#81858c));font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
2505
|
+
".dsh-auto-combobox-control{width:100%;height:34px;padding:0 8px;display:flex;align-items:center;gap:7px;border:1px solid var(--dsh-auto-border);border-radius:8px;background:var(--dsh-auto-input-bg);color:var(--dsh-auto-text);cursor:text;}",
|
|
2506
|
+
".dsh-auto-combobox-control:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsh-auto-input-bg));}",
|
|
2507
|
+
".dsh-auto-combobox-control:focus-within,.dsh-auto-combobox-control[data-open=\"true\"]{border-color:var(--dsh-auto-accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsh-auto-accent) 18%,transparent);}",
|
|
2508
|
+
".dsh-auto-combobox-control:has(.dsh-auto-combobox-input[aria-invalid=\"true\"]){border-color:var(--dsh-auto-danger);}",
|
|
2509
|
+
".dsh-auto-combobox-control:has(.dsh-auto-combobox-input:disabled){background:var(--dsh-auto-surface-active);color:var(--dsh-auto-muted);cursor:default;}",
|
|
2510
|
+
".dsh-auto-combobox-control-icon,.dsh-auto-combobox-chevron{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsh-auto-muted);pointer-events:none;}",
|
|
2511
|
+
".dsh-auto-combobox-input{appearance:none;min-width:0;height:100%;flex:1;padding:0;border:0;outline:0;background:transparent;color:inherit;font:inherit;font-size:13px;line-height:20px;}",
|
|
2512
|
+
".dsh-auto-combobox-input::placeholder{color:var(--dsh-auto-caption);}",
|
|
2513
|
+
".dsh-auto-combobox-input:disabled{cursor:default;}",
|
|
2514
|
+
".dsh-auto-combobox-chevron{transition:transform .12s ease;}",
|
|
2515
|
+
".dsh-auto-combobox-chevron-open{transform:rotate(180deg);}",
|
|
2516
|
+
".dsh-auto-combobox-popover{box-sizing:border-box;position:fixed;z-index:1100;max-height:min(360px,calc(100vh - 24px));padding:4px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-inverted,var(--dsw-alias-border-l2,rgba(15,17,21,.16)));border-radius:12px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,#fff));box-shadow:var(--dsw-shadow-lv3,0 12px 32px rgba(15,17,21,.16));color:var(--dsw-alias-label-primary,#0f1115);font-family:inherit;}",
|
|
2517
|
+
".dsh-auto-combobox-popover *,.dsh-auto-combobox-popover *::before,.dsh-auto-combobox-popover *::after{box-sizing:border-box;}",
|
|
2518
|
+
".dsh-auto-combobox-popover-title{flex:none;padding:7px 10px 5px;color:var(--dsw-alias-label-tertiary,#81858c);font-size:11px;font-weight:600;line-height:16px;text-transform:uppercase;letter-spacing:.04em;}",
|
|
2519
|
+
".dsh-auto-combobox-notice{flex:none;margin:2px 4px 4px;padding:7px 9px;border-radius:8px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06)));color:var(--dsw-alias-label-secondary,#4f5661);font-size:12px;line-height:18px;}",
|
|
2520
|
+
".dsh-auto-combobox-list{min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);}",
|
|
2521
|
+
".dsh-auto-combobox-option{width:100%;min-height:48px;padding:6px 9px;display:flex;align-items:center;gap:8px;border-radius:9px;cursor:pointer;user-select:none;}",
|
|
2522
|
+
".dsh-auto-combobox-option:hover,.dsh-auto-combobox-option[data-active=\"true\"]{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));}",
|
|
2523
|
+
".dsh-auto-combobox-option[data-disabled=\"true\"]{cursor:default;color:var(--dsw-alias-label-dimmed,var(--dsh-auto-caption));}",
|
|
2524
|
+
".dsh-auto-combobox-option[data-disabled=\"true\"]:hover{background:transparent;}",
|
|
2525
|
+
".dsh-auto-combobox-option[aria-selected=\"true\"]{color:var(--dsw-alias-state-business-primary,#4176e6);}",
|
|
2526
|
+
".dsh-auto-combobox-option-icon,.dsh-auto-combobox-option-check{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858c);}",
|
|
2527
|
+
".dsh-auto-combobox-option>.dsh-auto-picker-item-copy{min-width:0;flex:1;}",
|
|
2528
|
+
".dsh-auto-combobox-option .dsh-auto-picker-item-detail{color:var(--dsw-alias-label-tertiary,#81858c);}",
|
|
2529
|
+
".dsh-auto-combobox-option-check{color:var(--dsw-alias-state-business-primary,#4176e6);}",
|
|
2530
|
+
".dsh-auto-combobox-empty{margin:0;padding:16px 12px;color:var(--dsw-alias-label-secondary,#4f5661);font-size:12px;line-height:18px;text-align:center;}",
|
|
2531
|
+
".dsh-auto-combobox-popover-hint{flex:none;margin:4px 0 0;padding:7px 10px 5px;border-top:1px solid var(--dsw-alias-border-l2,rgba(15,17,21,.12));color:var(--dsw-alias-label-tertiary,#81858c);font-size:11px;line-height:16px;}",
|
|
1749
2532
|
".dsh-auto-permission-glyph{flex:none;}",
|
|
1750
2533
|
".dsh-auto-permission-glyph-read{color:var(--dsh-auto-muted);}",
|
|
1751
2534
|
".dsh-auto-permission-glyph-write{color:var(--dsh-auto-accent);}",
|
|
@@ -1888,7 +2671,21 @@ window.__ModuleLoader__.load({
|
|
|
1888
2671
|
exports.agentPresetDisplayLabel = agentPresetDisplayLabel;
|
|
1889
2672
|
exports.permissionPresetPresentation = permissionPresetPresentation;
|
|
1890
2673
|
exports.PermissionPresetPicker = PermissionPresetPicker;
|
|
1891
|
-
exports.
|
|
2674
|
+
exports.EditableCombobox = EditableCombobox;
|
|
2675
|
+
exports.ProviderPicker = ProviderPicker;
|
|
2676
|
+
exports.ReasoningEffortPicker = ReasoningEffortPicker;
|
|
2677
|
+
exports.TimeZonePicker = TimeZonePicker;
|
|
2678
|
+
exports.__testing = Object.freeze({
|
|
2679
|
+
nextFormInstancePrefix,
|
|
2680
|
+
canonicalTimezone,
|
|
2681
|
+
comboboxSearchResults,
|
|
2682
|
+
normalizedProviders,
|
|
2683
|
+
timezoneOffsetLabel,
|
|
2684
|
+
timezoneNavigationIndex,
|
|
2685
|
+
timezoneOptionPresentation,
|
|
2686
|
+
timezonePreferredValue,
|
|
2687
|
+
timezoneSearchResults,
|
|
2688
|
+
});
|
|
1892
2689
|
exports.inject = inject;
|
|
1893
2690
|
exports.apply = apply;
|
|
1894
2691
|
return module.exports;
|