dsh-global-todo 0.6.4 → 0.6.6
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 +24 -0
- package/docs/screenshots/panel-detail.png +0 -0
- package/docs/screenshots/panel-select.png +0 -0
- package/docs/screenshots/panel-settings.png +0 -0
- package/docs/screenshots/panel.png +0 -0
- package/lib/client.js +249 -120
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## [0.6.6] — 2026-10-20
|
|
2
|
+
|
|
3
|
+
Both forms now look like forms.
|
|
4
|
+
|
|
5
|
+
- **Add form** gets real labels and a hint under each field instead of placeholder-only inputs
|
|
6
|
+
(a placeholder vanishes the moment you type, so it can never carry a rule), and the expanded
|
|
7
|
+
area is separated from the title row by a hairline.
|
|
8
|
+
- **Detail editor** gets the same treatment: labelled Title / Priority / Due / Description rows.
|
|
9
|
+
- **The 340px detail card no longer fights its own grid.** Meta rows stack to one column inside
|
|
10
|
+
the card, attachments wrap to their own lines, and the raw path is dropped there because the
|
|
11
|
+
card cannot show it next to two buttons — the title still carries it as a tooltip.
|
|
12
|
+
|
|
13
|
+
## [0.6.5] — 2026-10-20
|
|
14
|
+
|
|
15
|
+
The three loose ends the spec called out but the earlier passes left behind.
|
|
16
|
+
|
|
17
|
+
- **Five type sizes, for real.** 12.5/11.5/10.5 collapse into 12 and 11, so the panel now uses
|
|
18
|
+
exactly 20 / 14 / 13 / 12 / 11 — one ladder instead of eight near-identical steps.
|
|
19
|
+
- **Group actions move behind one kebab.** `Collect` and `Exempt` stop competing with the group
|
|
20
|
+
title; an exemption keeps a small always-visible mute mark (it is a state, not an action) and
|
|
21
|
+
the menu states both intents in words (`Collect from this conversation`, `Mute this workspace`).
|
|
22
|
+
- **Escape closes the innermost layer.** Settings, then the add form, then the open row — and a
|
|
23
|
+
click outside the settings card dismisses it, matching every other popover. The gear toggles.
|
|
24
|
+
|
|
1
25
|
## [0.6.4] — 2026-10-20
|
|
2
26
|
|
|
3
27
|
The settings panel, rebuilt as an inspector instead of a stack of labelled rows.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -50,7 +50,7 @@ window.__ModuleLoader__.load({
|
|
|
50
50
|
* different mechanisms (restart vs. hot reload), so "which one am I looking
|
|
51
51
|
* at" needs an answer that the bundle itself carries.
|
|
52
52
|
*/
|
|
53
|
-
const UI_REV = '
|
|
53
|
+
const UI_REV = 'ui42'
|
|
54
54
|
/** Last language resolved for the shell; module-level so a fetch failure can be worded too. */
|
|
55
55
|
let activeLang = 'zh'
|
|
56
56
|
|
|
@@ -106,6 +106,10 @@ window.__ModuleLoader__.load({
|
|
|
106
106
|
'settings.cancel': '取消',
|
|
107
107
|
'settings.saved': '设置已保存',
|
|
108
108
|
'settings.about': '关于',
|
|
109
|
+
'group.actions': '分组操作',
|
|
110
|
+
'group.capture': '从本对话抓取',
|
|
111
|
+
'group.exemptOn': '本工作区免打扰',
|
|
112
|
+
'group.exemptOff': '取消本工作区免打扰',
|
|
109
113
|
'settings.remindersHint': '到期时在面板顶部提示,并按设置提前。',
|
|
110
114
|
'settings.ingestHint': '允许其他工具带 token 写入,先进收件箱。',
|
|
111
115
|
'settings.tokenHint': '留空表示不改动;轮换后旧 token 立刻失效。',
|
|
@@ -225,6 +229,10 @@ window.__ModuleLoader__.load({
|
|
|
225
229
|
'field.title': '要做什么?回车即存',
|
|
226
230
|
'field.description': '描述:验收标准、已知的坑…',
|
|
227
231
|
'field.links': '相关文档与地址,一行一个(/绝对路径 或 https://…)',
|
|
232
|
+
'field.tagsLabel': '标签',
|
|
233
|
+
'field.titleLabel': '标题',
|
|
234
|
+
'field.descriptionHint': '验收标准、已知的坑都可以写在这里。',
|
|
235
|
+
'field.linksHint': '一行一个:绝对路径或 https:// 链接。',
|
|
228
236
|
'field.tags': '标签,逗号分隔',
|
|
229
237
|
'field.priority': '优先级',
|
|
230
238
|
'field.due': '截止时间',
|
|
@@ -359,6 +367,10 @@ window.__ModuleLoader__.load({
|
|
|
359
367
|
'settings.cancel': 'Cancel',
|
|
360
368
|
'settings.saved': 'Settings saved',
|
|
361
369
|
'settings.about': 'About',
|
|
370
|
+
'group.actions': 'Group actions',
|
|
371
|
+
'group.capture': 'Collect from this conversation',
|
|
372
|
+
'group.exemptOn': 'Mute this workspace',
|
|
373
|
+
'group.exemptOff': 'Unmute this workspace',
|
|
362
374
|
'settings.remindersHint': 'Flag a deadline at the top of the panel when it comes due.',
|
|
363
375
|
'settings.ingestHint': 'Let other tools POST todos with a token; they start in the inbox.',
|
|
364
376
|
'settings.tokenHint': 'Leave blank to keep the current one; rotating invalidates the old token.',
|
|
@@ -479,6 +491,10 @@ window.__ModuleLoader__.load({
|
|
|
479
491
|
'field.title': 'What needs doing? Enter to save',
|
|
480
492
|
'field.description': 'Description: acceptance, known traps…',
|
|
481
493
|
'field.links': 'Documents and links, one per line (/abs/path or https://…)',
|
|
494
|
+
'field.tagsLabel': 'Tags',
|
|
495
|
+
'field.titleLabel': 'Title',
|
|
496
|
+
'field.descriptionHint': 'Acceptance criteria and known traps belong here.',
|
|
497
|
+
'field.linksHint': 'One per line: an absolute path or an https:// link.',
|
|
482
498
|
'field.tags': 'Tags, comma separated',
|
|
483
499
|
'field.priority': 'Priority',
|
|
484
500
|
'field.due': 'Due',
|
|
@@ -968,6 +984,19 @@ window.__ModuleLoader__.load({
|
|
|
968
984
|
.gt-card { background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1);
|
|
969
985
|
border-radius: 12px; padding: 12px 14px; }
|
|
970
986
|
.gt-new { display: flex; flex-direction: column; gap: 10px; }
|
|
987
|
+
.gt-new-fields { display: flex; flex-direction: column; gap: 12px; padding-top: 4px;
|
|
988
|
+
border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
989
|
+
.gt-new-fields .gt-textarea { min-height: 64px; }
|
|
990
|
+
.gt-new-fields .gt-field-row { display: grid; grid-template-columns: minmax(0, 1fr) 190px; gap: 12px; }
|
|
991
|
+
.gt-edit-meta { display: grid; grid-template-columns: minmax(0, 1fr) 190px; gap: 12px; align-items: start; }
|
|
992
|
+
/* The 340px card cannot hold the two-column meta row or a path beside two buttons:
|
|
993
|
+
stack them there, and let attachments wrap onto their own lines. */
|
|
994
|
+
.gt-root[data-wide="1"][data-wide-detail="1"] .gt-detail .gt-edit-meta,
|
|
995
|
+
.gt-root[data-wide="1"][data-wide-detail="1"] .gt-detail .gt-new-fields .gt-field-row {
|
|
996
|
+
grid-template-columns: minmax(0, 1fr); }
|
|
997
|
+
.gt-root[data-wide="1"][data-wide-detail="1"] .gt-detail .gt-link { flex-wrap: wrap; row-gap: 4px; }
|
|
998
|
+
.gt-root[data-wide="1"][data-wide-detail="1"] .gt-detail .gt-link .gt-mono { display: none; }
|
|
999
|
+
.gt-root[data-wide="1"][data-wide-detail="1"] .gt-detail .gt-link-main { flex: 1 1 100%; }
|
|
971
1000
|
.gt-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
972
1001
|
.gt-input, .gt-select, .gt-textarea { background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary);
|
|
973
1002
|
border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; padding: 7px 10px; font: inherit; }
|
|
@@ -983,7 +1012,7 @@ window.__ModuleLoader__.load({
|
|
|
983
1012
|
font: inherit; font-size: 13px; padding: 0; min-width: 0; }
|
|
984
1013
|
.gt-search input:focus { outline: none; }
|
|
985
1014
|
.gt-search:focus-within { border-color: var(--dsw-alias-brand-primary); }
|
|
986
|
-
.gt-kbd { font-size:
|
|
1015
|
+
.gt-kbd { font-size: 11px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 4px;
|
|
987
1016
|
padding: 0 4px; color: var(--dsw-alias-label-secondary); }
|
|
988
1017
|
.gt-dir { display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
|
|
989
1018
|
color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1);
|
|
@@ -1014,7 +1043,7 @@ window.__ModuleLoader__.load({
|
|
|
1014
1043
|
the two toggles. Chips stay for facts about a todo (tags, levels, notes). */
|
|
1015
1044
|
.gt-tabs { display: inline-flex; gap: 2px; padding: 2px; border-radius: 8px; background: var(--dsw-alias-bg-layer-2); }
|
|
1016
1045
|
.gt-tab { border: none; background: transparent; color: var(--dsw-alias-label-secondary); font: inherit;
|
|
1017
|
-
font-size:
|
|
1046
|
+
font-size: 12px; border-radius: 6px; padding: 0 10px; min-height: 24px; box-sizing: border-box;
|
|
1018
1047
|
cursor: pointer; white-space: nowrap; }
|
|
1019
1048
|
.gt-tab:hover { color: var(--dsw-alias-label-primary); }
|
|
1020
1049
|
.gt-tab[data-on="1"] { background: var(--dsw-alias-button-ghost-active-fill); color: var(--dsw-alias-label-primary); font-weight: 600; }
|
|
@@ -1047,29 +1076,26 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
1047
1076
|
display: inline-flex; align-items: center; gap: 6px; font: inherit; padding: 2px 0; }
|
|
1048
1077
|
.gt-group-head svg { transition: transform .12s ease; transform: rotate(90deg); }
|
|
1049
1078
|
.gt-group[data-collapsed="1"] .gt-group-head svg { transform: rotate(0deg); }
|
|
1050
|
-
.gt-group-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
.gt-group:hover .gt-group-
|
|
1055
|
-
.gt-group
|
|
1056
|
-
.gt-group-
|
|
1057
|
-
/* An exemption is a state, not an action: it stays visible and reads as "on" without a hover. */
|
|
1058
|
-
.gt-group-action[data-on="1"] { opacity: 1; color: var(--dsw-alias-label-primary);
|
|
1059
|
-
border-color: var(--dsw-alias-border-l2); }
|
|
1079
|
+
.gt-group-flag { margin-left: 2px; display: inline-flex; align-items: center; color: var(--dsw-alias-label-secondary); }
|
|
1080
|
+
/* The group kebab stays quiet until the row is hovered or focused. */
|
|
1081
|
+
.gt-group-head .gt-iconbtn { margin-left: 2px; min-width: 24px; min-height: 24px; opacity: 0;
|
|
1082
|
+
transition: opacity .12s ease; }
|
|
1083
|
+
.gt-group:hover .gt-group-head .gt-iconbtn, .gt-group-head .gt-iconbtn:focus-visible,
|
|
1084
|
+
.gt-group-head .gt-iconbtn[aria-expanded="true"] { opacity: .72; }
|
|
1085
|
+
.gt-group-head .gt-iconbtn:hover { opacity: 1; }
|
|
1060
1086
|
.gt-group-count { color: var(--dsw-alias-label-secondary); font-size: 12px; font-weight: 400; }
|
|
1061
1087
|
/* A group's level is redundant while it is expanded — the rows show it. */
|
|
1062
1088
|
.gt-group:not([data-collapsed="1"]) .gt-group-head .gt-level { display: none; }
|
|
1063
1089
|
.gt-group-label { color: var(--dsw-alias-label-primary); font-weight: 600; font-size: 12px;
|
|
1064
1090
|
line-height: 18px; max-width: 480px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1065
|
-
.gt-level { display: inline-flex; align-items: center; gap: 3px; font-size:
|
|
1091
|
+
.gt-level { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; padding: 0;
|
|
1066
1092
|
border: none; color: var(--dsw-alias-label-secondary); }
|
|
1067
1093
|
.gt-level[data-level="urgent"] { color: var(--dsw-alias-state-error-primary); border-color: var(--dsw-alias-state-error-primary); font-weight: 600; }
|
|
1068
1094
|
.gt-level[data-level="high"] { color: var(--dsw-alias-state-warn-primary); border-color: var(--dsw-alias-state-warn-primary); }
|
|
1069
1095
|
.gt-level[data-level="low"] { color: var(--dsw-alias-label-secondary); }
|
|
1070
1096
|
.gt-dot { width: 6px; height: 6px; border-radius: 999px; background: var(--dsw-alias-state-success-primary);
|
|
1071
1097
|
display: inline-block; }
|
|
1072
|
-
.gt-here { font-size:
|
|
1098
|
+
.gt-here { font-size: 11px; padding: 0 5px; border-radius: 999px; border: 1px solid var(--dsw-alias-brand-primary);
|
|
1073
1099
|
color: var(--dsw-alias-brand-primary); }
|
|
1074
1100
|
.gt-list { display: flex; flex-direction: column; gap: 8px; }
|
|
1075
1101
|
.gt-item { position: relative; display: flex; flex-direction: column; gap: 8px;
|
|
@@ -1102,7 +1128,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
1102
1128
|
.gt-item-title { font-weight: 600; font-size: 14px; word-break: break-word; line-height: 22px;
|
|
1103
1129
|
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
1104
1130
|
.gt-input-xs { width: 64px; flex: 0 0 auto; min-width: 0; padding: 0 8px; min-height: 28px;
|
|
1105
|
-
box-sizing: border-box; font-size:
|
|
1131
|
+
box-sizing: border-box; font-size: 12px; }
|
|
1106
1132
|
.gt-seq { font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary); }
|
|
1107
1133
|
.gt-reminders { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 12px;
|
|
1108
1134
|
min-height: 36px; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1);
|
|
@@ -1144,13 +1170,13 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
1144
1170
|
.gt-settings-foot { display: flex; flex: 0 0 auto; justify-content: flex-end; gap: 8px;
|
|
1145
1171
|
padding: 12px 16px; border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
1146
1172
|
.gt-group-select { display: none; min-height: 28px; max-width: 150px; padding: 0 8px; font: inherit;
|
|
1147
|
-
font-size:
|
|
1173
|
+
font-size: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1);
|
|
1148
1174
|
border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; }
|
|
1149
1175
|
.gt-root[data-wide="0"] .gt-group-tabs { display: none; }
|
|
1150
1176
|
.gt-root[data-wide="0"] .gt-group-select { display: inline-flex; align-items: center; }
|
|
1151
1177
|
.gt-inbox-note { padding: 0 2px 2px; color: var(--dsw-alias-label-secondary); font-size: 13px;
|
|
1152
1178
|
line-height: 20px; }
|
|
1153
|
-
.gt-settings-path { font-size:
|
|
1179
|
+
.gt-settings-path { font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1154
1180
|
.gt-settings .gt-input { flex: 0 0 auto; width: 100%; min-height: 32px; }
|
|
1155
1181
|
.gt-settings .gt-input-num { width: 76px; }
|
|
1156
1182
|
.gt-check-line { display: flex; align-items: center; gap: 8px; justify-content: flex-start;
|
|
@@ -1170,7 +1196,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
1170
1196
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1171
1197
|
.gt-truncated { color: var(--dsw-alias-state-warn-primary); font-size: 12px; }
|
|
1172
1198
|
.gt-bulk-tag { flex: 0 1 120px; min-width: 90px; }
|
|
1173
|
-
.gt-inbox { margin-left: 6px; padding: 1px 6px; border-radius: 999px; font-size:
|
|
1199
|
+
.gt-inbox { margin-left: 6px; padding: 1px 6px; border-radius: 999px; font-size: 11px; font-weight: 600;
|
|
1174
1200
|
color: var(--dsw-alias-state-warn-primary); border: 1px solid var(--dsw-alias-state-warn-primary);
|
|
1175
1201
|
vertical-align: 1px; }
|
|
1176
1202
|
.gt-item[data-status="done"] .gt-item-title, .gt-item[data-status="cancelled"] .gt-item-title {
|
|
@@ -1229,17 +1255,17 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
1229
1255
|
.gt-link-main { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; }
|
|
1230
1256
|
.gt-note-item { display: flex; flex-direction: column; gap: 4px; padding: 8px 0; }
|
|
1231
1257
|
.gt-note-item + .gt-note-item { border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
1232
|
-
.gt-note-meta { display: flex; align-items: center; gap: 6px; font-size:
|
|
1258
|
+
.gt-note-meta { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--dsw-alias-label-secondary);
|
|
1233
1259
|
white-space: nowrap; }
|
|
1234
1260
|
.gt-note-text { font-size: 13px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; max-width: 68ch; }
|
|
1235
1261
|
.gt-link a { color: var(--dsw-alias-brand-primary); text-decoration: none; }
|
|
1236
1262
|
.gt-link a:hover { text-decoration: underline; }
|
|
1237
1263
|
.gt-link-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 320px; }
|
|
1238
|
-
.gt-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size:
|
|
1264
|
+
.gt-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
|
|
1239
1265
|
color: var(--dsw-alias-label-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1240
1266
|
.gt-banner { border-radius: 9px; padding: 8px 11px; border: 1px solid var(--dsw-alias-state-error-primary);
|
|
1241
1267
|
color: var(--dsw-alias-state-error-primary); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
1242
|
-
.gt-activity { display: flex; flex-direction: column; gap: 3px; color: var(--dsw-alias-label-secondary); font-size:
|
|
1268
|
+
.gt-activity { display: flex; flex-direction: column; gap: 3px; color: var(--dsw-alias-label-secondary); font-size: 12px; }
|
|
1243
1269
|
.gt-empty { display: flex; flex-direction: column; gap: 6px; align-items: center; justify-content: center;
|
|
1244
1270
|
color: var(--dsw-alias-label-secondary); padding: 42px 16px; text-align: center; flex: 1; }
|
|
1245
1271
|
.gt-empty-title { font-size: 14px; color: var(--dsw-alias-label-primary); }
|
|
@@ -1900,34 +1926,59 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
1900
1926
|
h('div', { className: 'gt-section-title' }, t('detail.edit')),
|
|
1901
1927
|
h(
|
|
1902
1928
|
'div',
|
|
1903
|
-
{ className: 'gt-
|
|
1904
|
-
h('
|
|
1905
|
-
h(Segmented, {
|
|
1906
|
-
label: t('field.priority'),
|
|
1907
|
-
value: draft.priority,
|
|
1908
|
-
onChange: (priority) => setDraft({ ...draft, priority }),
|
|
1909
|
-
options: [
|
|
1910
|
-
{ value: 'low', label: t('priority.low') },
|
|
1911
|
-
{ value: 'normal', label: t('priority.normal') },
|
|
1912
|
-
{ value: 'high', label: t('priority.high') },
|
|
1913
|
-
{ value: 'urgent', label: t('priority.urgent') },
|
|
1914
|
-
],
|
|
1915
|
-
}),
|
|
1929
|
+
{ className: 'gt-field' },
|
|
1930
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-edit-title' }, t('field.titleLabel')),
|
|
1916
1931
|
h('input', {
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
value: draft.
|
|
1920
|
-
|
|
1921
|
-
|
|
1932
|
+
id: 'gt-edit-title',
|
|
1933
|
+
className: 'gt-input',
|
|
1934
|
+
value: draft.title,
|
|
1935
|
+
onChange: (event) => setDraft({ ...draft, title: event.target.value }),
|
|
1936
|
+
}),
|
|
1937
|
+
),
|
|
1938
|
+
h(
|
|
1939
|
+
'div',
|
|
1940
|
+
{ className: 'gt-field-row gt-edit-meta' },
|
|
1941
|
+
h(
|
|
1942
|
+
'div',
|
|
1943
|
+
{ className: 'gt-field' },
|
|
1944
|
+
h('span', { className: 'gt-field-label' }, t('field.priority')),
|
|
1945
|
+
h(Segmented, {
|
|
1946
|
+
label: t('field.priority'),
|
|
1947
|
+
value: draft.priority,
|
|
1948
|
+
onChange: (priority) => setDraft({ ...draft, priority }),
|
|
1949
|
+
options: [
|
|
1950
|
+
{ value: 'urgent', label: t('priority.urgent') },
|
|
1951
|
+
{ value: 'high', label: t('priority.high') },
|
|
1952
|
+
{ value: 'normal', label: t('priority.normal') },
|
|
1953
|
+
{ value: 'low', label: t('priority.low') },
|
|
1954
|
+
],
|
|
1955
|
+
}),
|
|
1956
|
+
),
|
|
1957
|
+
h(
|
|
1958
|
+
'div',
|
|
1959
|
+
{ className: 'gt-field' },
|
|
1960
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-edit-due' }, t('field.due')),
|
|
1961
|
+
h('input', {
|
|
1962
|
+
id: 'gt-edit-due',
|
|
1963
|
+
className: 'gt-input',
|
|
1964
|
+
type: 'datetime-local',
|
|
1965
|
+
value: draft.due,
|
|
1966
|
+
onChange: (event) => setDraft({ ...draft, due: event.target.value }),
|
|
1967
|
+
}),
|
|
1968
|
+
),
|
|
1969
|
+
),
|
|
1970
|
+
h(
|
|
1971
|
+
'div',
|
|
1972
|
+
{ className: 'gt-field' },
|
|
1973
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-edit-desc' }, t('detail.description')),
|
|
1974
|
+
h('textarea', {
|
|
1975
|
+
id: 'gt-edit-desc',
|
|
1976
|
+
className: 'gt-textarea',
|
|
1977
|
+
value: draft.description,
|
|
1978
|
+
placeholder: t('field.description'),
|
|
1979
|
+
onChange: (event) => setDraft({ ...draft, description: event.target.value }),
|
|
1922
1980
|
}),
|
|
1923
1981
|
),
|
|
1924
|
-
h('textarea', {
|
|
1925
|
-
className: 'gt-textarea',
|
|
1926
|
-
value: draft.description,
|
|
1927
|
-
placeholder: t('field.description'),
|
|
1928
|
-
'aria-label': t('detail.description'),
|
|
1929
|
-
onChange: (event) => setDraft({ ...draft, description: event.target.value }),
|
|
1930
|
-
}),
|
|
1931
1982
|
dirty
|
|
1932
1983
|
? h(
|
|
1933
1984
|
'div',
|
|
@@ -2411,6 +2462,8 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
2411
2462
|
// The wide-screen detail card floats over the panel, so its offsets are measured from
|
|
2412
2463
|
// the panel's own column rather than derived from the viewport width.
|
|
2413
2464
|
const innerRef = useRef(null)
|
|
2465
|
+
const settingsRef = useRef(null)
|
|
2466
|
+
const settingsTriggerRef = useRef(null)
|
|
2414
2467
|
const [detailAnchor, setDetailAnchor] = useState(null)
|
|
2415
2468
|
const titleRef = useRef(null)
|
|
2416
2469
|
const searchRef = useRef(null)
|
|
@@ -2572,8 +2625,8 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
2572
2625
|
return () => clearInterval(timer)
|
|
2573
2626
|
}, [reload])
|
|
2574
2627
|
|
|
2575
|
-
// Keyboard: `/` search, `n` new, Escape
|
|
2576
|
-
// user is typing in a field.
|
|
2628
|
+
// Keyboard: `/` search, `n` new, Escape closes the innermost layer — but never
|
|
2629
|
+
// while the user is typing in a field, where the first Escape only drops focus.
|
|
2577
2630
|
useEffect(() => {
|
|
2578
2631
|
if (typeof document === 'undefined') return undefined
|
|
2579
2632
|
const onKey = (event) => {
|
|
@@ -2583,8 +2636,16 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
2583
2636
|
target !== undefined &&
|
|
2584
2637
|
(target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable === true)
|
|
2585
2638
|
if (event.key === 'Escape') {
|
|
2586
|
-
setExpandedId('')
|
|
2587
2639
|
if (typing && typeof target.blur === 'function') target.blur()
|
|
2640
|
+
if (settingsOpen) {
|
|
2641
|
+
setSettingsOpen(false)
|
|
2642
|
+
return
|
|
2643
|
+
}
|
|
2644
|
+
if (adding) {
|
|
2645
|
+
setAdding(false)
|
|
2646
|
+
return
|
|
2647
|
+
}
|
|
2648
|
+
setExpandedId('')
|
|
2588
2649
|
return
|
|
2589
2650
|
}
|
|
2590
2651
|
if (typing || event.metaKey || event.ctrlKey || event.altKey) return
|
|
@@ -2598,7 +2659,23 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
2598
2659
|
}
|
|
2599
2660
|
document.addEventListener('keydown', onKey)
|
|
2600
2661
|
return () => document.removeEventListener('keydown', onKey)
|
|
2601
|
-
}, [])
|
|
2662
|
+
}, [settingsOpen, adding])
|
|
2663
|
+
|
|
2664
|
+
// The settings card floats over the list, so a click anywhere else dismisses it
|
|
2665
|
+
// the way every other popover on the web does.
|
|
2666
|
+
useEffect(() => {
|
|
2667
|
+
if (!settingsOpen || typeof document === 'undefined') return undefined
|
|
2668
|
+
const onDown = (event) => {
|
|
2669
|
+
const card = settingsRef.current
|
|
2670
|
+
const gear = settingsTriggerRef.current
|
|
2671
|
+
if (card === null) return
|
|
2672
|
+
if (card.contains(event.target) === true) return
|
|
2673
|
+
if (gear !== null && gear.contains(event.target) === true) return
|
|
2674
|
+
setSettingsOpen(false)
|
|
2675
|
+
}
|
|
2676
|
+
document.addEventListener('mousedown', onDown)
|
|
2677
|
+
return () => document.removeEventListener('mousedown', onDown)
|
|
2678
|
+
}, [settingsOpen])
|
|
2602
2679
|
|
|
2603
2680
|
const run = async (id, operation) => {
|
|
2604
2681
|
setBusyId(id)
|
|
@@ -3273,15 +3350,20 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
3273
3350
|
'div',
|
|
3274
3351
|
{ className: 'gt-head-actions' },
|
|
3275
3352
|
h(
|
|
3276
|
-
'
|
|
3277
|
-
{
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3353
|
+
'span',
|
|
3354
|
+
{ ref: settingsTriggerRef, style: { display: 'inline-flex' } },
|
|
3355
|
+
h(
|
|
3356
|
+
'button',
|
|
3357
|
+
{
|
|
3358
|
+
type: 'button',
|
|
3359
|
+
className: 'gt-iconbtn',
|
|
3360
|
+
title: t('settings.title'),
|
|
3361
|
+
'aria-label': t('settings.title'),
|
|
3362
|
+
'aria-expanded': settingsOpen,
|
|
3363
|
+
onClick: () => (settingsOpen ? setSettingsOpen(false) : openSettings()),
|
|
3364
|
+
},
|
|
3365
|
+
h(Icon, { name: 'gear' }),
|
|
3366
|
+
),
|
|
3285
3367
|
),
|
|
3286
3368
|
h(
|
|
3287
3369
|
'button',
|
|
@@ -3323,6 +3405,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
3323
3405
|
? h(
|
|
3324
3406
|
'div',
|
|
3325
3407
|
{
|
|
3408
|
+
ref: settingsRef,
|
|
3326
3409
|
className: 'gt-settings',
|
|
3327
3410
|
role: 'dialog',
|
|
3328
3411
|
'aria-label': t('settings.title'),
|
|
@@ -3518,31 +3601,65 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
3518
3601
|
more
|
|
3519
3602
|
? h(
|
|
3520
3603
|
'div',
|
|
3521
|
-
{ className: 'gt-new' },
|
|
3522
|
-
h('textarea', {
|
|
3523
|
-
className: 'gt-textarea',
|
|
3524
|
-
placeholder: t('field.description'),
|
|
3525
|
-
value: draft.description,
|
|
3526
|
-
'aria-label': t('detail.description'),
|
|
3527
|
-
onChange: (event) => setDraft({ ...draft, description: event.target.value }),
|
|
3528
|
-
}),
|
|
3529
|
-
h('textarea', {
|
|
3530
|
-
className: 'gt-textarea',
|
|
3531
|
-
placeholder: t('field.links'),
|
|
3532
|
-
value: draft.links,
|
|
3533
|
-
'aria-label': t('detail.links'),
|
|
3534
|
-
onChange: (event) => setDraft({ ...draft, links: event.target.value }),
|
|
3535
|
-
}),
|
|
3604
|
+
{ className: 'gt-new-fields' },
|
|
3536
3605
|
h(
|
|
3537
3606
|
'div',
|
|
3538
|
-
{ className: 'gt-
|
|
3539
|
-
h('
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3607
|
+
{ className: 'gt-field' },
|
|
3608
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-new-desc' }, t('detail.description')),
|
|
3609
|
+
h('textarea', {
|
|
3610
|
+
id: 'gt-new-desc',
|
|
3611
|
+
className: 'gt-textarea',
|
|
3612
|
+
placeholder: t('field.description'),
|
|
3613
|
+
value: draft.description,
|
|
3614
|
+
onChange: (event) => setDraft({ ...draft, description: event.target.value }),
|
|
3545
3615
|
}),
|
|
3616
|
+
h('span', { className: 'gt-hint' }, t('field.descriptionHint')),
|
|
3617
|
+
),
|
|
3618
|
+
h(
|
|
3619
|
+
'div',
|
|
3620
|
+
{ className: 'gt-field' },
|
|
3621
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-new-links' }, t('detail.links')),
|
|
3622
|
+
h('textarea', {
|
|
3623
|
+
id: 'gt-new-links',
|
|
3624
|
+
className: 'gt-textarea',
|
|
3625
|
+
placeholder: t('field.links'),
|
|
3626
|
+
value: draft.links,
|
|
3627
|
+
onChange: (event) => setDraft({ ...draft, links: event.target.value }),
|
|
3628
|
+
}),
|
|
3629
|
+
h('span', { className: 'gt-hint' }, t('field.linksHint')),
|
|
3630
|
+
),
|
|
3631
|
+
h(
|
|
3632
|
+
'div',
|
|
3633
|
+
{ className: 'gt-field-row' },
|
|
3634
|
+
h(
|
|
3635
|
+
'div',
|
|
3636
|
+
{ className: 'gt-field' },
|
|
3637
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-new-tags' }, t('field.tagsLabel')),
|
|
3638
|
+
h('input', {
|
|
3639
|
+
id: 'gt-new-tags',
|
|
3640
|
+
className: 'gt-input gt-input-sm',
|
|
3641
|
+
placeholder: t('field.tags'),
|
|
3642
|
+
value: draft.tags,
|
|
3643
|
+
onChange: (event) => setDraft({ ...draft, tags: event.target.value }),
|
|
3644
|
+
}),
|
|
3645
|
+
),
|
|
3646
|
+
h(
|
|
3647
|
+
'div',
|
|
3648
|
+
{ className: 'gt-field' },
|
|
3649
|
+
h('label', { className: 'gt-field-label', htmlFor: 'gt-new-due' }, t('field.due')),
|
|
3650
|
+
h('input', {
|
|
3651
|
+
id: 'gt-new-due',
|
|
3652
|
+
className: 'gt-input gt-input-sm',
|
|
3653
|
+
type: 'datetime-local',
|
|
3654
|
+
value: draft.due,
|
|
3655
|
+
onChange: (event) => setDraft({ ...draft, due: event.target.value }),
|
|
3656
|
+
}),
|
|
3657
|
+
),
|
|
3658
|
+
),
|
|
3659
|
+
h(
|
|
3660
|
+
'div',
|
|
3661
|
+
{ className: 'gt-field' },
|
|
3662
|
+
h('span', { className: 'gt-field-label' }, t('field.priority')),
|
|
3546
3663
|
h(Segmented, {
|
|
3547
3664
|
label: t('field.priority'),
|
|
3548
3665
|
value: draft.priority,
|
|
@@ -3554,14 +3671,6 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
3554
3671
|
{ value: 'low', label: t('priority.low') },
|
|
3555
3672
|
],
|
|
3556
3673
|
}),
|
|
3557
|
-
h('input', {
|
|
3558
|
-
className: 'gt-input gt-input-sm',
|
|
3559
|
-
type: 'datetime-local',
|
|
3560
|
-
value: draft.due,
|
|
3561
|
-
title: t('field.due'),
|
|
3562
|
-
'aria-label': t('field.due'),
|
|
3563
|
-
onChange: (event) => setDraft({ ...draft, due: event.target.value }),
|
|
3564
|
-
}),
|
|
3565
3674
|
),
|
|
3566
3675
|
)
|
|
3567
3676
|
: null,
|
|
@@ -4108,39 +4217,59 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
|
|
|
4108
4217
|
? h('span', { className: 'gt-dot', title: t('meta.session.live'), 'aria-label': t('meta.session.live') })
|
|
4109
4218
|
: null,
|
|
4110
4219
|
),
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4220
|
+
// An exemption is a state, so it keeps a small always-visible mark; the
|
|
4221
|
+
// actions themselves live behind one kebab instead of two text buttons
|
|
4222
|
+
// competing with the group title.
|
|
4223
|
+
exemptGroup
|
|
4224
|
+
? h(
|
|
4225
|
+
'span',
|
|
4115
4226
|
{
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
title: t('action.captureGroupHint'),
|
|
4119
|
-
onClick: (event) => {
|
|
4120
|
-
event.stopPropagation()
|
|
4121
|
-
void requestCapture(group.sessionId, group.label)
|
|
4122
|
-
},
|
|
4123
|
-
},
|
|
4124
|
-
h(Icon, { name: 'doc', size: 11 }),
|
|
4125
|
-
t('action.captureShort'),
|
|
4126
|
-
),
|
|
4127
|
-
group.dir === undefined || group.dir === ''
|
|
4128
|
-
? null
|
|
4129
|
-
: h(
|
|
4130
|
-
'button',
|
|
4131
|
-
{
|
|
4132
|
-
type: 'button',
|
|
4133
|
-
className: 'gt-group-action',
|
|
4134
|
-
'data-on': exemptGroup ? '1' : '0',
|
|
4135
|
-
title: exemptGroup ? t('action.exemptHintOn') : t('action.exemptHint'),
|
|
4136
|
-
onClick: (event) => {
|
|
4137
|
-
event.stopPropagation()
|
|
4138
|
-
void toggleExemptWorkspace(group.dir, !exemptGroup)
|
|
4139
|
-
},
|
|
4227
|
+
className: 'gt-group-flag',
|
|
4228
|
+
title: t('action.exemptHintOn'),
|
|
4140
4229
|
},
|
|
4141
4230
|
h(Icon, { name: 'mute', size: 11 }),
|
|
4142
|
-
|
|
4143
|
-
|
|
4231
|
+
)
|
|
4232
|
+
: null,
|
|
4233
|
+
[
|
|
4234
|
+
group.sessionId === undefined
|
|
4235
|
+
? null
|
|
4236
|
+
: {
|
|
4237
|
+
key: 'capture',
|
|
4238
|
+
label: t('action.captureShort'),
|
|
4239
|
+
icon: 'doc',
|
|
4240
|
+
onSelect: () => void requestCapture(group.sessionId, group.label),
|
|
4241
|
+
},
|
|
4242
|
+
group.dir === undefined || group.dir === ''
|
|
4243
|
+
? null
|
|
4244
|
+
: {
|
|
4245
|
+
key: 'exempt',
|
|
4246
|
+
label: exemptGroup ? t('action.exemptOn') : t('action.exempt'),
|
|
4247
|
+
icon: 'mute',
|
|
4248
|
+
onSelect: () => void toggleExemptWorkspace(group.dir, !exemptGroup),
|
|
4249
|
+
},
|
|
4250
|
+
].filter((item) => item !== null).length === 0
|
|
4251
|
+
? null
|
|
4252
|
+
: h(Menu, {
|
|
4253
|
+
label: t('group.actions'),
|
|
4254
|
+
items: [
|
|
4255
|
+
group.sessionId === undefined
|
|
4256
|
+
? null
|
|
4257
|
+
: {
|
|
4258
|
+
key: 'capture',
|
|
4259
|
+
label: t('group.capture'),
|
|
4260
|
+
icon: 'doc',
|
|
4261
|
+
onSelect: () => void requestCapture(group.sessionId, group.label),
|
|
4262
|
+
},
|
|
4263
|
+
group.dir === undefined || group.dir === ''
|
|
4264
|
+
? null
|
|
4265
|
+
: {
|
|
4266
|
+
key: 'exempt',
|
|
4267
|
+
label: exemptGroup ? t('group.exemptOff') : t('group.exemptOn'),
|
|
4268
|
+
icon: 'mute',
|
|
4269
|
+
onSelect: () => void toggleExemptWorkspace(group.dir, !exemptGroup),
|
|
4270
|
+
},
|
|
4271
|
+
].filter((item) => item !== null && item !== undefined),
|
|
4272
|
+
}),
|
|
4144
4273
|
),
|
|
4145
4274
|
isCollapsed
|
|
4146
4275
|
? null
|
package/lib/index.js
CHANGED
|
@@ -36,7 +36,7 @@ import { createStore, resolveStorePath } from './store.js'
|
|
|
36
36
|
import { createTools } from './tools.js'
|
|
37
37
|
|
|
38
38
|
/** Must equal `package.json`'s version; `npm run check` enforces that. */
|
|
39
|
-
export const VERSION = '0.6.
|
|
39
|
+
export const VERSION = '0.6.6'
|
|
40
40
|
|
|
41
41
|
/** Plugin id. Also the Loader row id in `cordis.patch.yml`. */
|
|
42
42
|
export const name = 'dsh-global-todo'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-global-todo",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"icon": "./docs/icon.svg",
|
|
5
5
|
"description": "Global, cross-session todos for DeepSeek Harness: one list owned by the human and the agent together, with creation time, attached documents and links, and one-click hand-off to an agent session. 全局待办:跨会话一份清单,记录加入时间,可挂文档与地址,可一键交给 Agent 继续干。",
|
|
6
6
|
"type": "module",
|