@michengai/dsh-automation 0.1.1 → 0.1.3

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/lib/client.js CHANGED
@@ -27,7 +27,7 @@ __export(index_exports, {
27
27
  name: () => name
28
28
  });
29
29
  module.exports = __toCommonJS(index_exports);
30
- var import_react6 = require("react");
30
+ var import_react7 = require("react");
31
31
 
32
32
  // src/client/AutomationView.tsx
33
33
  var import_react3 = require("react");
@@ -323,14 +323,32 @@ function SparkleIcon(props) {
323
323
  function FillIcon({ children, width = 16, height = 16 }) {
324
324
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", width, height, fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children });
325
325
  }
326
+ function CloseOutlineIcon(props) {
327
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FillIcon, { ...props, children: [
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M14.1168 13.197L13.197 14.1167L1.8833 2.80303L2.80309 1.88324L14.1168 13.197Z" }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M13.197 1.88326L14.1168 2.80305L2.80309 14.1168L1.8833 13.197L13.197 1.88326Z" })
330
+ ] });
331
+ }
332
+ function CheckOutlineIcon(props) {
333
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FillIcon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M15.0498 3.92579L8.49512 12.3818C8.25774 12.6881 8.04517 12.9645 7.84668 13.1689C7.63957 13.3823 7.38732 13.5841 7.04492 13.6719C6.86373 13.7183 6.6757 13.7346 6.48926 13.7197C6.13666 13.6915 5.8528 13.5355 5.6123 13.3604C5.38201 13.1926 5.12573 12.9567 4.83984 12.6953L1.03125 9.21289L1.96875 8.1875L5.77734 11.6699C6.08684 11.9529 6.27773 12.1249 6.43066 12.2363C6.50183 12.2882 6.54699 12.3135 6.57324 12.3252C6.58525 12.3305 6.59269 12.3322 6.5957 12.333C6.59802 12.3336 6.59961 12.334 6.59961 12.334C6.63317 12.3367 6.66758 12.3335 6.7002 12.3252C6.7002 12.3252 6.70211 12.3251 6.7041 12.3242C6.70698 12.3229 6.71348 12.319 6.72461 12.3115C6.74849 12.2956 6.78843 12.2642 6.84961 12.2012C6.98138 12.0654 7.13957 11.8628 7.39648 11.5313L13.9502 3.07422L15.0498 3.92579Z" }) });
334
+ }
335
+ function SearchOutlineIcon(props) {
336
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FillIcon, { ...props, children: [
337
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M11.894845 6.647401C11.894845 3.725463 9.534486 1.356779 6.623219 1.35657C3.711786 1.35657 1.351635 3.725338 1.351635 6.647401C1.351843 9.569296 3.711911 11.938273 6.623219 11.938273C9.534361 11.938064 11.894637 9.569171 11.894845 6.647401ZM13.245462 6.647401C13.245254 10.317935 10.280401 13.293613 6.623219 13.293821C2.965871 13.293821 0.000204 10.31806 0 6.647401C0 2.976574 2.965746 0 6.623219 0C10.280526 0.000205 13.245462 2.9767 13.245462 6.647401Z" }),
338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M16.000417 15.041079L15.044449 16.000433L11.530434 12.473588L12.486298 11.514234L16.000417 15.041079Z" })
339
+ ] });
340
+ }
341
+ function SlidersIcon(props) {
342
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FillIcon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M2.2 3.4h6.05a1.85 1.85 0 0 0 3.5 0H13.8v1.3H11.75a1.85 1.85 0 0 0-3.5 0H2.2V3.4Zm8.6 1.15A.75.75 0 1 1 10.05 4.55.75.75 0 0 1 10.8 4.55ZM2.2 7.35h2.35a1.85 1.85 0 0 0 3.5 0H13.8v1.3H8.05a1.85 1.85 0 0 0-3.5 0H2.2V7.35Zm4.1 1.15A.75.75 0 1 1 5.55 8.5a.75.75 0 0 1 .75-.75ZM2.2 11.3h7.35a1.85 1.85 0 0 0 3.5 0H13.8v1.3h-.75a1.85 1.85 0 0 0-3.5 0H2.2v-1.3Zm9.9 1.15a.75.75 0 1 1-.75-.75.75.75 0 0 1 .75.75Z" }) });
343
+ }
326
344
  function FolderClosedIcon(props) {
327
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FillIcon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M6.556 3.377 6.007 3.725l.549-.348ZM14.5 12.342h.65V6.397h-.65-.65v5.945h.65Zm-1.674-7.618v-.65H8.023v.65h4.803Zm-5.746-.519.55-.347-.525-.828-.549.348-.549.348.525.828.55-.348ZM5.613 2.858h0H3.174v.65h2.439v-.65ZM3 4.532v8.46h.65V4.532H3Zm11.326 9.484v-.65H4.674v.65h9.652ZM3 12.342h-.65A2.324 2.324 0 0 0 4.674 14.666v-1.3A.824.824 0 0 1 3.65 12.342H3Zm.174-9.484h0A2.324 2.324 0 0 0 2.35 4.532h1.3A.824.824 0 0 1 4.674 3.508h0V2.858Zm3.382.519.549-.348A1.824 1.824 0 0 0 5.613 2.208v1.3c.16 0 .308.082.394.217l.549-.348Zm1.467 1.347h0c-.16 0-.308-.082-.393-.216l-.55.347-.549.348A1.824 1.824 0 0 0 8.023 5.374v-1.3ZM14.5 6.397h.65A2.324 2.324 0 0 0 12.826 4.073v1.3c.565 0 1.024.458 1.024 1.024h.65Zm0 5.945h-.65c0 .565-.458 1.024-1.024 1.024v1.3A2.324 2.324 0 0 0 15.15 12.342h-.65Z" }) });
345
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FillIcon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", transform: "translate(1.5 2.429)", d: "M5.05582 0.518756L4.50669 0.86654L5.05582 0.518756ZM13 9.4837L13.65 9.4837L13.65 3.53962L13 3.53962L12.35 3.53962L12.35 9.4837L13 9.4837ZM11.3264 1.86603L11.3264 1.21603L6.52313 1.21603L6.52313 1.86603L6.52313 2.51603L11.3264 2.51603L11.3264 1.86603ZM5.58054 1.34727L6.12968 0.999489L5.60495 0.170972L5.05582 0.518756L4.50669 0.86654L5.03141 1.69506L5.58054 1.34727ZM4.11323 1.23058e-13L4.11323 -0.65L1.67359 -0.65L1.67359 5.00699e-14L1.67359 0.65L4.11323 0.65L4.11323 1.23058e-13ZM0 1.67359L-0.65 1.67359L-0.65 9.4837L0 9.4837L0.65 9.4837L0.65 1.67359L0 1.67359ZM11.3264 11.1573L11.3264 10.5073L1.67359 10.5073L1.67359 11.1573L1.67359 11.8073L11.3264 11.8073L11.3264 11.1573ZM0 9.4837L-0.65 9.4837C-0.65 10.767 0.390308 11.8073 1.67359 11.8073L1.67359 11.1573L1.67359 10.5073C1.10828 10.5073 0.65 10.049 0.65 9.4837L0 9.4837ZM1.67359 5.00699e-14L1.67359 -0.65C0.390307 -0.65 -0.65 0.390309 -0.65 1.67359L0 1.67359L0.65 1.67359C0.65 1.10828 1.10828 0.65 1.67359 0.65L1.67359 5.00699e-14ZM5.05582 0.518756L5.60495 0.170972C5.28121 -0.340193 4.71829 -0.65 4.11323 -0.65L4.11323 1.23058e-13L4.11323 0.65C4.27282 0.65 4.4213 0.731715 4.50669 0.86654L5.05582 0.518756ZM6.52313 1.86603L6.52313 1.21603C6.36354 1.21603 6.21507 1.13431 6.12968 0.999489L5.58054 1.34727L5.03141 1.69506C5.35515 2.20622 5.91808 2.51603 6.52313 2.51603L6.52313 1.86603ZM13 3.53962L13.65 3.53962C13.65 2.25634 12.6097 1.21603 11.3264 1.21603L11.3264 1.86603L11.3264 2.51603C11.8917 2.51603 12.35 2.97431 12.35 3.53962L13 3.53962ZM13 9.4837L12.35 9.4837C12.35 10.049 11.8917 10.5073 11.3264 10.5073L11.3264 11.1573L11.3264 11.8073C12.6097 11.8073 13.65 10.767 13.65 9.4837L13 9.4837Z" }) });
328
346
  }
329
347
  function FolderOpenIcon(props) {
330
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FillIcon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M5.196 1.571c.615 0 1.19.308 1.532.819l.471.708c.086.128.23.205.383.205h4.588A2.666 2.666 0 0 1 14.586 5.72v.907c.683.4 1.074 1.223.852 2.06l-1.053 3.971A2.666 2.666 0 0 1 12.05 14.453H2.917A2.416 2.416 0 0 1 .502 11.952V3.987A2.416 2.416 0 0 1 2.918 1.571h2.278Zm-1.417 6.185c-.469 0-.88.316-1.001.77l-.862 3.247c-.174.657.322 1.301 1.001 1.301H12.05c.469 0 .88-.316 1.001-.77l1.053-3.97c.078-.291-.142-.577-.444-.577H3.779Zm-.861-4.804c-.572 0-1.035.464-1.035 1.035v3.307a2.67 2.67 0 0 1 1.896-.919h9.426V5.72c0-.572-.464-1.035-1.035-1.035H7.582c-.615 0-1.19-.309-1.531-.82L5.579 3.156a.666.666 0 0 0-.383-.204H2.918Z" }) });
331
- }
332
- function ChevronIcon(props) {
333
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 14 14", width: props.width || 14, height: props.height || 14, fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M4.25 2.828v8.344c0 .49.592.735.939.389l4.172-4.172a.55.55 0 0 0 0-.778L5.189 2.439c-.347-.347-.939-.101-.939.389Z" }) });
348
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FillIcon, { ...props, children: [
349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M5.19629 1.57104C5.81144 1.5711 6.38623 1.8786 6.72754 2.39038L7.19922 3.09839C7.28454 3.22635 7.42824 3.30344 7.58203 3.30347H12.1699C13.5039 3.30348 14.5859 4.38548 14.5859 5.71948V6.62671C15.2694 7.02689 15.6605 7.85012 15.4385 8.68726L14.3848 12.658C14.1037 13.7164 13.1449 14.4527 12.0498 14.4529H2.91699C1.51651 14.4529 0.451662 13.2814 0.501954 11.9519V3.98706C0.501954 2.65305 1.58396 1.57104 2.91797 1.57104H5.19629ZM3.7793 7.75562C3.30994 7.75562 2.89883 8.07153 2.77832 8.52515L1.91602 11.7722C1.74167 12.4291 2.23734 13.073 2.91699 13.073H12.0498C12.5191 13.0728 12.9304 12.757 13.0508 12.3035L14.1045 8.33374C14.1819 8.04202 13.9619 7.756 13.6602 7.75562H3.7793ZM2.91797 2.9519C2.34625 2.9519 1.88281 3.41534 1.88281 3.98706V7.2937C2.33068 6.7269 3.02249 6.37476 3.7793 6.37476H13.2051V5.71948C13.2051 5.14777 12.7416 4.68434 12.1699 4.68433H7.58203C6.96675 4.6843 6.39209 4.37595 6.05078 3.86401L5.5791 3.15601C5.49379 3.02821 5.34995 2.95196 5.19629 2.9519H2.91797Z" }),
350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", opacity: "0.2", d: "M13.6602 7.75525C13.9618 7.7556 14.1815 8.04179 14.1045 8.33337L13.0508 12.3031C12.9304 12.7567 12.5191 13.0725 12.0498 13.0726H2.91701C2.23744 13.0725 1.7417 12.4287 1.91603 11.7719L2.77834 8.52478C2.89898 8.07146 3.31018 7.75532 3.77931 7.75525H13.6602ZM5.1963 2.95154C5.34985 2.95159 5.49377 3.02803 5.57912 3.15564L6.0508 3.86365C6.39205 4.37553 6.96685 4.68385 7.58205 4.68396H12.1699C12.7416 4.68396 13.2049 5.14754 13.2051 5.71912V6.37439H3.77931C3.02267 6.37444 2.33067 6.72671 1.88283 7.29333V3.98669C1.88299 3.4152 2.34649 2.95168 2.91798 2.95154H5.1963Z" })
351
+ ] });
334
352
  }
335
353
  function EllipsisIcon(props) {
336
354
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FillIcon, { ...props, children: [
@@ -379,6 +397,13 @@ function RunningStateDot() {
379
397
  // src/client/create-modal.tsx
380
398
  var import_react2 = require("react");
381
399
 
400
+ // src/client/create-modal-logic.ts
401
+ async function adoptPickedWorkspace(input) {
402
+ const path = await input.pick();
403
+ if (path == null || path.trim() === "") return null;
404
+ return input.add(path.trim());
405
+ }
406
+
382
407
  // src/client/menu.tsx
383
408
  var import_react = require("react");
384
409
  var import_react_dom = require("react-dom");
@@ -598,12 +623,11 @@ function CreateModal({
598
623
  editing,
599
624
  onClose,
600
625
  onSubmit,
601
- onAddWorkspace
626
+ onAddWorkspace,
627
+ pickWorkspaceDirectory
602
628
  }) {
603
629
  const [form, setForm] = (0, import_react2.useState)(() => ({ ...defaultFormState(/* @__PURE__ */ new Date(), workspaces, defaultModel), ...draft }));
604
630
  const [validationError, setValidationError] = (0, import_react2.useState)();
605
- const [addingWorkspace, setAddingWorkspace] = (0, import_react2.useState)(false);
606
- const [workspacePath, setWorkspacePath] = (0, import_react2.useState)("");
607
631
  const update = (patch) => {
608
632
  setForm((current) => ({ ...current, ...patch }));
609
633
  setValidationError(void 0);
@@ -656,206 +680,177 @@ function CreateModal({
656
680
  caretRef.current = next.caret;
657
681
  });
658
682
  };
659
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
660
- "div",
661
- {
662
- className: "dsh-st-mask",
663
- role: "presentation",
664
- onClick: (event) => {
665
- if (event.target.closest(".dsh-st-select-menu") !== null) return;
666
- onClose();
667
- },
668
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuHostProvider, { host: menuHost, children: [
669
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { className: "dsh-st-modal", onClick: (event) => event.stopPropagation(), onSubmit: handleSubmit, children: [
670
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-modal-head", children: [
671
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
672
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: editing === true ? t("modal.edit") : t("modal.title") }),
673
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: t("form.subtitle") })
674
- ] }),
675
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-st-icon", onClick: onClose, "aria-label": t("form.cancel"), children: "\xD7" })
683
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-mask", role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuHostProvider, { host: menuHost, children: [
684
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { className: "dsh-st-modal", onClick: (event) => event.stopPropagation(), onSubmit: handleSubmit, children: [
685
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-modal-head", children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
687
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: editing === true ? t("modal.edit") : t("modal.title") }),
688
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: t("form.subtitle") })
689
+ ] }),
690
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-st-icon", onClick: onClose, "aria-label": t("form.cancel"), children: "\xD7" })
691
+ ] }),
692
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dsh-st-field", children: [
693
+ t("form.name"),
694
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: form.name, placeholder: t("form.namePlaceholder"), onChange: (event) => update({ name: event.target.value }) })
695
+ ] }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-field", children: [
697
+ t("form.planTime"),
698
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-inline", children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
700
+ MenuSelect,
701
+ {
702
+ value: form.scheduleKind,
703
+ options: KINDS.map((kind) => ({ value: kind, label: t(`form.${kind}`) })),
704
+ onChange: (value) => update({ scheduleKind: value })
705
+ }
706
+ ),
707
+ form.scheduleKind === "once" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "date", min: today, value: datePart, onChange: (event) => update({ onceAt: clampOnceAt(`${event.target.value}T${timePart}`) }) }),
709
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: timePart, ...minOnceTime === void 0 ? {} : { minTime: minOnceTime }, onChange: (value) => update({ onceAt: clampOnceAt(`${datePart}T${value}`) }) })
676
710
  ] }),
677
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dsh-st-field", children: [
678
- t("form.name"),
679
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: form.name, placeholder: t("form.namePlaceholder"), onChange: (event) => update({ name: event.target.value }) })
711
+ form.scheduleKind === "interval" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
712
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "is-narrow", type: "number", min: 5, value: form.everyMinutes, onChange: (event) => update({ everyMinutes: event.target.value }) }),
713
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-suffix", children: t("form.minutesShort") })
680
714
  ] }),
681
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-field", children: [
682
- t("form.planTime"),
683
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-inline", children: [
684
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
685
- MenuSelect,
686
- {
687
- value: form.scheduleKind,
688
- options: KINDS.map((kind) => ({ value: kind, label: t(`form.${kind}`) })),
689
- onChange: (value) => update({ scheduleKind: value })
690
- }
691
- ),
692
- form.scheduleKind === "once" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
693
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "date", min: today, value: datePart, onChange: (event) => update({ onceAt: clampOnceAt(`${event.target.value}T${timePart}`) }) }),
694
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: timePart, ...minOnceTime === void 0 ? {} : { minTime: minOnceTime }, onChange: (value) => update({ onceAt: clampOnceAt(`${datePart}T${value}`) }) })
695
- ] }),
696
- form.scheduleKind === "interval" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
697
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "is-narrow", type: "number", min: 5, value: form.everyMinutes, onChange: (event) => update({ everyMinutes: event.target.value }) }),
698
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-suffix", children: t("form.minutesShort") })
699
- ] }),
700
- form.scheduleKind === "hourly" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
701
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MenuSelect, { value: form.hourlyMinute, options: MINUTES.map((item) => ({ value: item, label: item })), onChange: (value) => update({ hourlyMinute: value }) }),
702
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-suffix", children: t("form.minutesShort") })
703
- ] }),
704
- (form.scheduleKind === "daily" || form.scheduleKind === "weekly") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: form.time, onChange: (value) => update({ time: value }) }),
705
- form.scheduleKind === "monthly" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
706
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
707
- MenuSelect,
708
- {
709
- value: form.monthDay,
710
- options: Array.from({ length: 31 }, (_, index) => {
711
- const day = String(index + 1);
712
- return { value: day, label: t("form.monthDay", { day }) };
713
- }),
714
- onChange: (value) => update({ monthDay: value })
715
- }
716
- ),
717
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: form.time, onChange: (value) => update({ time: value }) })
718
- ] }),
719
- form.scheduleKind === "custom" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "is-narrow", type: "number", min: 1, value: form.customDays, onChange: (event) => update({ customDays: event.target.value }) }),
721
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-suffix", children: t("form.daysShort") }),
722
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: form.time, onChange: (value) => update({ time: value }) })
723
- ] })
724
- ] })
715
+ form.scheduleKind === "hourly" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
716
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MenuSelect, { value: form.hourlyMinute, options: MINUTES.map((item) => ({ value: item, label: item })), onChange: (value) => update({ hourlyMinute: value }) }),
717
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-suffix", children: t("form.minutesShort") })
725
718
  ] }),
726
- form.scheduleKind === "weekly" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-weekdays", children: WEEKDAYS.map((day) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
727
- "button",
728
- {
729
- type: "button",
730
- className: form.weekdays.includes(day) ? "is-on" : "",
731
- onClick: () => update({
732
- weekdays: form.weekdays.includes(day) ? form.weekdays.filter((value) => value !== day) : [...form.weekdays, day]
733
- }),
734
- children: t(`day.${day}`)
735
- },
736
- day
737
- )) }),
738
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-field", children: [
739
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("form.prompt") }),
740
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-prompt-card", children: [
741
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("textarea", { ref: promptRef, value: form.prompt, placeholder: t("form.promptPlaceholder"), onChange: (event) => {
742
- rememberCaret();
743
- update({ prompt: event.target.value });
744
- }, onSelect: rememberCaret, onClick: rememberCaret, onKeyUp: rememberCaret }),
745
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-composer", children: [
746
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-composer-left", children: [
747
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPanel, { ghost: true, up: true, label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
748
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FolderIcon, { width: 14, height: 14 }),
749
- workspace?.title || t("form.workspace")
750
- ] }), children: [
751
- workspaces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-select-empty", children: t("form.error.workspace") }),
752
- workspaces.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
753
- MenuRow,
754
- {
755
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FolderIcon, { width: 14, height: 14 }),
756
- label: item.title,
757
- active: item.id === form.workspaceId,
758
- onClick: () => update({ workspaceId: item.id })
759
- },
760
- item.id
761
- )),
762
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-menu-split" }),
763
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
764
- MenuRow,
765
- {
766
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlusIcon, { width: 14, height: 14 }),
767
- label: t("form.addWorkspace"),
768
- onClick: () => {
769
- setWorkspacePath("");
770
- setAddingWorkspace(true);
771
- }
772
- }
773
- )
774
- ] }),
775
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPanel, { ghost: true, up: true, label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
776
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, { width: 14, height: 14 }),
777
- t("form.skills")
778
- ] }), children: [
779
- skills.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-select-empty", children: t("form.skillsEmpty") }),
780
- skills.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
781
- MenuRow,
782
- {
783
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, { width: 14, height: 14 }),
784
- label: item.name,
785
- onClick: () => insertSkill(item)
786
- },
787
- item.id
788
- ))
789
- ] }),
719
+ (form.scheduleKind === "daily" || form.scheduleKind === "weekly") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: form.time, onChange: (value) => update({ time: value }) }),
720
+ form.scheduleKind === "monthly" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
721
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
722
+ MenuSelect,
723
+ {
724
+ value: form.monthDay,
725
+ options: Array.from({ length: 31 }, (_, index) => {
726
+ const day = String(index + 1);
727
+ return { value: day, label: t("form.monthDay", { day }) };
728
+ }),
729
+ onChange: (value) => update({ monthDay: value })
730
+ }
731
+ ),
732
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: form.time, onChange: (value) => update({ time: value }) })
733
+ ] }),
734
+ form.scheduleKind === "custom" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "is-narrow", type: "number", min: 1, value: form.customDays, onChange: (event) => update({ customDays: event.target.value }) }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-suffix", children: t("form.daysShort") }),
737
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TimeSelect, { value: form.time, onChange: (value) => update({ time: value }) })
738
+ ] })
739
+ ] })
740
+ ] }),
741
+ form.scheduleKind === "weekly" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-weekdays", children: WEEKDAYS.map((day) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
742
+ "button",
743
+ {
744
+ type: "button",
745
+ className: form.weekdays.includes(day) ? "is-on" : "",
746
+ onClick: () => update({
747
+ weekdays: form.weekdays.includes(day) ? form.weekdays.filter((value) => value !== day) : [...form.weekdays, day]
748
+ }),
749
+ children: t(`day.${day}`)
750
+ },
751
+ day
752
+ )) }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-field", children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("form.prompt") }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-prompt-card", children: [
756
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("textarea", { ref: promptRef, value: form.prompt, placeholder: t("form.promptPlaceholder"), onChange: (event) => {
757
+ rememberCaret();
758
+ update({ prompt: event.target.value });
759
+ }, onSelect: rememberCaret, onClick: rememberCaret, onKeyUp: rememberCaret }),
760
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-composer", children: [
761
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-composer-left", children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPanel, { ghost: true, up: true, label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FolderIcon, { width: 14, height: 14 }),
764
+ workspace?.title || t("form.workspace")
765
+ ] }), children: [
766
+ workspaces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-select-empty", children: t("form.error.workspace") }),
767
+ workspaces.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
768
+ MenuRow,
769
+ {
770
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FolderIcon, { width: 14, height: 14 }),
771
+ label: item.title,
772
+ active: item.id === form.workspaceId,
773
+ onClick: () => update({ workspaceId: item.id })
774
+ },
775
+ item.id
776
+ )),
777
+ pickWorkspaceDirectory !== void 0 && onAddWorkspace !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-menu-split" }),
790
779
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
791
- MenuSelect,
780
+ MenuRow,
792
781
  {
793
- pill: true,
794
- up: true,
795
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }),
796
- value: form.permission,
797
- options: [
798
- { value: "read-only", label: t("form.readOnly"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) },
799
- { value: "workspace-write", label: t("form.workspaceWrite"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) },
800
- { value: "full-access", label: t("form.fullAccess"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) }
801
- ],
802
- onChange: (value) => update({ permission: value })
782
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlusIcon, { width: 14, height: 14 }),
783
+ label: t("form.addWorkspace"),
784
+ onClick: () => {
785
+ void (async () => {
786
+ try {
787
+ const id = await adoptPickedWorkspace({
788
+ pick: pickWorkspaceDirectory,
789
+ add: onAddWorkspace
790
+ });
791
+ if (id !== null) update({ workspaceId: id });
792
+ } catch (caught) {
793
+ setValidationError(caught instanceof Error ? caught.message : t("error.action"));
794
+ }
795
+ })();
796
+ }
803
797
  }
804
798
  )
805
- ] }),
806
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-composer-right", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
- ModelPicker,
799
+ ] })
800
+ ] }),
801
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPanel, { ghost: true, up: true, label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
802
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, { width: 14, height: 14 }),
803
+ t("form.skills")
804
+ ] }), children: [
805
+ skills.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-select-empty", children: t("form.skillsEmpty") }),
806
+ skills.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
+ MenuRow,
808
808
  {
809
- t,
810
- models,
811
- modelKey: form.modelKey,
812
- effort: form.reasoningEffort,
813
- onModelKey: (value) => update({ modelKey: value }),
814
- onEffort: (value) => update({ reasoningEffort: value })
815
- }
816
- ) })
817
- ] })
818
- ] })
819
- ] }),
820
- addingWorkspace && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-subdialog", children: [
821
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: t("form.addWorkspace") }),
822
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dsh-st-field", children: [
823
- t("form.workspacePath"),
824
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: workspacePath, placeholder: t("form.workspacePathPlaceholder"), onChange: (event) => setWorkspacePath(event.target.value) })
825
- ] }),
826
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-modal-actions", children: [
827
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-st-btn", onClick: () => setAddingWorkspace(false), children: t("form.cancel") }),
809
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, { width: 14, height: 14 }),
810
+ label: item.name,
811
+ onClick: () => insertSkill(item)
812
+ },
813
+ item.id
814
+ ))
815
+ ] }),
828
816
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
829
- "button",
817
+ MenuSelect,
830
818
  {
831
- type: "button",
832
- className: "dsh-st-btn dsh-st-btn--primary",
833
- disabled: busy || onAddWorkspace === void 0,
834
- onClick: async () => {
835
- if (onAddWorkspace === void 0) return;
836
- try {
837
- const id = await onAddWorkspace(workspacePath);
838
- update({ workspaceId: id });
839
- setAddingWorkspace(false);
840
- } catch (caught) {
841
- setValidationError(caught instanceof Error ? caught.message : t("error.action"));
842
- }
843
- },
844
- children: t("modal.save")
819
+ pill: true,
820
+ up: true,
821
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }),
822
+ value: form.permission,
823
+ options: [
824
+ { value: "read-only", label: t("form.readOnly"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) },
825
+ { value: "workspace-write", label: t("form.workspaceWrite"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) },
826
+ { value: "full-access", label: t("form.fullAccess"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) }
827
+ ],
828
+ onChange: (value) => update({ permission: value })
845
829
  }
846
830
  )
847
- ] })
848
- ] }),
849
- validationError !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-st-error", children: validationError }),
850
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-modal-actions", children: [
851
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-st-btn", onClick: onClose, disabled: busy, children: t("form.cancel") }),
852
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", className: "dsh-st-btn dsh-st-btn--primary", disabled: busy, children: t("modal.save") })
831
+ ] }),
832
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-composer-right", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
833
+ ModelPicker,
834
+ {
835
+ t,
836
+ models,
837
+ modelKey: form.modelKey,
838
+ effort: form.reasoningEffort,
839
+ onModelKey: (value) => update({ modelKey: value }),
840
+ onEffort: (value) => update({ reasoningEffort: value })
841
+ }
842
+ ) })
853
843
  ] })
854
- ] }),
855
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-flyout-root", ref: setMenuHost })
844
+ ] })
845
+ ] }),
846
+ validationError !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-st-error", children: validationError }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-st-modal-actions", children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-st-btn", onClick: onClose, disabled: busy, children: t("form.cancel") }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", className: "dsh-st-btn dsh-st-btn--primary", disabled: busy, children: t("modal.save") })
856
850
  ] })
857
- }
858
- );
851
+ ] }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-flyout-root", ref: setMenuHost })
853
+ ] }) });
859
854
  }
860
855
  function ModelPicker({
861
856
  t,
@@ -1160,7 +1155,7 @@ var EXAMPLES = [
1160
1155
  { name: "\u6BCF\u5468\u4F9D\u8D56\u5DE1\u68C0", scheduleKind: "weekly", time: "10:00", weekdays: [1] },
1161
1156
  { name: "\u5DE5\u4F5C\u65E5\u65E9\u62A5", scheduleKind: "weekly", time: "08:00", weekdays: [1, 2, 3, 4, 5] }
1162
1157
  ];
1163
- function AutomationView({ t, runtime, closeSettings }) {
1158
+ function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1164
1159
  const state = (0, import_react3.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
1165
1160
  const [tab, setTab] = (0, import_react3.useState)("mine");
1166
1161
  const [query, setQuery] = (0, import_react3.useState)("");
@@ -1334,6 +1329,7 @@ function AutomationView({ t, runtime, closeSettings }) {
1334
1329
  defaultModel: snapshot?.defaultModel ?? null,
1335
1330
  skills: snapshot?.skills ?? [],
1336
1331
  onAddWorkspace: async (path) => runtime.addWorkspace(path).then((value) => value.id),
1332
+ ...pickWorkspaceDirectory === void 0 ? {} : { pickWorkspaceDirectory },
1337
1333
  editing: editingId !== void 0,
1338
1334
  ...draft === void 0 ? {} : { draft },
1339
1335
  onClose: closeModal,
@@ -1436,6 +1432,17 @@ var en = {
1436
1432
  "sidebar.tasksEmpty": "No web tasks yet.",
1437
1433
  "sidebar.ungrouped": "Ungrouped",
1438
1434
  "sidebar.section": "Scheduled",
1435
+ "sidebar.workspaces": "Workspaces",
1436
+ "sidebar.search": "Search",
1437
+ "sidebar.searchSessions": "Search sessions...",
1438
+ "sidebar.clearSearch": "Clear search",
1439
+ "sidebar.filter": "Filter",
1440
+ "sidebar.groupBy": "Group by",
1441
+ "sidebar.groupWorkspace": "By workspace",
1442
+ "sidebar.groupList": "Single list",
1443
+ "sidebar.sortBy": "Sort by",
1444
+ "sidebar.sortManual": "Manual",
1445
+ "sidebar.sortTime": "Recently updated",
1439
1446
  "header.lead": "Plan automatic tasks, or trigger them by hand. Describe the recurring work in any chat to create it quickly.",
1440
1447
  "examples.title": "Recommended examples",
1441
1448
  "examples.1.title": "Daily regression check",
@@ -1454,8 +1461,13 @@ var en = {
1454
1461
  "session.rename": "Rename",
1455
1462
  "session.fork": "Fork session",
1456
1463
  "session.archive": "Archive session",
1457
- "session.delete": "Delete session",
1458
- "session.deleteFolder": "Delete folder",
1464
+ "session.idle": "Idle",
1465
+ "session.runningStatus": "Running",
1466
+ "session.delete": "Remove from Scheduled",
1467
+ "session.deleteFolder": "Remove folder",
1468
+ "session.confirmDeleteFolder": "Remove this folder from Scheduled?",
1469
+ "session.confirmDeleteFolderHint": 'This only unlists {count} session(s) in "{name}" from Scheduled. Host sessions are not deleted and they stay hidden from Tasks.',
1470
+ "session.confirmDeleteFolderAction": "Remove from Scheduled",
1459
1471
  "history.range.day": "By day",
1460
1472
  "history.range.week": "By week",
1461
1473
  "history.range.month": "By month",
@@ -1620,6 +1632,17 @@ var zh = {
1620
1632
  "sidebar.tasksEmpty": "\u6682\u65E0\u7F51\u9875\u4EFB\u52A1",
1621
1633
  "sidebar.ungrouped": "\u672A\u5206\u7EC4",
1622
1634
  "sidebar.section": "\u5B9A\u65F6\u4EFB\u52A1",
1635
+ "sidebar.workspaces": "\u5DE5\u4F5C\u533A",
1636
+ "sidebar.search": "\u641C\u7D22",
1637
+ "sidebar.searchSessions": "\u641C\u7D22\u4F1A\u8BDD...",
1638
+ "sidebar.clearSearch": "\u6E05\u9664\u641C\u7D22",
1639
+ "sidebar.filter": "\u7B5B\u9009",
1640
+ "sidebar.groupBy": "\u5206\u7EC4\u65B9\u5F0F",
1641
+ "sidebar.groupWorkspace": "\u6309\u5DE5\u4F5C\u533A",
1642
+ "sidebar.groupList": "\u5355\u5217\u8868",
1643
+ "sidebar.sortBy": "\u6392\u5E8F\u65B9\u5F0F",
1644
+ "sidebar.sortManual": "\u624B\u52A8\u6392\u5E8F",
1645
+ "sidebar.sortTime": "\u6700\u8FD1\u66F4\u65B0",
1623
1646
  "header.lead": "\u6309\u8BA1\u5212\u81EA\u52A8\u6267\u884C\u4EFB\u52A1\uFF0C\u4E5F\u53EF\u968F\u65F6\u624B\u52A8\u89E6\u53D1\u3002\u5728\u4EFB\u610F\u5BF9\u8BDD\u4E2D\u63CF\u8FF0\u4F60\u60F3\u5B9A\u671F\u505A\u7684\u4E8B\uFF0C\u5373\u53EF\u5FEB\u901F\u521B\u5EFA",
1624
1647
  "examples.title": "\u63A8\u8350\u6848\u4F8B",
1625
1648
  "examples.1.title": "\u6BCF\u65E5\u56DE\u5F52\u68C0\u67E5",
@@ -1638,8 +1661,13 @@ var zh = {
1638
1661
  "session.rename": "\u91CD\u547D\u540D",
1639
1662
  "session.fork": "\u5206\u53C9\u4F1A\u8BDD",
1640
1663
  "session.archive": "\u5F52\u6863\u4F1A\u8BDD",
1641
- "session.delete": "\u5220\u9664\u4F1A\u8BDD",
1642
- "session.deleteFolder": "\u5220\u9664\u6587\u4EF6\u5939",
1664
+ "session.idle": "\u7A7A\u95F2",
1665
+ "session.runningStatus": "\u8FD0\u884C\u4E2D",
1666
+ "session.delete": "\u4ECE\u5B9A\u65F6\u9875\u79FB\u9664",
1667
+ "session.deleteFolder": "\u4ECE\u5B9A\u65F6\u9875\u79FB\u9664\u6587\u4EF6\u5939",
1668
+ "session.confirmDeleteFolder": "\u4ECE\u5B9A\u65F6\u9875\u79FB\u9664\u8FD9\u4E2A\u6587\u4EF6\u5939\uFF1F",
1669
+ "session.confirmDeleteFolderHint": "\u53EA\u4F1A\u628A\u300C{name}\u300D\u4E0B\u7684 {count} \u6761\u4F1A\u8BDD\u4ECE\u5B9A\u65F6\u9875\u6458\u6389\uFF0C\u4E0D\u4F1A\u5220\u9664\u5BBF\u4E3B Session\u3002\u4EFB\u52A1\u9875\u672C\u6765\u4E5F\u4E0D\u5C55\u793A\u8FD9\u4E9B\u81EA\u52A8\u5316\u4F1A\u8BDD\u3002",
1670
+ "session.confirmDeleteFolderAction": "\u4ECE\u5B9A\u65F6\u9875\u79FB\u9664",
1643
1671
  "history.range.day": "\u6309\u5929",
1644
1672
  "history.range.week": "\u6309\u5468",
1645
1673
  "history.range.month": "\u6309\u6708",
@@ -1872,7 +1900,80 @@ function isForeignSidebarHost(component) {
1872
1900
  }
1873
1901
 
1874
1902
  // src/client/native-session-list.tsx
1875
- var import_react4 = require("react");
1903
+ var import_react5 = require("react");
1904
+ var import_react_dom2 = require("react-dom");
1905
+
1906
+ // src/client/native-session-menu.ts
1907
+ function resolveEventElement(target) {
1908
+ if (target == null || typeof target !== "object") return null;
1909
+ const node = target;
1910
+ if (node.nodeType === 3 || node.nodeType === 8) {
1911
+ return node.parentElement ?? null;
1912
+ }
1913
+ return node;
1914
+ }
1915
+ function shouldCloseNativeSessionMenu(target, keepInside) {
1916
+ const el = resolveEventElement(target);
1917
+ if (el == null) return true;
1918
+ return !keepInside.some((root) => {
1919
+ if (root == null || typeof root !== "object") return false;
1920
+ if (root === el) return true;
1921
+ const box = root;
1922
+ return typeof box.contains === "function" && box.contains(el);
1923
+ });
1924
+ }
1925
+ function pointerPoint(event) {
1926
+ const x = Number(event?.clientX);
1927
+ const y = Number(event?.clientY);
1928
+ return {
1929
+ x: Number.isFinite(x) ? x : 8,
1930
+ y: Number.isFinite(y) ? y : 8
1931
+ };
1932
+ }
1933
+ function clampMenuPoint(x, y, width, height, viewport) {
1934
+ const pad = 8;
1935
+ const vw = viewport.width || width + pad * 2;
1936
+ const vh = viewport.height || height + pad * 2;
1937
+ return {
1938
+ x: Math.max(pad, Math.min(x, Math.max(pad, vw - width - pad))),
1939
+ y: Math.max(pad, Math.min(y, Math.max(pad, vh - height - pad)))
1940
+ };
1941
+ }
1942
+ function nextOpenSessionMenu(current, clicked, point) {
1943
+ if (current?.id === clicked) return null;
1944
+ return { id: clicked, x: point.x, y: point.y };
1945
+ }
1946
+ function nativeSessionMenuStyle(point, size = { width: 218, height: 176 }, viewport = { width: 1e3, height: 800 }) {
1947
+ const pos = clampMenuPoint(point.x, point.y, size.width, size.height, viewport);
1948
+ return {
1949
+ position: "fixed",
1950
+ zIndex: 4e3,
1951
+ left: `${Math.round(pos.x)}px`,
1952
+ top: `${Math.round(pos.y)}px`
1953
+ };
1954
+ }
1955
+ function nativeSessionHoverStyle(row, card, viewport) {
1956
+ const pad = 8;
1957
+ const left = row.right + pad;
1958
+ const top = row.top + card.height > viewport.height - pad ? Math.max(pad, viewport.height - card.height - pad) : Math.max(pad, row.top);
1959
+ return {
1960
+ position: "fixed",
1961
+ zIndex: 4100,
1962
+ left: `${Math.round(left)}px`,
1963
+ top: `${Math.round(top)}px`
1964
+ };
1965
+ }
1966
+ function relativeTime(value) {
1967
+ const ts = Date.parse(value || "");
1968
+ if (!Number.isFinite(ts)) return "";
1969
+ const delta = Math.max(0, Date.now() - ts);
1970
+ const min = Math.floor(delta / 6e4);
1971
+ if (min < 1) return "\u521A\u521A";
1972
+ if (min < 60) return String(min) + "\u5206\u949F\u524D";
1973
+ const hour = Math.floor(min / 60);
1974
+ if (hour < 24) return String(hour) + "\u5C0F\u65F6\u524D";
1975
+ return String(Math.floor(hour / 24)) + "\u5929\u524D";
1976
+ }
1876
1977
 
1877
1978
  // src/run-title.ts
1878
1979
  function formatRunStamp(iso) {
@@ -1920,21 +2021,35 @@ function groupScheduledSessions(automations, runs) {
1920
2021
  };
1921
2022
  }).filter((group) => group.sessions.length > 0);
1922
2023
  }
1923
- function isNativeTaskSession(item) {
2024
+ function collectScheduledSessionIds(runs) {
2025
+ const ids = /* @__PURE__ */ new Set();
2026
+ for (const run of runs ?? []) {
2027
+ const id = run.sessionId;
2028
+ if (typeof id === "string" && id !== "") ids.add(id);
2029
+ }
2030
+ return ids;
2031
+ }
2032
+ var AUTOMATION_TITLE_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/;
2033
+ function isAutomationSidebarSession(id, item, scheduledIds = /* @__PURE__ */ new Set()) {
2034
+ if (id.startsWith(AUTOMATION_SESSION_PREFIX) || scheduledIds.has(id)) return true;
2035
+ const title = String(item?.title ?? item?.displayTitle ?? "");
2036
+ return AUTOMATION_TITLE_RE.test(title);
2037
+ }
2038
+ function isNativeTaskSession(item, scheduledIds = /* @__PURE__ */ new Set()) {
1924
2039
  if (item === void 0 || item.blank === true) return false;
1925
2040
  if (item.origin === "im" || item.origin === "subagent") return false;
1926
2041
  const id = item.id ?? "";
1927
2042
  if (id.startsWith("im:")) return false;
1928
- if (id.startsWith(AUTOMATION_SESSION_PREFIX)) return false;
2043
+ if (isAutomationSidebarSession(id, item, scheduledIds)) return false;
1929
2044
  return true;
1930
2045
  }
1931
- function groupNativeTaskSessions(sessions, workspaces, ungroupedLabel) {
2046
+ function groupNativeTaskSessions(sessions, workspaces, ungroupedLabel, scheduledIds = /* @__PURE__ */ new Set()) {
1932
2047
  const byId = sessions.byId ?? {};
1933
2048
  const archived = new Set(workspaces?.archivedSessionIds ?? []);
1934
2049
  const assigned = /* @__PURE__ */ new Set();
1935
2050
  const groups = [];
1936
2051
  for (const workspace of workspaces?.items ?? []) {
1937
- const items = (workspace.sessionIds ?? []).map((id) => byId[id]).filter((item) => item !== void 0 && item.id !== void 0 && isNativeTaskSession(item) && !archived.has(item.id));
2052
+ const items = (workspace.sessionIds ?? []).map((id) => byId[id]).filter((item) => item !== void 0 && item.id !== void 0 && isNativeTaskSession(item, scheduledIds) && !archived.has(item.id));
1938
2053
  for (const item of items) {
1939
2054
  if (item.id !== void 0) assigned.add(item.id);
1940
2055
  }
@@ -1946,7 +2061,7 @@ function groupNativeTaskSessions(sessions, workspaces, ungroupedLabel) {
1946
2061
  });
1947
2062
  }
1948
2063
  }
1949
- const ungrouped = (sessions.ids ?? []).map((id) => byId[id]).filter((item) => item !== void 0 && item.id !== void 0 && !assigned.has(item.id) && isNativeTaskSession(item) && !archived.has(item.id));
2064
+ const ungrouped = (sessions.ids ?? []).map((id) => byId[id]).filter((item) => item !== void 0 && item.id !== void 0 && !assigned.has(item.id) && isNativeTaskSession(item, scheduledIds) && !archived.has(item.id));
1950
2065
  if (ungrouped.length > 0) groups.push({ id: "", label: ungroupedLabel, sessions: ungrouped });
1951
2066
  return groups;
1952
2067
  }
@@ -1954,9 +2069,29 @@ function readNativeSidebarTab(raw) {
1954
2069
  if (raw === "channels" || raw === "schedule" || raw === "tasks") return raw;
1955
2070
  return "tasks";
1956
2071
  }
1957
- function tabForSessionId(sessionId) {
2072
+ function shouldFollowSessionTab(previousCurrent, current) {
2073
+ const prev = previousCurrent ?? "";
2074
+ const next = current ?? "";
2075
+ return next !== "" && prev !== next;
2076
+ }
2077
+ function ownedSidebarTabIds(input) {
2078
+ const ids = ["tasks"];
2079
+ for (const id of input.extraTabIds) {
2080
+ if (id === "" || id === "tasks" || id === "schedule" || ids.includes(id)) continue;
2081
+ ids.push(id);
2082
+ }
2083
+ if (input.channelsReady && !ids.includes("channels")) ids.push("channels");
2084
+ ids.push("schedule");
2085
+ return ids;
2086
+ }
2087
+ function resolveVisibleSidebarTab(input) {
2088
+ if (input.extraTabIds.includes(input.tab)) return input.tab;
2089
+ if (input.tab === "channels" && !input.channelsReady) return "tasks";
2090
+ return input.tab;
2091
+ }
2092
+ function tabForSessionId(sessionId, scheduledIds) {
1958
2093
  if (sessionId === void 0 || sessionId === null || sessionId === "") return void 0;
1959
- if (sessionId.startsWith(AUTOMATION_SESSION_PREFIX)) return "schedule";
2094
+ if (scheduledIds !== void 0 ? scheduledIds.has(sessionId) : sessionId.startsWith(AUTOMATION_SESSION_PREFIX)) return "schedule";
1960
2095
  if (sessionId.startsWith("im:")) return "channels";
1961
2096
  return void 0;
1962
2097
  }
@@ -1972,41 +2107,77 @@ function slotOccupantName(item) {
1972
2107
  function hasCodexUiSidebar(entries) {
1973
2108
  return (entries ?? []).some((item) => occupantLooksLikeCodexUi(slotOccupantName(item)));
1974
2109
  }
1975
- function filterTaskSessionState(state) {
2110
+ var taskFilterCache = /* @__PURE__ */ new WeakMap();
2111
+ var workspaceFilterCache = /* @__PURE__ */ new WeakMap();
2112
+ function scheduledCacheKey(scheduledIds) {
2113
+ if (scheduledIds.size === 0) return "";
2114
+ return [...scheduledIds].sort().join("\0");
2115
+ }
2116
+ function filterTaskSessionState(state, scheduledIds = /* @__PURE__ */ new Set()) {
1976
2117
  const src = state ?? { ids: [], byId: {}, current: null };
2118
+ const key = scheduledCacheKey(scheduledIds);
2119
+ if (typeof src === "object" && src !== null) {
2120
+ const hit = taskFilterCache.get(src);
2121
+ if (hit !== void 0 && hit.key === key) return hit.result;
2122
+ }
1977
2123
  const ids = (src.ids ?? []).filter((id) => {
1978
2124
  const value = String(id);
1979
- return !value.startsWith(AUTOMATION_SESSION_PREFIX) && !value.startsWith("im:");
2125
+ return !value.startsWith("im:") && !isAutomationSidebarSession(value, src.byId?.[value], scheduledIds);
1980
2126
  });
1981
- const byId = {};
1982
- for (const id of ids) {
1983
- const item = src.byId?.[id];
1984
- if (item !== void 0) byId[id] = item;
1985
- }
1986
- return { ...src, ids, byId };
2127
+ const unchanged = ids.length === (src.ids ?? []).length;
2128
+ const result = unchanged ? src : { ...src, ids, byId: Object.fromEntries(ids.map((id) => [id, src.byId?.[id]]).filter((entry) => entry[1] !== void 0)) };
2129
+ if (typeof src === "object" && src !== null) taskFilterCache.set(src, { key, result });
2130
+ return result;
1987
2131
  }
1988
2132
  function openScheduledSession(id, openRuntime, openHost) {
1989
- if (id === "") return;
2133
+ if (id === "") return false;
1990
2134
  const attempts = id.startsWith(AUTOMATION_SESSION_PREFIX) || id.startsWith("im:") ? [openRuntime, openHost] : [openHost, openRuntime];
1991
2135
  for (const attempt of attempts) {
1992
2136
  if (typeof attempt !== "function") continue;
1993
2137
  try {
1994
2138
  attempt(id);
1995
- return;
2139
+ return true;
1996
2140
  } catch {
1997
2141
  }
1998
2142
  }
2143
+ return false;
2144
+ }
2145
+ async function ensureOpenScheduledSession(input) {
2146
+ const id = input.id.trim();
2147
+ if (id === "") return false;
2148
+ await input.adopt?.(id).catch(() => void 0);
2149
+ const listed = () => input.listed?.(id) === true;
2150
+ if (!listed() && input.refresh !== void 0) {
2151
+ await input.refresh().catch(() => void 0);
2152
+ }
2153
+ if (openScheduledSession(id, input.openRuntime, input.openHost)) return true;
2154
+ if (input.refresh !== void 0) {
2155
+ await input.refresh().catch(() => void 0);
2156
+ }
2157
+ return openScheduledSession(id, input.openRuntime, input.openHost);
1999
2158
  }
2000
- function isHiddenSidebarSessionId(id) {
2001
- return id.startsWith(AUTOMATION_SESSION_PREFIX) || id.startsWith("im:");
2159
+ function isHiddenSidebarSessionId(id, scheduledIds = /* @__PURE__ */ new Set()) {
2160
+ return id.startsWith("im:") || isAutomationSidebarSession(id, void 0, scheduledIds);
2002
2161
  }
2003
- function filterWorkspaceListState(state) {
2162
+ function filterWorkspaceListState(state, scheduledIds = /* @__PURE__ */ new Set()) {
2004
2163
  const src = state ?? { items: [], archivedSessionIds: [] };
2005
- const items = (src.items ?? []).map((workspace) => ({
2006
- ...workspace,
2007
- sessionIds: (workspace.sessionIds ?? []).filter((sid) => !isHiddenSidebarSessionId(String(sid)))
2008
- }));
2009
- return { ...src, items };
2164
+ const key = scheduledCacheKey(scheduledIds);
2165
+ if (typeof src === "object" && src !== null) {
2166
+ const hit = workspaceFilterCache.get(src);
2167
+ if (hit !== void 0 && hit.key === key) return hit.result;
2168
+ }
2169
+ let changed = false;
2170
+ const items = (src.items ?? []).map((workspace) => {
2171
+ const sessionIds = (workspace.sessionIds ?? []).filter((sid) => !isHiddenSidebarSessionId(String(sid), scheduledIds));
2172
+ if (sessionIds.length !== (workspace.sessionIds ?? []).length) {
2173
+ changed = true;
2174
+ return { ...workspace, sessionIds };
2175
+ }
2176
+ return workspace;
2177
+ });
2178
+ const result = changed ? { ...src, items } : src;
2179
+ if (typeof src === "object" && src !== null) workspaceFilterCache.set(src, { key, result });
2180
+ return result;
2010
2181
  }
2011
2182
  function wrapperFlags(component) {
2012
2183
  if (component === void 0 || component === null || typeof component !== "function" && typeof component !== "object") return {};
@@ -2046,29 +2217,140 @@ function pickWrappableWorkspacesEntry(entries) {
2046
2217
  }
2047
2218
  return void 0;
2048
2219
  }
2220
+ function applyWorkspaceBrowserQuery(groups, query, sort, groupMode = "workspace") {
2221
+ const needle = query.trim().toLocaleLowerCase();
2222
+ const filtered = groups.map((group) => {
2223
+ if (needle === "") return group;
2224
+ if (group.name.toLocaleLowerCase().includes(needle)) return group;
2225
+ const sessions = group.sessions.filter((session) => `${session.title ?? ""} ${session.label ?? ""}`.toLocaleLowerCase().includes(needle));
2226
+ return { ...group, sessions };
2227
+ }).filter((group) => group.sessions.length > 0 || needle !== "" && group.name.toLocaleLowerCase().includes(needle));
2228
+ if (groupMode === "list") {
2229
+ const sessions = filtered.flatMap((group) => [...group.sessions]);
2230
+ if (sort === "time") sessions.sort((left, right) => sessionTime(right) - sessionTime(left));
2231
+ return sessions.length === 0 ? [] : [{ ...filtered[0], name: "", sessions }];
2232
+ }
2233
+ if (sort === "manual") return filtered;
2234
+ return [...filtered].sort((left, right) => latestSessionTime(right) - latestSessionTime(left));
2235
+ }
2236
+ function sessionTime(session) {
2237
+ const ts = Date.parse(session.updatedAt ?? "");
2238
+ return Number.isFinite(ts) ? ts : 0;
2239
+ }
2240
+ function latestSessionTime(group) {
2241
+ let latest = 0;
2242
+ for (const session of group.sessions) {
2243
+ const ts = sessionTime(session);
2244
+ if (ts > latest) latest = ts;
2245
+ }
2246
+ return latest;
2247
+ }
2049
2248
 
2050
- // src/client/native-session-list.tsx
2249
+ // src/client/workspace-toolbar.tsx
2250
+ var import_react4 = require("react");
2051
2251
  var import_jsx_runtime5 = require("react/jsx-runtime");
2052
- var EMPTY_SESSION_BY_ID = {};
2053
- function relativeTime(value) {
2054
- const ts = Date.parse(value || "");
2055
- if (!Number.isFinite(ts)) return "";
2056
- const delta = Math.max(0, Date.now() - ts);
2057
- const min = Math.floor(delta / 6e4);
2058
- if (min < 1) return "\u521A\u521A";
2059
- if (min < 60) return String(min) + "\u5206\u949F";
2060
- const hour = Math.floor(min / 60);
2061
- if (hour < 24) return String(hour) + "\u5C0F\u65F6";
2062
- return String(Math.floor(hour / 24)) + "\u5929";
2252
+ function officialSearchIconSize(expanded) {
2253
+ return expanded ? 11 : 14;
2063
2254
  }
2255
+ function WorkspaceToolbar({
2256
+ t,
2257
+ query,
2258
+ sort,
2259
+ groupMode,
2260
+ onQueryChange,
2261
+ onSortChange,
2262
+ onGroupModeChange
2263
+ }) {
2264
+ const [searching, setSearching] = (0, import_react4.useState)(query.trim() !== "");
2265
+ const [filterOpen, setFilterOpen] = (0, import_react4.useState)(false);
2266
+ const inputRef = (0, import_react4.useRef)(null);
2267
+ const rootRef = (0, import_react4.useRef)(null);
2268
+ const searchSize = officialSearchIconSize(searching);
2269
+ (0, import_react4.useEffect)(() => {
2270
+ if (searching) inputRef.current?.focus();
2271
+ }, [searching]);
2272
+ (0, import_react4.useEffect)(() => {
2273
+ if (!filterOpen) return;
2274
+ const close = (event) => {
2275
+ const target = event.target;
2276
+ if (target instanceof Node && rootRef.current?.contains(target)) return;
2277
+ setFilterOpen(false);
2278
+ };
2279
+ document.addEventListener("mousedown", close);
2280
+ return () => document.removeEventListener("mousedown", close);
2281
+ }, [filterOpen]);
2282
+ const openSearch = () => {
2283
+ setFilterOpen(false);
2284
+ setSearching(true);
2285
+ };
2286
+ const closeSearch = () => {
2287
+ onQueryChange("");
2288
+ setSearching(false);
2289
+ };
2290
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: searching ? "dsh-st-n-toolbar is-search" : "dsh-st-n-toolbar", ref: rootRef, children: [
2291
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-head-label", children: t("sidebar.workspaces") }),
2292
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-search-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-search", onClick: openSearch, children: [
2293
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "dsh-st-n-search-btn", "aria-label": t("sidebar.search"), "aria-expanded": searching, onClick: openSearch, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchOutlineIcon, { width: searchSize, height: searchSize }) }),
2294
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { ref: inputRef, className: "dsh-st-n-search-input", value: query, placeholder: t("sidebar.searchSessions"), "aria-label": t("sidebar.searchSessions"), tabIndex: searching ? 0 : -1, "aria-hidden": !searching, onChange: (event) => onQueryChange(event.target.value), onKeyDown: (event) => {
2295
+ if (event.key === "Escape") closeSearch();
2296
+ } }),
2297
+ searching && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "dsh-st-n-search-clear", "aria-label": t("sidebar.clearSearch"), onClick: (event) => {
2298
+ event.stopPropagation();
2299
+ closeSearch();
2300
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseOutlineIcon, { width: 14, height: 14 }) })
2301
+ ] }) }),
2302
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-head-acts", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-head-filter", children: [
2303
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: filterOpen ? "dsh-st-n-head-btn is-on" : "dsh-st-n-head-btn", "aria-label": t("sidebar.filter"), "aria-expanded": filterOpen, onClick: () => setFilterOpen((open) => !open), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SlidersIcon, { width: 16, height: 16 }) }),
2304
+ filterOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-filter-menu", role: "menu", children: [
2305
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-filter-label", children: t("sidebar.groupBy") }),
2306
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FilterRow, { label: t("sidebar.groupWorkspace"), selected: groupMode === "workspace", onSelect: () => {
2307
+ onGroupModeChange("workspace");
2308
+ setFilterOpen(false);
2309
+ } }),
2310
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FilterRow, { label: t("sidebar.groupList"), selected: groupMode === "list", onSelect: () => {
2311
+ onGroupModeChange("list");
2312
+ setFilterOpen(false);
2313
+ } }),
2314
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-filter-split" }),
2315
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-filter-label", children: t("sidebar.sortBy") }),
2316
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FilterRow, { label: t("sidebar.sortManual"), selected: sort === "manual", onSelect: () => {
2317
+ onSortChange("manual");
2318
+ setFilterOpen(false);
2319
+ } }),
2320
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FilterRow, { label: t("sidebar.sortTime"), selected: sort === "time", onSelect: () => {
2321
+ onSortChange("time");
2322
+ setFilterOpen(false);
2323
+ } })
2324
+ ] })
2325
+ ] }) })
2326
+ ] });
2327
+ }
2328
+ function FilterRow({
2329
+ label,
2330
+ selected,
2331
+ onSelect
2332
+ }) {
2333
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", role: "menuitemradio", "aria-checked": selected, className: selected ? "is-on" : void 0, onClick: onSelect, children: [
2334
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: label }),
2335
+ selected ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CheckOutlineIcon, { width: 16, height: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-filter-tick" })
2336
+ ] });
2337
+ }
2338
+
2339
+ // src/client/native-session-list.tsx
2340
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2341
+ var EMPTY_SESSION_BY_ID = {};
2064
2342
  function NativeScheduleSessionList(props) {
2065
- const { t, runtime, openSession, useSessions, useWorkspaces, renameSession, archiveSession, deleteSession, forkSession } = props;
2066
- const state = (0, import_react4.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
2343
+ const { t, runtime, openSession, useSessions, useWorkspaces, renameSession, archiveSession, forkSession } = props;
2344
+ const state = (0, import_react5.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
2067
2345
  const selectedId = useSessions ? useSessions((snap) => snap?.current ?? null) : null;
2068
2346
  const sessionById = useSessions ? useSessions((snap) => snap?.byId ?? EMPTY_SESSION_BY_ID) : EMPTY_SESSION_BY_ID;
2069
2347
  const archivedIds = useWorkspaces ? useWorkspaces((snap) => snap?.archivedSessionIds ?? []) : [];
2070
- const [folded, setFolded] = (0, import_react4.useState)({});
2071
- (0, import_react4.useEffect)(() => {
2348
+ const [folded, setFolded] = (0, import_react5.useState)({});
2349
+ const [openMenu, setOpenMenu] = (0, import_react5.useState)(null);
2350
+ const [query, setQuery] = (0, import_react5.useState)("");
2351
+ const [sort, setSort] = (0, import_react5.useState)("time");
2352
+ const [groupMode, setGroupMode] = (0, import_react5.useState)("workspace");
2353
+ (0, import_react5.useEffect)(() => {
2072
2354
  void runtime.refresh().catch(() => void 0);
2073
2355
  const timer = window.setInterval(() => {
2074
2356
  void runtime.refresh().catch(() => void 0);
@@ -2077,8 +2359,8 @@ function NativeScheduleSessionList(props) {
2077
2359
  window.clearInterval(timer);
2078
2360
  };
2079
2361
  }, [runtime]);
2080
- const archived = (0, import_react4.useMemo)(() => new Set(archivedIds), [archivedIds]);
2081
- const groups = (0, import_react4.useMemo)(() => {
2362
+ const archived = (0, import_react5.useMemo)(() => new Set(archivedIds), [archivedIds]);
2363
+ const groups = (0, import_react5.useMemo)(() => {
2082
2364
  const snapshot = state.snapshot;
2083
2365
  if (snapshot === void 0) return [];
2084
2366
  return groupScheduledSessions(snapshot.automations, snapshot.runs).map((group) => ({
@@ -2094,94 +2376,128 @@ function NativeScheduleSessionList(props) {
2094
2376
  })
2095
2377
  })).filter((group) => group.sessions.length > 0);
2096
2378
  }, [archived, sessionById, state.snapshot]);
2097
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-tree", role: "tree", children: [
2098
- state.phase === "loading" && groups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-empty", children: t("loading") }),
2099
- groups.length === 0 && state.phase !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-empty", children: t("sidebar.empty") }),
2100
- groups.map((group) => {
2101
- const expanded = folded[group.id] !== true;
2102
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-group", children: [
2103
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-row", role: "treeitem", "aria-expanded": expanded, onClick: () => setFolded((current) => ({ ...current, [group.id]: expanded })), children: [
2104
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-slot dsh-st-n-folder", children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FolderOpenIcon, { width: 16, height: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FolderClosedIcon, { width: 16, height: 16 }) }),
2105
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-slot dsh-st-n-chevron", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: expanded ? "dsh-st-n-arrow is-open" : "dsh-st-n-arrow", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronIcon, { width: 14, height: 14 }) }) }),
2106
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-title", children: group.name }),
2107
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-acts", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2108
- "button",
2379
+ const visibleGroups = (0, import_react5.useMemo)(() => applyWorkspaceBrowserQuery(groups.map((group) => ({ ...group, name: group.name })), query, sort, groupMode), [groups, query, sort, groupMode]);
2380
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-n", children: [
2381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WorkspaceToolbar, { t, query, sort, groupMode, onQueryChange: setQuery, onSortChange: setSort, onGroupModeChange: setGroupMode }),
2382
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-n-tree", role: "tree", children: [
2383
+ state.phase === "loading" && visibleGroups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-n-empty", children: t("loading") }),
2384
+ visibleGroups.length === 0 && state.phase !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-n-empty", children: t("sidebar.empty") }),
2385
+ visibleGroups.map((group) => {
2386
+ const expanded = folded[group.id] !== true;
2387
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-n-group", children: [
2388
+ groupMode === "workspace" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-n-row", role: "treeitem", "aria-expanded": expanded, onClick: () => setFolded((current) => ({ ...current, [group.id]: expanded })), children: [
2389
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-slot dsh-st-n-folder", children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FolderOpenIcon, { width: 16, height: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FolderClosedIcon, { width: 16, height: 16 }) }),
2390
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-title", children: group.name })
2391
+ ] }),
2392
+ (groupMode === "list" || expanded) && group.sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2393
+ NativeSessionRow,
2109
2394
  {
2110
- type: "button",
2111
- className: "dsh-st-n-ico",
2112
- "aria-label": t("session.deleteFolder"),
2113
- onClick: (event) => {
2114
- event.stopPropagation();
2115
- void (async () => {
2116
- for (const session of group.sessions) {
2117
- try {
2118
- await deleteSession?.(session.id);
2119
- } catch {
2120
- }
2121
- }
2122
- await runtime.forgetAutomationSessions(group.id);
2123
- })();
2395
+ t,
2396
+ id: session.id,
2397
+ title: session.title,
2398
+ hoverTitle: String(sessionById[session.id]?.displayTitle ?? sessionById[session.id]?.title ?? group.name),
2399
+ updatedAt: session.updatedAt,
2400
+ running: session.running,
2401
+ selected: selectedId === session.id,
2402
+ menuOpen: openMenu?.id === session.id,
2403
+ menuPoint: openMenu === null || openMenu.id !== session.id ? { x: 8, y: 8 } : { x: openMenu.x, y: openMenu.y },
2404
+ onToggleMenu: (event) => setOpenMenu((current) => nextOpenSessionMenu(current, session.id, pointerPoint(event))),
2405
+ onCloseMenu: () => setOpenMenu((current) => current?.id === session.id ? null : current),
2406
+ onOpen: () => {
2407
+ setOpenMenu(null);
2408
+ openSession?.(session.id);
2124
2409
  },
2125
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TrashIcon, { width: 16, height: 16 })
2126
- }
2127
- ) })
2128
- ] }),
2129
- expanded && group.sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2130
- NativeSessionRow,
2131
- {
2132
- t,
2133
- id: session.id,
2134
- title: session.title,
2135
- updatedAt: session.updatedAt,
2136
- running: session.running,
2137
- selected: selectedId === session.id,
2138
- onOpen: () => {
2139
- openSession?.(session.id);
2140
- void runtime.adoptSession?.(session.id).catch(() => void 0);
2141
- },
2142
- onDelete: async () => {
2143
- try {
2144
- await deleteSession?.(session.id);
2145
- } catch {
2146
- }
2147
- await runtime.forgetSession(session.id);
2410
+ ...renameSession === void 0 ? {} : { renameSession },
2411
+ ...archiveSession === void 0 ? {} : { archiveSession },
2412
+ ...forkSession === void 0 ? {} : { forkSession }
2148
2413
  },
2149
- ...renameSession === void 0 ? {} : { renameSession },
2150
- ...archiveSession === void 0 ? {} : { archiveSession },
2151
- ...forkSession === void 0 ? {} : { forkSession }
2152
- },
2153
- session.id
2154
- ))
2155
- ] }, group.id);
2156
- })
2157
- ] }) });
2414
+ session.id
2415
+ ))
2416
+ ] }, group.id);
2417
+ })
2418
+ ] })
2419
+ ] });
2158
2420
  }
2159
2421
  function NativeSessionRow(props) {
2160
- const { t, id, title, updatedAt, running, selected, onOpen, onDelete, renameSession, archiveSession, forkSession } = props;
2161
- const [menu, setMenu] = (0, import_react4.useState)(false);
2162
- const [renaming, setRenaming] = (0, import_react4.useState)(false);
2163
- const [draft, setDraft] = (0, import_react4.useState)(title);
2164
- (0, import_react4.useEffect)(() => {
2422
+ const { t, id, title, hoverTitle, updatedAt, running, selected, menuOpen, menuPoint, onToggleMenu, onCloseMenu, onOpen, renameSession, archiveSession, forkSession } = props;
2423
+ const rowRef = (0, import_react5.useRef)(null);
2424
+ const menuRef = (0, import_react5.useRef)(null);
2425
+ const hoverRef = (0, import_react5.useRef)(null);
2426
+ const [hoverOpen, setHoverOpen] = (0, import_react5.useState)(false);
2427
+ const [hoverStyle, setHoverStyle] = (0, import_react5.useState)({});
2428
+ const hoverTimer = (0, import_react5.useRef)(void 0);
2429
+ const [renaming, setRenaming] = (0, import_react5.useState)(false);
2430
+ const [draft, setDraft] = (0, import_react5.useState)(title);
2431
+ const [menuStyle, setMenuStyle] = (0, import_react5.useState)({});
2432
+ (0, import_react5.useEffect)(() => {
2165
2433
  setDraft(title);
2166
2434
  }, [title]);
2167
- (0, import_react4.useEffect)(() => {
2168
- if (!menu) return;
2435
+ (0, import_react5.useEffect)(() => {
2436
+ if (!menuOpen) return;
2169
2437
  const close = (event) => {
2170
- const target = event.target;
2171
- if (target !== null && event.target.closest(".dsh-st-n-sess.is-menu") !== null) return;
2172
- setMenu(false);
2438
+ if (!shouldCloseNativeSessionMenu(event.target, [rowRef.current, menuRef.current])) return;
2439
+ onCloseMenu();
2440
+ };
2441
+ const onKey = (event) => {
2442
+ if (event.key === "Escape") onCloseMenu();
2173
2443
  };
2174
2444
  document.addEventListener("mousedown", close);
2445
+ document.addEventListener("keydown", onKey);
2175
2446
  return () => {
2176
2447
  document.removeEventListener("mousedown", close);
2448
+ document.removeEventListener("keydown", onKey);
2177
2449
  };
2178
- }, [menu]);
2450
+ }, [menuOpen, onCloseMenu]);
2451
+ (0, import_react5.useLayoutEffect)(() => {
2452
+ if (!menuOpen) return;
2453
+ const update = () => {
2454
+ const el = menuRef.current;
2455
+ const size = el === null ? { width: 218, height: 176 } : { width: el.offsetWidth, height: el.offsetHeight };
2456
+ setMenuStyle(nativeSessionMenuStyle(menuPoint, size, { width: window.innerWidth, height: window.innerHeight }));
2457
+ };
2458
+ update();
2459
+ window.addEventListener("resize", update);
2460
+ document.addEventListener("scroll", update, true);
2461
+ return () => {
2462
+ window.removeEventListener("resize", update);
2463
+ document.removeEventListener("scroll", update, true);
2464
+ };
2465
+ }, [menuOpen, menuPoint]);
2466
+ (0, import_react5.useEffect)(() => {
2467
+ if (!hoverOpen || menuOpen) return;
2468
+ const update = () => {
2469
+ const row = rowRef.current?.getBoundingClientRect();
2470
+ const card = hoverRef.current;
2471
+ if (row === void 0) return;
2472
+ const size = card === null ? { width: 220, height: 96 } : { width: card.offsetWidth, height: card.offsetHeight };
2473
+ setHoverStyle(nativeSessionHoverStyle({ right: row.right, top: row.top }, size, { width: window.innerWidth, height: window.innerHeight }));
2474
+ };
2475
+ update();
2476
+ window.addEventListener("resize", update);
2477
+ document.addEventListener("scroll", update, true);
2478
+ return () => {
2479
+ window.removeEventListener("resize", update);
2480
+ document.removeEventListener("scroll", update, true);
2481
+ };
2482
+ }, [hoverOpen, menuOpen, title, updatedAt, running]);
2483
+ (0, import_react5.useEffect)(() => () => {
2484
+ if (hoverTimer.current !== void 0) window.clearTimeout(hoverTimer.current);
2485
+ }, []);
2179
2486
  const run = (action) => {
2180
2487
  void Promise.resolve(action()).catch(() => void 0);
2181
2488
  };
2182
- const rowClass = "dsh-st-n-sess" + (selected ? " is-on" : "") + (menu ? " is-menu" : "");
2489
+ const showHover = () => {
2490
+ if (menuOpen) return;
2491
+ if (hoverTimer.current !== void 0) window.clearTimeout(hoverTimer.current);
2492
+ hoverTimer.current = window.setTimeout(() => setHoverOpen(true), 500);
2493
+ };
2494
+ const hideHover = () => {
2495
+ if (hoverTimer.current !== void 0) window.clearTimeout(hoverTimer.current);
2496
+ setHoverOpen(false);
2497
+ };
2498
+ const rowClass = "dsh-st-n-sess" + (selected ? " is-on" : "") + (menuOpen ? " is-menu" : "");
2183
2499
  if (renaming) {
2184
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: rowClass, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { className: "dsh-st-n-rename", value: draft, autoFocus: true, "aria-label": t("session.rename"), onChange: (event) => setDraft(event.target.value), onClick: (event) => event.stopPropagation(), onKeyDown: (event) => {
2500
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: rowClass, ref: rowRef, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { className: "dsh-st-n-rename", value: draft, autoFocus: true, "aria-label": t("session.rename"), onChange: (event) => setDraft(event.target.value), onClick: (event) => event.stopPropagation(), onKeyDown: (event) => {
2185
2501
  if (event.key === "Enter") {
2186
2502
  event.preventDefault();
2187
2503
  setRenaming(false);
@@ -2198,14 +2514,23 @@ function NativeSessionRow(props) {
2198
2514
  } }) });
2199
2515
  }
2200
2516
  const menuItems = [
2201
- { id: "rename", label: t("session.rename"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PencilIcon, { width: 16, height: 16 }), go: () => {
2517
+ { id: "rename", label: t("session.rename"), icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PencilIcon, { width: 16, height: 16 }), go: () => {
2202
2518
  setRenaming(true);
2203
2519
  } },
2204
- { id: "fork", label: t("session.fork"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BranchIcon, { width: 16, height: 16 }), go: () => run(() => forkSession?.(id)) },
2205
- { id: "archive", label: t("session.archive"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArchiveIcon, { width: 16, height: 16 }), go: () => run(() => archiveSession?.(id)) },
2206
- { id: "delete", label: t("session.delete"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TrashIcon, { width: 16, height: 16 }), danger: true, go: () => run(() => onDelete()) }
2520
+ { id: "fork", label: t("session.fork"), icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BranchIcon, { width: 16, height: 16 }), go: () => run(() => forkSession?.(id)) },
2521
+ { id: "archive", label: t("session.archive"), icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArchiveIcon, { width: 16, height: 16 }), go: () => run(() => archiveSession?.(id)) }
2207
2522
  ];
2208
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: rowClass, role: "treeitem", tabIndex: 0, "aria-selected": selected, onClick: onOpen, onKeyDown: (event) => {
2523
+ const menu = menuOpen && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2524
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: menuRef, className: "dsh-st-n-menu is-float", "data-n-menu": id, style: menuStyle, onMouseDown: (event) => event.stopPropagation(), onClick: (event) => event.stopPropagation(), children: menuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("button", { type: "button", className: void 0, onClick: () => {
2525
+ onCloseMenu();
2526
+ item.go();
2527
+ }, children: [
2528
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-mi", children: item.icon }),
2529
+ item.label
2530
+ ] }, item.id)) }),
2531
+ document.body
2532
+ ) : null;
2533
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: rowClass, ref: rowRef, role: "treeitem", tabIndex: 0, "aria-selected": selected, "data-n-menu-root": id, onClick: onOpen, onMouseEnter: showHover, onMouseLeave: hideHover, onKeyDown: (event) => {
2209
2534
  if (event.key === "Enter" || event.key === " ") {
2210
2535
  event.preventDefault();
2211
2536
  onOpen();
@@ -2213,30 +2538,37 @@ function NativeSessionRow(props) {
2213
2538
  }, onContextMenu: (event) => {
2214
2539
  event.preventDefault();
2215
2540
  event.stopPropagation();
2216
- setMenu((open) => !open);
2541
+ onToggleMenu(event);
2217
2542
  }, children: [
2218
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-slot", children: running ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RunningStateDot, {}) : null }),
2219
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-title", children: title }),
2220
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-time", children: relativeTime(updatedAt) }),
2221
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-acts", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "dsh-st-n-ico", "aria-label": title + " \u66F4\u591A", onClick: (event) => {
2543
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-slot", children: running ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RunningStateDot, {}) : null }),
2544
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-title", children: title }),
2545
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-time", children: relativeTime(updatedAt) }),
2546
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-acts", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "dsh-st-n-ico", "aria-label": title + " \u66F4\u591A", onMouseDown: (event) => event.stopPropagation(), onClick: (event) => {
2222
2547
  event.stopPropagation();
2223
- setMenu((open) => !open);
2224
- }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EllipsisIcon, { width: 16, height: 16 }) }) }),
2225
- menu && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-menu", onClick: (event) => event.stopPropagation(), children: menuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", className: item.danger === true ? "danger" : void 0, onClick: () => {
2226
- setMenu(false);
2227
- item.go();
2228
- }, children: [
2229
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-mi", children: item.icon }),
2230
- item.label
2231
- ] }, item.id)) })
2548
+ hideHover();
2549
+ onToggleMenu(event);
2550
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(EllipsisIcon, { width: 16, height: 16 }) }) }),
2551
+ menu,
2552
+ hoverOpen && !menuOpen && typeof document !== "undefined" && (0, import_react_dom2.createPortal)(
2553
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: hoverRef, className: "dsh-st-n-hover", style: hoverStyle, onMouseEnter: showHover, onMouseLeave: hideHover, children: [
2554
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-n-hover-title", children: (hoverTitle ?? title).trim() || title }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-n-hover-time", children: relativeTime(updatedAt) }),
2556
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-n-hover-state", children: [
2557
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: running ? "dsh-st-n-hover-dot is-run" : "dsh-st-n-hover-dot" }),
2558
+ running ? t("session.runningStatus") : t("session.idle")
2559
+ ] })
2560
+ ] }),
2561
+ document.body
2562
+ )
2232
2563
  ] });
2233
2564
  }
2234
2565
 
2235
2566
  // src/client/ScheduleRail.tsx
2236
- var import_react5 = require("react");
2237
- var import_jsx_runtime6 = require("react/jsx-runtime");
2567
+ var import_react6 = require("react");
2568
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2238
2569
  var EMPTY_EXTRA_TABS = [];
2239
- var OfficialTreeGuard = class extends import_react5.Component {
2570
+ var noopSubscribe = (_listener) => () => void 0;
2571
+ var OfficialTreeGuard = class extends import_react6.Component {
2240
2572
  state = { failed: false };
2241
2573
  static getDerivedStateFromError() {
2242
2574
  return { failed: true };
@@ -2253,9 +2585,9 @@ function ScheduleRail({
2253
2585
  runtime,
2254
2586
  openSession
2255
2587
  }) {
2256
- const state = (0, import_react5.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
2257
- const [folded, setFolded] = (0, import_react5.useState)({});
2258
- (0, import_react5.useEffect)(() => {
2588
+ const state = (0, import_react6.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
2589
+ const [folded, setFolded] = (0, import_react6.useState)({});
2590
+ (0, import_react6.useEffect)(() => {
2259
2591
  void runtime.refresh().catch(() => void 0);
2260
2592
  const timer = window.setInterval(() => {
2261
2593
  void runtime.refresh().catch(() => void 0);
@@ -2264,36 +2596,36 @@ function ScheduleRail({
2264
2596
  window.clearInterval(timer);
2265
2597
  };
2266
2598
  }, [runtime]);
2267
- const groups = (0, import_react5.useMemo)(() => {
2599
+ const groups = (0, import_react6.useMemo)(() => {
2268
2600
  const snapshot = state.snapshot;
2269
2601
  if (snapshot === void 0) return [];
2270
2602
  return groupScheduledSessions(snapshot.automations, snapshot.runs);
2271
2603
  }, [state.snapshot]);
2272
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-rail", children: [
2273
- state.phase === "loading" && groups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-empty", children: t("loading") }),
2274
- groups.length === 0 && state.phase !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-empty", children: t("sidebar.empty") }),
2275
- groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "dsh-st-rail-group", children: [
2276
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2604
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-st-rail", children: [
2605
+ state.phase === "loading" && groups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-rail-empty", children: t("loading") }),
2606
+ groups.length === 0 && state.phase !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-rail-empty", children: t("sidebar.empty") }),
2607
+ groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "dsh-st-rail-group", children: [
2608
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2277
2609
  "button",
2278
2610
  {
2279
2611
  type: "button",
2280
2612
  className: "dsh-st-rail-head",
2281
2613
  onClick: () => setFolded((current) => ({ ...current, [group.id]: !current[group.id] })),
2282
2614
  children: [
2283
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-rail-folder", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ClockIcon, { width: 16, height: 16 }) }),
2284
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-rail-title", children: group.name })
2615
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-st-rail-folder", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ClockIcon, { width: 16, height: 16 }) }),
2616
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-st-rail-title", children: group.name })
2285
2617
  ]
2286
2618
  }
2287
2619
  ),
2288
- folded[group.id] !== true && group.sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2620
+ folded[group.id] !== true && group.sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2289
2621
  "button",
2290
2622
  {
2291
2623
  type: "button",
2292
2624
  className: "dsh-st-rail-session",
2293
2625
  onClick: () => openSession?.(session.id),
2294
2626
  children: [
2295
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: session.label }),
2296
- session.running && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RunningStateDot, {})
2627
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: session.label }),
2628
+ session.running && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RunningStateDot, {})
2297
2629
  ]
2298
2630
  },
2299
2631
  session.id
@@ -2316,26 +2648,20 @@ function NativeScheduleShell({
2316
2648
  wide
2317
2649
  }) {
2318
2650
  const Official = officialTree;
2319
- const [tab, setTab] = (0, import_react5.useState)(() => {
2651
+ const [tab, setTab] = (0, import_react6.useState)(() => {
2320
2652
  try {
2321
2653
  return readNativeSidebarTab(window.localStorage.getItem(NATIVE_SIDEBAR_TAB_KEY));
2322
2654
  } catch {
2323
2655
  return "tasks";
2324
2656
  }
2325
2657
  });
2326
- const extraTabs = (0, import_react5.useSyncExternalStore)(
2327
- tabRegistry?.subscribe ?? ((listener) => {
2328
- listener();
2329
- return () => void 0;
2330
- }),
2658
+ const extraTabs = (0, import_react6.useSyncExternalStore)(
2659
+ tabRegistry?.subscribe ?? noopSubscribe,
2331
2660
  () => tabRegistry?.getTabs() ?? EMPTY_EXTRA_TABS,
2332
2661
  () => EMPTY_EXTRA_TABS
2333
2662
  );
2334
- const channelsReady = (0, import_react5.useSyncExternalStore)(
2335
- subscribeChannels ?? ((listener) => {
2336
- listener();
2337
- return () => void 0;
2338
- }),
2663
+ const channelsReady = (0, import_react6.useSyncExternalStore)(
2664
+ subscribeChannels ?? noopSubscribe,
2339
2665
  () => {
2340
2666
  try {
2341
2667
  return hasChannels?.() === true;
@@ -2346,26 +2672,39 @@ function NativeScheduleShell({
2346
2672
  () => false
2347
2673
  );
2348
2674
  const currentId = useSessions?.((state) => state?.current ?? null);
2349
- const useFilteredSessions = (0, import_react5.useCallback)((selector) => {
2675
+ const automationState = (0, import_react6.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
2676
+ const scheduledIds = (0, import_react6.useMemo)(() => collectScheduledSessionIds(automationState.snapshot?.runs), [automationState.snapshot]);
2677
+ const useFilteredSessions = (0, import_react6.useCallback)((selector, eq) => {
2350
2678
  if (useSessions === void 0) return selector({ ids: [], byId: {}, current: null });
2351
- return useSessions((state) => selector(filterTaskSessionState(state)));
2352
- }, [useSessions]);
2353
- const useFilteredWorkspaces = (0, import_react5.useCallback)((selector) => {
2679
+ return useSessions((state) => selector(filterTaskSessionState(state, scheduledIds)), eq);
2680
+ }, [useSessions, scheduledIds]);
2681
+ const useFilteredWorkspaces = (0, import_react6.useCallback)((selector, eq) => {
2354
2682
  if (useWorkspaces === void 0) return selector({ items: [], archivedSessionIds: [] });
2355
- return useWorkspaces((state) => selector(filterWorkspaceListState(state)));
2356
- }, [useWorkspaces]);
2357
- (0, import_react5.useEffect)(() => {
2683
+ return useWorkspaces((state) => selector(filterWorkspaceListState(state, scheduledIds)), eq);
2684
+ }, [useWorkspaces, scheduledIds]);
2685
+ (0, import_react6.useEffect)(() => {
2358
2686
  try {
2359
2687
  window.localStorage.setItem(NATIVE_SIDEBAR_TAB_KEY, tab);
2360
2688
  } catch {
2361
2689
  }
2362
2690
  }, [tab]);
2363
- (0, import_react5.useEffect)(() => {
2364
- const next = tabForSessionId(currentId ?? void 0);
2365
- if (next === "channels" && channelsReady) setTab("channels");
2691
+ const previousCurrentId = (0, import_react6.useRef)(currentId);
2692
+ const tabFollowReady = (0, import_react6.useRef)(false);
2693
+ (0, import_react6.useEffect)(() => {
2694
+ if (!tabFollowReady.current) {
2695
+ tabFollowReady.current = true;
2696
+ previousCurrentId.current = currentId;
2697
+ return;
2698
+ }
2699
+ const previous = previousCurrentId.current;
2700
+ previousCurrentId.current = currentId;
2701
+ if (!shouldFollowSessionTab(previous, currentId)) return;
2702
+ const next = tabForSessionId(currentId ?? void 0, scheduledIds);
2703
+ const extraIds = extraTabs.map((item) => item.id);
2704
+ if (next === "channels" && (channelsReady || extraIds.includes("channels"))) setTab("channels");
2366
2705
  const matched = extraTabs.find((item) => currentId !== void 0 && currentId !== null && item.matchSession?.(String(currentId)) === true);
2367
- if (matched !== void 0) setTab(matched.id);
2368
- }, [currentId, channelsReady, extraTabs]);
2706
+ if (matched !== void 0 && matched.id !== "schedule") setTab(matched.id);
2707
+ }, [currentId, channelsReady, extraTabs, scheduledIds]);
2369
2708
  const rawOfficialProps = { ...hostProps ?? {}, ...wide === void 0 ? {} : { wide } };
2370
2709
  const filteredOfficialProps = {
2371
2710
  ...rawOfficialProps,
@@ -2374,19 +2713,27 @@ function NativeScheduleShell({
2374
2713
  };
2375
2714
  const renderOfficial = (props) => {
2376
2715
  if (Official === void 0) {
2377
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NativeTaskRail, { t, ...openSession === void 0 ? {} : { openSession }, ...useSessions === void 0 ? {} : { useSessions: useFilteredSessions }, ...useWorkspaces === void 0 ? {} : { useWorkspaces } });
2716
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NativeTaskRail, { t, ...openSession === void 0 ? {} : { openSession }, ...useSessions === void 0 ? {} : { useSessions: useFilteredSessions }, ...useWorkspaces === void 0 ? {} : { useWorkspaces } });
2378
2717
  }
2379
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(OfficialTreeGuard, { fallback: (0, import_react5.createElement)(Official, rawOfficialProps), children: (0, import_react5.createElement)(Official, props) });
2718
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(OfficialTreeGuard, { fallback: (0, import_react6.createElement)(Official, rawOfficialProps), children: (0, import_react6.createElement)(Official, props) });
2380
2719
  };
2381
- if (wide === false) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderOfficial(rawOfficialProps) });
2382
- const visibleTab = tab === "channels" && !channelsReady ? "tasks" : tab;
2383
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-shell-rail", children: [
2384
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-shell-tabs", role: "tablist", "aria-label": t("sidebar.tabs"), children: [
2385
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === "tasks", className: visibleTab === "tasks" ? "is-on" : void 0, onClick: () => setTab("tasks"), children: t("sidebar.tasksTab") }),
2386
- extraTabs.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === item.id, className: visibleTab === item.id ? "is-on" : void 0, onClick: () => setTab(item.id), children: item.label }, item.id)),
2387
- channelsReady && extraTabs.every((item) => item.id !== "channels") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === "channels", className: visibleTab === "channels" ? "is-on" : void 0, onClick: () => setTab("channels"), children: t("sidebar.channelsTab") })
2720
+ if (wide === false) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: renderOfficial(filteredOfficialProps) });
2721
+ const foreignTabs = extraTabs.filter((item) => item.id !== "schedule");
2722
+ const visibleTab = resolveVisibleSidebarTab({
2723
+ tab,
2724
+ channelsReady,
2725
+ extraTabIds: ownedSidebarTabIds({ extraTabIds: foreignTabs.map((item) => item.id), channelsReady })
2726
+ });
2727
+ const hostedSchedule = extraTabs.find((item) => item.id === "schedule");
2728
+ const scheduleBody = hostedSchedule === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NativeScheduleSessionList, { t, runtime, ...openSession === void 0 ? {} : { openSession }, ...useSessions === void 0 ? {} : { useSessions }, ...useWorkspaces === void 0 ? {} : { useWorkspaces } }) : hostedSchedule.render({ ...hostProps ?? {}, openSession, open: openSession, useSessions, wide: true });
2729
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-st-shell-rail", children: [
2730
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-st-shell-tabs", role: "tablist", "aria-label": t("sidebar.tabs"), children: [
2731
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === "tasks", className: visibleTab === "tasks" ? "is-on" : void 0, onClick: () => setTab("tasks"), children: t("sidebar.tasksTab") }),
2732
+ foreignTabs.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === item.id, className: visibleTab === item.id ? "is-on" : void 0, onClick: () => setTab(item.id), children: item.label }, item.id)),
2733
+ channelsReady && foreignTabs.every((item) => item.id !== "channels") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === "channels", className: visibleTab === "channels" ? "is-on" : void 0, onClick: () => setTab("channels"), children: t("sidebar.channelsTab") }),
2734
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", role: "tab", "aria-selected": visibleTab === "schedule", className: visibleTab === "schedule" ? "is-on" : void 0, onClick: () => setTab("schedule"), children: t("sidebar.tab") })
2388
2735
  ] }),
2389
- extraTabs.find((item) => item.id === visibleTab) !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-shell-body", children: extraTabs.find((item) => item.id === visibleTab)?.render({ ...hostProps ?? {}, openSession, open: openSession, useSessions, wide: true }) }) : visibleTab === "channels" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-shell-body", children: renderSlot?.("sidebar.channels", { ...hostProps ?? {}, openSession, open: openSession, useSessions, wide: true }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-official-tree", children: renderOfficial(filteredOfficialProps) })
2736
+ visibleTab === "schedule" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-shell-body", children: scheduleBody }) : foreignTabs.find((item) => item.id === visibleTab) !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-shell-body", children: foreignTabs.find((item) => item.id === visibleTab)?.render({ ...hostProps ?? {}, openSession, open: openSession, useSessions, wide: true }) }) : visibleTab === "channels" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-shell-body", children: renderSlot?.("sidebar.channels", { ...hostProps ?? {}, openSession, open: openSession, useSessions, wide: true, skin: "native" }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-official-tree", children: renderOfficial(filteredOfficialProps) })
2390
2737
  ] });
2391
2738
  }
2392
2739
  function NativeTaskRail({
@@ -2398,10 +2745,10 @@ function NativeTaskRail({
2398
2745
  const snap = useSessions === void 0 ? { ids: [], byId: {}, current: null } : useSessions((state) => state ?? { ids: [], byId: {}, current: null });
2399
2746
  const workspaces = useWorkspaces === void 0 ? void 0 : useWorkspaces((state) => state ?? { items: [], archivedSessionIds: [] });
2400
2747
  const groups = groupNativeTaskSessions(snap, workspaces, t("sidebar.ungrouped"));
2401
- if (groups.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-empty", children: t("sidebar.tasksEmpty") });
2402
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail", children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "dsh-st-rail-group", children: [
2403
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-head is-static", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-rail-title", children: group.label }) }),
2404
- group.sessions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2748
+ if (groups.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-rail-empty", children: t("sidebar.tasksEmpty") });
2749
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-rail", children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "dsh-st-rail-group", children: [
2750
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-st-rail-head is-static", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-st-rail-title", children: group.label }) }),
2751
+ group.sessions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2405
2752
  "button",
2406
2753
  {
2407
2754
  type: "button",
@@ -2409,7 +2756,7 @@ function NativeTaskRail({
2409
2756
  onClick: () => {
2410
2757
  if (item.id !== void 0) openSession?.(item.id);
2411
2758
  },
2412
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: item.title || item.id })
2759
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: item.title || item.id })
2413
2760
  },
2414
2761
  item.id
2415
2762
  ))
@@ -2556,7 +2903,7 @@ var CSS_TEXT = `
2556
2903
  .dsh-st-shell-body>*{min-width:0;flex:1}.dsh-st-official-tree{display:flex;min-height:0;flex:1;overflow:hidden}.dsh-st-official-tree>*{min-width:0;flex:1}
2557
2904
  .dsh-st-rail-head.is-static{cursor:default;font-weight:600}
2558
2905
  .dsh-st-rail-session.is-on{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}
2559
- .dsh-st-n,.dsh-st-n *{box-sizing:border-box}.dsh-st-n{box-sizing:border-box;display:flex;flex:1;min-width:0;max-width:100%;min-height:0;flex-direction:column;padding:0;padding-right:var(--dsh-sidebar-inline-padding,12px);color:var(--dsw-alias-label-primary,inherit);font:14px/20px inherit;overflow:hidden}.dsh-st-n-head{flex:none;display:flex;align-items:center;height:36px;padding-left:4px;margin:2px 0 4px;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-head-label{font-size:14px;line-height:20px}.dsh-st-n-tree{flex:1;min-width:0;max-width:100%;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0 0 16px 4px;user-select:none}.dsh-st-n-empty{padding:14px 8px;color:var(--dsw-alias-label-tertiary,#8b8f98);font-size:12px}.dsh-st-n-group{min-width:0;max-width:100%}.dsh-st-n-row,.dsh-st-n-sess{display:flex;align-items:center;max-width:100%;border-radius:8px;padding:0 8px;cursor:pointer;border:0;background:transparent;color:var(--dsw-alias-label-primary,inherit);text-align:left;font:14px/20px inherit}.dsh-st-n-row{height:34px;gap:6px}.dsh-st-n-sess{height:32px;gap:0;position:relative;width:100%;appearance:none}.dsh-st-n-row:hover,.dsh-st-n-sess:hover,.dsh-st-n-sess.is-on,.dsh-st-n-sess.is-menu{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-slot{flex:none;width:16px;height:20px;display:inline-flex;align-items:center;justify-content:center}.dsh-st-n-row .dsh-st-n-chevron{display:none;color:var(--dsw-alias-label-caption,#ADB2B8)}.dsh-st-n-row:hover .dsh-st-n-chevron{display:inline-flex}.dsh-st-n-row:hover .dsh-st-n-folder{display:none}.dsh-st-n-arrow{transition:transform .15s ease}.dsh-st-n-arrow.is-open{transform:rotate(90deg)}.dsh-st-n-title{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px}.dsh-st-n-sess .dsh-st-n-title{margin:0 6px 0 4px}.dsh-st-n-time{flex:none;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary,#81858C);white-space:nowrap}.dsh-st-n-acts{flex:none;display:none;align-items:center;gap:12px}.dsh-st-n-row:hover .dsh-st-n-acts,.dsh-st-n-sess:hover .dsh-st-n-acts,.dsh-st-n-row.is-menu .dsh-st-n-acts,.dsh-st-n-sess.is-menu .dsh-st-n-acts{display:inline-flex}.dsh-st-n-sess:hover .dsh-st-n-time,.dsh-st-n-sess.is-menu .dsh-st-n-time{display:none}.dsh-st-n-ico{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#81858C);padding:0;cursor:pointer}.dsh-st-n-ico:hover{color:var(--dsw-alias-label-primary,inherit)}.dsh-st-n-menu{position:absolute;right:8px;top:calc(100% + 4px);z-index:1100;min-width:218px;max-width:360px;box-sizing:border-box;padding:4px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-inverted,rgba(255,255,255,.12));border-radius:12px;background:var(--dsw-specific-menu,#1c2128);box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.36))}.dsh-st-n-menu button{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:0;border-radius:10px;background:transparent;cursor:pointer;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary,inherit);text-align:left}.dsh-st-n-menu button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-mi{display:inline-flex;flex:none;width:16px;height:16px;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-menu button.danger{color:var(--dsw-alias-state-error-primary,#f85149)}.dsh-st-n-menu button.danger .dsh-st-n-mi{color:inherit}.dsh-st-n-menu button.danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(248,81,73,.12))}.dsh-st-n-rename{flex:1;min-width:0;margin:0 6px 0 4px;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:var(--dsw-alias-button-elevated-fill,rgba(255,255,255,.04));color:inherit;font:inherit;padding:0 2px}
2906
+ .dsh-st-n,.dsh-st-n *{box-sizing:border-box}.dsh-st-n{box-sizing:border-box;display:flex;flex:1;min-width:0;max-width:100%;min-height:0;flex-direction:column;padding:0;padding-right:var(--dsh-sidebar-inline-padding,12px);color:var(--dsw-alias-label-primary,inherit);font:14px/20px inherit;overflow:hidden}.dsh-st-n-toolbar{box-sizing:border-box;flex:none;height:36px;margin:2px -4px 4px 0;padding-left:4px;display:flex;justify-content:flex-end;align-items:center;gap:4px;overflow:visible;position:relative;z-index:2;color:var(--dsw-alias-label-tertiary,#81858C);border-radius:12px}.dsh-st-n-head-label{white-space:nowrap;min-width:0;max-width:45%;flex:none;line-height:20px;font-size:14px;overflow:hidden;transition:max-width .18s var(--ds-ease-in-out,ease),margin-right .18s var(--ds-ease-in-out,ease),opacity .12s var(--ds-ease-in-out,ease),transform .18s var(--ds-ease-in-out,ease),visibility 0s linear}.dsh-st-n-toolbar.is-search .dsh-st-n-head-label{opacity:0;visibility:hidden;max-width:0;margin-right:-4px;transform:translate(-4px);transition-delay:0s,0s,0s,0s,.18s}.dsh-st-n-search-slot{box-sizing:border-box;min-width:28px;max-width:28px;transition:max-width .18s var(--ds-ease-in-out,ease);flex:none;align-items:center;margin-left:auto;display:flex;position:relative;z-index:2}.dsh-st-n-toolbar.is-search .dsh-st-n-search-slot{flex:1;min-width:0;max-width:100%}.dsh-st-n-search{box-sizing:border-box;cursor:text;width:100%;height:28px;color:var(--dsw-alias-label-secondary);transition:width .18s var(--ds-ease-in-out,ease),padding .18s var(--ds-ease-in-out,ease),border-color .18s var(--ds-ease-in-out,ease);background:transparent;border:none;border-radius:50%;flex:none;align-items:center;margin:0;padding:0;display:flex;overflow:hidden}.dsh-st-n-toolbar.is-search .dsh-st-n-search{border:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,.10));width:calc(100% + 4px);height:30px;border-radius:10px;margin-inline:-2px;padding:0 4px 0 0}.dsh-st-n-search-btn,.dsh-st-n-head-btn{cursor:pointer;width:28px;height:28px;min-width:28px;min-height:28px;position:relative;z-index:1;color:var(--dsw-alias-label-secondary);background:transparent;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.dsh-st-n-toolbar.is-search .dsh-st-n-search-btn{width:28px;height:30px}.dsh-st-n-search-btn:hover,.dsh-st-n-head-btn:hover,.dsh-st-n-head-btn.is-on{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06));color:var(--dsw-alias-label-primary,inherit)}.dsh-st-n-toolbar.is-search .dsh-st-n-search-btn:hover{background:transparent}.dsh-st-n-head-acts{opacity:1;visibility:visible;max-width:32px;transition:max-width .18s var(--ds-ease-in-out,ease),opacity .12s var(--ds-ease-in-out,ease),transform .18s var(--ds-ease-in-out,ease),visibility 0s linear;flex:none;align-items:center;gap:4px;display:flex;overflow:visible;position:relative}.dsh-st-n-toolbar.is-search .dsh-st-n-head-acts{opacity:0;visibility:hidden;pointer-events:none;max-width:0;transform:translate(4px);transition-delay:0s,0s,0s,.18s}.dsh-st-n-head-filter{position:relative}.dsh-st-n-search-input{display:none;opacity:0;pointer-events:none;width:0;min-width:0;flex:none;color:var(--dsw-alias-label-primary,inherit);transition:opacity .12s var(--ds-ease-in-out,ease);background:transparent;border:none;outline:none;flex:1;font-size:13px;line-height:18px}.dsh-st-n-toolbar.is-search .dsh-st-n-search-input{display:block;opacity:1;pointer-events:auto;margin-left:-2px;width:auto;flex:1;min-width:0}.dsh-st-n-search-input::placeholder{color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-search-clear{cursor:pointer;width:24px;height:24px;color:var(--dsw-alias-label-secondary);background:transparent;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.dsh-st-n-search-clear:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}@media (prefers-reduced-motion:reduce){.dsh-st-n-head-label,.dsh-st-n-search-slot,.dsh-st-n-search,.dsh-st-n-head-acts,.dsh-st-n-search-input{transition:none}}.dsh-st-n-filter-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:196px;padding:8px 6px;border:1px solid var(--dsw-alias-border-inverted,rgba(255,255,255,.12));border-radius:12px;background:var(--dsw-specific-menu,#1c2128);box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.36))}.dsh-st-n-filter-label{padding:6px 10px 4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-filter-split{height:1px;margin:6px 8px;background:var(--dsw-alias-border-l2,rgba(255,255,255,.1))}.dsh-st-n-filter-menu button{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:36px;padding:6px 10px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary,inherit);font:14px/20px inherit;cursor:pointer;text-align:left}.dsh-st-n-filter-menu button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-filter-tick{width:16px;height:16px;flex:none}.dsh-st-n-tree{flex:1;min-width:0;max-width:100%;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0 0 16px;user-select:none}.dsh-st-n-empty{padding:14px 8px;color:var(--dsw-alias-label-tertiary,#8b8f98);font-size:12px}.dsh-st-n-group{min-width:0;max-width:100%}.dsh-st-n-row,.dsh-st-n-sess{display:flex;align-items:center;max-width:100%;border-radius:8px;padding:0 8px 0 12px;cursor:pointer;border:0;background:transparent;color:var(--dsw-alias-label-primary,inherit);text-align:left;font:14px/20px inherit}.dsh-st-n-row{height:34px;gap:6px}.dsh-st-n-sess{height:32px;gap:0;position:relative;width:100%;appearance:none}.dsh-st-n-row:hover,.dsh-st-n-sess:hover,.dsh-st-n-sess.is-on,.dsh-st-n-sess.is-menu{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-slot{flex:none;width:16px;height:20px;display:inline-flex;align-items:center;justify-content:center}.dsh-st-n-folder{color:var(--dsw-alias-label-secondary,#9ca39f)}.dsh-st-n-lead{color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-corner{color:var(--dsw-alias-label-caption,#ADB2B8);width:8px}.dsh-st-n-title{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px}.dsh-st-n-sess .dsh-st-n-title{margin:0 6px 0 4px}.dsh-st-n-time{flex:none;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary,#81858C);white-space:nowrap}.dsh-st-n-acts{flex:none;display:none;align-items:center;gap:12px}.dsh-st-n-row:hover .dsh-st-n-acts,.dsh-st-n-sess:hover .dsh-st-n-acts,.dsh-st-n-row.is-menu .dsh-st-n-acts,.dsh-st-n-sess.is-menu .dsh-st-n-acts{display:inline-flex}.dsh-st-n-sess:hover .dsh-st-n-time,.dsh-st-n-sess.is-menu .dsh-st-n-time{display:none}.dsh-st-n-ico{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#81858C);padding:0;cursor:pointer}.dsh-st-n-ico:hover{color:var(--dsw-alias-label-primary,inherit)}.dsh-st-n-menu.is-float{position:fixed;z-index:4000;right:auto;top:auto}.dsh-st-n-menu{position:absolute;right:8px;top:calc(100% + 4px);z-index:1100;min-width:218px;max-width:360px;box-sizing:border-box;padding:4px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-inverted,rgba(255,255,255,.12));border-radius:12px;background:var(--dsw-specific-menu,#1c2128);box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.36))}.dsh-st-n-menu button{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:0;border-radius:10px;background:transparent;cursor:pointer;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary,inherit);text-align:left}.dsh-st-n-menu button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-mi{display:inline-flex;flex:none;width:16px;height:16px;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-menu button.danger{color:var(--dsw-alias-state-error-primary,#f85149)}.dsh-st-n-menu button.danger .dsh-st-n-mi{color:inherit}.dsh-st-n-menu button.danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(248,81,73,.12))}.dsh-st-n-hover{position:fixed;z-index:4100;min-width:188px;max-width:280px;padding:12px 14px;border:1px solid var(--dsw-alias-border-inverted,rgba(255,255,255,.12));border-radius:12px;background:var(--dsw-specific-menu,#1c2128);box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.36));color:var(--dsw-alias-label-primary,inherit)}.dsh-st-n-hover-title{font-size:14px;line-height:20px;font-weight:500}.dsh-st-n-hover-time{margin-top:4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-hover-state{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#9ca39f)}.dsh-st-n-hover-dot{width:8px;height:8px;border-radius:50%;background:#34c759;flex:none}.dsh-st-n-hover-dot.is-run{background:#4c8dff}.dsh-st-n-rename{flex:1;min-width:0;margin:0 6px 0 4px;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:var(--dsw-alias-button-elevated-fill,rgba(255,255,255,.04));color:inherit;font:inherit;padding:0 2px}
2560
2907
  `;
2561
2908
  function installStyles() {
2562
2909
  const existing = document.getElementById(STYLE_ID);
@@ -2610,7 +2957,8 @@ function apply(ctx) {
2610
2957
  label: () => t("tab"),
2611
2958
  icon: "schedule"
2612
2959
  }, function ScheduledTasksSettings(props) {
2613
- return (0, import_react6.createElement)(AutomationView, { t, runtime, ...props.close === void 0 ? {} : { closeSettings: props.close } });
2960
+ const workspacesApi = readOptionalWorkspaces(ctx);
2961
+ return (0, import_react7.createElement)(AutomationView, { t, runtime, ...props.close === void 0 ? {} : { closeSettings: props.close }, ...workspacesApi === void 0 ? {} : { pickWorkspaceDirectory: () => workspacesApi.pickDirectory() } });
2614
2962
  }));
2615
2963
  ctx.slots.inject("sidebar.schedule", () => ctx.slots.register({
2616
2964
  name: "sidebar.schedule",
@@ -2619,27 +2967,30 @@ function apply(ctx) {
2619
2967
  locale: NS,
2620
2968
  label: () => t("sidebar.tab")
2621
2969
  }, function AutomationScheduleRail(props) {
2622
- return (0, import_react6.createElement)(ScheduleRail, {
2970
+ return (0, import_react7.createElement)(ScheduleRail, {
2623
2971
  t,
2624
2972
  runtime,
2625
- openSession: (id) => {
2626
- openScheduledSession(id, (sessionId) => {
2627
- ctx.sessions?.open(sessionId);
2628
- }, props.openSession);
2629
- }
2973
+ openSession: createScheduledSessionOpener(ctx, runtime, props.openSession)
2630
2974
  });
2631
2975
  }));
2632
2976
  ctx.slots.inject("sidebar.workspaces", () => {
2633
2977
  let wrappedEntry;
2634
2978
  let originalComp;
2635
2979
  let removeInsertedTab = () => void 0;
2636
- let removeFilter = () => void 0;
2980
+ let wrapped = false;
2637
2981
  let syncing = false;
2982
+ let retryTimer;
2983
+ const listenChannels = (listener) => ctx.slots.subscribe?.("sidebar.channels", listener) ?? (() => void 0);
2984
+ const stopRetry = () => {
2985
+ if (retryTimer !== void 0) {
2986
+ window.clearInterval(retryTimer);
2987
+ retryTimer = void 0;
2988
+ }
2989
+ };
2638
2990
  const unwrap = () => {
2991
+ stopRetry();
2639
2992
  removeInsertedTab();
2640
2993
  removeInsertedTab = () => void 0;
2641
- removeFilter();
2642
- removeFilter = () => void 0;
2643
2994
  if (wrappedEntry !== void 0 && originalComp !== void 0) {
2644
2995
  try {
2645
2996
  wrappedEntry.component = originalComp;
@@ -2648,12 +2999,12 @@ function apply(ctx) {
2648
2999
  }
2649
3000
  wrappedEntry = void 0;
2650
3001
  originalComp = void 0;
3002
+ wrapped = false;
2651
3003
  };
2652
3004
  const insertScheduleTab = (entry, openSession) => {
2653
3005
  const registry = findNativeTabRegistry(entry);
2654
3006
  if (registry === void 0) return false;
2655
3007
  if (registry.getTabs().some((item) => item.id === "schedule")) return true;
2656
- removeInsertedTab();
2657
3008
  removeInsertedTab = registry.insert({
2658
3009
  id: "schedule",
2659
3010
  label: t("sidebar.tab"),
@@ -2661,12 +3012,8 @@ function apply(ctx) {
2661
3012
  matchSession: (sessionId) => sessionId.startsWith(AUTOMATION_SESSION_PREFIX),
2662
3013
  render: (props) => {
2663
3014
  const hostOpen = typeof props.openSession === "function" ? props.openSession : typeof props.open === "function" ? props.open : openSession;
2664
- const opener = (id) => {
2665
- openScheduledSession(id, (sessionId) => {
2666
- ctx.sessions?.open(sessionId);
2667
- }, hostOpen);
2668
- };
2669
- return (0, import_react6.createElement)(NativeScheduleSessionList, {
3015
+ const opener = createScheduledSessionOpener(ctx, runtime, hostOpen);
3016
+ return (0, import_react7.createElement)(NativeScheduleSessionList, {
2670
3017
  t,
2671
3018
  runtime,
2672
3019
  openSession: opener,
@@ -2679,21 +3026,35 @@ function apply(ctx) {
2679
3026
  });
2680
3027
  }
2681
3028
  });
2682
- removeFilter();
2683
- removeFilter = registry.addSessionFilter((id) => !id.startsWith(AUTOMATION_SESSION_PREFIX));
2684
3029
  return true;
2685
3030
  };
3031
+ const insertIntoKnownHosts = () => {
3032
+ const entries = readSlotEntries(ctx, "sidebar.workspaces");
3033
+ for (const entry of entries) {
3034
+ const record = entry;
3035
+ if (insertScheduleTab(entry) || insertScheduleTab(record.component)) return true;
3036
+ }
3037
+ return wrappedEntry !== void 0 && insertScheduleTab(wrappedEntry);
3038
+ };
3039
+ const ensureScheduleTab = () => {
3040
+ if (insertIntoKnownHosts()) {
3041
+ stopRetry();
3042
+ return;
3043
+ }
3044
+ if (retryTimer !== void 0) return;
3045
+ let tries = 0;
3046
+ retryTimer = window.setInterval(() => {
3047
+ tries += 1;
3048
+ if (insertIntoKnownHosts() || tries >= 20) stopRetry();
3049
+ }, 250);
3050
+ };
2686
3051
  const sync = () => {
2687
3052
  if (syncing) return;
2688
3053
  syncing = true;
2689
3054
  try {
2690
3055
  let AutomationNativeWorkspaceShell2 = function(innerProps) {
2691
- const openSession = (id) => {
2692
- openScheduledSession(id, (sessionId) => {
2693
- ctx.sessions?.open(sessionId);
2694
- }, innerProps.openSession ?? innerProps.open);
2695
- };
2696
- return (0, import_react6.createElement)(NativeScheduleShell, {
3056
+ const openSession = createScheduledSessionOpener(ctx, runtime, innerProps.openSession ?? innerProps.open);
3057
+ return (0, import_react7.createElement)(NativeScheduleShell, {
2697
3058
  t,
2698
3059
  runtime,
2699
3060
  hostProps: innerProps,
@@ -2701,7 +3062,7 @@ function apply(ctx) {
2701
3062
  tabRegistry: registry,
2702
3063
  ...innerProps.wide === void 0 ? {} : { wide: innerProps.wide },
2703
3064
  hasChannels: () => slotHasEntries(ctx, "sidebar.channels"),
2704
- subscribeChannels: (listener) => ctx.slots.subscribe?.("sidebar.channels", listener) ?? (() => void 0),
3065
+ subscribeChannels: listenChannels,
2705
3066
  ...innerProps.useSessions === void 0 ? {} : { useSessions: innerProps.useSessions },
2706
3067
  ...innerProps.useWorkspaces === void 0 ? {} : { useWorkspaces: innerProps.useWorkspaces },
2707
3068
  ...innerProps.renderSlot === void 0 ? {} : { renderSlot: innerProps.renderSlot },
@@ -2717,14 +3078,14 @@ function apply(ctx) {
2717
3078
  const occupant = pickWrappableWorkspacesEntry(entries);
2718
3079
  const current = occupant?.component;
2719
3080
  if (current !== void 0 && isForeignSidebarHost(current)) {
2720
- if (insertScheduleTab(occupant) || insertScheduleTab(current)) return;
3081
+ ensureScheduleTab();
2721
3082
  return;
2722
3083
  }
2723
3084
  if (wrappedEntry?.component !== void 0 && wrappedEntry.component.__dshAutomationWrapped === true) {
2724
- insertScheduleTab(wrappedEntry);
3085
+ ensureScheduleTab();
2725
3086
  return;
2726
3087
  }
2727
- if (occupant?.component === void 0) return;
3088
+ if (occupant?.component === void 0 || wrapped) return;
2728
3089
  const resolved = resolveOfficialTreeComponent(occupant.component);
2729
3090
  if (resolved === void 0) return;
2730
3091
  originalComp = resolved;
@@ -2738,9 +3099,8 @@ function apply(ctx) {
2738
3099
  attachNativeTabRegistry(marked, registry);
2739
3100
  occupant.component = marked;
2740
3101
  wrappedEntry = occupant;
2741
- insertScheduleTab(occupant, (id) => {
2742
- ctx.sessions?.open(id);
2743
- });
3102
+ wrapped = true;
3103
+ ensureScheduleTab();
2744
3104
  } catch (error) {
2745
3105
  console.warn("[dsh-automation] \u5305\u88F9\u5B98\u65B9\u4EFB\u52A1\u6811\u5931\u8D25", error);
2746
3106
  } finally {
@@ -2749,6 +3109,7 @@ function apply(ctx) {
2749
3109
  };
2750
3110
  sync();
2751
3111
  const unsub = typeof ctx.slots.subscribe === "function" ? ctx.slots.subscribe("sidebar.workspaces", sync) : () => void 0;
3112
+ ensureScheduleTab();
2752
3113
  return () => {
2753
3114
  unsub();
2754
3115
  unwrap();
@@ -2761,8 +3122,25 @@ function apply(ctx) {
2761
3122
  locale: NS
2762
3123
  }, PrefillBridge));
2763
3124
  }
3125
+ function createScheduledSessionOpener(ctx, runtime, hostOpen) {
3126
+ return (sessionId) => {
3127
+ void ensureOpenScheduledSession({
3128
+ id: sessionId,
3129
+ adopt: (id) => runtime.adoptSession(id),
3130
+ listed: (id) => {
3131
+ const snap = ctx.sessions?.list?.getSnapshot();
3132
+ return snap?.byId?.[id] !== void 0 || (snap?.ids ?? []).some((item) => item === id);
3133
+ },
3134
+ ...ctx.sessions?.refresh === void 0 ? {} : { refresh: () => ctx.sessions.refresh() },
3135
+ ...ctx.sessions === void 0 ? {} : { openRuntime: (id) => {
3136
+ ctx.sessions.open(id);
3137
+ } },
3138
+ ...hostOpen === void 0 ? {} : { openHost: hostOpen }
3139
+ });
3140
+ };
3141
+ }
2764
3142
  function PrefillBridge(props) {
2765
- (0, import_react6.useEffect)(() => {
3143
+ (0, import_react7.useEffect)(() => {
2766
3144
  const applyPrefill = (text) => {
2767
3145
  if (text === null || text === "") return;
2768
3146
  if (props.inputActions !== void 0) {
@@ -2788,5 +3166,12 @@ function readSlotEntries(ctx, name2) {
2788
3166
  function slotHasEntries(ctx, name2) {
2789
3167
  return readSlotEntries(ctx, name2).length > 0;
2790
3168
  }
3169
+ function readOptionalWorkspaces(ctx) {
3170
+ try {
3171
+ return ctx.workspaces;
3172
+ } catch {
3173
+ return void 0;
3174
+ }
3175
+ }
2791
3176
  return module.exports; } });
2792
3177
  //# sourceMappingURL=client.js.map