@shendeguize/dsh-agent-sidecar 0.1.0 → 0.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/README.md +78 -11
- package/lib/client.js +4708 -2932
- package/lib/client.js.map +1 -1
- package/package.json +3 -1
- package/src/client/analysis/AnalysisPanel.tsx +19 -27
- package/src/client/analysis/analysis.module.css +36 -97
- package/src/client/board/Board.tsx +255 -48
- package/src/client/board/board.module.css +113 -92
- package/src/client/board/logic.ts +99 -21
- package/src/client/board/project-view-logic.ts +27 -34
- package/src/client/board/project-view.module.css +46 -83
- package/src/client/board/project-view.tsx +50 -8
- package/src/client/board/strings.ts +70 -85
- package/src/client/commands.ts +30 -15
- package/src/client/controller.ts +27 -7
- package/src/client/detail/SessionDetail.tsx +234 -37
- package/src/client/detail/detail.module.css +100 -153
- package/src/client/detail/logic.ts +220 -29
- package/src/client/detail/strings.ts +66 -77
- package/src/client/detail-glue.ts +33 -0
- package/src/client/detail-view.module.css +43 -35
- package/src/client/detail-view.tsx +138 -118
- package/src/client/dsh-tools/LineageTree.tsx +22 -13
- package/src/client/dsh-tools/SearchPanel.tsx +18 -11
- package/src/client/dsh-tools/dsh-tools.module.css +53 -140
- package/src/client/dsh-tools/strings.ts +42 -77
- package/src/client/index.ts +285 -124
- package/src/client/inject/InjectPanel.tsx +81 -61
- package/src/client/inject/inject.module.css +97 -197
- package/src/client/inject/logic.ts +38 -0
- package/src/client/lifecycle/handoff.ts +83 -0
- package/src/client/locales/en.ts +91 -4
- package/src/client/locales/host.ts +131 -0
- package/src/client/locales/index.ts +196 -8
- package/src/client/locales/react.ts +10 -0
- package/src/client/locales/view.ts +38 -0
- package/src/client/locales/zh.ts +200 -125
- package/src/client/mount.tsx +75 -41
- package/src/client/navigation/CenterOverlay.tsx +40 -0
- package/src/client/navigation/center-overlay.module.css +51 -0
- package/src/client/navigation/center.ts +45 -0
- package/src/client/navigation/modal-isolation.ts +152 -0
- package/src/client/navigation/modal-surface-anchor.ts +72 -0
- package/src/client/navigation/sidebar-entry.module.css +73 -0
- package/src/client/navigation/sidebar-entry.ts +309 -0
- package/src/client/primitives/StaticPill.tsx +21 -0
- package/src/client/settings-card.module.css +35 -119
- package/src/client/settings-card.tsx +31 -153
- package/src/client/settings-fields.tsx +131 -0
- package/src/client/sidebar/SidebarTab.tsx +156 -0
- package/src/client/sidebar/model.ts +65 -0
- package/src/client/sidebar/sidebar-tab.module.css +118 -0
- package/src/client/sidebar-tab.tsx +46 -320
- package/src/client/theme/agsc.module.css +31 -0
- package/src/client/theme/parts.ts +56 -0
- package/src/client/ui-integration.ts +86 -0
- package/src/client/widget.tsx +42 -16
- package/src/client/inject/overlay.module.css +0 -22
|
@@ -1,40 +1,30 @@
|
|
|
1
|
-
/* Inject panel (design §5.1 view 3). Every color rides a dsh --dsw-alias-*
|
|
2
|
-
* design token, no literal colors — same discipline as the settings card.
|
|
3
|
-
* The confirm-injection button is the one danger affordance: eye-catching
|
|
4
|
-
* (error-toned border/text, filled on hover) but restrained (no permanent
|
|
5
|
-
* solid red block). */
|
|
6
|
-
|
|
7
1
|
.panel {
|
|
8
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
9
|
-
border-radius: 12px;
|
|
10
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
11
|
-
max-width: 560px;
|
|
12
2
|
display: flex;
|
|
13
3
|
flex-direction: column;
|
|
4
|
+
max-width: 560px;
|
|
5
|
+
border: 1px solid var(--agsc-border-strong);
|
|
6
|
+
border-radius: var(--agsc-radius-card);
|
|
7
|
+
background: var(--agsc-bg);
|
|
14
8
|
}
|
|
15
9
|
|
|
16
|
-
/* ── header ────────────────────────────────────────────────────────────── */
|
|
17
|
-
|
|
18
10
|
.header {
|
|
19
11
|
display: flex;
|
|
20
12
|
align-items: center;
|
|
21
13
|
gap: 12px;
|
|
22
14
|
padding: 14px 16px 10px;
|
|
23
|
-
border-bottom: 1px solid var(--
|
|
15
|
+
border-bottom: 1px solid var(--agsc-border-strong);
|
|
24
16
|
}
|
|
25
17
|
|
|
26
18
|
.title {
|
|
27
19
|
flex: 1;
|
|
28
20
|
min-width: 0;
|
|
29
21
|
margin: 0;
|
|
22
|
+
color: var(--agsc-fg);
|
|
30
23
|
font-size: 15px;
|
|
31
24
|
font-weight: 600;
|
|
32
25
|
line-height: 1.4;
|
|
33
|
-
color: var(--dsw-alias-label-primary);
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
/* ── body ──────────────────────────────────────────────────────────────── */
|
|
37
|
-
|
|
38
28
|
.body {
|
|
39
29
|
display: flex;
|
|
40
30
|
flex-direction: column;
|
|
@@ -42,117 +32,120 @@
|
|
|
42
32
|
padding: 12px 16px 14px;
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
.capabilityOff
|
|
46
|
-
margin: 0;
|
|
47
|
-
border-radius: 8px;
|
|
48
|
-
padding: 10px 12px;
|
|
49
|
-
font-size: 13px;
|
|
50
|
-
line-height: 1.6;
|
|
51
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
52
|
-
color: var(--dsw-alias-label-secondary);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* ── notices (editor banners after a failed/expired attempt) ───────────── */
|
|
56
|
-
|
|
35
|
+
.capabilityOff,
|
|
57
36
|
.noticeWarn,
|
|
58
|
-
.noticeError
|
|
37
|
+
.noticeError,
|
|
38
|
+
.warnBar,
|
|
39
|
+
.auditNote,
|
|
40
|
+
.resultOk,
|
|
41
|
+
.resultFail,
|
|
42
|
+
.resultUnknown {
|
|
59
43
|
margin: 0;
|
|
60
|
-
border-radius: 8px;
|
|
61
44
|
padding: 8px 10px;
|
|
45
|
+
border-radius: var(--agsc-radius-control);
|
|
46
|
+
background: var(--agsc-bg-raised);
|
|
47
|
+
color: var(--agsc-fg-secondary);
|
|
62
48
|
font-size: 12px;
|
|
63
49
|
line-height: 1.6;
|
|
64
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
65
|
-
color: var(--dsw-alias-label-secondary);
|
|
66
50
|
}
|
|
67
51
|
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
.noticeWarn,
|
|
53
|
+
.warnBar {
|
|
54
|
+
border: 1px solid var(--agsc-warn);
|
|
55
|
+
color: var(--agsc-warn);
|
|
71
56
|
}
|
|
72
57
|
|
|
73
|
-
.
|
|
74
|
-
|
|
58
|
+
.noticeError,
|
|
59
|
+
.resultFail {
|
|
60
|
+
border: 1px solid var(--agsc-err);
|
|
61
|
+
color: var(--agsc-err);
|
|
75
62
|
}
|
|
76
63
|
|
|
77
|
-
|
|
64
|
+
.noticeDetail,
|
|
65
|
+
.noTarget,
|
|
66
|
+
.modeHint,
|
|
67
|
+
.planKey,
|
|
68
|
+
.observedNote,
|
|
69
|
+
.resultDetail {
|
|
70
|
+
color: var(--agsc-fg-dimmed);
|
|
71
|
+
}
|
|
78
72
|
|
|
79
|
-
.targetRow
|
|
73
|
+
.targetRow,
|
|
74
|
+
.planRow {
|
|
80
75
|
display: flex;
|
|
81
76
|
align-items: baseline;
|
|
82
77
|
gap: 10px;
|
|
83
78
|
min-width: 0;
|
|
84
79
|
}
|
|
85
80
|
|
|
86
|
-
.noTarget
|
|
81
|
+
.noTarget,
|
|
82
|
+
.planKey,
|
|
83
|
+
.modeHint,
|
|
84
|
+
.resultDetail {
|
|
87
85
|
font-size: 12px;
|
|
88
86
|
line-height: 1.5;
|
|
89
|
-
color: var(--dsw-alias-label-tertiary);
|
|
90
87
|
}
|
|
91
88
|
|
|
92
89
|
.agentTag {
|
|
93
90
|
flex: none;
|
|
94
|
-
border-radius: 999px;
|
|
95
|
-
padding: 1px 8px;
|
|
96
|
-
font-size: 11px;
|
|
97
|
-
line-height: 17px;
|
|
98
|
-
font-weight: 500;
|
|
99
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
100
|
-
color: var(--dsw-alias-label-secondary);
|
|
101
91
|
}
|
|
102
92
|
|
|
103
93
|
.planTitle {
|
|
104
94
|
min-width: 0;
|
|
105
95
|
overflow: hidden;
|
|
106
|
-
|
|
107
|
-
white-space: nowrap;
|
|
96
|
+
color: var(--agsc-fg);
|
|
108
97
|
font-size: 13px;
|
|
109
98
|
line-height: 1.5;
|
|
110
|
-
|
|
99
|
+
text-overflow: ellipsis;
|
|
100
|
+
white-space: nowrap;
|
|
111
101
|
}
|
|
112
102
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
103
|
+
.field,
|
|
104
|
+
.modeText,
|
|
105
|
+
.planPreview {
|
|
116
106
|
display: flex;
|
|
117
107
|
flex-direction: column;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.field {
|
|
118
111
|
gap: 6px;
|
|
119
112
|
}
|
|
120
113
|
|
|
121
|
-
.label
|
|
114
|
+
.label,
|
|
115
|
+
.modeLabel {
|
|
116
|
+
color: var(--agsc-fg);
|
|
122
117
|
font-size: 13px;
|
|
123
118
|
line-height: 1.5;
|
|
124
|
-
color: var(--dsw-alias-label-primary);
|
|
125
119
|
}
|
|
126
120
|
|
|
127
121
|
.textarea {
|
|
128
122
|
appearance: none;
|
|
129
|
-
resize: vertical;
|
|
130
123
|
min-height: 96px;
|
|
131
|
-
|
|
132
|
-
border-radius: 8px;
|
|
133
|
-
background: var(--dsw-alias-bg-layer-3);
|
|
124
|
+
resize: vertical;
|
|
134
125
|
padding: 8px 10px;
|
|
126
|
+
border: 1px solid var(--agsc-border-strong);
|
|
127
|
+
border-radius: var(--agsc-radius-control);
|
|
128
|
+
background: var(--agsc-bg-raised);
|
|
129
|
+
color: var(--agsc-fg);
|
|
135
130
|
font: inherit;
|
|
136
131
|
font-size: 13px;
|
|
137
132
|
line-height: 1.6;
|
|
138
|
-
color: var(--dsw-alias-label-primary);
|
|
139
133
|
}
|
|
140
134
|
|
|
141
135
|
.textarea:focus-visible {
|
|
136
|
+
border-color: var(--agsc-accent);
|
|
142
137
|
outline: none;
|
|
143
|
-
border-color: var(--dsw-alias-brand-primary);
|
|
144
138
|
}
|
|
145
139
|
|
|
146
140
|
.textarea::placeholder {
|
|
147
|
-
color: var(--
|
|
141
|
+
color: var(--agsc-fg-dimmed);
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
.textarea:disabled {
|
|
151
|
-
opacity: 0.5;
|
|
152
145
|
cursor: default;
|
|
146
|
+
opacity: 0.5;
|
|
153
147
|
}
|
|
154
148
|
|
|
155
|
-
/* Byte counter: thin progress track + numeric readout. */
|
|
156
149
|
.byteRow {
|
|
157
150
|
display: flex;
|
|
158
151
|
align-items: center;
|
|
@@ -162,52 +155,49 @@
|
|
|
162
155
|
.byteBar {
|
|
163
156
|
flex: 1;
|
|
164
157
|
height: 3px;
|
|
165
|
-
border-radius: 2px;
|
|
166
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
167
158
|
overflow: hidden;
|
|
159
|
+
border-radius: 2px;
|
|
160
|
+
background: var(--agsc-border);
|
|
168
161
|
}
|
|
169
162
|
|
|
170
163
|
.byteFill {
|
|
171
164
|
height: 100%;
|
|
172
165
|
border-radius: 2px;
|
|
173
|
-
background: var(--
|
|
166
|
+
background: var(--agsc-accent);
|
|
174
167
|
transition: width .12s;
|
|
175
168
|
}
|
|
176
169
|
|
|
177
|
-
/* Modifier applied ALONGSIDE .byteFill (no `composes`; see settings card). */
|
|
178
170
|
.byteFillOver {
|
|
179
|
-
background: var(--
|
|
171
|
+
background: var(--agsc-err);
|
|
180
172
|
}
|
|
181
173
|
|
|
182
174
|
.byteText,
|
|
183
175
|
.byteTextOver {
|
|
184
176
|
flex: none;
|
|
177
|
+
color: var(--agsc-fg-dimmed);
|
|
185
178
|
font-size: 11px;
|
|
186
|
-
line-height: 1.5;
|
|
187
179
|
font-variant-numeric: tabular-nums;
|
|
188
|
-
|
|
180
|
+
line-height: 1.5;
|
|
189
181
|
}
|
|
190
182
|
|
|
191
|
-
.byteTextOver
|
|
192
|
-
|
|
183
|
+
.byteTextOver,
|
|
184
|
+
.invalid {
|
|
185
|
+
color: var(--agsc-err);
|
|
193
186
|
}
|
|
194
187
|
|
|
195
188
|
.invalid {
|
|
196
189
|
margin: 0;
|
|
197
190
|
font-size: 12px;
|
|
198
191
|
line-height: 1.5;
|
|
199
|
-
color: var(--dsw-alias-label-error);
|
|
200
192
|
}
|
|
201
193
|
|
|
202
|
-
/* ── mode radios ───────────────────────────────────────────────────────── */
|
|
203
|
-
|
|
204
194
|
.modes {
|
|
205
|
-
margin: 0;
|
|
206
|
-
border: 0;
|
|
207
|
-
padding: 0;
|
|
208
195
|
display: flex;
|
|
209
196
|
flex-direction: column;
|
|
210
197
|
gap: 8px;
|
|
198
|
+
margin: 0;
|
|
199
|
+
padding: 0;
|
|
200
|
+
border: 0;
|
|
211
201
|
}
|
|
212
202
|
|
|
213
203
|
.modeOption {
|
|
@@ -218,10 +208,10 @@
|
|
|
218
208
|
}
|
|
219
209
|
|
|
220
210
|
.modeOption input {
|
|
221
|
-
accent-color: var(--dsw-alias-brand-primary);
|
|
222
211
|
width: 14px;
|
|
223
212
|
height: 14px;
|
|
224
213
|
margin: 3px 0 0;
|
|
214
|
+
accent-color: var(--agsc-accent);
|
|
225
215
|
cursor: pointer;
|
|
226
216
|
}
|
|
227
217
|
|
|
@@ -230,71 +220,26 @@
|
|
|
230
220
|
}
|
|
231
221
|
|
|
232
222
|
.modeText {
|
|
233
|
-
display: flex;
|
|
234
|
-
flex-direction: column;
|
|
235
223
|
gap: 2px;
|
|
236
224
|
min-width: 0;
|
|
237
225
|
}
|
|
238
226
|
|
|
239
|
-
.modeLabel {
|
|
240
|
-
font-size: 13px;
|
|
241
|
-
line-height: 1.5;
|
|
242
|
-
color: var(--dsw-alias-label-primary);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.modeHint {
|
|
246
|
-
font-size: 12px;
|
|
247
|
-
line-height: 1.5;
|
|
248
|
-
color: var(--dsw-alias-label-tertiary);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/* ── warnings & audit note ─────────────────────────────────────────────── */
|
|
252
|
-
|
|
253
|
-
.warnBar {
|
|
254
|
-
margin: 0;
|
|
255
|
-
border: 1px solid var(--dsw-alias-label-error);
|
|
256
|
-
border-radius: 8px;
|
|
257
|
-
padding: 8px 10px;
|
|
258
|
-
font-size: 12px;
|
|
259
|
-
line-height: 1.6;
|
|
260
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
261
|
-
color: var(--dsw-alias-label-error);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
227
|
.auditNote {
|
|
265
|
-
margin: 0;
|
|
266
|
-
border-radius: 8px;
|
|
267
228
|
padding: 8px 10px;
|
|
268
|
-
font-size: 12px;
|
|
269
|
-
line-height: 1.6;
|
|
270
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
271
|
-
color: var(--dsw-alias-label-secondary);
|
|
272
229
|
}
|
|
273
230
|
|
|
274
|
-
/* ── confirm plan ──────────────────────────────────────────────────────── */
|
|
275
|
-
|
|
276
231
|
.planBox {
|
|
277
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
278
|
-
border-radius: 8px;
|
|
279
|
-
background: var(--dsw-alias-bg-layer-3);
|
|
280
|
-
padding: 10px 12px;
|
|
281
232
|
display: flex;
|
|
282
233
|
flex-direction: column;
|
|
283
234
|
gap: 6px;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
align-items: baseline;
|
|
289
|
-
gap: 10px;
|
|
290
|
-
min-width: 0;
|
|
235
|
+
padding: 10px 12px;
|
|
236
|
+
border: 1px solid var(--agsc-border-strong);
|
|
237
|
+
border-radius: var(--agsc-radius-control);
|
|
238
|
+
background: var(--agsc-bg-raised);
|
|
291
239
|
}
|
|
292
240
|
|
|
293
241
|
.planKey {
|
|
294
242
|
flex: none;
|
|
295
|
-
font-size: 12px;
|
|
296
|
-
line-height: 1.5;
|
|
297
|
-
color: var(--dsw-alias-label-tertiary);
|
|
298
243
|
}
|
|
299
244
|
|
|
300
245
|
.planValue {
|
|
@@ -302,85 +247,67 @@
|
|
|
302
247
|
align-items: baseline;
|
|
303
248
|
gap: 8px;
|
|
304
249
|
min-width: 0;
|
|
250
|
+
color: var(--agsc-fg);
|
|
305
251
|
font-size: 13px;
|
|
306
252
|
line-height: 1.5;
|
|
307
|
-
color: var(--dsw-alias-label-primary);
|
|
308
253
|
}
|
|
309
254
|
|
|
310
255
|
.observedNote {
|
|
311
256
|
margin: 0;
|
|
312
257
|
font-size: 11px;
|
|
313
258
|
line-height: 1.5;
|
|
314
|
-
color: var(--dsw-alias-label-tertiary);
|
|
315
259
|
}
|
|
316
260
|
|
|
317
261
|
.planPreview {
|
|
318
|
-
display: flex;
|
|
319
|
-
flex-direction: column;
|
|
320
262
|
gap: 4px;
|
|
321
263
|
min-width: 0;
|
|
322
264
|
}
|
|
323
265
|
|
|
324
266
|
.preview {
|
|
325
|
-
margin: 0;
|
|
326
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
327
|
-
border-radius: 6px;
|
|
328
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
329
|
-
padding: 8px 10px;
|
|
330
267
|
max-height: 120px;
|
|
331
268
|
overflow: auto;
|
|
269
|
+
margin: 0;
|
|
270
|
+
padding: 8px 10px;
|
|
271
|
+
border: 1px solid var(--agsc-border-strong);
|
|
272
|
+
border-radius: 6px;
|
|
273
|
+
background: var(--agsc-bg);
|
|
274
|
+
color: var(--agsc-fg-secondary);
|
|
332
275
|
font-size: 12px;
|
|
333
276
|
line-height: 1.6;
|
|
334
277
|
white-space: pre-wrap;
|
|
335
278
|
word-break: break-word;
|
|
336
|
-
color: var(--dsw-alias-label-secondary);
|
|
337
279
|
}
|
|
338
280
|
|
|
339
281
|
.countdown {
|
|
340
282
|
margin: 0;
|
|
283
|
+
color: var(--agsc-fg-secondary);
|
|
341
284
|
font-size: 12px;
|
|
342
|
-
line-height: 1.5;
|
|
343
285
|
font-variant-numeric: tabular-nums;
|
|
344
|
-
|
|
286
|
+
line-height: 1.5;
|
|
345
287
|
}
|
|
346
288
|
|
|
347
|
-
/* ── results ───────────────────────────────────────────────────────────── */
|
|
348
|
-
|
|
349
289
|
.resultOk,
|
|
350
290
|
.resultFail,
|
|
351
291
|
.resultUnknown {
|
|
352
|
-
margin: 0;
|
|
353
|
-
border-radius: 8px;
|
|
354
292
|
padding: 10px 12px;
|
|
293
|
+
color: var(--agsc-fg);
|
|
355
294
|
font-size: 13px;
|
|
356
|
-
line-height: 1.6;
|
|
357
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
358
|
-
color: var(--dsw-alias-label-primary);
|
|
359
295
|
}
|
|
360
296
|
|
|
361
297
|
.resultOk {
|
|
362
|
-
border: 1px solid var(--
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.resultFail {
|
|
366
|
-
border: 1px solid var(--dsw-alias-label-error);
|
|
367
|
-
color: var(--dsw-alias-label-error);
|
|
298
|
+
border: 1px solid var(--agsc-ok);
|
|
368
299
|
}
|
|
369
300
|
|
|
370
301
|
.resultUnknown {
|
|
371
|
-
border: 1px solid var(--
|
|
372
|
-
color: var(--
|
|
302
|
+
border: 1px solid var(--agsc-fg-dimmed);
|
|
303
|
+
color: var(--agsc-fg-secondary);
|
|
373
304
|
}
|
|
374
305
|
|
|
375
306
|
.resultDetail {
|
|
376
307
|
margin: 0;
|
|
377
|
-
font-size: 12px;
|
|
378
308
|
line-height: 1.6;
|
|
379
|
-
color: var(--dsw-alias-label-tertiary);
|
|
380
309
|
}
|
|
381
310
|
|
|
382
|
-
/* ── footer buttons ────────────────────────────────────────────────────── */
|
|
383
|
-
|
|
384
311
|
.footer {
|
|
385
312
|
display: flex;
|
|
386
313
|
align-items: center;
|
|
@@ -389,58 +316,31 @@
|
|
|
389
316
|
padding-top: 4px;
|
|
390
317
|
}
|
|
391
318
|
|
|
392
|
-
.btn,
|
|
393
|
-
.btnPrimary,
|
|
394
319
|
.btnDanger {
|
|
395
320
|
appearance: none;
|
|
396
|
-
border: 1px solid transparent;
|
|
397
|
-
border-radius: 8px;
|
|
398
321
|
padding: 5px 14px;
|
|
322
|
+
border: 1px solid var(--agsc-err);
|
|
323
|
+
border-radius: var(--agsc-radius-control);
|
|
324
|
+
background: transparent;
|
|
325
|
+
color: var(--agsc-err);
|
|
399
326
|
font: inherit;
|
|
400
327
|
font-size: 13px;
|
|
328
|
+
font-weight: 600;
|
|
401
329
|
line-height: 1.5;
|
|
402
330
|
cursor: pointer;
|
|
403
331
|
}
|
|
404
332
|
|
|
405
|
-
.btn {
|
|
406
|
-
border-color: var(--dsw-alias-border-l2);
|
|
407
|
-
background: none;
|
|
408
|
-
color: var(--dsw-alias-label-secondary);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
.btn:hover:not(:disabled) {
|
|
412
|
-
color: var(--dsw-alias-label-primary);
|
|
413
|
-
border-color: var(--dsw-alias-label-dimmed);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.btnPrimary {
|
|
417
|
-
background: var(--dsw-alias-label-primary);
|
|
418
|
-
color: var(--dsw-alias-bg-layer-3);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/* The danger action: outlined error tone at rest, fills on hover. */
|
|
422
|
-
.btnDanger {
|
|
423
|
-
border-color: var(--dsw-alias-label-error);
|
|
424
|
-
background: none;
|
|
425
|
-
color: var(--dsw-alias-label-error);
|
|
426
|
-
font-weight: 600;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
333
|
.btnDanger:hover:not(:disabled) {
|
|
430
|
-
background: var(--
|
|
431
|
-
color: var(--
|
|
334
|
+
background: var(--agsc-err);
|
|
335
|
+
color: var(--agsc-bg-raised);
|
|
432
336
|
}
|
|
433
337
|
|
|
434
|
-
.btn:disabled,
|
|
435
|
-
.btnPrimary:disabled,
|
|
436
338
|
.btnDanger:disabled {
|
|
437
|
-
opacity: 0.4;
|
|
438
339
|
cursor: default;
|
|
340
|
+
opacity: 0.4;
|
|
439
341
|
}
|
|
440
342
|
|
|
441
|
-
.btn:focus-visible,
|
|
442
|
-
.btnPrimary:focus-visible,
|
|
443
343
|
.btnDanger:focus-visible {
|
|
444
|
-
outline: 2px solid var(--
|
|
344
|
+
outline: 2px solid var(--agsc-accent);
|
|
445
345
|
outline-offset: 1px;
|
|
446
346
|
}
|
|
@@ -418,6 +418,33 @@ export function deriveEditorGate(input: {
|
|
|
418
418
|
return { canPrepare: true, block: null }
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
// ---------------------------------------------------------------------------
|
|
422
|
+
// Keyboard affordances (UX-10).
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
|
|
425
|
+
export type PanelKeyIntent = 'close' | 'prepare' | null
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Map a keydown to a panel intent: Escape closes in every phase;
|
|
429
|
+
* Cmd/Ctrl+Enter submits the PREPARE step only (editor phase with a valid
|
|
430
|
+
* message). The confirm phase deliberately binds nothing — executing the
|
|
431
|
+
* injection stays an explicit click (two-phase discipline, §5.3), so no
|
|
432
|
+
* keyboard path can shortcut the confirmation.
|
|
433
|
+
*/
|
|
434
|
+
export function classifyPanelKey(input: {
|
|
435
|
+
key: string
|
|
436
|
+
metaKey: boolean
|
|
437
|
+
ctrlKey: boolean
|
|
438
|
+
phase: InjectPhase
|
|
439
|
+
canPrepare: boolean
|
|
440
|
+
}): PanelKeyIntent {
|
|
441
|
+
if (input.key === 'Escape') return 'close'
|
|
442
|
+
if (input.key === 'Enter' && (input.metaKey || input.ctrlKey)) {
|
|
443
|
+
return input.phase === 'idle' && input.canPrepare ? 'prepare' : null
|
|
444
|
+
}
|
|
445
|
+
return null
|
|
446
|
+
}
|
|
447
|
+
|
|
421
448
|
// ---------------------------------------------------------------------------
|
|
422
449
|
// Result actions (S6: unknown is terminal).
|
|
423
450
|
// ---------------------------------------------------------------------------
|
|
@@ -437,6 +464,17 @@ export function resultActions(outcome: InjectOutcome): ResultActions {
|
|
|
437
464
|
}
|
|
438
465
|
}
|
|
439
466
|
|
|
467
|
+
/**
|
|
468
|
+
* True when an execute response reports the message actually reached the
|
|
469
|
+
* target (UX-05 observation loop): a delivered outcome — including an
|
|
470
|
+
* idempotent replay of one — never an error envelope. failed/unknown must
|
|
471
|
+
* NOT trigger observation aids: unknown is a locked terminal state (S6)
|
|
472
|
+
* and any follow-up activity could read as "retrying is fine".
|
|
473
|
+
*/
|
|
474
|
+
export function isDeliveredResult(value: InjectResultView | ApiErrorLike): boolean {
|
|
475
|
+
return !isApiErrorLike(value) && value.outcome === 'delivered'
|
|
476
|
+
}
|
|
477
|
+
|
|
440
478
|
// ---------------------------------------------------------------------------
|
|
441
479
|
// Copy mapping (keys live in the T2.3 locale table, inject.* domain).
|
|
442
480
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export type ResourceDisposer = () => void
|
|
2
|
+
|
|
3
|
+
/** Narrow clock port for deterministic handoff tests. */
|
|
4
|
+
export interface HandoffScheduler {
|
|
5
|
+
setTimeout(callback: () => void, delayMs: number): unknown
|
|
6
|
+
clearTimeout(handle: unknown): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface HandoffOptions {
|
|
10
|
+
isCollision(error: unknown): boolean
|
|
11
|
+
onError(error: unknown): void
|
|
12
|
+
onTimeout(): void
|
|
13
|
+
scheduler?: HandoffScheduler
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const DEFAULT_DELAYS_MS = [8, 16, 32, 64, 128, 256, 496] as const
|
|
17
|
+
|
|
18
|
+
const defaultScheduler: HandoffScheduler = {
|
|
19
|
+
setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
|
|
20
|
+
clearTimeout: (handle) => globalThis.clearTimeout(handle as ReturnType<typeof setTimeout>),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Acquire a registry resource now, or briefly wait for an overlapping old
|
|
25
|
+
* fiber to release it. The returned lease owns both waiting and acquisition.
|
|
26
|
+
*/
|
|
27
|
+
export function acquireWithHandoff(
|
|
28
|
+
register: () => ResourceDisposer | undefined,
|
|
29
|
+
options: HandoffOptions,
|
|
30
|
+
): ResourceDisposer {
|
|
31
|
+
const scheduler = options.scheduler ?? defaultScheduler
|
|
32
|
+
let stopped = false
|
|
33
|
+
let attempts = 0
|
|
34
|
+
let delayIndex = 0
|
|
35
|
+
let timer: unknown
|
|
36
|
+
let acquired: ResourceDisposer | undefined
|
|
37
|
+
|
|
38
|
+
const finishError = (error: unknown): void => {
|
|
39
|
+
stopped = true
|
|
40
|
+
options.onError(error)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const attempt = (): void => {
|
|
44
|
+
if (stopped) return
|
|
45
|
+
timer = undefined
|
|
46
|
+
attempts += 1
|
|
47
|
+
try {
|
|
48
|
+
acquired = register()
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (!options.isCollision(error)) {
|
|
51
|
+
finishError(error)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (acquired !== undefined) return
|
|
56
|
+
if (attempts >= 8 || delayIndex >= DEFAULT_DELAYS_MS.length) {
|
|
57
|
+
stopped = true
|
|
58
|
+
options.onTimeout()
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
const delay = DEFAULT_DELAYS_MS[delayIndex++]
|
|
62
|
+
try {
|
|
63
|
+
timer = scheduler.setTimeout(attempt, delay!)
|
|
64
|
+
} catch (error) {
|
|
65
|
+
finishError(error)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
attempt()
|
|
70
|
+
return () => {
|
|
71
|
+
if (stopped) return
|
|
72
|
+
stopped = true
|
|
73
|
+
if (timer !== undefined) scheduler.clearTimeout(timer)
|
|
74
|
+
const dispose = acquired
|
|
75
|
+
acquired = undefined
|
|
76
|
+
dispose?.()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Only known duplicate-registry diagnostics are eligible for handoff. */
|
|
81
|
+
export function isRegistrationCollision(error: unknown): boolean {
|
|
82
|
+
return error instanceof Error && /\b(?:duplicate|already registered)\b/i.test(error.message)
|
|
83
|
+
}
|