@linxin666/dsh-client-ui-task-board 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -58,6 +58,10 @@ window.__ModuleLoader__.load({
58
58
  function isTaskStatus(value) {
59
59
  return typeof value === "string" && ALL_STATUSES.includes(value);
60
60
  }
61
+ /** Whether a manual move target is allowed from the given status. */
62
+ function canMoveManually(from, to) {
63
+ return from !== "running" && MANUAL_STATUSES.includes(to);
64
+ }
61
65
  /** Normalize one optional execution-target string: trim; blank collapses to undefined. */
62
66
  function normalizeTargetId(value) {
63
67
  const trimmed = value?.trim();
@@ -451,10 +455,30 @@ window.__ModuleLoader__.load({
451
455
  * fresh updatedAt. Pure ledger transition (no persistence or notify — the
452
456
  * controller orchestrates those).
453
457
  *
454
- * An explicit `undefined` in the patch clears the field (the task falls
455
- * back to the runtime default); an unknown permission string is ignored so
456
- * stale UI can never persist a value the execution service rejects.
458
+ * An explicit `undefined` in the patch clears an execution-target field
459
+ * (the task falls back to the runtime default); content fields are trimmed
460
+ * and never cleared. An unknown permission string is ignored so stale UI
461
+ * can never persist a value the execution service rejects.
462
+ */
463
+ /**
464
+ * The fields that edit the task's content (what the user reads and what the
465
+ * next execution sends). Unlike the execution targets they stay editable only
466
+ * while the task has never started executing — after the first run the
467
+ * recorded prompt is the record of what actually ran, so it becomes read-only.
457
468
  */
469
+ const TASK_CONTENT_FIELDS = [
470
+ "title",
471
+ "description",
472
+ "prompt"
473
+ ];
474
+ /**
475
+ * Whether a task's content may still be edited: the task must be on-board
476
+ * (not archived) and must never have started executing. Fail-closed: a
477
+ * running, settled, or cancelled-before-launch task keeps its content fixed.
478
+ */
479
+ function canEditTaskContent(task) {
480
+ return task.archivedAt === void 0 && task.status !== "running" && task.executions.length === 0;
481
+ }
458
482
  /** Keep an unknown permission string from entering the ledger. */
459
483
  function normalizePermission(current, value) {
460
484
  if (value === void 0) return void 0;
@@ -479,6 +503,11 @@ window.__ModuleLoader__.load({
479
503
  ...patch,
480
504
  updatedAt: now
481
505
  };
506
+ for (const field of TASK_CONTENT_FIELDS) {
507
+ if (!(field in patch)) continue;
508
+ const value = patch[field];
509
+ next[field] = value === void 0 ? task[field] : value.trim();
510
+ }
482
511
  if (workspaceId !== void 0 || "workspaceId" in patch) next.workspaceId = workspaceId;
483
512
  if (mode !== void 0 || "mode" in patch) next.mode = mode;
484
513
  if (permission !== void 0 || "permission" in patch) next.permission = permission;
@@ -502,10 +531,6 @@ window.__ModuleLoader__.load({
502
531
  const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
503
532
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
504
533
  }
505
- /** Read the current selection off a session-list snapshot (structural). */
506
- function currentOf(sessions) {
507
- return sessions.list.getSnapshot().current;
508
- }
509
534
  function messageOf(error) {
510
535
  return error instanceof Error ? error.message : String(error);
511
536
  }
@@ -586,7 +611,6 @@ window.__ModuleLoader__.load({
586
611
  }
587
612
  openBoard() {
588
613
  if (this.boardOpen) return;
589
- this.lastCurrent = currentOf(this.deps.sessions);
590
614
  this.boardOpen = true;
591
615
  this.notify();
592
616
  }
@@ -641,17 +665,23 @@ window.__ModuleLoader__.load({
641
665
  input
642
666
  }, id) ? this.tasks.find((task) => task.id === id) : void 0;
643
667
  }
644
- updateTask(id, patch) {
645
- if (this.deps.transport !== void 0) {
646
- this.commitRemote({
647
- kind: "update",
648
- taskId: id,
649
- patch
650
- }, id);
651
- return;
652
- }
668
+ /**
669
+ * Apply an editable-field patch (task content + execution targets).
670
+ * Host-backed: the Host ledger owns the fail-closed checks (the content of
671
+ * an executed task is read-only) and confirms the mutation, so the resolved
672
+ * value reflects whether the Host accepted it; the legacy in-memory path
673
+ * applies and persists synchronously.
674
+ * @returns true when the patch was accepted by the authority.
675
+ */
676
+ async updateTask(id, patch) {
677
+ if (this.deps.transport !== void 0) return await this.commitRemote({
678
+ kind: "update",
679
+ taskId: id,
680
+ patch
681
+ }, id);
653
682
  this.tasks = [...applyUpdateTask(this.tasks, id, patch, this.now())];
654
683
  this.persistAndNotify();
684
+ return true;
655
685
  }
656
686
  /**
657
687
  * Replace (a part of) the picker option sets the UI feeds (workspace list
@@ -800,14 +830,17 @@ window.__ModuleLoader__.load({
800
830
  taskId: id
801
831
  }, id);
802
832
  }
803
- /** Close the board when the user navigates to another session. */
804
- onSessionsChanged() {
805
- if (!this.boardOpen) return;
806
- const current = currentOf(this.deps.sessions);
807
- if (current !== this.lastCurrent) this.closeBoard();
808
- this.lastCurrent = current;
809
- }
810
- lastCurrent = void 0;
833
+ /**
834
+ * Session-list notifications fire for all kinds of incidental churn
835
+ * (background navigation, the Host runner creating and selecting a fresh
836
+ * execution session, settlement, other plugins), so closing on `current`
837
+ * changes would evict the board without the user asking. The board closes
838
+ * only on explicit user navigation: a sidebar session/workspace row click
839
+ * (board-mount onClickSidebarRow) or the board's own actions
840
+ * (openSession / close). Keeping the hook preserves the subscription
841
+ * contract for future listeners.
842
+ */
843
+ onSessionsChanged() {}
811
844
  persistAndNotify() {
812
845
  if (this.deps.transport === void 0) this.deps.store.save(this.tasks);
813
846
  this.notify();
@@ -1143,7 +1176,10 @@ window.__ModuleLoader__.load({
1143
1176
  "new.cancel": "取消",
1144
1177
  "new.required": "标题不能为空",
1145
1178
  "detail.title": "任务详情",
1179
+ "detail.edit": "编辑",
1146
1180
  "detail.close": "关闭",
1181
+ "edit.title": "编辑任务",
1182
+ "edit.save": "保存",
1147
1183
  "detail.prompt": "执行 Prompt",
1148
1184
  "detail.description": "描述",
1149
1185
  "detail.execution": "执行记录",
@@ -1264,7 +1300,10 @@ window.__ModuleLoader__.load({
1264
1300
  "new.cancel": "Cancel",
1265
1301
  "new.required": "Title is required",
1266
1302
  "detail.title": "Task Detail",
1303
+ "detail.edit": "Edit",
1267
1304
  "detail.close": "Close",
1305
+ "edit.title": "Edit Task",
1306
+ "edit.save": "Save",
1268
1307
  "detail.prompt": "Run Prompt",
1269
1308
  "detail.description": "Description",
1270
1309
  "detail.execution": "Execution History",
@@ -1762,17 +1801,22 @@ window.__ModuleLoader__.load({
1762
1801
  if (timeZone !== void 0) return formatHostTimestamp(ms, timeZone);
1763
1802
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
1764
1803
  }
1765
- /** One card in a column. */
1766
1804
  function TaskCardInner({ task, pending, timeZone, onClick }) {
1767
1805
  const latest = task.executions[task.executions.length - 1];
1768
1806
  const runs = task.executions.length;
1769
1807
  const archived = task.archivedAt !== void 0;
1808
+ const isDraggable = !archived && task.status !== "running" && !pending;
1770
1809
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1771
1810
  type: "button",
1772
1811
  className: board_module_css_default.card,
1773
1812
  "data-status": archived ? "archived" : task.status,
1774
1813
  "data-dsh-part": "card",
1775
1814
  "data-pending": pending || void 0,
1815
+ draggable: isDraggable,
1816
+ onDragStart: isDraggable ? (event) => {
1817
+ event.dataTransfer.setData("text/plain", task.id);
1818
+ event.dataTransfer.effectAllowed = "move";
1819
+ } : void 0,
1776
1820
  onClick,
1777
1821
  title: task.description !== "" ? task.description : task.title,
1778
1822
  children: [
@@ -1877,6 +1921,125 @@ window.__ModuleLoader__.load({
1877
1921
  });
1878
1922
  }
1879
1923
  //#endregion
1924
+ //#region src/client/board/EditTaskModal.tsx
1925
+ /**
1926
+ * Edit-task modal: title + description + the prompt the next execution will
1927
+ * send, pre-filled from the task. Shown only for tasks that have never
1928
+ * started executing (the detail view gates on canEditTaskContent); the Host
1929
+ * still re-checks at submit, so a task that started running while the modal
1930
+ * was open fails closed and the error surfaces here.
1931
+ */
1932
+ /** Edit-task form overlay. */
1933
+ function EditTaskModal({ controller, task, onClose }) {
1934
+ const [title, setTitle] = (0, react.useState)(task.title);
1935
+ const [description, setDescription] = (0, react.useState)(task.description);
1936
+ const [prompt, setPrompt] = (0, react.useState)(task.prompt);
1937
+ const [error, setError] = (0, react.useState)(void 0);
1938
+ const [pending, setPending] = (0, react.useState)(false);
1939
+ const submit = async () => {
1940
+ if (title.trim() === "") {
1941
+ setError(t("new.required"));
1942
+ return;
1943
+ }
1944
+ setPending(true);
1945
+ if (await controller.updateTask(task.id, {
1946
+ title,
1947
+ description,
1948
+ prompt
1949
+ })) {
1950
+ onClose();
1951
+ return;
1952
+ }
1953
+ setPending(false);
1954
+ setError(controller.getSnapshot().transportError ?? t("new.required"));
1955
+ };
1956
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1957
+ className: board_module_css_default.modalBackdrop,
1958
+ onMouseDown: (event) => {
1959
+ if (event.target === event.currentTarget) onClose();
1960
+ },
1961
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1962
+ className: board_module_css_default.modal,
1963
+ role: "dialog",
1964
+ "aria-label": t("edit.title"),
1965
+ onSubmit: (event) => {
1966
+ event.preventDefault();
1967
+ submit();
1968
+ },
1969
+ children: [
1970
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1971
+ className: board_module_css_default.modalTitle,
1972
+ children: t("edit.title")
1973
+ }),
1974
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1975
+ className: board_module_css_default.field,
1976
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1977
+ className: board_module_css_default.fieldLabel,
1978
+ children: t("new.title")
1979
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1980
+ className: board_module_css_default.input,
1981
+ value: title,
1982
+ autoFocus: true,
1983
+ placeholder: t("new.titlePlaceholder"),
1984
+ onChange: (event) => {
1985
+ setTitle(event.target.value);
1986
+ setError(void 0);
1987
+ }
1988
+ })]
1989
+ }),
1990
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1991
+ className: board_module_css_default.field,
1992
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1993
+ className: board_module_css_default.fieldLabel,
1994
+ children: t("new.description")
1995
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1996
+ className: board_module_css_default.input,
1997
+ rows: 3,
1998
+ value: description,
1999
+ placeholder: t("new.descriptionPlaceholder"),
2000
+ onChange: (event) => {
2001
+ setDescription(event.target.value);
2002
+ }
2003
+ })]
2004
+ }),
2005
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2006
+ className: board_module_css_default.field,
2007
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2008
+ className: board_module_css_default.fieldLabel,
2009
+ children: t("new.prompt")
2010
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2011
+ className: board_module_css_default.input,
2012
+ rows: 4,
2013
+ value: prompt,
2014
+ placeholder: t("new.promptPlaceholder"),
2015
+ onChange: (event) => {
2016
+ setPrompt(event.target.value);
2017
+ }
2018
+ })]
2019
+ }),
2020
+ error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2021
+ className: board_module_css_default.formError,
2022
+ children: error
2023
+ }),
2024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
2025
+ className: board_module_css_default.modalFooter,
2026
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2027
+ type: "button",
2028
+ className: board_module_css_default.ghostButton,
2029
+ onClick: onClose,
2030
+ children: t("new.cancel")
2031
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2032
+ type: "submit",
2033
+ className: board_module_css_default.primaryButton,
2034
+ disabled: pending,
2035
+ children: t("edit.save")
2036
+ })]
2037
+ })
2038
+ ]
2039
+ })
2040
+ });
2041
+ }
2042
+ //#endregion
1880
2043
  //#region src/client/board/TaskDetail.tsx
1881
2044
  /**
1882
2045
  * Task detail: the full view of one task — content, prompt, execution
@@ -2156,10 +2319,14 @@ window.__ModuleLoader__.load({
2156
2319
  /** Task detail overlay. */
2157
2320
  function TaskDetail({ controller, task }) {
2158
2321
  const [confirmDelete, setConfirmDelete] = (0, react.useState)(false);
2322
+ const [showEdit, setShowEdit] = (0, react.useState)(false);
2159
2323
  const [latest, setLatest] = (0, react.useState)(task);
2160
2324
  (0, react.useEffect)(() => {
2161
2325
  setLatest(task);
2162
2326
  }, [task]);
2327
+ (0, react.useEffect)(() => {
2328
+ setShowEdit(false);
2329
+ }, [task.id]);
2163
2330
  const current = latest;
2164
2331
  const snapshot = controller.getSnapshot();
2165
2332
  const running = current.status === "running";
@@ -2172,177 +2339,196 @@ window.__ModuleLoader__.load({
2172
2339
  onMouseDown: (event) => {
2173
2340
  if (event.target === event.currentTarget) controller.closeTask();
2174
2341
  },
2175
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2176
- className: board_module_css_default.detail,
2177
- role: "dialog",
2178
- "aria-label": t("detail.title"),
2179
- children: [
2180
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2181
- className: board_module_css_default.detailHeader,
2182
- children: [
2183
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2184
- className: board_module_css_default.detailTitle,
2185
- children: current.title
2186
- }),
2187
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2188
- className: board_module_css_default.statusBadge,
2189
- "data-status": archived ? "archived" : current.status,
2190
- children: archived ? t("board.archive") : t(STATUS_KEY[current.status])
2191
- }),
2192
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2193
- type: "button",
2194
- className: board_module_css_default.iconButton,
2195
- "aria-label": t("detail.close"),
2196
- onClick: () => {
2197
- controller.closeTask();
2198
- },
2199
- children: "×"
2200
- })
2201
- ]
2202
- }),
2203
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2204
- className: board_module_css_default.detailBody,
2205
- children: [
2206
- transportError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2207
- className: board_module_css_default.formError,
2208
- children: [
2209
- t("board.hostError", { error: transportError }),
2210
- " ",
2211
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2212
- type: "button",
2213
- className: board_module_css_default.linkButton,
2214
- onClick: () => {
2215
- controller.retryHostSync();
2216
- },
2217
- children: t("board.retryHost")
2218
- })
2219
- ]
2220
- }),
2221
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2222
- className: board_module_css_default.detailSection,
2223
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.description") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2224
- className: board_module_css_default.detailText,
2225
- children: current.description !== "" ? current.description : "—"
2226
- })]
2227
- }),
2228
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2229
- className: board_module_css_default.detailSection,
2230
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.prompt") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
2231
- className: board_module_css_default.promptBlock,
2232
- children: current.prompt !== "" ? current.prompt : current.title
2233
- })]
2234
- }),
2235
- !archived && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionSettingsSection, {
2236
- controller,
2237
- task: current,
2238
- pending
2239
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScheduleSection, {
2240
- controller,
2241
- task: current,
2242
- pending
2243
- })] }),
2244
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2245
- className: board_module_css_default.detailSection,
2246
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.execution") }), current.executions.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2247
- className: board_module_css_default.detailText,
2248
- children: t("detail.noExecution")
2249
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2250
- className: board_module_css_default.executionList,
2251
- children: [...current.executions].reverse().map((execution) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionRow, {
2252
- execution,
2253
- timeZone,
2254
- onOpen: (sessionId) => {
2255
- controller.openSession(sessionId);
2256
- }
2257
- }, execution.id))
2258
- })]
2259
- }),
2260
- !archived && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2261
- className: board_module_css_default.detailSection,
2262
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("board.status") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2263
- className: board_module_css_default.moveRow,
2264
- children: MANUAL_STATUSES.map((status) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2265
- type: "button",
2266
- className: board_module_css_default.ghostButton,
2267
- disabled: current.status === status || running || pending,
2268
- onClick: () => {
2269
- controller.moveTask(current.id, status);
2270
- },
2271
- children: t(`status.move.${status}`)
2272
- }, status))
2273
- })]
2274
- })
2275
- ]
2276
- }),
2277
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
2278
- className: board_module_css_default.detailFooter,
2279
- children: [
2280
- !archived && pending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2281
- className: board_module_css_default.detailMeta,
2282
- children: [t("board.pending"), "…"]
2283
- }),
2284
- !archived && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2285
- type: "button",
2286
- className: board_module_css_default.primaryButton,
2287
- disabled: running || pending,
2288
- onClick: () => {
2289
- controller.rerunTask(current.id).then(() => {
2290
- if (controller.getSnapshot().transportError === void 0) controller.closeTask();
2291
- });
2292
- },
2293
- children: current.executions.length === 0 ? t("detail.run") : t("detail.rerun")
2294
- }),
2295
- archived ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2296
- type: "button",
2297
- className: board_module_css_default.primaryButton,
2298
- disabled: pending,
2299
- onClick: () => {
2300
- controller.restoreTask(current.id);
2301
- },
2302
- children: t("detail.restore")
2303
- }) : (current.status === "done" || current.status === "failed") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2304
- type: "button",
2305
- className: board_module_css_default.ghostButton,
2306
- disabled: pending,
2307
- onClick: () => {
2308
- controller.archiveTask(current.id);
2309
- },
2310
- children: t("detail.archive")
2311
- }),
2312
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2313
- type: "button",
2314
- className: board_module_css_default.dangerButton,
2315
- disabled: pending,
2316
- onClick: () => {
2317
- setConfirmDelete(true);
2318
- },
2319
- children: t("detail.delete")
2320
- }),
2321
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2322
- className: board_module_css_default.detailMeta,
2323
- children: [
2324
- t("board.created"),
2325
- " ",
2326
- formatTime(current.createdAt, timeZone),
2327
- archived && ` · ${t("detail.archivedAt", { time: formatTime(current.archivedAt, timeZone) })}`
2328
- ]
2329
- })
2330
- ]
2331
- })
2332
- ]
2333
- }), confirmDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
2334
- title: t("delete.title"),
2335
- message: t("delete.confirm", { name: current.title }),
2336
- confirmLabel: t("delete.ok"),
2337
- danger: true,
2338
- onCancel: () => {
2339
- setConfirmDelete(false);
2340
- },
2341
- onConfirm: () => {
2342
- setConfirmDelete(false);
2343
- controller.deleteTask(current.id);
2344
- }
2345
- })]
2342
+ children: [
2343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2344
+ className: board_module_css_default.detail,
2345
+ role: "dialog",
2346
+ "aria-label": t("detail.title"),
2347
+ children: [
2348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2349
+ className: board_module_css_default.detailHeader,
2350
+ children: [
2351
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2352
+ className: board_module_css_default.detailTitle,
2353
+ children: current.title
2354
+ }),
2355
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2356
+ className: board_module_css_default.statusBadge,
2357
+ "data-status": archived ? "archived" : current.status,
2358
+ children: archived ? t("board.archive") : t(STATUS_KEY[current.status])
2359
+ }),
2360
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2361
+ type: "button",
2362
+ className: board_module_css_default.iconButton,
2363
+ "aria-label": t("detail.close"),
2364
+ onClick: () => {
2365
+ controller.closeTask();
2366
+ },
2367
+ children: "×"
2368
+ })
2369
+ ]
2370
+ }),
2371
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2372
+ className: board_module_css_default.detailBody,
2373
+ children: [
2374
+ transportError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2375
+ className: board_module_css_default.formError,
2376
+ children: [
2377
+ t("board.hostError", { error: transportError }),
2378
+ " ",
2379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2380
+ type: "button",
2381
+ className: board_module_css_default.linkButton,
2382
+ onClick: () => {
2383
+ controller.retryHostSync();
2384
+ },
2385
+ children: t("board.retryHost")
2386
+ })
2387
+ ]
2388
+ }),
2389
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2390
+ className: board_module_css_default.detailSection,
2391
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.description") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2392
+ className: board_module_css_default.detailText,
2393
+ children: current.description !== "" ? current.description : "—"
2394
+ })]
2395
+ }),
2396
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2397
+ className: board_module_css_default.detailSection,
2398
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.prompt") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
2399
+ className: board_module_css_default.promptBlock,
2400
+ children: current.prompt !== "" ? current.prompt : current.title
2401
+ })]
2402
+ }),
2403
+ !archived && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionSettingsSection, {
2404
+ controller,
2405
+ task: current,
2406
+ pending
2407
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScheduleSection, {
2408
+ controller,
2409
+ task: current,
2410
+ pending
2411
+ })] }),
2412
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2413
+ className: board_module_css_default.detailSection,
2414
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("detail.execution") }), current.executions.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2415
+ className: board_module_css_default.detailText,
2416
+ children: t("detail.noExecution")
2417
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2418
+ className: board_module_css_default.executionList,
2419
+ children: [...current.executions].reverse().map((execution) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionRow, {
2420
+ execution,
2421
+ timeZone,
2422
+ onOpen: (sessionId) => {
2423
+ controller.openSession(sessionId);
2424
+ }
2425
+ }, execution.id))
2426
+ })]
2427
+ }),
2428
+ !archived && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2429
+ className: board_module_css_default.detailSection,
2430
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("board.status") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2431
+ className: board_module_css_default.moveRow,
2432
+ children: MANUAL_STATUSES.map((status) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2433
+ type: "button",
2434
+ className: board_module_css_default.ghostButton,
2435
+ disabled: current.status === status || running || pending,
2436
+ onClick: () => {
2437
+ controller.moveTask(current.id, status);
2438
+ },
2439
+ children: t(`status.move.${status}`)
2440
+ }, status))
2441
+ })]
2442
+ })
2443
+ ]
2444
+ }),
2445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
2446
+ className: board_module_css_default.detailFooter,
2447
+ children: [
2448
+ !archived && pending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2449
+ className: board_module_css_default.detailMeta,
2450
+ children: [t("board.pending"), "…"]
2451
+ }),
2452
+ !archived && canEditTaskContent(current) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2453
+ type: "button",
2454
+ className: board_module_css_default.ghostButton,
2455
+ disabled: pending,
2456
+ onClick: () => {
2457
+ setShowEdit(true);
2458
+ },
2459
+ children: t("detail.edit")
2460
+ }),
2461
+ !archived && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2462
+ type: "button",
2463
+ className: board_module_css_default.primaryButton,
2464
+ disabled: running || pending,
2465
+ onClick: () => {
2466
+ controller.rerunTask(current.id).then(() => {
2467
+ if (controller.getSnapshot().transportError === void 0) controller.closeTask();
2468
+ });
2469
+ },
2470
+ children: current.executions.length === 0 ? t("detail.run") : t("detail.rerun")
2471
+ }),
2472
+ archived ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2473
+ type: "button",
2474
+ className: board_module_css_default.primaryButton,
2475
+ disabled: pending,
2476
+ onClick: () => {
2477
+ controller.restoreTask(current.id);
2478
+ },
2479
+ children: t("detail.restore")
2480
+ }) : (current.status === "done" || current.status === "failed") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2481
+ type: "button",
2482
+ className: board_module_css_default.ghostButton,
2483
+ disabled: pending,
2484
+ onClick: () => {
2485
+ controller.archiveTask(current.id);
2486
+ },
2487
+ children: t("detail.archive")
2488
+ }),
2489
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2490
+ type: "button",
2491
+ className: board_module_css_default.dangerButton,
2492
+ disabled: pending,
2493
+ onClick: () => {
2494
+ setConfirmDelete(true);
2495
+ },
2496
+ children: t("detail.delete")
2497
+ }),
2498
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2499
+ className: board_module_css_default.detailMeta,
2500
+ children: [
2501
+ t("board.created"),
2502
+ " ",
2503
+ formatTime(current.createdAt, timeZone),
2504
+ archived && ` · ${t("detail.archivedAt", { time: formatTime(current.archivedAt, timeZone) })}`
2505
+ ]
2506
+ })
2507
+ ]
2508
+ })
2509
+ ]
2510
+ }),
2511
+ confirmDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
2512
+ title: t("delete.title"),
2513
+ message: t("delete.confirm", { name: current.title }),
2514
+ confirmLabel: t("delete.ok"),
2515
+ danger: true,
2516
+ onCancel: () => {
2517
+ setConfirmDelete(false);
2518
+ },
2519
+ onConfirm: () => {
2520
+ setConfirmDelete(false);
2521
+ controller.deleteTask(current.id);
2522
+ }
2523
+ }),
2524
+ showEdit && !archived && canEditTaskContent(current) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditTaskModal, {
2525
+ controller,
2526
+ task: current,
2527
+ onClose: () => {
2528
+ setShowEdit(false);
2529
+ }
2530
+ })
2531
+ ]
2346
2532
  });
2347
2533
  }
2348
2534
  //#endregion
@@ -2490,10 +2676,22 @@ window.__ModuleLoader__.load({
2490
2676
  })]
2491
2677
  }) : COLUMNS.map((column) => {
2492
2678
  const tasks = visible.filter((task) => task.status === column.status);
2679
+ const isManualDropTarget = column.status === "backlog" || column.status === "todo";
2493
2680
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2494
2681
  className: board_module_css_default.column,
2495
2682
  "data-status": column.status,
2496
2683
  "data-dsh-part": "column",
2684
+ onDragOver: isManualDropTarget ? (event) => {
2685
+ event.preventDefault();
2686
+ event.dataTransfer.dropEffect = "move";
2687
+ } : void 0,
2688
+ onDrop: isManualDropTarget ? (event) => {
2689
+ event.preventDefault();
2690
+ const taskId = event.dataTransfer.getData("text/plain");
2691
+ if (!taskId) return;
2692
+ const dropped = snapshot.tasks.find((t) => t.id === taskId);
2693
+ if (dropped && canMoveManually(dropped.status, column.status) && dropped.status !== column.status) controller.moveTask(taskId, column.status);
2694
+ } : void 0,
2497
2695
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2498
2696
  className: board_module_css_default.columnHeader,
2499
2697
  children: [
@@ -2767,7 +2965,7 @@ window.__ModuleLoader__.load({
2767
2965
  }
2768
2966
  //#endregion
2769
2967
  //#region \0dsh-css:packages/dsh-task-board/src/client/settings-card.module.css.mjs
2770
- const css = ".Jh0q7G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Jh0q7G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Jh0q7G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Jh0q7G_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.Jh0q7G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Jh0q7G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Jh0q7G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Jh0q7G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_chevronOpen{transform:rotate(180deg)}.Jh0q7G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Jh0q7G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Jh0q7G_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.Jh0q7G_discard,.Jh0q7G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Jh0q7G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Jh0q7G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Jh0q7G_discard:disabled,.Jh0q7G_save:disabled{opacity:.4;cursor:default}.Jh0q7G_discard:focus-visible,.Jh0q7G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Jh0q7G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Jh0q7G_field+.Jh0q7G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Jh0q7G_head{align-items:center;gap:8px;display:flex}.Jh0q7G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Jh0q7G_badges{align-items:center;gap:8px;display:inline-flex}.Jh0q7G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Jh0q7G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Jh0q7G_reset:disabled{cursor:default}.Jh0q7G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Jh0q7G_input,.Jh0q7G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_input:focus-visible,.Jh0q7G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Jh0q7G_input:disabled,.Jh0q7G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Jh0q7G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Jh0q7G_selectWrap{position:relative}.Jh0q7G_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.Jh0q7G_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.Jh0q7G_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_selectChevronOpen{transform:rotate(180deg)}.Jh0q7G_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.Jh0q7G_selectPopupOpen{opacity:1;transform:none}.Jh0q7G_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.Jh0q7G_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;flex-shrink:0;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.Jh0q7G_selectOption:hover,.Jh0q7G_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.Jh0q7G_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.Jh0q7G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Jh0q7G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Jh0q7G_card,.Jh0q7G_header,.Jh0q7G_chevron,.Jh0q7G_chevronOpen,.Jh0q7G_discard,.Jh0q7G_save,.Jh0q7G_selectChevron,.Jh0q7G_selectChevronOpen,.Jh0q7G_selectPopup{transition:none}}";
2968
+ const css = ".Jh0q7G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Jh0q7G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_header{appearance:none;box-sizing:border-box;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Jh0q7G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Jh0q7G_headerStatic{box-sizing:border-box;border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.Jh0q7G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Jh0q7G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Jh0q7G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Jh0q7G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_chevronOpen{transform:rotate(180deg)}.Jh0q7G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Jh0q7G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Jh0q7G_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.Jh0q7G_discard,.Jh0q7G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Jh0q7G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Jh0q7G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Jh0q7G_discard:disabled,.Jh0q7G_save:disabled{opacity:.4;cursor:default}.Jh0q7G_discard:focus-visible,.Jh0q7G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Jh0q7G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Jh0q7G_field+.Jh0q7G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Jh0q7G_head{align-items:center;gap:8px;display:flex}.Jh0q7G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Jh0q7G_badges{align-items:center;gap:8px;display:inline-flex}.Jh0q7G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Jh0q7G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Jh0q7G_reset:disabled{cursor:default}.Jh0q7G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Jh0q7G_input,.Jh0q7G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_input:focus-visible,.Jh0q7G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Jh0q7G_input:disabled,.Jh0q7G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Jh0q7G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Jh0q7G_selectWrap{position:relative}.Jh0q7G_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.Jh0q7G_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.Jh0q7G_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_selectChevronOpen{transform:rotate(180deg)}.Jh0q7G_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.Jh0q7G_selectPopupOpen{opacity:1;transform:none}.Jh0q7G_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.Jh0q7G_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;flex-shrink:0;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.Jh0q7G_selectOption:hover,.Jh0q7G_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.Jh0q7G_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.Jh0q7G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Jh0q7G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Jh0q7G_card,.Jh0q7G_header,.Jh0q7G_chevron,.Jh0q7G_chevronOpen,.Jh0q7G_discard,.Jh0q7G_save,.Jh0q7G_selectChevron,.Jh0q7G_selectChevronOpen,.Jh0q7G_selectPopup{transition:none}}";
2771
2969
  const tagId = "@linxin666/dsh-client-ui-task-board/settings-card.module.css";
2772
2970
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2773
2971
  const tag = document.createElement("style");
@@ -2852,7 +3050,7 @@ window.__ModuleLoader__.load({
2852
3050
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2853
3051
  className: settings_card_module_css_default.description,
2854
3052
  title: description,
2855
- children: description
3053
+ children: props.descriptionNode ?? description
2856
3054
  })]
2857
3055
  }), state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2858
3056
  className: settings_card_module_css_default.pending,
@@ -2877,7 +3075,7 @@ window.__ModuleLoader__.load({
2877
3075
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2878
3076
  className: settings_card_module_css_default.description,
2879
3077
  title: description,
2880
- children: description
3078
+ children: props.descriptionNode ?? description
2881
3079
  })]
2882
3080
  }),
2883
3081
  state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -3675,7 +3873,7 @@ window.__ModuleLoader__.load({
3675
3873
  /** The building package's version, when the bundle carries it. */
3676
3874
  function bakedVersion() {
3677
3875
  try {
3678
- return "0.3.3";
3876
+ return "0.3.5";
3679
3877
  } catch {
3680
3878
  return;
3681
3879
  }