@huanlin/dsh-plugin-preface-context 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/cordis.patch.yml +11 -0
- package/lib/client.js +794 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +127 -0
- package/lib/types/client/bind-snapshot-selector.d.ts +7 -0
- package/lib/types/client/bind-snapshot-selector.js +19 -0
- package/lib/types/client/client/bind-snapshot-selector.d.ts +7 -0
- package/lib/types/client/client/bind-snapshot-selector.js +19 -0
- package/lib/types/client/client/index.d.ts +39 -0
- package/lib/types/client/client/index.js +50 -0
- package/lib/types/client/client/locales.d.ts +7 -0
- package/lib/types/client/client/locales.js +41 -0
- package/lib/types/client/client/preface-card-controller.d.ts +94 -0
- package/lib/types/client/client/preface-card-controller.js +181 -0
- package/lib/types/client/client/preface-card.css.d.ts +53 -0
- package/lib/types/client/client/preface-card.css.js +327 -0
- package/lib/types/client/client/preface-card.d.ts +27 -0
- package/lib/types/client/client/preface-card.js +41 -0
- package/lib/types/client/config.d.ts +41 -0
- package/lib/types/client/config.js +42 -0
- package/lib/types/client/index.d.ts +39 -0
- package/lib/types/client/index.js +50 -0
- package/lib/types/client/locales.d.ts +7 -0
- package/lib/types/client/locales.js +41 -0
- package/lib/types/client/preface-card-controller.d.ts +94 -0
- package/lib/types/client/preface-card-controller.js +181 -0
- package/lib/types/client/preface-card.css.d.ts +53 -0
- package/lib/types/client/preface-card.css.js +327 -0
- package/lib/types/client/preface-card.d.ts +27 -0
- package/lib/types/client/preface-card.js +41 -0
- package/lib/types/config.d.ts +40 -0
- package/lib/types/config.js +42 -0
- package/lib/types/index.d.ts +46 -0
- package/lib/types/index.js +64 -0
- package/lib/types/settings.d.ts +50 -0
- package/lib/types/settings.js +85 -0
- package/package.json +130 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card styles + runtime style injection for the preface-context settings
|
|
3
|
+
* card.
|
|
4
|
+
*
|
|
5
|
+
* The DSH client loader never serves `.css` artifacts — every style must be
|
|
6
|
+
* injected inside the bundle's factory closure (the same contract the
|
|
7
|
+
* upstream tsdown.client preset implements with lightningcss). A plain
|
|
8
|
+
* template-string stylesheet installed once at plugin activation is the slim
|
|
9
|
+
* equivalent for a hand-rolled bundle: static, prefixed class names
|
|
10
|
+
* (`dsh-pfc-*`, unique in the document) replace the hashed CSS-modules map,
|
|
11
|
+
* and the `<style data-plugin-css>` tag is removed when the plugin unloads.
|
|
12
|
+
*
|
|
13
|
+
* Colors resolve through `--dsw-alias-*` tokens so the card adapts to the
|
|
14
|
+
* light/dark theme.
|
|
15
|
+
*
|
|
16
|
+
* @module @huanlin/dsh-plugin-preface-context/client/preface-card.css
|
|
17
|
+
*/
|
|
18
|
+
/** Class-name map consumed by the card component (CSS-modules replacement). */
|
|
19
|
+
export const css = {
|
|
20
|
+
card: 'dsh-pfc-card',
|
|
21
|
+
cardOpen: 'dsh-pfc-card--open',
|
|
22
|
+
header: 'dsh-pfc-header',
|
|
23
|
+
headText: 'dsh-pfc-head-text',
|
|
24
|
+
name: 'dsh-pfc-name',
|
|
25
|
+
description: 'dsh-pfc-description',
|
|
26
|
+
pending: 'dsh-pfc-pending',
|
|
27
|
+
chevron: 'dsh-pfc-chevron',
|
|
28
|
+
chevronOpen: 'dsh-pfc-chevron--open',
|
|
29
|
+
body: 'dsh-pfc-body',
|
|
30
|
+
readOnly: 'dsh-pfc-readonly',
|
|
31
|
+
field: 'dsh-pfc-field',
|
|
32
|
+
head: 'dsh-pfc-field-head',
|
|
33
|
+
label: 'dsh-pfc-label',
|
|
34
|
+
badges: 'dsh-pfc-badges',
|
|
35
|
+
badge: 'dsh-pfc-badge',
|
|
36
|
+
reset: 'dsh-pfc-reset',
|
|
37
|
+
textarea: 'dsh-pfc-textarea',
|
|
38
|
+
toggleRow: 'dsh-pfc-toggle-row',
|
|
39
|
+
toggle: 'dsh-pfc-toggle',
|
|
40
|
+
toggleOn: 'dsh-pfc-toggle--on',
|
|
41
|
+
knob: 'dsh-pfc-knob',
|
|
42
|
+
knobOn: 'dsh-pfc-knob--on',
|
|
43
|
+
hint: 'dsh-pfc-hint',
|
|
44
|
+
footer: 'dsh-pfc-footer',
|
|
45
|
+
failed: 'dsh-pfc-failed',
|
|
46
|
+
discard: 'dsh-pfc-discard',
|
|
47
|
+
save: 'dsh-pfc-save',
|
|
48
|
+
};
|
|
49
|
+
/** `data-plugin-css` tag id of the injected stylesheet. */
|
|
50
|
+
const STYLE_TAG_ID = '@huanlin/dsh-plugin-preface-context/preface-card.css';
|
|
51
|
+
/** The card stylesheet (injected as one tagged `<style>` at activation). */
|
|
52
|
+
const CSS = `
|
|
53
|
+
.dsh-pfc-card {
|
|
54
|
+
list-style: none;
|
|
55
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.22));
|
|
56
|
+
background: var(--dsw-alias-bg-layer-3, transparent);
|
|
57
|
+
border-radius: 12px;
|
|
58
|
+
transition: border-color 0.16s, background 0.16s;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dsh-pfc-card--open {
|
|
63
|
+
border-color: var(--dsw-alias-border-l3, rgba(128, 128, 128, 0.35));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.dsh-pfc-header {
|
|
67
|
+
display: flex;
|
|
68
|
+
width: 100%;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: 12px;
|
|
71
|
+
padding: 14px 16px;
|
|
72
|
+
font: inherit;
|
|
73
|
+
color: inherit;
|
|
74
|
+
text-align: left;
|
|
75
|
+
background: transparent;
|
|
76
|
+
border: 0;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.dsh-pfc-head-text {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex: 1 1 auto;
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
gap: 4px;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.dsh-pfc-name {
|
|
89
|
+
font-size: 15px;
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
line-height: 1.4;
|
|
92
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.dsh-pfc-description {
|
|
96
|
+
font-size: 13px;
|
|
97
|
+
line-height: 1.5;
|
|
98
|
+
color: var(--dsw-alias-label-tertiary, rgba(128, 128, 128, 0.7));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.dsh-pfc-pending {
|
|
102
|
+
flex: none;
|
|
103
|
+
white-space: nowrap;
|
|
104
|
+
font-size: 11px;
|
|
105
|
+
font-weight: 500;
|
|
106
|
+
line-height: 17px;
|
|
107
|
+
padding: 1px 8px;
|
|
108
|
+
border-radius: 999px;
|
|
109
|
+
color: var(--dsw-alias-label-secondary, inherit);
|
|
110
|
+
background: var(--dsw-alias-bg-module-platform, rgba(128, 128, 128, 0.12));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.dsh-pfc-chevron {
|
|
114
|
+
flex: none;
|
|
115
|
+
display: inline-flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
color: var(--dsw-alias-label-tertiary, inherit);
|
|
118
|
+
transition: transform 0.16s;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.dsh-pfc-chevron--open {
|
|
122
|
+
transform: rotate(180deg);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.dsh-pfc-body {
|
|
126
|
+
margin: 0 16px;
|
|
127
|
+
padding: 12px 0 4px;
|
|
128
|
+
border-top: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.22));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.dsh-pfc-readonly {
|
|
132
|
+
margin: 0 0 8px;
|
|
133
|
+
font-size: 12px;
|
|
134
|
+
line-height: 1.5;
|
|
135
|
+
color: var(--dsw-alias-label-tertiary, rgba(128, 128, 128, 0.7));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.dsh-pfc-field {
|
|
139
|
+
display: flex;
|
|
140
|
+
flex-direction: column;
|
|
141
|
+
gap: 4px;
|
|
142
|
+
margin: 0 0 12px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.dsh-pfc-field-head {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: 8px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.dsh-pfc-label {
|
|
152
|
+
font-size: 13px;
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.dsh-pfc-badges {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
gap: 6px;
|
|
161
|
+
margin-left: auto;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.dsh-pfc-badge {
|
|
165
|
+
font-size: 11px;
|
|
166
|
+
font-weight: 500;
|
|
167
|
+
line-height: 17px;
|
|
168
|
+
padding: 1px 8px;
|
|
169
|
+
border-radius: 999px;
|
|
170
|
+
color: var(--dsw-alias-label-secondary, inherit);
|
|
171
|
+
background: var(--dsw-alias-bg-module-platform, rgba(128, 128, 128, 0.12));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.dsh-pfc-reset {
|
|
175
|
+
appearance: none;
|
|
176
|
+
font: inherit;
|
|
177
|
+
font-size: 12px;
|
|
178
|
+
padding: 0;
|
|
179
|
+
border: 0;
|
|
180
|
+
background: transparent;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
color: var(--dsw-alias-brand-primary, #0a84ff);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.dsh-pfc-reset:disabled {
|
|
186
|
+
cursor: not-allowed;
|
|
187
|
+
opacity: 0.5;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.dsh-pfc-textarea {
|
|
191
|
+
width: 100%;
|
|
192
|
+
box-sizing: border-box;
|
|
193
|
+
min-height: 96px;
|
|
194
|
+
resize: vertical;
|
|
195
|
+
padding: 6px 10px;
|
|
196
|
+
font-family: var(--dsw-font-mono, ui-monospace, monospace);
|
|
197
|
+
font-size: 13px;
|
|
198
|
+
line-height: 1.5;
|
|
199
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
200
|
+
background: var(--dsw-alias-bg-layer-3, transparent);
|
|
201
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.3));
|
|
202
|
+
border-radius: 8px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.dsh-pfc-textarea:focus {
|
|
206
|
+
outline: none;
|
|
207
|
+
border-color: var(--dsw-alias-brand-primary, #0a84ff);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.dsh-pfc-textarea:disabled {
|
|
211
|
+
opacity: 0.6;
|
|
212
|
+
cursor: not-allowed;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.dsh-pfc-toggle-row {
|
|
216
|
+
display: flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
gap: 10px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.dsh-pfc-toggle {
|
|
222
|
+
position: relative;
|
|
223
|
+
width: 40px;
|
|
224
|
+
height: 22px;
|
|
225
|
+
flex: none;
|
|
226
|
+
padding: 0;
|
|
227
|
+
background: var(--dsw-alias-bg-module-platform, rgba(128, 128, 128, 0.12));
|
|
228
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.3));
|
|
229
|
+
border-radius: 999px;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
transition: background 0.16s, border-color 0.16s;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.dsh-pfc-toggle--on {
|
|
235
|
+
background: var(--dsw-alias-brand-primary, #0a84ff);
|
|
236
|
+
border-color: var(--dsw-alias-brand-primary, #0a84ff);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.dsh-pfc-knob {
|
|
240
|
+
position: absolute;
|
|
241
|
+
top: 2px;
|
|
242
|
+
left: 2px;
|
|
243
|
+
width: 16px;
|
|
244
|
+
height: 16px;
|
|
245
|
+
background: var(--dsw-alias-bg-layer-1, #fff);
|
|
246
|
+
border-radius: 50%;
|
|
247
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
248
|
+
transition: transform 0.16s;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.dsh-pfc-knob--on {
|
|
252
|
+
transform: translateX(18px);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.dsh-pfc-toggle:disabled {
|
|
256
|
+
cursor: not-allowed;
|
|
257
|
+
opacity: 0.5;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.dsh-pfc-hint {
|
|
261
|
+
margin: 0;
|
|
262
|
+
font-size: 12px;
|
|
263
|
+
line-height: 1.5;
|
|
264
|
+
color: var(--dsw-alias-label-tertiary, rgba(128, 128, 128, 0.6));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.dsh-pfc-footer {
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: flex-end;
|
|
271
|
+
gap: 8px;
|
|
272
|
+
padding: 12px 0 4px;
|
|
273
|
+
border-top: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.22));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.dsh-pfc-failed {
|
|
277
|
+
flex: 1 1 auto;
|
|
278
|
+
min-width: 0;
|
|
279
|
+
margin: 0;
|
|
280
|
+
font-size: 12px;
|
|
281
|
+
line-height: 1.5;
|
|
282
|
+
color: var(--dsw-alias-label-error, #ff453a);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.dsh-pfc-discard,
|
|
286
|
+
.dsh-pfc-save {
|
|
287
|
+
appearance: none;
|
|
288
|
+
font: inherit;
|
|
289
|
+
font-size: 13px;
|
|
290
|
+
font-weight: 500;
|
|
291
|
+
line-height: 20px;
|
|
292
|
+
padding: 5px 14px;
|
|
293
|
+
border: 1px solid transparent;
|
|
294
|
+
border-radius: 8px;
|
|
295
|
+
cursor: pointer;
|
|
296
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
297
|
+
background: var(--dsw-alias-bg-module-platform, rgba(128, 128, 128, 0.12));
|
|
298
|
+
transition: background 0.16s, opacity 0.16s;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.dsh-pfc-save {
|
|
302
|
+
background: var(--dsw-alias-brand-primary, #0a84ff);
|
|
303
|
+
color: var(--dsw-alias-bg-layer-1, #fff);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.dsh-pfc-discard:disabled,
|
|
307
|
+
.dsh-pfc-save:disabled {
|
|
308
|
+
cursor: not-allowed;
|
|
309
|
+
opacity: 0.5;
|
|
310
|
+
}
|
|
311
|
+
`;
|
|
312
|
+
/**
|
|
313
|
+
* Install the card stylesheet as one tagged `<style>` element.
|
|
314
|
+
* @returns the disposer removing the tag (idempotent outside a document).
|
|
315
|
+
*/
|
|
316
|
+
export function installPrefaceCardStyles() {
|
|
317
|
+
if (typeof document === 'undefined')
|
|
318
|
+
return () => { };
|
|
319
|
+
if (document.querySelector(`style[data-plugin-css='${STYLE_TAG_ID}']`) !== null)
|
|
320
|
+
return () => { };
|
|
321
|
+
const style = document.createElement('style');
|
|
322
|
+
style.dataset.plugin = '@huanlin/dsh-plugin-preface-context';
|
|
323
|
+
style.dataset.pluginCss = STYLE_TAG_ID;
|
|
324
|
+
style.textContent = CSS;
|
|
325
|
+
document.head.appendChild(style);
|
|
326
|
+
return () => { style.remove(); };
|
|
327
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preface-context settings card: a collapsible card on the "插件配置"
|
|
3
|
+
* settings page. Renders an enable toggle and a multiline textarea for the
|
|
4
|
+
* context text. Stages edits locally and writes them on save through the
|
|
5
|
+
* bound {@link PrefaceCardController}.
|
|
6
|
+
*
|
|
7
|
+
* The chrome mirrors the upstream `PluginCard` shape (a header button
|
|
8
|
+
* stacking name over description, a dirty pill, a rotating chevron; a body
|
|
9
|
+
* with the form controls and a footer with Discard/Save) because the
|
|
10
|
+
* upstream client value face exports no reusable card. Styles come from the
|
|
11
|
+
* statically injected `preface-card.css` stylesheet (the loader never serves
|
|
12
|
+
* `.css` artifacts); every color resolves through a `--dsw-alias-*` token so
|
|
13
|
+
* the card adapts to the light/dark theme.
|
|
14
|
+
*/
|
|
15
|
+
import { type ReactNode } from 'react';
|
|
16
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
17
|
+
import type { PrefaceCardFace, PrefaceCardActions } from './preface-card-controller.ts';
|
|
18
|
+
/** Injected dependencies of the card (slot `inject`). */
|
|
19
|
+
export type PrefaceCardInjected = PrefaceCardFace & PrefaceCardActions;
|
|
20
|
+
/** Props the renderer binds for the card. */
|
|
21
|
+
export type PrefaceCardProps = PropsRuntime<'settings.plugin.item'> & PropsLocale<'settings.preface-context'> & InjectFace<PrefaceCardInjected>;
|
|
22
|
+
/**
|
|
23
|
+
* Render the preface-context card.
|
|
24
|
+
* @param props - locale copy, the card snapshot, and its form actions.
|
|
25
|
+
* @returns the card, or nothing when the namespace is unavailable.
|
|
26
|
+
*/
|
|
27
|
+
export declare function PrefaceCard(props: PrefaceCardProps): ReactNode;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The preface-context settings card: a collapsible card on the "插件配置"
|
|
4
|
+
* settings page. Renders an enable toggle and a multiline textarea for the
|
|
5
|
+
* context text. Stages edits locally and writes them on save through the
|
|
6
|
+
* bound {@link PrefaceCardController}.
|
|
7
|
+
*
|
|
8
|
+
* The chrome mirrors the upstream `PluginCard` shape (a header button
|
|
9
|
+
* stacking name over description, a dirty pill, a rotating chevron; a body
|
|
10
|
+
* with the form controls and a footer with Discard/Save) because the
|
|
11
|
+
* upstream client value face exports no reusable card. Styles come from the
|
|
12
|
+
* statically injected `preface-card.css` stylesheet (the loader never serves
|
|
13
|
+
* `.css` artifacts); every color resolves through a `--dsw-alias-*` token so
|
|
14
|
+
* the card adapts to the light/dark theme.
|
|
15
|
+
*/
|
|
16
|
+
import { useState } from 'react';
|
|
17
|
+
import clsx from 'clsx';
|
|
18
|
+
import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
19
|
+
import { css } from "./preface-card.css.js";
|
|
20
|
+
/**
|
|
21
|
+
* Render the preface-context card.
|
|
22
|
+
* @param props - locale copy, the card snapshot, and its form actions.
|
|
23
|
+
* @returns the card, or nothing when the namespace is unavailable.
|
|
24
|
+
*/
|
|
25
|
+
export function PrefaceCard(props) {
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const { t } = props;
|
|
28
|
+
const state = props.usePrefaceCard(snapshot => snapshot);
|
|
29
|
+
if (!state.available)
|
|
30
|
+
return null;
|
|
31
|
+
const title = t('title');
|
|
32
|
+
const blocked = !state.dirty || state.saving;
|
|
33
|
+
const enabledOn = state.enabled.text === 'true';
|
|
34
|
+
return (_jsxs("li", { className: clsx(css.card, open && css.cardOpen), children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${t(open ? 'collapse' : 'expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, children: title }), _jsx("span", { className: css.description, children: t('description') })] }), state.dirty ? _jsx("span", { className: css.pending, children: t('unsaved') }) : null, _jsx(IconChevronDownOutline14, { className: clsx(css.chevron, open && css.chevronOpen) })] }), open
|
|
35
|
+
? (_jsxs("div", { className: css.body, children: [!state.writable ? _jsx("p", { className: css.readOnly, role: "status", children: t('readOnly') }) : null, _jsxs("div", { className: css.field, children: [_jsxs("div", { className: css.head, children: [_jsx("label", { className: css.label, children: t('enabled') }), state.enabled.overridden
|
|
36
|
+
? (_jsxs("span", { className: css.badges, children: [_jsx("span", { className: css.badge, children: t('overridden') }), _jsx("button", { type: "button", className: css.reset, disabled: !state.writable, onClick: () => { props.resetField('enabled'); }, children: t('reset') })] }))
|
|
37
|
+
: null] }), _jsx("div", { className: css.toggleRow, children: _jsx("button", { type: "button", role: "switch", "aria-checked": enabledOn, className: clsx(css.toggle, enabledOn && css.toggleOn), disabled: !state.writable, onClick: () => { props.edit('enabled', enabledOn ? 'false' : 'true'); }, children: _jsx("span", { className: clsx(css.knob, enabledOn && css.knobOn) }) }) }), _jsx("p", { className: css.hint, children: t('enabledHint') })] }), _jsxs("div", { className: css.field, children: [_jsxs("div", { className: css.head, children: [_jsx("label", { className: css.label, children: t('contextText') }), state.contextText.overridden
|
|
38
|
+
? (_jsxs("span", { className: css.badges, children: [_jsx("span", { className: css.badge, children: t('overridden') }), _jsx("button", { type: "button", className: css.reset, disabled: !state.writable, onClick: () => { props.resetField('contextText'); }, children: t('reset') })] }))
|
|
39
|
+
: null] }), _jsx("textarea", { id: "plugin-config-preface-context-text", className: css.textarea, value: state.contextText.text, placeholder: t('contextTextPlaceholder'), disabled: !state.writable, onChange: (event) => { props.edit('contextText', event.target.value); } }), _jsx("p", { className: css.hint, children: t('contextTextHint') })] }), _jsxs("div", { className: css.footer, children: [state.failed ? _jsx("p", { className: css.failed, role: "status", children: t('saveFailed') }) : null, _jsx("button", { type: "button", className: css.discard, disabled: !state.dirty || state.saving, onClick: props.discard, children: t('discard') }), _jsx("button", { type: "button", className: css.save, disabled: blocked, onClick: props.save, children: t(state.saving ? 'saving' : 'save') })] })] }))
|
|
40
|
+
: null] }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config schema for the preface-context plugin (Schemastery, strict).
|
|
3
|
+
*
|
|
4
|
+
* The plugin-row config (the `entry` passed to `apply`) is the composition
|
|
5
|
+
* BASE of the `preface-context` settings namespace. The settings user layer
|
|
6
|
+
* is layered on top (schema defaults → base → user layer) when a settings
|
|
7
|
+
* service is mounted; without one, the entry is the sole source.
|
|
8
|
+
*
|
|
9
|
+
* @module @huanlin/dsh-plugin-preface-context/config
|
|
10
|
+
*/
|
|
11
|
+
/** User-editable configuration for the preface-context plugin. */
|
|
12
|
+
export interface PrefaceConfig {
|
|
13
|
+
/** Master switch: when false, no context is injected at session start. */
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
/** The text block injected as model-visible instructions context. */
|
|
16
|
+
contextText: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Schemastery schema for the `preface-context` settings namespace.
|
|
20
|
+
*
|
|
21
|
+
* Strict by construction: unknown keys fail validation here, even though the
|
|
22
|
+
* settings service itself is non-strict and would otherwise accept them.
|
|
23
|
+
*/
|
|
24
|
+
export declare const Config: import("C:/Users/Administrator/.dsh/source/current/vendor/schemastery/lib/types").default<Schemastery.ObjectS<{
|
|
25
|
+
enabled: import("C:/Users/Administrator/.dsh/source/current/vendor/schemastery/lib/types").default<boolean, boolean>;
|
|
26
|
+
contextText: import("C:/Users/Administrator/.dsh/source/current/vendor/schemastery/lib/types").default<string, string>;
|
|
27
|
+
}>, Schemastery.ObjectT<{
|
|
28
|
+
enabled: import("C:/Users/Administrator/.dsh/source/current/vendor/schemastery/lib/types").default<boolean, boolean>;
|
|
29
|
+
contextText: import("C:/Users/Administrator/.dsh/source/current/vendor/schemastery/lib/types").default<string, string>;
|
|
30
|
+
}>>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a raw config patch through the schema, returning a full
|
|
33
|
+
* {@link PrefaceConfig} with defaults applied. Unknown keys are rejected
|
|
34
|
+
* here (the settings service itself is non-strict and would otherwise accept
|
|
35
|
+
* them), matching the Loader's strict validation.
|
|
36
|
+
* @param input - a partial or complete config object.
|
|
37
|
+
* @returns the schema-resolved config.
|
|
38
|
+
* @throws when the input carries an unknown key or a value the schema rejects.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolvePrefaceConfig(input: Partial<PrefaceConfig>): PrefaceConfig;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config schema for the preface-context plugin (Schemastery, strict).
|
|
3
|
+
*
|
|
4
|
+
* The plugin-row config (the `entry` passed to `apply`) is the composition
|
|
5
|
+
* BASE of the `preface-context` settings namespace. The settings user layer
|
|
6
|
+
* is layered on top (schema defaults → base → user layer) when a settings
|
|
7
|
+
* service is mounted; without one, the entry is the sole source.
|
|
8
|
+
*
|
|
9
|
+
* @module @huanlin/dsh-plugin-preface-context/config
|
|
10
|
+
*/
|
|
11
|
+
import z from 'schemastery';
|
|
12
|
+
/**
|
|
13
|
+
* Schemastery schema for the `preface-context` settings namespace.
|
|
14
|
+
*
|
|
15
|
+
* Strict by construction: unknown keys fail validation here, even though the
|
|
16
|
+
* settings service itself is non-strict and would otherwise accept them.
|
|
17
|
+
*/
|
|
18
|
+
export const Config = z.object({
|
|
19
|
+
enabled: z.boolean().default(true),
|
|
20
|
+
contextText: z.string().default(''),
|
|
21
|
+
});
|
|
22
|
+
/** Known config keys (for strict unknown-key rejection). */
|
|
23
|
+
const CONFIG_KEYS = new Set(['enabled', 'contextText']);
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a raw config patch through the schema, returning a full
|
|
26
|
+
* {@link PrefaceConfig} with defaults applied. Unknown keys are rejected
|
|
27
|
+
* here (the settings service itself is non-strict and would otherwise accept
|
|
28
|
+
* them), matching the Loader's strict validation.
|
|
29
|
+
* @param input - a partial or complete config object.
|
|
30
|
+
* @returns the schema-resolved config.
|
|
31
|
+
* @throws when the input carries an unknown key or a value the schema rejects.
|
|
32
|
+
*/
|
|
33
|
+
export function resolvePrefaceConfig(input) {
|
|
34
|
+
if (input !== null && typeof input === 'object' && !Array.isArray(input)) {
|
|
35
|
+
for (const key of Object.keys(input)) {
|
|
36
|
+
if (!CONFIG_KEYS.has(key)) {
|
|
37
|
+
throw new Error(`preface-context: unknown config key "${key}"`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return Config(input);
|
|
42
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-plugin-preface-context — injects a user-configured text block as
|
|
3
|
+
* model-visible instructions context at the start of every session.
|
|
4
|
+
*
|
|
5
|
+
* The plugin listens to `agent/session-start` and, when enabled and the
|
|
6
|
+
* context text is non-empty, calls `agent.inject()` with a plugin-sourced
|
|
7
|
+
* `user/message` carrying the text as a single text block. The message
|
|
8
|
+
* enters the session inbox and is claimed by the first `agent/pre-step`,
|
|
9
|
+
* reaching the model as instructions context closest to its first answer.
|
|
10
|
+
*
|
|
11
|
+
* The text and the enable switch are editable on the "插件配置" settings
|
|
12
|
+
* page: the host registers the `preface-context` settings namespace (the
|
|
13
|
+
* plugin-row config is the composition base), and the client half binds a
|
|
14
|
+
* card to that namespace through `ctx.settingsScope.bind()`. The runtime
|
|
15
|
+
* reads the LIVE composed value through the bridge source, so a settings
|
|
16
|
+
* edit takes effect on the next session start without a restart.
|
|
17
|
+
*
|
|
18
|
+
* @module @huanlin/dsh-plugin-preface-context
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
21
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
22
|
+
import type { PrefaceConfig } from './config.ts';
|
|
23
|
+
export declare const name = "dsh-plugin-preface-context";
|
|
24
|
+
/** Services the plugin consumes; the row loads once all are available. */
|
|
25
|
+
export declare const inject: string[];
|
|
26
|
+
/** Loader schema (schemastery, strict) — validated by the cordis Loader. */
|
|
27
|
+
export { Config } from './config.ts';
|
|
28
|
+
export type { PrefaceConfig } from './config.ts';
|
|
29
|
+
/**
|
|
30
|
+
* Build the user-message to inject for one session start.
|
|
31
|
+
*
|
|
32
|
+
* The message carries the context text as a single text block and is tagged
|
|
33
|
+
* `source.kind: 'plugin'` with `form: 'instructions'` so the model and UI
|
|
34
|
+
* can present it as workspace instructions rather than a user prompt.
|
|
35
|
+
* @param config - the live composed config.
|
|
36
|
+
* @returns the message to inject, or `undefined` when injection is disabled
|
|
37
|
+
* or the text is empty/whitespace.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildPrefaceMessage(config: PrefaceConfig): ReturnType<typeof createUserMessage> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Plugin entry: install the settings bridge, then inject the preface text on
|
|
42
|
+
* every session start.
|
|
43
|
+
* @param ctx - the plugin's cordis context.
|
|
44
|
+
* @param config - the composition entry config (base layer of the settings namespace).
|
|
45
|
+
*/
|
|
46
|
+
export declare function apply(ctx: Context, config: PrefaceConfig): void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-plugin-preface-context — injects a user-configured text block as
|
|
3
|
+
* model-visible instructions context at the start of every session.
|
|
4
|
+
*
|
|
5
|
+
* The plugin listens to `agent/session-start` and, when enabled and the
|
|
6
|
+
* context text is non-empty, calls `agent.inject()` with a plugin-sourced
|
|
7
|
+
* `user/message` carrying the text as a single text block. The message
|
|
8
|
+
* enters the session inbox and is claimed by the first `agent/pre-step`,
|
|
9
|
+
* reaching the model as instructions context closest to its first answer.
|
|
10
|
+
*
|
|
11
|
+
* The text and the enable switch are editable on the "插件配置" settings
|
|
12
|
+
* page: the host registers the `preface-context` settings namespace (the
|
|
13
|
+
* plugin-row config is the composition base), and the client half binds a
|
|
14
|
+
* card to that namespace through `ctx.settingsScope.bind()`. The runtime
|
|
15
|
+
* reads the LIVE composed value through the bridge source, so a settings
|
|
16
|
+
* edit takes effect on the next session start without a restart.
|
|
17
|
+
*
|
|
18
|
+
* @module @huanlin/dsh-plugin-preface-context
|
|
19
|
+
*/
|
|
20
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
21
|
+
import { installPrefaceSettings } from "./settings.js";
|
|
22
|
+
export const name = 'dsh-plugin-preface-context';
|
|
23
|
+
/** Services the plugin consumes; the row loads once all are available. */
|
|
24
|
+
export const inject = ['agents', 'llm'];
|
|
25
|
+
/** Loader schema (schemastery, strict) — validated by the cordis Loader. */
|
|
26
|
+
export { Config } from "./config.js";
|
|
27
|
+
/** Plugin source tag carried by every injected message. */
|
|
28
|
+
const PLUGIN_SOURCE = { kind: 'plugin', plugin: 'dsh-plugin-preface-context', form: 'instructions' };
|
|
29
|
+
/**
|
|
30
|
+
* Build the user-message to inject for one session start.
|
|
31
|
+
*
|
|
32
|
+
* The message carries the context text as a single text block and is tagged
|
|
33
|
+
* `source.kind: 'plugin'` with `form: 'instructions'` so the model and UI
|
|
34
|
+
* can present it as workspace instructions rather than a user prompt.
|
|
35
|
+
* @param config - the live composed config.
|
|
36
|
+
* @returns the message to inject, or `undefined` when injection is disabled
|
|
37
|
+
* or the text is empty/whitespace.
|
|
38
|
+
*/
|
|
39
|
+
export function buildPrefaceMessage(config) {
|
|
40
|
+
if (!config.enabled)
|
|
41
|
+
return undefined;
|
|
42
|
+
const text = config.contextText.trim();
|
|
43
|
+
if (text === '')
|
|
44
|
+
return undefined;
|
|
45
|
+
return createUserMessage({
|
|
46
|
+
content: [{ type: 'text', text: config.contextText }],
|
|
47
|
+
source: PLUGIN_SOURCE,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Plugin entry: install the settings bridge, then inject the preface text on
|
|
52
|
+
* every session start.
|
|
53
|
+
* @param ctx - the plugin's cordis context.
|
|
54
|
+
* @param config - the composition entry config (base layer of the settings namespace).
|
|
55
|
+
*/
|
|
56
|
+
export function apply(ctx, config) {
|
|
57
|
+
const bridge = installPrefaceSettings(ctx, config);
|
|
58
|
+
ctx.on('agent/session-start', ({ agent }) => {
|
|
59
|
+
const message = buildPrefaceMessage(bridge.source());
|
|
60
|
+
if (message === undefined)
|
|
61
|
+
return;
|
|
62
|
+
agent.inject(message);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side `preface-context` settings namespace + live source wiring.
|
|
3
|
+
*
|
|
4
|
+
* The plugin-row config (the `entry` passed to the plugin's `apply`) is the
|
|
5
|
+
* composition BASE of the `preface-context` settings namespace: when a dsh
|
|
6
|
+
* settings service is mounted, its user layer is layered on top (schema
|
|
7
|
+
* defaults → base → user layer) and the runtime reads the live resolved
|
|
8
|
+
* value through the bridge's `source` thunk. Without a settings service the
|
|
9
|
+
* conditional `ctx.inject(['settings'], ...)` child never activates and the
|
|
10
|
+
* source is exactly the entry: behavior identical to a no-settings build.
|
|
11
|
+
*
|
|
12
|
+
* The namespace reaches the web configuration boundary directly: the
|
|
13
|
+
* apiproxy `settings.mutate` / `settings.describe` RPCs accept any
|
|
14
|
+
* registered namespace (no allowlist gate in current upstream dsh), so the
|
|
15
|
+
* client card reads/writes through `ctx.settingsScope.bind({ namespace })`
|
|
16
|
+
* — no gateway channel is needed.
|
|
17
|
+
*
|
|
18
|
+
* @module @huanlin/dsh-plugin-preface-context/settings
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
21
|
+
import type { PrefaceConfig } from './config.ts';
|
|
22
|
+
/** The `preface-context` settings namespace (registered when a settings service exists). */
|
|
23
|
+
export declare const PREFACE_SETTINGS_NAMESPACE = "preface-context";
|
|
24
|
+
/**
|
|
25
|
+
* The live configuration source for the runtime.
|
|
26
|
+
*
|
|
27
|
+
* `source()` returns the composed config (schema defaults → plugin-row base
|
|
28
|
+
* → settings user layer). `onChange` registers a callback that fires at
|
|
29
|
+
* attach, on committed changes, and at detach back to the entry.
|
|
30
|
+
*/
|
|
31
|
+
export interface PrefaceSettingsBridge {
|
|
32
|
+
/** Read the currently authoritative config. */
|
|
33
|
+
source(): PrefaceConfig;
|
|
34
|
+
/** Register a change callback (fires at attach, on change, at detach). */
|
|
35
|
+
onChange(callback: () => void): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Install the `preface-context` settings namespace and wire the live source.
|
|
39
|
+
*
|
|
40
|
+
* Mirrors the dsh `installSettingsSection` pattern. The registration rides a
|
|
41
|
+
* conditional `ctx.inject(['settings'], ...)` child, so with no settings
|
|
42
|
+
* service the source stays the entry config. Multi-fiber dedupe: the host
|
|
43
|
+
* may compose several preface-context fibers, and `settings.register` fails
|
|
44
|
+
* loud on a duplicate namespace — the catch lets the first registration own
|
|
45
|
+
* the live namespace while later instances fall back to the entry source.
|
|
46
|
+
* @param ctx - consumer plugin context owning the wiring.
|
|
47
|
+
* @param entry - the composition entry config, used as `base`.
|
|
48
|
+
* @returns the bridge the runtime and gateway read through.
|
|
49
|
+
*/
|
|
50
|
+
export declare function installPrefaceSettings(ctx: Context, entry: PrefaceConfig): PrefaceSettingsBridge;
|