dsh-later 1.0.0 → 1.2.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 +247 -0
- package/README.en.md +55 -2
- package/README.md +53 -2
- package/icon.svg +10 -0
- package/lib/bar-geometry.js +139 -0
- package/lib/client.js +1349 -423
- package/lib/command-outcome.js +15 -0
- package/lib/commands.js +56 -8
- package/lib/delivery.js +66 -0
- package/lib/domain.js +6 -0
- package/lib/index.js +152 -29
- package/lib/ownership-store.js +113 -15
- package/lib/presence.js +122 -4
- package/lib/projection.js +149 -20
- package/lib/runtime.js +160 -24
- package/lib/schedule-compat.js +179 -0
- package/lib/schedule-summary-store.js +160 -0
- package/lib/schedule-summary.js +113 -0
- package/lib/types/bar-geometry.d.ts +111 -0
- package/lib/types/command-outcome.d.ts +19 -0
- package/lib/types/delivery.d.ts +51 -0
- package/lib/types/domain.d.ts +32 -0
- package/lib/types/index.d.ts +28 -8
- package/lib/types/ownership-store.d.ts +42 -2
- package/lib/types/presence.d.ts +75 -4
- package/lib/types/projection.d.ts +68 -6
- package/lib/types/runtime.d.ts +58 -1
- package/lib/types/schedule-compat.d.ts +111 -0
- package/lib/types/schedule-summary-store.d.ts +75 -0
- package/lib/types/schedule-summary.d.ts +92 -0
- package/lib/types/upstream-compat.d.ts +123 -0
- package/lib/types/user-tools.d.ts +48 -7
- package/lib/upstream-compat.js +143 -0
- package/lib/user-tools.js +268 -44
- package/package.json +25 -22
package/lib/client.js
CHANGED
|
@@ -26,8 +26,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
26
26
|
var index_exports = {};
|
|
27
27
|
__export(index_exports, {
|
|
28
28
|
apply: () => apply,
|
|
29
|
+
buildScheduleSummary: () => buildScheduleSummary,
|
|
30
|
+
createScheduleSummaryStore: () => createScheduleSummaryStore,
|
|
31
|
+
getScheduleSummaryStore: () => getScheduleSummaryStore,
|
|
29
32
|
inject: () => inject,
|
|
30
|
-
name: () => name
|
|
33
|
+
name: () => name,
|
|
34
|
+
schedulesOfProjection: () => schedulesOfProjection,
|
|
35
|
+
subscribeScheduleSummaryStore: () => subscribeScheduleSummaryStore,
|
|
36
|
+
summarizeScheduleGroup: () => summarizeScheduleGroup,
|
|
37
|
+
useScheduleSummary: () => useScheduleSummary
|
|
31
38
|
});
|
|
32
39
|
module.exports = __toCommonJS(index_exports);
|
|
33
40
|
|
|
@@ -131,8 +138,51 @@ var COMPONENTS = [
|
|
|
131
138
|
".ss-dock-bar-fill {",
|
|
132
139
|
" position: relative; z-index: 1;",
|
|
133
140
|
" height: 100%; border-radius: 999px; background: var(--dsw-alias-brand-primary);",
|
|
134
|
-
|
|
141
|
+
/* 活动期的推进由 CSS 动画驱动(见 ScheduleDock 的 fillStyle):
|
|
142
|
+
旧实现是「每秒 tick 改一次宽度 + transition 补间」,1 分钟任务每秒跳约 8px,
|
|
143
|
+
肉眼可见锯齿。改为动画后由合成器逐帧插值,真 60fps 且零重渲染。
|
|
144
|
+
这里**不保留** transition: width —— 它与动画叠加会造成双重补间、反而更抖。 */
|
|
145
|
+
" /* \u6781\u77ED\u4EFB\u52A1/\u65E9\u671F\u6682\u505C\u65F6\u8FDB\u5EA6\u6BD4\u63A5\u8FD1 0\uFF1A\u7ED9\u6700\u5C0F\u53EF\u89C1\u5BBD\u5EA6\uFF0C\u4E14 active \u4E0E paused \u540C\u4E00\u89C4\u5219\uFF0C",
|
|
146
|
+
" \u907F\u514D\u6682\u505C\u524D\u540E\u4FDD\u5E95\u9608\u503C\u4E0D\u4E00\u81F4\u5BFC\u81F4\u6761\u957F\u8DF3\u53D8\u3002 */",
|
|
147
|
+
" min-width: 10px;",
|
|
135
148
|
"}",
|
|
149
|
+
"/* \u8FDB\u5EA6\u63A8\u8FDB\uFF1ACSS \u52A8\u753B\u7EBF\u6027\u63A8\u8FDB\uFF08\u66FF\u4EE3\u300C\u6BCF\u79D2 tick \u6539\u5BBD\u5EA6 + transition \u8865\u95F4\u300D\uFF09\u3002",
|
|
150
|
+
" \u53C2\u6570\u7531 JS \u6CE8\u5165 --ss-bar-duration / --ss-bar-delay\uFF1A",
|
|
151
|
+
" duration \u56FA\u5B9A\u4E3A\u5B8C\u6574\u7A97\u53E3\uFF08\u5426\u5219\u6BCF\u6B21 tick \u90FD\u6539\u53D8\u53C2\u6570\u3001\u6D4F\u89C8\u5668\u4F1A\u91CD\u542F\u52A8\u753B\uFF09\uFF1B",
|
|
152
|
+
" delay \u4E3A -(\u5DF2\u8FC7\u53BB) \u7684\u8D1F\u503C\uFF08\u628A\u64AD\u653E\u5934\u5B9A\u4F4D\u5230\u7EDD\u5BF9\u76F8\u4F4D\uFF0C\u91CD\u6302\u8F7D\u540E\u4E0D\u5F52\u96F6\uFF09\u3002",
|
|
153
|
+
" JS \u4FA7\u53EA\u4F20\u53D8\u91CF\u3001\u4E0D\u5199 animationName \u7B80\u5199\uFF0C\u907F\u514D\u8986\u76D6\u8FD9\u91CC\u7684\u58F0\u660E\u3002",
|
|
154
|
+
" \u6CE8\u610F\u6B64\u5904**\u4E0D\u5E26** :not(.ss-paused)\uFF1A\u6682\u505C\u6001\u53E6\u6709 animation: none \u8986\u76D6\u3002 */",
|
|
155
|
+
".ss-dock-bar-fill {",
|
|
156
|
+
" animation-name: ss-dock-bar-progress;",
|
|
157
|
+
" animation-duration: var(--ss-bar-duration, 3600s);",
|
|
158
|
+
" animation-delay: var(--ss-bar-delay, 0ms);",
|
|
159
|
+
" animation-timing-function: linear;",
|
|
160
|
+
" animation-fill-mode: forwards;",
|
|
161
|
+
" animation-iteration-count: 1;",
|
|
162
|
+
"}",
|
|
163
|
+
"@keyframes ss-dock-bar-progress { from { width: 0%; } to { width: 100%; } }",
|
|
164
|
+
"@media (prefers-reduced-motion: reduce) {",
|
|
165
|
+
" /* \u65E0\u969C\u788D\uFF1A\u5173\u6389\u9010\u5E27\u63A8\u8FDB\u3002\u6B64\u65F6 animation-fill-mode \u4F1A\u8BA9\u5BBD\u5EA6\u505C\u5728\u8D77\u59CB\u503C(0)\uFF0C",
|
|
166
|
+
" \u6545\u7531 JS \u4FA7\u6539\u8D70\u9759\u6001 width \u8DEF\u5F84\uFF08\u89C1 ScheduleDock \u7684 prefersReducedMotion\uFF09\u3002 */",
|
|
167
|
+
" .ss-dock-bar-fill { animation: none !important; }",
|
|
168
|
+
"}",
|
|
169
|
+
"/* \u6682\u505C\u6001\uFF08P1\uFF1A\u4E0D\u989D\u5916\u52A0\u6837\u5F0F\uFF09\u3002",
|
|
170
|
+
" \u6682\u505C = \u539F\u6761 + \u5B8C\u5168\u9759\u6B62\uFF1A\u4E0D\u53E0\u52A0\u659C\u7EB9/\u51B0\u5C01/\u963B\u585E\u5934\uFF0C\u4E5F\u4E0D\u6539\u5E95\u8272\u3001\u4E0D\u53D8\u8272\u3002",
|
|
171
|
+
" \u72B6\u6001\u7531\u300C\u52A8 / \u9759\u300D\u5BF9\u6BD4\u8868\u8FBE\u2014\u2014\u8FDB\u884C\u4E2D\u8FDB\u5EA6\u6761\u5728\u63A8\u8FDB\uFF0C\u6682\u505C\u65F6\u505C\u4F4F\u3002",
|
|
172
|
+
' \u6CE8\u610F\uFF1A**\u4E0D\u989D\u5916\u53E0\u6D41\u52A8\u7EB9\u7406**\u3002\u5148\u524D\u8BEF\u628A"\u8FDB\u884C\u4E2D\u52A0\u6D41\u5149"\u5F53\u4F5C P1 \u7684\u4E00\u90E8\u5206\u5B9E\u73B0\uFF0C',
|
|
173
|
+
" \u4F46\u90A3\u4E0D\u5728 P1 \u5B9A\u4E49\u5185\uFF08P1 \u539F\u6587\uFF1A\u6682\u505C = \u539F\u6761 + \u9759\u6B62\uFF0C\u4E0D\u52A0\u4EFB\u4F55\u7EB9\u7406\uFF09\uFF0C\u5DF2\u79FB\u9664\u3002",
|
|
174
|
+
" \u53EA\u4FDD\u7559\u4E0B\u9762\u8FD9\u884C\u9632\u5FA1\uFF1A\u4E07\u4E00\u52A8\u753B\u56E0\u6545\u6B8B\u7559\uFF0C\u6682\u505C\u65F6\u5FC5\u987B\u505C\u4F4F\u3002",
|
|
175
|
+
" \uFF08\u51E0\u4F55\u4E0E\u4FDD\u5E95\u5BBD\u5EA6\u6CBF\u7528 .ss-dock-bar-fill \u7684\u516C\u5171\u89C4\u5219\uFF0C\u6545\u6682\u505C\u524D\u540E\u957F\u5EA6\u4E00\u81F4\u3002\uFF09 */",
|
|
176
|
+
".ss-dock-row.ss-paused .ss-dock-bar-fill {",
|
|
177
|
+
" animation: none;",
|
|
178
|
+
"}",
|
|
179
|
+
"@media (prefers-reduced-motion: reduce) {",
|
|
180
|
+
" .ss-dock-row:not(.ss-paused) .ss-dock-bar-fill { animation: none; background: var(--dsw-alias-brand-primary); }",
|
|
181
|
+
"}",
|
|
182
|
+
".ss-dock-row.ss-paused .ss-dock-row-countdown { color: var(--dsw-alias-label-tertiary); }",
|
|
183
|
+
".ss-dock-row.ss-paused .ss-dock-row-time,",
|
|
184
|
+
".ss-dock-row.ss-paused .ss-dock-row-prompt { color: var(--dsw-alias-label-secondary); }",
|
|
185
|
+
".ss-dock-action.ss-paused-active { color: var(--dsw-alias-state-success-primary); }",
|
|
136
186
|
"/* \u5206\u9694\u7EBF\uFF1Aabsolute \u8DE8\u6574\u4E2A bar \u753B N-1 \u6761\u7B49\u6BD4\u7AD6\u7EBF\uFF0C\u6E32\u67D3\u5728 fill \u4E4B\u524D\uFF08fill \u7528 z-index:1 \u76D6\u4F4F\u300C\u5DF2\u586B\u5145\u6BB5\u300D\u4E0A\u7684\u523B\u5EA6\uFF09\u3002 */",
|
|
137
187
|
".ss-dock-bar-divider {",
|
|
138
188
|
" position: absolute; inset: 0; pointer-events: none;",
|
|
@@ -152,6 +202,29 @@ var COMPONENTS = [
|
|
|
152
202
|
".ss-dock-steer-err {",
|
|
153
203
|
" color: var(--dsw-alias-state-warn-secondary); font-size: 12px; line-height: 16px;",
|
|
154
204
|
"}",
|
|
205
|
+
"/*",
|
|
206
|
+
" * \u6295\u9012\u672A\u786E\u8BA4\u63D0\u793A\uFF1A\u6761\u76EE\u88AB\u5BBF\u4E3B\u4FDD\u7559\uFF08\u672A\u5199 dispatch\uFF09\uFF0C\u884C\u4ECD\u5728\u53F0\u4E0A\u3002",
|
|
207
|
+
' * \u4E0E\u666E\u901A\u9519\u8BEF\uFF08\u7EAF\u6587\u5B57\uFF09\u533A\u5206\uFF1A\u5B83\u4E0D\u662F\u4E00\u4E2A"\u5931\u8D25"\uFF0C\u800C\u662F"\u7ED3\u679C\u672A\u77E5\u4E14\u53EF\u633D\u56DE"\uFF0C',
|
|
208
|
+
" * \u8BED\u6C14\u5E94\u662F\u63D0\u793A\u800C\u975E\u62A5\u9519\uFF0C\u6545\u7ED9\u6D45\u5E95 + \u8FB9\u6846 + \u53EF\u70B9\u7684\u91CD\u8BD5\u3002",
|
|
209
|
+
" */",
|
|
210
|
+
".ss-dock-steer-warn {",
|
|
211
|
+
" display: flex; align-items: center; gap: 8px; flex-wrap: wrap;",
|
|
212
|
+
" margin-top: 4px; padding: 4px 8px; border-radius: 6px;",
|
|
213
|
+
" font-size: 12px; line-height: 16px;",
|
|
214
|
+
" color: var(--dsw-alias-state-warn-secondary);",
|
|
215
|
+
" background: color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 12%, transparent);",
|
|
216
|
+
" border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 32%, transparent);",
|
|
217
|
+
"}",
|
|
218
|
+
".ss-dock-steer-retry {",
|
|
219
|
+
" margin-left: auto; padding: 1px 8px; border-radius: 5px; cursor: pointer;",
|
|
220
|
+
" font: inherit; font-size: 12px;",
|
|
221
|
+
" color: var(--dsw-alias-state-warn-secondary);",
|
|
222
|
+
" background: color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 18%, transparent);",
|
|
223
|
+
" border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 38%, transparent);",
|
|
224
|
+
"}",
|
|
225
|
+
".ss-dock-steer-retry:hover {",
|
|
226
|
+
" background: color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 28%, transparent);",
|
|
227
|
+
"}",
|
|
155
228
|
"/* \u884C\u5185\u7F16\u8F91\uFF08QueueDock .editor \u540C\u6B3E\u8F93\u5165\u6846\uFF09\u3002 */",
|
|
156
229
|
".ss-dock-edit-input {",
|
|
157
230
|
" box-sizing: border-box; flex: auto; min-width: 0; height: 28px; padding: 0 8px;",
|
|
@@ -170,23 +243,6 @@ var COMPONENTS = [
|
|
|
170
243
|
" flex-basis: 100%; color: var(--dsw-alias-state-error-secondary); font-size: 12px; line-height: 16px;",
|
|
171
244
|
"}",
|
|
172
245
|
".ss-dock-editing { flex-wrap: wrap; }",
|
|
173
|
-
".ss-toast-host {",
|
|
174
|
-
" position: fixed; right: 16px; bottom: 96px; z-index: 300;",
|
|
175
|
-
" display: flex; flex-direction: column; gap: 8px; pointer-events: none;",
|
|
176
|
-
"}",
|
|
177
|
-
".ss-toast {",
|
|
178
|
-
" pointer-events: auto; max-width: min(360px, calc(100vw - 32px));",
|
|
179
|
-
" display: flex; align-items: center; gap: 8px;",
|
|
180
|
-
" padding: 9px 12px; border-radius: 10px;",
|
|
181
|
-
" background: var(--dsw-specific-menu); color: var(--dsw-alias-label-primary);",
|
|
182
|
-
" border: 1px solid var(--dsw-alias-line-primary);",
|
|
183
|
-
" box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);",
|
|
184
|
-
" font-size: 13px; line-height: 18px;",
|
|
185
|
-
" animation: ss-toast-in 180ms var(--ds-ease-out);",
|
|
186
|
-
"}",
|
|
187
|
-
".ss-toast-icon { color: var(--dsw-alias-brand-primary); flex: none; }",
|
|
188
|
-
".ss-toast-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
189
|
-
"@keyframes ss-toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }",
|
|
190
246
|
"",
|
|
191
247
|
"/* ==================== \u9762\u677F ==================== */",
|
|
192
248
|
"/* \u5BF9\u9F50\u5B98\u65B9 popover panel\uFF1Az-index 100\u3001border-inverted\u3001radius 12\u3001\u5B57\u53F7 12\u3001secondary \u6587\u5B57 */",
|
|
@@ -253,23 +309,22 @@ var COMPONENTS = [
|
|
|
253
309
|
".ss-tasklist-head { margin-top: 10px; }",
|
|
254
310
|
"",
|
|
255
311
|
"/* ==================== \u8868\u5355\u5B57\u6BB5 ==================== */",
|
|
256
|
-
"/* \u5BF9\u9F50\u5B98\u65B9 fields.module.css\uFF1Acolumn \u5E03\u5C40\u3001label \u5728\u4E0A 13px/500\
|
|
312
|
+
"/* \u5BF9\u9F50\u5B98\u65B9 fields.module.css\uFF1Acolumn \u5E03\u5C40\u3001label \u5728\u4E0A 13px/500\uFF0C\u5B57\u6BB5\u95F4\u65E0\u5206\u9694\u7EBF */",
|
|
257
313
|
".ss-field {",
|
|
258
314
|
" display: flex; flex-direction: column; gap: 6px; padding: 12px 0;",
|
|
259
315
|
"}",
|
|
260
|
-
".ss-field + .ss-field { border-top: 1px solid var(--dsw-alias-border-l2); }",
|
|
261
316
|
".ss-input {",
|
|
262
|
-
" height:
|
|
263
|
-
" border:
|
|
317
|
+
" min-height: 36px; padding: 0 12px;",
|
|
318
|
+
" border: .5px solid var(--dsw-alias-border-l4); border-radius: var(--dsw-radius-md);",
|
|
264
319
|
" background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary);",
|
|
265
|
-
" font-size: 13px; line-height: 1.5;
|
|
320
|
+
" font: inherit; font-size: 13px; line-height: 1.5;",
|
|
266
321
|
" flex: 1; min-width: 0; width: 100%; box-sizing: border-box;",
|
|
267
322
|
"}",
|
|
268
323
|
".ss-input:focus-visible {",
|
|
269
|
-
" border-color: var(--dsw-alias-
|
|
324
|
+
" outline: none; border-color: var(--dsw-alias-state-business-primary);",
|
|
270
325
|
"}",
|
|
271
326
|
".ss-input:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }",
|
|
272
|
-
".ss-input.ss-inputInvalid { border-color: var(--dsw-alias-
|
|
327
|
+
".ss-input.ss-inputInvalid { border-color: var(--dsw-alias-state-error-primary); }",
|
|
273
328
|
"",
|
|
274
329
|
"/* ==================== \u4EFB\u52A1\u5217\u8868 ==================== */",
|
|
275
330
|
".ss-tasks { max-height: 160px; overflow-y: auto; margin-bottom: 8px; }",
|
|
@@ -362,46 +417,156 @@ var COMPONENTS = [
|
|
|
362
417
|
"/* \u7D2B\u4E3A\u5B9A\u65F6\u4E13\u5C5E\uFF08\u6D45\u6DF1\u901A\u7528\uFF0C\u5BF9\u767D/\u6DF1\u5E95\u5BF9\u6BD4\u5747 \u22653:1\uFF09\uFF1Burgent/overdue \u590D\u7528\u5BBF\u4E3B warn/error \u522B\u540D\u3002 */",
|
|
363
418
|
".ss-presence { display: inline-flex; flex: none; color: #8b5cf6; }",
|
|
364
419
|
".ss-presence-beside { margin-left: 4px; }",
|
|
420
|
+
"",
|
|
421
|
+
"/* \u2500\u2500 \u65E7\u7248\u70B9\u9635\u65F6\u949F\uFF08classic\uFF09 \u2500\u2500 */",
|
|
365
422
|
".ss-presence rect { fill: currentColor; }",
|
|
366
423
|
".ss-presence .ss-presence-ring { opacity: 0.8; animation: ss-presence-breathe 2.4s var(--ds-ease-in-out, ease-in-out) infinite; }",
|
|
367
424
|
".ss-presence .ss-presence-hand { opacity: 1; }",
|
|
368
|
-
'.ss-presence[data-state="urgent"] { color: var(--dsw-alias-state-warn-secondary); }',
|
|
369
425
|
'.ss-presence[data-state="urgent"] .ss-presence-ring { animation-name: ss-presence-pulse; animation-duration: 0.9s; }',
|
|
370
|
-
'.ss-presence[data-state="overdue"] { color: var(--dsw-alias-state-error-primary); }',
|
|
371
426
|
'.ss-presence[data-state="overdue"] .ss-presence-ring { animation: none; }',
|
|
427
|
+
"",
|
|
428
|
+
"/* \u65B0\u7248\u63CF\u8FB9\u65F6\u949F\uFF08modern\uFF09\u2014 \u5BF9\u9F50 DSH \u5B98\u65B9 spinner \u6837\u5F0F \u2500\u2500 */",
|
|
429
|
+
"/* \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",
|
|
430
|
+
" \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 */",
|
|
431
|
+
".ss-presence .ss-clock-track {",
|
|
432
|
+
" fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;",
|
|
433
|
+
" opacity: 0.25; animation: ss-clock-breathe 2.4s var(--ds-ease-in-out, ease-in-out) infinite;",
|
|
434
|
+
"}",
|
|
435
|
+
".ss-presence .ss-clock-prog {",
|
|
436
|
+
" fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;",
|
|
437
|
+
" opacity: 1; transition: stroke-dashoffset 0.5s var(--ds-ease-in-out, ease-in-out);",
|
|
438
|
+
"}",
|
|
439
|
+
".ss-presence .ss-clock-hand { stroke: currentColor; stroke-width: 2; stroke-linecap: round; }",
|
|
440
|
+
".ss-presence .ss-clock-hand-h { opacity: 1; }",
|
|
441
|
+
".ss-presence .ss-clock-hand-m { opacity: 0.85; }",
|
|
442
|
+
"/* 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 */",
|
|
443
|
+
'.ss-presence[data-state="urgent"] .ss-clock-track { animation-name: ss-clock-pulse; animation-duration: 0.9s; }',
|
|
444
|
+
'.ss-presence[data-state="overdue"] .ss-clock-track { animation: none; opacity: 1; }',
|
|
445
|
+
"",
|
|
446
|
+
"/* \u2500\u2500 \u4E09\u6001\u989C\u8272 \u2500\u2500 */",
|
|
447
|
+
'.ss-presence[data-state="urgent"] { color: var(--dsw-alias-state-warn-secondary); }',
|
|
448
|
+
'.ss-presence[data-state="overdue"] { color: var(--dsw-alias-state-error-primary); }',
|
|
449
|
+
"",
|
|
450
|
+
"/* \u2500\u2500 \u52A8\u753B\u5173\u952E\u5E27 \u2500\u2500 */",
|
|
372
451
|
"@keyframes ss-presence-breathe { 0%, 100% { opacity: 0.65; } 50% { opacity: 1; } }",
|
|
373
452
|
"@keyframes ss-presence-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }",
|
|
374
|
-
"@
|
|
453
|
+
"@keyframes ss-clock-breathe { 0%, 100% { opacity: 0.65; } 50% { opacity: 1; } }",
|
|
454
|
+
"@keyframes ss-clock-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }",
|
|
455
|
+
"@media (prefers-reduced-motion: reduce) {",
|
|
456
|
+
" .ss-presence .ss-presence-ring { animation: none; }",
|
|
457
|
+
" .ss-presence .ss-clock-track { animation: none; }",
|
|
458
|
+
' /* \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 */',
|
|
459
|
+
" .ss-presence .ss-clock-prog { transition: none; }",
|
|
460
|
+
"}",
|
|
375
461
|
"",
|
|
376
|
-
"/* ==================== \
|
|
377
|
-
"/* \
|
|
378
|
-
" \
|
|
379
|
-
"
|
|
380
|
-
"
|
|
381
|
-
"
|
|
382
|
-
"
|
|
383
|
-
"
|
|
384
|
-
"
|
|
385
|
-
"
|
|
386
|
-
"
|
|
387
|
-
"
|
|
388
|
-
"
|
|
389
|
-
"
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"}",
|
|
393
|
-
"
|
|
394
|
-
"
|
|
395
|
-
"
|
|
396
|
-
".ss-
|
|
397
|
-
"
|
|
398
|
-
"
|
|
399
|
-
"
|
|
400
|
-
"
|
|
401
|
-
".ss-
|
|
402
|
-
"
|
|
403
|
-
|
|
404
|
-
"
|
|
462
|
+
"/* ==================== \u60AC\u6D6E\u5361\u7247\u72B6\u6001\u884C\u589E\u8865\uFF08issue #4\uFF09 ==================== */",
|
|
463
|
+
"/* \u4E0E\u5BBF\u4E3B .hoverStatus \u884C\u4E25\u683C\u540C\u6784\u3002\u5BBF\u4E3B StateDot \u5B9E\u6D4B\u7ED3\u6784\uFF08dsh-client-ui-primitives",
|
|
464
|
+
" \u7684 ._dot_* \u8FD0\u884C\u65F6\u6837\u5F0F\uFF0C10\xD710 relative + ::before 10px/inset:0 + ::after 6px/inset:2px\uFF0C",
|
|
465
|
+
" \u4E24\u5C42\u540C\u8272 \u2192 \u89C6\u89C9\u76F4\u5F84\u7EA6 8px\uFF09\uFF1A\u672C\u63D2\u4EF6\u6309\u4E0B\u8FF0\u540C\u6837\u7684\u53CC\u5C42\u5199\u6CD5\u81EA\u7ED8\uFF0C\u907F\u514D\u590D\u7528\u5176\u6784\u5EFA\u671F hash",
|
|
466
|
+
" \u7C7B\u540D\uFF08_dot_xxxxxx_n \u4E0D\u7A33\u5B9A\uFF0C\u5BBF\u4E3B\u5347\u7EA7\u4F1A\u9759\u9ED8\u5931\u6548\uFF09\u3002",
|
|
467
|
+
" \u989C\u8272\u8DDF\u968F\u5BBF\u4E3B\u8BED\u4E49\u8272\u677F\uFF0C\u4E0D\u5F15\u5165\u63D2\u4EF6\u4E13\u5C5E\u8272\u2014\u2014\u6587\u6848\u5DF2\u8BF4\u660E\u662F\u5B9A\u65F6\u63D0\u9192\uFF0C\u70B9\u4E0D\u5FC5\u518D\u627F\u62C5",
|
|
468
|
+
" \u300C\u533A\u5206\u6765\u6E90\u300D\u7684\u804C\u8D23\uFF1B\u6DF7\u5165\u7B2C\u4E94\u79CD\u989C\u8272\u53CD\u800C\u4E0E\u5BBF\u4E3B warning \u8BED\u4E49\u649E\u8F66\u3002",
|
|
469
|
+
"",
|
|
470
|
+
" \u26A0\uFE0F \u5DF2\u77E5\u9650\u5236\uFF08\u6D45\u8272\u9002\u914D\u5C1A\u672A\u5904\u7406\uFF09\uFF1A\u4EE5\u4E0B\u8272\u503C\u662F\u5728**\u5F53\u524D\u6DF1\u8272\u4E3B\u9898**\u4E0B\u4ECE\u5BBF\u4E3B",
|
|
471
|
+
" \u5B9E\u6D4B\u5F97\u5230\u7684\u8FD0\u884C\u65F6\u503C\uFF0C\u5BBF\u4E3B\u81EA\u8EAB\u5F15\u7528 --dsw-alias-* token\uFF0C\u4F1A\u968F\u4E3B\u9898\u53D8\u5316\uFF0C",
|
|
472
|
+
" \u800C\u6B64\u5904\u662F\u786C\u7F16\u7801\u5E38\u91CF\u3002\u56E0\u6B64\u5F53\u524D\u4EC5\u5728\u6DF1\u8272\u4E3B\u9898\u4E0B\u4E0E\u5BBF\u4E3B\u4E00\u81F4\uFF1B\u5207\u6362\u5230\u6D45\u8272\u4E3B\u9898/",
|
|
473
|
+
" \u5176\u4ED6\u914D\u8272\u65B9\u6848\u65F6\uFF0C\u672C\u884C\u4E0D\u4F1A\u8DDF\u968F\uFF0C\u53EF\u80FD\u4E0E\u8BE5\u5361\u7247\u5176\u4F59\u5185\u5BB9\u4E0D\u540C\u6B65\u3002",
|
|
474
|
+
" \u4FEE\u6CD5\uFF1A\u5B9E\u6D4B\u6D45\u8272\u4E3B\u9898\u4E0B\u8BE5\u5361\u7247\u7684\u5B9E\u9645\u5E95\u8272\u540E\uFF0C\u6539\u7528\u5BF9\u5E94 token\uFF08\u5F85\u529E\uFF09\u3002 */",
|
|
475
|
+
".ss-hover-status {",
|
|
476
|
+
" display: flex; align-items: center; gap: 8px;",
|
|
477
|
+
" color: #adb2b8; font-size: 12px; line-height: 20px;",
|
|
478
|
+
"}",
|
|
479
|
+
"/* \u72B6\u6001\u70B9\uFF1A\u5BBF\u4E3B\u540C\u6B3E\u53CC\u5C42\u7ED3\u6784\u3002\u5173\u952E\u2014\u2014::before \u662F 10% \u900F\u660E\u5EA6\u7684**\u5149\u6655**\uFF0C",
|
|
480
|
+
" \u4E0D\u662F\u5B9E\u5FC3\u5916\u73AF\uFF1B::after \u624D\u662F 6px \u5B9E\u5FC3\u6838\u5FC3\u3002\u82E5 ::before \u4E0D\u8BBE opacity\uFF0C",
|
|
481
|
+
" \u5B83\u4F1A\u76D6\u4F4F\u6838\u5FC3\u5E76\u6E32\u67D3\u6210 10px \u5B9E\u5FC3\u5706\uFF08\u6BD4\u5BBF\u4E3B\u660E\u663E\u504F\u5927\u504F\u5B9E\uFF09\u3002 */",
|
|
482
|
+
".ss-hover-status-dot {",
|
|
483
|
+
" position: relative; display: block; flex: none;",
|
|
484
|
+
" width: 10px; height: 10px;",
|
|
485
|
+
" color: #adb2b8;",
|
|
486
|
+
"}",
|
|
487
|
+
".ss-hover-status-dot::before,",
|
|
488
|
+
".ss-hover-status-dot::after {",
|
|
489
|
+
' content: ""; position: absolute; border-radius: 50%; background: currentColor;',
|
|
490
|
+
"}",
|
|
491
|
+
".ss-hover-status-dot::before { inset: 0; opacity: 0.1; }",
|
|
492
|
+
".ss-hover-status-dot::after { inset: 2px; }",
|
|
493
|
+
"/* \u8BED\u4E49\u6620\u5C04\uFF08\u5BF9\u9F50\u5BBF\u4E3B done/ongoing/warning/error \u56DB\u6001\u8272\u503C\uFF09\uFF1A",
|
|
494
|
+
" scheduled \u2192 ongoing \u7070\uFF08\u4E2D\u6027\uFF1A\u5DF2\u6392\u671F\uFF0C\u65E0\u7D27\u8FEB\u6027\uFF09",
|
|
495
|
+
" urgent \u2192 warning \u7425\u73C0\uFF08\u22645min \u5373\u5C06\u89E6\u53D1\uFF09",
|
|
496
|
+
" overdue \u2192 error \u7EA2\uFF08\u5DF2\u5230\u671F\uFF0C\u7B49\u5F85\u4F1A\u8BDD\u7A7A\u95F2\u53D1\u9001\uFF09 */",
|
|
497
|
+
'.ss-hover-status[data-state="urgent"] .ss-hover-status-dot { color: #f59e0b; }',
|
|
498
|
+
'.ss-hover-status[data-state="overdue"] .ss-hover-status-dot { color: #f25a5a; }',
|
|
499
|
+
"",
|
|
500
|
+
"/* ==================== \u8BBE\u7F6E\u9875\uFF08\u5B9A\u65F6\u63D0\u9192\u8868\u5355\uFF09 ==================== */",
|
|
501
|
+
"/* \u89C6\u89C9\u5BF9\u9F50\u5B98\u65B9 SettingsForm\uFF08dsh-client-ui-primitives\uFF09\uFF1A\u88F8\u8868\u5355\uFF0C\u65E0\u5361\u7247\u5BB9\u5668\uFF0C",
|
|
502
|
+
" \u5B57\u6BB5\u6309 section \u5206\u7EC4\uFF0C\u6BCF\u7EC4\u6709\u6807\u9898\uFF0C\u5E95\u90E8\u4FDD\u5B58\u6309\u94AE\u3002\u7C7B\u540D\u7EDF\u4E00 ss- \u524D\u7F00\u3002 */",
|
|
503
|
+
".ss-settings-form {",
|
|
504
|
+
" display: flex; flex-direction: column;",
|
|
505
|
+
"}",
|
|
506
|
+
"/* Section \u5206\u7EC4\uFF08\u5BF9\u9F50\u5B98\u65B9 AZSXYW_section \u7ED3\u6784\uFF09 */",
|
|
507
|
+
".ss-section {",
|
|
508
|
+
" padding: 16px 0;",
|
|
509
|
+
"}",
|
|
510
|
+
".ss-section-heading {",
|
|
511
|
+
" margin: 0; font-size: 13px; font-weight: 600; line-height: 1.5;",
|
|
512
|
+
" color: var(--dsw-alias-label-primary);",
|
|
513
|
+
"}",
|
|
514
|
+
".ss-section-fields {",
|
|
515
|
+
" display: flex; flex-direction: column;",
|
|
516
|
+
"}",
|
|
517
|
+
/* 紧凑字段并排(对齐官方 hF0LTq_limits 网格:两等宽列、间距 16px) */
|
|
518
|
+
".ss-section-fieldsGrid {",
|
|
519
|
+
" display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px;",
|
|
520
|
+
"}",
|
|
521
|
+
/* 帮助按钮(对齐官方 _helpButton_:24×24、radius 8px、12px 圆圈问号图标) */
|
|
522
|
+
".ss-help-btn {",
|
|
523
|
+
" width: 24px; height: 24px; padding: 0; flex: none;",
|
|
524
|
+
" display: inline-flex; align-items: center; justify-content: center;",
|
|
525
|
+
" background: none; border: none; border-radius: 8px; cursor: pointer;",
|
|
526
|
+
" color: var(--dsw-alias-label-tertiary);",
|
|
527
|
+
"}",
|
|
528
|
+
".ss-help-btn:hover, .ss-help-btnOpen { color: var(--dsw-alias-label-secondary); }",
|
|
529
|
+
".ss-help-btn:focus-visible {",
|
|
530
|
+
" outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: -1px;",
|
|
531
|
+
"}",
|
|
532
|
+
/* 展开后的说明区(对齐官方 _help_:12px / 1.6 行高、顶部 10px、次级文字色) */
|
|
533
|
+
".ss-help {",
|
|
534
|
+
" margin: 0; padding: 10px 0 0; font-size: 12px; line-height: 1.6;",
|
|
535
|
+
" color: var(--dsw-alias-label-secondary);",
|
|
536
|
+
"}",
|
|
537
|
+
".ss-help p { margin: 0; }",
|
|
538
|
+
/* Toggle 行(对齐官方子智能体插件的开关行:label 左 + switch 右) */
|
|
539
|
+
".ss-toggle-row {",
|
|
540
|
+
" display: flex; align-items: center; justify-content: space-between;",
|
|
541
|
+
" gap: 16px;",
|
|
542
|
+
"}",
|
|
543
|
+
".ss-toggle-label {",
|
|
544
|
+
" flex: 1; min-width: 0; font-size: 13px; font-weight: 500; line-height: 1.5;",
|
|
545
|
+
" color: var(--dsw-alias-label-primary);",
|
|
546
|
+
"}",
|
|
547
|
+
".ss-toggle-switch {",
|
|
548
|
+
" position: relative; width: 36px; height: 20px; flex: none;",
|
|
549
|
+
" background: var(--dsw-alias-bg-module-platform); border-radius: 999px;",
|
|
550
|
+
" cursor: pointer; transition: background var(--ds-transition-duration-fast) var(--ds-ease-in-out);",
|
|
551
|
+
" border: none; padding: 0;",
|
|
552
|
+
"}",
|
|
553
|
+
'.ss-toggle-switch[aria-checked="true"] { background: var(--dsw-alias-state-business-primary); }',
|
|
554
|
+
".ss-toggle-switch::after {",
|
|
555
|
+
' content: ""; position: absolute; top: 2px; left: 2px;',
|
|
556
|
+
" width: 16px; height: 16px; border-radius: 999px;",
|
|
557
|
+
" background: #fff; transition: transform var(--ds-transition-duration-fast) var(--ds-ease-in-out);",
|
|
558
|
+
"}",
|
|
559
|
+
'.ss-toggle-switch[aria-checked="true"]::after { transform: translateX(16px); }',
|
|
560
|
+
".ss-toggle-switch:disabled { opacity: 0.4; cursor: default; }",
|
|
561
|
+
".ss-toggle-switch:focus-visible {",
|
|
562
|
+
" outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: 2px;",
|
|
563
|
+
"}",
|
|
564
|
+
".ss-toggle-description {",
|
|
565
|
+
" margin: 0; padding: 0 0 12px; font-size: 12px; line-height: 1.6;",
|
|
566
|
+
" color: var(--dsw-alias-label-secondary);",
|
|
567
|
+
"}",
|
|
568
|
+
".ss-label-group {",
|
|
569
|
+
" display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0;",
|
|
405
570
|
"}",
|
|
406
571
|
".ss-card-fieldHead { align-items: center; gap: 8px; display: flex; }",
|
|
407
572
|
".ss-card-label {",
|
|
@@ -420,11 +585,21 @@ var COMPONENTS = [
|
|
|
420
585
|
"}",
|
|
421
586
|
".ss-card-reset:hover:not(:disabled) { color: var(--dsw-alias-label-primary); }",
|
|
422
587
|
".ss-card-reset:disabled { cursor: default; }",
|
|
423
|
-
".ss-card-invalid { color: var(--dsw-alias-
|
|
588
|
+
".ss-card-invalid { color: var(--dsw-alias-state-error-primary); margin: 0; font-size: 12px; line-height: 1.5; }",
|
|
589
|
+
"/*",
|
|
590
|
+
" * \u9009\u9879\u98CE\u9669\u63D0\u793A\uFF08steer \u53EF\u80FD\u88AB\u56DE\u5408\u542F\u52A8\u541E\u6389 / discard \u4F1A\u8BA9\u9759\u9ED8\u4E22\u5931\u590D\u6D3B\uFF09\u3002",
|
|
591
|
+
' * \u5E38\u9A7B\u663E\u793A\u800C\u975E tooltip\uFF1A\u8FD9\u51E0\u9879\u914D\u7F6E\u7684\u540E\u679C\u662F"\u9759\u9ED8\u5931\u8D25"\uFF0C\u85CF\u5728\u60AC\u6D6E\u63D0\u793A\u91CC\u7B49\u4E8E\u6CA1\u6709\u3002',
|
|
592
|
+
" */",
|
|
593
|
+
".ss-card-risk {",
|
|
594
|
+
" margin: 6px 0 0; padding: 6px 9px; border-radius: 6px;",
|
|
595
|
+
" font-size: 12px; line-height: 1.55;",
|
|
596
|
+
" color: var(--dsw-alias-state-warn-secondary);",
|
|
597
|
+
" background: color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 12%, transparent);",
|
|
598
|
+
" border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-secondary) 30%, transparent);",
|
|
599
|
+
"}",
|
|
424
600
|
".ss-card-footer {",
|
|
425
|
-
" border-top: 1px solid var(--dsw-alias-border-l2);",
|
|
426
601
|
" justify-content: flex-end; align-items: center; gap: 8px;",
|
|
427
|
-
" padding:
|
|
602
|
+
" padding-top: 16px; display: flex;",
|
|
428
603
|
"}",
|
|
429
604
|
".ss-card-failed {",
|
|
430
605
|
" min-width: 0; color: var(--dsw-alias-label-error); flex: 1; margin: 0;",
|
|
@@ -432,7 +607,7 @@ var COMPONENTS = [
|
|
|
432
607
|
"}",
|
|
433
608
|
".ss-card-discard, .ss-card-save {",
|
|
434
609
|
" appearance: none; font: inherit; cursor: pointer;",
|
|
435
|
-
" border: 1px solid transparent; border-radius:
|
|
610
|
+
" border: 1px solid transparent; border-radius: var(--dsw-radius-md);",
|
|
436
611
|
" padding: 5px 14px; font-size: 13px; line-height: 1.5;",
|
|
437
612
|
"}",
|
|
438
613
|
".ss-card-discard { border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-secondary); background: none; }",
|
|
@@ -441,7 +616,7 @@ var COMPONENTS = [
|
|
|
441
616
|
".ss-card-save:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover); }",
|
|
442
617
|
".ss-card-discard:disabled, .ss-card-save:disabled { opacity: 0.4; cursor: default; }",
|
|
443
618
|
".ss-card-discard:focus-visible, .ss-card-save:focus-visible {",
|
|
444
|
-
" outline:
|
|
619
|
+
" outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: 1px;",
|
|
445
620
|
"}"
|
|
446
621
|
].join("\n");
|
|
447
622
|
function injectStyles() {
|
|
@@ -561,6 +736,16 @@ var zh = {
|
|
|
561
736
|
steerAlreadySent: "\u5DF2\u63D2\u8BDD",
|
|
562
737
|
steerFailed: "\u63D2\u8BDD\u5931\u8D25\uFF1A{message}",
|
|
563
738
|
steerUnsupportedKind: "\u5468\u671F\u63D0\u9192\u6682\u4E0D\u652F\u6301\u63D2\u8BDD",
|
|
739
|
+
/** 投递未确认(宿主保留条目,可重试) */
|
|
740
|
+
steerUnconfirmed: "\u6295\u9012\u672A\u786E\u8BA4\uFF0C\u63D0\u9192\u5DF2\u4FDD\u7559\uFF0C\u53EF\u91CD\u8BD5",
|
|
741
|
+
steerRetry: "\u91CD\u8BD5",
|
|
742
|
+
/** 暂停 / 恢复(仅 after;原地冻结进度条) */
|
|
743
|
+
pauseTask: "\u6682\u505C\u63D0\u9192",
|
|
744
|
+
resumeTask: "\u6062\u590D\u63D0\u9192",
|
|
745
|
+
pausedLabel: "\u5DF2\u6682\u505C",
|
|
746
|
+
pauseFailed: "\u6682\u505C\u5931\u8D25\uFF1A{message}",
|
|
747
|
+
resumeFailed: "\u6062\u590D\u5931\u8D25\uFF1A{message}",
|
|
748
|
+
pauseUnsupportedKind: "\u4EC5\u652F\u6301\u6682\u505C\u300C\u518D\u7B49 N \u5206\u949F\u300D\u7C7B\u63D0\u9192",
|
|
564
749
|
/** dock 折叠头计数(QueueDock「N 条排队消息」样式) */
|
|
565
750
|
panelCount: "{n} \u6761\u5B9A\u65F6\u63D0\u9192",
|
|
566
751
|
/** 精准倒计时(dock 行 + 折叠头) */
|
|
@@ -587,6 +772,13 @@ var zh = {
|
|
|
587
772
|
presenceOverdueAria: "\u6709 {n} \u6761\u5B9A\u65F6\u63D0\u9192\u5DF2\u5230\u671F\uFF0C\u7B49\u5F85\u53D1\u9001",
|
|
588
773
|
presenceTooltip: "\u4E0B\u6B21 {time} \xB7 {n} \u6761\u4EFB\u52A1",
|
|
589
774
|
presenceOverdueTooltip: "\u5DF2\u5230\u671F \xB7 {n} \u6761\u4EFB\u52A1\uFF08\u7B49\u5F85\u4F1A\u8BDD\u7A7A\u95F2\u53D1\u9001\uFF09",
|
|
775
|
+
/** 全部任务都处于暂停:没有排定的触发时刻,故不显示"下次"或"已到期"。 */
|
|
776
|
+
presencePausedAria: "\u6709 {n} \u6761\u5B9A\u65F6\u63D0\u9192\u5DF2\u6682\u505C",
|
|
777
|
+
presencePausedTooltip: "\u5DF2\u6682\u505C \xB7 {n} \u6761\u4EFB\u52A1",
|
|
778
|
+
/** 悬浮卡片状态区追加行(与宿主 hoverStatus 同构,纯追加不改写既有状态) */
|
|
779
|
+
hoverScheduleStatus: "\u6709 {n} \u6761\u5B9A\u65F6\u63D0\u9192\uFF0C\u4E0B\u6B21 {time}",
|
|
780
|
+
hoverScheduleOverdue: "\u6709 {n} \u6761\u5B9A\u65F6\u63D0\u9192\u5DF2\u5230\u671F\uFF0C\u7B49\u5F85\u53D1\u9001",
|
|
781
|
+
hoverSchedulePaused: "\u6709 {n} \u6761\u5B9A\u65F6\u63D0\u9192\u5DF2\u6682\u505C",
|
|
590
782
|
relMinutes: "{m} \u5206\u540E",
|
|
591
783
|
relHours: "{h} \u5C0F\u65F6\u540E",
|
|
592
784
|
relTomorrow: "\u660E\u5929 {time}",
|
|
@@ -623,6 +815,15 @@ var en = {
|
|
|
623
815
|
steerAlreadySent: "Steered",
|
|
624
816
|
steerFailed: "Steer failed: {message}",
|
|
625
817
|
steerUnsupportedKind: "Recurring reminders can't be steered",
|
|
818
|
+
/** Delivery unconfirmed (host kept the item; retryable) */
|
|
819
|
+
steerUnconfirmed: "Delivery unconfirmed \u2014 reminder kept, you can retry",
|
|
820
|
+
steerRetry: "Retry",
|
|
821
|
+
pauseTask: "Pause reminder",
|
|
822
|
+
resumeTask: "Resume reminder",
|
|
823
|
+
pausedLabel: "Paused",
|
|
824
|
+
pauseFailed: "Pause failed: {message}",
|
|
825
|
+
resumeFailed: "Resume failed: {message}",
|
|
826
|
+
pauseUnsupportedKind: 'Only "wait N minutes" (after) reminders can be paused',
|
|
626
827
|
panelCount: "{n} scheduled reminders",
|
|
627
828
|
countdownLeft: "in",
|
|
628
829
|
dueAnyMoment: "Sending soon",
|
|
@@ -641,6 +842,11 @@ var en = {
|
|
|
641
842
|
presenceOverdueAria: "{n} scheduled reminder(s) due, waiting to send",
|
|
642
843
|
presenceTooltip: "Next at {time} \xB7 {n} task(s)",
|
|
643
844
|
presenceOverdueTooltip: "Due \xB7 {n} task(s) (sends when the session is idle)",
|
|
845
|
+
presencePausedAria: "{n} paused reminder(s)",
|
|
846
|
+
presencePausedTooltip: "Paused \xB7 {n} task(s)",
|
|
847
|
+
hoverScheduleStatus: "{n} scheduled reminder(s), next {time}",
|
|
848
|
+
hoverScheduleOverdue: "{n} scheduled reminder(s) due, waiting to send",
|
|
849
|
+
hoverSchedulePaused: "{n} paused reminder(s)",
|
|
644
850
|
relMinutes: "in {m} min",
|
|
645
851
|
relHours: "in {h} h",
|
|
646
852
|
relTomorrow: "Tomorrow {time}",
|
|
@@ -860,11 +1066,11 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
860
1066
|
function scheduleStateOf(item, now) {
|
|
861
1067
|
return Date.parse(item.scheduled_at) <= now ? "overdue" : "scheduled";
|
|
862
1068
|
}
|
|
863
|
-
function TaskList({ items
|
|
864
|
-
if (
|
|
1069
|
+
function TaskList({ items, now, deliveredIds, cancellingIds, onDelete, timeZone, t, lang }) {
|
|
1070
|
+
if (items.length === 0) {
|
|
865
1071
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ss-empty", "data-ss-empty": "", children: t.emptyTasks });
|
|
866
1072
|
}
|
|
867
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "ss-tasks", "data-ss-tasks": "", children:
|
|
1073
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "ss-tasks", "data-ss-tasks": "", children: items.map((item) => {
|
|
868
1074
|
const dotState = deliveredIds?.has(item.id) ? "delivered" : cancellingIds?.has(item.id) ? "cancelled" : scheduleStateOf(item, now);
|
|
869
1075
|
const isCancelled = dotState === "cancelled";
|
|
870
1076
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: isCancelled ? "ss-task cancelled" : "ss-task", "data-ss-task": item.id, children: [
|
|
@@ -1237,12 +1443,12 @@ function useSchedules(inject2) {
|
|
|
1237
1443
|
if (pendingSync.length === 0 || !sessionId) return;
|
|
1238
1444
|
let cancelled = false;
|
|
1239
1445
|
const syncAll = async () => {
|
|
1240
|
-
const
|
|
1241
|
-
for (const item of
|
|
1446
|
+
const items = [...pendingSync];
|
|
1447
|
+
for (const item of items) {
|
|
1242
1448
|
if (cancelled) return;
|
|
1243
1449
|
try {
|
|
1244
1450
|
const accepted = await callCommand(sessionId, createLine(item.payload));
|
|
1245
|
-
if (accepted && !cancelled) {
|
|
1451
|
+
if (accepted.matched && !cancelled) {
|
|
1246
1452
|
removePending(item.clientSeq);
|
|
1247
1453
|
setPendingSync((prev) => prev.filter((p) => p.clientSeq !== item.clientSeq));
|
|
1248
1454
|
}
|
|
@@ -1268,7 +1474,7 @@ function useSchedules(inject2) {
|
|
|
1268
1474
|
setBusy(true);
|
|
1269
1475
|
try {
|
|
1270
1476
|
const accepted = await callCommand(sessionId, createLine(inputPayload));
|
|
1271
|
-
if (!accepted) {
|
|
1477
|
+
if (!accepted.matched) {
|
|
1272
1478
|
appendPending(inputPayload);
|
|
1273
1479
|
setPendingSync((prev) => [...prev, { clientSeq: Date.now(), createdAt: Date.now(), payload: inputPayload }]);
|
|
1274
1480
|
throw new Error(CLIENT_ERR.NOT_ACCEPTED);
|
|
@@ -1457,65 +1663,79 @@ function SchedButton(props) {
|
|
|
1457
1663
|
}
|
|
1458
1664
|
|
|
1459
1665
|
// src/client/components/ScheduleDock.tsx
|
|
1460
|
-
var import_react8 = require("react");
|
|
1461
|
-
|
|
1462
|
-
// src/client/toast.tsx
|
|
1463
|
-
var import_client = require("react-dom/client");
|
|
1464
1666
|
var import_react7 = require("react");
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
var
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
function
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1667
|
+
|
|
1668
|
+
// src/bar-geometry.ts
|
|
1669
|
+
var BAR_UNIT_MS = 36e5;
|
|
1670
|
+
function barWindow(item, end, now, firstSeen) {
|
|
1671
|
+
let start;
|
|
1672
|
+
if (item.kind === "after") {
|
|
1673
|
+
const windowSec = item.window_seconds ?? item.after_seconds;
|
|
1674
|
+
if (windowSec !== void 0) {
|
|
1675
|
+
start = end - windowSec * 1e3;
|
|
1676
|
+
} else if (item.created_at !== void 0) {
|
|
1677
|
+
const parsed = Date.parse(item.created_at);
|
|
1678
|
+
start = Number.isNaN(parsed) ? firstSeen.get(item.id) ?? now : parsed;
|
|
1679
|
+
} else {
|
|
1680
|
+
start = firstSeen.get(item.id) ?? now;
|
|
1681
|
+
}
|
|
1682
|
+
} else if (item.kind === "every" && item.every_seconds !== void 0) {
|
|
1683
|
+
start = end - item.every_seconds * 1e3;
|
|
1684
|
+
} else if (item.created_at !== void 0) {
|
|
1685
|
+
const parsed = Date.parse(item.created_at);
|
|
1686
|
+
start = Number.isNaN(parsed) ? firstSeen.get(item.id) ?? now : parsed;
|
|
1687
|
+
} else {
|
|
1688
|
+
start = firstSeen.get(item.id) ?? now;
|
|
1689
|
+
}
|
|
1690
|
+
return { start, totalMs: Math.max(1e3, end - start) };
|
|
1691
|
+
}
|
|
1692
|
+
function ratioAt(item, at, firstSeen) {
|
|
1693
|
+
const end = Date.parse(item.scheduled_at);
|
|
1694
|
+
const { start, totalMs } = barWindow(item, end, at, firstSeen);
|
|
1695
|
+
const elapsedMs = Math.min(Math.max(at - start, 0), totalMs);
|
|
1696
|
+
return {
|
|
1697
|
+
totalRatio: elapsedMs / totalMs,
|
|
1698
|
+
count: Math.max(1, Math.ceil(totalMs / BAR_UNIT_MS))
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
function barSegments(item, now, firstSeen) {
|
|
1702
|
+
return ratioAt(item, now, firstSeen);
|
|
1703
|
+
}
|
|
1704
|
+
function frozenBarSegments(item, frozenLeftMs, firstSeen, renderedAt) {
|
|
1705
|
+
const windowSec = item.window_seconds ?? item.after_seconds;
|
|
1706
|
+
if (windowSec === void 0 || windowSec <= 0) {
|
|
1707
|
+
const end = Date.parse(item.scheduled_at);
|
|
1708
|
+
const pausedAt2 = Date.parse(item.paused_at ?? "");
|
|
1709
|
+
const exact = Number.isNaN(pausedAt2) ? end - Math.floor(frozenLeftMs / 1e3) * 1e3 : pausedAt2;
|
|
1710
|
+
return ratioAt(item, Number.isNaN(exact) ? end : exact, firstSeen);
|
|
1711
|
+
}
|
|
1712
|
+
const totalMs = Math.max(1e3, windowSec * 1e3);
|
|
1713
|
+
let leftMs = Math.min(Math.max(frozenLeftMs, 0), totalMs);
|
|
1714
|
+
const pausedAt = Date.parse(item.paused_at ?? "");
|
|
1715
|
+
if (renderedAt !== void 0 && !Number.isNaN(pausedAt) && pausedAt > renderedAt) {
|
|
1716
|
+
leftMs = Math.min(leftMs + (pausedAt - renderedAt), totalMs);
|
|
1717
|
+
}
|
|
1718
|
+
const elapsedMs = Math.min(Math.max(totalMs - leftMs, 0), totalMs);
|
|
1719
|
+
return {
|
|
1720
|
+
totalRatio: elapsedMs / totalMs,
|
|
1721
|
+
count: Math.max(1, Math.ceil(totalMs / BAR_UNIT_MS))
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
function planBarAnimation(totalMs, elapsedMs) {
|
|
1725
|
+
if (!Number.isFinite(totalMs) || totalMs <= 0) return void 0;
|
|
1726
|
+
const raw = Number.isFinite(elapsedMs) ? elapsedMs : 0;
|
|
1727
|
+
const clamped = Math.min(Math.max(raw, 0), totalMs);
|
|
1728
|
+
const elapsed = clamped === 0 ? 0 : clamped;
|
|
1729
|
+
if (elapsed >= totalMs) return void 0;
|
|
1730
|
+
return {
|
|
1731
|
+
durationMs: totalMs,
|
|
1732
|
+
// -0 会在 CSS 里序列化成难看的 "-0ms",故显式归零
|
|
1733
|
+
delayMs: elapsed === 0 ? 0 : -elapsed
|
|
1514
1734
|
};
|
|
1515
1735
|
}
|
|
1516
1736
|
|
|
1517
1737
|
// src/client/components/ScheduleDock.tsx
|
|
1518
|
-
var
|
|
1738
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1519
1739
|
function formatCountdown(ms) {
|
|
1520
1740
|
const total = Math.max(0, Math.round(ms / 1e3));
|
|
1521
1741
|
const h = Math.floor(total / 3600);
|
|
@@ -1525,53 +1745,151 @@ function formatCountdown(ms) {
|
|
|
1525
1745
|
const ss = String(s).padStart(2, "0");
|
|
1526
1746
|
return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
1527
1747
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
start = end - item.after_seconds * 1e3;
|
|
1534
|
-
} else if (item.kind === "every" && item.every_seconds !== void 0) {
|
|
1535
|
-
start = end - item.every_seconds * 1e3;
|
|
1536
|
-
} else if (item.created_at !== void 0) {
|
|
1537
|
-
const parsed = Date.parse(item.created_at);
|
|
1538
|
-
start = Number.isNaN(parsed) ? firstSeen.get(item.id) ?? now : parsed;
|
|
1539
|
-
} else {
|
|
1540
|
-
start = firstSeen.get(item.id) ?? now;
|
|
1748
|
+
function prefersReducedMotion() {
|
|
1749
|
+
try {
|
|
1750
|
+
return globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
1751
|
+
} catch {
|
|
1752
|
+
return false;
|
|
1541
1753
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
|
|
1754
|
+
}
|
|
1755
|
+
function planBarAnimationFor(item, now, firstSeen) {
|
|
1756
|
+
const end = Date.parse(item.scheduled_at);
|
|
1757
|
+
if (!Number.isFinite(end)) return void 0;
|
|
1758
|
+
const { start, totalMs } = barWindow(item, end, now, firstSeen);
|
|
1759
|
+
return planBarAnimation(totalMs, now - start);
|
|
1547
1760
|
}
|
|
1548
1761
|
var deleteLine2 = (id) => `/user-schedule-delete ${JSON.stringify({ id })}`;
|
|
1549
1762
|
var editLine = (id, prompt) => `/user-schedule-edit ${JSON.stringify({ id, prompt })}`;
|
|
1550
1763
|
var steerLine = (id) => `/user-schedule-steer-now ${JSON.stringify({ id })}`;
|
|
1764
|
+
var STEER_CONFIRM_WINDOW_MS = 2600;
|
|
1765
|
+
var pauseLine = (id) => `/user-schedule-pause ${JSON.stringify({ id })}`;
|
|
1766
|
+
var resumeLine = (uid) => `/user-schedule-resume ${JSON.stringify({ uid })}`;
|
|
1767
|
+
function canPause(item, now) {
|
|
1768
|
+
if (item.status === "paused") return false;
|
|
1769
|
+
if (item.kind !== "after") return false;
|
|
1770
|
+
const epoch = Date.parse(item.scheduled_at);
|
|
1771
|
+
return Number.isFinite(epoch) && epoch > now;
|
|
1772
|
+
}
|
|
1551
1773
|
function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
1552
1774
|
const now = useNow(1e3);
|
|
1553
1775
|
const { t } = useSchedT(locale);
|
|
1554
1776
|
const projection = typeof useProjection === "function" ? useProjection("userSchedules") : void 0;
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1777
|
+
const [dismissedPaused, setDismissedPaused] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1778
|
+
(0, import_react7.useEffect)(() => {
|
|
1779
|
+
const live = projection?.schedules;
|
|
1780
|
+
if (live === void 0) return;
|
|
1781
|
+
setDismissedPaused((current) => {
|
|
1782
|
+
if (current.size === 0) return current;
|
|
1783
|
+
let changed = false;
|
|
1784
|
+
const nextSet = new Set(current);
|
|
1785
|
+
for (const item of live) {
|
|
1786
|
+
if (item.status !== "paused" && nextSet.delete(item.id)) changed = true;
|
|
1787
|
+
}
|
|
1788
|
+
return changed ? nextSet : current;
|
|
1789
|
+
});
|
|
1790
|
+
}, [projection]);
|
|
1791
|
+
const [promptOverrides, setPromptOverrides] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
|
|
1792
|
+
(0, import_react7.useEffect)(() => {
|
|
1793
|
+
const live = projection?.schedules;
|
|
1794
|
+
if (live === void 0 || promptOverrides.size === 0) return;
|
|
1795
|
+
setPromptOverrides((current) => {
|
|
1796
|
+
if (current.size === 0) return current;
|
|
1797
|
+
let changed = false;
|
|
1798
|
+
const nextMap = new Map(current);
|
|
1799
|
+
for (const [id, text] of current) {
|
|
1800
|
+
const item = live.find((s) => s.id === id);
|
|
1801
|
+
if (item === void 0 || item.prompt === text) {
|
|
1802
|
+
nextMap.delete(id);
|
|
1803
|
+
changed = true;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
return changed ? nextMap : current;
|
|
1807
|
+
});
|
|
1808
|
+
}, [projection, promptOverrides]);
|
|
1809
|
+
const schedules = (0, import_react7.useMemo)(
|
|
1810
|
+
() => (projection?.schedules ?? []).filter((item) => !(item.status === "paused" && dismissedPaused.has(item.id))).map((item) => {
|
|
1811
|
+
const override = promptOverrides.get(item.id);
|
|
1812
|
+
return override === void 0 ? item : { ...item, prompt: override };
|
|
1813
|
+
}).sort(
|
|
1557
1814
|
(a, b) => Date.parse(a.scheduled_at) - Date.parse(b.scheduled_at)
|
|
1558
1815
|
),
|
|
1559
|
-
[projection]
|
|
1816
|
+
[projection, dismissedPaused, promptOverrides]
|
|
1560
1817
|
);
|
|
1561
|
-
const [collapsed, setCollapsed] = (0,
|
|
1562
|
-
const [cancelling, setCancelling] = (0,
|
|
1563
|
-
const [editingId, setEditingId] = (0,
|
|
1564
|
-
const [editingText, setEditingText] = (0,
|
|
1565
|
-
const [editErr, setEditErr] = (0,
|
|
1566
|
-
const [saving, setSaving] = (0,
|
|
1567
|
-
const [steering, setSteering] = (0,
|
|
1568
|
-
const [steerDone, setSteerDone] = (0,
|
|
1569
|
-
const [steerErr, setSteerErr] = (0,
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1818
|
+
const [collapsed, setCollapsed] = (0, import_react7.useState)(true);
|
|
1819
|
+
const [cancelling, setCancelling] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1820
|
+
const [editingId, setEditingId] = (0, import_react7.useState)(null);
|
|
1821
|
+
const [editingText, setEditingText] = (0, import_react7.useState)("");
|
|
1822
|
+
const [editErr, setEditErr] = (0, import_react7.useState)(null);
|
|
1823
|
+
const [saving, setSaving] = (0, import_react7.useState)(false);
|
|
1824
|
+
const [steering, setSteering] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1825
|
+
const [steerDone, setSteerDone] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1826
|
+
const [steerErr, setSteerErr] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
|
|
1827
|
+
const [steerUnconfirmed, setSteerUnconfirmed] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1828
|
+
(0, import_react7.useEffect)(() => {
|
|
1829
|
+
const live = projection?.schedules;
|
|
1830
|
+
if (live === void 0) return;
|
|
1831
|
+
const present = new Set(live.filter((item) => item.status !== "paused").map((item) => item.id));
|
|
1832
|
+
setSteerDone((current) => {
|
|
1833
|
+
if (current.size === 0) return current;
|
|
1834
|
+
let changed = false;
|
|
1835
|
+
const next2 = new Set(current);
|
|
1836
|
+
for (const id of current) {
|
|
1837
|
+
if (!present.has(id)) {
|
|
1838
|
+
next2.delete(id);
|
|
1839
|
+
changed = true;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
return changed ? next2 : current;
|
|
1843
|
+
});
|
|
1844
|
+
}, [projection]);
|
|
1845
|
+
(0, import_react7.useEffect)(() => {
|
|
1846
|
+
if (steerDone.size === 0) return void 0;
|
|
1847
|
+
const timer = window.setTimeout(() => {
|
|
1848
|
+
const live = projection?.schedules;
|
|
1849
|
+
if (live === void 0) return;
|
|
1850
|
+
const present = new Set(live.filter((item) => item.status !== "paused").map((item) => item.id));
|
|
1851
|
+
setSteerUnconfirmed((current) => {
|
|
1852
|
+
let changed = false;
|
|
1853
|
+
const next2 = new Set(current);
|
|
1854
|
+
for (const id of steerDone) {
|
|
1855
|
+
if (present.has(id) && !next2.has(id)) {
|
|
1856
|
+
next2.add(id);
|
|
1857
|
+
changed = true;
|
|
1858
|
+
}
|
|
1859
|
+
if (!present.has(id) && next2.delete(id)) changed = true;
|
|
1860
|
+
}
|
|
1861
|
+
return changed ? next2 : current;
|
|
1862
|
+
});
|
|
1863
|
+
}, STEER_CONFIRM_WINDOW_MS);
|
|
1864
|
+
return () => window.clearTimeout(timer);
|
|
1865
|
+
}, [steerDone, projection]);
|
|
1866
|
+
const [pausing, setPausing] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1867
|
+
const [resuming, setResuming] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1868
|
+
const [pauseErr, setPauseErr] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
|
|
1869
|
+
const firstSeenRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
1870
|
+
const lastActiveAtRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
1871
|
+
const wallClock = Date.now();
|
|
1872
|
+
{
|
|
1873
|
+
const next2 = new Map(lastActiveAtRef.current);
|
|
1874
|
+
let changed = false;
|
|
1875
|
+
for (const item of schedules) {
|
|
1876
|
+
if (item.status === "paused") continue;
|
|
1877
|
+
const prev = next2.get(item.id);
|
|
1878
|
+
if (prev === void 0 || wallClock > prev) {
|
|
1879
|
+
next2.set(item.id, wallClock);
|
|
1880
|
+
changed = true;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
for (const id of [...next2.keys()]) {
|
|
1884
|
+
if (!schedules.some((item) => item.id === id)) {
|
|
1885
|
+
next2.delete(id);
|
|
1886
|
+
changed = true;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
if (changed) lastActiveAtRef.current = next2;
|
|
1890
|
+
}
|
|
1891
|
+
const byId = (0, import_react7.useMemo)(() => new Map(schedules.map((item) => [item.id, item])), [schedules]);
|
|
1892
|
+
(0, import_react7.useEffect)(() => {
|
|
1575
1893
|
let changed = false;
|
|
1576
1894
|
const next2 = new Map(firstSeenRef.current);
|
|
1577
1895
|
for (const [id] of byId) {
|
|
@@ -1587,32 +1905,30 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1587
1905
|
}
|
|
1588
1906
|
}
|
|
1589
1907
|
if (changed) firstSeenRef.current = next2;
|
|
1590
|
-
const prev = prevRef.current;
|
|
1591
|
-
if (prev.size > 0) {
|
|
1592
|
-
for (const [id, prevItem] of prev) {
|
|
1593
|
-
if (byId.has(id)) continue;
|
|
1594
|
-
if (suppressedRef.current.has(id)) continue;
|
|
1595
|
-
if (Date.parse(prevItem.scheduled_at) <= Date.now()) {
|
|
1596
|
-
showReminderToast(prevItem.prompt);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
prevRef.current = byId;
|
|
1601
1908
|
}, [byId]);
|
|
1602
|
-
|
|
1603
|
-
suppressedRef.current = new Set(suppressedRef.current).add(id);
|
|
1604
|
-
};
|
|
1605
|
-
(0, import_react8.useEffect)(() => {
|
|
1909
|
+
(0, import_react7.useEffect)(() => {
|
|
1606
1910
|
if (schedules.length <= 1) setCollapsed(true);
|
|
1607
1911
|
}, [schedules.length]);
|
|
1608
1912
|
if (schedules.length === 0 || sessionId === void 0) return null;
|
|
1609
1913
|
const next = schedules[0];
|
|
1610
1914
|
if (next === void 0) return null;
|
|
1611
1915
|
const expanded = schedules.length === 1 || !collapsed;
|
|
1612
|
-
const handleCancel = (id) => {
|
|
1613
|
-
suppress(id);
|
|
1916
|
+
const handleCancel = (id, status) => {
|
|
1614
1917
|
setCancelling((current) => new Set(current).add(id));
|
|
1615
|
-
|
|
1918
|
+
const restore = () => {
|
|
1919
|
+
setDismissedPaused((current) => {
|
|
1920
|
+
if (!current.has(id)) return current;
|
|
1921
|
+
const nextSet = new Set(current);
|
|
1922
|
+
nextSet.delete(id);
|
|
1923
|
+
return nextSet;
|
|
1924
|
+
});
|
|
1925
|
+
};
|
|
1926
|
+
if (status === "paused") {
|
|
1927
|
+
setDismissedPaused((current) => new Set(current).add(id));
|
|
1928
|
+
}
|
|
1929
|
+
void callCommand(sessionId, deleteLine2(id)).then((outcome) => {
|
|
1930
|
+
if (!outcome.matched) restore();
|
|
1931
|
+
}).catch(restore).finally(() => {
|
|
1616
1932
|
window.setTimeout(() => {
|
|
1617
1933
|
setCancelling((current) => {
|
|
1618
1934
|
const nextSet = new Set(current);
|
|
@@ -1624,16 +1940,19 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1624
1940
|
};
|
|
1625
1941
|
const handleSteer = (item) => {
|
|
1626
1942
|
const id = item.id;
|
|
1627
|
-
|
|
1943
|
+
const wasPaused = item.status === "paused";
|
|
1628
1944
|
setSteering((current) => new Set(current).add(id));
|
|
1629
1945
|
setSteerErr((current) => {
|
|
1630
1946
|
const next2 = new Map(current);
|
|
1631
1947
|
next2.delete(id);
|
|
1632
1948
|
return next2;
|
|
1633
1949
|
});
|
|
1634
|
-
void callCommand(sessionId, steerLine(id)).then((
|
|
1635
|
-
if (
|
|
1950
|
+
void callCommand(sessionId, steerLine(id)).then((outcome) => {
|
|
1951
|
+
if (outcome.matched) {
|
|
1636
1952
|
setSteerDone((current) => new Set(current).add(id));
|
|
1953
|
+
if (wasPaused) {
|
|
1954
|
+
setDismissedPaused((current) => new Set(current).add(id));
|
|
1955
|
+
}
|
|
1637
1956
|
} else {
|
|
1638
1957
|
setSteerErr((current) => new Map(current).set(id, t.editErrNotAccepted));
|
|
1639
1958
|
}
|
|
@@ -1647,6 +1966,67 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1647
1966
|
});
|
|
1648
1967
|
});
|
|
1649
1968
|
};
|
|
1969
|
+
const handleSteerRetry = (item) => {
|
|
1970
|
+
const id = item.id;
|
|
1971
|
+
setSteerUnconfirmed((current) => {
|
|
1972
|
+
if (!current.has(id)) return current;
|
|
1973
|
+
const next2 = new Set(current);
|
|
1974
|
+
next2.delete(id);
|
|
1975
|
+
return next2;
|
|
1976
|
+
});
|
|
1977
|
+
setSteerDone((current) => {
|
|
1978
|
+
if (!current.has(id)) return current;
|
|
1979
|
+
const next2 = new Set(current);
|
|
1980
|
+
next2.delete(id);
|
|
1981
|
+
return next2;
|
|
1982
|
+
});
|
|
1983
|
+
handleSteer(item);
|
|
1984
|
+
};
|
|
1985
|
+
const handlePause = (item) => {
|
|
1986
|
+
const id = item.id;
|
|
1987
|
+
if (!canPause(item, now)) return;
|
|
1988
|
+
setPausing((current) => new Set(current).add(id));
|
|
1989
|
+
setPauseErr((current) => {
|
|
1990
|
+
const next2 = new Map(current);
|
|
1991
|
+
next2.delete(id);
|
|
1992
|
+
return next2;
|
|
1993
|
+
});
|
|
1994
|
+
void callCommand(sessionId, pauseLine(id)).then((outcome) => {
|
|
1995
|
+
if (!outcome.matched) {
|
|
1996
|
+
setPauseErr((current) => new Map(current).set(id, t.editErrNotAccepted));
|
|
1997
|
+
}
|
|
1998
|
+
}).catch(() => {
|
|
1999
|
+
setPauseErr((current) => new Map(current).set(id, t.editErrCommandFailed));
|
|
2000
|
+
}).finally(() => {
|
|
2001
|
+
setPausing((current) => {
|
|
2002
|
+
const next2 = new Set(current);
|
|
2003
|
+
next2.delete(id);
|
|
2004
|
+
return next2;
|
|
2005
|
+
});
|
|
2006
|
+
});
|
|
2007
|
+
};
|
|
2008
|
+
const handleResume = (item) => {
|
|
2009
|
+
const uid = item.id;
|
|
2010
|
+
setResuming((current) => new Set(current).add(uid));
|
|
2011
|
+
setPauseErr((current) => {
|
|
2012
|
+
const next2 = new Map(current);
|
|
2013
|
+
next2.delete(uid);
|
|
2014
|
+
return next2;
|
|
2015
|
+
});
|
|
2016
|
+
void callCommand(sessionId, resumeLine(uid)).then((outcome) => {
|
|
2017
|
+
if (!outcome.matched) {
|
|
2018
|
+
setPauseErr((current) => new Map(current).set(uid, t.editErrNotAccepted));
|
|
2019
|
+
}
|
|
2020
|
+
}).catch(() => {
|
|
2021
|
+
setPauseErr((current) => new Map(current).set(uid, t.editErrCommandFailed));
|
|
2022
|
+
}).finally(() => {
|
|
2023
|
+
setResuming((current) => {
|
|
2024
|
+
const next2 = new Set(current);
|
|
2025
|
+
next2.delete(uid);
|
|
2026
|
+
return next2;
|
|
2027
|
+
});
|
|
2028
|
+
});
|
|
2029
|
+
};
|
|
1650
2030
|
const startEdit = (item) => {
|
|
1651
2031
|
setEditingId(item.id);
|
|
1652
2032
|
setEditingText(item.prompt);
|
|
@@ -1663,12 +2043,15 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1663
2043
|
setEditErr(t.editErrEmpty);
|
|
1664
2044
|
return;
|
|
1665
2045
|
}
|
|
1666
|
-
suppress(id);
|
|
1667
2046
|
setSaving(true);
|
|
1668
2047
|
setEditErr(null);
|
|
1669
|
-
void callCommand(sessionId, editLine(id, trimmed)).then((
|
|
1670
|
-
if (!
|
|
1671
|
-
|
|
2048
|
+
void callCommand(sessionId, editLine(id, trimmed)).then((outcome) => {
|
|
2049
|
+
if (!outcome.matched) {
|
|
2050
|
+
setEditErr(format(t.editErrFailed, { message: t.editErrNotAccepted }));
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
setPromptOverrides((current) => new Map(current).set(id, trimmed));
|
|
2054
|
+
cancelEdit();
|
|
1672
2055
|
}).catch(() => {
|
|
1673
2056
|
setEditErr(format(t.editErrFailed, { message: t.editErrCommandFailed }));
|
|
1674
2057
|
}).finally(() => setSaving(false));
|
|
@@ -1677,8 +2060,8 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1677
2060
|
const epoch = Date.parse(item.scheduled_at);
|
|
1678
2061
|
const state = epoch <= now ? "overdue" : "scheduled";
|
|
1679
2062
|
if (editingId === item.id) {
|
|
1680
|
-
return /* @__PURE__ */ (0,
|
|
1681
|
-
/* @__PURE__ */ (0,
|
|
2063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "ss-dock-row ss-editing", "data-ss-dock-row": item.id, children: [
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1682
2065
|
"input",
|
|
1683
2066
|
{
|
|
1684
2067
|
className: "ss-dock-edit-input",
|
|
@@ -1695,7 +2078,7 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1695
2078
|
"aria-label": `${t.editTask}: ${item.prompt}`
|
|
1696
2079
|
}
|
|
1697
2080
|
),
|
|
1698
|
-
/* @__PURE__ */ (0,
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1699
2082
|
"button",
|
|
1700
2083
|
{
|
|
1701
2084
|
type: "button",
|
|
@@ -1705,60 +2088,98 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1705
2088
|
children: saving ? "\u2026" : t.editSave
|
|
1706
2089
|
}
|
|
1707
2090
|
),
|
|
1708
|
-
/* @__PURE__ */ (0,
|
|
1709
|
-
editErr !== null && /* @__PURE__ */ (0,
|
|
2091
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "ss-dock-action", onClick: cancelEdit, "aria-label": t.editCancel, children: "\u2715" }),
|
|
2092
|
+
editErr !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-edit-err", children: editErr })
|
|
1710
2093
|
] }, item.id);
|
|
1711
2094
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
"
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
2095
|
+
const isPaused = item.status === "paused";
|
|
2096
|
+
const frozenLeft = isPaused ? (item.remaining_seconds ?? 0) * 1e3 : epoch - now;
|
|
2097
|
+
const countdownText = isPaused ? t.pausedLabel : state === "overdue" ? t.dueAnyMoment : `${t.countdownLeft} ${formatCountdown(Math.max(0, frozenLeft))}`;
|
|
2098
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2099
|
+
"li",
|
|
2100
|
+
{
|
|
2101
|
+
className: `ss-dock-row${isPaused ? " ss-paused" : ""}`,
|
|
2102
|
+
"data-ss-dock-row": item.id,
|
|
2103
|
+
children: [
|
|
2104
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ss-dock-preview", children: [
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ss-dock-row-line", children: [
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-row-time", children: formatHhmm(epoch, detectTimeZone()) }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `ss-dock-row-countdown${!isPaused && state === "overdue" ? " ss-due" : ""}${isPaused ? " ss-paused-label" : ""}`, children: countdownText }),
|
|
2108
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-row-prompt", title: item.prompt, children: item.prompt })
|
|
2109
|
+
] }),
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MultiBar, { item, now: wallClock, firstSeen: firstSeenRef.current, t, frozen: isPaused, frozenLeftMs: frozenLeft, renderedAt: lastActiveAtRef.current.get(item.id) ?? wallClock }),
|
|
2111
|
+
(steerErr.get(item.id) ?? pauseErr.get(item.id)) !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-steer-err", children: steerErr.get(item.id) ?? pauseErr.get(item.id) }),
|
|
2112
|
+
steerUnconfirmed.has(item.id) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "ss-dock-steer-warn", role: "status", children: [
|
|
2113
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t.steerUnconfirmed }),
|
|
2114
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "ss-dock-steer-retry", onClick: () => handleSteerRetry(item), children: t.steerRetry })
|
|
2115
|
+
] })
|
|
2116
|
+
] }),
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ss-dock-actions", children: [
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2119
|
+
"button",
|
|
2120
|
+
{
|
|
2121
|
+
type: "button",
|
|
2122
|
+
className: "ss-dock-action",
|
|
2123
|
+
title: t.editTask,
|
|
2124
|
+
"aria-label": t.editTask,
|
|
2125
|
+
onClick: () => startEdit(item),
|
|
2126
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueueEditIcon, {})
|
|
2127
|
+
}
|
|
2128
|
+
),
|
|
2129
|
+
isPaused ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2130
|
+
"button",
|
|
2131
|
+
{
|
|
2132
|
+
type: "button",
|
|
2133
|
+
className: `ss-dock-action ss-paused-active${resuming.has(item.id) ? " ss-pending" : ""}`,
|
|
2134
|
+
title: t.resumeTask,
|
|
2135
|
+
"aria-label": t.resumeTask,
|
|
2136
|
+
disabled: resuming.has(item.id),
|
|
2137
|
+
onClick: () => handleResume(item),
|
|
2138
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueueResumeIcon, {})
|
|
2139
|
+
}
|
|
2140
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2141
|
+
"button",
|
|
2142
|
+
{
|
|
2143
|
+
type: "button",
|
|
2144
|
+
className: `ss-dock-action${pausing.has(item.id) ? " ss-pending" : ""}`,
|
|
2145
|
+
title: canPause(item, now) ? t.pauseTask : t.pauseUnsupportedKind,
|
|
2146
|
+
"aria-label": canPause(item, now) ? t.pauseTask : t.pauseUnsupportedKind,
|
|
2147
|
+
disabled: !canPause(item, now) || pausing.has(item.id),
|
|
2148
|
+
onClick: () => handlePause(item),
|
|
2149
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueuePauseIcon, {})
|
|
2150
|
+
}
|
|
2151
|
+
),
|
|
2152
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2153
|
+
"button",
|
|
2154
|
+
{
|
|
2155
|
+
type: "button",
|
|
2156
|
+
className: "ss-dock-action",
|
|
2157
|
+
title: t.deleteTask,
|
|
2158
|
+
"aria-label": t.deleteTask,
|
|
2159
|
+
onClick: () => handleCancel(item.id, isPaused ? "paused" : "active"),
|
|
2160
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueueDeleteIcon, {})
|
|
2161
|
+
}
|
|
2162
|
+
),
|
|
2163
|
+
item.kind !== "every" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2164
|
+
"button",
|
|
2165
|
+
{
|
|
2166
|
+
type: "button",
|
|
2167
|
+
className: `ss-dock-action ss-dock-action-steer${steering.has(item.id) ? " ss-pending" : ""}${steerDone.has(item.id) ? " ss-done" : ""}`,
|
|
2168
|
+
title: steerDone.has(item.id) ? t.steerAlreadySent : t.steerTask,
|
|
2169
|
+
"aria-label": steerDone.has(item.id) ? t.steerAlreadySent : t.steerTask,
|
|
2170
|
+
disabled: steering.has(item.id) || steerDone.has(item.id),
|
|
2171
|
+
onClick: () => handleSteer(item),
|
|
2172
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueueSendIcon, {})
|
|
2173
|
+
}
|
|
2174
|
+
)
|
|
2175
|
+
] })
|
|
2176
|
+
]
|
|
2177
|
+
},
|
|
2178
|
+
item.id
|
|
2179
|
+
);
|
|
1759
2180
|
};
|
|
1760
|
-
return /* @__PURE__ */ (0,
|
|
1761
|
-
schedules.length > 1 && /* @__PURE__ */ (0,
|
|
2181
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ss-dock-root", "data-ss-dock": "", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ss-dock-body", children: [
|
|
2182
|
+
schedules.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1762
2183
|
"button",
|
|
1763
2184
|
{
|
|
1764
2185
|
type: "button",
|
|
@@ -1766,23 +2187,48 @@ function ScheduleDock({ callCommand, sessionId, useProjection, locale }) {
|
|
|
1766
2187
|
"aria-expanded": !collapsed,
|
|
1767
2188
|
onClick: () => setCollapsed((value) => !value),
|
|
1768
2189
|
children: [
|
|
1769
|
-
/* @__PURE__ */ (0,
|
|
1770
|
-
/* @__PURE__ */ (0,
|
|
1771
|
-
/* @__PURE__ */ (0,
|
|
2190
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-lead", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueueClockIcon, {}) }),
|
|
2191
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ss-dock-count", children: format(t.panelCount, { n: schedules.length }) }),
|
|
2192
|
+
/* @__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)(QueueChevronIcon, {}) })
|
|
1772
2193
|
]
|
|
1773
2194
|
}
|
|
1774
2195
|
),
|
|
1775
|
-
expanded && /* @__PURE__ */ (0,
|
|
2196
|
+
expanded && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "ss-dock-list", children: schedules.map(renderRow) })
|
|
1776
2197
|
] }) });
|
|
1777
2198
|
}
|
|
1778
2199
|
function MultiBar({
|
|
1779
2200
|
item,
|
|
1780
2201
|
now,
|
|
1781
2202
|
firstSeen,
|
|
1782
|
-
t
|
|
2203
|
+
t,
|
|
2204
|
+
frozen = false,
|
|
2205
|
+
frozenLeftMs,
|
|
2206
|
+
renderedAt
|
|
1783
2207
|
}) {
|
|
1784
|
-
const
|
|
1785
|
-
|
|
2208
|
+
const frozenSeg = frozen ? frozenBarSegments(item, frozenLeftMs ?? 0, firstSeen, renderedAt) : void 0;
|
|
2209
|
+
const activeSeg = frozen ? void 0 : barSegments(item, now, firstSeen);
|
|
2210
|
+
const { totalRatio, count } = frozenSeg ?? activeSeg ?? { totalRatio: 0, count: 1 };
|
|
2211
|
+
const phaseAtRef = (0, import_react7.useRef)(null);
|
|
2212
|
+
if (frozen || prefersReducedMotion()) {
|
|
2213
|
+
phaseAtRef.current = null;
|
|
2214
|
+
} else if (phaseAtRef.current === null) {
|
|
2215
|
+
phaseAtRef.current = now;
|
|
2216
|
+
}
|
|
2217
|
+
const anim = frozen || prefersReducedMotion() ? void 0 : planBarAnimationFor(item, phaseAtRef.current ?? now, firstSeen);
|
|
2218
|
+
const fillStyle = anim === void 0 ? { width: `${totalRatio * 100}%` } : {
|
|
2219
|
+
/**
|
|
2220
|
+
* 只注入**参数**(CSS 变量),动画的组装交给 CSS(见 styles.ts)。
|
|
2221
|
+
*
|
|
2222
|
+
* 这样做的原因:动画声明留在 CSS 里,JS 不碰 `animationName` 等简写属性
|
|
2223
|
+
* ——否则会整条覆盖 CSS 的声明。当前只有进度推进一个动画,但保持这个
|
|
2224
|
+
* 分工,将来若再加纹理/状态动效不必回头改 JS。
|
|
2225
|
+
*/
|
|
2226
|
+
["--ss-bar-duration"]: `${anim.durationMs}ms`,
|
|
2227
|
+
// 负 delay 把播放头定位到挂载时刻的绝对相位;此后恒定不变
|
|
2228
|
+
// (重算会让动画多跳——见上方 phaseAtRef 注释)。
|
|
2229
|
+
["--ss-bar-delay"]: `${anim.delayMs}ms`
|
|
2230
|
+
};
|
|
2231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1786
2232
|
"div",
|
|
1787
2233
|
{
|
|
1788
2234
|
className: `ss-dock-bars${count > 4 ? " ss-dock-bars-compact" : ""}`,
|
|
@@ -1791,8 +2237,8 @@ function MultiBar({
|
|
|
1791
2237
|
"aria-valuemax": 100,
|
|
1792
2238
|
"aria-valuenow": Math.round(totalRatio * 100),
|
|
1793
2239
|
"aria-label": t.panelTitle,
|
|
1794
|
-
children: /* @__PURE__ */ (0,
|
|
1795
|
-
count > 1 && /* @__PURE__ */ (0,
|
|
2240
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ss-dock-bar", children: [
|
|
2241
|
+
count > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1796
2242
|
"div",
|
|
1797
2243
|
{
|
|
1798
2244
|
className: "ss-dock-bar-divider",
|
|
@@ -1801,55 +2247,120 @@ function MultiBar({
|
|
|
1801
2247
|
}
|
|
1802
2248
|
}
|
|
1803
2249
|
),
|
|
1804
|
-
/* @__PURE__ */ (0,
|
|
2250
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ss-dock-bar-fill", style: fillStyle })
|
|
1805
2251
|
] })
|
|
1806
2252
|
}
|
|
1807
2253
|
);
|
|
1808
2254
|
}
|
|
1809
2255
|
function QueueClockIcon() {
|
|
1810
|
-
return /* @__PURE__ */ (0,
|
|
2256
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M7.00049 0.199829C3.24488 0.199829 0.199952 3.24408 0.199707 6.99963C0.199707 8.0414 0.434087 9.03061 0.854004 9.91467L1.11279 10.4576L2.19775 9.94202L1.94092 9.39905L1.81787 9.12268C1.5498 8.46885 1.40186 7.75171 1.40186 6.99963C1.4021 3.90808 3.90888 1.40198 7.00049 1.40198C10.0919 1.40219 12.5979 3.90821 12.5981 6.99963C12.5981 10.0913 10.0921 12.5981 7.00049 12.5983C6.36734 12.5983 5.90348 12.5535 5.49268 12.4401C5.08803 12.3283 4.7041 12.1414 4.24463 11.8209C3.57111 11.3511 2.60588 11.1855 1.81006 11.6881L1.79736 11.6959L1.78467 11.7047L1.25537 12.0778L1.65381 13.2672L2.46045 12.6989C2.75029 12.5214 3.18004 12.5442 3.55615 12.8063C4.10063 13.1861 4.60863 13.4423 5.17334 13.5983C5.73194 13.7525 6.31665 13.8004 7.00049 13.8004C10.7561 13.8002 13.8003 10.7553 13.8003 6.99963C13.8 3.24421 10.7559 0.200041 7.00049 0.199829ZM3.81201 7.47327V8.67542H7.11572V7.47327H3.81201ZM3.81201 6.34924H10.2173V5.14709H3.81201V6.34924Z", fill: "currentColor" }) });
|
|
1811
2257
|
}
|
|
1812
2258
|
function QueueChevronIcon() {
|
|
1813
|
-
return /* @__PURE__ */ (0,
|
|
2259
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) });
|
|
1814
2260
|
}
|
|
1815
2261
|
function QueueEditIcon() {
|
|
1816
|
-
return /* @__PURE__ */ (0,
|
|
2262
|
+
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: "M9.94076 1.34942C10.7047 0.90231 11.6503 0.902415 12.4143 1.34942C12.7061 1.52015 12.9688 1.79118 13.3104 2.13284C13.6521 2.47448 13.9231 2.73721 14.0939 3.02894C14.5408 3.79294 14.5409 4.73856 14.0939 5.50251C13.9231 5.79415 13.652 6.05704 13.3104 6.39861L6.65932 13.0497C6.28068 13.4284 6.00695 13.7108 5.66543 13.9097C5.32391 14.1085 4.94315 14.2074 4.42705 14.3498L3.24394 14.6761C2.77527 14.8054 2.34538 14.9262 2.00131 14.9684C1.65196 15.0112 1.17964 15.0013 0.810764 14.6325C0.441921 14.2637 0.432107 13.7913 0.47486 13.442C0.517035 13.0979 0.6379 12.668 0.767181 12.1993L1.09352 11.0162C1.23588 10.5001 1.33481 10.1193 1.5336 9.77784C1.7325 9.43632 2.0149 9.1626 2.39355 8.78395L9.04466 2.13284C9.38625 1.79126 9.64911 1.52016 9.94076 1.34942ZM15.5427 14.8398H7.55223L8.96707 13.425H15.5427V14.8398ZM3.39382 9.78422C2.965 10.213 2.84244 10.3436 2.75709 10.49C2.67183 10.6366 2.61862 10.8079 2.45733 11.3925L2.13099 12.5756C2.00183 13.0439 1.92194 13.3419 1.88863 13.5536C2.10041 13.5204 2.39872 13.4416 2.86764 13.3123L4.05075 12.9859C4.63544 12.8246 4.80669 12.7715 4.95323 12.6862C5.09968 12.6008 5.23022 12.4783 5.65905 12.0494L10.721 6.98644L8.45577 4.72121L3.39382 9.78422ZM11.7 2.57079C11.3774 2.38198 10.9777 2.38198 10.6551 2.57079C10.5602 2.62647 10.4487 2.72931 10.0449 3.13311L9.45604 3.72094L11.7213 5.98617L12.3102 5.39833C12.7139 4.99457 12.8168 4.88307 12.8725 4.78818C13.0613 4.46561 13.0612 4.06585 12.8725 3.74326C12.8169 3.64827 12.7146 3.53752 12.3102 3.13311C11.9057 2.72863 11.795 2.6264 11.7 2.57079Z", fill: "currentColor" }) });
|
|
1817
2263
|
}
|
|
1818
2264
|
function QueueDeleteIcon() {
|
|
1819
|
-
return /* @__PURE__ */ (0,
|
|
2265
|
+
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: "M14.4782 4.84067L14.2138 10.1152C14.1102 12.1872 14.067 13.0115 13.3866 13.9607C13.1044 14.3546 12.7498 14.6912 12.3424 14.9535C11.8239 15.2872 11.2415 15.4316 10.5585 15.4998C9.88727 15.5668 9.04946 15.5656 7.99998 15.5656C6.95051 15.5656 6.1127 15.5668 5.44142 15.4998C4.75851 15.4316 4.17602 15.2872 3.65753 14.9535C3.25012 14.6912 2.89559 14.3546 2.61332 13.9607C1.93296 13.0115 1.88979 12.1872 1.78619 10.1152L1.52179 4.84067L2.89006 4.77277L3.15343 10.0463C3.26221 12.2218 3.32452 12.6015 3.72646 13.1624C3.90825 13.4161 4.13686 13.6334 4.39927 13.8023C4.66204 13.9714 5.00263 14.0792 5.57825 14.1367C6.16562 14.1953 6.92298 14.1963 7.99998 14.1963C9.07699 14.1963 9.83434 14.1953 10.4217 14.1367C10.9973 14.0792 11.3379 13.9714 11.6007 13.8023C11.8631 13.6334 12.0917 13.4161 12.2735 13.1624C12.6755 12.6015 12.7378 12.2218 12.8465 10.0463L13.1099 4.77277L14.4782 4.84067ZM5.43011 6.22849H6.7994V11.3909H5.43011V6.22849ZM9.20056 6.22849H10.5699V11.3909H9.20056V6.22849ZM8.53597 0.434431C9.17976 0.434431 9.6522 0.426926 10.0966 0.571258C10.2357 0.616451 10.3717 0.672554 10.502 0.738948C10.9182 0.951107 11.2464 1.29099 11.7015 1.74612L12.4978 2.54136H15.3742V3.91169H0.625732V2.54136H3.50218L4.29845 1.74612C4.75358 1.29099 5.08174 0.951107 5.49801 0.738948C5.62831 0.672554 5.76425 0.616451 5.90334 0.571258C6.34776 0.426926 6.82021 0.434431 7.46399 0.434431H8.53597ZM7.46399 1.80476C6.73208 1.80476 6.51641 1.81187 6.32617 1.87369C6.25545 1.89667 6.18668 1.92533 6.12041 1.95907C5.96398 2.03878 5.82348 2.16253 5.44142 2.54136H10.5585C10.1765 2.16253 10.036 2.03878 9.87955 1.95907C9.81329 1.92533 9.74452 1.89667 9.6738 1.87369C9.48356 1.81187 9.26789 1.80476 8.53597 1.80476H7.46399Z", fill: "currentColor" }) });
|
|
1820
2266
|
}
|
|
1821
2267
|
function QueueSendIcon() {
|
|
1822
|
-
return /* @__PURE__ */ (0,
|
|
2268
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M7.24707 1.01771C7.52897 1.07653 7.77619 1.19694 8.00391 1.38001C8.19202 1.53136 8.39884 1.73784 8.61914 1.95814L12.6396 5.9806L11.6299 6.99134L7.71484 3.0763V13.0001H6.28516V3.0763L2.36914 6.99134L1.35938 5.9806L5.38086 1.95814C5.60116 1.73784 5.80798 1.53136 5.99609 1.38001C6.19476 1.22027 6.4385 1.06739 6.75195 1.01771C6.91296 0.992304 7.07471 0.997504 7.24707 1.01771Z", fill: "currentColor" }) });
|
|
2269
|
+
}
|
|
2270
|
+
function QueuePauseIcon() {
|
|
2271
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
2272
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "3.6", y: "2.6", width: "3.2", height: "10.8", rx: "1.1", fill: "currentColor" }),
|
|
2273
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "9.2", y: "2.6", width: "3.2", height: "10.8", rx: "1.1", fill: "currentColor" })
|
|
2274
|
+
] });
|
|
2275
|
+
}
|
|
2276
|
+
function QueueResumeIcon() {
|
|
2277
|
+
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" }) });
|
|
1823
2278
|
}
|
|
1824
2279
|
|
|
1825
2280
|
// src/client/components/SchedulerSettingsCard.tsx
|
|
1826
|
-
var
|
|
1827
|
-
var
|
|
1828
|
-
var
|
|
1829
|
-
{
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
2281
|
+
var import_react8 = require("react");
|
|
2282
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2283
|
+
var SECTIONS = [
|
|
2284
|
+
{
|
|
2285
|
+
id: "display",
|
|
2286
|
+
titleKey: "sectionDisplay",
|
|
2287
|
+
layout: "stack",
|
|
2288
|
+
fields: [
|
|
2289
|
+
{
|
|
2290
|
+
kind: "boolean",
|
|
2291
|
+
key: "showButton",
|
|
2292
|
+
labelKey: "showButtonLabel",
|
|
2293
|
+
hintKey: "showButtonHint",
|
|
2294
|
+
defaultValue: true
|
|
2295
|
+
},
|
|
2296
|
+
{
|
|
2297
|
+
kind: "enum",
|
|
2298
|
+
key: "clockIconStyle",
|
|
2299
|
+
labelKey: "clockIconStyleLabel",
|
|
2300
|
+
hintKey: "clockIconStyleHint",
|
|
2301
|
+
options: ["auto", "modern", "classic"],
|
|
2302
|
+
defaultValue: "auto"
|
|
2303
|
+
},
|
|
2304
|
+
{ kind: "number", key: "maxSchedules", labelKey: "maxSchedulesLabel", hintKey: "maxSchedulesHint", defaultValue: 100 }
|
|
2305
|
+
]
|
|
2306
|
+
},
|
|
2307
|
+
{
|
|
2308
|
+
id: "time",
|
|
2309
|
+
titleKey: "sectionTime",
|
|
2310
|
+
layout: "grid",
|
|
2311
|
+
fields: [
|
|
2312
|
+
{ kind: "time", key: "workStart", labelKey: "workStartLabel", hintKey: "smartWindowHint" },
|
|
2313
|
+
{ kind: "time", key: "workEnd", labelKey: "workEndLabel" },
|
|
2314
|
+
{ kind: "time", key: "lunchStart", labelKey: "lunchStartLabel" },
|
|
2315
|
+
{ kind: "time", key: "lunchEnd", labelKey: "lunchEndLabel" },
|
|
2316
|
+
{ kind: "time", key: "eveningEnd", labelKey: "eveningEndLabel" }
|
|
2317
|
+
]
|
|
2318
|
+
},
|
|
2319
|
+
{
|
|
2320
|
+
id: "delivery",
|
|
2321
|
+
titleKey: "sectionDelivery",
|
|
2322
|
+
layout: "stack",
|
|
2323
|
+
fields: [
|
|
2324
|
+
{
|
|
2325
|
+
kind: "enum",
|
|
2326
|
+
key: "steerDelivery",
|
|
2327
|
+
labelKey: "steerDeliveryLabel",
|
|
2328
|
+
hintKey: "steerDeliveryHint",
|
|
2329
|
+
options: ["followup", "steer"],
|
|
2330
|
+
defaultValue: "followup"
|
|
2331
|
+
},
|
|
2332
|
+
{
|
|
2333
|
+
kind: "boolean",
|
|
2334
|
+
key: "confirmDelivery",
|
|
2335
|
+
labelKey: "confirmDeliveryLabel",
|
|
2336
|
+
hintKey: "confirmDeliveryHint",
|
|
2337
|
+
defaultValue: true
|
|
2338
|
+
},
|
|
2339
|
+
{
|
|
2340
|
+
kind: "enum",
|
|
2341
|
+
key: "onUnconfirmed",
|
|
2342
|
+
labelKey: "onUnconfirmedLabel",
|
|
2343
|
+
hintKey: "onUnconfirmedHint",
|
|
2344
|
+
options: ["keep", "discard"],
|
|
2345
|
+
defaultValue: "keep"
|
|
2346
|
+
}
|
|
2347
|
+
]
|
|
2348
|
+
}
|
|
1836
2349
|
];
|
|
1837
|
-
|
|
1838
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", className, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M3.5 5.25 7 8.75l3.5-3.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
1839
|
-
}
|
|
2350
|
+
var FIELDS = SECTIONS.flatMap((s) => s.fields);
|
|
1840
2351
|
function SchedulerSettingsCard({ scope, t }) {
|
|
1841
|
-
const [snapshot, setSnapshot] = (0,
|
|
1842
|
-
const [pending, setPending] = (0,
|
|
1843
|
-
const [saving, setSaving] = (0,
|
|
1844
|
-
const [failed, setFailed] = (0,
|
|
1845
|
-
const [
|
|
1846
|
-
(0,
|
|
2352
|
+
const [snapshot, setSnapshot] = (0, import_react8.useState)(() => scope.getSnapshot());
|
|
2353
|
+
const [pending, setPending] = (0, import_react8.useState)({});
|
|
2354
|
+
const [saving, setSaving] = (0, import_react8.useState)(false);
|
|
2355
|
+
const [failed, setFailed] = (0, import_react8.useState)(false);
|
|
2356
|
+
const [helpOpen, setHelpOpen] = (0, import_react8.useState)({});
|
|
2357
|
+
(0, import_react8.useEffect)(() => {
|
|
1847
2358
|
const initial = scope.getSnapshot();
|
|
1848
2359
|
setSnapshot(initial);
|
|
1849
2360
|
setPending({});
|
|
1850
2361
|
if (initial.status === "ready") setFailed(false);
|
|
1851
2362
|
}, [scope]);
|
|
1852
|
-
(0,
|
|
2363
|
+
(0, import_react8.useEffect)(() => {
|
|
1853
2364
|
return scope.subscribe(() => {
|
|
1854
2365
|
const snap = scope.getSnapshot();
|
|
1855
2366
|
setSnapshot(snap);
|
|
@@ -1858,10 +2369,10 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
1858
2369
|
}
|
|
1859
2370
|
});
|
|
1860
2371
|
}, [scope]);
|
|
1861
|
-
if (snapshot.status === "unavailable") return /* @__PURE__ */ (0,
|
|
2372
|
+
if (snapshot.status === "unavailable") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, {});
|
|
1862
2373
|
if (snapshot.status !== "ready") {
|
|
1863
|
-
if (snapshot.status === "loading") return /* @__PURE__ */ (0,
|
|
1864
|
-
return /* @__PURE__ */ (0,
|
|
2374
|
+
if (snapshot.status === "loading") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-hint", children: t("loading") });
|
|
2375
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-hint", children: t("unavailable") });
|
|
1865
2376
|
}
|
|
1866
2377
|
const value = snapshot.value ?? {};
|
|
1867
2378
|
const base = snapshot.base ?? {};
|
|
@@ -1879,6 +2390,10 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
1879
2390
|
const v2 = value[field.key];
|
|
1880
2391
|
return v2 === false ? "false" : "true";
|
|
1881
2392
|
}
|
|
2393
|
+
if (field.kind === "enum") {
|
|
2394
|
+
const v2 = value[field.key];
|
|
2395
|
+
return typeof v2 === "string" ? v2 : field.defaultValue;
|
|
2396
|
+
}
|
|
1882
2397
|
const v = value[field.key];
|
|
1883
2398
|
return v ?? "";
|
|
1884
2399
|
};
|
|
@@ -1891,6 +2406,10 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
1891
2406
|
const v2 = base[field.key];
|
|
1892
2407
|
return v2 === false ? "false" : "true";
|
|
1893
2408
|
}
|
|
2409
|
+
if (field.kind === "enum") {
|
|
2410
|
+
const v2 = base[field.key];
|
|
2411
|
+
return typeof v2 === "string" ? v2 : field.defaultValue;
|
|
2412
|
+
}
|
|
1894
2413
|
const v = base[field.key];
|
|
1895
2414
|
return v ?? "";
|
|
1896
2415
|
};
|
|
@@ -1903,6 +2422,7 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
1903
2422
|
return null;
|
|
1904
2423
|
}
|
|
1905
2424
|
if (field.kind === "boolean") return null;
|
|
2425
|
+
if (field.kind === "enum") return field.options.includes(raw) ? null : t("invalidLabel");
|
|
1906
2426
|
if (raw.trim().length === 0) return null;
|
|
1907
2427
|
if (!isValidHhmm(raw)) return t("invalidTimeLabel");
|
|
1908
2428
|
return null;
|
|
@@ -1918,11 +2438,11 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
1918
2438
|
setFieldPending(field.key, baselineValue(field), true);
|
|
1919
2439
|
setFailed(false);
|
|
1920
2440
|
};
|
|
1921
|
-
const handleDiscard = (0,
|
|
2441
|
+
const handleDiscard = (0, import_react8.useCallback)(() => {
|
|
1922
2442
|
setPending({});
|
|
1923
2443
|
setFailed(false);
|
|
1924
2444
|
}, []);
|
|
1925
|
-
const handleSave = (0,
|
|
2445
|
+
const handleSave = (0, import_react8.useCallback)(async () => {
|
|
1926
2446
|
if (!dirty || saving || !writable) return;
|
|
1927
2447
|
setSaving(true);
|
|
1928
2448
|
setFailed(false);
|
|
@@ -1955,66 +2475,91 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
1955
2475
|
};
|
|
1956
2476
|
const anyInvalid = FIELDS.some((field) => invalidField(field) !== null);
|
|
1957
2477
|
const saveDisabled = !dirty || saving || !writable || anyInvalid;
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
"aria-expanded": open,
|
|
1965
|
-
"aria-label": `${t(open ? "collapse" : "expand")}: ${t("title")}`,
|
|
1966
|
-
onClick: () => setOpen(!open),
|
|
1967
|
-
children: [
|
|
1968
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-card-headText", children: [
|
|
1969
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-card-name", children: t("title") }),
|
|
1970
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-card-description", children: t("description") })
|
|
1971
|
-
] }),
|
|
1972
|
-
dirty && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-card-pending", children: t("pendingBadge") }),
|
|
1973
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronDown, { className: open ? "ss-card-chevron ss-card-chevronOpen" : "ss-card-chevron" })
|
|
1974
|
-
]
|
|
1975
|
-
}
|
|
1976
|
-
),
|
|
1977
|
-
open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-card-body", children: [
|
|
1978
|
-
!writable && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ss-card-readOnly", role: "status", children: t("readOnly") }),
|
|
1979
|
-
FIELDS.map((field) => {
|
|
2478
|
+
const confirmEnabled = (pending.confirmDelivery?.value ?? (value.confirmDelivery === false ? "false" : "true")) !== "false";
|
|
2479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-settings-form", children: [
|
|
2480
|
+
!writable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-hint", role: "status", children: t("readOnly") }),
|
|
2481
|
+
SECTIONS.map((section) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "ss-section", "aria-labelledby": `ss-section-${section.id}`, children: [
|
|
2482
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "ss-section-heading", id: `ss-section-${section.id}`, children: t(section.titleKey) }),
|
|
2483
|
+
/* @__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) => {
|
|
1980
2484
|
const invalid = invalidField(field);
|
|
1981
2485
|
const isTime = field.kind === "time";
|
|
1982
2486
|
const labelKey = field.labelKey;
|
|
1983
2487
|
const hintKey = field.hintKey;
|
|
1984
2488
|
const userHas = isUserOverridden(field.key);
|
|
1985
2489
|
const fieldId = `ss-${field.key}`;
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2490
|
+
const helpHint = hintKey !== void 0 && t(hintKey).length > 0;
|
|
2491
|
+
const badges = userHas ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-card-badges", children: [
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-card-badge", children: t("overriddenLabel") }),
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2494
|
+
"button",
|
|
2495
|
+
{
|
|
2496
|
+
type: "button",
|
|
2497
|
+
className: "ss-card-reset",
|
|
2498
|
+
disabled: !writable || saving,
|
|
2499
|
+
onClick: () => handleResetField(field),
|
|
2500
|
+
children: t("resetLabel")
|
|
2501
|
+
}
|
|
2502
|
+
)
|
|
2503
|
+
] }) : null;
|
|
2504
|
+
if (field.kind === "boolean") {
|
|
2505
|
+
const checked = displayValue(field) !== "false";
|
|
2506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-field ss-fieldToggle", children: [
|
|
2507
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-toggle-row", children: [
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-label-group", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: fieldId, className: "ss-toggle-label", children: t(labelKey) }) }),
|
|
2509
|
+
badges,
|
|
2510
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1992
2511
|
"button",
|
|
1993
2512
|
{
|
|
1994
2513
|
type: "button",
|
|
1995
|
-
|
|
2514
|
+
id: fieldId,
|
|
2515
|
+
role: "switch",
|
|
2516
|
+
"aria-checked": checked,
|
|
2517
|
+
"aria-label": t(labelKey),
|
|
2518
|
+
className: "ss-toggle-switch",
|
|
1996
2519
|
disabled: !writable || saving,
|
|
1997
|
-
onClick: () =>
|
|
1998
|
-
children: t("resetLabel")
|
|
2520
|
+
onClick: () => handleFieldEdit(field, checked ? "false" : "true")
|
|
1999
2521
|
}
|
|
2000
2522
|
)
|
|
2001
|
-
] })
|
|
2523
|
+
] }),
|
|
2524
|
+
helpHint ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-toggle-description", children: t(hintKey) }) : null,
|
|
2525
|
+
invalid !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-card-invalid", role: "status", children: invalid }) : null
|
|
2526
|
+
] }, field.key);
|
|
2527
|
+
}
|
|
2528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-field", children: [
|
|
2529
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-card-fieldHead", children: [
|
|
2530
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-label-group", children: [
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: fieldId, className: "ss-card-label", children: t(labelKey) }),
|
|
2532
|
+
helpHint && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2533
|
+
"button",
|
|
2534
|
+
{
|
|
2535
|
+
type: "button",
|
|
2536
|
+
className: helpOpen[field.key] === true ? "ss-help-btn ss-help-btnOpen" : "ss-help-btn",
|
|
2537
|
+
"aria-label": `${t(labelKey)}\u8BF4\u660E`,
|
|
2538
|
+
"aria-expanded": helpOpen[field.key] === true,
|
|
2539
|
+
"aria-controls": `ss-help-${field.key}`,
|
|
2540
|
+
onClick: () => setHelpOpen((prev) => ({ ...prev, [field.key]: !prev[field.key] })),
|
|
2541
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "12", height: "12", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", strokeWidth: "1", children: [
|
|
2542
|
+
/* @__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" }),
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M7.6127 3.18921V4.55986H6.38735V3.18921H7.6127Z", fill: "currentColor" }),
|
|
2544
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M7.6127 5.68921V10.8109H6.38735V5.68921H7.6127Z", fill: "currentColor" })
|
|
2545
|
+
] })
|
|
2546
|
+
}
|
|
2547
|
+
)
|
|
2548
|
+
] }),
|
|
2549
|
+
badges
|
|
2002
2550
|
] }),
|
|
2003
|
-
field.kind === "
|
|
2551
|
+
field.kind === "enum" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2004
2552
|
"select",
|
|
2005
2553
|
{
|
|
2006
2554
|
id: fieldId,
|
|
2007
2555
|
className: invalid !== null ? "ss-input ss-inputInvalid" : "ss-input",
|
|
2008
2556
|
"aria-invalid": invalid !== null || void 0,
|
|
2009
|
-
value: displayValue(field)
|
|
2557
|
+
value: displayValue(field),
|
|
2010
2558
|
disabled: !writable || saving,
|
|
2011
2559
|
onChange: (event) => handleFieldEdit(field, event.target.value),
|
|
2012
|
-
children:
|
|
2013
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "true", children: t("optionOn") }),
|
|
2014
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "false", children: t("optionOff") })
|
|
2015
|
-
]
|
|
2560
|
+
children: field.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: option, children: t(`${field.key}Option_${option}`) }, option))
|
|
2016
2561
|
}
|
|
2017
|
-
) : /* @__PURE__ */ (0,
|
|
2562
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2018
2563
|
"input",
|
|
2019
2564
|
{
|
|
2020
2565
|
id: fieldId,
|
|
@@ -2028,33 +2573,36 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
2028
2573
|
onChange: (event) => handleFieldEdit(field, event.target.value)
|
|
2029
2574
|
}
|
|
2030
2575
|
),
|
|
2031
|
-
invalid !== null
|
|
2576
|
+
invalid !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-card-invalid", role: "status", children: invalid }) : null,
|
|
2577
|
+
helpOpen[field.key] === true && hintKey !== void 0 && t(hintKey).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { id: `ss-help-${field.key}`, role: "region", "aria-label": `${t(labelKey)}\u8BF4\u660E`, className: "ss-help", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: t(hintKey) }) }) : null,
|
|
2578
|
+
field.key === "steerDelivery" && displayValue(field) === "steer" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-card-risk", role: "status", children: confirmEnabled ? t("steerRiskGuarded") : t("steerRiskUnguarded") }) : null,
|
|
2579
|
+
field.key === "onUnconfirmed" && displayValue(field) === "discard" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-card-risk", role: "status", children: t("discardRisk") }) : null
|
|
2032
2580
|
] }, field.key);
|
|
2033
|
-
})
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
] })
|
|
2581
|
+
}) })
|
|
2582
|
+
] }, section.id)),
|
|
2583
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-card-footer", children: [
|
|
2584
|
+
failed && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ss-card-failed", role: "status", children: t("saveFailed") }),
|
|
2585
|
+
dirty && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2586
|
+
"button",
|
|
2587
|
+
{
|
|
2588
|
+
type: "button",
|
|
2589
|
+
className: "ss-card-discard",
|
|
2590
|
+
disabled: saving,
|
|
2591
|
+
onClick: handleDiscard,
|
|
2592
|
+
children: t("discard")
|
|
2593
|
+
}
|
|
2594
|
+
),
|
|
2595
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2596
|
+
"button",
|
|
2597
|
+
{
|
|
2598
|
+
type: "button",
|
|
2599
|
+
className: "ss-card-save",
|
|
2600
|
+
disabled: saveDisabled,
|
|
2601
|
+
onClick: () => void handleSave(),
|
|
2602
|
+
children: saving ? t("saving") : t("save")
|
|
2603
|
+
}
|
|
2604
|
+
)
|
|
2605
|
+
] })
|
|
2058
2606
|
] });
|
|
2059
2607
|
}
|
|
2060
2608
|
|
|
@@ -2062,16 +2610,57 @@ function SchedulerSettingsCard({ scope, t }) {
|
|
|
2062
2610
|
var PRESENCE_URGENT_WINDOW_MS = 5 * 6e4;
|
|
2063
2611
|
function summarizeSchedules(schedules) {
|
|
2064
2612
|
let nextAt;
|
|
2613
|
+
let progressFrom;
|
|
2065
2614
|
let count = 0;
|
|
2615
|
+
let pausedCount = 0;
|
|
2066
2616
|
for (const item of schedules ?? []) {
|
|
2067
2617
|
const raw = item.scheduled_at ?? item.scheduledAt;
|
|
2068
2618
|
if (raw === void 0) continue;
|
|
2069
2619
|
const epoch = Date.parse(raw);
|
|
2070
2620
|
if (!Number.isFinite(epoch)) continue;
|
|
2071
2621
|
count += 1;
|
|
2072
|
-
if (
|
|
2622
|
+
if (item.status === "paused") {
|
|
2623
|
+
pausedCount += 1;
|
|
2624
|
+
continue;
|
|
2625
|
+
}
|
|
2626
|
+
if (nextAt === void 0 || epoch < nextAt) {
|
|
2627
|
+
nextAt = epoch;
|
|
2628
|
+
progressFrom = windowStartOf(item, epoch);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
if (count === 0) return { count: 0, pausedCount: 0 };
|
|
2632
|
+
return nextAt === void 0 ? { count, pausedCount } : { count, nextAt, pausedCount, ...progressFrom !== void 0 ? { progressFrom } : {} };
|
|
2633
|
+
}
|
|
2634
|
+
function windowStartOf(item, scheduledEpoch) {
|
|
2635
|
+
const explicit = toFiniteSeconds(item.window_seconds ?? item.windowSeconds);
|
|
2636
|
+
if (explicit !== void 0) return scheduledEpoch - explicit * 1e3;
|
|
2637
|
+
const afterSec = toFiniteSeconds(item.after_seconds ?? item.afterSeconds);
|
|
2638
|
+
const everySec = toFiniteSeconds(item.every_seconds ?? item.everySeconds);
|
|
2639
|
+
const span = item.kind === "every" ? everySec : afterSec ?? everySec;
|
|
2640
|
+
if (span !== void 0) return scheduledEpoch - span * 1e3;
|
|
2641
|
+
const created = toEpochMs(item.created_at) ?? toEpochMs(item.createdAt);
|
|
2642
|
+
if (created !== null) return created;
|
|
2643
|
+
return void 0;
|
|
2644
|
+
}
|
|
2645
|
+
function toFiniteSeconds(value) {
|
|
2646
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2647
|
+
}
|
|
2648
|
+
function toEpochMs(value) {
|
|
2649
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
2650
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2651
|
+
const parsed = Date.parse(value);
|
|
2652
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
2073
2653
|
}
|
|
2074
|
-
return
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
function progressOf(entry, now) {
|
|
2657
|
+
const { nextAt, progressFrom } = entry;
|
|
2658
|
+
if (nextAt === void 0 || progressFrom === void 0) return void 0;
|
|
2659
|
+
const span = nextAt - progressFrom;
|
|
2660
|
+
if (!(span > 0)) return void 0;
|
|
2661
|
+
const ratio = (now - progressFrom) / span;
|
|
2662
|
+
if (!Number.isFinite(ratio)) return void 0;
|
|
2663
|
+
return ratio <= 0 ? 0 : ratio >= 1 ? 1 : ratio;
|
|
2075
2664
|
}
|
|
2076
2665
|
function badgeStateFor(nextAt, now) {
|
|
2077
2666
|
if (nextAt === void 0) return "scheduled";
|
|
@@ -2080,6 +2669,24 @@ function badgeStateFor(nextAt, now) {
|
|
|
2080
2669
|
if (diff <= PRESENCE_URGENT_WINDOW_MS) return "urgent";
|
|
2081
2670
|
return "scheduled";
|
|
2082
2671
|
}
|
|
2672
|
+
function hoverScheduleStatus(entry, now, labels, strings, formatHhmm3, formatDate2) {
|
|
2673
|
+
const tone = badgeStateFor(entry.nextAt, now);
|
|
2674
|
+
if (entry.nextAt === void 0 && entry.pausedCount > 0) {
|
|
2675
|
+
const paused = strings.hoverSchedulePaused ?? strings.hoverScheduleOverdue;
|
|
2676
|
+
return { label: formatTemplate(paused, { n: entry.count }), tone };
|
|
2677
|
+
}
|
|
2678
|
+
if (tone === "overdue" || entry.nextAt === void 0) {
|
|
2679
|
+
return { label: formatTemplate(strings.hoverScheduleOverdue, { n: entry.count }), tone };
|
|
2680
|
+
}
|
|
2681
|
+
const time = relativeFireLabel(entry.nextAt, now, labels, formatHhmm3, formatDate2);
|
|
2682
|
+
return { label: formatTemplate(strings.hoverScheduleStatus, { n: entry.count, time }), tone };
|
|
2683
|
+
}
|
|
2684
|
+
function formatTemplate(template, params) {
|
|
2685
|
+
return template.replace(
|
|
2686
|
+
/\{(\w+)\}/g,
|
|
2687
|
+
(match, key) => key in params ? String(params[key]) : match
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2083
2690
|
function startOfDay(epoch) {
|
|
2084
2691
|
const d = new Date(epoch);
|
|
2085
2692
|
d.setHours(0, 0, 0, 0);
|
|
@@ -2099,17 +2706,26 @@ function relativeFireLabel(nextAt, now, labels, formatHhmm3, formatDate2) {
|
|
|
2099
2706
|
|
|
2100
2707
|
// src/client/session-presence.ts
|
|
2101
2708
|
var BADGE_CLASS = "ss-presence";
|
|
2709
|
+
var HOVER_ROW_CLASS = "ss-hover-status";
|
|
2102
2710
|
var FIBER_MAX_DEPTH = 8;
|
|
2103
2711
|
var SCAN_DEBOUNCE_MS = 60;
|
|
2104
2712
|
var STATE_TICK_MS = 3e4;
|
|
2105
|
-
var
|
|
2106
|
-
var
|
|
2713
|
+
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>';
|
|
2714
|
+
var CLOCK_FACE_R = 9.5;
|
|
2715
|
+
var CLOCK_FACE_CIRCUMFERENCE = 2 * Math.PI * CLOCK_FACE_R;
|
|
2716
|
+
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>`;
|
|
2107
2717
|
var hhmmFormatter = new Intl.DateTimeFormat(void 0, { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
2108
2718
|
var dateFormatter = new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric" });
|
|
2109
2719
|
var formatHhmm2 = (epoch) => hhmmFormatter.format(epoch);
|
|
2110
2720
|
var formatDate = (epoch) => dateFormatter.format(epoch);
|
|
2111
2721
|
function sessionIdOfRow(row) {
|
|
2112
|
-
|
|
2722
|
+
return sessionIdOfNode(row);
|
|
2723
|
+
}
|
|
2724
|
+
function sessionIdOfHoverCard(card) {
|
|
2725
|
+
return sessionIdOfNode(card);
|
|
2726
|
+
}
|
|
2727
|
+
function sessionIdOfNode(element) {
|
|
2728
|
+
const host = element;
|
|
2113
2729
|
const fiberKey = Object.keys(host).find((key) => key.startsWith("__reactFiber$"));
|
|
2114
2730
|
if (fiberKey === void 0) return void 0;
|
|
2115
2731
|
let fiber = host[fiberKey];
|
|
@@ -2120,14 +2736,9 @@ function sessionIdOfRow(row) {
|
|
|
2120
2736
|
}
|
|
2121
2737
|
return void 0;
|
|
2122
2738
|
}
|
|
2123
|
-
function
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
const schedules = summary.projectionValues?.["userSchedules"]?.schedules;
|
|
2127
|
-
const entry = summarizeSchedules(schedules);
|
|
2128
|
-
if (entry.count > 0) index.set(sessionId, entry);
|
|
2129
|
-
}
|
|
2130
|
-
return index;
|
|
2739
|
+
function currentIndex(summary) {
|
|
2740
|
+
summary.reload();
|
|
2741
|
+
return summary.getSnapshot();
|
|
2131
2742
|
}
|
|
2132
2743
|
function findBadge(row) {
|
|
2133
2744
|
for (const child of Array.from(row.children)) {
|
|
@@ -2148,23 +2759,28 @@ function labelsFor(dict) {
|
|
|
2148
2759
|
};
|
|
2149
2760
|
}
|
|
2150
2761
|
function mountSessionPresence(options) {
|
|
2151
|
-
const {
|
|
2762
|
+
const { summary, locale, getClockIconStyle } = options;
|
|
2152
2763
|
if (typeof document === "undefined" || typeof MutationObserver === "undefined") {
|
|
2153
2764
|
return () => void 0;
|
|
2154
2765
|
}
|
|
2766
|
+
const getClockSvg = () => {
|
|
2767
|
+
const style = getClockIconStyle?.() ?? "auto";
|
|
2768
|
+
return style === "classic" ? CLOCK_SVG_CLASSIC : CLOCK_SVG_MODERN;
|
|
2769
|
+
};
|
|
2155
2770
|
let index = /* @__PURE__ */ new Map();
|
|
2156
2771
|
let scanTimer;
|
|
2157
2772
|
let disposed = false;
|
|
2158
2773
|
const dictForNow = () => dictFor(locale?.getSnapshot().active);
|
|
2159
2774
|
const scan = () => {
|
|
2160
2775
|
if (disposed) return;
|
|
2161
|
-
index =
|
|
2776
|
+
index = currentIndex(summary);
|
|
2162
2777
|
const dict = dictForNow();
|
|
2163
2778
|
const now = Date.now();
|
|
2164
2779
|
for (const row of Array.from(document.querySelectorAll('[role="treeitem"][draggable="true"]'))) {
|
|
2165
2780
|
if (row.hasAttribute("aria-expanded")) continue;
|
|
2166
2781
|
applyBadge(row, now, dict);
|
|
2167
2782
|
}
|
|
2783
|
+
applyHoverCard(now, dict);
|
|
2168
2784
|
};
|
|
2169
2785
|
const scheduleScan = () => {
|
|
2170
2786
|
if (disposed || scanTimer !== void 0) return;
|
|
@@ -2184,12 +2800,15 @@ function mountSessionPresence(options) {
|
|
|
2184
2800
|
}
|
|
2185
2801
|
const state = badgeStateFor(entry.nextAt, now);
|
|
2186
2802
|
const time = entry.nextAt === void 0 ? "" : relativeFireLabel(entry.nextAt, now, labelsFor(dict), formatHhmm2, formatDate);
|
|
2187
|
-
const
|
|
2188
|
-
const
|
|
2803
|
+
const allPaused = entry.nextAt === void 0 && entry.pausedCount > 0;
|
|
2804
|
+
const aria = allPaused ? format(dict.presencePausedAria, { n: entry.count }) : state === "overdue" ? format(dict.presenceOverdueAria, { n: entry.count }) : format(dict.presenceBadgeAria, { n: entry.count, time });
|
|
2805
|
+
const tooltip = allPaused ? format(dict.presencePausedTooltip, { n: entry.count }) : state === "overdue" ? format(dict.presenceOverdueTooltip, { n: entry.count }) : format(dict.presenceTooltip, { n: entry.count, time });
|
|
2806
|
+
const progress = progressOf(entry, now);
|
|
2189
2807
|
if (existing !== null) {
|
|
2190
2808
|
if (existing.dataset.state !== state) existing.dataset.state = state;
|
|
2191
2809
|
if (existing.getAttribute("aria-label") !== aria) existing.setAttribute("aria-label", aria);
|
|
2192
2810
|
if (existing.getAttribute("title") !== tooltip) existing.setAttribute("title", tooltip);
|
|
2811
|
+
applyProgress(existing, progress);
|
|
2193
2812
|
return;
|
|
2194
2813
|
}
|
|
2195
2814
|
const slot = row.firstElementChild;
|
|
@@ -2201,66 +2820,293 @@ function mountSessionPresence(options) {
|
|
|
2201
2820
|
badge.setAttribute("role", "img");
|
|
2202
2821
|
badge.setAttribute("aria-label", aria);
|
|
2203
2822
|
badge.setAttribute("title", tooltip);
|
|
2204
|
-
badge.innerHTML =
|
|
2823
|
+
badge.innerHTML = getClockSvg();
|
|
2824
|
+
applyProgress(badge, progress);
|
|
2205
2825
|
if (beside) slot.after(badge);
|
|
2206
2826
|
else slot.append(badge);
|
|
2207
2827
|
}
|
|
2208
|
-
|
|
2828
|
+
function applyProgress(badge, ratio) {
|
|
2829
|
+
const arc = badge.querySelector(".ss-clock-prog");
|
|
2830
|
+
if (arc === null) return;
|
|
2831
|
+
if (ratio === void 0) {
|
|
2832
|
+
arc.style.display = "none";
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
if (arc.style.display === "none") arc.style.display = "";
|
|
2836
|
+
arc.style.strokeDashoffset = String(CLOCK_FACE_CIRCUMFERENCE * (1 - ratio));
|
|
2837
|
+
}
|
|
2838
|
+
function findHoverCard() {
|
|
2839
|
+
for (const card of Array.from(document.body.children)) {
|
|
2840
|
+
if (!(card instanceof HTMLElement)) continue;
|
|
2841
|
+
const statusHost = card.firstElementChild;
|
|
2842
|
+
if (!(statusHost instanceof HTMLElement)) continue;
|
|
2843
|
+
const hasStatusRow = Array.from(statusHost.children).some((child) => {
|
|
2844
|
+
const dot = child.firstElementChild;
|
|
2845
|
+
return child.children.length >= 2 && dot !== null && dot.tagName === "SPAN" && dot.getAttribute("aria-hidden") === "true";
|
|
2846
|
+
});
|
|
2847
|
+
if (!hasStatusRow) continue;
|
|
2848
|
+
const sessionId = sessionIdOfHoverCard(card);
|
|
2849
|
+
if (sessionId === void 0) continue;
|
|
2850
|
+
return { card, statusHost, sessionId };
|
|
2851
|
+
}
|
|
2852
|
+
return null;
|
|
2853
|
+
}
|
|
2854
|
+
function applyHoverCard(now, dict) {
|
|
2855
|
+
const found = findHoverCard();
|
|
2856
|
+
if (found === null) return;
|
|
2857
|
+
const { statusHost, sessionId } = found;
|
|
2858
|
+
const existing = statusHost.querySelector(`:scope > .${HOVER_ROW_CLASS}`);
|
|
2859
|
+
const entry = index.get(sessionId);
|
|
2860
|
+
if (entry === void 0) {
|
|
2861
|
+
existing?.remove();
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
const { label } = hoverScheduleStatus(entry, now, labelsFor(dict), dict, formatHhmm2, formatDate);
|
|
2865
|
+
const state = badgeStateFor(entry.nextAt, now);
|
|
2866
|
+
if (existing !== null) {
|
|
2867
|
+
const text2 = existing.lastElementChild;
|
|
2868
|
+
if (text2 !== null && text2.textContent !== label) text2.textContent = label;
|
|
2869
|
+
if (existing.dataset.state !== state) existing.dataset.state = state;
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
const row = document.createElement("div");
|
|
2873
|
+
row.className = HOVER_ROW_CLASS;
|
|
2874
|
+
row.dataset.state = state;
|
|
2875
|
+
const dot = document.createElement("span");
|
|
2876
|
+
dot.setAttribute("aria-hidden", "true");
|
|
2877
|
+
dot.className = "ss-hover-status-dot";
|
|
2878
|
+
const text = document.createElement("span");
|
|
2879
|
+
text.textContent = label;
|
|
2880
|
+
row.append(dot, text);
|
|
2881
|
+
statusHost.append(row);
|
|
2882
|
+
}
|
|
2883
|
+
const unsubscribeSummary = summary.subscribe(scheduleScan);
|
|
2209
2884
|
const unsubscribeLocale = locale?.subscribe(scheduleScan);
|
|
2210
2885
|
const observer = new MutationObserver(scheduleScan);
|
|
2211
2886
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
2212
|
-
const refreshNow = () => {
|
|
2213
|
-
if (disposed) return;
|
|
2214
|
-
try {
|
|
2215
|
-
void sessions.refresh().catch(() => void 0);
|
|
2216
|
-
} catch {
|
|
2217
|
-
}
|
|
2218
|
-
};
|
|
2219
|
-
const onVisible = () => {
|
|
2220
|
-
if (document.visibilityState === "visible") {
|
|
2221
|
-
refreshNow();
|
|
2222
|
-
scheduleScan();
|
|
2223
|
-
}
|
|
2224
|
-
};
|
|
2225
|
-
document.addEventListener("visibilitychange", onVisible);
|
|
2226
|
-
window.addEventListener("online", onVisible);
|
|
2227
2887
|
const stateTick = setInterval(scheduleScan, STATE_TICK_MS);
|
|
2228
|
-
const refreshTick = setInterval(() => {
|
|
2229
|
-
if (document.visibilityState === "visible") refreshNow();
|
|
2230
|
-
}, REFRESH_INTERVAL_MS);
|
|
2231
|
-
refreshNow();
|
|
2232
2888
|
scheduleScan();
|
|
2233
2889
|
return () => {
|
|
2234
2890
|
disposed = true;
|
|
2235
2891
|
if (scanTimer !== void 0) clearTimeout(scanTimer);
|
|
2236
2892
|
clearInterval(stateTick);
|
|
2237
|
-
clearInterval(refreshTick);
|
|
2238
2893
|
observer.disconnect();
|
|
2239
|
-
|
|
2894
|
+
unsubscribeSummary();
|
|
2240
2895
|
unsubscribeLocale?.();
|
|
2241
|
-
document.removeEventListener("visibilitychange", onVisible);
|
|
2242
|
-
window.removeEventListener("online", onVisible);
|
|
2243
2896
|
for (const badge of Array.from(document.querySelectorAll(`.${BADGE_CLASS}`))) badge.remove();
|
|
2897
|
+
for (const row of Array.from(document.querySelectorAll(`.${HOVER_ROW_CLASS}`))) row.remove();
|
|
2244
2898
|
};
|
|
2245
2899
|
}
|
|
2246
2900
|
|
|
2901
|
+
// src/schedule-summary.ts
|
|
2902
|
+
var SCHEDULE_PROJECTION_KEY = "userSchedules";
|
|
2903
|
+
function schedulesOfProjection(projectionValues) {
|
|
2904
|
+
const value = projectionValues?.[SCHEDULE_PROJECTION_KEY];
|
|
2905
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2906
|
+
const schedules = value.schedules;
|
|
2907
|
+
return Array.isArray(schedules) ? schedules : void 0;
|
|
2908
|
+
}
|
|
2909
|
+
function buildScheduleSummary(source, now) {
|
|
2910
|
+
const summary = /* @__PURE__ */ new Map();
|
|
2911
|
+
for (const [sessionId, row] of Object.entries(source.byId)) {
|
|
2912
|
+
if (row === void 0) continue;
|
|
2913
|
+
const entry = summarizeSchedules(schedulesOfProjection(row.projectionValues));
|
|
2914
|
+
if (entry.count === 0) continue;
|
|
2915
|
+
summary.set(sessionId, { ...entry, state: badgeStateFor(entry.nextAt, now) });
|
|
2916
|
+
}
|
|
2917
|
+
return summary;
|
|
2918
|
+
}
|
|
2919
|
+
function sameScheduleSummary(a, b) {
|
|
2920
|
+
if (a === b) return true;
|
|
2921
|
+
if (a.size !== b.size) return false;
|
|
2922
|
+
for (const [sessionId, left] of a) {
|
|
2923
|
+
const right = b.get(sessionId);
|
|
2924
|
+
if (right === void 0) return false;
|
|
2925
|
+
if (left.count !== right.count || left.pausedCount !== right.pausedCount || left.nextAt !== right.nextAt || left.progressFrom !== right.progressFrom || left.state !== right.state) {
|
|
2926
|
+
return false;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
return true;
|
|
2930
|
+
}
|
|
2931
|
+
var STATE_RANK = { scheduled: 0, urgent: 1, overdue: 2 };
|
|
2932
|
+
function summarizeScheduleGroup(summary, sessionIds) {
|
|
2933
|
+
let sessions = 0;
|
|
2934
|
+
let count = 0;
|
|
2935
|
+
let pausedCount = 0;
|
|
2936
|
+
let nextAt;
|
|
2937
|
+
let state;
|
|
2938
|
+
for (const sessionId of sessionIds) {
|
|
2939
|
+
const entry = summary.get(sessionId);
|
|
2940
|
+
if (entry === void 0) continue;
|
|
2941
|
+
sessions += 1;
|
|
2942
|
+
count += entry.count;
|
|
2943
|
+
pausedCount += entry.pausedCount;
|
|
2944
|
+
if (entry.nextAt !== void 0 && (nextAt === void 0 || entry.nextAt < nextAt)) nextAt = entry.nextAt;
|
|
2945
|
+
if (state === void 0 || STATE_RANK[entry.state] > STATE_RANK[state]) state = entry.state;
|
|
2946
|
+
}
|
|
2947
|
+
return nextAt === void 0 ? { sessions, count, pausedCount, state } : { sessions, count, pausedCount, nextAt, state };
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
// src/schedule-summary-store.ts
|
|
2951
|
+
var SUMMARY_STATE_TICK_MS = 3e4;
|
|
2952
|
+
var SUMMARY_REFRESH_INTERVAL_MS = 6e4;
|
|
2953
|
+
function documentOf() {
|
|
2954
|
+
const doc = globalThis.document;
|
|
2955
|
+
return typeof doc === "object" && doc !== null ? doc : void 0;
|
|
2956
|
+
}
|
|
2957
|
+
function createScheduleSummaryStore(sessions, options = {}) {
|
|
2958
|
+
const now = options.now ?? Date.now;
|
|
2959
|
+
const stateTickMs = options.stateTickMs ?? SUMMARY_STATE_TICK_MS;
|
|
2960
|
+
const refreshIntervalMs = options.refreshIntervalMs ?? SUMMARY_REFRESH_INTERVAL_MS;
|
|
2961
|
+
const autoRefresh = options.autoRefresh ?? true;
|
|
2962
|
+
const canRefresh = options.canRefresh ?? (() => {
|
|
2963
|
+
const doc2 = documentOf();
|
|
2964
|
+
return doc2?.visibilityState === void 0 || doc2.visibilityState === "visible";
|
|
2965
|
+
});
|
|
2966
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2967
|
+
let snapshot = /* @__PURE__ */ new Map();
|
|
2968
|
+
let disposed = false;
|
|
2969
|
+
const notify = () => {
|
|
2970
|
+
for (const listener of [...listeners]) {
|
|
2971
|
+
try {
|
|
2972
|
+
listener();
|
|
2973
|
+
} catch {
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
const reload = () => {
|
|
2978
|
+
if (disposed) return;
|
|
2979
|
+
const next = buildScheduleSummary(sessions.list.getSnapshot(), now());
|
|
2980
|
+
if (sameScheduleSummary(snapshot, next)) return;
|
|
2981
|
+
snapshot = next;
|
|
2982
|
+
notify();
|
|
2983
|
+
};
|
|
2984
|
+
const unsubscribeList = sessions.list.subscribe(reload);
|
|
2985
|
+
const stateTick = setInterval(reload, stateTickMs);
|
|
2986
|
+
let refreshTick;
|
|
2987
|
+
let onVisible;
|
|
2988
|
+
const doc = documentOf();
|
|
2989
|
+
const target = globalThis;
|
|
2990
|
+
const refreshNow = () => {
|
|
2991
|
+
if (disposed || !canRefresh()) return;
|
|
2992
|
+
try {
|
|
2993
|
+
void sessions.refresh().catch(() => void 0);
|
|
2994
|
+
} catch {
|
|
2995
|
+
}
|
|
2996
|
+
};
|
|
2997
|
+
if (autoRefresh) {
|
|
2998
|
+
onVisible = () => {
|
|
2999
|
+
if (!disposed && canRefresh()) {
|
|
3000
|
+
refreshNow();
|
|
3001
|
+
reload();
|
|
3002
|
+
}
|
|
3003
|
+
};
|
|
3004
|
+
refreshTick = setInterval(() => {
|
|
3005
|
+
if (canRefresh()) refreshNow();
|
|
3006
|
+
}, refreshIntervalMs);
|
|
3007
|
+
doc?.addEventListener?.("visibilitychange", onVisible);
|
|
3008
|
+
target.addEventListener?.("online", onVisible);
|
|
3009
|
+
refreshNow();
|
|
3010
|
+
}
|
|
3011
|
+
reload();
|
|
3012
|
+
const dispose = () => {
|
|
3013
|
+
if (disposed) return;
|
|
3014
|
+
disposed = true;
|
|
3015
|
+
clearInterval(stateTick);
|
|
3016
|
+
if (refreshTick !== void 0) clearInterval(refreshTick);
|
|
3017
|
+
if (onVisible !== void 0) {
|
|
3018
|
+
doc?.removeEventListener?.("visibilitychange", onVisible);
|
|
3019
|
+
target.removeEventListener?.("online", onVisible);
|
|
3020
|
+
}
|
|
3021
|
+
unsubscribeList();
|
|
3022
|
+
listeners.clear();
|
|
3023
|
+
};
|
|
3024
|
+
return {
|
|
3025
|
+
getSnapshot: () => snapshot,
|
|
3026
|
+
subscribe: (listener) => {
|
|
3027
|
+
listeners.add(listener);
|
|
3028
|
+
return () => {
|
|
3029
|
+
listeners.delete(listener);
|
|
3030
|
+
};
|
|
3031
|
+
},
|
|
3032
|
+
reload,
|
|
3033
|
+
dispose
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
var activeStore;
|
|
3037
|
+
var registryListeners = /* @__PURE__ */ new Set();
|
|
3038
|
+
function setActiveScheduleSummaryStore(store) {
|
|
3039
|
+
if (activeStore === store) return;
|
|
3040
|
+
activeStore = store;
|
|
3041
|
+
for (const listener of [...registryListeners]) {
|
|
3042
|
+
try {
|
|
3043
|
+
listener();
|
|
3044
|
+
} catch {
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
function getScheduleSummaryStore() {
|
|
3049
|
+
return activeStore;
|
|
3050
|
+
}
|
|
3051
|
+
function subscribeScheduleSummaryStore(listener) {
|
|
3052
|
+
registryListeners.add(listener);
|
|
3053
|
+
return () => {
|
|
3054
|
+
registryListeners.delete(listener);
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
// src/client/useScheduleSummary.ts
|
|
3059
|
+
var import_react9 = require("react");
|
|
3060
|
+
var EMPTY_SUMMARY = /* @__PURE__ */ new Map();
|
|
3061
|
+
var noopSubscribe2 = () => () => void 0;
|
|
3062
|
+
var emptySnapshot = () => EMPTY_SUMMARY;
|
|
3063
|
+
var missingSnapshot = () => void 0;
|
|
3064
|
+
function useScheduleSummary(store) {
|
|
3065
|
+
const registered = (0, import_react9.useSyncExternalStore)(subscribeScheduleSummaryStore, getScheduleSummaryStore, missingSnapshot);
|
|
3066
|
+
const target = store ?? registered;
|
|
3067
|
+
const subscribe = (0, import_react9.useMemo)(
|
|
3068
|
+
() => target === void 0 ? noopSubscribe2 : (listener) => target.subscribe(listener),
|
|
3069
|
+
[target]
|
|
3070
|
+
);
|
|
3071
|
+
const getSnapshot = (0, import_react9.useMemo)(
|
|
3072
|
+
() => target === void 0 ? emptySnapshot : () => target.getSnapshot(),
|
|
3073
|
+
[target]
|
|
3074
|
+
);
|
|
3075
|
+
return (0, import_react9.useSyncExternalStore)(subscribe, getSnapshot, emptySnapshot);
|
|
3076
|
+
}
|
|
3077
|
+
|
|
2247
3078
|
// src/client/index.tsx
|
|
2248
3079
|
var INPUT_RIGHT_SLOT = "conversation.input.right";
|
|
2249
|
-
var inject = ["slots", "sessions", "conversation", "
|
|
3080
|
+
var inject = ["slots", "sessions", "conversation", "locale"];
|
|
2250
3081
|
var NS = "settings.plugins.later";
|
|
2251
3082
|
var SETTINGS_NS = "dsh-later";
|
|
3083
|
+
var ENTRY_ID = "later";
|
|
2252
3084
|
function apply(ctx) {
|
|
2253
3085
|
const slots = ctx.get("slots");
|
|
2254
3086
|
const sessions = ctx.get("sessions");
|
|
2255
|
-
const settingsScope = ctx.get("settingsScope");
|
|
2256
3087
|
const locale = ctx.get("locale");
|
|
2257
3088
|
if (slots === void 0 || sessions === void 0) return;
|
|
2258
3089
|
ctx.effect(() => injectStyles(), "dsh-later: styles");
|
|
2259
|
-
|
|
3090
|
+
let detectedVersion = "classic";
|
|
3091
|
+
let getClockIconStyle = () => "auto";
|
|
3092
|
+
const summary = createScheduleSummaryStore(sessions);
|
|
3093
|
+
setActiveScheduleSummaryStore(summary);
|
|
3094
|
+
ctx.effect(
|
|
3095
|
+
() => () => {
|
|
3096
|
+
if (getScheduleSummaryStore() === summary) setActiveScheduleSummaryStore(void 0);
|
|
3097
|
+
summary.dispose();
|
|
3098
|
+
},
|
|
3099
|
+
"dsh-later: schedule summary"
|
|
3100
|
+
);
|
|
2260
3101
|
ctx.effect(
|
|
2261
3102
|
() => mountSessionPresence({
|
|
2262
|
-
|
|
2263
|
-
...locale === void 0 ? {} : { locale }
|
|
3103
|
+
summary,
|
|
3104
|
+
...locale === void 0 ? {} : { locale },
|
|
3105
|
+
getClockIconStyle: () => {
|
|
3106
|
+
const style = getClockIconStyle();
|
|
3107
|
+
if (style === "auto") return detectedVersion;
|
|
3108
|
+
return style;
|
|
3109
|
+
}
|
|
2264
3110
|
}),
|
|
2265
3111
|
"dsh-later: presence"
|
|
2266
3112
|
);
|
|
@@ -2273,10 +3119,11 @@ function apply(ctx) {
|
|
|
2273
3119
|
expand: "\u5C55\u5F00\u8BBE\u7F6E",
|
|
2274
3120
|
collapse: "\u6536\u8D77\u8BBE\u7F6E",
|
|
2275
3121
|
readOnly: "\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002",
|
|
3122
|
+
sectionDisplay: "\u663E\u793A\u8BBE\u7F6E",
|
|
3123
|
+
sectionTime: "\u65F6\u95F4\u8BBE\u7F6E",
|
|
3124
|
+
sectionDelivery: "\u6295\u9012\u8BBE\u7F6E",
|
|
2276
3125
|
showButtonLabel: "\u663E\u793A\u5B9A\u65F6\u6309\u94AE",
|
|
2277
3126
|
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",
|
|
2278
|
-
optionOn: "\u663E\u793A",
|
|
2279
|
-
optionOff: "\u9690\u85CF",
|
|
2280
3127
|
maxSchedulesLabel: "\u5355\u4F1A\u8BDD\u4EFB\u52A1\u4E0A\u9650",
|
|
2281
3128
|
maxSchedulesHint: "\u5355\u4E2A\u4F1A\u8BDD\u5185\u5141\u8BB8\u521B\u5EFA\u7684\u6700\u5927\u5B9A\u65F6\u4EFB\u52A1\u6570\u91CF\u3002",
|
|
2282
3129
|
smartWindowHint: "\u7528\u4E8E\u300C\u5DE5\u4F5C\u65F6\u95F4\u300D\u82AF\u7247\u4E0E\u667A\u80FD\u6A21\u5F0F\u81EA\u52A8\u6392\u671F\u3002",
|
|
@@ -2285,6 +3132,24 @@ function apply(ctx) {
|
|
|
2285
3132
|
lunchStartLabel: "\u5348\u4F11\u5F00\u59CB",
|
|
2286
3133
|
lunchEndLabel: "\u5348\u4F11\u7ED3\u675F",
|
|
2287
3134
|
eveningEndLabel: "\u591C\u95F4\u9759\u9ED8\u8D77\u70B9",
|
|
3135
|
+
steerDeliveryLabel: "\u63D2\u8BDD\u6295\u9012\u65B9\u5F0F",
|
|
3136
|
+
steerDeliveryHint: "\u300C\u63D2\u8BDD\u53D1\u9001\u300D\u628A\u63D0\u9192\u5185\u5BB9\u63A8\u7ED9\u4F1A\u8BDD\u65F6\u8FDB\u5165\u7684\u961F\u5217\u3002",
|
|
3137
|
+
steerDeliveryOption_followup: "\u72EC\u7ACB\u6210\u4E00\u8F6E\uFF08\u63A8\u8350\uFF09",
|
|
3138
|
+
steerDeliveryOption_steer: "\u63D2\u5165\u5F53\u524D\u56DE\u5408\uFF08\u66F4\u5FEB\uFF09",
|
|
3139
|
+
confirmDeliveryLabel: "\u6295\u9012\u786E\u8BA4",
|
|
3140
|
+
confirmDeliveryHint: "\u6295\u9012\u540E\u6838\u5BF9\u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u786E\u8BA4\u6D88\u606F\u771F\u6B63\u8FDB\u5165\u8BF7\u6C42\u3002\u5173\u95ED\u5219\u4E0D\u518D\u6838\u5BF9\uFF0C\u4E5F\u4E0D\u4FDD\u8BC1\u9001\u8FBE\u3002",
|
|
3141
|
+
onUnconfirmedLabel: "\u672A\u786E\u8BA4\u65F6",
|
|
3142
|
+
onUnconfirmedHint: "\u6295\u9012\u672A\u80FD\u786E\u8BA4\u65F6\u7684\u5904\u7406\u65B9\u5F0F\u3002",
|
|
3143
|
+
onUnconfirmedOption_keep: "\u4FDD\u7559\u6761\u76EE\u53EF\u91CD\u8BD5\uFF08\u63A8\u8350\uFF09",
|
|
3144
|
+
onUnconfirmedOption_discard: "\u4E22\u5F03",
|
|
3145
|
+
clockIconStyleLabel: "\u65F6\u949F\u56FE\u6807\u98CE\u683C",
|
|
3146
|
+
clockIconStyleHint: "\u4FA7\u680F\u4F1A\u8BDD\u884C\u5B9A\u65F6\u72B6\u6001\u56FE\u6807\u7684\u663E\u793A\u98CE\u683C\u3002",
|
|
3147
|
+
clockIconStyleOption_auto: "\u81EA\u52A8\uFF08\u63A8\u8350\uFF09",
|
|
3148
|
+
clockIconStyleOption_modern: "\u65B0\u7248\u63CF\u8FB9\u98CE\u683C",
|
|
3149
|
+
clockIconStyleOption_classic: "\u65E7\u7248\u70B9\u9635\u98CE\u683C",
|
|
3150
|
+
steerRiskGuarded: "\u26A0 \u649E\u4E0A\u56DE\u5408\u542F\u52A8\u65F6\u53EF\u80FD\u88AB\u541E\u6389\uFF1B\u5DF2\u5F00\u542F\u6295\u9012\u786E\u8BA4\uFF0C\u672A\u9001\u8FBE\u4F1A\u4FDD\u7559\u6761\u76EE\u5E76\u53EF\u91CD\u8BD5\u3002",
|
|
3151
|
+
steerRiskUnguarded: "\u26A0 \u649E\u4E0A\u56DE\u5408\u542F\u52A8\u65F6\u53EF\u80FD\u88AB\u541E\u6389\uFF0C\u4E14\u4E0D\u4F1A\u4FDD\u7559\u6761\u76EE\uFF08\u6295\u9012\u786E\u8BA4\u5DF2\u5173\u95ED\uFF09\u3002",
|
|
3152
|
+
discardRisk: "\u26A0 \u672A\u786E\u8BA4\u7684\u63D0\u9192\u4F1A\u88AB\u79FB\u51FA\u5217\u8868 \u2014\u2014 \u9759\u9ED8\u4E22\u5931\u5C06\u91CD\u65B0\u53D8\u5F97\u53EF\u80FD\u3002",
|
|
2288
3153
|
overriddenLabel: "\u5DF2\u8986\u76D6",
|
|
2289
3154
|
resetLabel: "\u91CD\u7F6E",
|
|
2290
3155
|
invalidLabel: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57\u3002",
|
|
@@ -2303,10 +3168,11 @@ function apply(ctx) {
|
|
|
2303
3168
|
expand: "Show settings",
|
|
2304
3169
|
collapse: "Hide settings",
|
|
2305
3170
|
readOnly: "Settings for this deployment are read-only.",
|
|
3171
|
+
sectionDisplay: "Display",
|
|
3172
|
+
sectionTime: "Time windows",
|
|
3173
|
+
sectionDelivery: "Delivery",
|
|
2306
3174
|
showButtonLabel: "Show schedule button",
|
|
2307
3175
|
showButtonHint: "Hide the clock button on the input bar. Existing reminders keep firing and stay visible in the dock below the composer.",
|
|
2308
|
-
optionOn: "Show",
|
|
2309
|
-
optionOff: "Hide",
|
|
2310
3176
|
maxSchedulesLabel: "Max schedules per session",
|
|
2311
3177
|
maxSchedulesHint: "Maximum number of scheduled reminders allowed per session.",
|
|
2312
3178
|
smartWindowHint: "Used by the Work-hours chip and smart auto-scheduling.",
|
|
@@ -2315,6 +3181,24 @@ function apply(ctx) {
|
|
|
2315
3181
|
lunchStartLabel: "Lunch break start",
|
|
2316
3182
|
lunchEndLabel: "Lunch break end",
|
|
2317
3183
|
eveningEndLabel: "Night quiet starts at",
|
|
3184
|
+
steerDeliveryLabel: "Interject delivery",
|
|
3185
|
+
steerDeliveryHint: 'Which queue the reminder content enters when you press "Send interjection".',
|
|
3186
|
+
steerDeliveryOption_followup: "Own turn (recommended)",
|
|
3187
|
+
steerDeliveryOption_steer: "Current turn (faster)",
|
|
3188
|
+
confirmDeliveryLabel: "Confirm delivery",
|
|
3189
|
+
confirmDeliveryHint: "After sending, check the session log to confirm the message actually entered a request. Turning this off skips the check and no longer guarantees delivery.",
|
|
3190
|
+
onUnconfirmedLabel: "When unconfirmed",
|
|
3191
|
+
onUnconfirmedHint: "What to do when delivery cannot be confirmed.",
|
|
3192
|
+
onUnconfirmedOption_keep: "Keep for retry (recommended)",
|
|
3193
|
+
onUnconfirmedOption_discard: "Discard",
|
|
3194
|
+
clockIconStyleLabel: "Clock icon style",
|
|
3195
|
+
clockIconStyleHint: "Display style of the schedule status icon in the sidebar.",
|
|
3196
|
+
clockIconStyleOption_auto: "Auto (recommended)",
|
|
3197
|
+
clockIconStyleOption_modern: "Modern stroke style",
|
|
3198
|
+
clockIconStyleOption_classic: "Classic dot-matrix style",
|
|
3199
|
+
steerRiskGuarded: "\u26A0 May be swallowed when it collides with turn start; delivery confirmation is on, so an undelivered reminder is kept and can be retried.",
|
|
3200
|
+
steerRiskUnguarded: "\u26A0 May be swallowed when it collides with turn start, and the reminder will NOT be kept (delivery confirmation is off).",
|
|
3201
|
+
discardRisk: "\u26A0 Unconfirmed reminders are removed from the list \u2014 silent loss becomes possible again.",
|
|
2318
3202
|
overriddenLabel: "Overridden",
|
|
2319
3203
|
resetLabel: "Reset",
|
|
2320
3204
|
invalidLabel: "Please enter a valid number.",
|
|
@@ -2331,27 +3215,14 @@ function apply(ctx) {
|
|
|
2331
3215
|
const callCommand = async (sessionId, line) => {
|
|
2332
3216
|
const actx = sessions.scope(sessionId);
|
|
2333
3217
|
const face = actx === void 0 ? void 0 : sessions.sessionOf(actx);
|
|
2334
|
-
if (face === void 0) return false;
|
|
3218
|
+
if (face === void 0) return { matched: false };
|
|
2335
3219
|
try {
|
|
2336
3220
|
const result = await face.command(line);
|
|
2337
|
-
return result?.ok === true && result.value?.matched === true;
|
|
3221
|
+
return { matched: result?.ok === true && result.value?.matched === true };
|
|
2338
3222
|
} catch {
|
|
2339
|
-
return false;
|
|
3223
|
+
return { matched: false };
|
|
2340
3224
|
}
|
|
2341
3225
|
};
|
|
2342
|
-
const schedulerScope = settingsScope === void 0 ? void 0 : settingsScope.bind({ namespace: SETTINGS_NS });
|
|
2343
|
-
ctx.slots.inject(
|
|
2344
|
-
INPUT_RIGHT_SLOT,
|
|
2345
|
-
() => ctx.slots.register(
|
|
2346
|
-
{
|
|
2347
|
-
name: INPUT_RIGHT_SLOT,
|
|
2348
|
-
id: "later",
|
|
2349
|
-
order: 50,
|
|
2350
|
-
inject: () => ({ callCommand, locale, schedulerScope })
|
|
2351
|
-
},
|
|
2352
|
-
SchedButton
|
|
2353
|
-
)
|
|
2354
|
-
);
|
|
2355
3226
|
ctx.slots.inject(
|
|
2356
3227
|
"conversation.input.dock",
|
|
2357
3228
|
() => ctx.slots.register(
|
|
@@ -2364,19 +3235,74 @@ function apply(ctx) {
|
|
|
2364
3235
|
ScheduleDock
|
|
2365
3236
|
)
|
|
2366
3237
|
);
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
3238
|
+
let composerMounted = false;
|
|
3239
|
+
const mountComposer = (scope) => {
|
|
3240
|
+
if (composerMounted) return;
|
|
3241
|
+
composerMounted = true;
|
|
3242
|
+
ctx.slots.inject(
|
|
3243
|
+
INPUT_RIGHT_SLOT,
|
|
3244
|
+
() => ctx.slots.register(
|
|
3245
|
+
{
|
|
3246
|
+
name: INPUT_RIGHT_SLOT,
|
|
3247
|
+
id: "later",
|
|
3248
|
+
order: 50,
|
|
3249
|
+
inject: () => ({ callCommand, locale, schedulerScope: scope })
|
|
3250
|
+
},
|
|
3251
|
+
SchedButton
|
|
3252
|
+
)
|
|
3253
|
+
);
|
|
3254
|
+
};
|
|
3255
|
+
const cardT = locale === void 0 ? void 0 : locale.bind(NS);
|
|
3256
|
+
const cardDeclaration = (slot, scope) => ({
|
|
3257
|
+
name: slot,
|
|
3258
|
+
key: SETTINGS_NS,
|
|
3259
|
+
locale: NS,
|
|
3260
|
+
inject: () => ({ scope, t: cardT })
|
|
3261
|
+
});
|
|
3262
|
+
ctx.inject(["settingsScope"], () => {
|
|
3263
|
+
const binder = ctx.get("settingsScope");
|
|
3264
|
+
if (binder === void 0) return;
|
|
3265
|
+
detectedVersion = "classic";
|
|
3266
|
+
const scope = binder.bind({ namespace: SETTINGS_NS });
|
|
3267
|
+
getClockIconStyle = () => {
|
|
3268
|
+
const snap = scope.getSnapshot();
|
|
3269
|
+
if (snap.status !== "ready") return "auto";
|
|
3270
|
+
const value = snap.value ?? {};
|
|
3271
|
+
const style = value["clockIconStyle"];
|
|
3272
|
+
return style === "modern" || style === "classic" ? style : "auto";
|
|
3273
|
+
};
|
|
3274
|
+
mountComposer(scope);
|
|
3275
|
+
if (cardT === void 0) return;
|
|
3276
|
+
ctx.slots.inject(
|
|
3277
|
+
"settings.plugin.item",
|
|
3278
|
+
() => ctx.slots.register(cardDeclaration("settings.plugin.item", scope), SchedulerSettingsCard)
|
|
3279
|
+
);
|
|
3280
|
+
});
|
|
3281
|
+
ctx.inject(["configForms"], () => {
|
|
3282
|
+
const forms = ctx.get("configForms");
|
|
3283
|
+
if (forms === void 0 || typeof forms.get !== "function" || typeof forms.whileServed !== "function") return;
|
|
3284
|
+
detectedVersion = "modern";
|
|
3285
|
+
const scope = forms.get(ENTRY_ID);
|
|
3286
|
+
getClockIconStyle = () => {
|
|
3287
|
+
const snap = scope.getSnapshot();
|
|
3288
|
+
if (snap.status !== "ready") return "auto";
|
|
3289
|
+
const value = snap.value ?? {};
|
|
3290
|
+
const style = value["clockIconStyle"];
|
|
3291
|
+
return style === "modern" || style === "classic" ? style : "auto";
|
|
3292
|
+
};
|
|
3293
|
+
mountComposer(scope);
|
|
3294
|
+
if (cardT === void 0) return;
|
|
3295
|
+
ctx.effect(
|
|
3296
|
+
() => forms.whileServed(
|
|
3297
|
+
[ENTRY_ID],
|
|
3298
|
+
() => ctx.slots.inject(
|
|
3299
|
+
"plugins.bundle.config",
|
|
3300
|
+
() => ctx.slots.register(cardDeclaration("plugins.bundle.config", scope), SchedulerSettingsCard)
|
|
3301
|
+
)
|
|
3302
|
+
),
|
|
3303
|
+
"dsh-later: plugins-page card"
|
|
3304
|
+
);
|
|
3305
|
+
});
|
|
2380
3306
|
}
|
|
2381
3307
|
|
|
2382
3308
|
return module.exports;
|