@syncended/dsh-automations 0.3.0 → 0.4.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 +1 -1
- package/lib/client.js +533 -33
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ dsh plugin --profile web remove @syncended/dsh-automations
|
|
|
56
56
|
|---|---|
|
|
57
57
|
| Name | Human-readable job name. |
|
|
58
58
|
| Cron | Five fields: `minute hour day-of-month month day-of-week`. Example: `0 9 * * 1-5`. |
|
|
59
|
-
|
|
|
59
|
+
| Time zone | Search by city or region in the picker, or enter `UTC` / an IANA name such as `Europe/Berlin`. Current UTC offsets are shown; DST is handled by `cron-parser`. |
|
|
60
60
|
| Project | Existing absolute directory. Its canonical filesystem identity becomes the Session cwd and `workspace-write` root. |
|
|
61
61
|
| Prompt | The user message sent to a fresh Harness Agent. |
|
|
62
62
|
| Provider / model | Leave both blank to resolve the current Harness default at dispatch time. Set both to pin a route. |
|
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,9 +24,11 @@ window.__ModuleLoader__.load({
|
|
|
23
24
|
IconChevronDownOutline14,
|
|
24
25
|
IconChevronLeftOutline14,
|
|
25
26
|
IconEditOutline16,
|
|
27
|
+
IconGlobeOutline14,
|
|
26
28
|
IconPlayOutline16,
|
|
27
29
|
IconPlusOutline16,
|
|
28
30
|
IconRefreshOutline16,
|
|
31
|
+
IconSearchOutline16,
|
|
29
32
|
IconSettingsOutline16,
|
|
30
33
|
IconTrashOutline16,
|
|
31
34
|
IconWarningOutline16,
|
|
@@ -40,6 +43,7 @@ window.__ModuleLoader__.load({
|
|
|
40
43
|
const MIN_TIMEOUT_MS = 1000;
|
|
41
44
|
const MAX_TIMEOUT_MS = 86400000;
|
|
42
45
|
const JOB_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
46
|
+
let formInstanceSerial = 0;
|
|
43
47
|
|
|
44
48
|
const BROWSER_TIMEZONE = (() => {
|
|
45
49
|
try {
|
|
@@ -49,24 +53,75 @@ window.__ModuleLoader__.load({
|
|
|
49
53
|
}
|
|
50
54
|
})();
|
|
51
55
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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)),
|
|
69
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
|
+
});
|
|
70
125
|
|
|
71
126
|
const REASONING_EFFORTS = ["low", "medium", "high"];
|
|
72
127
|
const OVERLAP_OPTIONS = [
|
|
@@ -146,6 +201,110 @@ window.__ModuleLoader__.load({
|
|
|
146
201
|
});
|
|
147
202
|
}
|
|
148
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 timezoneNavigationIndex(key, current, resultCount, reopening) {
|
|
282
|
+
if (resultCount === 0) return -1;
|
|
283
|
+
if (reopening) return key === "ArrowDown" ? 0 : resultCount - 1;
|
|
284
|
+
if (key === "ArrowDown") return current < 0 || current >= resultCount - 1 ? 0 : current + 1;
|
|
285
|
+
return current <= 0 ? resultCount - 1 : current - 1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function timezoneOptionPresentation(zone, offsetCache) {
|
|
289
|
+
let offset = offsetCache?.get(zone);
|
|
290
|
+
if (!offset) {
|
|
291
|
+
offset = timezoneOffsetLabel(zone);
|
|
292
|
+
offsetCache?.set(zone, offset);
|
|
293
|
+
}
|
|
294
|
+
const current = BROWSER_TIMEZONE === zone;
|
|
295
|
+
const preferred = timezonePreferredValue(zone);
|
|
296
|
+
if (zone === "UTC") {
|
|
297
|
+
return {
|
|
298
|
+
label: "UTC",
|
|
299
|
+
detail: offset + " now · Coordinated Universal Time" + (current ? " · Your browser time zone" : ""),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
label: timezoneCityLabel(preferred),
|
|
304
|
+
detail: preferred + " · " + offset + " now" + (current ? " · Your browser time zone" : ""),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
149
308
|
function browserTimezone() {
|
|
150
309
|
return BROWSER_TIMEZONE;
|
|
151
310
|
}
|
|
@@ -445,6 +604,310 @@ window.__ModuleLoader__.load({
|
|
|
445
604
|
);
|
|
446
605
|
}
|
|
447
606
|
|
|
607
|
+
function useTimeZonePopoverLayout(open, anchorRef, panelRef) {
|
|
608
|
+
const [layout, setLayout] = useState(null);
|
|
609
|
+
useEffect(() => {
|
|
610
|
+
if (!open) {
|
|
611
|
+
setLayout(null);
|
|
612
|
+
return undefined;
|
|
613
|
+
}
|
|
614
|
+
const place = () => {
|
|
615
|
+
const anchor = anchorRef.current;
|
|
616
|
+
if (!anchor) return;
|
|
617
|
+
const rect = anchor.getBoundingClientRect();
|
|
618
|
+
const panel = panelRef.current;
|
|
619
|
+
const margin = 12;
|
|
620
|
+
const gap = 4;
|
|
621
|
+
const width = Math.min(Math.max(rect.width, 280), window.innerWidth - margin * 2);
|
|
622
|
+
const left = Math.min(Math.max(rect.left, margin), window.innerWidth - width - margin);
|
|
623
|
+
const desiredHeight = Math.min(360, panel?.scrollHeight || 360);
|
|
624
|
+
const below = window.innerHeight - rect.bottom - gap - margin;
|
|
625
|
+
const above = rect.top - gap - margin;
|
|
626
|
+
const side = below >= Math.min(desiredHeight, 220) || below >= above ? "bottom" : "top";
|
|
627
|
+
const available = Math.max(80, side === "bottom" ? below : above);
|
|
628
|
+
const maxHeight = Math.min(360, available);
|
|
629
|
+
const renderedHeight = Math.min(panel?.offsetHeight || desiredHeight, maxHeight);
|
|
630
|
+
const top = side === "bottom"
|
|
631
|
+
? rect.bottom + gap
|
|
632
|
+
: Math.max(margin, rect.top - gap - renderedHeight);
|
|
633
|
+
setLayout((current) => {
|
|
634
|
+
const next = { left, top, width, maxHeight, side };
|
|
635
|
+
return current
|
|
636
|
+
&& current.left === next.left
|
|
637
|
+
&& current.top === next.top
|
|
638
|
+
&& current.width === next.width
|
|
639
|
+
&& current.maxHeight === next.maxHeight
|
|
640
|
+
&& current.side === next.side
|
|
641
|
+
? current
|
|
642
|
+
: next;
|
|
643
|
+
});
|
|
644
|
+
};
|
|
645
|
+
const frame = requestAnimationFrame(place);
|
|
646
|
+
window.addEventListener("scroll", place, true);
|
|
647
|
+
window.addEventListener("resize", place);
|
|
648
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(place);
|
|
649
|
+
if (anchorRef.current) observer?.observe(anchorRef.current);
|
|
650
|
+
if (panelRef.current) observer?.observe(panelRef.current);
|
|
651
|
+
return () => {
|
|
652
|
+
cancelAnimationFrame(frame);
|
|
653
|
+
observer?.disconnect();
|
|
654
|
+
window.removeEventListener("scroll", place, true);
|
|
655
|
+
window.removeEventListener("resize", place);
|
|
656
|
+
};
|
|
657
|
+
}, [open, anchorRef, panelRef]);
|
|
658
|
+
return layout;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function TimeZonePicker({ id, value, onChange }) {
|
|
662
|
+
const [open, setOpen] = useState(false);
|
|
663
|
+
const [searching, setSearching] = useState(false);
|
|
664
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
665
|
+
const [announcement, setAnnouncement] = useState("");
|
|
666
|
+
const anchorRef = useRef(null);
|
|
667
|
+
const inputRef = useRef(null);
|
|
668
|
+
const panelRef = useRef(null);
|
|
669
|
+
const offsetCacheRef = useRef(new Map());
|
|
670
|
+
const listboxId = id + "-options";
|
|
671
|
+
const query = searching ? value : "";
|
|
672
|
+
const results = timezoneSearchResults(query, value);
|
|
673
|
+
const active = activeIndex >= 0 && activeIndex < results.length ? activeIndex : -1;
|
|
674
|
+
const selected = canonicalTimezone(value);
|
|
675
|
+
const resumeSearch = value.trim() !== "" && selected === null;
|
|
676
|
+
const popoverLayout = useTimeZonePopoverLayout(open, anchorRef, panelRef);
|
|
677
|
+
const close = () => {
|
|
678
|
+
setOpen(false);
|
|
679
|
+
setSearching(false);
|
|
680
|
+
setActiveIndex(-1);
|
|
681
|
+
setAnnouncement("");
|
|
682
|
+
};
|
|
683
|
+
const show = () => {
|
|
684
|
+
offsetCacheRef.current.clear();
|
|
685
|
+
setAnnouncement("");
|
|
686
|
+
setOpen(true);
|
|
687
|
+
};
|
|
688
|
+
const choose = (zone) => {
|
|
689
|
+
onChange(timezonePreferredValue(zone));
|
|
690
|
+
close();
|
|
691
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
useEffect(() => {
|
|
695
|
+
if (!open) return undefined;
|
|
696
|
+
const dismiss = (event) => {
|
|
697
|
+
const target = event.target;
|
|
698
|
+
if (!(target instanceof Node)) return;
|
|
699
|
+
if (anchorRef.current?.contains(target) || panelRef.current?.contains(target)) return;
|
|
700
|
+
close();
|
|
701
|
+
};
|
|
702
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
703
|
+
return () => document.removeEventListener("pointerdown", dismiss, true);
|
|
704
|
+
}, [open]);
|
|
705
|
+
|
|
706
|
+
useEffect(() => {
|
|
707
|
+
if (!open) return undefined;
|
|
708
|
+
const timer = window.setTimeout(() => {
|
|
709
|
+
setAnnouncement(
|
|
710
|
+
results.length === 0
|
|
711
|
+
? "No matching time zones."
|
|
712
|
+
: results.length + (results.length === 1 ? " time zone suggestion available." : " time zone suggestions available."),
|
|
713
|
+
);
|
|
714
|
+
}, 250);
|
|
715
|
+
return () => window.clearTimeout(timer);
|
|
716
|
+
}, [open, query, results.length]);
|
|
717
|
+
|
|
718
|
+
useEffect(() => {
|
|
719
|
+
if (!open || active < 0) return;
|
|
720
|
+
document.getElementById(listboxId + "-option-" + active)?.scrollIntoView({ block: "nearest" });
|
|
721
|
+
}, [open, active, listboxId]);
|
|
722
|
+
|
|
723
|
+
const onInputKeyDown = (event) => {
|
|
724
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
725
|
+
event.preventDefault();
|
|
726
|
+
const navigationResults = !open && resumeSearch
|
|
727
|
+
? timezoneSearchResults(value, value)
|
|
728
|
+
: results;
|
|
729
|
+
if (!open) {
|
|
730
|
+
setSearching(resumeSearch);
|
|
731
|
+
show();
|
|
732
|
+
}
|
|
733
|
+
if (navigationResults.length === 0) {
|
|
734
|
+
setActiveIndex(-1);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
setActiveIndex((current) =>
|
|
738
|
+
timezoneNavigationIndex(event.key, current, navigationResults.length, !open),
|
|
739
|
+
);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
if (event.key === "Enter" && open) {
|
|
743
|
+
event.preventDefault();
|
|
744
|
+
if (active >= 0) {
|
|
745
|
+
choose(results[active]);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const canonical = canonicalTimezone(value);
|
|
749
|
+
if (canonical) choose(canonical);
|
|
750
|
+
else setAnnouncement("Enter a valid IANA time zone, such as Europe/Berlin.");
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (event.key === "Escape" && open) {
|
|
754
|
+
event.preventDefault();
|
|
755
|
+
event.stopPropagation();
|
|
756
|
+
(event.nativeEvent || event).__dshAutomationsNested = true;
|
|
757
|
+
close();
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (event.key === "Tab" && open) close();
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
const panel = open
|
|
764
|
+
? h(
|
|
765
|
+
"div",
|
|
766
|
+
{
|
|
767
|
+
ref: panelRef,
|
|
768
|
+
className: "dsh-auto-timezone-popover",
|
|
769
|
+
style: popoverLayout
|
|
770
|
+
? {
|
|
771
|
+
left: popoverLayout.left,
|
|
772
|
+
top: popoverLayout.top,
|
|
773
|
+
width: popoverLayout.width,
|
|
774
|
+
maxHeight: popoverLayout.maxHeight,
|
|
775
|
+
}
|
|
776
|
+
: { left: 12, top: 12, width: 320, visibility: "hidden" },
|
|
777
|
+
"data-side": popoverLayout?.side,
|
|
778
|
+
"data-dsh-auto-timezone-popover": "true",
|
|
779
|
+
},
|
|
780
|
+
h(
|
|
781
|
+
"div",
|
|
782
|
+
{ className: "dsh-auto-timezone-popover-title" },
|
|
783
|
+
searching && query.trim() !== "" ? "Matching time zones" : "Suggested time zones",
|
|
784
|
+
),
|
|
785
|
+
h(
|
|
786
|
+
"div",
|
|
787
|
+
{
|
|
788
|
+
id: listboxId,
|
|
789
|
+
className: "dsh-auto-timezone-list",
|
|
790
|
+
role: "listbox",
|
|
791
|
+
"aria-label": "Time zones",
|
|
792
|
+
},
|
|
793
|
+
results.length > 0
|
|
794
|
+
? results.map((zone, index) => {
|
|
795
|
+
const copy = timezoneOptionPresentation(zone, offsetCacheRef.current);
|
|
796
|
+
const optionId = listboxId + "-option-" + index;
|
|
797
|
+
return h(
|
|
798
|
+
"div",
|
|
799
|
+
{
|
|
800
|
+
key: zone,
|
|
801
|
+
id: optionId,
|
|
802
|
+
className: "dsh-auto-timezone-option",
|
|
803
|
+
role: "option",
|
|
804
|
+
"aria-selected": selected === zone,
|
|
805
|
+
"data-active": active === index ? "true" : undefined,
|
|
806
|
+
onPointerMove: () => setActiveIndex(index),
|
|
807
|
+
onPointerDown: (event) => event.preventDefault(),
|
|
808
|
+
onClick: () => choose(zone),
|
|
809
|
+
},
|
|
810
|
+
h("span", { className: "dsh-auto-timezone-option-icon", "aria-hidden": "true" }, h(IconGlobeOutline14)),
|
|
811
|
+
h(
|
|
812
|
+
"span",
|
|
813
|
+
{ className: "dsh-auto-picker-item-copy" },
|
|
814
|
+
h("span", { className: "dsh-auto-picker-item-label" }, copy.label),
|
|
815
|
+
h("span", { className: "dsh-auto-picker-item-detail" }, copy.detail),
|
|
816
|
+
),
|
|
817
|
+
selected === zone
|
|
818
|
+
? h("span", { className: "dsh-auto-timezone-option-check", "aria-hidden": "true" }, h(IconCheckOutline16))
|
|
819
|
+
: null,
|
|
820
|
+
);
|
|
821
|
+
})
|
|
822
|
+
: h(
|
|
823
|
+
"div",
|
|
824
|
+
{
|
|
825
|
+
className: "dsh-auto-timezone-empty",
|
|
826
|
+
role: "option",
|
|
827
|
+
"aria-disabled": "true",
|
|
828
|
+
"aria-selected": false,
|
|
829
|
+
},
|
|
830
|
+
"No matching time zones. You can still enter a recognized IANA time zone, such as Europe/Berlin.",
|
|
831
|
+
),
|
|
832
|
+
),
|
|
833
|
+
h("p", { className: "dsh-auto-timezone-popover-hint" }, "Type a city, region, or IANA time zone."),
|
|
834
|
+
)
|
|
835
|
+
: null;
|
|
836
|
+
|
|
837
|
+
return h(
|
|
838
|
+
React.Fragment,
|
|
839
|
+
null,
|
|
840
|
+
h(
|
|
841
|
+
"span",
|
|
842
|
+
{
|
|
843
|
+
ref: anchorRef,
|
|
844
|
+
className: "dsh-auto-timezone-control",
|
|
845
|
+
"data-open": open ? "true" : undefined,
|
|
846
|
+
onPointerDown: (event) => {
|
|
847
|
+
if (event.target === inputRef.current) return;
|
|
848
|
+
event.preventDefault();
|
|
849
|
+
if (open) close();
|
|
850
|
+
else {
|
|
851
|
+
setSearching(resumeSearch);
|
|
852
|
+
show();
|
|
853
|
+
requestAnimationFrame(() => {
|
|
854
|
+
inputRef.current?.focus();
|
|
855
|
+
inputRef.current?.select();
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
},
|
|
860
|
+
h("span", { className: "dsh-auto-timezone-control-icon", "aria-hidden": "true" }, h(IconGlobeOutline14)),
|
|
861
|
+
h("input", {
|
|
862
|
+
ref: inputRef,
|
|
863
|
+
id,
|
|
864
|
+
className: "dsh-auto-timezone-input",
|
|
865
|
+
type: "text",
|
|
866
|
+
role: "combobox",
|
|
867
|
+
value,
|
|
868
|
+
placeholder: "Search city or time zone",
|
|
869
|
+
autoComplete: "off",
|
|
870
|
+
spellCheck: false,
|
|
871
|
+
"aria-autocomplete": "list",
|
|
872
|
+
"aria-haspopup": "listbox",
|
|
873
|
+
"aria-expanded": open,
|
|
874
|
+
"aria-controls": open ? listboxId : undefined,
|
|
875
|
+
"aria-activedescendant": open && active >= 0 ? listboxId + "-option-" + active : undefined,
|
|
876
|
+
"aria-invalid": !open && value.trim() !== "" && selected === null ? "true" : undefined,
|
|
877
|
+
onFocus: (event) => {
|
|
878
|
+
setSearching(resumeSearch);
|
|
879
|
+
setActiveIndex(-1);
|
|
880
|
+
show();
|
|
881
|
+
event.currentTarget.select();
|
|
882
|
+
},
|
|
883
|
+
onClick: () => {
|
|
884
|
+
if (open) return;
|
|
885
|
+
setSearching(resumeSearch);
|
|
886
|
+
setActiveIndex(-1);
|
|
887
|
+
show();
|
|
888
|
+
},
|
|
889
|
+
onChange: (event) => {
|
|
890
|
+
setSearching(true);
|
|
891
|
+
setActiveIndex(0);
|
|
892
|
+
show();
|
|
893
|
+
onChange(event.target.value);
|
|
894
|
+
},
|
|
895
|
+
onKeyDown: onInputKeyDown,
|
|
896
|
+
}),
|
|
897
|
+
h(
|
|
898
|
+
"span",
|
|
899
|
+
{
|
|
900
|
+
className: "dsh-auto-timezone-chevron" + (open ? " dsh-auto-timezone-chevron-open" : ""),
|
|
901
|
+
"aria-hidden": "true",
|
|
902
|
+
},
|
|
903
|
+
h(IconChevronDownOutline14),
|
|
904
|
+
),
|
|
905
|
+
),
|
|
906
|
+
open && typeof document !== "undefined" ? createPortal(panel, document.body) : null,
|
|
907
|
+
h("span", { className: "dsh-auto-sr-only", role: "status", "aria-live": "polite" }, announcement),
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
448
911
|
function AgentPresetPicker({ id, value, presets, onChange }) {
|
|
449
912
|
const [open, setOpen] = useState(false);
|
|
450
913
|
const ownerId = useId();
|
|
@@ -600,6 +1063,11 @@ window.__ModuleLoader__.load({
|
|
|
600
1063
|
return h("span", { className: "dsh-auto-status dsh-auto-status-" + props.status }, props.status);
|
|
601
1064
|
}
|
|
602
1065
|
|
|
1066
|
+
function nextFormInstancePrefix(reactFormId) {
|
|
1067
|
+
formInstanceSerial += 1;
|
|
1068
|
+
return "dsh-auto-form-" + formInstanceSerial + "-" + reactFormId;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
603
1071
|
function JobForm(props) {
|
|
604
1072
|
const { meta, draft, editing, saving, error, onChange, onIdTouched, onSubmit, onCancel } = props;
|
|
605
1073
|
const providers = meta && Array.isArray(meta.providers) ? meta.providers : [];
|
|
@@ -611,8 +1079,12 @@ window.__ModuleLoader__.load({
|
|
|
611
1079
|
: ["workspace-write"];
|
|
612
1080
|
const agentPresets = meta && Array.isArray(meta.agentPresets) ? meta.agentPresets : [];
|
|
613
1081
|
const creating = editing === null;
|
|
614
|
-
const
|
|
615
|
-
const
|
|
1082
|
+
const reactFormId = String(useId()).replace(/[^A-Za-z0-9_-]/g, "");
|
|
1083
|
+
const formPrefixRef = useRef(null);
|
|
1084
|
+
if (formPrefixRef.current === null) {
|
|
1085
|
+
formPrefixRef.current = nextFormInstancePrefix(reactFormId);
|
|
1086
|
+
}
|
|
1087
|
+
const fieldId = (name) => formPrefixRef.current + "-" + name;
|
|
616
1088
|
|
|
617
1089
|
return h(
|
|
618
1090
|
"form",
|
|
@@ -682,21 +1154,17 @@ window.__ModuleLoader__.load({
|
|
|
682
1154
|
),
|
|
683
1155
|
h(
|
|
684
1156
|
Field,
|
|
685
|
-
{
|
|
686
|
-
|
|
1157
|
+
{
|
|
1158
|
+
label: "Time zone",
|
|
1159
|
+
htmlFor: fieldId("timezone"),
|
|
1160
|
+
required: true,
|
|
1161
|
+
hint: "Enter an IANA time zone. Current UTC offsets may change with daylight saving time.",
|
|
1162
|
+
},
|
|
1163
|
+
h(TimeZonePicker, {
|
|
687
1164
|
id: fieldId("timezone"),
|
|
688
|
-
className: "dsh-auto-input",
|
|
689
|
-
type: "text",
|
|
690
|
-
list: fieldId("timezone-list"),
|
|
691
1165
|
value: draft.timezone,
|
|
692
|
-
|
|
693
|
-
onChange: (event) => onChange("timezone", event.target.value),
|
|
1166
|
+
onChange: (value) => onChange("timezone", value),
|
|
694
1167
|
}),
|
|
695
|
-
h(
|
|
696
|
-
"datalist",
|
|
697
|
-
{ id: fieldId("timezone-list") },
|
|
698
|
-
TIMEZONES.map((zone) => h("option", { key: zone, value: zone })),
|
|
699
|
-
),
|
|
700
1168
|
),
|
|
701
1169
|
h(
|
|
702
1170
|
Field,
|
|
@@ -1647,7 +2115,7 @@ window.__ModuleLoader__.load({
|
|
|
1647
2115
|
const onKeyDown = (event) => {
|
|
1648
2116
|
if (event.key !== "Escape" || event.defaultPrevented || event.__dshAutomationsNested) return;
|
|
1649
2117
|
if (event.target?.closest?.('[role="dialog"],[role="menu"]')) return;
|
|
1650
|
-
if (document.querySelector('.dsh-auto-picker-trigger[aria-expanded="true"]')) return;
|
|
2118
|
+
if (document.querySelector('.dsh-auto-picker-trigger[aria-expanded="true"],.dsh-auto-timezone-input[aria-expanded="true"]')) return;
|
|
1651
2119
|
event.preventDefault();
|
|
1652
2120
|
disclosure.close();
|
|
1653
2121
|
};
|
|
@@ -1735,7 +2203,29 @@ window.__ModuleLoader__.load({
|
|
|
1735
2203
|
".dsh-auto-picker-chevron-open{transform:rotate(180deg);}",
|
|
1736
2204
|
".dsh-auto-picker-item-copy{min-width:0;display:flex;flex-direction:column;white-space:normal;}",
|
|
1737
2205
|
".dsh-auto-picker-item-label{color:inherit;font-size:14px;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
1738
|
-
".dsh-auto-picker-item-detail{color:var(--dsh-auto-muted);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
|
|
2206
|
+
".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;}",
|
|
2207
|
+
".dsh-auto-timezone-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;}",
|
|
2208
|
+
".dsh-auto-timezone-control:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsh-auto-input-bg));}",
|
|
2209
|
+
".dsh-auto-timezone-control:focus-within,.dsh-auto-timezone-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);}",
|
|
2210
|
+
".dsh-auto-timezone-control:has(.dsh-auto-timezone-input[aria-invalid=\"true\"]){border-color:var(--dsh-auto-danger);}",
|
|
2211
|
+
".dsh-auto-timezone-control-icon,.dsh-auto-timezone-chevron{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsh-auto-muted);pointer-events:none;}",
|
|
2212
|
+
".dsh-auto-timezone-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;}",
|
|
2213
|
+
".dsh-auto-timezone-input::placeholder{color:var(--dsh-auto-caption);}",
|
|
2214
|
+
".dsh-auto-timezone-chevron{transition:transform .12s ease;}",
|
|
2215
|
+
".dsh-auto-timezone-chevron-open{transform:rotate(180deg);}",
|
|
2216
|
+
".dsh-auto-timezone-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;}",
|
|
2217
|
+
".dsh-auto-timezone-popover *,.dsh-auto-timezone-popover *::before,.dsh-auto-timezone-popover *::after{box-sizing:border-box;}",
|
|
2218
|
+
".dsh-auto-timezone-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;}",
|
|
2219
|
+
".dsh-auto-timezone-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);}",
|
|
2220
|
+
".dsh-auto-timezone-option{width:100%;min-height:48px;padding:6px 9px;display:flex;align-items:center;gap:8px;border-radius:9px;cursor:pointer;user-select:none;}",
|
|
2221
|
+
".dsh-auto-timezone-option:hover,.dsh-auto-timezone-option[data-active=\"true\"]{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));}",
|
|
2222
|
+
".dsh-auto-timezone-option[aria-selected=\"true\"]{color:var(--dsw-alias-state-business-primary,#4176e6);}",
|
|
2223
|
+
".dsh-auto-timezone-option-icon,.dsh-auto-timezone-option-check{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858c);}",
|
|
2224
|
+
".dsh-auto-timezone-option>.dsh-auto-picker-item-copy{min-width:0;flex:1;}",
|
|
2225
|
+
".dsh-auto-timezone-option .dsh-auto-picker-item-detail{color:var(--dsw-alias-label-tertiary,#81858c);}",
|
|
2226
|
+
".dsh-auto-timezone-option-check{color:var(--dsw-alias-state-business-primary,#4176e6);}",
|
|
2227
|
+
".dsh-auto-timezone-empty{margin:0;padding:16px 12px;color:var(--dsw-alias-label-secondary,#4f5661);font-size:12px;line-height:18px;text-align:center;}",
|
|
2228
|
+
".dsh-auto-timezone-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;}",
|
|
1739
2229
|
".dsh-auto-permission-glyph{flex:none;}",
|
|
1740
2230
|
".dsh-auto-permission-glyph-read{color:var(--dsh-auto-muted);}",
|
|
1741
2231
|
".dsh-auto-permission-glyph-write{color:var(--dsh-auto-accent);}",
|
|
@@ -1878,6 +2368,16 @@ window.__ModuleLoader__.load({
|
|
|
1878
2368
|
exports.agentPresetDisplayLabel = agentPresetDisplayLabel;
|
|
1879
2369
|
exports.permissionPresetPresentation = permissionPresetPresentation;
|
|
1880
2370
|
exports.PermissionPresetPicker = PermissionPresetPicker;
|
|
2371
|
+
exports.TimeZonePicker = TimeZonePicker;
|
|
2372
|
+
exports.__testing = Object.freeze({
|
|
2373
|
+
nextFormInstancePrefix,
|
|
2374
|
+
canonicalTimezone,
|
|
2375
|
+
timezoneOffsetLabel,
|
|
2376
|
+
timezoneNavigationIndex,
|
|
2377
|
+
timezoneOptionPresentation,
|
|
2378
|
+
timezonePreferredValue,
|
|
2379
|
+
timezoneSearchResults,
|
|
2380
|
+
});
|
|
1881
2381
|
exports.inject = inject;
|
|
1882
2382
|
exports.apply = apply;
|
|
1883
2383
|
return module.exports;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncended/dsh-automations",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Durable cron automations for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -91,7 +91,8 @@
|
|
|
91
91
|
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
92
92
|
"@deepseek-ai/dsh-permission-presets": "^0.1.1-rc.2",
|
|
93
93
|
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
94
|
-
"react": "^18.2.0"
|
|
94
|
+
"react": "^18.2.0",
|
|
95
|
+
"react-dom": "^18.2.0"
|
|
95
96
|
},
|
|
96
97
|
"devDependencies": {
|
|
97
98
|
"@deepseek-ai/cordis": "4.0.1",
|
|
@@ -110,6 +111,7 @@
|
|
|
110
111
|
"@deepseek-ai/dsh-session": "0.1.1-rc.2",
|
|
111
112
|
"@types/node": "^22.0.0",
|
|
112
113
|
"react": "^18.2.0",
|
|
114
|
+
"react-dom": "^18.2.0",
|
|
113
115
|
"typescript": "^5.9.3"
|
|
114
116
|
}
|
|
115
117
|
}
|