dsh-ntr 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +6 -3
- package/README.md +6 -3
- package/cordis.patch.yml +18 -18
- package/lib/bridge.js +134 -314
- package/lib/client/acp-ui.js +60 -32
- package/lib/client/index.js +17 -0
- package/lib/client/panel.js +127 -114
- package/lib/client/permission-dock.js +72 -0
- package/lib/client/session-controls.js +4 -4
- package/lib/client/store.js +58 -12
- package/lib/client.js +696 -393
- package/lib/client.js.map +1 -1
- package/lib/impl.js +30 -19
- package/lib/index.js +6 -51
- package/lib/types/bridge.d.ts +63 -157
- package/lib/types/client/panel.d.ts +12 -20
- package/lib/types/client/permission-dock.d.ts +31 -0
- package/lib/types/client/store.d.ts +42 -12
- package/lib/types/impl.d.ts +31 -17
- package/lib/types/protocol.d.ts +27 -12
- package/package.json +5 -1
- package/src/bridge.ts +163 -379
- package/src/client/acp-ui.tsx +60 -32
- package/src/client/index.tsx +18 -0
- package/src/client/panel.tsx +243 -258
- package/src/client/permission-dock.tsx +133 -0
- package/src/client/session-controls.tsx +4 -4
- package/src/client/store.ts +81 -25
- package/src/impl.ts +49 -29
- package/src/index.ts +6 -65
- package/src/protocol.ts +32 -12
package/lib/client/panel.js
CHANGED
|
@@ -3,11 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
* React surface for ACP: the settings configuration section.
|
|
4
4
|
*
|
|
5
5
|
* Scope is deliberately narrow — this is a CONFIGURATION surface. Runtime
|
|
6
|
-
* interaction lives in the conversation: permission
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* interaction lives in the conversation: every permission ask with a chat
|
|
7
|
+
* identity is answered on a card above that conversation's composer
|
|
8
|
+
* (`permission-dock.tsx`). The ledger card here is the cross-session mirror —
|
|
9
|
+
* asks whose conversation is not open, or which have no chat identity at all —
|
|
10
|
+
* and is where those are answered (see `permissionLedgerCard`).
|
|
11
11
|
*
|
|
12
12
|
* Each configured agent is one compact row (status + current model + badges)
|
|
13
13
|
* that expands into the full connection form and model catalog. The row stays
|
|
@@ -42,22 +42,40 @@ const STYLE = `
|
|
|
42
42
|
--ntr-radius: 10px; --ntr-radius-sm: 7px;
|
|
43
43
|
--ntr-font: 13px; --ntr-font-sm: 12px; --ntr-font-xs: 11px;
|
|
44
44
|
--ntr-line: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
|
|
45
|
-
--ntr-muted: var(--dsw-alias-
|
|
46
|
-
--ntr-danger:
|
|
45
|
+
--ntr-muted: var(--dsw-alias-label-secondary, #6b7280);
|
|
46
|
+
--ntr-danger: var(--dsw-alias-state-error-primary, #dc2626);
|
|
47
|
+
--ntr-warn: var(--dsw-alias-state-warn-label, #b45309);
|
|
48
|
+
--ntr-success: color-mix(in srgb, var(--dsw-alias-state-success-primary, #16a34a) 72%, var(--dsw-alias-label-primary, #111827));
|
|
47
49
|
--ntr-accent: var(--dsw-alias-button-primary-fill, #2563eb);
|
|
48
50
|
width: 100%; max-width: 960px; box-sizing: border-box; margin: 0 auto;
|
|
49
|
-
padding:
|
|
51
|
+
padding: 28px 32px; min-width: 0; container-type: inline-size; font-size: var(--ntr-font); line-height: 1.55;
|
|
50
52
|
}
|
|
51
|
-
.dwa-root
|
|
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; }
|
|
52
72
|
.dwa-root button:focus-visible, .dwa-root input:focus-visible, .dwa-root select:focus-visible {
|
|
53
73
|
outline: 2px solid var(--ntr-accent); outline-offset: 2px;
|
|
54
74
|
}
|
|
55
|
-
.dwa-page-header { display: flex; align-items:
|
|
75
|
+
.dwa-page-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 28px; }
|
|
56
76
|
.dwa-page-heading { min-width: 0; }
|
|
57
|
-
.dwa-page-title { margin: 0; font-size: 20px; line-height: 1.35; font-weight:
|
|
77
|
+
.dwa-page-title { margin: 0; font-size: 20px; line-height: 1.35; font-weight: 550; letter-spacing: -0.02em; }
|
|
58
78
|
.dwa-page-description { margin: 5px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-sm); }
|
|
59
|
-
.dwa-page-status { display: flex; align-items: center; gap: 8px; margin-top: 10px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
60
|
-
.dwa-page-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
|
61
79
|
.dwa-btn {
|
|
62
80
|
min-height: 32px; box-sizing: border-box; font: inherit; border-radius: var(--ntr-radius-sm);
|
|
63
81
|
border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-1, #fff);
|
|
@@ -67,58 +85,58 @@ const STYLE = `
|
|
|
67
85
|
}
|
|
68
86
|
.dwa-btn:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); border-color: var(--dsw-alias-border-l1, #9ca3af); }
|
|
69
87
|
.dwa-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
70
|
-
.dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-
|
|
88
|
+
.dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); border-color: transparent; font-weight: 550; }
|
|
71
89
|
.dwa-btn-primary:hover { background: var(--ntr-accent); border-color: transparent; opacity: 0.88; }
|
|
72
90
|
.dwa-btn-secondary { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
|
|
73
|
-
.dwa-btn-
|
|
74
|
-
.dwa-btn-
|
|
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); }
|
|
93
|
+
.dwa-btn-danger { color: var(--ntr-danger); border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }
|
|
94
|
+
.dwa-btn-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, #fee2e2); }
|
|
75
95
|
.dwa-section { margin-bottom: 24px; }
|
|
76
96
|
.dwa-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
|
|
77
|
-
.dwa-section-title { font-size:
|
|
97
|
+
.dwa-section-title { font-size: 13px; font-weight: 550; margin: 0; display: flex; align-items: center; gap: 8px; }
|
|
78
98
|
.dwa-section-copy { margin: 3px 0 0; font-size: var(--ntr-font-xs); color: var(--ntr-muted); }
|
|
79
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; }
|
|
80
|
-
.dwa-card-warn { border-
|
|
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); }
|
|
81
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; }
|
|
82
102
|
.dwa-mode-title { display: flex; align-items: center; gap: 8px; font-weight: 650; }
|
|
83
103
|
.dwa-mode-copy { grid-column: 1 / -1; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
84
104
|
.dwa-mode-source { display: block; margin-top: 5px; overflow-wrap: anywhere; opacity: 0.85; }
|
|
85
105
|
.dwa-badge { font-size: var(--ntr-font-xs); line-height: 1.4; border-radius: 999px; padding: 2px 8px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font-weight: 550; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
|
|
86
|
-
.dwa-badge-success { background: #
|
|
87
|
-
.dwa-badge-warn { background: #
|
|
88
|
-
.dwa-badge-info { background: #
|
|
106
|
+
.dwa-badge-success { background: var(--dsw-alias-state-success-tertiary, #dcfce7); color: var(--ntr-success); }
|
|
107
|
+
.dwa-badge-warn { background: var(--dsw-alias-state-warn-tertiary, #fef3c7); color: var(--ntr-warn); }
|
|
108
|
+
.dwa-badge-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
89
109
|
.dwa-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
|
|
90
|
-
.dwa-agent-list { display: flex; flex-direction: column;
|
|
91
|
-
.dwa-agent-card { border: var(--ntr-line);
|
|
92
|
-
.dwa-agent-
|
|
93
|
-
.dwa-agent-
|
|
110
|
+
.dwa-agent-list { display: flex; flex-direction: column; border-top: var(--ntr-line); }
|
|
111
|
+
.dwa-agent-card { border-bottom: var(--ntr-line); min-width: 0; }
|
|
112
|
+
.dwa-agent-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; align-items: center; gap: 14px; padding: 16px 0; }
|
|
113
|
+
.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); }
|
|
94
114
|
.dwa-caret { font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; width: 1em; }
|
|
95
115
|
.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; }
|
|
96
116
|
.dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); color: inherit; }
|
|
97
117
|
.dwa-agent-summary { min-width: 0; }
|
|
98
118
|
.dwa-agent-titleline { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
|
|
99
|
-
.dwa-agent-name { font-size: 13px; font-weight:
|
|
119
|
+
.dwa-agent-name { font-size: 13px; font-weight: 550; overflow-wrap: anywhere; }
|
|
100
120
|
.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); }
|
|
101
121
|
.dwa-agent-meta-separator { opacity: 0.45; }
|
|
102
|
-
.dwa-agent-body {
|
|
103
|
-
.dwa-subsection {
|
|
104
|
-
.dwa-
|
|
105
|
-
.dwa-subsection-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
|
|
106
|
-
.dwa-subsection-title { margin: 0; font-size: 13px; font-weight: 650; }
|
|
107
|
-
.dwa-subsection-copy { margin: 3px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
108
|
-
.dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: flex-start; }
|
|
122
|
+
.dwa-agent-body { padding: 0 0 24px 52px; }
|
|
123
|
+
.dwa-subsection { min-width: 0; }
|
|
124
|
+
.dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: stretch; }
|
|
109
125
|
.dwa-form-col { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
110
|
-
.dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-
|
|
126
|
+
.dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-label-primary, #374151); }
|
|
111
127
|
.dwa-hint { font-size: var(--ntr-font-xs); line-height: 1.55; color: var(--ntr-muted); }
|
|
112
128
|
.dwa-error { font-size: var(--ntr-font-xs); color: var(--ntr-danger); }
|
|
113
129
|
.dwa-input { min-height: 36px; font: inherit; font-size: var(--ntr-font-sm); padding: 7px 10px; 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; width: 100%; box-sizing: border-box; }
|
|
114
130
|
.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; }
|
|
115
131
|
.dwa-input:hover, .dwa-select:hover { border-color: var(--dsw-alias-border-l1, #9ca3af); }
|
|
116
132
|
.dwa-input:focus, .dwa-select:focus { border-color: var(--ntr-accent); }
|
|
117
|
-
.dwa-input-mono, .dwa-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
133
|
+
.dwa-input-mono, .dwa-mono { font-family: var(--ds-font-family-code, ui-monospace), SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
118
134
|
.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; }
|
|
119
135
|
.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; }
|
|
120
136
|
.dwa-form-actions { display: flex; align-items: center; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: var(--ntr-line); flex-wrap: wrap; }
|
|
121
137
|
.dwa-form-actions .dwa-btn-primary { min-width: 88px; }
|
|
138
|
+
.dwa-form-actions > .dwa-hint:first-child { margin-right: auto; }
|
|
139
|
+
.dwa-form-actions > .dwa-hint:last-child:not(:first-child) { width: 100%; }
|
|
122
140
|
.dwa-menu-wrap { position: relative; }
|
|
123
141
|
.dwa-menu-wrap > .dwa-btn { width: 32px; padding: 0; }
|
|
124
142
|
.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; }
|
|
@@ -126,13 +144,13 @@ const STYLE = `
|
|
|
126
144
|
.dwa-menu-item:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
127
145
|
.dwa-menu-item-danger { color: var(--ntr-danger); }
|
|
128
146
|
.dwa-menu-confirm { display: flex; align-items: center; gap: 4px; padding: 4px; }
|
|
129
|
-
.dwa-add-panel
|
|
130
|
-
.dwa-add-
|
|
131
|
-
.dwa-add-
|
|
132
|
-
.dwa-add-content {
|
|
147
|
+
.dwa-add-panel .dwa-agent-row { display: none; }
|
|
148
|
+
.dwa-add-panel .dwa-agent-body { padding: 0 0 24px; }
|
|
149
|
+
.dwa-add-panel .dwa-agent-card { border: 0; }
|
|
150
|
+
.dwa-add-content { padding-top: 10px; }
|
|
133
151
|
.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; }
|
|
134
152
|
.dwa-chip:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
135
|
-
.dwa-chip-active { border-color: var(--ntr-accent); background: #
|
|
153
|
+
.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; }
|
|
136
154
|
.dwa-chip-list { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }
|
|
137
155
|
.dwa-switch { display: inline-flex; align-items: center; gap: 7px; font-size: var(--ntr-font-sm); cursor: pointer; user-select: none; }
|
|
138
156
|
.dwa-inline-setting { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-2, #f9fafb); }
|
|
@@ -145,7 +163,7 @@ const STYLE = `
|
|
|
145
163
|
/* Tall enough for a comfortable selection (about ten rows) before scrolling;
|
|
146
164
|
the cap keeps the two stacked panes from pushing the rest of the card away. */
|
|
147
165
|
.dwa-model-pane { min-width: 0; max-height: 360px; display: flex; flex-direction: column; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }
|
|
148
|
-
.dwa-model-pane-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; border-bottom: var(--ntr-line); font-size: var(--ntr-font-xs); font-weight: 600; color: var(--dsw-alias-
|
|
166
|
+
.dwa-model-pane-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; border-bottom: var(--ntr-line); font-size: var(--ntr-font-xs); font-weight: 600; color: var(--dsw-alias-label-secondary, #4b5563); }
|
|
149
167
|
.dwa-model-pane-head .dwa-btn { margin-left: auto; min-height: 26px; padding: 2px 8px; font-size: var(--ntr-font-xs); }
|
|
150
168
|
.dwa-model-pane-head .dwa-caret-btn { flex: none; width: 22px; height: 22px; background: transparent; }
|
|
151
169
|
.dwa-model-pane-head .dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); }
|
|
@@ -161,13 +179,13 @@ const STYLE = `
|
|
|
161
179
|
.dwa-model-status { margin-left: auto; white-space: nowrap; }
|
|
162
180
|
.dwa-model-search { width: 200px; min-height: 30px; }
|
|
163
181
|
.dwa-model-empty { padding: 6px; }
|
|
164
|
-
.dwa-model-row { display: flex; align-items: center; gap:
|
|
182
|
+
.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; }
|
|
165
183
|
.dwa-model-row:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
166
|
-
.dwa-model-row.is-on { background: #
|
|
184
|
+
.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); }
|
|
167
185
|
.dwa-model-row-mark { width: 12px; flex: none; text-align: center; opacity: 0.75; }
|
|
168
186
|
.dwa-model-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
169
187
|
/* Name and credit keep their room; the id is the only cell that collapses. */
|
|
170
|
-
.dwa-model-row-desc { flex: 0 0 auto; max-width: 45%; opacity: 0.75; font-size:
|
|
188
|
+
.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; }
|
|
171
189
|
/* The trailing 14px is reserved for the fade mask: while the id fits, the
|
|
172
190
|
gradient only covers empty padding; once it overflows, its tail dissolves
|
|
173
191
|
instead of ending in a hard ellipsis. armModelIdMarquee reads the same
|
|
@@ -181,12 +199,12 @@ const STYLE = `
|
|
|
181
199
|
}
|
|
182
200
|
.dwa-model-row-remove { flex: none; opacity: 0.5; padding: 0 2px; }
|
|
183
201
|
.dwa-model-row:hover .dwa-model-row-remove { opacity: 1; }
|
|
184
|
-
.dwa-seg { display: inline-flex; border-radius: var(--ntr-radius-sm); border:
|
|
185
|
-
.dwa-seg > button { min-height:
|
|
186
|
-
.dwa-seg > button + button { border-left: 1px solid var(--dsw-alias-border-l2, #d1d5db); }
|
|
202
|
+
.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; }
|
|
203
|
+
.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; }
|
|
187
204
|
.dwa-seg > button:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
188
|
-
.dwa-seg > button.is-on { background: var(--ntr-
|
|
189
|
-
.dwa-
|
|
205
|
+
.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; }
|
|
206
|
+
.dwa-preferences { border-top: var(--ntr-line); }
|
|
207
|
+
.dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 0; }
|
|
190
208
|
.dwa-preference-row + .dwa-preference-row { border-top: var(--ntr-line); }
|
|
191
209
|
/* A control that needs the whole card: the label sits above it, not beside
|
|
192
210
|
it. A right-aligned segmented control squeezed its labels into two lines
|
|
@@ -211,13 +229,20 @@ const STYLE = `
|
|
|
211
229
|
.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; }
|
|
212
230
|
.dwa-deadline-reset { display: flex; justify-content: flex-end; margin-top: 8px; }
|
|
213
231
|
@media (max-width: 640px) {
|
|
214
|
-
.dwa-root { padding:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
.dwa-page-
|
|
232
|
+
.dwa-root { padding: 20px 16px; }
|
|
233
|
+
}
|
|
234
|
+
@container (max-width: 640px) {
|
|
235
|
+
.dwa-page-header { align-items: flex-start; flex-wrap: wrap; }
|
|
236
|
+
.dwa-agent-body { padding-left: 0; }
|
|
237
|
+
.dwa-agent-row { grid-template-columns: 32px minmax(0, 1fr) auto; gap: 10px; }
|
|
238
|
+
.dwa-agent-avatar { width: 32px; height: 32px; }
|
|
239
|
+
.dwa-section-heading { flex-wrap: wrap; }
|
|
240
|
+
.dwa-tabs { gap: 20px; }
|
|
241
|
+
.dwa-notice { flex-wrap: wrap; }
|
|
242
|
+
.dwa-seg-wide > button { padding-inline: 5px; }
|
|
218
243
|
.dwa-model-toolbar-actions { width: 100%; }
|
|
219
244
|
.dwa-model-search { flex: 1 1 auto; width: auto; }
|
|
220
|
-
.dwa-
|
|
245
|
+
.dwa-preference-row { align-items: flex-start; flex-direction: column; }
|
|
221
246
|
.dwa-default-hint { align-items: flex-start; flex-direction: column; }
|
|
222
247
|
/* The slider's scale: up to eight rungs with full unit words ("15 分钟")
|
|
223
248
|
need a smaller face before they can collide on a narrow panel. The rung
|
|
@@ -244,9 +269,9 @@ function useUi(acp) {
|
|
|
244
269
|
}
|
|
245
270
|
function stateVisual(state, acp) {
|
|
246
271
|
switch (state) {
|
|
247
|
-
case 'ready': return { color: '#22c55e', text: acp.label('已连接', 'Connected') };
|
|
248
|
-
case 'starting': return { color: '#eab308', text: acp.label('启动中', 'Starting') };
|
|
249
|
-
case 'error': return { color: '#ef4444', text: acp.label('错误', 'Error') };
|
|
272
|
+
case 'ready': return { color: 'var(--dsw-alias-state-success-primary, #22c55e)', text: acp.label('已连接', 'Connected') };
|
|
273
|
+
case 'starting': return { color: 'var(--dsw-alias-state-warn-primary, #eab308)', text: acp.label('启动中', 'Starting') };
|
|
274
|
+
case 'error': return { color: 'var(--dsw-alias-state-error-primary, #ef4444)', text: acp.label('错误', 'Error') };
|
|
250
275
|
// A configured-but-resting agent is the normal state, not a fault: the
|
|
251
276
|
// process is not kept resident and connects on demand the first time a DSH
|
|
252
277
|
// chat turn uses it. Surfacing a runtime lifecycle here ("stopped"/"idle")
|
|
@@ -254,7 +279,7 @@ function stateVisual(state, acp) {
|
|
|
254
279
|
// which the config-time 测试连接 (agent/test) already answers. So the
|
|
255
280
|
// resting state just says 已配置.
|
|
256
281
|
default: return {
|
|
257
|
-
color: '#9ca3af',
|
|
282
|
+
color: 'var(--dsw-alias-state-idle-primary, #9ca3af)',
|
|
258
283
|
text: acp.label('已配置', 'Configured'),
|
|
259
284
|
title: acp.label('已配置,进程不常驻。在 DSH 聊天中首次使用时自动连接;展开区可用「测试连接」验证命令能否正常起来。', 'Configured; the process is not kept resident. It connects automatically the first time a DSH chat turn uses it — use “Test connection” in the expanded panel to verify the command starts.'),
|
|
260
285
|
};
|
|
@@ -457,22 +482,15 @@ export function payloadEquals(payload, current) {
|
|
|
457
482
|
/**
|
|
458
483
|
* The settings panel's permission-ledger rule.
|
|
459
484
|
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* (`approvalRouted` → a read-only count, no buttons) hid the only answer
|
|
467
|
-
* entry exactly when the native route had degraded to unavailable, and the
|
|
468
|
-
* ask then hung until its timeout.
|
|
469
|
-
*
|
|
470
|
-
* `fellBackFromConversation` is the case the card has to explain: the host
|
|
471
|
-
* can route asks, and this one still did not make it.
|
|
485
|
+
* Pending asks are shown in the conversation card while that conversation is
|
|
486
|
+
* open; this card is the cross-session mirror for the ones it cannot show —
|
|
487
|
+
* asks belonging to another conversation, and `/acp` RPC-only asks with no
|
|
488
|
+
* chat identity at all. It therefore appears whenever ANY ask is pending: a
|
|
489
|
+
* pending entry with no conversation card would otherwise have no answer
|
|
490
|
+
* surface anywhere.
|
|
472
491
|
*/
|
|
473
492
|
export function permissionLedgerCard(ui) {
|
|
474
|
-
|
|
475
|
-
return { visible, fellBackFromConversation: visible && ui.approvalRouted };
|
|
493
|
+
return { visible: ui.permissions.length > 0 };
|
|
476
494
|
}
|
|
477
495
|
/**
|
|
478
496
|
* The ONE wait window, as the rungs the slider snaps to.
|
|
@@ -692,46 +710,42 @@ function ApprovalDeadlinesCard(props) {
|
|
|
692
710
|
// In `wait` mode there is no deadline at all, so the slider shows the one
|
|
693
711
|
// thing that is true: no deadline.
|
|
694
712
|
const waitMode = deadlines.mode === 'wait';
|
|
695
|
-
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-
|
|
713
|
+
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') }) }))] })] }));
|
|
696
714
|
}
|
|
697
|
-
/** Settings configuration section for ACP agents. */
|
|
698
715
|
export function AcpSettingsSection(props) {
|
|
699
716
|
const acp = props.acp;
|
|
700
717
|
ensureStyle();
|
|
701
718
|
const ui = useUi(acp);
|
|
702
719
|
const ledger = permissionLedgerCard(ui);
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
// selection until the next create resets it.
|
|
706
|
-
const [addOpen, setAddOpen] = useState(true);
|
|
720
|
+
const [page, setPage] = useState('agents');
|
|
721
|
+
const [addOpen, setAddOpen] = useState(false);
|
|
707
722
|
const [addTarget, setAddTarget] = useState(undefined);
|
|
708
|
-
const [createNonce, setCreateNonce] = useState(0);
|
|
709
723
|
const { configured, available } = partitionAgents(ui.detectedAgents, ui.agents);
|
|
710
724
|
const selected = addTarget ?? available[0] ?? NEW_AGENT_SEED;
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
725
|
+
const openAdd = () => {
|
|
726
|
+
setAddOpen(true);
|
|
727
|
+
setPage('add');
|
|
728
|
+
};
|
|
729
|
+
const presetProblem = ui.preset !== undefined && (!ui.preset.installed || ui.preset.problem !== undefined);
|
|
730
|
+
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 settings') }), _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') }))] }), 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
|
|
715
731
|
? acp.label('已加入宿主模式列表,新建会话时选择「NTR 模式」即可使用。', 'Added to the harness roster. Pick “NTR mode” when creating a session.')
|
|
716
|
-
: 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: [
|
|
732
|
+
: 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 }) => {
|
|
717
733
|
const activeAgent = ui.agents.find(a => a.id === info.id);
|
|
718
|
-
return (_jsx(AgentRow, { detected: info, activeAgent: activeAgent, acp: acp, isDefault: ui.defaultAgent === info.id, synthesized: synthesized }, info.id));
|
|
719
|
-
}) })] }),
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
setAddTarget(undefined);
|
|
725
|
-
setCreateNonce(nonce => nonce + 1);
|
|
726
|
-
} }, 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))) })] }))] })] })] }));
|
|
734
|
+
return (_jsx(AgentRow, { detected: info, activeAgent: activeAgent, acp: acp, isDefault: ui.defaultAgent === info.id, synthesized: synthesized, active: page === 'agents' }, info.id));
|
|
735
|
+
}) })] }), _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: () => {
|
|
736
|
+
setAddTarget(undefined);
|
|
737
|
+
setAddOpen(false);
|
|
738
|
+
setPage('agents');
|
|
739
|
+
} }, 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') })] })] }));
|
|
727
740
|
}
|
|
728
741
|
/** One row per agent: compact when collapsed, the full form when expanded. */
|
|
729
|
-
function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, newCustom = false, onCreated, }) {
|
|
742
|
+
function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, active, newCustom = false, onCreated, }) {
|
|
730
743
|
const ui = useUi(acp);
|
|
731
744
|
const configured = activeAgent !== undefined || detected.currentConfig !== undefined;
|
|
732
745
|
// A configured agent starts collapsed; a creation card opens straight away
|
|
733
746
|
// (nothing to summarise yet).
|
|
734
747
|
const [expanded, setExpanded] = useState(!configured);
|
|
748
|
+
const [detailPage, setDetailPage] = useState(configured ? 'models' : 'connection');
|
|
735
749
|
const [form, setForm] = useState(() => initialForm(detected, activeAgent));
|
|
736
750
|
const [saving, setSaving] = useState(false);
|
|
737
751
|
const [savedSuccess, setSavedSuccess] = useState(false);
|
|
@@ -762,7 +776,8 @@ function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, newCusto
|
|
|
762
776
|
const formIncomplete = splitCommandLine(form.commandLine).command === '' || !idValid || idTaken || cwdInvalid || rootsInvalid || idleInvalid || mcpInvalid;
|
|
763
777
|
const payload = buildPayload(form, detected, newCustom);
|
|
764
778
|
const current = persistedDef(detected, activeAgent);
|
|
765
|
-
const
|
|
779
|
+
const clearingIdleOverride = payload.idleTimeoutMs === undefined && detected.currentConfig?.idleTimeoutMs !== undefined;
|
|
780
|
+
const dirty = current !== undefined && (!payloadEquals(payload, current) || clearingIdleOverride);
|
|
766
781
|
// A save stops the child and kills every live session (see BridgeManager.saveAgent).
|
|
767
782
|
const showSaveWarning = dirty && activeAgent !== undefined && activeAgent.state !== 'stopped';
|
|
768
783
|
const handleUseDefault = () => patch({ commandLine: formatCommandLine(detected.defaultCommand, detected.defaultArgs) });
|
|
@@ -815,14 +830,14 @@ function AgentRow({ detected, activeAgent, acp, isDefault, synthesized, newCusto
|
|
|
815
830
|
: (activeAgent?.title ?? detected.currentConfig?.title ?? detected.name);
|
|
816
831
|
const stderr = activeAgent?.state === 'error' ? (activeAgent.stderrTail ?? []) : [];
|
|
817
832
|
const errorSummary = stderr.length > 0 ? stderr[stderr.length - 1] : undefined;
|
|
818
|
-
const
|
|
833
|
+
const selectedModelCount = form.exposed?.length ?? 0;
|
|
819
834
|
const isPreset = PRESET_IDS.has(detected.id);
|
|
820
835
|
// An error row opens itself so the stderr tail is never behind a click.
|
|
821
836
|
const open = expanded || (activeAgent?.state === 'error' && configured);
|
|
822
|
-
return (_jsxs("div", { className: `dwa-agent-card${open ? ' is-open' : ''}`, children: [_jsxs("div", { className: "dwa-agent-row", children: [_jsx("
|
|
837
|
+
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),
|
|
823
838
|
// The store already surfaced the failure as a notice; swallowing it
|
|
824
839
|
// here only prevents an unhandled rejection.
|
|
825
|
-
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') }),
|
|
840
|
+
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() }) })] })] }));
|
|
826
841
|
}
|
|
827
842
|
/**
|
|
828
843
|
* Hand copy of `DEFAULT_TUNING.idleTimeoutMs` (src/bridge.ts): the browser
|
|
@@ -939,9 +954,7 @@ export function buildPayload(form, detected, newCustom) {
|
|
|
939
954
|
return payload;
|
|
940
955
|
}
|
|
941
956
|
/** The expanded body: connection form, workspace allow-list, env, actions. */
|
|
942
|
-
function AgentDetails({ acp, detected, form, patch, isConfigured, newCustom, formIncomplete, idValid, idTaken, cwdInvalid, rootsInvalid, idleInvalid, mcpInvalid, saving, savedSuccess, testing, testResult, showSaveWarning, onUseDefault, onSave, onTest, }) {
|
|
943
|
-
const ui = useUi(acp);
|
|
944
|
-
const [advancedOpen, setAdvancedOpen] = useState(false);
|
|
957
|
+
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, }) {
|
|
945
958
|
// The same parse the host will run (`validateAgentDef` → `splitCommandLine`).
|
|
946
959
|
// Showing it is what turns "the first box swallowed my arguments" into a
|
|
947
960
|
// visible, fixable state instead of a spawn error.
|
|
@@ -950,26 +963,26 @@ function AgentDetails({ acp, detected, form, patch, isConfigured, newCustom, for
|
|
|
950
963
|
? acp.label('填整条命令,例如:codebuddy --acp', 'Type the whole command, e.g. codebuddy --acp')
|
|
951
964
|
: `${acp.label('程序', 'Program')}: ${parsedCommand.command}`
|
|
952
965
|
+ (parsedCommand.args.length === 0 ? '' : ` ${acp.label('参数', 'Arguments')}: ${parsedCommand.args.join(' ')}`);
|
|
953
|
-
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
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
966
|
+
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
|
|
967
|
+
? acp.label('该 ID 已被现有 Agent 占用。', 'This id is already taken by a configured agent.')
|
|
968
|
+
: idValid
|
|
969
|
+
? acp.label('小写字母或数字开头,可用 a-z 0-9 . _ -,最长 64 字符。', 'Starts with a lowercase letter or digit; a-z 0-9 . _ - allowed; max 64 chars.')
|
|
970
|
+
: 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 === ''
|
|
971
|
+
? 'e.g. codebuddy --acp'
|
|
972
|
+
: 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
|
|
960
973
|
? acp.label('必须是 0(永不释放)或 [1000, 86400000] 之间的整数毫秒。', 'Must be 0 (never release) or an integer in [1000, 86400000] ms.')
|
|
961
|
-
: (
|
|
974
|
+
: (_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
|
|
962
975
|
? acp.label('保存中...', 'Saving...')
|
|
963
976
|
: savedSuccess
|
|
964
977
|
? acp.label('✓ 已保存', '✓ Saved')
|
|
965
978
|
: isConfigured
|
|
966
|
-
? acp.label('
|
|
979
|
+
? acp.label('保存更改', 'Save changes')
|
|
967
980
|
: 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: {
|
|
968
981
|
marginTop: 8,
|
|
969
982
|
fontSize: 12,
|
|
970
983
|
whiteSpace: 'pre-wrap',
|
|
971
984
|
wordBreak: 'break-all',
|
|
972
|
-
color: testResult.ok ? '
|
|
985
|
+
color: testResult.ok ? 'var(--ntr-success)' : 'var(--ntr-danger)',
|
|
973
986
|
}, children: [testResult.ok ? '✓ ' : '✕ ', testResult.text] }))] }));
|
|
974
987
|
}
|
|
975
988
|
/**
|
|
@@ -978,7 +991,7 @@ function AgentDetails({ acp, detected, form, patch, isConfigured, newCustom, for
|
|
|
978
991
|
* may touch (and refuses anything else with `cwd_denied`).
|
|
979
992
|
*/
|
|
980
993
|
function WorkspaceBlock({ acp, form, patch, cwdInvalid, rootsInvalid, }) {
|
|
981
|
-
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('
|
|
994
|
+
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.') })] }));
|
|
982
995
|
}
|
|
983
996
|
/** Row overflow menu: the dangerous actions, plus the inline remove confirm. */
|
|
984
997
|
function RowMenu({ acp, canStop, onStop, onRemove, }) {
|
|
@@ -1226,7 +1239,7 @@ function ModelCatalogBlock({ agentId, detected, acp, active, exposed, onExposedC
|
|
|
1226
1239
|
: acp.label('该 Agent 未暴露模型选择器(category: model)。', 'This agent exposes no model selector (category: model).');
|
|
1227
1240
|
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 && (
|
|
1228
1241
|
// This default belongs to the plugin, not the deployment model route.
|
|
1229
|
-
_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: '#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('
|
|
1242
|
+
_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 => {
|
|
1230
1243
|
const checked = selected.has(choice.value);
|
|
1231
1244
|
// group 前缀仅来自 agent 返回的 SessionConfigSelectGroup(见 impl.ts
|
|
1232
1245
|
// pushCatalogChoice);供应商可见性兜底靠原样展示 value,严禁从
|
|
@@ -1235,5 +1248,5 @@ function ModelCatalogBlock({ agentId, detected, acp, active, exposed, onExposedC
|
|
|
1235
1248
|
? choice.value
|
|
1236
1249
|
: `${choice.value}\n${choice.description}`;
|
|
1237
1250
|
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));
|
|
1238
|
-
}), 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('
|
|
1251
|
+
}), 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)))] }))] })] }));
|
|
1239
1252
|
}
|