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/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': field.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': field.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('设置失败:' + (error instanceof Error ? error.message : String(error)))
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 ? '收起 PTC Plus 设置' : '展开 PTC Plus 设置',
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' }, 'PTC 模式的会话级 TypeScript REPL。')),
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' }, '当前 DSH 实例未提供设置服务')
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 || (snapshot.writable ? '设置会在修改后立即生效' : '当前设置为只读'))),
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]?.agentPreset !== 'code'
213
+ if (!sessionUsesPtcPreset(sessions.byId?.[sessionId])
152
214
  || settings.status !== 'ready' || settings.value?.enabled !== true) return null
153
- return h('span', { className: 'ptcPlusActive', title: 'PTC Plus 已启用' },
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