dsh-ptc-plus 0.2.2 → 0.2.4
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 +30 -11
- package/README.zh.md +30 -11
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +139 -46
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +25 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +2 -1
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +31 -0
- package/docs/architecture.md +6 -4
- package/docs/client-ui.md +2 -2
- package/docs/installation.md +5 -5
- package/docs/publishing.md +25 -1
- package/docs/runtime-reference.md +4 -2
- package/index.js +13 -11
- package/internal/config-spec.js +46 -1
- package/internal/direct-surface-owner.js +21 -18
- package/internal/edit-transport-owner.js +12 -1
- package/internal/failure-reporting.js +1 -0
- package/internal/recovery-tips.js +0 -1
- package/internal/rejected-cell-editor.js +21 -7
- package/internal/session-cell-executor.js +52 -26
- package/internal/session-runtime.js +12 -4
- package/internal/settings-compat.js +31 -0
- package/internal/validated-parse-repair.js +82 -0
- package/package.json +17 -11
- package/src/client.js +88 -26
- package/assets/ptc-plus-settings.png +0 -0
- /package/assets/{dsh-ptc-plus-banner.webp → dsh-ptc-plus-banner-zh.webp} +0 -0
package/src/client.js
CHANGED
|
@@ -13,6 +13,64 @@ const CLIENT_CSS = `
|
|
|
13
13
|
@media(prefers-reduced-motion:reduce){.ptcPlusHeader,.ptcPlusChevron,.ptcPlusBody,.ptcPlusButton{transition:none}}
|
|
14
14
|
`
|
|
15
15
|
|
|
16
|
+
/** Locale namespace owning every settings-card string (field copy plus chrome). */
|
|
17
|
+
const LOCALE_NS = 'settings.ptcPlus'
|
|
18
|
+
|
|
19
|
+
/** Card chrome copy; field labels and hints ride the shared config spec. */
|
|
20
|
+
const CHROME_COPY = Object.freeze({
|
|
21
|
+
zh: Object.freeze({
|
|
22
|
+
'card.description': 'PTC 模式的会话级 TypeScript REPL。',
|
|
23
|
+
'status.enabled': '已启用',
|
|
24
|
+
'status.disabled': '已停用',
|
|
25
|
+
'action.expand': '展开 PTC Plus 设置',
|
|
26
|
+
'action.collapse': '收起 PTC Plus 设置',
|
|
27
|
+
'state.syncing': '正在同步设置...',
|
|
28
|
+
'state.unavailable': '当前 DSH 实例未提供设置服务',
|
|
29
|
+
'footer.live': '设置会在修改后立即生效',
|
|
30
|
+
'footer.readOnly': '当前设置为只读',
|
|
31
|
+
'status.applied': '设置已立即生效',
|
|
32
|
+
'status.conflict': '设置未生效,请检查设置冲突',
|
|
33
|
+
'status.failed': '设置失败:{error}',
|
|
34
|
+
'indicator.title': 'PTC Plus 已启用',
|
|
35
|
+
}),
|
|
36
|
+
en: Object.freeze({
|
|
37
|
+
'card.description': 'The session-bound TypeScript REPL for PTC mode.',
|
|
38
|
+
'status.enabled': 'Enabled',
|
|
39
|
+
'status.disabled': 'Disabled',
|
|
40
|
+
'action.expand': 'Expand PTC Plus settings',
|
|
41
|
+
'action.collapse': 'Collapse PTC Plus settings',
|
|
42
|
+
'state.syncing': 'Syncing settings...',
|
|
43
|
+
'state.unavailable': 'This DSH instance does not provide a settings service',
|
|
44
|
+
'footer.live': 'Changes take effect immediately.',
|
|
45
|
+
'footer.readOnly': 'These settings are read-only.',
|
|
46
|
+
'status.applied': 'Setting applied immediately.',
|
|
47
|
+
'status.conflict': 'The setting did not take effect; check for conflicting settings.',
|
|
48
|
+
'status.failed': 'Could not save: {error}',
|
|
49
|
+
'indicator.title': 'PTC Plus is active',
|
|
50
|
+
}),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
/** Project one field's copy into locale dictionary keys; an empty hint has no key. */
|
|
54
|
+
function fieldCopy(field, locale) {
|
|
55
|
+
const copy = { [`${field.key}.label`]: locale === 'en' ? field.labelEn : field.label }
|
|
56
|
+
const description = locale === 'en' ? field.descriptionEn : field.description
|
|
57
|
+
if (description !== '') copy[`${field.key}.description`] = description
|
|
58
|
+
return copy
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Complete dictionaries for both shipped locales, derived once from the spec. */
|
|
62
|
+
const SETTINGS_COPY = Object.freeze(Object.fromEntries(
|
|
63
|
+
['zh', 'en'].map(locale => [locale, Object.freeze({
|
|
64
|
+
...CHROME_COPY[locale],
|
|
65
|
+
...Object.assign({}, ...CONFIG_FIELDS.map(field => fieldCopy(field, locale))),
|
|
66
|
+
})]),
|
|
67
|
+
))
|
|
68
|
+
|
|
69
|
+
function sessionUsesPtcPreset(session) {
|
|
70
|
+
const preset = session?.projectionValues?.agentPreset ?? session?.agentPreset
|
|
71
|
+
return preset === 'ptc' || preset === 'code'
|
|
72
|
+
}
|
|
73
|
+
|
|
16
74
|
window.__ModuleLoader__.load({
|
|
17
75
|
// Replaced by the bundle entry with the package name from package.json.
|
|
18
76
|
id: __PTC_PLUS_CLIENT_MODULE_ID__,
|
|
@@ -21,7 +79,6 @@ window.__ModuleLoader__.load({
|
|
|
21
79
|
const {
|
|
22
80
|
IconCheckOutline14,
|
|
23
81
|
IconChevronDownOutline14,
|
|
24
|
-
IconSettingsOutline16,
|
|
25
82
|
} = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
26
83
|
const module = { exports: {} }
|
|
27
84
|
const h = React.createElement
|
|
@@ -35,18 +92,18 @@ window.__ModuleLoader__.load({
|
|
|
35
92
|
return () => style.remove()
|
|
36
93
|
}
|
|
37
94
|
|
|
38
|
-
function fieldInput(field, value, disabled, onChange) {
|
|
95
|
+
function fieldInput(field, value, disabled, onChange, label) {
|
|
39
96
|
if (field.type === 'boolean') {
|
|
40
97
|
return h('input', {
|
|
41
98
|
type: 'checkbox', role: 'switch', className: 'ptcPlusCheck', checked: value === true,
|
|
42
|
-
disabled, 'aria-label':
|
|
99
|
+
disabled, 'aria-label': label,
|
|
43
100
|
onChange: event => onChange(field, event.target.checked),
|
|
44
101
|
})
|
|
45
102
|
}
|
|
46
103
|
return h('input', {
|
|
47
104
|
type: 'number', className: 'ptcPlusInput',
|
|
48
105
|
value: Number.isSafeInteger(value) ? String(value) : '',
|
|
49
|
-
min: field.min, max: field.max, step: 1, disabled, 'aria-label':
|
|
106
|
+
min: field.min, max: field.max, step: 1, disabled, 'aria-label': label,
|
|
50
107
|
onChange: event => {
|
|
51
108
|
const input = event.target.value
|
|
52
109
|
const parsed = input === '' ? '' : Number(input)
|
|
@@ -57,11 +114,12 @@ window.__ModuleLoader__.load({
|
|
|
57
114
|
|
|
58
115
|
function apply(ctx) {
|
|
59
116
|
const preferenceScope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE })
|
|
117
|
+
ctx.effect(() => ctx.locale.register(LOCALE_NS, SETTINGS_COPY), 'ptc-plus: settings dictionaries')
|
|
60
118
|
ctx.effect(installStyles, 'ptc-plus: client styles')
|
|
61
119
|
|
|
62
|
-
function PTCPlusSettingsCard() {
|
|
120
|
+
function PTCPlusSettingsCard({ t }) {
|
|
63
121
|
const [open, setOpen] = React.useState(false)
|
|
64
|
-
const [status, setStatus] = React.useState(
|
|
122
|
+
const [status, setStatus] = React.useState(null)
|
|
65
123
|
const [pending, setPending] = React.useState(() => new Set())
|
|
66
124
|
const writeTail = React.useRef(Promise.resolve())
|
|
67
125
|
const subscribe = React.useCallback(listener => preferenceScope.subscribe(listener), [])
|
|
@@ -78,17 +136,20 @@ window.__ModuleLoader__.load({
|
|
|
78
136
|
if (field.key !== 'enabled' && before.value?.enabled !== true) return
|
|
79
137
|
if (before.value?.[field.key] === nextValue) return
|
|
80
138
|
setPending(current => new Set(current).add(field.key))
|
|
81
|
-
setStatus(
|
|
139
|
+
setStatus(null)
|
|
82
140
|
try {
|
|
83
141
|
await preferenceScope.set(field.key, nextValue)
|
|
84
142
|
const after = preferenceScope.getSnapshot()
|
|
85
143
|
if (after.status !== 'ready' || after.value?.[field.key] !== nextValue) {
|
|
86
|
-
setStatus('
|
|
144
|
+
setStatus({ key: 'status.conflict' })
|
|
87
145
|
} else {
|
|
88
|
-
setStatus('
|
|
146
|
+
setStatus({ key: 'status.applied' })
|
|
89
147
|
}
|
|
90
148
|
} catch (error) {
|
|
91
|
-
setStatus(
|
|
149
|
+
setStatus({
|
|
150
|
+
key: 'status.failed',
|
|
151
|
+
params: { error: error instanceof Error ? error.message : String(error) },
|
|
152
|
+
})
|
|
92
153
|
} finally {
|
|
93
154
|
setPending(current => {
|
|
94
155
|
const next = new Set(current)
|
|
@@ -105,39 +166,40 @@ window.__ModuleLoader__.load({
|
|
|
105
166
|
return h('li', { className: 'ptcPlusCard' },
|
|
106
167
|
h('button', {
|
|
107
168
|
type: 'button', className: 'ptcPlusHeader', 'aria-expanded': open,
|
|
108
|
-
'aria-label': open ? '
|
|
169
|
+
'aria-label': t(open ? 'action.collapse' : 'action.expand'),
|
|
109
170
|
'aria-controls': 'ptc-plus-settings-body', onClick: () => setOpen(current => !current),
|
|
110
171
|
},
|
|
111
|
-
h(IconSettingsOutline16, { size: 16 }),
|
|
112
172
|
h('span', { className: 'ptcPlusHeadText' },
|
|
113
173
|
h('span', { className: 'ptcPlusName' }, 'PTC Plus'),
|
|
114
|
-
h('span', { className: 'ptcPlusDescription' }, '
|
|
115
|
-
h('span', { className: 'ptcPlusStatus', 'data-enabled': enabled }, enabled ? '
|
|
174
|
+
h('span', { className: 'ptcPlusDescription' }, t('card.description'))),
|
|
175
|
+
h('span', { className: 'ptcPlusStatus', 'data-enabled': enabled }, t(enabled ? 'status.enabled' : 'status.disabled')),
|
|
116
176
|
h('span', { className: 'ptcPlusChevron', 'data-open': open, 'aria-hidden': true }, h(IconChevronDownOutline14, { size: 14 }))),
|
|
117
177
|
h('div', { id: 'ptc-plus-settings-body', className: 'ptcPlusBody', 'data-open': open, 'aria-hidden': !open },
|
|
118
178
|
h('div', { className: 'ptcPlusBodyInner' }, h('div', { className: 'ptcPlusFields' },
|
|
119
179
|
snapshot.status === 'loading'
|
|
120
|
-
? h('p', { className: 'ptcPlusMessage' }, '
|
|
180
|
+
? h('p', { className: 'ptcPlusMessage' }, t('state.syncing'))
|
|
121
181
|
: snapshot.status === 'unavailable'
|
|
122
|
-
? h('p', { className: 'ptcPlusMessage' }, '
|
|
182
|
+
? h('p', { className: 'ptcPlusMessage' }, t('state.unavailable'))
|
|
123
183
|
: [
|
|
124
184
|
...CONFIG_FIELDS.map(field => h('div', { key: field.key, className: 'ptcPlusRow' },
|
|
125
185
|
h('div', { className: 'ptcPlusMain' },
|
|
126
|
-
h('div', { className: 'ptcPlusLabel' }, field.label),
|
|
127
|
-
field.description === '' ? null : h('div', { className: 'ptcPlusDetail' }, field.description)),
|
|
128
|
-
fieldInput(field, value[field.key], fieldDisabled(field), persist))),
|
|
186
|
+
h('div', { className: 'ptcPlusLabel' }, t(`${field.key}.label`)),
|
|
187
|
+
field.description === '' ? null : h('div', { className: 'ptcPlusDetail' }, t(`${field.key}.description`))),
|
|
188
|
+
fieldInput(field, value[field.key], fieldDisabled(field), persist, t(`${field.key}.label`)))),
|
|
129
189
|
h('div', { key: 'footer', className: 'ptcPlusFooter' },
|
|
130
|
-
h('span', { className: 'ptcPlusMessage', role: 'status' }, status
|
|
190
|
+
h('span', { className: 'ptcPlusMessage', role: 'status' }, status === null
|
|
191
|
+
? t(snapshot.writable ? 'footer.live' : 'footer.readOnly')
|
|
192
|
+
: t(status.key, status.params))),
|
|
131
193
|
]))),
|
|
132
194
|
)
|
|
133
195
|
}
|
|
134
196
|
|
|
135
197
|
ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
|
|
136
|
-
name: 'settings.plugin.item', key: SETTINGS_NAMESPACE,
|
|
198
|
+
name: 'settings.plugin.item', key: SETTINGS_NAMESPACE, locale: LOCALE_NS,
|
|
137
199
|
}, PTCPlusSettingsCard))
|
|
138
200
|
|
|
139
201
|
ctx.inject(['slots', 'sessions'], (scope) => {
|
|
140
|
-
function PTCPlusSessionIndicator({ sessionId }) {
|
|
202
|
+
function PTCPlusSessionIndicator({ sessionId, t }) {
|
|
141
203
|
const sessions = React.useSyncExternalStore(
|
|
142
204
|
listener => scope.sessions.list.subscribe(listener),
|
|
143
205
|
() => scope.sessions.list.getSnapshot(),
|
|
@@ -148,18 +210,18 @@ window.__ModuleLoader__.load({
|
|
|
148
210
|
() => preferenceScope.getSnapshot(),
|
|
149
211
|
() => preferenceScope.getSnapshot(),
|
|
150
212
|
)
|
|
151
|
-
if (sessions.byId?.[sessionId]
|
|
213
|
+
if (!sessionUsesPtcPreset(sessions.byId?.[sessionId])
|
|
152
214
|
|| settings.status !== 'ready' || settings.value?.enabled !== true) return null
|
|
153
|
-
return h('span', { className: 'ptcPlusActive', title: '
|
|
215
|
+
return h('span', { className: 'ptcPlusActive', title: t('indicator.title') },
|
|
154
216
|
h(IconCheckOutline14, { size: 14 }), 'PTC Plus')
|
|
155
217
|
}
|
|
156
218
|
scope.slots.inject('conversation.session.header.actions', () => scope.slots.register({
|
|
157
|
-
name: 'conversation.session.header.actions', id: 'ptc-plus-active', order: -9,
|
|
219
|
+
name: 'conversation.session.header.actions', id: 'ptc-plus-active', order: -9, locale: LOCALE_NS,
|
|
158
220
|
}, PTCPlusSessionIndicator))
|
|
159
221
|
})
|
|
160
222
|
}
|
|
161
223
|
|
|
162
|
-
module.exports = { apply, inject: ['settingsScope', 'slots', 'sessions'] }
|
|
224
|
+
module.exports = { apply, inject: ['settingsScope', 'slots', 'sessions', 'locale'] }
|
|
163
225
|
return module.exports
|
|
164
226
|
},
|
|
165
227
|
})
|
|
Binary file
|
|
File without changes
|