dsh-later 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.en.md +88 -3
- package/README.md +86 -3
- package/lib/client.js +828 -294
- package/lib/commands.js +486 -51
- package/lib/index.js +20 -2
- package/lib/ownership-store.js +50 -3
- package/lib/presence.js +72 -2
- package/lib/runtime.js +39 -5
- package/lib/schedule-summary-store.js +160 -0
- package/lib/schedule-summary.js +113 -0
- package/lib/smart-window.js +28 -3
- package/lib/types/commands.d.ts +60 -16
- package/lib/types/index.d.ts +2 -0
- package/lib/types/ownership-store.d.ts +30 -1
- package/lib/types/presence.d.ts +32 -0
- package/lib/types/runtime.d.ts +10 -1
- package/lib/types/schedule-summary-store.d.ts +75 -0
- package/lib/types/schedule-summary.d.ts +92 -0
- package/lib/types/user-tools.d.ts +9 -1
- package/lib/user-tools.js +13 -4
- package/package.json +5 -4
package/lib/client.js
CHANGED
|
@@ -4,9 +4,11 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
"use strict";
|
|
7
|
+
var __create = Object.create;
|
|
7
8
|
var __defProp = Object.defineProperty;
|
|
8
9
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
10
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
10
12
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
13
|
var __export = (target, all) => {
|
|
12
14
|
for (var name2 in all)
|
|
@@ -20,14 +22,29 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
20
22
|
}
|
|
21
23
|
return to;
|
|
22
24
|
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
23
33
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
34
|
|
|
25
35
|
// src/client/index.tsx
|
|
26
36
|
var index_exports = {};
|
|
27
37
|
__export(index_exports, {
|
|
28
38
|
apply: () => apply,
|
|
39
|
+
buildScheduleSummary: () => buildScheduleSummary,
|
|
40
|
+
createScheduleSummaryStore: () => createScheduleSummaryStore,
|
|
41
|
+
getScheduleSummaryStore: () => getScheduleSummaryStore,
|
|
29
42
|
inject: () => inject,
|
|
30
|
-
name: () => name
|
|
43
|
+
name: () => name,
|
|
44
|
+
schedulesOfProjection: () => schedulesOfProjection,
|
|
45
|
+
subscribeScheduleSummaryStore: () => subscribeScheduleSummaryStore,
|
|
46
|
+
summarizeScheduleGroup: () => summarizeScheduleGroup,
|
|
47
|
+
useScheduleSummary: () => useScheduleSummary
|
|
31
48
|
});
|
|
32
49
|
module.exports = __toCommonJS(index_exports);
|
|
33
50
|
|
|
@@ -71,25 +88,27 @@ var COMPONENTS = [
|
|
|
71
88
|
" margin: 0 auto calc(0px - var(--dsh-composer-stack-gap) - 3px);",
|
|
72
89
|
" padding: 0 var(--dsh-composer-dock-inset);",
|
|
73
90
|
"}",
|
|
74
|
-
"/* \u9762\u677F\u4E0E QueueDock .panel \u4E00\u81F4\uFF1Abg
|
|
91
|
+
"/* \u9762\u677F\u4E0E QueueDock .panel \u4E00\u81F4\uFF1Abg menu + backdrop-filter\u3001\u9876\u90E8\u5706\u89D2\u3001::before \u753B\u63CF\u8FB9\uFF08\u7559\u5E95\u8FB9\u5F00\u53E3\uFF09\u3002 */",
|
|
75
92
|
".ss-dock-body {",
|
|
76
93
|
" position: relative; overflow: hidden; width: 100%; padding: 2px 0;",
|
|
77
|
-
"
|
|
78
|
-
" border-radius:
|
|
94
|
+
" isolation: isolate;",
|
|
95
|
+
" border-radius: var(--dsw-radius-lg) var(--dsw-radius-lg) 0 0;",
|
|
79
96
|
" --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);",
|
|
80
97
|
" --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);",
|
|
81
98
|
"}",
|
|
82
|
-
".ss-dock-body::
|
|
83
|
-
"
|
|
84
|
-
"
|
|
99
|
+
".ss-dock-body::before {",
|
|
100
|
+
" z-index: -1; border-radius: inherit; background: var(--dsw-specific-menu);",
|
|
101
|
+
" backdrop-filter: var(--dsw-menu-backdrop-filter); content: '';",
|
|
102
|
+
" position: absolute; inset: 0; pointer-events: none;",
|
|
103
|
+
" border: 1px solid var(--dsw-alias-border-l1); border-bottom: none;",
|
|
85
104
|
"}",
|
|
86
105
|
".ss-dock-header {",
|
|
87
106
|
" box-sizing: border-box; display: flex; align-items: center; gap: 10px;",
|
|
88
107
|
" width: 100%; height: 36px; padding: 4px 12px;",
|
|
89
|
-
" background: none; border: none; border-radius:
|
|
108
|
+
" background: none; border: none; border-radius: var(--dsw-radius-md); cursor: pointer; text-align: left;",
|
|
90
109
|
" color: var(--dsw-alias-label-primary);",
|
|
91
110
|
"}",
|
|
92
|
-
".ss-dock-header:focus-visible { outline:
|
|
111
|
+
".ss-dock-header:focus-visible { outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: -2px; }",
|
|
93
112
|
".ss-dock-lead { display: grid; place-items: center; color: var(--dsw-alias-label-tertiary); flex: none; }",
|
|
94
113
|
".ss-dock-count {",
|
|
95
114
|
" flex: auto; min-width: 0;",
|
|
@@ -182,13 +201,30 @@ var COMPONENTS = [
|
|
|
182
201
|
" background-repeat: no-repeat;",
|
|
183
202
|
"}",
|
|
184
203
|
".ss-dock-actions { display: flex; align-items: center; gap: 10px; flex: none; }",
|
|
204
|
+
"/* DockTip\uFF1A\u9010\u5B57\u5BF9\u9F50\u5B98\u65B9 Tooltip.module.css .bubble\uFF08portal: true \u5C42\uFF09\u3002 */",
|
|
205
|
+
".ss-dock-tip-anchor { display: inline-grid; flex: none; }",
|
|
206
|
+
".ss-dock-tip {",
|
|
207
|
+
" position: fixed; z-index: 1100;",
|
|
208
|
+
" display: inline-flex; align-items: center; gap: 8px;",
|
|
209
|
+
" width: max-content; max-width: 50vw;",
|
|
210
|
+
" padding: 3px 7px; border-radius: var(--dsw-radius-sm);",
|
|
211
|
+
" background: var(--dsw-alias-tooltip-bg); color: var(--dsw-static-neutral-bluish-00);",
|
|
212
|
+
" font-size: 13px; line-height: 20px;",
|
|
213
|
+
" white-space: pre-line; overflow-wrap: break-word;",
|
|
214
|
+
" pointer-events: none;",
|
|
215
|
+
" transform: translateX(-50%);",
|
|
216
|
+
" animation: ss-dock-tip-in 150ms var(--ds-ease-in-out);",
|
|
217
|
+
"}",
|
|
218
|
+
'.ss-dock-tip[data-side="top"] { transform: translate(-50%, -100%); }',
|
|
219
|
+
"@keyframes ss-dock-tip-in { from { opacity: 0; } }",
|
|
220
|
+
"@media (prefers-reduced-motion: reduce) { .ss-dock-tip { animation: none; } }",
|
|
185
221
|
".ss-dock-action {",
|
|
186
222
|
" display: grid; place-items: center; width: 28px; height: 28px; padding: 0; flex: none;",
|
|
187
223
|
" border: none; border-radius: 999px; background: none; cursor: pointer;",
|
|
188
224
|
" color: var(--dsw-alias-label-tertiary);",
|
|
189
225
|
"}",
|
|
190
226
|
".ss-dock-action:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }",
|
|
191
|
-
".ss-dock-action:focus-visible { outline:
|
|
227
|
+
".ss-dock-action:focus-visible { outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: -2px; }",
|
|
192
228
|
".ss-dock-action:disabled { cursor: default; opacity: 0.45; }",
|
|
193
229
|
"/* \u63D2\u8BDD\u5B8C\u6210\u6001\uFF1AQueueDock \u65E0\u6B64\u6001\uFF0C\u7528\u6210\u529F\u8272\u505A\u8F7B\u63D0\u793A\uFF08\u53E0\u52A0 disabled \u534A\u900F\u660E\uFF09\u3002 */",
|
|
194
230
|
".ss-dock-action-steer.ss-done { color: var(--dsw-alias-state-success-primary); }",
|
|
@@ -238,19 +274,40 @@ var COMPONENTS = [
|
|
|
238
274
|
".ss-dock-editing { flex-wrap: wrap; }",
|
|
239
275
|
"",
|
|
240
276
|
"/* ==================== \u9762\u677F ==================== */",
|
|
241
|
-
"/*
|
|
277
|
+
"/*",
|
|
278
|
+
" * \u62AC\u5347\u9762\u9010\u9879\u5BF9\u9F50\u5B98\u65B9 PickerPopover\uFF08@deepseek-ai/dsh-client-ui-schedule \u7684",
|
|
279
|
+
" * PickerPopover.module.css + primitives MenuSurface.module.css\uFF09\uFF1A",
|
|
280
|
+
" * \u80CC\u666F --dsw-menu-surface-fill\uFF08\u5E26\u900F\u660E\u5EA6\u7684\u83DC\u5355\u9762\uFF09+ blur(40px) \u6BDB\u73BB\u7483",
|
|
281
|
+
" * \u63CF\u8FB9\u8D70\u4E3B\u9898\u7684 --dsw-elevation-stroke\uFF080 0 0 .5px\uFF09\uFF0C\u800C\u4E0D\u662F\u624B\u5199 1px \u2014\u2014",
|
|
282
|
+
" * 0.5px \u63CF\u8FB9\u662F\u9AD8\u5206\u5C4F\u4E0B\u7684\u6B63\u786E\u7EC6\u7EBF\uFF0C1px \u5728 1x \u4E0A\u8FC7\u91CD\u3001\u5728 2x \u4E0A\u53C8\u53D1\u865A\u3002",
|
|
283
|
+
" * \u9634\u5F71 --dsw-elevation-prominent\uFF08\u542B\u63CF\u8FB9\u5C42\uFF0C\u4E0D\u662F\u7B2C\u4E8C\u5957\u9634\u5F71\uFF09\u3002",
|
|
284
|
+
" * \u5706\u89D2 16px = --dsw-radius-lg\uFF08\u5B98\u65B9\u9762\u677F\u540C\u4E00\u6863\uFF1B\u65E7\u7684 12px \u662F --dsw-radius-md\uFF09\u3002",
|
|
285
|
+
" * z-index 1100 = \u5B98\u65B9\u6D6E\u5C42\u6863\uFF08\u65E7\u7684 101 \u662F\u81EA\u9020\u503C\uFF0C\u6392\u5728 dock \u4E4B\u4E0B\uFF09\u3002",
|
|
286
|
+
" *",
|
|
287
|
+
" * \u26A0\uFE0F \u52FF\u518D\u5F15\u5165 --dsw-shadow-lv3 / --dsw-alias-border-inverted\uFF1A\u90A3\u662F\u66F4\u65E9\u8BBE\u8BA1\u4EE3\u6B21\u7684",
|
|
288
|
+
" * token\uFF0C\u6DF7\u7528\u4F1A\u8BA9\u9762\u677F\u5728\u6D45\u8272/\u6DF1\u8272\u4E0B\u90FD\u504F\u79BB\u5B98\u65B9\u89C2\u611F\uFF08issue #24\uFF09\u3002",
|
|
289
|
+
" */",
|
|
242
290
|
".ss-backdrop {",
|
|
243
|
-
" position: fixed; inset: 0; z-index:
|
|
291
|
+
" position: fixed; inset: 0; z-index: 1099; background: transparent;",
|
|
244
292
|
"}",
|
|
245
293
|
".ss-panel {",
|
|
246
|
-
" position: fixed; z-index:
|
|
294
|
+
" position: fixed; top: auto; left: auto; z-index: 1100;",
|
|
295
|
+
" box-sizing: border-box; display: flex; flex-direction: column;",
|
|
247
296
|
" width: min(340px, calc(100vw - 24px));",
|
|
248
|
-
"
|
|
297
|
+
" padding: 12px;",
|
|
298
|
+
" background: var(--dsw-menu-surface-fill);",
|
|
299
|
+
" backdrop-filter: var(--dsw-menu-backdrop-filter);",
|
|
300
|
+
" -webkit-backdrop-filter: var(--dsw-menu-backdrop-filter);",
|
|
301
|
+
" border: 0; border-radius: var(--dsw-radius-lg);",
|
|
302
|
+
" box-shadow: var(--dsw-elevation-prominent);",
|
|
303
|
+
" /* \u63CF\u8FB9\u4E0E\u9634\u5F71\u540C\u53D8\u91CF\uFF1A--dsw-elevation-prominent \u81EA\u8EAB\u542B --dsw-elevation-stroke\uFF0C",
|
|
304
|
+
" \u8FD9\u91CC\u53EA\u9700\u7ED9 stroke \u989C\u8272\u3002\u5B98\u65B9 schedule/input-trigger/subagent \u4E09\u4E2A\u5305\u4E00\u81F4\u53D6 l1\u3002 */",
|
|
305
|
+
" --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);",
|
|
249
306
|
" color: var(--dsw-alias-label-secondary);",
|
|
250
|
-
" border: 1px solid var(--dsw-alias-border-inverted); border-radius: 12px;",
|
|
251
|
-
" box-shadow: var(--dsw-shadow-lv3); padding: 12px; box-sizing: border-box;",
|
|
252
307
|
" font-size: 12px; line-height: 20px; font-family: var(--dsw-font-family);",
|
|
253
308
|
"}",
|
|
309
|
+
/* 兜底定位分支(无官方 useAnchoredPosition 的旧代次宿主)才用 bottom,
|
|
310
|
+
有官方锚定时 left/top 由 hook 注入,这里只兜住首帧测量前的 top:auto。 */
|
|
254
311
|
".ss-panel-header {",
|
|
255
312
|
" display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;",
|
|
256
313
|
"}",
|
|
@@ -419,15 +476,22 @@ var COMPONENTS = [
|
|
|
419
476
|
'.ss-presence[data-state="overdue"] .ss-presence-ring { animation: none; }',
|
|
420
477
|
"",
|
|
421
478
|
"/* \u65B0\u7248\u63CF\u8FB9\u65F6\u949F\uFF08modern\uFF09\u2014 \u5BF9\u9F50 DSH \u5B98\u65B9 spinner \u6837\u5F0F \u2500\u2500 */",
|
|
422
|
-
"
|
|
479
|
+
"/* \u53CC\u73AF\uFF1Atrack \u6052\u5B9A\u4E3A\u5E95\uFF08\u7ED9\u51FA\u300C\u603B\u7A97\u53E3\u300D\u7684\u89C6\u89C9\u53C2\u7167\uFF09\uFF0Cprog \u662F\u4ECE 12 \u70B9\u8D77\u7684\u8FDB\u5EA6\u5F27\u3002",
|
|
480
|
+
" \u65E0\u4ECE\u7B97\u8FDB\u5EA6\u65F6 JS \u628A prog \u7F6E\u4E3A display:none \u2014\u2014 \u6B64\u65F6\u89C6\u89C9\u4E0E\u6539\u9020\u524D\u7684\u5355\u73AF\u5B8C\u5168\u4E00\u81F4\u3002 */",
|
|
481
|
+
".ss-presence .ss-clock-track {",
|
|
423
482
|
" fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;",
|
|
424
483
|
" opacity: 0.25; animation: ss-clock-breathe 2.4s var(--ds-ease-in-out, ease-in-out) infinite;",
|
|
425
484
|
"}",
|
|
485
|
+
".ss-presence .ss-clock-prog {",
|
|
486
|
+
" fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;",
|
|
487
|
+
" opacity: 1; transition: stroke-dashoffset 0.5s var(--ds-ease-in-out, ease-in-out);",
|
|
488
|
+
"}",
|
|
426
489
|
".ss-presence .ss-clock-hand { stroke: currentColor; stroke-width: 2; stroke-linecap: round; }",
|
|
427
490
|
".ss-presence .ss-clock-hand-h { opacity: 1; }",
|
|
428
491
|
".ss-presence .ss-clock-hand-m { opacity: 0.85; }",
|
|
429
|
-
|
|
430
|
-
'.ss-presence[data-state="
|
|
492
|
+
"/* urgent \u53EA\u8109\u51B2\u5E95\u73AF\uFF1A\u8FDB\u5EA6\u5F27\u627F\u8F7D\u786E\u5B9A\u4FE1\u606F\uFF0C\u8DDF\u7740\u95EA\u4F1A\u8BA9\u6BD4\u4F8B\u4E0D\u53EF\u8BFB\u3002 */",
|
|
493
|
+
'.ss-presence[data-state="urgent"] .ss-clock-track { animation-name: ss-clock-pulse; animation-duration: 0.9s; }',
|
|
494
|
+
'.ss-presence[data-state="overdue"] .ss-clock-track { animation: none; opacity: 1; }',
|
|
431
495
|
"",
|
|
432
496
|
"/* \u2500\u2500 \u4E09\u6001\u989C\u8272 \u2500\u2500 */",
|
|
433
497
|
'.ss-presence[data-state="urgent"] { color: var(--dsw-alias-state-warn-secondary); }',
|
|
@@ -440,7 +504,9 @@ var COMPONENTS = [
|
|
|
440
504
|
"@keyframes ss-clock-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }",
|
|
441
505
|
"@media (prefers-reduced-motion: reduce) {",
|
|
442
506
|
" .ss-presence .ss-presence-ring { animation: none; }",
|
|
443
|
-
" .ss-presence .ss-clock-
|
|
507
|
+
" .ss-presence .ss-clock-track { animation: none; }",
|
|
508
|
+
' /* \u51CF\u5C11\u52A8\u6548\uFF1A\u8FDB\u5EA6\u5F27\u7684\u4F4D\u79FB\u8FC7\u6E21\u4E5F\u4E00\u5E76\u505C\uFF0C\u907F\u514D"\u8FDB\u5EA6\u81EA\u5DF1\u5728\u722C"\u7684\u89C2\u611F\u3002 */',
|
|
509
|
+
" .ss-presence .ss-clock-prog { transition: none; }",
|
|
444
510
|
"}",
|
|
445
511
|
"",
|
|
446
512
|
"/* ==================== \u60AC\u6D6E\u5361\u7247\u72B6\u6001\u884C\u589E\u8865\uFF08issue #4\uFF09 ==================== */",
|
|
@@ -619,7 +685,8 @@ function injectStyles() {
|
|
|
619
685
|
}
|
|
620
686
|
|
|
621
687
|
// src/client/components/SchedButton.tsx
|
|
622
|
-
var
|
|
688
|
+
var import_react7 = require("react");
|
|
689
|
+
var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
623
690
|
|
|
624
691
|
// src/time-utils.ts
|
|
625
692
|
function detectTimeZone() {
|
|
@@ -863,7 +930,9 @@ function useSchedT(locale) {
|
|
|
863
930
|
}
|
|
864
931
|
|
|
865
932
|
// src/client/components/SchedPanel.tsx
|
|
866
|
-
var
|
|
933
|
+
var import_react4 = require("react");
|
|
934
|
+
var import_react_dom = require("react-dom");
|
|
935
|
+
var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
867
936
|
|
|
868
937
|
// src/smart-window.ts
|
|
869
938
|
var DEFAULT_SMART_WINDOW = Object.freeze({
|
|
@@ -878,13 +947,13 @@ function isValidHhmm(value) {
|
|
|
878
947
|
return typeof value === "string" && HHMM_RE.test(value);
|
|
879
948
|
}
|
|
880
949
|
function sanitizeSmartWindowConfig(raw) {
|
|
881
|
-
const
|
|
950
|
+
const pick2 = (key, fallback) => isValidHhmm(raw?.[key]) ? raw[key] : fallback;
|
|
882
951
|
return {
|
|
883
|
-
workStart:
|
|
884
|
-
workEnd:
|
|
885
|
-
lunchStart:
|
|
886
|
-
lunchEnd:
|
|
887
|
-
eveningEnd:
|
|
952
|
+
workStart: pick2("workStart", DEFAULT_SMART_WINDOW.workStart),
|
|
953
|
+
workEnd: pick2("workEnd", DEFAULT_SMART_WINDOW.workEnd),
|
|
954
|
+
lunchStart: pick2("lunchStart", DEFAULT_SMART_WINDOW.lunchStart),
|
|
955
|
+
lunchEnd: pick2("lunchEnd", DEFAULT_SMART_WINDOW.lunchEnd),
|
|
956
|
+
eveningEnd: pick2("eveningEnd", DEFAULT_SMART_WINDOW.eveningEnd)
|
|
888
957
|
};
|
|
889
958
|
}
|
|
890
959
|
var SMART_MIN_DELAY_MS = 2 * 60 * 1e3;
|
|
@@ -1019,8 +1088,21 @@ function epochFromLocal(date, time, timeZone) {
|
|
|
1019
1088
|
if (hour > 23 || minute > 59 || second > 59 || month < 1 || month > 12 || day < 1 || day > 31) {
|
|
1020
1089
|
return void 0;
|
|
1021
1090
|
}
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1091
|
+
const localEpoch = Date.UTC(year, month - 1, day, hour, minute, second, 0);
|
|
1092
|
+
const offsets = /* @__PURE__ */ new Set();
|
|
1093
|
+
for (let delta = -2; delta <= 2; delta += 1) {
|
|
1094
|
+
offsets.add(offsetOf(localEpoch + delta * DAY_MS2, timeZone));
|
|
1095
|
+
}
|
|
1096
|
+
const candidates = [];
|
|
1097
|
+
for (const offset of offsets) {
|
|
1098
|
+
const candidate = localEpoch - offset;
|
|
1099
|
+
const f = localFieldsOf(candidate, timeZone);
|
|
1100
|
+
if (f.year === year && f.month === month && f.day === day && f.hour === hour && f.minute === minute && f.second === second) {
|
|
1101
|
+
candidates.push(candidate);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
candidates.sort((a, b) => a - b);
|
|
1105
|
+
return candidates[0];
|
|
1024
1106
|
}
|
|
1025
1107
|
function nextSmartAt(now, timeZone, window2 = DEFAULT_SMART_WINDOW) {
|
|
1026
1108
|
let target;
|
|
@@ -1046,6 +1128,7 @@ function nextSmartAt(now, timeZone, window2 = DEFAULT_SMART_WINDOW) {
|
|
|
1046
1128
|
}
|
|
1047
1129
|
|
|
1048
1130
|
// src/client/components/TaskList.tsx
|
|
1131
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1049
1132
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1050
1133
|
function scheduleStateOf(item, now) {
|
|
1051
1134
|
return Date.parse(item.scheduled_at) <= now ? "overdue" : "scheduled";
|
|
@@ -1079,7 +1162,7 @@ function TaskList({ items, now, deliveredIds, cancellingIds, onDelete, timeZone,
|
|
|
1079
1162
|
event.stopPropagation();
|
|
1080
1163
|
onDelete(item.id);
|
|
1081
1164
|
},
|
|
1082
|
-
children:
|
|
1165
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCloseOutlineRegular, { size: 14 })
|
|
1083
1166
|
}
|
|
1084
1167
|
)
|
|
1085
1168
|
] }, item.id);
|
|
@@ -1097,6 +1180,96 @@ function useNow(intervalMs = 1e3) {
|
|
|
1097
1180
|
return now;
|
|
1098
1181
|
}
|
|
1099
1182
|
|
|
1183
|
+
// src/client/primitives-compat.ts
|
|
1184
|
+
var import_react3 = require("react");
|
|
1185
|
+
var primitives = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
|
|
1186
|
+
function pick(name2) {
|
|
1187
|
+
return primitives[name2];
|
|
1188
|
+
}
|
|
1189
|
+
var hostUseAnchoredPosition = pick("useAnchoredPosition");
|
|
1190
|
+
var hostUseDismissOnOutsidePointer = pick("useDismissOnOutsidePointer");
|
|
1191
|
+
function useLegacyAnchoredPosition({
|
|
1192
|
+
open,
|
|
1193
|
+
anchorRef,
|
|
1194
|
+
panelRef,
|
|
1195
|
+
side = "bottom",
|
|
1196
|
+
align = "start",
|
|
1197
|
+
gap,
|
|
1198
|
+
margin
|
|
1199
|
+
}) {
|
|
1200
|
+
const [position, setPosition] = (0, import_react3.useState)(null);
|
|
1201
|
+
(0, import_react3.useLayoutEffect)(() => {
|
|
1202
|
+
if (!open) {
|
|
1203
|
+
setPosition(null);
|
|
1204
|
+
return void 0;
|
|
1205
|
+
}
|
|
1206
|
+
const measure = () => {
|
|
1207
|
+
const anchor = anchorRef.current;
|
|
1208
|
+
const panel2 = panelRef.current;
|
|
1209
|
+
if (anchor === null || panel2 === null) return;
|
|
1210
|
+
const rect = anchor.getBoundingClientRect();
|
|
1211
|
+
const panelWidth = panel2.offsetWidth;
|
|
1212
|
+
const panelHeight = panel2.offsetHeight;
|
|
1213
|
+
const viewportWidth = window.innerWidth;
|
|
1214
|
+
const viewportHeight = window.innerHeight;
|
|
1215
|
+
const left = align === "end" ? rect.right - panelWidth : rect.left;
|
|
1216
|
+
const rawTop = side === "top" ? rect.top - panelHeight - gap : rect.bottom + gap;
|
|
1217
|
+
const clampedLeft = Math.min(
|
|
1218
|
+
Math.max(margin, left),
|
|
1219
|
+
Math.max(margin, viewportWidth - panelWidth - margin)
|
|
1220
|
+
);
|
|
1221
|
+
const overflowsTop = rawTop < margin;
|
|
1222
|
+
const overflowsBottom = rawTop + panelHeight > viewportHeight - margin;
|
|
1223
|
+
const top = overflowsTop && !overflowsBottom ? rect.bottom + gap : overflowsBottom && !overflowsTop ? rect.top - panelHeight - gap : rawTop;
|
|
1224
|
+
setPosition({
|
|
1225
|
+
left: `${Math.round(clampedLeft)}px`,
|
|
1226
|
+
top: `${Math.round(Math.max(margin, Math.min(top, viewportHeight - panelHeight - margin)))}px`
|
|
1227
|
+
});
|
|
1228
|
+
};
|
|
1229
|
+
measure();
|
|
1230
|
+
window.addEventListener("scroll", measure, true);
|
|
1231
|
+
window.addEventListener("resize", measure);
|
|
1232
|
+
const panel = panelRef.current;
|
|
1233
|
+
const observer = typeof ResizeObserver === "undefined" || panel === null ? void 0 : new ResizeObserver(measure);
|
|
1234
|
+
if (observer !== void 0 && panel !== null) observer.observe(panel);
|
|
1235
|
+
return () => {
|
|
1236
|
+
window.removeEventListener("scroll", measure, true);
|
|
1237
|
+
window.removeEventListener("resize", measure);
|
|
1238
|
+
observer?.disconnect();
|
|
1239
|
+
};
|
|
1240
|
+
}, [open, anchorRef, panelRef, side, align, gap, margin]);
|
|
1241
|
+
return position;
|
|
1242
|
+
}
|
|
1243
|
+
function noopDismiss() {
|
|
1244
|
+
}
|
|
1245
|
+
var anchorImpl = hostUseAnchoredPosition ?? useLegacyAnchoredPosition;
|
|
1246
|
+
var dismissImpl = hostUseDismissOnOutsidePointer ?? noopDismiss;
|
|
1247
|
+
var hasOfficialOutsideDismiss = hostUseDismissOnOutsidePointer !== void 0;
|
|
1248
|
+
var hasOfficialAnchoredPosition = hostUseAnchoredPosition !== void 0;
|
|
1249
|
+
function usePanelAnchorPosition(options) {
|
|
1250
|
+
return anchorImpl(options);
|
|
1251
|
+
}
|
|
1252
|
+
function usePanelOutsideDismiss(root, open, setOpen, portal) {
|
|
1253
|
+
return dismissImpl(root, open, setOpen, portal);
|
|
1254
|
+
}
|
|
1255
|
+
var PANEL_MEASURE_STYLE = {
|
|
1256
|
+
visibility: "hidden",
|
|
1257
|
+
left: 0,
|
|
1258
|
+
top: 0
|
|
1259
|
+
};
|
|
1260
|
+
function useEscapeToClose(open, setOpen) {
|
|
1261
|
+
const setOpenRef = (0, import_react3.useRef)(setOpen);
|
|
1262
|
+
setOpenRef.current = setOpen;
|
|
1263
|
+
(0, import_react3.useEffect)(() => {
|
|
1264
|
+
if (!open) return void 0;
|
|
1265
|
+
const onKey = (event) => {
|
|
1266
|
+
if (event.key === "Escape") setOpenRef.current(false);
|
|
1267
|
+
};
|
|
1268
|
+
window.addEventListener("keydown", onKey, true);
|
|
1269
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
1270
|
+
}, [open]);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1100
1273
|
// src/client/components/SchedPanel.tsx
|
|
1101
1274
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1102
1275
|
function atOnDay(dayOffset, hhmm, timeZone, now) {
|
|
@@ -1119,22 +1292,36 @@ function SchedPanel({
|
|
|
1119
1292
|
onCreate,
|
|
1120
1293
|
onDelete,
|
|
1121
1294
|
bottom,
|
|
1295
|
+
anchorRef,
|
|
1296
|
+
panelRef: externalPanelRef,
|
|
1122
1297
|
smartWindow = DEFAULT_SMART_WINDOW,
|
|
1123
1298
|
promptLimits
|
|
1124
1299
|
}) {
|
|
1125
1300
|
const now = useNow(1e3);
|
|
1126
|
-
const
|
|
1127
|
-
const
|
|
1301
|
+
const localPanelRef = (0, import_react4.useRef)(null);
|
|
1302
|
+
const panelRef = externalPanelRef ?? localPanelRef;
|
|
1303
|
+
const anchored = usePanelAnchorPosition({
|
|
1304
|
+
open: true,
|
|
1305
|
+
anchorRef: anchorRef ?? { current: null },
|
|
1306
|
+
panelRef,
|
|
1307
|
+
side: "top",
|
|
1308
|
+
align: "start",
|
|
1309
|
+
gap: 4,
|
|
1310
|
+
margin: 12
|
|
1311
|
+
});
|
|
1312
|
+
const panelStyle = hasOfficialAnchoredPosition ? anchored ?? PANEL_MEASURE_STYLE : { bottom };
|
|
1313
|
+
const [selected, setSelected] = (0, import_react4.useState)("after-600");
|
|
1314
|
+
const [customDate, setCustomDate] = (0, import_react4.useState)(() => {
|
|
1128
1315
|
const f = localFieldsOf(Date.now(), timeZone);
|
|
1129
1316
|
return `${String(f.year).padStart(4, "0")}-${String(f.month).padStart(2, "0")}-${String(f.day).padStart(2, "0")}`;
|
|
1130
1317
|
});
|
|
1131
|
-
const [customTime, setCustomTime] = (0,
|
|
1132
|
-
const [error, setError] = (0,
|
|
1133
|
-
const [cancelling, setCancelling] = (0,
|
|
1134
|
-
const [delivered, setDelivered] = (0,
|
|
1135
|
-
const deliveredTimers = (0,
|
|
1136
|
-
const prevIds = (0,
|
|
1137
|
-
(0,
|
|
1318
|
+
const [customTime, setCustomTime] = (0, import_react4.useState)("18:00");
|
|
1319
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
1320
|
+
const [cancelling, setCancelling] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
|
|
1321
|
+
const [delivered, setDelivered] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
|
|
1322
|
+
const deliveredTimers = (0, import_react4.useRef)([]);
|
|
1323
|
+
const prevIds = (0, import_react4.useRef)(null);
|
|
1324
|
+
(0, import_react4.useEffect)(() => {
|
|
1138
1325
|
const currentIds = new Set(schedules.map((item) => item.id));
|
|
1139
1326
|
const previous = prevIds.current;
|
|
1140
1327
|
prevIds.current = currentIds;
|
|
@@ -1153,13 +1340,13 @@ function SchedPanel({
|
|
|
1153
1340
|
deliveredTimers.current.push(timer);
|
|
1154
1341
|
}
|
|
1155
1342
|
}, [schedules, cancelling]);
|
|
1156
|
-
(0,
|
|
1343
|
+
(0, import_react4.useEffect)(
|
|
1157
1344
|
() => () => {
|
|
1158
1345
|
for (const timer of deliveredTimers.current) window.clearTimeout(timer);
|
|
1159
1346
|
},
|
|
1160
1347
|
[]
|
|
1161
1348
|
);
|
|
1162
|
-
const quickOptions = (0,
|
|
1349
|
+
const quickOptions = (0, import_react4.useMemo)(() => {
|
|
1163
1350
|
const options = [
|
|
1164
1351
|
{ kind: "after", seconds: 600, label: t.quick10m },
|
|
1165
1352
|
{ kind: "after", seconds: 3600, label: t.quick1h }
|
|
@@ -1184,7 +1371,7 @@ function SchedPanel({
|
|
|
1184
1371
|
options.push({ kind: "custom", label: t.quickCustom });
|
|
1185
1372
|
return options;
|
|
1186
1373
|
}, [timeZone, now, t, smartWindow]);
|
|
1187
|
-
const target = (0,
|
|
1374
|
+
const target = (0, import_react4.useMemo)(() => {
|
|
1188
1375
|
if (selected === "custom") {
|
|
1189
1376
|
const match = /^(\d{2}):(\d{2})$/.exec(customTime);
|
|
1190
1377
|
if (match === null) return null;
|
|
@@ -1197,7 +1384,7 @@ function SchedPanel({
|
|
|
1197
1384
|
if (option.kind === "after") return { after_seconds: option.seconds, epoch: now + option.seconds * 1e3 };
|
|
1198
1385
|
return { at: option.at, epoch: option.epoch };
|
|
1199
1386
|
}, [selected, customDate, customTime, timeZone, now, quickOptions]);
|
|
1200
|
-
const targetLabel = (0,
|
|
1387
|
+
const targetLabel = (0, import_react4.useMemo)(() => {
|
|
1201
1388
|
if (target === void 0 || target === null) return null;
|
|
1202
1389
|
return `${formatAbsolute(target.epoch, timeZone, lang)} \xB7 ${formatRelative(target.epoch, now, lang)}`;
|
|
1203
1390
|
}, [target, timeZone, now]);
|
|
@@ -1245,87 +1432,101 @@ function SchedPanel({
|
|
|
1245
1432
|
};
|
|
1246
1433
|
const promptEmpty = defaultPrompt.trim().length === 0;
|
|
1247
1434
|
const canConfirm = !busy && !promptEmpty && target !== null;
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
"aria-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1435
|
+
const panel = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1436
|
+
"div",
|
|
1437
|
+
{
|
|
1438
|
+
ref: panelRef,
|
|
1439
|
+
className: "ss-panel",
|
|
1440
|
+
"data-ss-panel": "",
|
|
1441
|
+
role: "dialog",
|
|
1442
|
+
"aria-label": t.panelTitle,
|
|
1443
|
+
style: panelStyle,
|
|
1444
|
+
onClick: (event) => event.stopPropagation(),
|
|
1445
|
+
children: [
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ss-panel-header", children: [
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ss-panel-title", children: t.panelTitle }),
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "ss-panel-close", "aria-label": t.close, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconCloseOutlineRegular, { size: 14 }) })
|
|
1449
|
+
] }),
|
|
1450
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-quick-grid", role: "radiogroup", "aria-label": t.panelTitle, children: quickOptions.map((option) => {
|
|
1451
|
+
const key = keyOf(option);
|
|
1452
|
+
const active = selected === key || option.kind === "custom" && selected === "custom";
|
|
1453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1454
|
+
"button",
|
|
1455
|
+
{
|
|
1456
|
+
type: "button",
|
|
1457
|
+
role: "radio",
|
|
1458
|
+
"aria-checked": active,
|
|
1459
|
+
className: active ? "ss-quick-chip ss-active" : "ss-quick-chip",
|
|
1460
|
+
onClick: () => {
|
|
1461
|
+
setSelected(key);
|
|
1462
|
+
setError(null);
|
|
1463
|
+
},
|
|
1464
|
+
children: option.label
|
|
1465
|
+
},
|
|
1466
|
+
key
|
|
1467
|
+
);
|
|
1468
|
+
}) }),
|
|
1469
|
+
selected === "custom" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ss-custom-row", children: [
|
|
1470
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1471
|
+
"input",
|
|
1472
|
+
{
|
|
1473
|
+
type: "date",
|
|
1474
|
+
className: "ss-input",
|
|
1475
|
+
"aria-label": t.dateLabel,
|
|
1476
|
+
value: customDate,
|
|
1477
|
+
onChange: (event) => {
|
|
1478
|
+
if (event.target.value) setCustomDate(event.target.value);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1483
|
+
"input",
|
|
1484
|
+
{
|
|
1485
|
+
type: "time",
|
|
1486
|
+
className: "ss-input",
|
|
1487
|
+
"aria-label": t.timeLabel,
|
|
1488
|
+
value: customTime,
|
|
1489
|
+
onChange: (event) => {
|
|
1490
|
+
if (event.target.value) setCustomTime(event.target.value);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
)
|
|
1494
|
+
] }),
|
|
1495
|
+
target !== null && targetLabel !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-hint", style: { margin: "6px 0 0" }, children: targetLabel }),
|
|
1496
|
+
error !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-error", role: "alert", style: { marginTop: 6 }, children: error }),
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-tasklist-head", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "ss-hint", children: [
|
|
1498
|
+
t.taskListTitle,
|
|
1499
|
+
schedules.length > 0 ? ` (${schedules.length})` : ""
|
|
1500
|
+
] }) }),
|
|
1501
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1502
|
+
TaskList,
|
|
1503
|
+
{
|
|
1504
|
+
items: schedules,
|
|
1505
|
+
now,
|
|
1506
|
+
deliveredIds: delivered,
|
|
1507
|
+
cancellingIds: cancelling,
|
|
1508
|
+
onDelete: handleDelete,
|
|
1509
|
+
timeZone,
|
|
1510
|
+
t,
|
|
1511
|
+
lang
|
|
1282
1512
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
onChange: (event) => {
|
|
1293
|
-
if (event.target.value) setCustomTime(event.target.value);
|
|
1513
|
+
),
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-footer", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1515
|
+
"button",
|
|
1516
|
+
{
|
|
1517
|
+
type: "button",
|
|
1518
|
+
className: "ss-confirm",
|
|
1519
|
+
disabled: !canConfirm,
|
|
1520
|
+
onClick: () => void handleConfirm(),
|
|
1521
|
+
children: busy ? t.sending : target !== null ? format(t.confirmAdd, { time: formatAbsolute(target.epoch, timeZone, lang) }) : t.confirmAddNoTime
|
|
1294
1522
|
}
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
schedules.length > 0 ? ` (${schedules.length})` : ""
|
|
1303
|
-
] }) }),
|
|
1304
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1305
|
-
TaskList,
|
|
1306
|
-
{
|
|
1307
|
-
items: schedules,
|
|
1308
|
-
now,
|
|
1309
|
-
deliveredIds: delivered,
|
|
1310
|
-
cancellingIds: cancelling,
|
|
1311
|
-
onDelete: handleDelete,
|
|
1312
|
-
timeZone,
|
|
1313
|
-
t,
|
|
1314
|
-
lang
|
|
1315
|
-
}
|
|
1316
|
-
),
|
|
1317
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-footer", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1318
|
-
"button",
|
|
1319
|
-
{
|
|
1320
|
-
type: "button",
|
|
1321
|
-
className: "ss-confirm",
|
|
1322
|
-
disabled: !canConfirm,
|
|
1323
|
-
onClick: () => void handleConfirm(),
|
|
1324
|
-
children: busy ? t.sending : target !== null ? format(t.confirmAdd, { time: formatAbsolute(target.epoch, timeZone, lang) }) : t.confirmAddNoTime
|
|
1325
|
-
}
|
|
1326
|
-
) }),
|
|
1327
|
-
promptEmpty && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-hint", style: { marginTop: 6 }, children: t.promptFromDraft })
|
|
1328
|
-
] });
|
|
1523
|
+
) }),
|
|
1524
|
+
promptEmpty && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ss-hint", style: { marginTop: 6 }, children: t.promptFromDraft })
|
|
1525
|
+
]
|
|
1526
|
+
}
|
|
1527
|
+
);
|
|
1528
|
+
if (typeof document === "undefined") return panel;
|
|
1529
|
+
return (0, import_react_dom.createPortal)(panel, document.body);
|
|
1329
1530
|
}
|
|
1330
1531
|
function keyOf(option) {
|
|
1331
1532
|
switch (option.kind) {
|
|
@@ -1341,7 +1542,7 @@ function keyOf(option) {
|
|
|
1341
1542
|
}
|
|
1342
1543
|
|
|
1343
1544
|
// src/client/hooks/useSchedules.ts
|
|
1344
|
-
var
|
|
1545
|
+
var import_react5 = require("react");
|
|
1345
1546
|
|
|
1346
1547
|
// src/client/local-store.ts
|
|
1347
1548
|
var STORAGE_KEY = "dsh-later:pending";
|
|
@@ -1394,28 +1595,28 @@ var createLine = (input) => {
|
|
|
1394
1595
|
var deleteLine = (id) => `/user-schedule-delete ${JSON.stringify({ id })}`;
|
|
1395
1596
|
function useSchedules(inject2) {
|
|
1396
1597
|
const { callCommand, sessionId, inputActions, input, useProjection, useInput } = inject2;
|
|
1397
|
-
const [busy, setBusy] = (0,
|
|
1398
|
-
const [anchorBottom, setAnchorBottom] = (0,
|
|
1399
|
-
const [pendingSync, setPendingSync] = (0,
|
|
1400
|
-
const rowRef = (0,
|
|
1598
|
+
const [busy, setBusy] = (0, import_react5.useState)(false);
|
|
1599
|
+
const [anchorBottom, setAnchorBottom] = (0, import_react5.useState)(80);
|
|
1600
|
+
const [pendingSync, setPendingSync] = (0, import_react5.useState)(() => readPending());
|
|
1601
|
+
const rowRef = (0, import_react5.useRef)(null);
|
|
1401
1602
|
const now = useNow(1e3);
|
|
1402
1603
|
const draft = input?.draft ?? (typeof useInput === "function" ? useInput((s) => s.draft) : "") ?? "";
|
|
1403
1604
|
const projection = typeof useProjection === "function" ? useProjection("userSchedules") : void 0;
|
|
1404
|
-
const schedules = (0,
|
|
1405
|
-
const nextEpoch = (0,
|
|
1605
|
+
const schedules = (0, import_react5.useMemo)(() => schedulesOf(projection), [projection]);
|
|
1606
|
+
const nextEpoch = (0, import_react5.useMemo)(() => {
|
|
1406
1607
|
return schedules.map((item) => Date.parse(item.scheduled_at)).filter((value) => !Number.isNaN(value)).sort((a, b) => a - b)[0];
|
|
1407
1608
|
}, [schedules]);
|
|
1408
1609
|
const enabled = draft.trim().length > 0 || schedules.length > 0;
|
|
1409
|
-
const reposition = (0,
|
|
1610
|
+
const reposition = (0, import_react5.useCallback)(() => {
|
|
1410
1611
|
const el = rowRef.current;
|
|
1411
1612
|
if (el === null) return;
|
|
1412
1613
|
const rect = el.getBoundingClientRect();
|
|
1413
1614
|
setAnchorBottom(Math.max(12, window.innerHeight - rect.top + 10));
|
|
1414
1615
|
}, []);
|
|
1415
|
-
const syncedPrompts = (0,
|
|
1616
|
+
const syncedPrompts = (0, import_react5.useMemo)(() => {
|
|
1416
1617
|
return new Set(schedules.map((item) => item.prompt));
|
|
1417
1618
|
}, [schedules]);
|
|
1418
|
-
(0,
|
|
1619
|
+
(0, import_react5.useEffect)(() => {
|
|
1419
1620
|
if (pendingSync.length === 0) return;
|
|
1420
1621
|
const remaining = pendingSync.filter((item) => !syncedPrompts.has(item.payload.prompt));
|
|
1421
1622
|
if (remaining.length !== pendingSync.length) {
|
|
@@ -1423,7 +1624,7 @@ function useSchedules(inject2) {
|
|
|
1423
1624
|
writePendingLocal(remaining);
|
|
1424
1625
|
}
|
|
1425
1626
|
}, [syncedPrompts, pendingSync]);
|
|
1426
|
-
(0,
|
|
1627
|
+
(0, import_react5.useEffect)(() => {
|
|
1427
1628
|
if (pendingSync.length === 0 || !sessionId) return;
|
|
1428
1629
|
let cancelled = false;
|
|
1429
1630
|
const syncAll = async () => {
|
|
@@ -1451,7 +1652,7 @@ function useSchedules(inject2) {
|
|
|
1451
1652
|
window.removeEventListener("online", onOnline);
|
|
1452
1653
|
};
|
|
1453
1654
|
}, [pendingSync, sessionId, callCommand]);
|
|
1454
|
-
const handleCreate = (0,
|
|
1655
|
+
const handleCreate = (0, import_react5.useCallback)(async (inputPayload) => {
|
|
1455
1656
|
if (!sessionId) {
|
|
1456
1657
|
throw new Error(CLIENT_ERR.NO_SESSION);
|
|
1457
1658
|
}
|
|
@@ -1467,15 +1668,15 @@ function useSchedules(inject2) {
|
|
|
1467
1668
|
setBusy(false);
|
|
1468
1669
|
}
|
|
1469
1670
|
}, [sessionId, callCommand]);
|
|
1470
|
-
const handleDelete = (0,
|
|
1671
|
+
const handleDelete = (0, import_react5.useCallback)(async (id) => {
|
|
1471
1672
|
if (!sessionId) return;
|
|
1472
1673
|
await callCommand(sessionId, deleteLine(id));
|
|
1473
1674
|
}, [sessionId, callCommand]);
|
|
1474
|
-
const handleCancelAll = (0,
|
|
1675
|
+
const handleCancelAll = (0, import_react5.useCallback)(async () => {
|
|
1475
1676
|
if (!sessionId) return;
|
|
1476
1677
|
await Promise.all(schedules.map((item) => callCommand(sessionId, deleteLine(item.id))));
|
|
1477
1678
|
}, [sessionId, schedules, callCommand]);
|
|
1478
|
-
const handleClearDraft = (0,
|
|
1679
|
+
const handleClearDraft = (0, import_react5.useCallback)(() => {
|
|
1479
1680
|
inputActions?.setDraft("");
|
|
1480
1681
|
}, [inputActions]);
|
|
1481
1682
|
return {
|
|
@@ -1503,7 +1704,7 @@ function writePendingLocal(pending) {
|
|
|
1503
1704
|
}
|
|
1504
1705
|
|
|
1505
1706
|
// src/client/hooks/useSchedulerSettings.ts
|
|
1506
|
-
var
|
|
1707
|
+
var import_react6 = require("react");
|
|
1507
1708
|
var CLIENT_DEFAULT_MAX_PROMPT_CHARS = 1e3;
|
|
1508
1709
|
var FALLBACK_PROMPT = {
|
|
1509
1710
|
allowLong: false,
|
|
@@ -1547,16 +1748,16 @@ function resolveFromSnapshot(snap) {
|
|
|
1547
1748
|
var NOOP_SUBSCRIBE = () => () => void 0;
|
|
1548
1749
|
var EMPTY_SNAPSHOT = { status: "unavailable" };
|
|
1549
1750
|
function useSchedulerSettings(scope) {
|
|
1550
|
-
const getSnapshot = (0,
|
|
1751
|
+
const getSnapshot = (0, import_react6.useCallback)(
|
|
1551
1752
|
() => scope === void 0 ? EMPTY_SNAPSHOT : scope.getSnapshot(),
|
|
1552
1753
|
[scope]
|
|
1553
1754
|
);
|
|
1554
|
-
const subscribe = (0,
|
|
1755
|
+
const subscribe = (0, import_react6.useCallback)(
|
|
1555
1756
|
(onStoreChange) => scope === void 0 ? NOOP_SUBSCRIBE() : scope.subscribe(onStoreChange),
|
|
1556
1757
|
[scope]
|
|
1557
1758
|
);
|
|
1558
|
-
const raw = (0,
|
|
1559
|
-
return (0,
|
|
1759
|
+
const raw = (0, import_react6.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
1760
|
+
return (0, import_react6.useMemo)(() => resolveFromSnapshot(raw), [raw]);
|
|
1560
1761
|
}
|
|
1561
1762
|
|
|
1562
1763
|
// src/client/components/SchedButton.tsx
|
|
@@ -1565,7 +1766,7 @@ function SchedButton(props) {
|
|
|
1565
1766
|
const { callCommand, sessionId, inputActions, input, useProjection, useInput, locale, schedulerScope } = props;
|
|
1566
1767
|
const { lang, t } = useSchedT(locale);
|
|
1567
1768
|
const { smartWindow, promptLimits, showButton } = useSchedulerSettings(schedulerScope);
|
|
1568
|
-
const [open, setOpen] = (0,
|
|
1769
|
+
const [open, setOpen] = (0, import_react7.useState)(false);
|
|
1569
1770
|
const {
|
|
1570
1771
|
draft,
|
|
1571
1772
|
schedules,
|
|
@@ -1584,19 +1785,15 @@ function SchedButton(props) {
|
|
|
1584
1785
|
useProjection,
|
|
1585
1786
|
useInput
|
|
1586
1787
|
});
|
|
1587
|
-
(
|
|
1588
|
-
|
|
1788
|
+
useEscapeToClose(open, setOpen);
|
|
1789
|
+
(0, import_react7.useEffect)(() => {
|
|
1790
|
+
if (!open) return void 0;
|
|
1589
1791
|
reposition();
|
|
1590
1792
|
window.addEventListener("resize", reposition);
|
|
1591
|
-
|
|
1592
|
-
if (event.key === "Escape") setOpen(false);
|
|
1593
|
-
};
|
|
1594
|
-
window.addEventListener("keydown", onKey, true);
|
|
1595
|
-
return () => {
|
|
1596
|
-
window.removeEventListener("resize", reposition);
|
|
1597
|
-
window.removeEventListener("keydown", onKey, true);
|
|
1598
|
-
};
|
|
1793
|
+
return () => window.removeEventListener("resize", reposition);
|
|
1599
1794
|
}, [open, reposition]);
|
|
1795
|
+
const panelRef = (0, import_react7.useRef)(null);
|
|
1796
|
+
usePanelOutsideDismiss(rowRef, open, setOpen, panelRef);
|
|
1600
1797
|
if (!showButton) return null;
|
|
1601
1798
|
const handleCreateWithErrorBoundary = async (inputPayload) => {
|
|
1602
1799
|
try {
|
|
@@ -1617,14 +1814,11 @@ function SchedButton(props) {
|
|
|
1617
1814
|
"aria-expanded": open,
|
|
1618
1815
|
disabled: !enabled,
|
|
1619
1816
|
onClick: () => setOpen((value) => !value),
|
|
1620
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1621
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1622
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "12 6 12 12 16 14" })
|
|
1623
|
-
] })
|
|
1817
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.IconClockOutlineRegular, { size: 16 })
|
|
1624
1818
|
}
|
|
1625
1819
|
),
|
|
1626
1820
|
open && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1627
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ss-backdrop", onClick: () => setOpen(false), "aria-hidden": "true" }),
|
|
1821
|
+
!hasOfficialOutsideDismiss && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ss-backdrop", onClick: () => setOpen(false), "aria-hidden": "true" }),
|
|
1628
1822
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1629
1823
|
SchedPanel,
|
|
1630
1824
|
{
|
|
@@ -1638,6 +1832,8 @@ function SchedButton(props) {
|
|
|
1638
1832
|
onCreate: handleCreateWithErrorBoundary,
|
|
1639
1833
|
onDelete: (id) => void handleDelete(id),
|
|
1640
1834
|
bottom: anchorBottom,
|
|
1835
|
+
anchorRef: rowRef,
|
|
1836
|
+
panelRef,
|
|
1641
1837
|
smartWindow,
|
|
1642
1838
|
promptLimits
|
|
1643
1839
|
}
|
|
@@ -1647,7 +1843,9 @@ function SchedButton(props) {
|
|
|
1647
1843
|
}
|
|
1648
1844
|
|
|
1649
1845
|
// src/client/components/ScheduleDock.tsx
|
|
1650
|
-
var
|
|
1846
|
+
var import_react8 = require("react");
|
|
1847
|
+
var import_react_dom2 = require("react-dom");
|
|
1848
|
+
var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1651
1849
|
|
|
1652
1850
|
// src/bar-geometry.ts
|
|
1653
1851
|
var BAR_UNIT_MS = 36e5;
|
|
@@ -1758,8 +1956,8 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1758
1956
|
const now = useNow(1e3);
|
|
1759
1957
|
const { t } = useSchedT(locale);
|
|
1760
1958
|
const projection = typeof useProjection === "function" ? useProjection("userSchedules") : void 0;
|
|
1761
|
-
const [dismissedPaused, setDismissedPaused] = (0,
|
|
1762
|
-
(0,
|
|
1959
|
+
const [dismissedPaused, setDismissedPaused] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
1960
|
+
(0, import_react8.useEffect)(() => {
|
|
1763
1961
|
const live = projection?.schedules;
|
|
1764
1962
|
if (live === void 0) return;
|
|
1765
1963
|
setDismissedPaused((current) => {
|
|
@@ -1772,8 +1970,8 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1772
1970
|
return changed ? nextSet : current;
|
|
1773
1971
|
});
|
|
1774
1972
|
}, [projection]);
|
|
1775
|
-
const [promptOverrides, setPromptOverrides] = (0,
|
|
1776
|
-
(0,
|
|
1973
|
+
const [promptOverrides, setPromptOverrides] = (0, import_react8.useState)(/* @__PURE__ */ new Map());
|
|
1974
|
+
(0, import_react8.useEffect)(() => {
|
|
1777
1975
|
const live = projection?.schedules;
|
|
1778
1976
|
if (live === void 0 || promptOverrides.size === 0) return;
|
|
1779
1977
|
setPromptOverrides((current) => {
|
|
@@ -1790,7 +1988,7 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1790
1988
|
return changed ? nextMap : current;
|
|
1791
1989
|
});
|
|
1792
1990
|
}, [projection, promptOverrides]);
|
|
1793
|
-
const schedules = (0,
|
|
1991
|
+
const schedules = (0, import_react8.useMemo)(
|
|
1794
1992
|
() => (projection?.schedules ?? []).filter((item) => !(item.status === "paused" && dismissedPaused.has(item.id))).map((item) => {
|
|
1795
1993
|
const override = promptOverrides.get(item.id);
|
|
1796
1994
|
return override === void 0 ? item : { ...item, prompt: override };
|
|
@@ -1799,17 +1997,17 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1799
1997
|
),
|
|
1800
1998
|
[projection, dismissedPaused, promptOverrides]
|
|
1801
1999
|
);
|
|
1802
|
-
const [collapsed, setCollapsed] = (0,
|
|
1803
|
-
const [cancelling, setCancelling] = (0,
|
|
1804
|
-
const [editingId, setEditingId] = (0,
|
|
1805
|
-
const [editingText, setEditingText] = (0,
|
|
1806
|
-
const [editErr, setEditErr] = (0,
|
|
1807
|
-
const [saving, setSaving] = (0,
|
|
1808
|
-
const [steering, setSteering] = (0,
|
|
1809
|
-
const [steerDone, setSteerDone] = (0,
|
|
1810
|
-
const [steerErr, setSteerErr] = (0,
|
|
1811
|
-
const [steerUnconfirmed, setSteerUnconfirmed] = (0,
|
|
1812
|
-
(0,
|
|
2000
|
+
const [collapsed, setCollapsed] = (0, import_react8.useState)(true);
|
|
2001
|
+
const [cancelling, setCancelling] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2002
|
+
const [editingId, setEditingId] = (0, import_react8.useState)(null);
|
|
2003
|
+
const [editingText, setEditingText] = (0, import_react8.useState)("");
|
|
2004
|
+
const [editErr, setEditErr] = (0, import_react8.useState)(null);
|
|
2005
|
+
const [saving, setSaving] = (0, import_react8.useState)(false);
|
|
2006
|
+
const [steering, setSteering] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2007
|
+
const [steerDone, setSteerDone] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2008
|
+
const [steerErr, setSteerErr] = (0, import_react8.useState)(/* @__PURE__ */ new Map());
|
|
2009
|
+
const [steerUnconfirmed, setSteerUnconfirmed] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2010
|
+
(0, import_react8.useEffect)(() => {
|
|
1813
2011
|
const live = projection?.schedules;
|
|
1814
2012
|
if (live === void 0) return;
|
|
1815
2013
|
const present = new Set(live.filter((item) => item.status !== "paused").map((item) => item.id));
|
|
@@ -1826,7 +2024,7 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1826
2024
|
return changed ? next2 : current;
|
|
1827
2025
|
});
|
|
1828
2026
|
}, [projection]);
|
|
1829
|
-
(0,
|
|
2027
|
+
(0, import_react8.useEffect)(() => {
|
|
1830
2028
|
if (steerDone.size === 0) return void 0;
|
|
1831
2029
|
const timer = window.setTimeout(() => {
|
|
1832
2030
|
const live = projection?.schedules;
|
|
@@ -1847,11 +2045,11 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1847
2045
|
}, STEER_CONFIRM_WINDOW_MS);
|
|
1848
2046
|
return () => window.clearTimeout(timer);
|
|
1849
2047
|
}, [steerDone, projection]);
|
|
1850
|
-
const [pausing, setPausing] = (0,
|
|
1851
|
-
const [resuming, setResuming] = (0,
|
|
1852
|
-
const [pauseErr, setPauseErr] = (0,
|
|
1853
|
-
const firstSeenRef = (0,
|
|
1854
|
-
const lastActiveAtRef = (0,
|
|
2048
|
+
const [pausing, setPausing] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2049
|
+
const [resuming, setResuming] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2050
|
+
const [pauseErr, setPauseErr] = (0, import_react8.useState)(/* @__PURE__ */ new Map());
|
|
2051
|
+
const firstSeenRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
2052
|
+
const lastActiveAtRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
1855
2053
|
const wallClock = Date.now();
|
|
1856
2054
|
{
|
|
1857
2055
|
const next2 = new Map(lastActiveAtRef.current);
|
|
@@ -1872,8 +2070,8 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1872
2070
|
}
|
|
1873
2071
|
if (changed) lastActiveAtRef.current = next2;
|
|
1874
2072
|
}
|
|
1875
|
-
const byId = (0,
|
|
1876
|
-
(0,
|
|
2073
|
+
const byId = (0, import_react8.useMemo)(() => new Map(schedules.map((item) => [item.id, item])), [schedules]);
|
|
2074
|
+
(0, import_react8.useEffect)(() => {
|
|
1877
2075
|
let changed = false;
|
|
1878
2076
|
const next2 = new Map(firstSeenRef.current);
|
|
1879
2077
|
for (const [id] of byId) {
|
|
@@ -1890,7 +2088,7 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1890
2088
|
}
|
|
1891
2089
|
if (changed) firstSeenRef.current = next2;
|
|
1892
2090
|
}, [byId]);
|
|
1893
|
-
(0,
|
|
2091
|
+
(0, import_react8.useEffect)(() => {
|
|
1894
2092
|
if (schedules.length <= 1) setCollapsed(true);
|
|
1895
2093
|
}, [schedules.length]);
|
|
1896
2094
|
if (schedules.length === 0 || sessionId === void 0) return null;
|
|
@@ -2072,7 +2270,7 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
2072
2270
|
children: saving ? "\u2026" : t.editSave
|
|
2073
2271
|
}
|
|
2074
2272
|
),
|
|
2075
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "ss-dock-action", onClick: cancelEdit, "aria-label": t.editCancel, children:
|
|
2273
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "ss-dock-action", onClick: cancelEdit, "aria-label": t.editCancel, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconCloseOutlineRegular, { size: 14 }) }),
|
|
2076
2274
|
editErr !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-edit-err", children: editErr })
|
|
2077
2275
|
] }, item.id);
|
|
2078
2276
|
}
|
|
@@ -2099,63 +2297,58 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
2099
2297
|
] })
|
|
2100
2298
|
] }),
|
|
2101
2299
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ss-dock-actions", children: [
|
|
2102
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2300
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DockTip, { label: t.editTask, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2103
2301
|
"button",
|
|
2104
2302
|
{
|
|
2105
2303
|
type: "button",
|
|
2106
2304
|
className: "ss-dock-action",
|
|
2107
|
-
title: t.editTask,
|
|
2108
2305
|
"aria-label": t.editTask,
|
|
2109
2306
|
onClick: () => startEdit(item),
|
|
2110
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2307
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconEditOutlineRegular, { size: 14 })
|
|
2111
2308
|
}
|
|
2112
|
-
),
|
|
2113
|
-
isPaused ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2309
|
+
) }),
|
|
2310
|
+
isPaused ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DockTip, { label: t.resumeTask, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2114
2311
|
"button",
|
|
2115
2312
|
{
|
|
2116
2313
|
type: "button",
|
|
2117
2314
|
className: `ss-dock-action ss-paused-active${resuming.has(item.id) ? " ss-pending" : ""}`,
|
|
2118
|
-
title: t.resumeTask,
|
|
2119
2315
|
"aria-label": t.resumeTask,
|
|
2120
2316
|
disabled: resuming.has(item.id),
|
|
2121
2317
|
onClick: () => handleResume(item),
|
|
2122
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2318
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconPlayOutlineRegular, { size: 16 })
|
|
2123
2319
|
}
|
|
2124
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2320
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DockTip, { label: canPause(item, now) ? t.pauseTask : t.pauseUnsupportedKind, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2125
2321
|
"button",
|
|
2126
2322
|
{
|
|
2127
2323
|
type: "button",
|
|
2128
2324
|
className: `ss-dock-action${pausing.has(item.id) ? " ss-pending" : ""}`,
|
|
2129
|
-
title: canPause(item, now) ? t.pauseTask : t.pauseUnsupportedKind,
|
|
2130
2325
|
"aria-label": canPause(item, now) ? t.pauseTask : t.pauseUnsupportedKind,
|
|
2131
2326
|
disabled: !canPause(item, now) || pausing.has(item.id),
|
|
2132
2327
|
onClick: () => handlePause(item),
|
|
2133
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2328
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconPauseOutlineRegular, { size: 16 })
|
|
2134
2329
|
}
|
|
2135
|
-
),
|
|
2136
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2330
|
+
) }),
|
|
2331
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DockTip, { label: t.deleteTask, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2137
2332
|
"button",
|
|
2138
2333
|
{
|
|
2139
2334
|
type: "button",
|
|
2140
2335
|
className: "ss-dock-action",
|
|
2141
|
-
title: t.deleteTask,
|
|
2142
2336
|
"aria-label": t.deleteTask,
|
|
2143
2337
|
onClick: () => handleCancel(item.id, isPaused ? "paused" : "active"),
|
|
2144
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2338
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconTrashOutlineRegular, { size: 14 })
|
|
2145
2339
|
}
|
|
2146
|
-
),
|
|
2147
|
-
item.kind !== "every" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2340
|
+
) }),
|
|
2341
|
+
item.kind !== "every" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DockTip, { label: steerDone.has(item.id) ? t.steerAlreadySent : t.steerTask, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2148
2342
|
"button",
|
|
2149
2343
|
{
|
|
2150
2344
|
type: "button",
|
|
2151
2345
|
className: `ss-dock-action ss-dock-action-steer${steering.has(item.id) ? " ss-pending" : ""}${steerDone.has(item.id) ? " ss-done" : ""}`,
|
|
2152
|
-
title: steerDone.has(item.id) ? t.steerAlreadySent : t.steerTask,
|
|
2153
2346
|
"aria-label": steerDone.has(item.id) ? t.steerAlreadySent : t.steerTask,
|
|
2154
2347
|
disabled: steering.has(item.id) || steerDone.has(item.id),
|
|
2155
2348
|
onClick: () => handleSteer(item),
|
|
2156
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2349
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconSendOutlineRegular, { size: 16 })
|
|
2157
2350
|
}
|
|
2158
|
-
)
|
|
2351
|
+
) })
|
|
2159
2352
|
] })
|
|
2160
2353
|
]
|
|
2161
2354
|
},
|
|
@@ -2171,9 +2364,9 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
2171
2364
|
"aria-expanded": !collapsed,
|
|
2172
2365
|
onClick: () => setCollapsed((value) => !value),
|
|
2173
2366
|
children: [
|
|
2174
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-lead", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2367
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-lead", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconClockOutlineRegular, { size: 16 }) }),
|
|
2175
2368
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-count", children: format(t.panelCount, { n: schedules.length }) }),
|
|
2176
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: expanded ? "ss-dock-chevron ss-open" : "ss-dock-chevron", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: expanded ? "ss-dock-chevron ss-open" : "ss-dock-chevron", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconChevronDownOutlineRegular, { size: 14 }) })
|
|
2177
2370
|
]
|
|
2178
2371
|
}
|
|
2179
2372
|
),
|
|
@@ -2192,7 +2385,7 @@ function MultiBar({
|
|
|
2192
2385
|
const frozenSeg = frozen ? frozenBarSegments(item, frozenLeftMs ?? 0, firstSeen, renderedAt) : void 0;
|
|
2193
2386
|
const activeSeg = frozen ? void 0 : barSegments(item, now, firstSeen);
|
|
2194
2387
|
const { totalRatio, count } = frozenSeg ?? activeSeg ?? { totalRatio: 0, count: 1 };
|
|
2195
|
-
const phaseAtRef = (0,
|
|
2388
|
+
const phaseAtRef = (0, import_react8.useRef)(null);
|
|
2196
2389
|
if (frozen || prefersReducedMotion()) {
|
|
2197
2390
|
phaseAtRef.current = null;
|
|
2198
2391
|
} else if (phaseAtRef.current === null) {
|
|
@@ -2236,33 +2429,76 @@ function MultiBar({
|
|
|
2236
2429
|
}
|
|
2237
2430
|
);
|
|
2238
2431
|
}
|
|
2239
|
-
function
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2432
|
+
function DockTip({ label, children }) {
|
|
2433
|
+
const anchorRef = (0, import_react8.useRef)(null);
|
|
2434
|
+
const bubbleRef = (0, import_react8.useRef)(null);
|
|
2435
|
+
const [pos, setPos] = (0, import_react8.useState)(null);
|
|
2436
|
+
const timerRef = (0, import_react8.useRef)(null);
|
|
2437
|
+
const cancel = () => {
|
|
2438
|
+
if (timerRef.current !== null) {
|
|
2439
|
+
window.clearTimeout(timerRef.current);
|
|
2440
|
+
timerRef.current = null;
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
const show = () => {
|
|
2444
|
+
const el = anchorRef.current;
|
|
2445
|
+
if (!el) return;
|
|
2446
|
+
const r = el.getBoundingClientRect();
|
|
2447
|
+
setPos({ left: r.left + r.width / 2, top: r.bottom + 8, side: "bottom" });
|
|
2448
|
+
};
|
|
2449
|
+
const enter = () => {
|
|
2450
|
+
cancel();
|
|
2451
|
+
timerRef.current = window.setTimeout(show, 500);
|
|
2452
|
+
};
|
|
2453
|
+
const leave = () => {
|
|
2454
|
+
cancel();
|
|
2455
|
+
setPos(null);
|
|
2456
|
+
};
|
|
2457
|
+
(0, import_react8.useEffect)(() => cancel, []);
|
|
2458
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
2459
|
+
if (!pos || !bubbleRef.current) return;
|
|
2460
|
+
const b = bubbleRef.current;
|
|
2461
|
+
const w = b.offsetWidth;
|
|
2462
|
+
const h = b.offsetHeight;
|
|
2463
|
+
const edge = 12;
|
|
2464
|
+
let left = pos.left;
|
|
2465
|
+
let top = pos.top;
|
|
2466
|
+
let side = pos.side;
|
|
2467
|
+
left = Math.max(edge + w / 2, Math.min(left, window.innerWidth - edge - w / 2));
|
|
2468
|
+
if (side === "bottom" && top + h > window.innerHeight - edge) {
|
|
2469
|
+
const anchorEl = anchorRef.current;
|
|
2470
|
+
const r = anchorEl.getBoundingClientRect();
|
|
2471
|
+
top = r.top - 8;
|
|
2472
|
+
side = "top";
|
|
2473
|
+
}
|
|
2474
|
+
b.style.left = `${left}px`;
|
|
2475
|
+
b.style.top = `${top}px`;
|
|
2476
|
+
b.dataset.side = side;
|
|
2477
|
+
b.style.visibility = "visible";
|
|
2478
|
+
}, [pos]);
|
|
2479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
2480
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2481
|
+
"span",
|
|
2482
|
+
{
|
|
2483
|
+
ref: anchorRef,
|
|
2484
|
+
className: "ss-dock-tip-anchor",
|
|
2485
|
+
onPointerEnter: enter,
|
|
2486
|
+
onPointerLeave: leave,
|
|
2487
|
+
onFocus: enter,
|
|
2488
|
+
onBlur: leave,
|
|
2489
|
+
children
|
|
2490
|
+
}
|
|
2491
|
+
),
|
|
2492
|
+
pos && typeof document !== "undefined" && (0, import_react_dom2.createPortal)(
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: bubbleRef, className: "ss-dock-tip", style: { left: pos.left, top: pos.top, visibility: "hidden" }, children: label }),
|
|
2494
|
+
document.body
|
|
2495
|
+
)
|
|
2258
2496
|
] });
|
|
2259
2497
|
}
|
|
2260
|
-
function QueueResumeIcon() {
|
|
2261
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4.4 2.9c0-.6.66-1 1.18-.68l7.1 4.1c.52.3.52 1.06 0 1.36l-7.1 4.1A.8.8 0 0 1 4.4 11.1V2.9Z", fill: "currentColor" }) });
|
|
2262
|
-
}
|
|
2263
2498
|
|
|
2264
2499
|
// src/client/components/SchedulerSettingsCard.tsx
|
|
2265
|
-
var
|
|
2500
|
+
var import_react9 = require("react");
|
|
2501
|
+
var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
2266
2502
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2267
2503
|
var SECTIONS = [
|
|
2268
2504
|
{
|
|
@@ -2329,22 +2565,50 @@ var SECTIONS = [
|
|
|
2329
2565
|
defaultValue: "keep"
|
|
2330
2566
|
}
|
|
2331
2567
|
]
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
id: "advanced",
|
|
2571
|
+
titleKey: "sectionAdvanced",
|
|
2572
|
+
layout: "stack",
|
|
2573
|
+
beta: true,
|
|
2574
|
+
fields: [
|
|
2575
|
+
{
|
|
2576
|
+
kind: "boolean",
|
|
2577
|
+
key: "advancedSyntax",
|
|
2578
|
+
labelKey: "advancedSyntaxLabel",
|
|
2579
|
+
hintKey: "advancedSyntaxHint",
|
|
2580
|
+
defaultValue: true
|
|
2581
|
+
}
|
|
2582
|
+
]
|
|
2332
2583
|
}
|
|
2333
2584
|
];
|
|
2334
2585
|
var FIELDS = SECTIONS.flatMap((s) => s.fields);
|
|
2335
|
-
function SchedulerSettingsCard({ scope, t }) {
|
|
2336
|
-
const [snapshot, setSnapshot] = (0,
|
|
2337
|
-
const [pending, setPending] = (0,
|
|
2338
|
-
const [saving, setSaving] = (0,
|
|
2339
|
-
const [failed, setFailed] = (0,
|
|
2340
|
-
const [helpOpen, setHelpOpen] = (0,
|
|
2341
|
-
|
|
2586
|
+
function SchedulerSettingsCard({ scope, t, surface }) {
|
|
2587
|
+
const [snapshot, setSnapshot] = (0, import_react9.useState)(() => scope.getSnapshot());
|
|
2588
|
+
const [pending, setPending] = (0, import_react9.useState)({});
|
|
2589
|
+
const [saving, setSaving] = (0, import_react9.useState)(false);
|
|
2590
|
+
const [failed, setFailed] = (0, import_react9.useState)(false);
|
|
2591
|
+
const [helpOpen, setHelpOpen] = (0, import_react9.useState)({});
|
|
2592
|
+
const SHOW_SETTINGS_KEY = "dsh-later:show-settings-section";
|
|
2593
|
+
const [showSettings, setShowSettings] = (0, import_react9.useState)(() => {
|
|
2594
|
+
if (typeof localStorage === "undefined") return false;
|
|
2595
|
+
return localStorage.getItem(SHOW_SETTINGS_KEY) === "true";
|
|
2596
|
+
});
|
|
2597
|
+
const toggleShowSettings = () => {
|
|
2598
|
+
const next = !showSettings;
|
|
2599
|
+
setShowSettings(next);
|
|
2600
|
+
if (typeof localStorage !== "undefined") {
|
|
2601
|
+
if (next) localStorage.setItem(SHOW_SETTINGS_KEY, "true");
|
|
2602
|
+
else localStorage.removeItem(SHOW_SETTINGS_KEY);
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
(0, import_react9.useEffect)(() => {
|
|
2342
2606
|
const initial = scope.getSnapshot();
|
|
2343
2607
|
setSnapshot(initial);
|
|
2344
2608
|
setPending({});
|
|
2345
2609
|
if (initial.status === "ready") setFailed(false);
|
|
2346
2610
|
}, [scope]);
|
|
2347
|
-
(0,
|
|
2611
|
+
(0, import_react9.useEffect)(() => {
|
|
2348
2612
|
return scope.subscribe(() => {
|
|
2349
2613
|
const snap = scope.getSnapshot();
|
|
2350
2614
|
setSnapshot(snap);
|
|
@@ -2422,11 +2686,11 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
2422
2686
|
setFieldPending(field.key, baselineValue(field), true);
|
|
2423
2687
|
setFailed(false);
|
|
2424
2688
|
};
|
|
2425
|
-
const handleDiscard = (0,
|
|
2689
|
+
const handleDiscard = (0, import_react9.useCallback)(() => {
|
|
2426
2690
|
setPending({});
|
|
2427
2691
|
setFailed(false);
|
|
2428
2692
|
}, []);
|
|
2429
|
-
const handleSave = (0,
|
|
2693
|
+
const handleSave = (0, import_react9.useCallback)(async () => {
|
|
2430
2694
|
if (!dirty || saving || !writable) return;
|
|
2431
2695
|
setSaving(true);
|
|
2432
2696
|
setFailed(false);
|
|
@@ -2462,8 +2726,32 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
2462
2726
|
const confirmEnabled = (pending.confirmDelivery?.value ?? (value.confirmDelivery === false ? "false" : "true")) !== "false";
|
|
2463
2727
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-settings-form", children: [
|
|
2464
2728
|
!writable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-hint", role: "status", children: t("readOnly") }),
|
|
2729
|
+
surface === "detail" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "ss-section", "aria-labelledby": "ss-section-display-toggle", children: [
|
|
2730
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "ss-section-heading", id: "ss-section-display-toggle", children: t("sectionDisplayToggle") }),
|
|
2731
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-section-fields", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-field ss-fieldToggle", children: [
|
|
2732
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-toggle-row", children: [
|
|
2733
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-label-group", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "ss-toggle-label", htmlFor: "ss-show-settings", children: t("showInSettings") }) }),
|
|
2734
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2735
|
+
"button",
|
|
2736
|
+
{
|
|
2737
|
+
type: "button",
|
|
2738
|
+
id: "ss-show-settings",
|
|
2739
|
+
role: "switch",
|
|
2740
|
+
"aria-checked": showSettings,
|
|
2741
|
+
"aria-label": t("showInSettings"),
|
|
2742
|
+
className: "ss-toggle-switch",
|
|
2743
|
+
onClick: toggleShowSettings
|
|
2744
|
+
}
|
|
2745
|
+
)
|
|
2746
|
+
] }),
|
|
2747
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-toggle-description", children: t("showInSettingsHint") })
|
|
2748
|
+
] }) })
|
|
2749
|
+
] }),
|
|
2465
2750
|
SECTIONS.map((section) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "ss-section", "aria-labelledby": `ss-section-${section.id}`, children: [
|
|
2466
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.
|
|
2751
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h3", { className: "ss-section-heading", id: `ss-section-${section.id}`, children: [
|
|
2752
|
+
t(section.titleKey),
|
|
2753
|
+
section.beta === true ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-section-beta", "aria-label": t("betaBadgeAria"), children: t("betaBadge") }) : null
|
|
2754
|
+
] }),
|
|
2467
2755
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: section.layout === "grid" ? "ss-section-fields ss-section-fieldsGrid" : "ss-section-fields", children: section.fields.map((field) => {
|
|
2468
2756
|
const invalid = invalidField(field);
|
|
2469
2757
|
const isTime = field.kind === "time";
|
|
@@ -2522,11 +2810,7 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
2522
2810
|
"aria-expanded": helpOpen[field.key] === true,
|
|
2523
2811
|
"aria-controls": `ss-help-${field.key}`,
|
|
2524
2812
|
onClick: () => setHelpOpen((prev) => ({ ...prev, [field.key]: !prev[field.key] })),
|
|
2525
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.
|
|
2526
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12.5757 7.00012C12.5757 3.92085 10.0794 1.42463 7.00012 1.42456C3.9208 1.42456 1.42456 3.9208 1.42456 7.00012C1.42463 10.0794 3.92085 12.5757 7.00012 12.5757C10.0793 12.5756 12.5756 10.0793 12.5757 7.00012ZM13.8002 7.00012C13.8001 10.7559 10.7559 13.8001 7.00012 13.8002C3.2443 13.8002 0.199291 10.7559 0.199219 7.00012C0.199219 3.24426 3.24426 0.199219 7.00012 0.199219C10.7559 0.199291 13.8002 3.2443 13.8002 7.00012Z", fill: "currentColor" }),
|
|
2527
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M7.6127 3.18921V4.55986H6.38735V3.18921H7.6127Z", fill: "currentColor" }),
|
|
2528
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M7.6127 5.68921V10.8109H6.38735V5.68921H7.6127Z", fill: "currentColor" })
|
|
2529
|
-
] })
|
|
2813
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.IconInfoOutlineRegular, { size: 12 })
|
|
2530
2814
|
}
|
|
2531
2815
|
)
|
|
2532
2816
|
] }),
|
|
@@ -2594,6 +2878,7 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
2594
2878
|
var PRESENCE_URGENT_WINDOW_MS = 5 * 6e4;
|
|
2595
2879
|
function summarizeSchedules(schedules) {
|
|
2596
2880
|
let nextAt;
|
|
2881
|
+
let progressFrom;
|
|
2597
2882
|
let count = 0;
|
|
2598
2883
|
let pausedCount = 0;
|
|
2599
2884
|
for (const item of schedules ?? []) {
|
|
@@ -2606,10 +2891,44 @@ function summarizeSchedules(schedules) {
|
|
|
2606
2891
|
pausedCount += 1;
|
|
2607
2892
|
continue;
|
|
2608
2893
|
}
|
|
2609
|
-
if (nextAt === void 0 || epoch < nextAt)
|
|
2894
|
+
if (nextAt === void 0 || epoch < nextAt) {
|
|
2895
|
+
nextAt = epoch;
|
|
2896
|
+
progressFrom = windowStartOf(item, epoch);
|
|
2897
|
+
}
|
|
2610
2898
|
}
|
|
2611
2899
|
if (count === 0) return { count: 0, pausedCount: 0 };
|
|
2612
|
-
return nextAt === void 0 ? { count, pausedCount } : { count, nextAt, pausedCount };
|
|
2900
|
+
return nextAt === void 0 ? { count, pausedCount } : { count, nextAt, pausedCount, ...progressFrom !== void 0 ? { progressFrom } : {} };
|
|
2901
|
+
}
|
|
2902
|
+
function windowStartOf(item, scheduledEpoch) {
|
|
2903
|
+
const explicit = toFiniteSeconds(item.window_seconds ?? item.windowSeconds);
|
|
2904
|
+
if (explicit !== void 0) return scheduledEpoch - explicit * 1e3;
|
|
2905
|
+
const afterSec = toFiniteSeconds(item.after_seconds ?? item.afterSeconds);
|
|
2906
|
+
const everySec = toFiniteSeconds(item.every_seconds ?? item.everySeconds);
|
|
2907
|
+
const span = item.kind === "every" ? everySec : afterSec ?? everySec;
|
|
2908
|
+
if (span !== void 0) return scheduledEpoch - span * 1e3;
|
|
2909
|
+
const created = toEpochMs(item.created_at) ?? toEpochMs(item.createdAt);
|
|
2910
|
+
if (created !== null) return created;
|
|
2911
|
+
return void 0;
|
|
2912
|
+
}
|
|
2913
|
+
function toFiniteSeconds(value) {
|
|
2914
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2915
|
+
}
|
|
2916
|
+
function toEpochMs(value) {
|
|
2917
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
2918
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2919
|
+
const parsed = Date.parse(value);
|
|
2920
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
2921
|
+
}
|
|
2922
|
+
return null;
|
|
2923
|
+
}
|
|
2924
|
+
function progressOf(entry, now) {
|
|
2925
|
+
const { nextAt, progressFrom } = entry;
|
|
2926
|
+
if (nextAt === void 0 || progressFrom === void 0) return void 0;
|
|
2927
|
+
const span = nextAt - progressFrom;
|
|
2928
|
+
if (!(span > 0)) return void 0;
|
|
2929
|
+
const ratio = (now - progressFrom) / span;
|
|
2930
|
+
if (!Number.isFinite(ratio)) return void 0;
|
|
2931
|
+
return ratio <= 0 ? 0 : ratio >= 1 ? 1 : ratio;
|
|
2613
2932
|
}
|
|
2614
2933
|
function badgeStateFor(nextAt, now) {
|
|
2615
2934
|
if (nextAt === void 0) return "scheduled";
|
|
@@ -2659,9 +2978,10 @@ var HOVER_ROW_CLASS = "ss-hover-status";
|
|
|
2659
2978
|
var FIBER_MAX_DEPTH = 8;
|
|
2660
2979
|
var SCAN_DEBOUNCE_MS = 60;
|
|
2661
2980
|
var STATE_TICK_MS = 3e4;
|
|
2662
|
-
var REFRESH_INTERVAL_MS = 6e4;
|
|
2663
2981
|
var CLOCK_SVG_CLASSIC = '<svg width="10" height="10" viewBox="0 0 10 10" shape-rendering="crispEdges" aria-hidden="true"><g class="ss-presence-ring"><rect x="0" y="0" width="2" height="2"/><rect x="4" y="0" width="2" height="2"/><rect x="8" y="0" width="2" height="2"/><rect x="8" y="4" width="2" height="2"/><rect x="8" y="8" width="2" height="2"/><rect x="4" y="8" width="2" height="2"/><rect x="0" y="8" width="2" height="2"/><rect x="0" y="4" width="2" height="2"/></g><g class="ss-presence-hand"><rect x="4" y="2" width="2" height="2"/><rect x="6" y="4" width="2" height="2"/></g></svg>';
|
|
2664
|
-
var
|
|
2982
|
+
var CLOCK_FACE_R = 9.5;
|
|
2983
|
+
var CLOCK_FACE_CIRCUMFERENCE = 2 * Math.PI * CLOCK_FACE_R;
|
|
2984
|
+
var CLOCK_SVG_MODERN = `<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true"><circle class="ss-clock-track" cx="12" cy="12" r="${CLOCK_FACE_R}"/><circle class="ss-clock-prog" cx="12" cy="12" r="${CLOCK_FACE_R}" stroke-dasharray="${CLOCK_FACE_CIRCUMFERENCE.toFixed(3)}" stroke-dashoffset="${CLOCK_FACE_CIRCUMFERENCE.toFixed(3)}" transform="rotate(-90 12 12)"/><g class="ss-clock-hand"><line class="ss-clock-hand-h" x1="12" y1="12" x2="12" y2="6.5"/><line class="ss-clock-hand-m" x1="12" y1="12" x2="17" y2="12"/></g></svg>`;
|
|
2665
2985
|
var hhmmFormatter = new Intl.DateTimeFormat(void 0, { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
2666
2986
|
var dateFormatter = new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric" });
|
|
2667
2987
|
var formatHhmm2 = (epoch) => hhmmFormatter.format(epoch);
|
|
@@ -2684,14 +3004,9 @@ function sessionIdOfNode(element) {
|
|
|
2684
3004
|
}
|
|
2685
3005
|
return void 0;
|
|
2686
3006
|
}
|
|
2687
|
-
function
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
const schedules = summary.projectionValues?.["userSchedules"]?.schedules;
|
|
2691
|
-
const entry = summarizeSchedules(schedules);
|
|
2692
|
-
if (entry.count > 0) index.set(sessionId, entry);
|
|
2693
|
-
}
|
|
2694
|
-
return index;
|
|
3007
|
+
function currentIndex(summary) {
|
|
3008
|
+
summary.reload();
|
|
3009
|
+
return summary.getSnapshot();
|
|
2695
3010
|
}
|
|
2696
3011
|
function findBadge(row) {
|
|
2697
3012
|
for (const child of Array.from(row.children)) {
|
|
@@ -2712,7 +3027,7 @@ function labelsFor(dict) {
|
|
|
2712
3027
|
};
|
|
2713
3028
|
}
|
|
2714
3029
|
function mountSessionPresence(options) {
|
|
2715
|
-
const {
|
|
3030
|
+
const { summary, locale, getClockIconStyle } = options;
|
|
2716
3031
|
if (typeof document === "undefined" || typeof MutationObserver === "undefined") {
|
|
2717
3032
|
return () => void 0;
|
|
2718
3033
|
}
|
|
@@ -2726,7 +3041,7 @@ function mountSessionPresence(options) {
|
|
|
2726
3041
|
const dictForNow = () => dictFor(locale?.getSnapshot().active);
|
|
2727
3042
|
const scan = () => {
|
|
2728
3043
|
if (disposed) return;
|
|
2729
|
-
index =
|
|
3044
|
+
index = currentIndex(summary);
|
|
2730
3045
|
const dict = dictForNow();
|
|
2731
3046
|
const now = Date.now();
|
|
2732
3047
|
for (const row of Array.from(document.querySelectorAll('[role="treeitem"][draggable="true"]'))) {
|
|
@@ -2756,10 +3071,12 @@ function mountSessionPresence(options) {
|
|
|
2756
3071
|
const allPaused = entry.nextAt === void 0 && entry.pausedCount > 0;
|
|
2757
3072
|
const aria = allPaused ? format(dict.presencePausedAria, { n: entry.count }) : state === "overdue" ? format(dict.presenceOverdueAria, { n: entry.count }) : format(dict.presenceBadgeAria, { n: entry.count, time });
|
|
2758
3073
|
const tooltip = allPaused ? format(dict.presencePausedTooltip, { n: entry.count }) : state === "overdue" ? format(dict.presenceOverdueTooltip, { n: entry.count }) : format(dict.presenceTooltip, { n: entry.count, time });
|
|
3074
|
+
const progress = progressOf(entry, now);
|
|
2759
3075
|
if (existing !== null) {
|
|
2760
3076
|
if (existing.dataset.state !== state) existing.dataset.state = state;
|
|
2761
3077
|
if (existing.getAttribute("aria-label") !== aria) existing.setAttribute("aria-label", aria);
|
|
2762
3078
|
if (existing.getAttribute("title") !== tooltip) existing.setAttribute("title", tooltip);
|
|
3079
|
+
applyProgress(existing, progress);
|
|
2763
3080
|
return;
|
|
2764
3081
|
}
|
|
2765
3082
|
const slot = row.firstElementChild;
|
|
@@ -2772,9 +3089,20 @@ function mountSessionPresence(options) {
|
|
|
2772
3089
|
badge.setAttribute("aria-label", aria);
|
|
2773
3090
|
badge.setAttribute("title", tooltip);
|
|
2774
3091
|
badge.innerHTML = getClockSvg();
|
|
3092
|
+
applyProgress(badge, progress);
|
|
2775
3093
|
if (beside) slot.after(badge);
|
|
2776
3094
|
else slot.append(badge);
|
|
2777
3095
|
}
|
|
3096
|
+
function applyProgress(badge, ratio) {
|
|
3097
|
+
const arc = badge.querySelector(".ss-clock-prog");
|
|
3098
|
+
if (arc === null) return;
|
|
3099
|
+
if (ratio === void 0) {
|
|
3100
|
+
arc.style.display = "none";
|
|
3101
|
+
return;
|
|
3102
|
+
}
|
|
3103
|
+
if (arc.style.display === "none") arc.style.display = "";
|
|
3104
|
+
arc.style.strokeDashoffset = String(CLOCK_FACE_CIRCUMFERENCE * (1 - ratio));
|
|
3105
|
+
}
|
|
2778
3106
|
function findHoverCard() {
|
|
2779
3107
|
for (const card of Array.from(document.body.children)) {
|
|
2780
3108
|
if (!(card instanceof HTMLElement)) continue;
|
|
@@ -2820,46 +3148,201 @@ function mountSessionPresence(options) {
|
|
|
2820
3148
|
row.append(dot, text);
|
|
2821
3149
|
statusHost.append(row);
|
|
2822
3150
|
}
|
|
2823
|
-
const
|
|
3151
|
+
const unsubscribeSummary = summary.subscribe(scheduleScan);
|
|
2824
3152
|
const unsubscribeLocale = locale?.subscribe(scheduleScan);
|
|
2825
3153
|
const observer = new MutationObserver(scheduleScan);
|
|
2826
3154
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
2827
|
-
const refreshNow = () => {
|
|
2828
|
-
if (disposed) return;
|
|
2829
|
-
try {
|
|
2830
|
-
void sessions.refresh().catch(() => void 0);
|
|
2831
|
-
} catch {
|
|
2832
|
-
}
|
|
2833
|
-
};
|
|
2834
|
-
const onVisible = () => {
|
|
2835
|
-
if (document.visibilityState === "visible") {
|
|
2836
|
-
refreshNow();
|
|
2837
|
-
scheduleScan();
|
|
2838
|
-
}
|
|
2839
|
-
};
|
|
2840
|
-
document.addEventListener("visibilitychange", onVisible);
|
|
2841
|
-
window.addEventListener("online", onVisible);
|
|
2842
3155
|
const stateTick = setInterval(scheduleScan, STATE_TICK_MS);
|
|
2843
|
-
const refreshTick = setInterval(() => {
|
|
2844
|
-
if (document.visibilityState === "visible") refreshNow();
|
|
2845
|
-
}, REFRESH_INTERVAL_MS);
|
|
2846
|
-
refreshNow();
|
|
2847
3156
|
scheduleScan();
|
|
2848
3157
|
return () => {
|
|
2849
3158
|
disposed = true;
|
|
2850
3159
|
if (scanTimer !== void 0) clearTimeout(scanTimer);
|
|
2851
3160
|
clearInterval(stateTick);
|
|
2852
|
-
clearInterval(refreshTick);
|
|
2853
3161
|
observer.disconnect();
|
|
2854
|
-
|
|
3162
|
+
unsubscribeSummary();
|
|
2855
3163
|
unsubscribeLocale?.();
|
|
2856
|
-
document.removeEventListener("visibilitychange", onVisible);
|
|
2857
|
-
window.removeEventListener("online", onVisible);
|
|
2858
3164
|
for (const badge of Array.from(document.querySelectorAll(`.${BADGE_CLASS}`))) badge.remove();
|
|
2859
3165
|
for (const row of Array.from(document.querySelectorAll(`.${HOVER_ROW_CLASS}`))) row.remove();
|
|
2860
3166
|
};
|
|
2861
3167
|
}
|
|
2862
3168
|
|
|
3169
|
+
// src/schedule-summary.ts
|
|
3170
|
+
var SCHEDULE_PROJECTION_KEY = "userSchedules";
|
|
3171
|
+
function schedulesOfProjection(projectionValues) {
|
|
3172
|
+
const value = projectionValues?.[SCHEDULE_PROJECTION_KEY];
|
|
3173
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
3174
|
+
const schedules = value.schedules;
|
|
3175
|
+
return Array.isArray(schedules) ? schedules : void 0;
|
|
3176
|
+
}
|
|
3177
|
+
function buildScheduleSummary(source, now) {
|
|
3178
|
+
const summary = /* @__PURE__ */ new Map();
|
|
3179
|
+
for (const [sessionId, row] of Object.entries(source.byId)) {
|
|
3180
|
+
if (row === void 0) continue;
|
|
3181
|
+
const entry = summarizeSchedules(schedulesOfProjection(row.projectionValues));
|
|
3182
|
+
if (entry.count === 0) continue;
|
|
3183
|
+
summary.set(sessionId, { ...entry, state: badgeStateFor(entry.nextAt, now) });
|
|
3184
|
+
}
|
|
3185
|
+
return summary;
|
|
3186
|
+
}
|
|
3187
|
+
function sameScheduleSummary(a, b) {
|
|
3188
|
+
if (a === b) return true;
|
|
3189
|
+
if (a.size !== b.size) return false;
|
|
3190
|
+
for (const [sessionId, left] of a) {
|
|
3191
|
+
const right = b.get(sessionId);
|
|
3192
|
+
if (right === void 0) return false;
|
|
3193
|
+
if (left.count !== right.count || left.pausedCount !== right.pausedCount || left.nextAt !== right.nextAt || left.progressFrom !== right.progressFrom || left.state !== right.state) {
|
|
3194
|
+
return false;
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
return true;
|
|
3198
|
+
}
|
|
3199
|
+
var STATE_RANK = { scheduled: 0, urgent: 1, overdue: 2 };
|
|
3200
|
+
function summarizeScheduleGroup(summary, sessionIds) {
|
|
3201
|
+
let sessions = 0;
|
|
3202
|
+
let count = 0;
|
|
3203
|
+
let pausedCount = 0;
|
|
3204
|
+
let nextAt;
|
|
3205
|
+
let state;
|
|
3206
|
+
for (const sessionId of sessionIds) {
|
|
3207
|
+
const entry = summary.get(sessionId);
|
|
3208
|
+
if (entry === void 0) continue;
|
|
3209
|
+
sessions += 1;
|
|
3210
|
+
count += entry.count;
|
|
3211
|
+
pausedCount += entry.pausedCount;
|
|
3212
|
+
if (entry.nextAt !== void 0 && (nextAt === void 0 || entry.nextAt < nextAt)) nextAt = entry.nextAt;
|
|
3213
|
+
if (state === void 0 || STATE_RANK[entry.state] > STATE_RANK[state]) state = entry.state;
|
|
3214
|
+
}
|
|
3215
|
+
return nextAt === void 0 ? { sessions, count, pausedCount, state } : { sessions, count, pausedCount, nextAt, state };
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// src/schedule-summary-store.ts
|
|
3219
|
+
var SUMMARY_STATE_TICK_MS = 3e4;
|
|
3220
|
+
var SUMMARY_REFRESH_INTERVAL_MS = 6e4;
|
|
3221
|
+
function documentOf() {
|
|
3222
|
+
const doc = globalThis.document;
|
|
3223
|
+
return typeof doc === "object" && doc !== null ? doc : void 0;
|
|
3224
|
+
}
|
|
3225
|
+
function createScheduleSummaryStore(sessions, options = {}) {
|
|
3226
|
+
const now = options.now ?? Date.now;
|
|
3227
|
+
const stateTickMs = options.stateTickMs ?? SUMMARY_STATE_TICK_MS;
|
|
3228
|
+
const refreshIntervalMs = options.refreshIntervalMs ?? SUMMARY_REFRESH_INTERVAL_MS;
|
|
3229
|
+
const autoRefresh = options.autoRefresh ?? true;
|
|
3230
|
+
const canRefresh = options.canRefresh ?? (() => {
|
|
3231
|
+
const doc2 = documentOf();
|
|
3232
|
+
return doc2?.visibilityState === void 0 || doc2.visibilityState === "visible";
|
|
3233
|
+
});
|
|
3234
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3235
|
+
let snapshot = /* @__PURE__ */ new Map();
|
|
3236
|
+
let disposed = false;
|
|
3237
|
+
const notify = () => {
|
|
3238
|
+
for (const listener of [...listeners]) {
|
|
3239
|
+
try {
|
|
3240
|
+
listener();
|
|
3241
|
+
} catch {
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
};
|
|
3245
|
+
const reload = () => {
|
|
3246
|
+
if (disposed) return;
|
|
3247
|
+
const next = buildScheduleSummary(sessions.list.getSnapshot(), now());
|
|
3248
|
+
if (sameScheduleSummary(snapshot, next)) return;
|
|
3249
|
+
snapshot = next;
|
|
3250
|
+
notify();
|
|
3251
|
+
};
|
|
3252
|
+
const unsubscribeList = sessions.list.subscribe(reload);
|
|
3253
|
+
const stateTick = setInterval(reload, stateTickMs);
|
|
3254
|
+
let refreshTick;
|
|
3255
|
+
let onVisible;
|
|
3256
|
+
const doc = documentOf();
|
|
3257
|
+
const target = globalThis;
|
|
3258
|
+
const refreshNow = () => {
|
|
3259
|
+
if (disposed || !canRefresh()) return;
|
|
3260
|
+
try {
|
|
3261
|
+
void sessions.refresh().catch(() => void 0);
|
|
3262
|
+
} catch {
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
if (autoRefresh) {
|
|
3266
|
+
onVisible = () => {
|
|
3267
|
+
if (!disposed && canRefresh()) {
|
|
3268
|
+
refreshNow();
|
|
3269
|
+
reload();
|
|
3270
|
+
}
|
|
3271
|
+
};
|
|
3272
|
+
refreshTick = setInterval(() => {
|
|
3273
|
+
if (canRefresh()) refreshNow();
|
|
3274
|
+
}, refreshIntervalMs);
|
|
3275
|
+
doc?.addEventListener?.("visibilitychange", onVisible);
|
|
3276
|
+
target.addEventListener?.("online", onVisible);
|
|
3277
|
+
refreshNow();
|
|
3278
|
+
}
|
|
3279
|
+
reload();
|
|
3280
|
+
const dispose = () => {
|
|
3281
|
+
if (disposed) return;
|
|
3282
|
+
disposed = true;
|
|
3283
|
+
clearInterval(stateTick);
|
|
3284
|
+
if (refreshTick !== void 0) clearInterval(refreshTick);
|
|
3285
|
+
if (onVisible !== void 0) {
|
|
3286
|
+
doc?.removeEventListener?.("visibilitychange", onVisible);
|
|
3287
|
+
target.removeEventListener?.("online", onVisible);
|
|
3288
|
+
}
|
|
3289
|
+
unsubscribeList();
|
|
3290
|
+
listeners.clear();
|
|
3291
|
+
};
|
|
3292
|
+
return {
|
|
3293
|
+
getSnapshot: () => snapshot,
|
|
3294
|
+
subscribe: (listener) => {
|
|
3295
|
+
listeners.add(listener);
|
|
3296
|
+
return () => {
|
|
3297
|
+
listeners.delete(listener);
|
|
3298
|
+
};
|
|
3299
|
+
},
|
|
3300
|
+
reload,
|
|
3301
|
+
dispose
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
var activeStore;
|
|
3305
|
+
var registryListeners = /* @__PURE__ */ new Set();
|
|
3306
|
+
function setActiveScheduleSummaryStore(store) {
|
|
3307
|
+
if (activeStore === store) return;
|
|
3308
|
+
activeStore = store;
|
|
3309
|
+
for (const listener of [...registryListeners]) {
|
|
3310
|
+
try {
|
|
3311
|
+
listener();
|
|
3312
|
+
} catch {
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
function getScheduleSummaryStore() {
|
|
3317
|
+
return activeStore;
|
|
3318
|
+
}
|
|
3319
|
+
function subscribeScheduleSummaryStore(listener) {
|
|
3320
|
+
registryListeners.add(listener);
|
|
3321
|
+
return () => {
|
|
3322
|
+
registryListeners.delete(listener);
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
// src/client/useScheduleSummary.ts
|
|
3327
|
+
var import_react10 = require("react");
|
|
3328
|
+
var EMPTY_SUMMARY = /* @__PURE__ */ new Map();
|
|
3329
|
+
var noopSubscribe2 = () => () => void 0;
|
|
3330
|
+
var emptySnapshot = () => EMPTY_SUMMARY;
|
|
3331
|
+
var missingSnapshot = () => void 0;
|
|
3332
|
+
function useScheduleSummary(store) {
|
|
3333
|
+
const registered = (0, import_react10.useSyncExternalStore)(subscribeScheduleSummaryStore, getScheduleSummaryStore, missingSnapshot);
|
|
3334
|
+
const target = store ?? registered;
|
|
3335
|
+
const subscribe = (0, import_react10.useMemo)(
|
|
3336
|
+
() => target === void 0 ? noopSubscribe2 : (listener) => target.subscribe(listener),
|
|
3337
|
+
[target]
|
|
3338
|
+
);
|
|
3339
|
+
const getSnapshot = (0, import_react10.useMemo)(
|
|
3340
|
+
() => target === void 0 ? emptySnapshot : () => target.getSnapshot(),
|
|
3341
|
+
[target]
|
|
3342
|
+
);
|
|
3343
|
+
return (0, import_react10.useSyncExternalStore)(subscribe, getSnapshot, emptySnapshot);
|
|
3344
|
+
}
|
|
3345
|
+
|
|
2863
3346
|
// src/client/index.tsx
|
|
2864
3347
|
var INPUT_RIGHT_SLOT = "conversation.input.right";
|
|
2865
3348
|
var inject = ["slots", "sessions", "conversation", "locale"];
|
|
@@ -2874,9 +3357,18 @@ function apply(ctx) {
|
|
|
2874
3357
|
ctx.effect(() => injectStyles(), "dsh-later: styles");
|
|
2875
3358
|
let detectedVersion = "classic";
|
|
2876
3359
|
let getClockIconStyle = () => "auto";
|
|
3360
|
+
const summary = createScheduleSummaryStore(sessions);
|
|
3361
|
+
setActiveScheduleSummaryStore(summary);
|
|
3362
|
+
ctx.effect(
|
|
3363
|
+
() => () => {
|
|
3364
|
+
if (getScheduleSummaryStore() === summary) setActiveScheduleSummaryStore(void 0);
|
|
3365
|
+
summary.dispose();
|
|
3366
|
+
},
|
|
3367
|
+
"dsh-later: schedule summary"
|
|
3368
|
+
);
|
|
2877
3369
|
ctx.effect(
|
|
2878
3370
|
() => mountSessionPresence({
|
|
2879
|
-
|
|
3371
|
+
summary,
|
|
2880
3372
|
...locale === void 0 ? {} : { locale },
|
|
2881
3373
|
getClockIconStyle: () => {
|
|
2882
3374
|
const style = getClockIconStyle();
|
|
@@ -2898,6 +3390,11 @@ function apply(ctx) {
|
|
|
2898
3390
|
sectionDisplay: "\u663E\u793A\u8BBE\u7F6E",
|
|
2899
3391
|
sectionTime: "\u65F6\u95F4\u8BBE\u7F6E",
|
|
2900
3392
|
sectionDelivery: "\u6295\u9012\u8BBE\u7F6E",
|
|
3393
|
+
sectionAdvanced: "\u9AD8\u7EA7\u8BED\u6CD5",
|
|
3394
|
+
betaBadge: "Beta",
|
|
3395
|
+
betaBadgeAria: "Beta \u529F\u80FD\uFF0C\u884C\u4E3A\u53EF\u80FD\u968F\u7248\u672C\u8C03\u6574",
|
|
3396
|
+
advancedSyntaxLabel: "\u542F\u7528\u591A\u6B21\u5199\u6CD5\uFF08+1h*3 / 15:32+16:32\uFF09",
|
|
3397
|
+
advancedSyntaxHint: "\u8BA9 /later \u4E0E /schedule \u652F\u6301\u4E00\u6B21\u5199\u591A\u6761\uFF1A+1h*3\uFF08\u7B49\u95F4\u9694 3 \u6B21\uFF09\u3001+1h+1h+0.5h\uFF08\u76F8\u5BF9\u5E8F\u5217\uFF09\u300115:32+16:32 \u4E0E 9:00+1d\uFF08\u951A\u70B9\u5E8F\u5217\uFF0C\u6309\u5899\u949F\u63A8\u8FDB\uFF0C\u8DE8\u590F\u4EE4\u65F6\u4E0D\u6F02\uFF09\u3002\u6BCF\u6761\u90FD\u662F\u72EC\u7ACB\u4EFB\u52A1\uFF0C\u53EF\u5355\u72EC\u53D6\u6D88\u6216\u7F16\u8F91\uFF1B\u5355\u6B21\u6700\u591A 10 \u6761\u3002\u6807\u8BB0\u4E3A Beta\uFF1A\u5C55\u5F00\u540E\u7684\u89C2\u611F\u4E0E\u6761\u6570\u4E0A\u9650\u4ECD\u9700\u89C2\u5BDF\u3002",
|
|
2901
3398
|
showButtonLabel: "\u663E\u793A\u5B9A\u65F6\u6309\u94AE",
|
|
2902
3399
|
showButtonHint: "\u5173\u95ED\u540E\u8F93\u5165\u6846\u53F3\u4FA7\u4E0D\u518D\u663E\u793A\u65F6\u949F\u6309\u94AE\uFF1B\u5DF2\u521B\u5EFA\u7684\u63D0\u9192\u4ECD\u4F1A\u7167\u5E38\u89E6\u53D1\uFF0C\u5E76\u5728\u8F93\u5165\u6846\u4E0B\u65B9\u7684\u5F85\u53D1\u9001\u5217\u8868\u4E2D\u663E\u793A\u3002",
|
|
2903
3400
|
maxSchedulesLabel: "\u5355\u4F1A\u8BDD\u4EFB\u52A1\u4E0A\u9650",
|
|
@@ -2935,7 +3432,10 @@ function apply(ctx) {
|
|
|
2935
3432
|
discard: "\u653E\u5F03\u4FEE\u6539",
|
|
2936
3433
|
saveFailed: "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
2937
3434
|
loading: "\u52A0\u8F7D\u4E2D\u2026",
|
|
2938
|
-
unavailable: "\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002"
|
|
3435
|
+
unavailable: "\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002",
|
|
3436
|
+
sectionDisplayToggle: "\u663E\u793A\u8BBE\u7F6E",
|
|
3437
|
+
showInSettings: "\u5728\u8BBE\u7F6E\u4E2D\u663E\u793A",
|
|
3438
|
+
showInSettingsHint: "\u5728\u8BBE\u7F6E\u5BF9\u8BDD\u6846\u4E2D\u663E\u793A\u672C\u63D2\u4EF6\u7684\u8BBE\u7F6E\u9875\uFF08\u5237\u65B0\u9875\u9762\u540E\u751F\u6548\uFF09"
|
|
2939
3439
|
},
|
|
2940
3440
|
en: {
|
|
2941
3441
|
title: "Session Scheduler",
|
|
@@ -2947,6 +3447,11 @@ function apply(ctx) {
|
|
|
2947
3447
|
sectionDisplay: "Display",
|
|
2948
3448
|
sectionTime: "Time windows",
|
|
2949
3449
|
sectionDelivery: "Delivery",
|
|
3450
|
+
sectionAdvanced: "Advanced syntax",
|
|
3451
|
+
betaBadge: "Beta",
|
|
3452
|
+
betaBadgeAria: "Beta feature; behaviour may change between versions",
|
|
3453
|
+
advancedSyntaxLabel: "Multi-shot syntax (+1h*3 / 15:32+16:32)",
|
|
3454
|
+
advancedSyntaxHint: "Let /later and /schedule create several tasks at once: +1h*3 (same interval N times), +1h+1h+0.5h (relative chain), 15:32+16:32 and 9:00+1d (anchored on a wall clock \u2014 it keeps the clock time across DST). Each occurrence is an independent task you can cancel or edit on its own; up to 10 per command. Marked Beta: the expanded result and the 10-task cap still need real-world observation.",
|
|
2950
3455
|
showButtonLabel: "Show schedule button",
|
|
2951
3456
|
showButtonHint: "Hide the clock button on the input bar. Existing reminders keep firing and stay visible in the dock below the composer.",
|
|
2952
3457
|
maxSchedulesLabel: "Max schedules per session",
|
|
@@ -2984,7 +3489,10 @@ function apply(ctx) {
|
|
|
2984
3489
|
discard: "Discard",
|
|
2985
3490
|
saveFailed: "Save failed, please try again.",
|
|
2986
3491
|
loading: "Loading\u2026",
|
|
2987
|
-
unavailable: "Settings unavailable."
|
|
3492
|
+
unavailable: "Settings unavailable.",
|
|
3493
|
+
sectionDisplayToggle: "Display",
|
|
3494
|
+
showInSettings: "Show in settings",
|
|
3495
|
+
showInSettingsHint: "Show this plugin's settings page in the settings dialog (refresh to take effect)"
|
|
2988
3496
|
}
|
|
2989
3497
|
}), "dsh-later: locale");
|
|
2990
3498
|
}
|
|
@@ -3029,11 +3537,17 @@ function apply(ctx) {
|
|
|
3029
3537
|
);
|
|
3030
3538
|
};
|
|
3031
3539
|
const cardT = locale === void 0 ? void 0 : locale.bind(NS);
|
|
3032
|
-
const cardDeclaration = (slot, scope) => ({
|
|
3540
|
+
const cardDeclaration = (slot, scope, surface = "detail") => ({
|
|
3033
3541
|
name: slot,
|
|
3542
|
+
id: SETTINGS_NS,
|
|
3543
|
+
order: 30,
|
|
3034
3544
|
key: SETTINGS_NS,
|
|
3035
3545
|
locale: NS,
|
|
3036
|
-
|
|
3546
|
+
label: () => {
|
|
3547
|
+
const active = locale === void 0 ? "zh" : locale.getSnapshot().active;
|
|
3548
|
+
return active === "en" ? "Session Scheduler" : "\u5B9A\u65F6\u63D0\u9192";
|
|
3549
|
+
},
|
|
3550
|
+
inject: () => ({ scope, t: cardT, surface })
|
|
3037
3551
|
});
|
|
3038
3552
|
ctx.inject(["settingsScope"], () => {
|
|
3039
3553
|
const binder = ctx.get("settingsScope");
|
|
@@ -3049,10 +3563,18 @@ function apply(ctx) {
|
|
|
3049
3563
|
};
|
|
3050
3564
|
mountComposer(scope);
|
|
3051
3565
|
if (cardT === void 0) return;
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3566
|
+
const SHOW_SETTINGS_KEY = "dsh-later:show-settings-section";
|
|
3567
|
+
const showSettingsSection = typeof localStorage !== "undefined" && localStorage.getItem(SHOW_SETTINGS_KEY) === "true";
|
|
3568
|
+
if (!showSettingsSection) return;
|
|
3569
|
+
const disposeSection = ctx.slots.inject(
|
|
3570
|
+
"settings.section",
|
|
3571
|
+
() => ctx.slots.register(cardDeclaration("settings.section", scope, "settings"), SchedulerSettingsCard)
|
|
3055
3572
|
);
|
|
3573
|
+
if (typeof disposeSection === "function") {
|
|
3574
|
+
ctx.effect(() => () => {
|
|
3575
|
+
disposeSection();
|
|
3576
|
+
});
|
|
3577
|
+
}
|
|
3056
3578
|
});
|
|
3057
3579
|
ctx.inject(["configForms"], () => {
|
|
3058
3580
|
const forms = ctx.get("configForms");
|
|
@@ -3073,11 +3595,23 @@ function apply(ctx) {
|
|
|
3073
3595
|
[ENTRY_ID],
|
|
3074
3596
|
() => ctx.slots.inject(
|
|
3075
3597
|
"plugins.bundle.config",
|
|
3076
|
-
() => ctx.slots.register(cardDeclaration("plugins.bundle.config", scope), SchedulerSettingsCard)
|
|
3598
|
+
() => ctx.slots.register(cardDeclaration("plugins.bundle.config", scope, "detail"), SchedulerSettingsCard)
|
|
3077
3599
|
)
|
|
3078
3600
|
),
|
|
3079
3601
|
"dsh-later: plugins-page card"
|
|
3080
3602
|
);
|
|
3603
|
+
const SHOW_SETTINGS_KEY = "dsh-later:show-settings-section";
|
|
3604
|
+
const showSettingsSection = typeof localStorage !== "undefined" && localStorage.getItem(SHOW_SETTINGS_KEY) === "true";
|
|
3605
|
+
if (!showSettingsSection) return;
|
|
3606
|
+
const disposeSection = ctx.slots.inject(
|
|
3607
|
+
"settings.section",
|
|
3608
|
+
() => ctx.slots.register(cardDeclaration("settings.section", scope, "settings"), SchedulerSettingsCard)
|
|
3609
|
+
);
|
|
3610
|
+
if (typeof disposeSection === "function") {
|
|
3611
|
+
ctx.effect(() => () => {
|
|
3612
|
+
disposeSection();
|
|
3613
|
+
});
|
|
3614
|
+
}
|
|
3081
3615
|
});
|
|
3082
3616
|
}
|
|
3083
3617
|
|