dsh-ntr 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,18 +48,34 @@ const STYLE = `
48
48
  --ntr-success: color-mix(in srgb, var(--dsw-alias-state-success-primary, #16a34a) 72%, var(--dsw-alias-label-primary, #111827));
49
49
  --ntr-accent: var(--dsw-alias-button-primary-fill, #2563eb);
50
50
  width: 100%; max-width: 960px; box-sizing: border-box; margin: 0 auto;
51
- padding: 20px; overflow: auto; font-size: var(--ntr-font); line-height: 1.55;
51
+ padding: 28px 32px; min-width: 0; container-type: inline-size; font-size: var(--ntr-font); line-height: 1.55;
52
52
  }
53
- .dwa-root button, .dwa-root input, .dwa-root select { font-family: inherit; }
53
+ .dwa-root [hidden] { display: none !important; }
54
+ .dwa-root button, .dwa-root input, .dwa-root select, .dwa-root textarea { font-family: inherit; }
55
+ .dwa-root input, .dwa-root textarea { min-width: 0; }
56
+ .dwa-tabs { display: flex; gap: 24px; flex-wrap: wrap; border-bottom: var(--ntr-line); margin-bottom: 24px; }
57
+ .dwa-tabs > button { font: inherit; color: var(--ntr-muted); background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 0 0 12px; min-height: 40px; white-space: nowrap; cursor: pointer; }
58
+ .dwa-tabs > button:hover { color: var(--dsw-alias-label-primary, #111827); }
59
+ .dwa-tabs > button[aria-pressed="true"] { color: var(--dsw-alias-label-primary, #111827); border-bottom-color: currentColor; font-weight: 550; }
60
+ .dwa-detail-tabs { margin: 4px 0 20px; gap: 20px; }
61
+ .dwa-detail-tabs > button { font-size: var(--ntr-font-sm); }
62
+ .dwa-row-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
63
+ .dwa-notice { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
64
+ .dwa-notice > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
65
+ .dwa-empty { text-align: center; padding: 40px 16px; color: var(--ntr-muted); }
66
+ .dwa-empty h3 { font-size: 16px; font-weight: 550; color: var(--dsw-alias-label-primary, #111827); margin: 0 0 8px; }
67
+ .dwa-empty .dwa-btn { margin-top: 20px; }
68
+ .dwa-health { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 24px; color: var(--ntr-muted); font-size: var(--ntr-font-sm); flex-wrap: wrap; }
69
+ .dwa-health-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
70
+ .dwa-health-status > span:last-child:not(:nth-child(2)) { padding-left: 12px; border-left: var(--ntr-line); }
71
+ .dwa-back { margin-bottom: 16px; }
54
72
  .dwa-root button:focus-visible, .dwa-root input:focus-visible, .dwa-root select:focus-visible {
55
73
  outline: 2px solid var(--ntr-accent); outline-offset: 2px;
56
74
  }
57
- .dwa-page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
75
+ .dwa-page-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 28px; }
58
76
  .dwa-page-heading { min-width: 0; }
59
- .dwa-page-title { margin: 0; font-size: 20px; line-height: 1.35; font-weight: 650; letter-spacing: -0.02em; }
77
+ .dwa-page-title { margin: 0; font-size: 20px; line-height: 1.35; font-weight: 550; letter-spacing: -0.02em; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
60
78
  .dwa-page-description { margin: 5px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-sm); }
61
- .dwa-page-status { display: flex; align-items: center; gap: 8px; margin-top: 10px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
62
- .dwa-page-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
63
79
  .dwa-btn {
64
80
  min-height: 32px; box-sizing: border-box; font: inherit; border-radius: var(--ntr-radius-sm);
65
81
  border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-1, #fff);
@@ -72,14 +88,16 @@ const STYLE = `
72
88
  .dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); border-color: transparent; font-weight: 550; }
73
89
  .dwa-btn-primary:hover { background: var(--ntr-accent); border-color: transparent; opacity: 0.88; }
74
90
  .dwa-btn-secondary { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
91
+ .dwa-btn-quiet { background: transparent; border-color: transparent; color: var(--ntr-muted); }
92
+ .dwa-btn-quiet:hover { border-color: transparent; color: var(--dsw-alias-label-primary, #111827); }
75
93
  .dwa-btn-danger { color: var(--ntr-danger); border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }
76
94
  .dwa-btn-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, #fee2e2); }
77
95
  .dwa-section { margin-bottom: 24px; }
78
96
  .dwa-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
79
- .dwa-section-title { font-size: 14px; font-weight: 650; margin: 0; display: flex; align-items: center; gap: 8px; }
97
+ .dwa-section-title { font-size: 13px; font-weight: 550; margin: 0; display: flex; align-items: center; gap: 8px; }
80
98
  .dwa-section-copy { margin: 3px 0 0; font-size: var(--ntr-font-xs); color: var(--ntr-muted); }
81
99
  .dwa-card { border: var(--ntr-line); border-radius: var(--ntr-radius); padding: 16px; background: var(--dsw-alias-bg-layer-1, #fff); font-size: var(--ntr-font); line-height: 1.55; }
82
- .dwa-card-warn { border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-tertiary, #fffbeb); }
100
+ .dwa-card-warn { border-color: color-mix(in srgb, var(--ntr-warn) 30%, var(--dsw-alias-border-l2, #e5e7eb)); background: var(--dsw-alias-state-warn-tertiary, #fffbeb); }
83
101
  .dwa-mode-banner { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 16px; align-items: start; margin-bottom: 24px; padding: 14px 16px; }
84
102
  .dwa-mode-title { display: flex; align-items: center; gap: 8px; font-weight: 650; }
85
103
  .dwa-mode-copy { grid-column: 1 / -1; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
@@ -88,26 +106,36 @@ const STYLE = `
88
106
  .dwa-badge-success { background: var(--dsw-alias-state-success-tertiary, #dcfce7); color: var(--ntr-success); }
89
107
  .dwa-badge-warn { background: var(--dsw-alias-state-warn-tertiary, #fef3c7); color: var(--ntr-warn); }
90
108
  .dwa-badge-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }
109
+ /* The update badge is a button (it expands the update card): strip the
110
+ browser's default button chrome, keep the badge look. */
111
+ .dwa-update-badge { border: 0; font-family: inherit; cursor: pointer; }
112
+ .dwa-update-badge:hover { opacity: 0.85; }
113
+ /* The version badge doubles as the update entry: same badge look, no chrome. */
114
+ .dwa-version-btn { border: 0; font-family: inherit; cursor: pointer; }
115
+ .dwa-version-btn:hover { opacity: 0.85; }
116
+ .dwa-version { font-family: var(--ds-font-family-code, monospace); }
117
+ /* The update card sits between the page header and the permission mirror. */
118
+ .dwa-update-card { margin-bottom: 16px; }
119
+ .dwa-update-summary { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-weight: 550; }
120
+ .dwa-update-copy { margin: 6px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
121
+ .dwa-update-message { margin: 6px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); white-space: pre-wrap; overflow-wrap: anywhere; }
122
+ .dwa-update-actions { margin-top: 10px; }
91
123
  .dwa-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
92
- .dwa-agent-list { display: flex; flex-direction: column; gap: 8px; }
93
- .dwa-agent-card { border: var(--ntr-line); border-radius: var(--ntr-radius); background: var(--dsw-alias-bg-layer-1, #fff); overflow: visible; transition: border-color 0.15s ease; }
94
- .dwa-agent-card.is-open { border-color: var(--dsw-alias-border-l1, #c7ccd1); }
95
- .dwa-agent-row { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 12px 14px; }
124
+ .dwa-agent-list { display: flex; flex-direction: column; border-top: var(--ntr-line); }
125
+ .dwa-agent-card { border-bottom: var(--ntr-line); min-width: 0; }
126
+ .dwa-agent-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; align-items: center; gap: 14px; padding: 16px 0; }
127
+ .dwa-agent-avatar { display: grid; place-items: center; width: 38px; height: 38px; border: var(--ntr-line); border-radius: 9px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font: 12px var(--ds-font-family-code, monospace); }
96
128
  .dwa-caret { font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; width: 1em; }
97
129
  .dwa-caret-btn { font: inherit; border: none; background: var(--dsw-alias-bg-layer-2, #f3f4f6); width: 28px; height: 28px; padding: 0; border-radius: var(--ntr-radius-sm); display: inline-flex; align-items: center; justify-content: center; color: var(--ntr-muted); cursor: pointer; }
98
130
  .dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); color: inherit; }
99
131
  .dwa-agent-summary { min-width: 0; }
100
132
  .dwa-agent-titleline { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
101
- .dwa-agent-name { font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
133
+ .dwa-agent-name { font-size: 13px; font-weight: 550; overflow-wrap: anywhere; }
102
134
  .dwa-agent-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 4px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
103
135
  .dwa-agent-meta-separator { opacity: 0.45; }
104
- .dwa-agent-body { border-top: var(--ntr-line); padding: 0 16px 16px; }
105
- .dwa-subsection { padding-top: 16px; }
106
- .dwa-subsection + .dwa-subsection { margin-top: 16px; border-top: var(--ntr-line); }
107
- .dwa-subsection-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
108
- .dwa-subsection-title { margin: 0; font-size: 13px; font-weight: 650; }
109
- .dwa-subsection-copy { margin: 3px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
110
- .dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: flex-start; }
136
+ .dwa-agent-body { padding: 0 0 24px 52px; }
137
+ .dwa-subsection { min-width: 0; }
138
+ .dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: stretch; }
111
139
  .dwa-form-col { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
112
140
  .dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-label-primary, #374151); }
113
141
  .dwa-hint { font-size: var(--ntr-font-xs); line-height: 1.55; color: var(--ntr-muted); }
@@ -116,11 +144,13 @@ const STYLE = `
116
144
  .dwa-select { min-height: 32px; font: inherit; font-size: var(--ntr-font-sm); padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; }
117
145
  .dwa-input:hover, .dwa-select:hover { border-color: var(--dsw-alias-border-l1, #9ca3af); }
118
146
  .dwa-input:focus, .dwa-select:focus { border-color: var(--ntr-accent); }
119
- .dwa-input-mono, .dwa-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
147
+ .dwa-input-mono, .dwa-mono { font-family: var(--ds-font-family-code, ui-monospace), SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
120
148
  .dwa-stderr { max-height: 120px; overflow: auto; font-family: monospace; font-size: var(--ntr-font-xs); white-space: pre-wrap; color: var(--ntr-warn); margin: 12px 0 0; }
121
149
  .dwa-default-hint { background: var(--dsw-alias-bg-layer-2, #f9fafb); border: var(--ntr-line); border-radius: var(--ntr-radius-sm); padding: 10px 12px; font-size: var(--ntr-font-sm); display: flex; align-items: center; justify-content: space-between; margin: 12px 0 14px; gap: 12px; }
122
150
  .dwa-form-actions { display: flex; align-items: center; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: var(--ntr-line); flex-wrap: wrap; }
123
151
  .dwa-form-actions .dwa-btn-primary { min-width: 88px; }
152
+ .dwa-form-actions > .dwa-hint:first-child { margin-right: auto; }
153
+ .dwa-form-actions > .dwa-hint:last-child:not(:first-child) { width: 100%; }
124
154
  .dwa-menu-wrap { position: relative; }
125
155
  .dwa-menu-wrap > .dwa-btn { width: 32px; padding: 0; }
126
156
  .dwa-menu { position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 190px; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); padding: 4px; display: flex; flex-direction: column; }
@@ -128,10 +158,10 @@ const STYLE = `
128
158
  .dwa-menu-item:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
129
159
  .dwa-menu-item-danger { color: var(--ntr-danger); }
130
160
  .dwa-menu-confirm { display: flex; align-items: center; gap: 4px; padding: 4px; }
131
- .dwa-add-panel { border: var(--ntr-line); border-radius: var(--ntr-radius); background: var(--dsw-alias-bg-layer-1, #fff); }
132
- .dwa-add-toggle { width: 100%; border: 0; background: transparent; color: inherit; padding: 13px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; text-align: left; cursor: pointer; border-radius: var(--ntr-radius); }
133
- .dwa-add-toggle:hover { background: var(--dsw-alias-interactive-bg-active, #f9fafb); }
134
- .dwa-add-content { border-top: var(--ntr-line); padding: 14px 16px 16px; }
161
+ .dwa-add-panel .dwa-agent-row { display: none; }
162
+ .dwa-add-panel .dwa-agent-body { padding: 0 0 24px; }
163
+ .dwa-add-panel .dwa-agent-card { border: 0; }
164
+ .dwa-add-content { padding-top: 10px; }
135
165
  .dwa-chip { min-height: 30px; font: inherit; font-size: var(--ntr-font-sm); border: var(--ntr-line); border-radius: 999px; padding: 4px 12px; background: var(--dsw-alias-bg-layer-1, #fff); cursor: pointer; color: inherit; }
136
166
  .dwa-chip:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
137
167
  .dwa-chip-active { border-color: var(--ntr-accent); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); font-weight: 550; }
@@ -163,13 +193,13 @@ const STYLE = `
163
193
  .dwa-model-status { margin-left: auto; white-space: nowrap; }
164
194
  .dwa-model-search { width: 200px; min-height: 30px; }
165
195
  .dwa-model-empty { padding: 6px; }
166
- .dwa-model-row { display: flex; align-items: center; gap: 6px; width: 100%; border: none; background: none; padding: 5px 6px; border-radius: 5px; cursor: pointer; color: inherit; font: inherit; font-size: var(--ntr-font-sm); text-align: left; }
196
+ .dwa-model-row { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 38px; border: none; background: none; padding: 7px 8px; border-radius: 5px; cursor: pointer; color: inherit; font: inherit; font-size: var(--ntr-font-sm); text-align: left; }
167
197
  .dwa-model-row:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
168
198
  .dwa-model-row.is-on { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); }
169
199
  .dwa-model-row-mark { width: 12px; flex: none; text-align: center; opacity: 0.75; }
170
200
  .dwa-model-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
171
201
  /* Name and credit keep their room; the id is the only cell that collapses. */
172
- .dwa-model-row-desc { flex: 0 0 auto; max-width: 45%; opacity: 0.75; font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
202
+ .dwa-model-row-desc { flex: 0 0 auto; max-width: 45%; opacity: 0.75; font-size: var(--ntr-font-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
173
203
  /* The trailing 14px is reserved for the fade mask: while the id fits, the
174
204
  gradient only covers empty padding; once it overflows, its tail dissolves
175
205
  instead of ending in a hard ellipsis. armModelIdMarquee reads the same
@@ -183,12 +213,12 @@ const STYLE = `
183
213
  }
184
214
  .dwa-model-row-remove { flex: none; opacity: 0.5; padding: 0 2px; }
185
215
  .dwa-model-row:hover .dwa-model-row-remove { opacity: 1; }
186
- .dwa-seg { display: inline-flex; border-radius: var(--ntr-radius-sm); border: 1px solid var(--dsw-alias-border-l2, #d1d5db); overflow: hidden; vertical-align: middle; }
187
- .dwa-seg > button { min-height: 30px; font: inherit; font-size: var(--ntr-font-sm); color: inherit; border: none; background: var(--dsw-alias-bg-layer-1, #fff); padding: 4px 14px; cursor: pointer; }
188
- .dwa-seg > button + button { border-left: 1px solid var(--dsw-alias-border-l2, #d1d5db); }
216
+ .dwa-seg { display: inline-flex; gap: 4px; padding: 4px; box-sizing: border-box; border-radius: var(--ntr-radius-sm); border: var(--ntr-line); background: var(--dsw-alias-bg-layer-2, #f3f4f6); vertical-align: middle; }
217
+ .dwa-seg > button { min-height: 32px; font: inherit; font-size: var(--ntr-font-sm); color: var(--ntr-muted); border: 1px solid transparent; border-radius: 4px; background: transparent; padding: 4px 12px; cursor: pointer; white-space: nowrap; }
189
218
  .dwa-seg > button:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
190
- .dwa-seg > button.is-on { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); font-weight: 550; }
191
- .dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; }
219
+ .dwa-seg > button.is-on { background: var(--dsw-alias-bg-layer-1, #fff); border: var(--ntr-line); color: var(--dsw-alias-label-primary, #111827); font-weight: 550; }
220
+ .dwa-preferences { border-top: var(--ntr-line); }
221
+ .dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 0; }
192
222
  .dwa-preference-row + .dwa-preference-row { border-top: var(--ntr-line); }
193
223
  /* A control that needs the whole card: the label sits above it, not beside
194
224
  it. A right-aligned segmented control squeezed its labels into two lines
@@ -213,13 +243,20 @@ const STYLE = `
213
243
  .dwa-deadline-warn { margin-top: 10px; padding: 9px 12px; border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-secondary, #fffbeb); border-radius: var(--ntr-radius-sm); font-size: var(--ntr-font-xs); line-height: 1.55; }
214
244
  .dwa-deadline-reset { display: flex; justify-content: flex-end; margin-top: 8px; }
215
245
  @media (max-width: 640px) {
216
- .dwa-root { padding: 14px; }
217
- .dwa-page-header { flex-direction: column; }
218
- .dwa-page-actions { width: 100%; }
219
- .dwa-page-actions .dwa-btn { width: 100%; }
246
+ .dwa-root { padding: 20px 16px; }
247
+ }
248
+ @container (max-width: 640px) {
249
+ .dwa-page-header { align-items: flex-start; flex-wrap: wrap; }
250
+ .dwa-agent-body { padding-left: 0; }
251
+ .dwa-agent-row { grid-template-columns: 32px minmax(0, 1fr) auto; gap: 10px; }
252
+ .dwa-agent-avatar { width: 32px; height: 32px; }
253
+ .dwa-section-heading { flex-wrap: wrap; }
254
+ .dwa-tabs { gap: 20px; }
255
+ .dwa-notice { flex-wrap: wrap; }
256
+ .dwa-seg-wide > button { padding-inline: 5px; }
220
257
  .dwa-model-toolbar-actions { width: 100%; }
221
258
  .dwa-model-search { flex: 1 1 auto; width: auto; }
222
- .dwa-subsection-head, .dwa-preference-row { align-items: flex-start; flex-direction: column; }
259
+ .dwa-preference-row { align-items: flex-start; flex-direction: column; }
223
260
  .dwa-default-hint { align-items: flex-start; flex-direction: column; }
224
261
  /* The slider's scale: up to eight rungs with full unit words ("15 分钟")
225
262
  need a smaller face before they can collide on a narrow panel. The rung
@@ -687,46 +724,156 @@ function ApprovalDeadlinesCard(props) {
687
724
  // In `wait` mode there is no deadline at all, so the slider shows the one
688
725
  // thing that is true: no deadline.
689
726
  const waitMode = deadlines.mode === 'wait';
690
- return (_jsxs("section", { className: "dwa-section", children: [_jsx("div", { className: "dwa-section-heading", children: _jsxs("div", { children: [_jsx("h3", { className: "dwa-section-title", children: acp.label('审批时限', 'Approval deadlines') }), _jsx("p", { className: "dwa-section-copy", children: acp.label('权限请求的等待时长与超时处理方式。', 'How long to wait for a permission request, and what happens when nobody answers.') })] }) }), _jsxs("div", { className: "dwa-card", style: { paddingTop: 4, paddingBottom: 4 }, children: [_jsx(DeadlineModeBlock, { acp: acp, mode: deadlines.mode, override: modeNote }), _jsx(DeadlineSliderBlock, { acp: acp, value: waitMode ? undefined : deadlines.permissionTimeoutMs, mode: deadlines.mode, override: baselineNote }), facts.overridden && (_jsx("div", { className: "dwa-deadline-reset", children: _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: () => void acp.resetApprovalDeadlines(), children: acp.label('恢复配置文件的值', 'Use config file value') }) }))] })] }));
727
+ return (_jsxs("section", { className: "dwa-section", children: [_jsx("div", { className: "dwa-section-heading", children: _jsxs("div", { children: [_jsx("h3", { className: "dwa-section-title", children: acp.label('审批时限', 'Approval deadlines') }), _jsx("p", { className: "dwa-section-copy", children: acp.label('权限请求的等待时长与超时处理方式。', 'How long to wait for a permission request, and what happens when nobody answers.') })] }) }), _jsxs("div", { className: "dwa-preferences", children: [_jsx(DeadlineModeBlock, { acp: acp, mode: deadlines.mode, override: modeNote }), _jsx(DeadlineSliderBlock, { acp: acp, value: waitMode ? undefined : deadlines.permissionTimeoutMs, mode: deadlines.mode, override: baselineNote }), facts.overridden && (_jsx("div", { className: "dwa-deadline-reset", children: _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: () => void acp.resetApprovalDeadlines(), children: acp.label('恢复配置文件的值', 'Use config file value') }) }))] })] }));
728
+ }
729
+ /** The states that carry an update badge next to the version badge. */
730
+ function updateBadgeText(update, acp) {
731
+ if (update.status === 'available') {
732
+ return acp.label(`可更新 v${update.latest ?? update.current}`, `Update v${update.latest ?? update.current}`);
733
+ }
734
+ if (update.status === 'applying')
735
+ return acp.label('更新中…', 'Updating…');
736
+ if (update.status === 'applied') {
737
+ const installed = update.installed ?? update.latest ?? update.current;
738
+ return acp.label(`已更新 v${installed}`, `Updated v${installed}`);
739
+ }
740
+ if (update.status === 'restart-required') {
741
+ // Only a KNOWN disk version can be named. An interrupted install leaves
742
+ // `installed` unset, and a badge would have to invent a version.
743
+ if (update.installed === undefined)
744
+ return undefined;
745
+ // Neutral: the disk version can be newer OR older than the running one.
746
+ return acp.label(`待重启 v${update.installed}`, `Restart for v${update.installed}`);
747
+ }
748
+ return undefined;
749
+ }
750
+ /**
751
+ * The version badge's update companion. Every badge is a button, `applying`
752
+ * included: the card is where the install's outcome is read, so the running
753
+ * install must be able to open it too. States without anything to act on get
754
+ * no badge at all — the version badge itself still opens the card.
755
+ */
756
+ function PluginUpdateBadge(props) {
757
+ const { acp, update, open, onToggle } = props;
758
+ const text = updateBadgeText(update, acp);
759
+ if (text === undefined)
760
+ return null;
761
+ return (_jsx("button", { type: "button", className: "dwa-badge dwa-badge-info dwa-update-badge", "aria-expanded": open, "aria-controls": "ntr-plugin-update-card", onClick: onToggle, children: text }));
762
+ }
763
+ /**
764
+ * The update card. In-page, no dialog: the settings page already owns the one
765
+ * action (install the exact version the host reported), and a modal on top of
766
+ * a settings section would hide the version it is updating.
767
+ *
768
+ * The copy stays to one line per state (constraint 33); the host's raw error
769
+ * (`message`) is the only long text, and it wraps on its own line so a stack
770
+ * trace or a registry error is readable without stretching the card.
771
+ */
772
+ function PluginUpdateCard(props) {
773
+ const { acp, update, onClose } = props;
774
+ const applying = update.status === 'applying';
775
+ const latest = update.latest ?? update.current;
776
+ const onDisk = update.installed !== undefined && update.installed !== update.current
777
+ ? update.installed
778
+ : undefined;
779
+ const published = update.publishedAt !== undefined && !Number.isNaN(new Date(update.publishedAt).getTime())
780
+ ? new Date(update.publishedAt).toLocaleDateString()
781
+ : undefined;
782
+ const copy = update.status === 'available'
783
+ ? acp.label('更新会卸载并重装插件,之后可能需要重启 DSH。', 'Updating uninstalls and reinstalls the plugin, and may require restarting DSH.')
784
+ : update.status === 'applied'
785
+ ? acp.label('已应用,刷新页面即可使用新版本。', 'Applied. Reload the page to use the new version.')
786
+ : update.status === 'restart-required'
787
+ ? update.installed !== undefined
788
+ ? acp.label(`磁盘已是 v${update.installed},重启 DSH 后生效(桌面 App 退出重开;命令行重启 dsh web)。`, `On disk v${update.installed}. Restart DSH to apply (quit and reopen the app, or restart dsh web).`)
789
+ : acp.label('更新可能已完成,请刷新页面确认当前版本。', 'The update may already be complete. Refresh the page to confirm the current version.')
790
+ : update.status === 'busy'
791
+ ? acp.label('有进行中的对话,结束后才能更新。', 'Finish the running conversation before updating.')
792
+ : update.status === 'failed'
793
+ ? acp.label('更新失败。', 'Update failed.')
794
+ : update.status === 'current'
795
+ ? acp.label('已是最新版本。', 'Up to date.')
796
+ : update.status === 'source-install'
797
+ ? acp.label('源码或本地安装,无法通过 registry 更新。', 'Source or local install; update it through its own checkout.')
798
+ : update.status === 'managed'
799
+ ? acp.label('此安装由宿主管理,不能在这里更新。', 'This install is managed by the host and cannot be updated here.')
800
+ : undefined;
801
+ const showMessage = (update.status === 'failed' || update.status === 'busy' || update.status === 'managed')
802
+ && update.message !== undefined;
803
+ const apply = async () => {
804
+ if (update.latest === undefined)
805
+ return;
806
+ try {
807
+ await acp.applyPluginUpdate(update.latest);
808
+ }
809
+ catch {
810
+ // The store keeps the "disk may have changed" state and writes its own notice.
811
+ }
812
+ };
813
+ return (_jsxs("div", { id: "ntr-plugin-update-card", className: "dwa-card dwa-update-card", role: "group", "aria-label": acp.label('插件更新', 'Plugin update'), children: [_jsxs("div", { className: "dwa-update-summary", children: [_jsx("span", { children: acp.label(`当前 v${update.current}`, `Current v${update.current}`) }), onDisk !== undefined && _jsx("span", { className: "dwa-hint", children: acp.label(`磁盘 v${onDisk}`, `On disk v${onDisk}`) }), update.latest !== undefined && (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: "\u2192" }), _jsx("span", { children: acp.label(`最新 v${latest}`, `Latest v${latest}`) }), published !== undefined && _jsx("span", { className: "dwa-hint", children: published })] }))] }), copy !== undefined && _jsx("p", { className: "dwa-update-copy", children: copy }), showMessage && _jsx("div", { className: "dwa-update-message", children: update.message }), _jsxs("div", { className: "dwa-row-actions dwa-update-actions", children: [update.status === 'available' && (_jsx("button", { type: "button", className: "dwa-btn dwa-btn-primary", onClick: () => { void apply(); }, children: acp.label('更新', 'Update') })), applying && (_jsx("button", { type: "button", className: "dwa-btn dwa-btn-primary", disabled: true, children: acp.label('正在更新…', 'Updating…') })), update.status !== 'applying' && (_jsx("button", { type: "button", className: "dwa-btn", onClick: onClose, children: acp.label('关闭', 'Close') })), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-quiet", disabled: applying, onClick: () => void acp.checkPluginUpdate(true), children: acp.label('重新检查', 'Check again') })] })] }));
814
+ }
815
+ /**
816
+ * The card is drawn for every state a host can report about this install,
817
+ * except the two where there is no install to talk about at all.
818
+ */
819
+ function updateCardVisible(update, open) {
820
+ if (update === undefined || !open)
821
+ return false;
822
+ return update.status !== 'unsupported' && update.status !== 'not-installed';
691
823
  }
692
- /** Settings configuration section for ACP agents. */
693
824
  export function AcpSettingsSection(props) {
694
825
  const acp = props.acp;
695
826
  ensureStyle();
696
827
  const ui = useUi(acp);
697
828
  const ledger = permissionLedgerCard(ui);
698
- // The agent list section opens by default. `addTarget === undefined` means
699
- // "follow the first available preset"; an explicit chip click pins the
700
- // selection until the next create resets it.
701
- const [addOpen, setAddOpen] = useState(true);
829
+ const [page, setPage] = useState('agents');
830
+ const [addOpen, setAddOpen] = useState(false);
702
831
  const [addTarget, setAddTarget] = useState(undefined);
703
- const [createNonce, setCreateNonce] = useState(0);
832
+ const [updateOpen, setUpdateOpen] = useState(false);
833
+ // The store throttles this to one registry read per ten minutes, so every
834
+ // remount (both settings entries mount this section) is cheap.
835
+ useEffect(() => {
836
+ void acp.checkPluginUpdate();
837
+ }, [acp]);
704
838
  const { configured, available } = partitionAgents(ui.detectedAgents, ui.agents);
705
839
  const selected = addTarget ?? available[0] ?? NEW_AGENT_SEED;
706
- return (_jsxs("div", { className: "dwa-root", children: [_jsxs("header", { className: "dwa-page-header", children: [_jsxs("div", { className: "dwa-page-heading", children: [_jsx("h2", { className: "dwa-page-title", children: acp.label('NTR 智能体', 'NTR Agents') }), _jsx("p", { className: "dwa-page-description", children: acp.label('管理智能体连接、可用模型与对话显示偏好。', 'Manage agent connections, available models, and conversation display preferences.') }), _jsxs("div", { className: "dwa-page-status", children: [_jsx("span", { className: "dwa-dot", style: { background: ui.stream === 'live' ? 'var(--dsw-alias-state-success-primary, #22c55e)' : ui.stream === 'down' ? 'var(--dsw-alias-state-error-primary, #ef4444)' : 'var(--dsw-alias-state-idle-primary, #9ca3af)' }, title: ui.stream }), _jsx("span", { children: streamText(ui.stream, acp) }), ui.detectSummary !== undefined && _jsxs("span", { children: ["\u00B7 ", ui.detectSummary] })] })] }), _jsx("div", { className: "dwa-page-actions", children: _jsx("button", { className: "dwa-btn", onClick: () => {
707
- void acp.resync();
708
- void acp.detectAgents();
709
- }, disabled: ui.detecting, children: ui.detecting ? acp.label('检测中...', 'Detecting...') : acp.label('重新检测与刷新', 'Re-detect & Refresh') }) })] }), ledger.visible && (_jsxs("div", { className: "dwa-card dwa-card-warn", style: { marginBottom: 16 }, children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: acp.label('待处理权限请求', 'Pending Permission Requests') }), _jsx("div", { className: "dwa-hint", style: { marginBottom: 8 }, children: acp.label('对话打开时在那边的卡片上回答;这里兜底显示其他会话的询问。', 'Answer in the conversation card while that conversation is open; this list covers the asks whose conversation is not.') }), ui.permissions.map(permission => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, flexWrap: 'wrap' }, children: [_jsxs("span", { style: { flex: 1, minWidth: 160 }, children: [_jsx("span", { style: { fontWeight: 600 }, children: permission.toolName }), permission.reason !== undefined && (_jsxs("span", { style: { color: 'var(--ntr-muted)' }, children: [" \u00B7 ", permission.reason] }))] }), permission.options.map(option => (_jsx("button", { className: "dwa-btn dwa-btn-primary", onClick: () => { void acp.respondPermission(permission.requestId, option.optionId).catch(() => undefined); }, children: option.name }, option.optionId))), _jsx("button", { className: "dwa-btn", onClick: () => { void acp.respondPermission(permission.requestId).catch(() => undefined); }, children: acp.label('拒绝', 'Dismiss') })] }, permission.requestId)))] })), ui.notice !== undefined && (_jsxs("div", { className: "dwa-card dwa-card-warn", style: { marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { flex: 1 }, children: ui.notice }), _jsx("button", { className: "dwa-btn", onClick: () => acp.clearNotice(), children: "\u2715" })] })), ui.preset !== undefined && (_jsxs("div", { className: "dwa-card dwa-mode-banner", children: [_jsxs("div", { className: "dwa-mode-title", children: [acp.label('NTR 模式', 'NTR mode'), _jsx("span", { className: "dwa-badge", children: ui.preset.id })] }), _jsx("span", { className: `dwa-badge ${ui.preset.installed ? 'dwa-badge-success' : 'dwa-badge-warn'}`, children: ui.preset.installed ? acp.label('已就绪', 'Ready') : acp.label('需检查', 'Needs attention') }), _jsxs("div", { className: "dwa-mode-copy", children: [ui.preset.installed
840
+ const openAdd = () => {
841
+ setAddOpen(true);
842
+ setPage('add');
843
+ };
844
+ const presetProblem = ui.preset !== undefined && (!ui.preset.installed || ui.preset.problem !== undefined);
845
+ const update = ui.pluginUpdate;
846
+ // A source install cannot be replaced by this browser; the version badge
847
+ // says so on hover. Neutral wording: a checkout may be git or not.
848
+ const versionTitle = update?.status === 'source-install'
849
+ ? acp.label('源码或本地安装,无法通过 registry 更新。', 'Source or local install; update it through its own checkout.')
850
+ : undefined;
851
+ // The running version is the update entry: every state with a real install
852
+ // report can open the card, including "up to date" (which offers a re-check).
853
+ const versionOpensUpdate = update !== undefined
854
+ && update.status !== 'unsupported'
855
+ && update.status !== 'not-installed';
856
+ return (_jsxs("div", { className: "dwa-root", children: [_jsxs("header", { className: "dwa-page-header", children: [_jsxs("div", { className: "dwa-page-heading", children: [_jsxs("h2", { className: "dwa-page-title", "aria-label": acp.label('NTR 设置', 'NTR settings'), children: [acp.label('NTR 设置', 'NTR settings'), ui.pluginVersion !== undefined && (versionOpensUpdate
857
+ ? (_jsx("button", { type: "button", className: "dwa-badge dwa-version dwa-version-btn", title: versionTitle, "aria-expanded": updateOpen, "aria-controls": "ntr-plugin-update-card", onClick: () => setUpdateOpen(open => !open), children: `v${ui.pluginVersion}` }))
858
+ : _jsx("span", { className: "dwa-badge dwa-version", title: versionTitle, children: `v${ui.pluginVersion}` })), update !== undefined && (_jsx(PluginUpdateBadge, { acp: acp, update: update, open: updateOpen, onToggle: () => setUpdateOpen(open => !open) }))] }), _jsx("p", { className: "dwa-page-description", children: acp.label('连接智能体,设置你的对话偏好。', 'Connect agents and set your conversation preferences.') })] }), page === 'agents' && configured.length > 0 && (_jsx("button", { type: "button", className: "dwa-btn dwa-btn-primary", onClick: openAdd, children: acp.label('添加智能体', 'Add agent') }))] }), update !== undefined && updateCardVisible(update, updateOpen) && (_jsx(PluginUpdateCard, { acp: acp, update: update, onClose: () => setUpdateOpen(false) })), ledger.visible && (_jsxs("div", { className: "dwa-card dwa-card-warn", style: { marginBottom: 16 }, children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: acp.label('待处理权限请求', 'Pending Permission Requests') }), _jsx("div", { className: "dwa-hint", style: { marginBottom: 8 }, children: acp.label('对话打开时在那边的卡片上回答;这里兜底显示其他会话的询问。', 'Answer in the conversation card while that conversation is open; this list covers the asks whose conversation is not.') }), ui.permissions.map(permission => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, flexWrap: 'wrap' }, children: [_jsxs("span", { style: { flex: 1, minWidth: 160 }, children: [_jsx("span", { style: { fontWeight: 600 }, children: permission.toolName }), permission.reason !== undefined && (_jsxs("span", { style: { color: 'var(--ntr-muted)' }, children: [" \u00B7 ", permission.reason] }))] }), permission.options.map(option => (_jsx("button", { type: "button", className: "dwa-btn", onClick: () => { void acp.respondPermission(permission.requestId, option.optionId).catch(() => undefined); }, children: option.name }, option.optionId))), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-quiet", onClick: () => { void acp.respondPermission(permission.requestId).catch(() => undefined); }, children: acp.label('取消', 'Cancel') })] }, permission.requestId)))] })), ui.notice !== undefined && (_jsxs("div", { className: "dwa-card dwa-card-warn", style: { marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { flex: 1 }, children: ui.notice }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-quiet", "aria-label": acp.label('关闭通知', 'Dismiss notice'), onClick: () => acp.clearNotice(), children: "\u2715" })] })), presetProblem && (_jsxs("div", { className: "dwa-card dwa-card-warn dwa-notice", role: "status", children: [_jsx("span", { children: acp.label('NTR 模式需要检查,当前可能无法开始对话。', 'NTR mode needs attention; new conversations may be unavailable.') }), _jsx("button", { type: "button", className: "dwa-btn", onClick: () => setPage('diagnostics'), children: acp.label('查看诊断', 'View diagnostics') })] })), _jsxs("nav", { className: "dwa-tabs", "aria-label": acp.label('NTR 设置分类', 'NTR settings categories'), children: [_jsx("button", { type: "button", "aria-pressed": page === 'agents', onClick: () => setPage('agents'), children: acp.label('智能体', 'Agents') }), ui.approvalDeadlines !== undefined && _jsx("button", { type: "button", "aria-pressed": page === 'approvals', onClick: () => setPage('approvals'), children: acp.label('审批时限', 'Approval deadlines') }), _jsx("button", { type: "button", "aria-pressed": page === 'appearance', onClick: () => setPage('appearance'), children: acp.label('对话外观', 'Appearance') })] }), page === 'diagnostics' && (_jsxs("section", { className: "dwa-section", children: [_jsx("h3", { className: "dwa-section-title", children: acp.label('连接诊断', 'Connection diagnostics') }), _jsxs("div", { className: "dwa-preference-row", children: [_jsx("span", { children: acp.label('宿主事件流', 'Host event stream') }), _jsx("span", { className: "dwa-hint", children: streamText(ui.stream, acp) })] }), _jsxs("div", { className: "dwa-row-actions", children: [_jsx("button", { type: "button", className: "dwa-btn", onClick: () => { void acp.resync(); void acp.detectAgents(); }, disabled: ui.detecting, children: ui.detecting ? acp.label('检测中...', 'Detecting...') : acp.label('重新检测与刷新', 'Re-detect & Refresh') }), ui.detectSummary !== undefined && _jsx("span", { className: "dwa-hint", role: "status", children: ui.detectSummary })] })] })), page === 'diagnostics' && ui.preset !== undefined && (_jsxs("div", { className: "dwa-card dwa-mode-banner", children: [_jsxs("div", { className: "dwa-mode-title", children: [acp.label('NTR 模式', 'NTR mode'), _jsx("span", { className: "dwa-badge", children: ui.preset.id })] }), _jsx("span", { className: `dwa-badge ${ui.preset.installed ? 'dwa-badge-success' : 'dwa-badge-warn'}`, children: ui.preset.installed ? acp.label('已就绪', 'Ready') : acp.label('需检查', 'Needs attention') }), _jsxs("div", { className: "dwa-mode-copy", children: [ui.preset.installed
710
859
  ? acp.label('已加入宿主模式列表,新建会话时选择「NTR 模式」即可使用。', 'Added to the harness roster. Pick “NTR mode” when creating a session.')
711
- : acp.label('宿主模式列表中没有可用的「NTR 模式」,请检查预设声明。', 'The harness roster has no usable “NTR mode”. Check the preset declaration.'), _jsx("code", { className: "dwa-mode-source dwa-mono", children: ui.preset.source })] }), ui.preset.problem !== undefined && (_jsx("div", { className: "dwa-hint", style: { gridColumn: '1 / -1', color: 'var(--ntr-warn)' }, children: acp.label(`宿主报告:${ui.preset.problem}`, `Harness reported: ${ui.preset.problem}`) }))] })), _jsxs("section", { className: "dwa-section", children: [_jsx("div", { className: "dwa-section-heading", children: _jsxs("div", { children: [_jsxs("h3", { className: "dwa-section-title", children: [acp.label('已接入的智能体', 'Connected agents'), _jsx("span", { className: "dwa-badge", children: configured.length })] }), _jsx("p", { className: "dwa-section-copy", children: acp.label('展开智能体可管理模型、启动命令与工作区。', 'Expand an agent to manage its models, launch command, and workspace.') })] }) }), configured.length === 0 && (_jsx("div", { className: "dwa-card", style: { textAlign: 'center', color: 'var(--ntr-muted)', padding: 20 }, children: acp.label('尚未启用任何 Agent。从下方「添加智能体」点一个检测到的预设(会展开预填命令的创建卡),再点「保存并启用」;也可以直接新建自定义 Agent。', 'No agents configured yet. Pick a detected preset from "Add agent" below (it opens a creation card prefilled with its command), then click "Save & Enable" — or create a custom one from scratch.') })), _jsx("div", { className: "dwa-agent-list", children: configured.map(({ info, synthesized }) => {
860
+ : acp.label('宿主模式列表中没有可用的「NTR 模式」,请检查预设声明。', 'The harness roster has no usable “NTR mode”. Check the preset declaration.'), _jsx("code", { className: "dwa-mode-source dwa-mono", children: ui.preset.source })] }), ui.preset.problem !== undefined && (_jsx("div", { className: "dwa-hint", style: { gridColumn: '1 / -1', color: 'var(--ntr-warn)' }, children: acp.label(`宿主报告:${ui.preset.problem}`, `Harness reported: ${ui.preset.problem}`) }))] })), _jsxs("section", { className: "dwa-section", hidden: page !== 'agents', "aria-label": acp.label('已接入的智能体', 'Connected agents'), children: [_jsxs("div", { className: "dwa-section-heading", children: [_jsx("div", { children: _jsxs("h3", { className: "dwa-section-title", children: [acp.label('已接入的智能体', 'Connected agents'), ui.agentsLoaded && _jsx("span", { className: "dwa-badge", children: configured.length })] }) }), configured.length > 0 && _jsx("span", { className: "dwa-hint", children: acp.label('默认项用于新的 NTR 会话', 'The default serves new NTR conversations') })] }), !ui.agentsLoaded && _jsx("div", { className: "dwa-empty", role: "status", children: acp.label('正在读取智能体…', 'Loading agents…') }), ui.agentsLoaded && configured.length === 0 && (_jsxs("div", { className: "dwa-empty", children: [_jsx("h3", { children: acp.label('接入第一个智能体', 'Connect your first agent') }), _jsx("p", { className: "dwa-section-copy", children: acp.label('选择已有预设,或填写自己的 ACP 启动命令。', 'Choose a preset or enter your own ACP launch command.') }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-primary", onClick: openAdd, children: acp.label('添加智能体', 'Add agent') })] })), _jsx("div", { className: "dwa-agent-list", children: configured.map(({ info, synthesized }) => {
712
861
  const activeAgent = ui.agents.find(a => a.id === info.id);
713
- return (_jsx(AgentRow, { detected: info, activeAgent: activeAgent, acp: acp, isDefault: ui.defaultAgent === info.id, synthesized: synthesized }, info.id));
714
- }) })] }), _jsx("section", { className: "dwa-section", children: _jsxs("div", { className: "dwa-add-panel", children: [_jsxs("button", { type: "button", className: "dwa-add-toggle", "aria-expanded": addOpen, onClick: () => setAddOpen(open => !open), children: [_jsxs("span", { children: [_jsx("span", { className: "dwa-section-title", children: acp.label('添加智能体', 'Add agent') }), _jsx("span", { className: "dwa-section-copy", style: { display: 'block' }, children: acp.label('从预设快速启用,或创建自定义连接。', 'Enable a preset or create a custom connection.') })] }), _jsx("span", { className: "dwa-caret-btn", "aria-hidden": true, children: _jsx("span", { className: "dwa-caret", children: addOpen ? '▾' : '▸' }) })] }), addOpen && (_jsxs("div", { className: "dwa-add-content", children: [_jsxs("div", { className: "dwa-chip-list", children: [available.map(info => (_jsx("button", { type: "button", className: `dwa-chip${selected.id === info.id ? ' dwa-chip-active' : ''}`, onClick: () => setAddTarget(info), title: info.description, children: info.name }, info.id))), _jsx("button", { type: "button", className: `dwa-chip${selected.id === '' ? ' dwa-chip-active' : ''}`, onClick: () => setAddTarget(NEW_AGENT_SEED), children: acp.label('自定义', 'Custom') })] }), available.length === 0 && selected.id === '' && (_jsx("div", { className: "dwa-hint", style: { marginBottom: 8 }, children: acp.label('没有更多可启用的预设 Agent。', 'No more detected presets to enable.') })), _jsx(AgentRow, { detected: selected, acp: acp, isDefault: false, synthesized: false, newCustom: selected.id === '', onCreated: () => {
715
- // Creation done: drop the pin so the selection re-derives to
716
- // the next available preset (or a fresh custom card), and bump
717
- // the nonce so an all-custom reset still remounts empty
718
- // instead of keeping the just-saved values in the form.
719
- setAddTarget(undefined);
720
- setCreateNonce(nonce => nonce + 1);
721
- } }, selected.id === '' ? `custom-${createNonce}` : selected.id)] }))] }) }), _jsx(ApprovalDeadlinesCard, { acp: acp, ui: ui }), _jsxs("section", { className: "dwa-section", children: [_jsx("div", { className: "dwa-section-heading", children: _jsxs("div", { children: [_jsx("h3", { className: "dwa-section-title", children: acp.label('对话外观', 'Conversation appearance') }), _jsx("p", { className: "dwa-section-copy", children: acp.label('设置对话中 NTR 挂牌的进程存活提示。', 'Choose how the NTR tag indicates a running agent process.') })] }) }), _jsxs("div", { className: "dwa-card", style: { paddingTop: 4, paddingBottom: 4 }, children: [_jsxs("div", { className: "dwa-preference-row", children: [_jsxs("div", { children: [_jsx("div", { className: "dwa-preference-label", children: acp.label('挂牌光晕', 'Tag halo') }), _jsx("div", { className: "dwa-preference-copy", children: acp.label('呼吸为脉动光,常亮为恒定光。', 'Breathe pulses gently; steady keeps a constant glow.') })] }), _jsx("div", { className: "dwa-seg", children: ['breath', 'steady'].map(mode => (_jsx("button", { type: "button", className: ui.tagHalo.mode === mode ? 'is-on' : '', onClick: () => void acp.setTagHalo({ ...ui.tagHalo, mode }), children: mode === 'breath' ? acp.label('呼吸', 'Breathe') : acp.label('常亮', 'Steady') }, mode))) })] }), ui.tagHalo.mode === 'breath' && (_jsxs("div", { className: "dwa-preference-row", children: [_jsxs("div", { children: [_jsx("div", { className: "dwa-preference-label", children: acp.label('呼吸频率', 'Breathing speed') }), _jsx("div", { className: "dwa-preference-copy", children: acp.label('控制光晕的脉动速度。', 'Controls how quickly the halo pulses.') })] }), _jsx("div", { className: "dwa-seg", children: ['slow', 'normal', 'fast'].map(speed => (_jsx("button", { type: "button", className: ui.tagHalo.speed === speed ? 'is-on' : '', onClick: () => void acp.setTagHalo({ ...ui.tagHalo, speed }), children: speed === 'slow' ? acp.label('慢速', 'Slow') : speed === 'normal' ? acp.label('正常', 'Normal') : acp.label('快速', 'Fast') }, speed))) })] }))] })] })] }));
862
+ return (_jsx(AgentRow, { detected: info, activeAgent: activeAgent, acp: acp, isDefault: ui.defaultAgent === info.id, synthesized: synthesized, active: page === 'agents' }, info.id));
863
+ }) })] }), _jsxs("section", { className: "dwa-section", hidden: page !== 'add', "aria-label": acp.label('添加智能体', 'Add agent'), children: [_jsx("button", { type: "button", className: "dwa-btn dwa-btn-quiet dwa-back", onClick: () => setPage('agents'), children: acp.label('返回智能体列表', 'Back to agents') }), _jsxs("div", { className: "dwa-add-panel", children: [_jsx("div", { className: "dwa-section-heading", children: _jsxs("div", { children: [_jsx("h3", { className: "dwa-section-title", children: acp.label('添加智能体', 'Add agent') }), _jsx("p", { className: "dwa-section-copy", children: acp.label('从预设快速启用,或创建自定义连接。', 'Enable a preset or create a custom connection.') })] }) }), addOpen && (_jsxs("div", { className: "dwa-add-content", children: [_jsxs("div", { className: "dwa-chip-list", children: [available.map(info => (_jsx("button", { type: "button", className: `dwa-chip${selected.id === info.id ? ' dwa-chip-active' : ''}`, onClick: () => setAddTarget(info), title: info.description, children: info.name }, info.id))), _jsx("button", { type: "button", className: `dwa-chip${selected.id === '' ? ' dwa-chip-active' : ''}`, onClick: () => setAddTarget(NEW_AGENT_SEED), children: acp.label('自定义', 'Custom') })] }), available.length === 0 && selected.id === '' && (_jsx("div", { className: "dwa-hint", style: { marginBottom: 8 }, children: acp.label('没有更多可启用的预设 Agent。', 'No more detected presets to enable.') })), _jsx(AgentRow, { detected: selected, acp: acp, isDefault: false, synthesized: false, newCustom: selected.id === '', active: page === 'add', onCreated: () => {
864
+ setAddTarget(undefined);
865
+ setAddOpen(false);
866
+ setPage('agents');
867
+ } }, selected.id)] }))] })] }), _jsx("div", { hidden: page !== 'approvals', children: _jsx(ApprovalDeadlinesCard, { acp: acp, ui: ui }) }), _jsxs("section", { className: "dwa-section", hidden: page !== 'appearance', "aria-label": acp.label('对话外观', 'Appearance'), children: [_jsx("div", { className: "dwa-section-heading", children: _jsxs("div", { children: [_jsx("h3", { className: "dwa-section-title", children: acp.label('对话外观', 'Conversation appearance') }), _jsx("p", { className: "dwa-section-copy", children: acp.label('设置对话中 NTR 挂牌的进程存活提示。', 'Choose how the NTR tag indicates a running agent process.') })] }) }), _jsxs("div", { className: "dwa-preferences", children: [_jsxs("div", { className: "dwa-preference-row", children: [_jsxs("div", { children: [_jsx("div", { className: "dwa-preference-label", children: acp.label('挂牌光晕', 'Tag halo') }), _jsx("div", { className: "dwa-preference-copy", children: acp.label('呼吸为脉动光,常亮为恒定光。', 'Breathe pulses gently; steady keeps a constant glow.') })] }), _jsx("div", { className: "dwa-seg", children: ['breath', 'steady'].map(mode => (_jsx("button", { type: "button", className: ui.tagHalo.mode === mode ? 'is-on' : '', onClick: () => void acp.setTagHalo({ ...ui.tagHalo, mode }), children: mode === 'breath' ? acp.label('呼吸', 'Breathe') : acp.label('常亮', 'Steady') }, mode))) })] }), ui.tagHalo.mode === 'breath' && (_jsxs("div", { className: "dwa-preference-row", children: [_jsxs("div", { children: [_jsx("div", { className: "dwa-preference-label", children: acp.label('呼吸频率', 'Breathing speed') }), _jsx("div", { className: "dwa-preference-copy", children: acp.label('控制光晕的脉动速度。', 'Controls how quickly the halo pulses.') })] }), _jsx("div", { className: "dwa-seg", children: ['slow', 'normal', 'fast'].map(speed => (_jsx("button", { type: "button", className: ui.tagHalo.speed === speed ? 'is-on' : '', onClick: () => void acp.setTagHalo({ ...ui.tagHalo, speed }), children: speed === 'slow' ? acp.label('慢速', 'Slow') : speed === 'normal' ? acp.label('正常', 'Normal') : acp.label('快速', 'Fast') }, speed))) })] }))] })] }), _jsxs("footer", { className: "dwa-health", children: [_jsxs("div", { className: "dwa-health-status", children: [_jsx("span", { className: "dwa-dot", style: { background: ui.stream === 'live' ? 'var(--ntr-success)' : ui.stream === 'down' ? 'var(--ntr-danger)' : 'var(--ntr-muted)' } }), _jsx("span", { children: streamText(ui.stream, acp) }), ui.preset !== undefined && _jsx("span", { children: ui.preset.installed && !presetProblem ? acp.label('NTR 模式已就绪', 'NTR mode ready') : acp.label('NTR 模式需检查', 'NTR mode needs attention') })] }), page !== 'diagnostics' && _jsx("button", { type: "button", className: "dwa-btn dwa-btn-quiet", onClick: () => setPage('diagnostics'), children: acp.label('查看诊断', 'View diagnostics') })] })] }));
722
868
  }
723
869
  /** One row per agent: compact when collapsed, the full form when expanded. */
724
- function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, newCustom = false, onCreated, }) {
870
+ function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, active, newCustom = false, onCreated, }) {
725
871
  const ui = useUi(acp);
726
872
  const configured = activeAgent !== undefined || detected.currentConfig !== undefined;
727
873
  // A configured agent starts collapsed; a creation card opens straight away
728
874
  // (nothing to summarise yet).
729
875
  const [expanded, setExpanded] = useState(!configured);
876
+ const [detailPage, setDetailPage] = useState(configured ? 'models' : 'connection');
730
877
  const [form, setForm] = useState(() => initialForm(detected, activeAgent));
731
878
  const [saving, setSaving] = useState(false);
732
879
  const [savedSuccess, setSavedSuccess] = useState(false);
@@ -757,7 +904,8 @@ function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, newCusto
757
904
  const formIncomplete = splitCommandLine(form.commandLine).command === '' || !idValid || idTaken || cwdInvalid || rootsInvalid || idleInvalid || mcpInvalid;
758
905
  const payload = buildPayload(form, detected, newCustom);
759
906
  const current = persistedDef(detected, activeAgent);
760
- const dirty = current !== undefined && !payloadEquals(payload, current);
907
+ const clearingIdleOverride = payload.idleTimeoutMs === undefined && detected.currentConfig?.idleTimeoutMs !== undefined;
908
+ const dirty = current !== undefined && (!payloadEquals(payload, current) || clearingIdleOverride);
761
909
  // A save stops the child and kills every live session (see BridgeManager.saveAgent).
762
910
  const showSaveWarning = dirty && activeAgent !== undefined && activeAgent.state !== 'stopped';
763
911
  const handleUseDefault = () => patch({ commandLine: formatCommandLine(detected.defaultCommand, detected.defaultArgs) });
@@ -810,14 +958,14 @@ function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, newCusto
810
958
  : (activeAgent?.title ?? detected.currentConfig?.title ?? detected.name);
811
959
  const stderr = activeAgent?.state === 'error' ? (activeAgent.stderrTail ?? []) : [];
812
960
  const errorSummary = stderr.length > 0 ? stderr[stderr.length - 1] : undefined;
813
- const currentModel = ui.modelCatalogs[detected.id]?.catalog.model?.currentValue;
961
+ const selectedModelCount = form.exposed?.length ?? 0;
814
962
  const isPreset = PRESET_IDS.has(detected.id);
815
963
  // An error row opens itself so the stderr tail is never behind a click.
816
964
  const open = expanded || (activeAgent?.state === 'error' && configured);
817
- return (_jsxs("div", { className: `dwa-agent-card${open ? ' is-open' : ''}`, children: [_jsxs("div", { className: "dwa-agent-row", children: [_jsx("button", { type: "button", className: "dwa-caret-btn", "aria-expanded": open, onClick: () => setExpanded(value => !value), title: open ? acp.label('收起', 'Collapse') : acp.label('展开配置与模型', 'Expand configuration and models'), children: _jsx("span", { className: "dwa-caret", "aria-hidden": true, children: open ? '▾' : '▸' }) }), _jsxs("div", { className: "dwa-agent-summary", children: [_jsxs("div", { className: "dwa-agent-titleline", children: [_jsx("span", { className: "dwa-agent-name", title: detected.executablePath ?? detected.description, children: displayName }), isDefault && _jsx("span", { className: "dwa-badge dwa-badge-success", children: acp.label('默认', 'Default') }), !isPreset && !newCustom && _jsx("span", { className: "dwa-badge", children: acp.label('自定义', 'Custom') })] }), _jsxs("div", { className: "dwa-agent-meta", children: [state !== undefined && (_jsxs("span", { title: state.title ?? activeAgent?.state, style: { display: 'inline-flex', alignItems: 'center', gap: 6 }, children: [_jsx("span", { className: "dwa-dot", style: { background: state.color } }), state.text] })), configured && (_jsxs(_Fragment, { children: [state !== undefined && _jsx("span", { className: "dwa-agent-meta-separator", children: "\u00B7" }), _jsx("span", { children: currentModel === undefined ? acp.label('未探测模型', 'Model not probed') : `${acp.label('模型', 'Model')}: ${currentModel}` })] })), !synthesized && !newCustom && !detected.detected && (_jsxs("span", { style: { color: 'var(--ntr-warn)' }, title: detected.description, children: ["\u00B7 ", acp.label('未在 PATH 中检测到', 'Not in PATH')] }))] }), errorSummary !== undefined && (_jsx("div", { className: "dwa-error dwa-mono", style: { marginTop: 6, wordBreak: 'break-all' }, children: errorSummary }))] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [activeAgent?.state === 'error' && (_jsx("button", { className: "dwa-btn", disabled: testing, onClick: () => void handleTest(), children: testing ? acp.label('测试中...', 'Testing...') : acp.label('测试', 'Test') })), configured && (_jsx(RowMenu, { acp: acp, canStop: activeAgent !== undefined && activeAgent.state !== 'stopped', onStop: () => void acp.stopAgent(detected.id),
965
+ return (_jsxs("div", { className: `dwa-agent-card${open ? ' is-open' : ''}`, children: [_jsxs("div", { className: "dwa-agent-row", children: [_jsx("span", { className: "dwa-agent-avatar", "aria-hidden": true, children: displayName.slice(0, 2).toUpperCase() }), _jsxs("div", { className: "dwa-agent-summary", children: [_jsxs("div", { className: "dwa-agent-titleline", children: [_jsx("span", { className: "dwa-agent-name", title: detected.executablePath ?? detected.description, children: displayName }), isDefault && _jsx("span", { className: "dwa-badge dwa-badge-info", children: acp.label('默认', 'Default') }), !isPreset && !newCustom && _jsx("span", { className: "dwa-badge", children: acp.label('自定义', 'Custom') })] }), _jsxs("div", { className: "dwa-agent-meta", children: [state !== undefined && (_jsxs("span", { title: state.title ?? activeAgent?.state, style: { display: 'inline-flex', alignItems: 'center', gap: 6 }, children: [_jsx("span", { className: "dwa-dot", style: { background: state.color } }), state.text] })), configured && (_jsxs(_Fragment, { children: [state !== undefined && _jsx("span", { className: "dwa-agent-meta-separator", children: "\u00B7" }), _jsx("span", { children: acp.label(`${String(selectedModelCount)} 个已选模型`, `${String(selectedModelCount)} selected models`) })] })), !synthesized && !newCustom && !detected.detected && (_jsxs("span", { style: { color: 'var(--ntr-warn)' }, title: detected.description, children: ["\u00B7 ", acp.label('未在 PATH 中检测到', 'Not in PATH')] }))] }), errorSummary !== undefined && (_jsx("div", { className: "dwa-error dwa-mono", style: { marginTop: 6, wordBreak: 'break-all' }, children: errorSummary }))] }), _jsxs("div", { className: "dwa-row-actions", children: [configured && (_jsx("button", { type: "button", className: "dwa-btn dwa-btn-quiet", "aria-expanded": open, "aria-label": acp.label(`${open ? '收起' : '管理'} ${displayName}`, `${open ? 'Collapse' : 'Manage'} ${displayName}`), onClick: () => setExpanded(value => !value), children: open ? acp.label('收起', 'Collapse') : acp.label('管理', 'Manage') })), activeAgent?.state === 'error' && (_jsx("button", { className: "dwa-btn", disabled: testing, onClick: () => void handleTest(), children: testing ? acp.label('测试中...', 'Testing...') : acp.label('测试', 'Test') })), configured && (_jsx(RowMenu, { acp: acp, canStop: activeAgent !== undefined && activeAgent.state !== 'stopped', onStop: () => void acp.stopAgent(detected.id),
818
966
  // The store already surfaced the failure as a notice; swallowing it
819
967
  // here only prevents an unhandled rejection.
820
- onRemove: async () => { await acp.deleteAgent(detected.id).catch(() => { }); } }))] })] }), _jsxs("div", { className: "dwa-agent-body", style: open ? undefined : { display: 'none' }, children: [stderr.length > 0 && _jsx("pre", { className: "dwa-stderr", children: stderr.join('\n') }), configured && (_jsxs("section", { className: "dwa-subsection", children: [_jsx("div", { className: "dwa-subsection-head", children: _jsxs("div", { children: [_jsx("h4", { className: "dwa-subsection-title", children: acp.label('模型与默认值', 'Models & defaults') }), _jsx("p", { className: "dwa-subsection-copy", children: acp.label('检测模型目录,并控制对话中可选择的模型。', 'Detect the model catalog and choose what appears in conversations.') })] }) }), _jsx(ModelCatalogBlock, { agentId: detected.id, detected: detected, acp: acp, active: open, exposed: form.exposed, onExposedChange: exposed => patch({ exposed }), autoDiscover: form.autoDiscoverModels, onAutoDiscoverChange: autoDiscoverModels => patch({ autoDiscoverModels }), isDefault: isDefault })] })), _jsxs("section", { className: "dwa-subsection", children: [_jsx("div", { className: "dwa-subsection-head", children: _jsxs("div", { children: [_jsx("h4", { className: "dwa-subsection-title", children: acp.label('连接配置', 'Connection') }), _jsx("p", { className: "dwa-subsection-copy", children: acp.label('设置显示名与启动命令,高级选项按需展开。', 'Set the display name and launch command; expand advanced options only when needed.') })] }) }), _jsx(AgentDetails, { acp: acp, detected: detected, form: form, patch: patch, isConfigured: configured, newCustom: newCustom, formIncomplete: formIncomplete, idValid: idValid, idTaken: idTaken, cwdInvalid: cwdInvalid, rootsInvalid: rootsInvalid, idleInvalid: idleInvalid, mcpInvalid: mcpInvalid, saving: saving, savedSuccess: savedSuccess, testing: testing, testResult: testResult, showSaveWarning: showSaveWarning, onUseDefault: handleUseDefault, onSave: () => void handleSave(), onTest: () => void handleTest() })] })] })] }));
968
+ onRemove: async () => { await acp.deleteAgent(detected.id).catch(() => { }); } }))] })] }), _jsxs("div", { className: "dwa-agent-body", style: open ? undefined : { display: 'none' }, children: [stderr.length > 0 && _jsx("pre", { className: "dwa-stderr", children: stderr.join('\n') }), _jsxs("nav", { className: "dwa-tabs dwa-detail-tabs", "aria-label": acp.label(`${displayName} 配置`, `${displayName} settings`), children: [configured && _jsx("button", { type: "button", "aria-pressed": detailPage === 'models', onClick: () => setDetailPage('models'), children: acp.label('模型', 'Models') }), _jsx("button", { type: "button", "aria-pressed": detailPage === 'connection', onClick: () => setDetailPage('connection'), children: acp.label('连接', 'Connection') }), _jsx("button", { type: "button", "aria-pressed": detailPage === 'advanced', onClick: () => setDetailPage('advanced'), children: acp.label('高级', 'Advanced') })] }), configured && (_jsx("section", { className: "dwa-subsection", hidden: detailPage !== 'models', children: _jsx(ModelCatalogBlock, { agentId: detected.id, detected: detected, acp: acp, active: active && open && detailPage === 'models', exposed: form.exposed, onExposedChange: exposed => patch({ exposed }), autoDiscover: form.autoDiscoverModels, onAutoDiscoverChange: autoDiscoverModels => patch({ autoDiscoverModels }), isDefault: isDefault }) })), _jsx("section", { className: "dwa-subsection", hidden: detailPage === 'models', children: _jsx(AgentDetails, { acp: acp, advancedOpen: detailPage === 'advanced', dirty: dirty, detected: detected, form: form, patch: patch, isConfigured: configured, newCustom: newCustom, formIncomplete: formIncomplete, idValid: idValid, idTaken: idTaken, cwdInvalid: cwdInvalid, rootsInvalid: rootsInvalid, idleInvalid: idleInvalid, mcpInvalid: mcpInvalid, saving: saving, savedSuccess: savedSuccess, testing: testing, testResult: testResult, showSaveWarning: showSaveWarning, onUseDefault: handleUseDefault, onSave: () => void handleSave(), onTest: () => void handleTest() }) })] })] }));
821
969
  }
822
970
  /**
823
971
  * Hand copy of `DEFAULT_TUNING.idleTimeoutMs` (src/bridge.ts): the browser
@@ -934,9 +1082,7 @@ export function buildPayload(form, detected, newCustom) {
934
1082
  return payload;
935
1083
  }
936
1084
  /** The expanded body: connection form, workspace allow-list, env, actions. */
937
- function AgentDetails({ acp, detected, form, patch, isConfigured, newCustom, formIncomplete, idValid, idTaken, cwdInvalid, rootsInvalid, idleInvalid, mcpInvalid, saving, savedSuccess, testing, testResult, showSaveWarning, onUseDefault, onSave, onTest, }) {
938
- const ui = useUi(acp);
939
- const [advancedOpen, setAdvancedOpen] = useState(false);
1085
+ function AgentDetails({ acp, advancedOpen, dirty, detected, form, patch, isConfigured, newCustom, formIncomplete, idValid, idTaken, cwdInvalid, rootsInvalid, idleInvalid, mcpInvalid, saving, savedSuccess, testing, testResult, showSaveWarning, onUseDefault, onSave, onTest, }) {
940
1086
  // The same parse the host will run (`validateAgentDef` → `splitCommandLine`).
941
1087
  // Showing it is what turns "the first box swallowed my arguments" into a
942
1088
  // visible, fixable state instead of a spawn error.
@@ -945,20 +1091,20 @@ function AgentDetails({ acp, detected, form, patch, isConfigured, newCustom, for
945
1091
  ? acp.label('填整条命令,例如:codebuddy --acp', 'Type the whole command, e.g. codebuddy --acp')
946
1092
  : `${acp.label('程序', 'Program')}: ${parsedCommand.command}`
947
1093
  + (parsedCommand.args.length === 0 ? '' : ` ${acp.label('参数', 'Arguments')}: ${parsedCommand.args.join(' ')}`);
948
- return (_jsxs("div", { children: [newCustom && (_jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('Agent ID(唯一标识,创建后不可改)', 'Agent ID (unique, immutable after creation)') }), _jsx("input", { className: "dwa-input dwa-input-mono", value: form.agentId, onChange: e => patch({ agentId: e.target.value }), placeholder: "e.g. my-agent" }), _jsx("span", { className: idValid && !idTaken ? 'dwa-hint' : 'dwa-error', children: idTaken
949
- ? acp.label('该 ID 已被现有 Agent 占用。', 'This id is already taken by a configured agent.')
950
- : idValid
951
- ? acp.label('小写字母或数字开头,可用 a-z 0-9 . _ -,最长 64 字符。', 'Starts with a lowercase letter or digit; a-z 0-9 . _ - allowed; max 64 chars.')
952
- : acp.label('ID 格式不合法:小写字母或数字开头,可用 a-z 0-9 . _ -。', 'Invalid id: start with a lowercase letter or digit; only a-z 0-9 . _ - allowed.') })] }) })), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('显示名', 'Display Name') }), _jsx("input", { className: "dwa-input", value: form.title, onChange: e => patch({ title: e.target.value }), placeholder: detected.name === '' ? acp.label('我的 Agent', 'My Agent') : detected.name })] }) }), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)') }), _jsx("input", { className: "dwa-input dwa-input-mono", value: form.commandLine, onChange: e => patch({ commandLine: e.target.value }), placeholder: detected.defaultCommand === ''
953
- ? 'e.g. codebuddy --acp'
954
- : formatCommandLine(detected.defaultCommand, detected.defaultArgs) }), _jsx("span", { className: "dwa-hint", children: commandPreview }), _jsx("span", { className: "dwa-hint", children: acp.label('参数直接跟在程序后面即可;路径里含空格时用引号包住整条路径,例如 "C:\\Program Files\\nodejs\\node.exe" --acp。', 'Arguments go right after the program; quote a whole path that contains spaces, e.g. "C:\\Program Files\\nodejs\\node.exe" --acp.') })] }) }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, flexWrap: 'wrap' }, children: [_jsxs("button", { type: "button", className: "dwa-btn dwa-btn-secondary", "aria-expanded": advancedOpen, onClick: () => setAdvancedOpen(value => !value), children: [_jsx("span", { className: "dwa-caret", "aria-hidden": true, children: advancedOpen ? '▾' : '▸' }), " ", acp.label('高级参数', 'Advanced Parameters')] }), !advancedOpen && (cwdInvalid || rootsInvalid) && (_jsx("span", { className: "dwa-error", children: acp.label('里面有非法的工作空间路径', 'Contains an invalid workspace path') })), !advancedOpen && mcpInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('里面的 MCP 服务器配置不是合法 JSON', 'Contains an invalid MCP server list') }))] }), _jsxs("div", { style: advancedOpen ? undefined : { display: 'none' }, children: [!newCustom && (_jsxs("div", { className: "dwa-default-hint", children: [_jsxs("div", { children: [_jsx("span", { style: { fontWeight: 600, marginRight: 6 }, children: acp.label('官方测试默认命令:', 'Tested Default Command:') }), _jsxs("code", { className: "dwa-input-mono", style: { background: 'var(--dsw-alias-markdown-inline-code, #0000000a)', padding: '2px 6px', borderRadius: 4 }, children: [detected.defaultCommand, " ", detected.defaultArgs.join(' ')] })] }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: onUseDefault, title: acp.label('把命令与参数重置为官方验证值', 'Reset command and arguments to the tested default'), children: acp.label('使用预设值', 'Use Preset') })] })), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('npm 包名(PATH 中找不到时自动安装)', 'npm Package (auto-installed when missing from PATH)') }), _jsx("input", { className: "dwa-input dwa-input-mono", value: form.npmPackage, onChange: e => patch({ npmPackage: e.target.value }), placeholder: acp.label('原生支持 ACP 的 agent 无需填写', 'Not needed for agents with native ACP support') })] }) }), _jsx(WorkspaceBlock, { acp: acp, form: form, patch: patch, cwdInvalid: cwdInvalid, rootsInvalid: rootsInvalid }), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('空闲释放(毫秒)', 'Idle Release (ms)') }), _jsx("input", { className: "dwa-input dwa-input-mono", value: form.idleStr, onChange: e => patch({ idleStr: e.target.value }), placeholder: acp.label(`留空 = 宿主默认(承载会话 ${String(DEFAULT_IDLE_RELEASE_MS / 60_000)} 分钟 / 探测进程 ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} 分钟)`, `Blank = host default (${String(DEFAULT_IDLE_RELEASE_MS / 60_000)} min for a chat process / ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} min for a probe)`) }), _jsx("span", { className: idleInvalid ? 'dwa-error' : 'dwa-hint', children: idleInvalid
1094
+ return (_jsxs("div", { children: [_jsxs("div", { hidden: advancedOpen, children: [newCustom && (_jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('Agent ID(唯一标识,创建后不可改)', 'Agent ID (unique, immutable after creation)') }), _jsx("input", { className: "dwa-input dwa-input-mono", "aria-label": acp.label('Agent ID', 'Agent ID'), value: form.agentId, onChange: e => patch({ agentId: e.target.value }), placeholder: "e.g. my-agent" }), _jsx("span", { className: idValid && !idTaken ? 'dwa-hint' : 'dwa-error', children: idTaken
1095
+ ? acp.label('该 ID 已被现有 Agent 占用。', 'This id is already taken by a configured agent.')
1096
+ : idValid
1097
+ ? acp.label('小写字母或数字开头,可用 a-z 0-9 . _ -,最长 64 字符。', 'Starts with a lowercase letter or digit; a-z 0-9 . _ - allowed; max 64 chars.')
1098
+ : acp.label('ID 格式不合法:小写字母或数字开头,可用 a-z 0-9 . _ -。', 'Invalid id: start with a lowercase letter or digit; only a-z 0-9 . _ - allowed.') })] }) })), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('显示名', 'Display Name') }), _jsx("input", { className: "dwa-input", "aria-label": acp.label('显示名', 'Display Name'), value: form.title, onChange: e => patch({ title: e.target.value }), placeholder: detected.name === '' ? acp.label('我的 Agent', 'My Agent') : detected.name })] }) }), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)') }), _jsx("input", { className: "dwa-input dwa-input-mono", "aria-label": acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)'), value: form.commandLine, onChange: e => patch({ commandLine: e.target.value }), placeholder: detected.defaultCommand === ''
1099
+ ? 'e.g. codebuddy --acp'
1100
+ : formatCommandLine(detected.defaultCommand, detected.defaultArgs) }), _jsx("span", { className: "dwa-hint", children: commandPreview }), _jsx("span", { className: "dwa-hint", children: acp.label('含空格的程序路径请加引号。', 'Quote the program path if it contains spaces.') })] }) }), _jsx(WorkspaceBlock, { acp: acp, form: form, patch: patch, cwdInvalid: cwdInvalid, rootsInvalid: rootsInvalid })] }), _jsxs("div", { hidden: !advancedOpen, children: [!newCustom && (_jsxs("div", { className: "dwa-default-hint", children: [_jsxs("div", { children: [_jsx("span", { style: { fontWeight: 600, marginRight: 6 }, children: acp.label('官方测试默认命令:', 'Tested Default Command:') }), _jsxs("code", { className: "dwa-input-mono", style: { background: 'var(--dsw-alias-markdown-inline-code, #0000000a)', padding: '2px 6px', borderRadius: 4 }, children: [detected.defaultCommand, " ", detected.defaultArgs.join(' ')] })] }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: onUseDefault, title: acp.label('把命令与参数重置为官方验证值', 'Reset command and arguments to the tested default'), children: acp.label('使用预设值', 'Use Preset') })] })), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('npm 包名(PATH 中找不到时自动安装)', 'npm Package (auto-installed when missing from PATH)') }), _jsx("input", { className: "dwa-input dwa-input-mono", value: form.npmPackage, onChange: e => patch({ npmPackage: e.target.value }), placeholder: acp.label('原生支持 ACP 的 agent 无需填写', 'Not needed for agents with native ACP support') })] }) }), _jsx("div", { className: "dwa-form-row", children: _jsxs("div", { className: "dwa-form-col", children: [_jsx("label", { className: "dwa-form-label", children: acp.label('空闲释放(毫秒)', 'Idle Release (ms)') }), _jsx("input", { className: "dwa-input dwa-input-mono", "aria-label": acp.label('空闲释放(毫秒)', 'Idle Release (ms)'), value: form.idleStr, onChange: e => patch({ idleStr: e.target.value }), placeholder: acp.label(`留空 = 宿主默认(承载会话 ${String(DEFAULT_IDLE_RELEASE_MS / 60_000)} 分钟 / 探测进程 ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} 分钟)`, `Blank = host default (${String(DEFAULT_IDLE_RELEASE_MS / 60_000)} min for a chat process / ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} min for a probe)`) }), _jsx("span", { className: idleInvalid ? 'dwa-error' : 'dwa-hint', children: idleInvalid
955
1101
  ? acp.label('必须是 0(永不释放)或 [1000, 86400000] 之间的整数毫秒。', 'Must be 0 (never release) or an integer in [1000, 86400000] ms.')
956
- : (_jsxs(_Fragment, { children: [acp.label('空闲超过此时长就挂起进程(会话保留,下一条消息自动恢复)。', 'The process is parked once it has been idle this long (the session is kept and resumes on the next message).'), _jsx("br", {}), acp.label('0 = 永不释放;回合在跑、取消未结算或审批待答时不释放。', '0 = never; a process with a running turn, an unsettled cancel, or a pending approval is never released.'), _jsx("br", {}), acp.label('只对承载过会话的进程生效;探测进程固定 5 分钟释放,填 0 也拦不住。', 'This window applies only to a process that has hosted a session; the model-probe process is always released after 5 minutes, and 0 does not stop it.')] })) })] }) }), _jsxs("div", { className: "dwa-form-row", style: { flexDirection: 'column', gap: 6 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('环境变量', 'Environment Variables') }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: () => patch({ envRows: [...form.envRows, { key: '', value: '' }] }), children: acp.label('添加', 'Add') })] }), form.envRows.map((row, index) => (_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [_jsx("input", { className: "dwa-input dwa-input-mono", style: { flex: 2 }, value: row.key, onChange: e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, key: e.target.value } : r)) }), placeholder: "KEY" }), _jsx("input", { className: "dwa-input dwa-input-mono", style: { flex: 3 }, value: row.value, onChange: e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, value: e.target.value } : r)) }), placeholder: "VALUE" }), _jsx("button", { type: "button", className: "dwa-btn", title: acp.label('删除该变量', 'Remove this variable'), onClick: () => patch({ envRows: form.envRows.filter((_, i) => i !== index) }), children: "\u2715" })] }, index))), form.envRows.length === 0 && (_jsx("span", { className: "dwa-hint", children: acp.label('无自定义变量;子进程继承宿主环境。', 'No custom variables; the child inherits the host environment.') }))] }), _jsxs("div", { className: "dwa-form-row", style: { flexDirection: 'column', gap: 6 }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)') }), _jsx("textarea", { className: "dwa-input dwa-input-mono", style: { minHeight: 96, resize: 'vertical' }, value: form.mcpText, onChange: e => patch({ mcpText: e.target.value }), spellCheck: false, placeholder: '[\n { "name": "example", "transport": "stdio", "command": "/abs/path/to/server", "args": ["--mcp"] }\n]' }), mcpInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('必须是一个 JSON 数组;每个条目写明 name 与 transport。', 'Must be a JSON array; every entry needs a name and a transport.') })), _jsx("span", { className: "dwa-hint", children: acp.label('这些 MCP 服务器会在建会话时交给 agent,由 agent 自己拉起并使用(stdio 需填 command,可选 args / env;http 与 sse 需填 url,可选 headers)。agent 自己配置的 MCP 服务器不受影响,这里只是额外追加。', 'These MCP servers are handed to the agent when a session is created; the agent launches and uses them itself (stdio needs command, with optional args / env; http and sse need url, with optional headers). The MCP servers the agent configures for itself are unaffected — this only adds more.') })] })] }), _jsxs("div", { className: "dwa-form-actions", children: [_jsx("button", { className: "dwa-btn", onClick: onTest, disabled: testing || saving || formIncomplete, title: acp.label('用当前表单值拉起一次性进程并完成 ACP 握手,不保存配置', 'Spawns a throwaway process from the form values and completes the ACP handshake; nothing is saved'), children: testing ? acp.label('测试中...', 'Testing...') : acp.label('测试连接', 'Test Connection') }), _jsx("button", { className: "dwa-btn dwa-btn-primary", onClick: onSave, disabled: saving || testing || formIncomplete, children: saving
1102
+ : (_jsx(_Fragment, { children: acp.label('承载过会话的进程空闲后挂起;0 不释放,探测进程固定 5 分钟。', 'Park idle chat processes; 0 keeps them alive. Probe processes always release after 5 minutes.') })) })] }) }), _jsxs("div", { className: "dwa-form-row", style: { flexDirection: 'column', gap: 6 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('环境变量', 'Environment Variables') }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: () => patch({ envRows: [...form.envRows, { key: '', value: '' }] }), children: acp.label('添加', 'Add') })] }), form.envRows.map((row, index) => (_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [_jsx("input", { className: "dwa-input dwa-input-mono", style: { flex: 2 }, value: row.key, onChange: e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, key: e.target.value } : r)) }), placeholder: "KEY" }), _jsx("input", { className: "dwa-input dwa-input-mono", style: { flex: 3 }, value: row.value, onChange: e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, value: e.target.value } : r)) }), placeholder: "VALUE" }), _jsx("button", { type: "button", className: "dwa-btn", title: acp.label('删除该变量', 'Remove this variable'), onClick: () => patch({ envRows: form.envRows.filter((_, i) => i !== index) }), children: "\u2715" })] }, index))), form.envRows.length === 0 && (_jsx("span", { className: "dwa-hint", children: acp.label('无自定义变量;子进程继承宿主环境。', 'No custom variables; the child inherits the host environment.') }))] }), _jsxs("div", { className: "dwa-form-row", style: { flexDirection: 'column', gap: 6 }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)') }), _jsx("textarea", { className: "dwa-input dwa-input-mono", style: { minHeight: 96, resize: 'vertical' }, "aria-label": acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)'), value: form.mcpText, onChange: e => patch({ mcpText: e.target.value }), spellCheck: false, placeholder: '[\n { "name": "example", "transport": "stdio", "command": "/abs/path/to/server", "args": ["--mcp"] }\n]' }), mcpInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('必须是一个 JSON 数组;每个条目写明 name 与 transport。', 'Must be a JSON array; every entry needs a name and a transport.') })), _jsx("span", { className: "dwa-hint", children: acp.label('额外追加给智能体的 MCP 服务器,不影响其已有配置。', 'Additional MCP servers for the agent; its own configuration is unchanged.') })] })] }), !advancedOpen && (mcpInvalid || idleInvalid) && _jsx("p", { className: "dwa-error", role: "alert", children: acp.label('请检查「高级」中的无效配置。', 'Check the invalid settings in Advanced.') }), advancedOpen && (!idValid || idTaken || cwdInvalid || rootsInvalid || parsedCommand.command === '') && _jsx("p", { className: "dwa-error", role: "alert", children: acp.label('请检查「连接」中的未完成或无效配置。', 'Check the incomplete or invalid settings in Connection.') }), _jsxs("div", { className: "dwa-form-actions", children: [_jsx("span", { className: "dwa-hint", children: isConfigured ? dirty ? acp.label('有未保存的更改', 'Unsaved changes') : acp.label('配置已保存', 'Settings saved') : '' }), _jsx("button", { className: "dwa-btn", onClick: onTest, disabled: testing || saving || formIncomplete, title: acp.label('用当前表单值拉起一次性进程并完成 ACP 握手,不保存配置', 'Spawns a throwaway process from the form values and completes the ACP handshake; nothing is saved'), children: testing ? acp.label('测试中...', 'Testing...') : acp.label('测试连接', 'Test Connection') }), _jsx("button", { className: "dwa-btn dwa-btn-primary", onClick: onSave, disabled: saving || testing || formIncomplete || (isConfigured && !dirty), children: saving
957
1103
  ? acp.label('保存中...', 'Saving...')
958
1104
  : savedSuccess
959
1105
  ? acp.label('✓ 已保存', '✓ Saved')
960
1106
  : isConfigured
961
- ? acp.label('保存', 'Save')
1107
+ ? acp.label('保存更改', 'Save changes')
962
1108
  : acp.label('保存并启用', 'Save & Enable') }), showSaveWarning && (_jsxs("span", { className: "dwa-hint", style: { color: 'var(--ntr-warn)' }, children: ["\u26A0 ", acp.label('保存会停止该 Agent 进程并中断进行中的会话', 'Saving stops this agent process and interrupts live sessions')] }))] }), testResult !== undefined && (_jsxs("div", { className: "dwa-mono", style: {
963
1109
  marginTop: 8,
964
1110
  fontSize: 12,
@@ -973,7 +1119,7 @@ function AgentDetails({ acp, detected, form, patch, isConfigured, newCustom, for
973
1119
  * may touch (and refuses anything else with `cwd_denied`).
974
1120
  */
975
1121
  function WorkspaceBlock({ acp, form, patch, cwdInvalid, rootsInvalid, }) {
976
- return (_jsxs("div", { className: "dwa-form-row", style: { flexDirection: 'column', gap: 6 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('工作空间根目录', 'Workspace Roots') }), _jsxs("label", { className: "dwa-hint", style: { display: 'inline-flex', alignItems: 'center', gap: 4, cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: form.rootsOpen, onChange: e => patch({ rootsOpen: e.target.checked }), style: { margin: 0 } }), acp.label('白名单根目录(高级)', 'Root allow-list (advanced)')] })] }), _jsxs("div", { className: "dwa-form-col", children: [_jsx("input", { className: "dwa-input", value: form.cwd, onChange: e => patch({ cwd: e.target.value }), placeholder: acp.label('默认 ~/.dsh/dsh-ntr/workspaces', 'Default: ~/.dsh/dsh-ntr/workspaces') }), cwdInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('必须是绝对路径(如 /work 或 C:\\work)', 'Must be an absolute path (e.g. /work or C:\\work)') })), _jsx("span", { className: "dwa-hint", children: acp.label('对话里的 NTR 会话以该 DSH 会话选中的工作区为工作目录(agent 进程也从那里启动);此项只在 /acp 直连会话与宿主未上报工作区时生效。', 'An NTR session inside a conversation runs \u2014 and starts its agent process \u2014 in that DSH session\u2019s own workspace; this value applies to /acp-direct sessions and when the host reports no workspace.') }), !form.rootsOpen && (_jsxs("span", { className: "dwa-hint", children: [acp.label('当前白名单:', 'Current allow-list: '), form.roots.length === 0 ? acp.label('(未设置)', '(unset)') : form.roots.join('、'), form.cwd.trim() === '' ? '' : acp.label(';保存时将收窄为工作空间目录', '; saving narrows it to the workspace directory')] }))] }), form.rootsOpen && (_jsxs("div", { className: "dwa-form-col", style: { gap: 4 }, children: [form.roots.map((root, index) => (_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [_jsx("input", { className: "dwa-input dwa-input-mono", value: root, onChange: e => patch({ roots: form.roots.map((r, i) => (i === index ? e.target.value : r)) }), placeholder: "/absolute/path" }), _jsx("button", { type: "button", className: "dwa-btn", title: acp.label('删除该根目录', 'Remove this root'), onClick: () => patch({ roots: form.roots.filter((_, i) => i !== index) }), children: "\u2715" })] }, index))), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: () => patch({ roots: [...form.roots, ''] }), children: acp.label('添加根目录', 'Add root') }), _jsx("span", { className: "dwa-hint", children: acp.label('留空的工作空间则使用宿主默认目录。', 'An empty workspace falls back to the host default directory.') })] }), rootsInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('至少需要一个绝对路径根目录', 'At least one absolute root is required') }))] })), _jsx("span", { className: "dwa-hint", children: acp.label('白名单之外的路径会被宿主拒绝(cwd_denied);当前 DSH 会话自己的工作区始终被信任。', 'Paths outside the allow-list are refused by the host (cwd_denied); the current DSH session\u2019s own workspace is always trusted.') })] }));
1122
+ return (_jsxs("div", { className: "dwa-form-row", style: { flexDirection: 'column', gap: 6 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('工作空间根目录', 'Workspace Roots') }), _jsxs("label", { className: "dwa-hint", style: { display: 'inline-flex', alignItems: 'center', gap: 4, cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: form.rootsOpen, onChange: e => patch({ rootsOpen: e.target.checked }), style: { margin: 0 } }), acp.label('白名单根目录(高级)', 'Root allow-list (advanced)')] })] }), _jsxs("div", { className: "dwa-form-col", children: [_jsx("input", { className: "dwa-input", value: form.cwd, onChange: e => patch({ cwd: e.target.value }), placeholder: acp.label('默认 ~/.dsh/dsh-ntr/workspaces', 'Default: ~/.dsh/dsh-ntr/workspaces') }), cwdInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('必须是绝对路径(如 /work 或 C:\\work)', 'Must be an absolute path (e.g. /work or C:\\work)') })), _jsx("span", { className: "dwa-hint", children: acp.label('聊天使用会话工作区;此处用于直连会话或宿主未提供工作区时。', 'Chats use their session workspace; this applies to direct sessions or when the host provides no workspace.') }), !form.rootsOpen && (_jsxs("span", { className: "dwa-hint", children: [acp.label('当前白名单:', 'Current allow-list: '), form.roots.length === 0 ? acp.label('(未设置)', '(unset)') : form.roots.join('、'), form.cwd.trim() === '' ? '' : acp.label(';保存时将收窄为工作空间目录', '; saving narrows it to the workspace directory')] }))] }), form.rootsOpen && (_jsxs("div", { className: "dwa-form-col", style: { gap: 4 }, children: [form.roots.map((root, index) => (_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [_jsx("input", { className: "dwa-input dwa-input-mono", value: root, onChange: e => patch({ roots: form.roots.map((r, i) => (i === index ? e.target.value : r)) }), placeholder: "/absolute/path" }), _jsx("button", { type: "button", className: "dwa-btn", title: acp.label('删除该根目录', 'Remove this root'), onClick: () => patch({ roots: form.roots.filter((_, i) => i !== index) }), children: "\u2715" })] }, index))), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", onClick: () => patch({ roots: [...form.roots, ''] }), children: acp.label('添加根目录', 'Add root') }), _jsx("span", { className: "dwa-hint", children: acp.label('留空的工作空间则使用宿主默认目录。', 'An empty workspace falls back to the host default directory.') })] }), rootsInvalid && (_jsx("span", { className: "dwa-error", children: acp.label('至少需要一个绝对路径根目录', 'At least one absolute root is required') }))] })), _jsx("span", { className: "dwa-hint", children: acp.label('白名单之外的路径会被宿主拒绝(cwd_denied);当前 DSH 会话自己的工作区始终被信任。', 'Paths outside the allow-list are refused by the host (cwd_denied); the current DSH session\u2019s own workspace is always trusted.') })] }));
977
1123
  }
978
1124
  /** Row overflow menu: the dangerous actions, plus the inline remove confirm. */
979
1125
  function RowMenu({ acp, canStop, onStop, onRemove, }) {
@@ -1221,7 +1367,7 @@ function ModelCatalogBlock({ agentId, detected, acp, active, exposed, onExposedC
1221
1367
  : acp.label('该 Agent 未暴露模型选择器(category: model)。', 'This agent exposes no model selector (category: model).');
1222
1368
  return (_jsxs("div", { children: [!isDefault && (_jsx("div", { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }, children: _jsx("button", { className: "dwa-btn", onClick: () => void acp.setDefaultAgent(agentId), children: acp.label('设为默认智能体', 'Set as default agent') }) })), isDefault && (
1223
1369
  // This default belongs to the plugin, not the deployment model route.
1224
- _jsx("div", { className: "dwa-hint", style: { marginTop: 8 }, children: acp.label('当前为默认智能体:新的 NTR 会话由它服务,不影响其他模式的默认模型。', "This is the default agent for new NTR sessions; other modes' default models are untouched.") })), _jsxs("div", { className: "dwa-inline-setting", style: { marginTop: 12 }, children: [_jsxs("label", { className: "dwa-switch", title: acp.label('Agent 新发布的模型会自动加入右侧已选列表;首次检测到的目录只作为基线,不会一次性全选。', 'Models the agent newly publishes are added to the selected list; the first observed catalog is only a baseline and is not selected wholesale.'), children: [_jsx("input", { type: "checkbox", checked: autoDiscover, onChange: e => void toggleAutoDiscover(e.target.checked), style: { margin: 0, accentColor: 'var(--dsw-alias-state-business-primary, #2563eb)' } }), acp.label('自动发现新模型', 'Auto-discover new models')] }), _jsx("span", { className: "dwa-setting-copy", children: acp.label('新增模型自动加入已选列表,现有勾选不受影响。', 'New models join the selected list automatically; existing picks are untouched.') })] }), saveError !== undefined && _jsx("div", { className: "dwa-error", style: { marginTop: 4 }, children: saveError }), probeError !== undefined && (_jsxs("div", { className: "dwa-error", style: { marginTop: 8, wordBreak: 'break-word' }, children: ["\u26A0 ", acp.label('模型检测失败:', 'Model detection failed: '), probeError] })), _jsxs("div", { className: "dwa-model-section", children: [_jsxs("div", { className: "dwa-model-toolbar", children: [_jsxs("span", { className: "dwa-form-label", children: [acp.label('对话中可见的模型', 'Models offered in the conversation'), _jsx("span", { className: "dwa-hint", style: { marginLeft: 6, fontWeight: 400 }, title: acp.label('NTR 模式下的对话只列出这里勾选的模型;未勾选的仍可用——正在生效的那个也会以「· 当前」列出来,其余不列出。', 'The NTR surfaces only list what is checked here; unchecked models still work, and the one currently in force is still listed with a "· current" row — the rest are not offered.'), children: acp.label('(NTR 座位 / 芯片的下拉列表)', '(the NTR seat and chip pickers)') })] }), _jsx("span", { className: "dwa-badge dwa-badge-info", children: selectionBadge }), catalogStatus !== undefined && _jsx("span", { className: "dwa-hint dwa-model-status", children: catalogStatus }), _jsxs("div", { className: "dwa-model-toolbar-actions", children: [_jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", disabled: probing, onClick: () => void detect(), title: acp.label('重新读取 Agent 的模型目录:新增的加入左侧,已下架的从左侧移除;已选列表不受影响。', 'Re-read the agent catalog: newly published models join the left list, withdrawn ones drop out. The selection is untouched.'), children: probing ? acp.label('模型检测中...', 'Probing models...') : acp.label('检测可用模型', 'Detect available models') }), _jsx("input", { className: "dwa-input dwa-model-search", value: search, onChange: e => setSearch(e.target.value), placeholder: acp.label('搜索模型…', 'Search models…') })] })] }), _jsxs("div", { className: "dwa-model-transfer", children: [_jsxs("div", { className: `dwa-model-pane${allCollapsed ? ' is-collapsed' : ''}`, children: [_jsxs("div", { className: "dwa-model-pane-head", children: [_jsx("span", { children: acp.label('全部模型', 'All models') }), result !== undefined && _jsx("span", { className: "dwa-badge", children: modelChoices.length }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", disabled: visibleChoices.length === 0, onClick: addVisible, title: acp.label('把当前搜索范围内的模型全部加入右侧', 'Add every model matching the current search'), children: acp.label('全部加入', 'Add all') }), _jsx("button", { type: "button", className: "dwa-caret-btn", "aria-expanded": !allCollapsed, onClick: () => setAllCollapsed(value => !value), title: allCollapsed ? acp.label('展开全部模型', 'Expand all models') : acp.label('收起全部模型', 'Collapse all models'), children: _jsx("span", { className: "dwa-caret", "aria-hidden": true, children: allCollapsed ? '▸' : '▾' }) })] }), _jsxs("div", { className: "dwa-model-list", children: [visibleChoices.map(choice => {
1370
+ _jsx("div", { className: "dwa-hint", style: { marginTop: 8 }, children: acp.label('当前为默认智能体:新的 NTR 会话由它服务,不影响其他模式的默认模型。', "This is the default agent for new NTR sessions; other modes' default models are untouched.") })), _jsxs("div", { className: "dwa-inline-setting", style: { marginTop: 12 }, children: [_jsxs("label", { className: "dwa-switch", title: acp.label('Agent 新发布的模型会自动加入右侧已选列表;首次检测到的目录只作为基线,不会一次性全选。', 'Models the agent newly publishes are added to the selected list; the first observed catalog is only a baseline and is not selected wholesale.'), children: [_jsx("input", { type: "checkbox", checked: autoDiscover, onChange: e => void toggleAutoDiscover(e.target.checked), style: { margin: 0, accentColor: 'var(--dsw-alias-state-business-primary, #2563eb)' } }), acp.label('自动发现新模型', 'Auto-discover new models')] }), _jsx("span", { className: "dwa-setting-copy", children: acp.label('新增模型自动加入已选列表,现有勾选不受影响。', 'New models join the selected list automatically; existing picks are untouched.') })] }), saveError !== undefined && _jsx("div", { className: "dwa-error", style: { marginTop: 4 }, children: saveError }), probeError !== undefined && (_jsxs("div", { className: "dwa-error", style: { marginTop: 8, wordBreak: 'break-word' }, children: ["\u26A0 ", acp.label('模型检测失败:', 'Model detection failed: '), probeError] })), _jsxs("div", { className: "dwa-model-section", children: [_jsxs("div", { className: "dwa-model-toolbar", children: [_jsxs("span", { className: "dwa-form-label", children: [acp.label('对话中可见的模型', 'Models offered in the conversation'), _jsx("span", { className: "dwa-hint", style: { marginLeft: 6, fontWeight: 400 }, title: acp.label('NTR 模式下的对话只列出这里勾选的模型;未勾选的仍可用——正在生效的那个也会以「· 当前」列出来,其余不列出。', 'The NTR surfaces only list what is checked here; unchecked models still work, and the one currently in force is still listed with a "· current" row — the rest are not offered.'), children: acp.label('仅影响菜单可见性', 'Menu visibility only') })] }), _jsx("span", { className: "dwa-badge dwa-badge-info", children: selectionBadge }), catalogStatus !== undefined && _jsx("span", { className: "dwa-hint dwa-model-status", children: catalogStatus }), _jsxs("div", { className: "dwa-model-toolbar-actions", children: [_jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", disabled: probing, onClick: () => void detect(), title: acp.label('重新读取 Agent 的模型目录:新增的加入左侧,已下架的从左侧移除;已选列表不受影响。', 'Re-read the agent catalog: newly published models join the left list, withdrawn ones drop out. The selection is untouched.'), children: probing ? acp.label('模型检测中...', 'Probing models...') : acp.label('检测可用模型', 'Detect available models') }), _jsx("input", { className: "dwa-input dwa-model-search", "aria-label": acp.label('搜索模型', 'Search models'), value: search, onChange: e => setSearch(e.target.value), placeholder: acp.label('搜索模型…', 'Search models…') })] })] }), _jsxs("div", { className: "dwa-model-transfer", children: [_jsxs("div", { className: `dwa-model-pane${allCollapsed ? ' is-collapsed' : ''}`, children: [_jsxs("div", { className: "dwa-model-pane-head", children: [_jsx("span", { children: acp.label('全部模型', 'All models') }), result !== undefined && _jsx("span", { className: "dwa-badge", children: modelChoices.length }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", disabled: visibleChoices.length === 0, onClick: addVisible, title: acp.label('把当前搜索范围内的模型全部加入右侧', 'Add every model matching the current search'), children: acp.label('全部加入', 'Add all') }), _jsx("button", { type: "button", className: "dwa-caret-btn", "aria-expanded": !allCollapsed, onClick: () => setAllCollapsed(value => !value), title: allCollapsed ? acp.label('展开全部模型', 'Expand all models') : acp.label('收起全部模型', 'Collapse all models'), children: _jsx("span", { className: "dwa-caret", "aria-hidden": true, children: allCollapsed ? '▸' : '▾' }) })] }), _jsxs("div", { className: "dwa-model-list", children: [visibleChoices.map(choice => {
1225
1371
  const checked = selected.has(choice.value);
1226
1372
  // group 前缀仅来自 agent 返回的 SessionConfigSelectGroup(见 impl.ts
1227
1373
  // pushCatalogChoice);供应商可见性兜底靠原样展示 value,严禁从
@@ -1230,5 +1376,5 @@ function ModelCatalogBlock({ agentId, detected, acp, active, exposed, onExposedC
1230
1376
  ? choice.value
1231
1377
  : `${choice.value}\n${choice.description}`;
1232
1378
  return (_jsxs("button", { type: "button", className: `dwa-model-row${checked ? ' is-on' : ''}`, title: title, onClick: () => toggleChoice(choice.value), onPointerEnter: event => { armModelIdMarquee(event.currentTarget); }, children: [_jsx("span", { className: "dwa-model-row-mark", children: checked ? '✓' : '+' }), _jsxs("span", { className: "dwa-model-row-name", children: [choice.group !== undefined && _jsxs("span", { style: { opacity: 0.7 }, children: [choice.group, " \u00B7 "] }), choice.name] }), choice.value !== choice.name && (_jsx("span", { className: "dwa-model-row-value", children: _jsx("span", { className: "dwa-model-row-value-text", children: choice.value }) })), choice.description !== undefined && choice.description !== choice.group && (_jsx("span", { className: "dwa-model-row-desc", children: choice.description }))] }, choice.value));
1233
- }), leftEmptyCopy !== undefined && _jsx("span", { className: "dwa-hint dwa-model-empty", children: leftEmptyCopy })] })] }), _jsxs("div", { className: `dwa-model-pane${selectedCollapsed ? ' is-collapsed' : ''}`, children: [_jsxs("div", { className: "dwa-model-pane-head", children: [_jsx("span", { children: acp.label('已选模型', 'Selected models') }), _jsx("span", { className: "dwa-badge dwa-badge-info", children: basis.length }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", disabled: basis.length === 0, onClick: clearAll, children: acp.label('清空', 'Clear') }), _jsx("button", { type: "button", className: "dwa-caret-btn", "aria-expanded": !selectedCollapsed, onClick: () => setSelectedCollapsed(value => !value), title: selectedCollapsed ? acp.label('展开已选模型', 'Expand selected models') : acp.label('收起已选模型', 'Collapse selected models'), children: _jsx("span", { className: "dwa-caret", "aria-hidden": true, children: selectedCollapsed ? '▸' : '▾' }) })] }), _jsxs("div", { className: "dwa-model-list", children: [selectedChoices.length === 0 && (_jsx("span", { className: "dwa-hint dwa-model-empty", children: acp.label('还没有选择模型(点左侧的模型加入)', 'Nothing selected yet — pick models on the left') })), selectedChoices.map(choice => (_jsxs("button", { type: "button", className: "dwa-model-row is-on", title: choice.description === undefined ? choice.value : `${choice.value}\n${choice.description}`, onClick: () => toggleChoice(choice.value), onPointerEnter: event => { armModelIdMarquee(event.currentTarget); }, children: [_jsxs("span", { className: "dwa-model-row-name", children: [choice.group !== undefined && _jsxs("span", { style: { opacity: 0.7 }, children: [choice.group, " \u00B7 "] }), choice.name] }), choice.value !== choice.name && (_jsx("span", { className: "dwa-model-row-value", children: _jsx("span", { className: "dwa-model-row-value-text", children: choice.value }) })), choice.description !== undefined && choice.description !== choice.group && (_jsx("span", { className: "dwa-model-row-desc", children: choice.description })), _jsx("span", { className: "dwa-model-row-remove", "aria-hidden": "true", children: "\u00D7" })] }, choice.value)))] })] })] }), result?.catalog.thought !== undefined && (_jsxs("div", { style: { display: 'flex', gap: 4, flexWrap: 'wrap', alignItems: 'center' }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('思考等级', 'Thought Levels') }), result.catalog.thought.choices.map(choice => (_jsx("span", { className: "dwa-badge", title: choice.description, children: choice.name }, choice.value)))] }))] })] }));
1379
+ }), leftEmptyCopy !== undefined && _jsx("span", { className: "dwa-hint dwa-model-empty", children: leftEmptyCopy })] })] }), _jsxs("div", { className: `dwa-model-pane${selectedCollapsed ? ' is-collapsed' : ''}`, children: [_jsxs("div", { className: "dwa-model-pane-head", children: [_jsx("span", { children: acp.label('已选模型', 'Selected models') }), _jsx("span", { className: "dwa-badge dwa-badge-info", children: basis.length }), _jsx("button", { type: "button", className: "dwa-btn dwa-btn-secondary", disabled: basis.length === 0, onClick: clearAll, children: acp.label('清空', 'Clear') }), _jsx("button", { type: "button", className: "dwa-caret-btn", "aria-expanded": !selectedCollapsed, onClick: () => setSelectedCollapsed(value => !value), title: selectedCollapsed ? acp.label('展开已选模型', 'Expand selected models') : acp.label('收起已选模型', 'Collapse selected models'), children: _jsx("span", { className: "dwa-caret", "aria-hidden": true, children: selectedCollapsed ? '▸' : '▾' }) })] }), _jsxs("div", { className: "dwa-model-list", children: [selectedChoices.length === 0 && (_jsx("span", { className: "dwa-hint dwa-model-empty", children: acp.label('尚未选择模型,从「全部模型」中加入。', 'No models selected. Add from All models.') })), selectedChoices.map(choice => (_jsxs("button", { type: "button", className: "dwa-model-row is-on", title: choice.description === undefined ? choice.value : `${choice.value}\n${choice.description}`, onClick: () => toggleChoice(choice.value), onPointerEnter: event => { armModelIdMarquee(event.currentTarget); }, children: [_jsxs("span", { className: "dwa-model-row-name", children: [choice.group !== undefined && _jsxs("span", { style: { opacity: 0.7 }, children: [choice.group, " \u00B7 "] }), choice.name] }), choice.value !== choice.name && (_jsx("span", { className: "dwa-model-row-value", children: _jsx("span", { className: "dwa-model-row-value-text", children: choice.value }) })), choice.description !== undefined && choice.description !== choice.group && (_jsx("span", { className: "dwa-model-row-desc", children: choice.description })), _jsx("span", { className: "dwa-model-row-remove", "aria-hidden": "true", children: "\u00D7" })] }, choice.value)))] })] })] }), result?.catalog.thought !== undefined && (_jsxs("div", { style: { display: 'flex', gap: 4, flexWrap: 'wrap', alignItems: 'center' }, children: [_jsx("span", { className: "dwa-form-label", children: acp.label('思考等级', 'Thought Levels') }), result.catalog.thought.choices.map(choice => (_jsx("span", { className: "dwa-badge", title: choice.description, children: choice.name }, choice.value)))] }))] })] }));
1234
1380
  }