@michengai/dsh-automation 0.1.2 → 0.1.4

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.
Files changed (29) hide show
  1. package/README.md +54 -31
  2. package/README.zh-CN.md +50 -27
  3. package/assets/branding/dsh-banner.png +0 -0
  4. package/assets/screenshots/chat-approval.png +0 -0
  5. package/assets/screenshots/chat-create.png +0 -0
  6. package/assets/screenshots/chat-created.png +0 -0
  7. package/assets/screenshots/settings-runs.png +0 -0
  8. package/assets/screenshots/settings-tasks.png +0 -0
  9. package/assets/screenshots/workspace-scheduled.png +0 -0
  10. package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/02-/345/275/223/345/211/215/347/212/266/346/200/201.md +2 -3
  11. package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/03-/345/276/205/345/212/236/344/270/216/351/230/273/345/241/236.md +2 -2
  12. package/docs/01-/345/275/223/345/211/215/345/267/245/344/275/234/I001-/344/273/273/345/212/241/350/207/252/345/212/250/345/214/226/346/217/222/344/273/266/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +10 -4
  13. package/lib/client.js +746 -424
  14. package/lib/client.js.map +4 -4
  15. package/lib/index.js +46 -5
  16. package/lib/types/client/AutomationView.d.ts +1 -1
  17. package/lib/types/client/ScheduleRail.d.ts +2 -2
  18. package/lib/types/client/contracts.d.ts +12 -0
  19. package/lib/types/client/create-modal-logic.d.ts +7 -0
  20. package/lib/types/client/create-modal.d.ts +2 -1
  21. package/lib/types/client/icons.d.ts +6 -0
  22. package/lib/types/client/locales.d.ts +18 -2
  23. package/lib/types/client/native-session-list.d.ts +1 -1
  24. package/lib/types/client/native-session-menu.d.ts +43 -4
  25. package/lib/types/client/schedule-rail-model.d.ts +46 -7
  26. package/lib/types/client/workspace-toolbar.d.ts +13 -0
  27. package/lib/types/index.d.ts +18 -1
  28. package/lib/types/prompt.d.ts +6 -0
  29. package/package.json +2 -6
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,7 @@ 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");
1876
1904
  var import_react_dom2 = require("react-dom");
1877
1905
 
1878
1906
  // src/client/native-session-menu.ts
@@ -1894,15 +1922,45 @@ function shouldCloseNativeSessionMenu(target, keepInside) {
1894
1922
  return typeof box.contains === "function" && box.contains(el);
1895
1923
  });
1896
1924
  }
1897
- function nextOpenSessionMenuId(current, clicked) {
1898
- return current === clicked ? null : clicked;
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 };
1899
1945
  }
1900
- function nativeSessionMenuStyle(box, viewportWidth) {
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);
1901
1948
  return {
1902
1949
  position: "fixed",
1903
- zIndex: 1200,
1904
- top: `${Math.round(box.bottom + 4)}px`,
1905
- right: `${Math.max(8, Math.round(viewportWidth - box.right))}px`
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`
1906
1964
  };
1907
1965
  }
1908
1966
  function relativeTime(value) {
@@ -1911,10 +1969,10 @@ function relativeTime(value) {
1911
1969
  const delta = Math.max(0, Date.now() - ts);
1912
1970
  const min = Math.floor(delta / 6e4);
1913
1971
  if (min < 1) return "\u521A\u521A";
1914
- if (min < 60) return String(min) + "\u5206\u949F";
1972
+ if (min < 60) return String(min) + "\u5206\u949F\u524D";
1915
1973
  const hour = Math.floor(min / 60);
1916
- if (hour < 24) return String(hour) + "\u5C0F\u65F6";
1917
- return String(Math.floor(hour / 24)) + "\u5929";
1974
+ if (hour < 24) return String(hour) + "\u5C0F\u65F6\u524D";
1975
+ return String(Math.floor(hour / 24)) + "\u5929\u524D";
1918
1976
  }
1919
1977
 
1920
1978
  // src/run-title.ts
@@ -1963,21 +2021,35 @@ function groupScheduledSessions(automations, runs) {
1963
2021
  };
1964
2022
  }).filter((group) => group.sessions.length > 0);
1965
2023
  }
1966
- 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()) {
1967
2039
  if (item === void 0 || item.blank === true) return false;
1968
2040
  if (item.origin === "im" || item.origin === "subagent") return false;
1969
2041
  const id = item.id ?? "";
1970
2042
  if (id.startsWith("im:")) return false;
1971
- if (id.startsWith(AUTOMATION_SESSION_PREFIX)) return false;
2043
+ if (isAutomationSidebarSession(id, item, scheduledIds)) return false;
1972
2044
  return true;
1973
2045
  }
1974
- function groupNativeTaskSessions(sessions, workspaces, ungroupedLabel) {
2046
+ function groupNativeTaskSessions(sessions, workspaces, ungroupedLabel, scheduledIds = /* @__PURE__ */ new Set()) {
1975
2047
  const byId = sessions.byId ?? {};
1976
2048
  const archived = new Set(workspaces?.archivedSessionIds ?? []);
1977
2049
  const assigned = /* @__PURE__ */ new Set();
1978
2050
  const groups = [];
1979
2051
  for (const workspace of workspaces?.items ?? []) {
1980
- 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));
1981
2053
  for (const item of items) {
1982
2054
  if (item.id !== void 0) assigned.add(item.id);
1983
2055
  }
@@ -1989,7 +2061,7 @@ function groupNativeTaskSessions(sessions, workspaces, ungroupedLabel) {
1989
2061
  });
1990
2062
  }
1991
2063
  }
1992
- 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));
1993
2065
  if (ungrouped.length > 0) groups.push({ id: "", label: ungroupedLabel, sessions: ungrouped });
1994
2066
  return groups;
1995
2067
  }
@@ -1997,9 +2069,29 @@ function readNativeSidebarTab(raw) {
1997
2069
  if (raw === "channels" || raw === "schedule" || raw === "tasks") return raw;
1998
2070
  return "tasks";
1999
2071
  }
2000
- 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) {
2001
2093
  if (sessionId === void 0 || sessionId === null || sessionId === "") return void 0;
2002
- if (sessionId.startsWith(AUTOMATION_SESSION_PREFIX)) return "schedule";
2094
+ if (scheduledIds !== void 0 ? scheduledIds.has(sessionId) : sessionId.startsWith(AUTOMATION_SESSION_PREFIX)) return "schedule";
2003
2095
  if (sessionId.startsWith("im:")) return "channels";
2004
2096
  return void 0;
2005
2097
  }
@@ -2015,41 +2107,77 @@ function slotOccupantName(item) {
2015
2107
  function hasCodexUiSidebar(entries) {
2016
2108
  return (entries ?? []).some((item) => occupantLooksLikeCodexUi(slotOccupantName(item)));
2017
2109
  }
2018
- 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()) {
2019
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
+ }
2020
2123
  const ids = (src.ids ?? []).filter((id) => {
2021
2124
  const value = String(id);
2022
- return !value.startsWith(AUTOMATION_SESSION_PREFIX) && !value.startsWith("im:");
2125
+ return !value.startsWith("im:") && !isAutomationSidebarSession(value, src.byId?.[value], scheduledIds);
2023
2126
  });
2024
- const byId = {};
2025
- for (const id of ids) {
2026
- const item = src.byId?.[id];
2027
- if (item !== void 0) byId[id] = item;
2028
- }
2029
- 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;
2030
2131
  }
2031
2132
  function openScheduledSession(id, openRuntime, openHost) {
2032
- if (id === "") return;
2133
+ if (id === "") return false;
2033
2134
  const attempts = id.startsWith(AUTOMATION_SESSION_PREFIX) || id.startsWith("im:") ? [openRuntime, openHost] : [openHost, openRuntime];
2034
2135
  for (const attempt of attempts) {
2035
2136
  if (typeof attempt !== "function") continue;
2036
2137
  try {
2037
2138
  attempt(id);
2038
- return;
2139
+ return true;
2039
2140
  } catch {
2040
2141
  }
2041
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);
2042
2158
  }
2043
- function isHiddenSidebarSessionId(id) {
2044
- 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);
2045
2161
  }
2046
- function filterWorkspaceListState(state) {
2162
+ function filterWorkspaceListState(state, scheduledIds = /* @__PURE__ */ new Set()) {
2047
2163
  const src = state ?? { items: [], archivedSessionIds: [] };
2048
- const items = (src.items ?? []).map((workspace) => ({
2049
- ...workspace,
2050
- sessionIds: (workspace.sessionIds ?? []).filter((sid) => !isHiddenSidebarSessionId(String(sid)))
2051
- }));
2052
- 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;
2053
2181
  }
2054
2182
  function wrapperFlags(component) {
2055
2183
  if (component === void 0 || component === null || typeof component !== "function" && typeof component !== "object") return {};
@@ -2089,19 +2217,140 @@ function pickWrappableWorkspacesEntry(entries) {
2089
2217
  }
2090
2218
  return void 0;
2091
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
+ }
2092
2248
 
2093
- // src/client/native-session-list.tsx
2249
+ // src/client/workspace-toolbar.tsx
2250
+ var import_react4 = require("react");
2094
2251
  var import_jsx_runtime5 = require("react/jsx-runtime");
2252
+ function officialSearchIconSize(expanded) {
2253
+ return expanded ? 11 : 14;
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");
2095
2341
  var EMPTY_SESSION_BY_ID = {};
2096
2342
  function NativeScheduleSessionList(props) {
2097
- const { t, runtime, openSession, useSessions, useWorkspaces, renameSession, archiveSession, deleteSession, forkSession } = props;
2098
- 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);
2099
2345
  const selectedId = useSessions ? useSessions((snap) => snap?.current ?? null) : null;
2100
2346
  const sessionById = useSessions ? useSessions((snap) => snap?.byId ?? EMPTY_SESSION_BY_ID) : EMPTY_SESSION_BY_ID;
2101
2347
  const archivedIds = useWorkspaces ? useWorkspaces((snap) => snap?.archivedSessionIds ?? []) : [];
2102
- const [folded, setFolded] = (0, import_react4.useState)({});
2103
- const [openMenuId, setOpenMenuId] = (0, import_react4.useState)(null);
2104
- (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)(() => {
2105
2354
  void runtime.refresh().catch(() => void 0);
2106
2355
  const timer = window.setInterval(() => {
2107
2356
  void runtime.refresh().catch(() => void 0);
@@ -2110,8 +2359,8 @@ function NativeScheduleSessionList(props) {
2110
2359
  window.clearInterval(timer);
2111
2360
  };
2112
2361
  }, [runtime]);
2113
- const archived = (0, import_react4.useMemo)(() => new Set(archivedIds), [archivedIds]);
2114
- const groups = (0, import_react4.useMemo)(() => {
2362
+ const archived = (0, import_react5.useMemo)(() => new Set(archivedIds), [archivedIds]);
2363
+ const groups = (0, import_react5.useMemo)(() => {
2115
2364
  const snapshot = state.snapshot;
2116
2365
  if (snapshot === void 0) return [];
2117
2366
  return groupScheduledSessions(snapshot.automations, snapshot.runs).map((group) => ({
@@ -2127,84 +2376,63 @@ function NativeScheduleSessionList(props) {
2127
2376
  })
2128
2377
  })).filter((group) => group.sessions.length > 0);
2129
2378
  }, [archived, sessionById, state.snapshot]);
2130
- 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: [
2131
- state.phase === "loading" && groups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-empty", children: t("loading") }),
2132
- groups.length === 0 && state.phase !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-empty", children: t("sidebar.empty") }),
2133
- groups.map((group) => {
2134
- const expanded = folded[group.id] !== true;
2135
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-st-n-group", children: [
2136
- /* @__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: [
2137
- /* @__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 }) }),
2138
- /* @__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 }) }) }),
2139
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-title", children: group.name }),
2140
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-acts", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2141
- "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,
2142
2394
  {
2143
- type: "button",
2144
- className: "dsh-st-n-ico",
2145
- "aria-label": t("session.deleteFolder"),
2146
- onClick: (event) => {
2147
- event.stopPropagation();
2148
- setOpenMenuId(null);
2149
- void (async () => {
2150
- for (const session of group.sessions) {
2151
- try {
2152
- await deleteSession?.(session.id);
2153
- } catch {
2154
- }
2155
- }
2156
- await runtime.forgetAutomationSessions(group.id);
2157
- })();
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);
2158
2409
  },
2159
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TrashIcon, { width: 16, height: 16 })
2160
- }
2161
- ) })
2162
- ] }),
2163
- expanded && group.sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2164
- NativeSessionRow,
2165
- {
2166
- t,
2167
- id: session.id,
2168
- title: session.title,
2169
- updatedAt: session.updatedAt,
2170
- running: session.running,
2171
- selected: selectedId === session.id,
2172
- menuOpen: openMenuId === session.id,
2173
- onToggleMenu: () => setOpenMenuId((current) => nextOpenSessionMenuId(current, session.id)),
2174
- onCloseMenu: () => setOpenMenuId((current) => current === session.id ? null : current),
2175
- onOpen: () => {
2176
- setOpenMenuId(null);
2177
- openSession?.(session.id);
2178
- void runtime.adoptSession?.(session.id).catch(() => void 0);
2179
- },
2180
- onDelete: async () => {
2181
- try {
2182
- await deleteSession?.(session.id);
2183
- } catch {
2184
- }
2185
- await runtime.forgetSession(session.id);
2410
+ ...renameSession === void 0 ? {} : { renameSession },
2411
+ ...archiveSession === void 0 ? {} : { archiveSession },
2412
+ ...forkSession === void 0 ? {} : { forkSession }
2186
2413
  },
2187
- ...renameSession === void 0 ? {} : { renameSession },
2188
- ...archiveSession === void 0 ? {} : { archiveSession },
2189
- ...forkSession === void 0 ? {} : { forkSession }
2190
- },
2191
- session.id
2192
- ))
2193
- ] }, group.id);
2194
- })
2195
- ] }) });
2414
+ session.id
2415
+ ))
2416
+ ] }, group.id);
2417
+ })
2418
+ ] })
2419
+ ] });
2196
2420
  }
2197
2421
  function NativeSessionRow(props) {
2198
- const { t, id, title, updatedAt, running, selected, menuOpen, onToggleMenu, onCloseMenu, onOpen, onDelete, renameSession, archiveSession, forkSession } = props;
2199
- const rowRef = (0, import_react4.useRef)(null);
2200
- const menuRef = (0, import_react4.useRef)(null);
2201
- const [renaming, setRenaming] = (0, import_react4.useState)(false);
2202
- const [draft, setDraft] = (0, import_react4.useState)(title);
2203
- const [menuStyle, setMenuStyle] = (0, import_react4.useState)({});
2204
- (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)(() => {
2205
2433
  setDraft(title);
2206
2434
  }, [title]);
2207
- (0, import_react4.useEffect)(() => {
2435
+ (0, import_react5.useEffect)(() => {
2208
2436
  if (!menuOpen) return;
2209
2437
  const close = (event) => {
2210
2438
  if (!shouldCloseNativeSessionMenu(event.target, [rowRef.current, menuRef.current])) return;
@@ -2220,12 +2448,12 @@ function NativeSessionRow(props) {
2220
2448
  document.removeEventListener("keydown", onKey);
2221
2449
  };
2222
2450
  }, [menuOpen, onCloseMenu]);
2223
- (0, import_react4.useLayoutEffect)(() => {
2451
+ (0, import_react5.useLayoutEffect)(() => {
2224
2452
  if (!menuOpen) return;
2225
2453
  const update = () => {
2226
- const box = rowRef.current?.getBoundingClientRect();
2227
- if (box === void 0) return;
2228
- setMenuStyle(nativeSessionMenuStyle(box, window.innerWidth));
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 }));
2229
2457
  };
2230
2458
  update();
2231
2459
  window.addEventListener("resize", update);
@@ -2234,13 +2462,42 @@ function NativeSessionRow(props) {
2234
2462
  window.removeEventListener("resize", update);
2235
2463
  document.removeEventListener("scroll", update, true);
2236
2464
  };
2237
- }, [menuOpen]);
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
+ }, []);
2238
2486
  const run = (action) => {
2239
2487
  void Promise.resolve(action()).catch(() => void 0);
2240
2488
  };
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
+ };
2241
2498
  const rowClass = "dsh-st-n-sess" + (selected ? " is-on" : "") + (menuOpen ? " is-menu" : "");
2242
2499
  if (renaming) {
2243
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: rowClass, ref: rowRef, 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) => {
2244
2501
  if (event.key === "Enter") {
2245
2502
  event.preventDefault();
2246
2503
  setRenaming(false);
@@ -2257,24 +2514,23 @@ function NativeSessionRow(props) {
2257
2514
  } }) });
2258
2515
  }
2259
2516
  const menuItems = [
2260
- { 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: () => {
2261
2518
  setRenaming(true);
2262
2519
  } },
2263
- { id: "fork", label: t("session.fork"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BranchIcon, { width: 16, height: 16 }), go: () => run(() => forkSession?.(id)) },
2264
- { id: "archive", label: t("session.archive"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArchiveIcon, { width: 16, height: 16 }), go: () => run(() => archiveSession?.(id)) },
2265
- { 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)) }
2266
2522
  ];
2267
2523
  const menu = menuOpen && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2268
- /* @__PURE__ */ (0, import_jsx_runtime5.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_runtime5.jsxs)("button", { type: "button", className: item.danger === true ? "danger" : void 0, onClick: () => {
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: () => {
2269
2525
  onCloseMenu();
2270
2526
  item.go();
2271
2527
  }, children: [
2272
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-mi", children: item.icon }),
2528
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-n-mi", children: item.icon }),
2273
2529
  item.label
2274
2530
  ] }, item.id)) }),
2275
2531
  document.body
2276
2532
  ) : null;
2277
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: rowClass, ref: rowRef, role: "treeitem", tabIndex: 0, "aria-selected": selected, "data-n-menu-root": id, onClick: onOpen, onKeyDown: (event) => {
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) => {
2278
2534
  if (event.key === "Enter" || event.key === " ") {
2279
2535
  event.preventDefault();
2280
2536
  onOpen();
@@ -2282,24 +2538,37 @@ function NativeSessionRow(props) {
2282
2538
  }, onContextMenu: (event) => {
2283
2539
  event.preventDefault();
2284
2540
  event.stopPropagation();
2285
- onToggleMenu();
2541
+ onToggleMenu(event);
2286
2542
  }, children: [
2287
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-slot", children: running ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RunningStateDot, {}) : null }),
2288
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-title", children: title }),
2289
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-time", children: relativeTime(updatedAt) }),
2290
- /* @__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", onMouseDown: (event) => event.stopPropagation(), 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) => {
2291
2547
  event.stopPropagation();
2292
- onToggleMenu();
2293
- }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EllipsisIcon, { width: 16, height: 16 }) }) }),
2294
- menu
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
+ )
2295
2563
  ] });
2296
2564
  }
2297
2565
 
2298
2566
  // src/client/ScheduleRail.tsx
2299
- var import_react5 = require("react");
2300
- var import_jsx_runtime6 = require("react/jsx-runtime");
2567
+ var import_react6 = require("react");
2568
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2301
2569
  var EMPTY_EXTRA_TABS = [];
2302
- var OfficialTreeGuard = class extends import_react5.Component {
2570
+ var noopSubscribe = (_listener) => () => void 0;
2571
+ var OfficialTreeGuard = class extends import_react6.Component {
2303
2572
  state = { failed: false };
2304
2573
  static getDerivedStateFromError() {
2305
2574
  return { failed: true };
@@ -2316,9 +2585,9 @@ function ScheduleRail({
2316
2585
  runtime,
2317
2586
  openSession
2318
2587
  }) {
2319
- const state = (0, import_react5.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
2320
- const [folded, setFolded] = (0, import_react5.useState)({});
2321
- (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)(() => {
2322
2591
  void runtime.refresh().catch(() => void 0);
2323
2592
  const timer = window.setInterval(() => {
2324
2593
  void runtime.refresh().catch(() => void 0);
@@ -2327,36 +2596,36 @@ function ScheduleRail({
2327
2596
  window.clearInterval(timer);
2328
2597
  };
2329
2598
  }, [runtime]);
2330
- const groups = (0, import_react5.useMemo)(() => {
2599
+ const groups = (0, import_react6.useMemo)(() => {
2331
2600
  const snapshot = state.snapshot;
2332
2601
  if (snapshot === void 0) return [];
2333
2602
  return groupScheduledSessions(snapshot.automations, snapshot.runs);
2334
2603
  }, [state.snapshot]);
2335
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-rail", children: [
2336
- state.phase === "loading" && groups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-empty", children: t("loading") }),
2337
- groups.length === 0 && state.phase !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-empty", children: t("sidebar.empty") }),
2338
- groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "dsh-st-rail-group", children: [
2339
- /* @__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)(
2340
2609
  "button",
2341
2610
  {
2342
2611
  type: "button",
2343
2612
  className: "dsh-st-rail-head",
2344
2613
  onClick: () => setFolded((current) => ({ ...current, [group.id]: !current[group.id] })),
2345
2614
  children: [
2346
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-st-rail-folder", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ClockIcon, { width: 16, height: 16 }) }),
2347
- /* @__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 })
2348
2617
  ]
2349
2618
  }
2350
2619
  ),
2351
- 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)(
2352
2621
  "button",
2353
2622
  {
2354
2623
  type: "button",
2355
2624
  className: "dsh-st-rail-session",
2356
2625
  onClick: () => openSession?.(session.id),
2357
2626
  children: [
2358
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: session.label }),
2359
- 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, {})
2360
2629
  ]
2361
2630
  },
2362
2631
  session.id
@@ -2379,26 +2648,20 @@ function NativeScheduleShell({
2379
2648
  wide
2380
2649
  }) {
2381
2650
  const Official = officialTree;
2382
- const [tab, setTab] = (0, import_react5.useState)(() => {
2651
+ const [tab, setTab] = (0, import_react6.useState)(() => {
2383
2652
  try {
2384
2653
  return readNativeSidebarTab(window.localStorage.getItem(NATIVE_SIDEBAR_TAB_KEY));
2385
2654
  } catch {
2386
2655
  return "tasks";
2387
2656
  }
2388
2657
  });
2389
- const extraTabs = (0, import_react5.useSyncExternalStore)(
2390
- tabRegistry?.subscribe ?? ((listener) => {
2391
- listener();
2392
- return () => void 0;
2393
- }),
2658
+ const extraTabs = (0, import_react6.useSyncExternalStore)(
2659
+ tabRegistry?.subscribe ?? noopSubscribe,
2394
2660
  () => tabRegistry?.getTabs() ?? EMPTY_EXTRA_TABS,
2395
2661
  () => EMPTY_EXTRA_TABS
2396
2662
  );
2397
- const channelsReady = (0, import_react5.useSyncExternalStore)(
2398
- subscribeChannels ?? ((listener) => {
2399
- listener();
2400
- return () => void 0;
2401
- }),
2663
+ const channelsReady = (0, import_react6.useSyncExternalStore)(
2664
+ subscribeChannels ?? noopSubscribe,
2402
2665
  () => {
2403
2666
  try {
2404
2667
  return hasChannels?.() === true;
@@ -2409,26 +2672,39 @@ function NativeScheduleShell({
2409
2672
  () => false
2410
2673
  );
2411
2674
  const currentId = useSessions?.((state) => state?.current ?? null);
2412
- 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) => {
2413
2678
  if (useSessions === void 0) return selector({ ids: [], byId: {}, current: null });
2414
- return useSessions((state) => selector(filterTaskSessionState(state)));
2415
- }, [useSessions]);
2416
- 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) => {
2417
2682
  if (useWorkspaces === void 0) return selector({ items: [], archivedSessionIds: [] });
2418
- return useWorkspaces((state) => selector(filterWorkspaceListState(state)));
2419
- }, [useWorkspaces]);
2420
- (0, import_react5.useEffect)(() => {
2683
+ return useWorkspaces((state) => selector(filterWorkspaceListState(state, scheduledIds)), eq);
2684
+ }, [useWorkspaces, scheduledIds]);
2685
+ (0, import_react6.useEffect)(() => {
2421
2686
  try {
2422
2687
  window.localStorage.setItem(NATIVE_SIDEBAR_TAB_KEY, tab);
2423
2688
  } catch {
2424
2689
  }
2425
2690
  }, [tab]);
2426
- (0, import_react5.useEffect)(() => {
2427
- const next = tabForSessionId(currentId ?? void 0);
2428
- 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");
2429
2705
  const matched = extraTabs.find((item) => currentId !== void 0 && currentId !== null && item.matchSession?.(String(currentId)) === true);
2430
- if (matched !== void 0) setTab(matched.id);
2431
- }, [currentId, channelsReady, extraTabs]);
2706
+ if (matched !== void 0 && matched.id !== "schedule") setTab(matched.id);
2707
+ }, [currentId, channelsReady, extraTabs, scheduledIds]);
2432
2708
  const rawOfficialProps = { ...hostProps ?? {}, ...wide === void 0 ? {} : { wide } };
2433
2709
  const filteredOfficialProps = {
2434
2710
  ...rawOfficialProps,
@@ -2437,19 +2713,27 @@ function NativeScheduleShell({
2437
2713
  };
2438
2714
  const renderOfficial = (props) => {
2439
2715
  if (Official === void 0) {
2440
- 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 } });
2441
2717
  }
2442
- 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) });
2443
2719
  };
2444
- if (wide === false) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderOfficial(rawOfficialProps) });
2445
- const visibleTab = tab === "channels" && !channelsReady ? "tasks" : tab;
2446
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-shell-rail", children: [
2447
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-st-shell-tabs", role: "tablist", "aria-label": t("sidebar.tabs"), children: [
2448
- /* @__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") }),
2449
- 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)),
2450
- 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") })
2451
2735
  ] }),
2452
- 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) })
2453
2737
  ] });
2454
2738
  }
2455
2739
  function NativeTaskRail({
@@ -2461,10 +2745,10 @@ function NativeTaskRail({
2461
2745
  const snap = useSessions === void 0 ? { ids: [], byId: {}, current: null } : useSessions((state) => state ?? { ids: [], byId: {}, current: null });
2462
2746
  const workspaces = useWorkspaces === void 0 ? void 0 : useWorkspaces((state) => state ?? { items: [], archivedSessionIds: [] });
2463
2747
  const groups = groupNativeTaskSessions(snap, workspaces, t("sidebar.ungrouped"));
2464
- if (groups.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-st-rail-empty", children: t("sidebar.tasksEmpty") });
2465
- 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: [
2466
- /* @__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 }) }),
2467
- 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)(
2468
2752
  "button",
2469
2753
  {
2470
2754
  type: "button",
@@ -2472,7 +2756,7 @@ function NativeTaskRail({
2472
2756
  onClick: () => {
2473
2757
  if (item.id !== void 0) openSession?.(item.id);
2474
2758
  },
2475
- 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 })
2476
2760
  },
2477
2761
  item.id
2478
2762
  ))
@@ -2619,7 +2903,7 @@ var CSS_TEXT = `
2619
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}
2620
2904
  .dsh-st-rail-head.is-static{cursor:default;font-weight:600}
2621
2905
  .dsh-st-rail-session.is-on{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}
2622
- .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.is-float{position:fixed;z-index:1200}.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}
2623
2907
  `;
2624
2908
  function installStyles() {
2625
2909
  const existing = document.getElementById(STYLE_ID);
@@ -2673,7 +2957,8 @@ function apply(ctx) {
2673
2957
  label: () => t("tab"),
2674
2958
  icon: "schedule"
2675
2959
  }, function ScheduledTasksSettings(props) {
2676
- 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() } });
2677
2962
  }));
2678
2963
  ctx.slots.inject("sidebar.schedule", () => ctx.slots.register({
2679
2964
  name: "sidebar.schedule",
@@ -2682,27 +2967,30 @@ function apply(ctx) {
2682
2967
  locale: NS,
2683
2968
  label: () => t("sidebar.tab")
2684
2969
  }, function AutomationScheduleRail(props) {
2685
- return (0, import_react6.createElement)(ScheduleRail, {
2970
+ return (0, import_react7.createElement)(ScheduleRail, {
2686
2971
  t,
2687
2972
  runtime,
2688
- openSession: (id) => {
2689
- openScheduledSession(id, (sessionId) => {
2690
- ctx.sessions?.open(sessionId);
2691
- }, props.openSession);
2692
- }
2973
+ openSession: createScheduledSessionOpener(ctx, runtime, props.openSession)
2693
2974
  });
2694
2975
  }));
2695
2976
  ctx.slots.inject("sidebar.workspaces", () => {
2696
2977
  let wrappedEntry;
2697
2978
  let originalComp;
2698
2979
  let removeInsertedTab = () => void 0;
2699
- let removeFilter = () => void 0;
2980
+ let wrapped = false;
2700
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
+ };
2701
2990
  const unwrap = () => {
2991
+ stopRetry();
2702
2992
  removeInsertedTab();
2703
2993
  removeInsertedTab = () => void 0;
2704
- removeFilter();
2705
- removeFilter = () => void 0;
2706
2994
  if (wrappedEntry !== void 0 && originalComp !== void 0) {
2707
2995
  try {
2708
2996
  wrappedEntry.component = originalComp;
@@ -2711,12 +2999,12 @@ function apply(ctx) {
2711
2999
  }
2712
3000
  wrappedEntry = void 0;
2713
3001
  originalComp = void 0;
3002
+ wrapped = false;
2714
3003
  };
2715
3004
  const insertScheduleTab = (entry, openSession) => {
2716
3005
  const registry = findNativeTabRegistry(entry);
2717
3006
  if (registry === void 0) return false;
2718
3007
  if (registry.getTabs().some((item) => item.id === "schedule")) return true;
2719
- removeInsertedTab();
2720
3008
  removeInsertedTab = registry.insert({
2721
3009
  id: "schedule",
2722
3010
  label: t("sidebar.tab"),
@@ -2724,12 +3012,8 @@ function apply(ctx) {
2724
3012
  matchSession: (sessionId) => sessionId.startsWith(AUTOMATION_SESSION_PREFIX),
2725
3013
  render: (props) => {
2726
3014
  const hostOpen = typeof props.openSession === "function" ? props.openSession : typeof props.open === "function" ? props.open : openSession;
2727
- const opener = (id) => {
2728
- openScheduledSession(id, (sessionId) => {
2729
- ctx.sessions?.open(sessionId);
2730
- }, hostOpen);
2731
- };
2732
- return (0, import_react6.createElement)(NativeScheduleSessionList, {
3015
+ const opener = createScheduledSessionOpener(ctx, runtime, hostOpen);
3016
+ return (0, import_react7.createElement)(NativeScheduleSessionList, {
2733
3017
  t,
2734
3018
  runtime,
2735
3019
  openSession: opener,
@@ -2742,21 +3026,35 @@ function apply(ctx) {
2742
3026
  });
2743
3027
  }
2744
3028
  });
2745
- removeFilter();
2746
- removeFilter = registry.addSessionFilter((id) => !id.startsWith(AUTOMATION_SESSION_PREFIX));
2747
3029
  return true;
2748
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
+ };
2749
3051
  const sync = () => {
2750
3052
  if (syncing) return;
2751
3053
  syncing = true;
2752
3054
  try {
2753
3055
  let AutomationNativeWorkspaceShell2 = function(innerProps) {
2754
- const openSession = (id) => {
2755
- openScheduledSession(id, (sessionId) => {
2756
- ctx.sessions?.open(sessionId);
2757
- }, innerProps.openSession ?? innerProps.open);
2758
- };
2759
- return (0, import_react6.createElement)(NativeScheduleShell, {
3056
+ const openSession = createScheduledSessionOpener(ctx, runtime, innerProps.openSession ?? innerProps.open);
3057
+ return (0, import_react7.createElement)(NativeScheduleShell, {
2760
3058
  t,
2761
3059
  runtime,
2762
3060
  hostProps: innerProps,
@@ -2764,7 +3062,7 @@ function apply(ctx) {
2764
3062
  tabRegistry: registry,
2765
3063
  ...innerProps.wide === void 0 ? {} : { wide: innerProps.wide },
2766
3064
  hasChannels: () => slotHasEntries(ctx, "sidebar.channels"),
2767
- subscribeChannels: (listener) => ctx.slots.subscribe?.("sidebar.channels", listener) ?? (() => void 0),
3065
+ subscribeChannels: listenChannels,
2768
3066
  ...innerProps.useSessions === void 0 ? {} : { useSessions: innerProps.useSessions },
2769
3067
  ...innerProps.useWorkspaces === void 0 ? {} : { useWorkspaces: innerProps.useWorkspaces },
2770
3068
  ...innerProps.renderSlot === void 0 ? {} : { renderSlot: innerProps.renderSlot },
@@ -2780,14 +3078,14 @@ function apply(ctx) {
2780
3078
  const occupant = pickWrappableWorkspacesEntry(entries);
2781
3079
  const current = occupant?.component;
2782
3080
  if (current !== void 0 && isForeignSidebarHost(current)) {
2783
- if (insertScheduleTab(occupant) || insertScheduleTab(current)) return;
3081
+ ensureScheduleTab();
2784
3082
  return;
2785
3083
  }
2786
3084
  if (wrappedEntry?.component !== void 0 && wrappedEntry.component.__dshAutomationWrapped === true) {
2787
- insertScheduleTab(wrappedEntry);
3085
+ ensureScheduleTab();
2788
3086
  return;
2789
3087
  }
2790
- if (occupant?.component === void 0) return;
3088
+ if (occupant?.component === void 0 || wrapped) return;
2791
3089
  const resolved = resolveOfficialTreeComponent(occupant.component);
2792
3090
  if (resolved === void 0) return;
2793
3091
  originalComp = resolved;
@@ -2801,9 +3099,8 @@ function apply(ctx) {
2801
3099
  attachNativeTabRegistry(marked, registry);
2802
3100
  occupant.component = marked;
2803
3101
  wrappedEntry = occupant;
2804
- insertScheduleTab(occupant, (id) => {
2805
- ctx.sessions?.open(id);
2806
- });
3102
+ wrapped = true;
3103
+ ensureScheduleTab();
2807
3104
  } catch (error) {
2808
3105
  console.warn("[dsh-automation] \u5305\u88F9\u5B98\u65B9\u4EFB\u52A1\u6811\u5931\u8D25", error);
2809
3106
  } finally {
@@ -2812,6 +3109,7 @@ function apply(ctx) {
2812
3109
  };
2813
3110
  sync();
2814
3111
  const unsub = typeof ctx.slots.subscribe === "function" ? ctx.slots.subscribe("sidebar.workspaces", sync) : () => void 0;
3112
+ ensureScheduleTab();
2815
3113
  return () => {
2816
3114
  unsub();
2817
3115
  unwrap();
@@ -2824,8 +3122,25 @@ function apply(ctx) {
2824
3122
  locale: NS
2825
3123
  }, PrefillBridge));
2826
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
+ }
2827
3142
  function PrefillBridge(props) {
2828
- (0, import_react6.useEffect)(() => {
3143
+ (0, import_react7.useEffect)(() => {
2829
3144
  const applyPrefill = (text) => {
2830
3145
  if (text === null || text === "") return;
2831
3146
  if (props.inputActions !== void 0) {
@@ -2851,5 +3166,12 @@ function readSlotEntries(ctx, name2) {
2851
3166
  function slotHasEntries(ctx, name2) {
2852
3167
  return readSlotEntries(ctx, name2).length > 0;
2853
3168
  }
3169
+ function readOptionalWorkspaces(ctx) {
3170
+ try {
3171
+ return ctx.workspaces;
3172
+ } catch {
3173
+ return void 0;
3174
+ }
3175
+ }
2854
3176
  return module.exports; } });
2855
3177
  //# sourceMappingURL=client.js.map