@chengzhao-dev/dsh-cpp-workflow 0.6.5 → 0.6.7
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 +5 -3
- package/README.md +5 -3
- package/lib/client.js +164 -39
- package/lib/comment-policy.js +16 -2
- package/lib/index.js +3 -3
- package/package.json +4 -2
- package/skills/developing-cpp/references/comments.md +3 -3
- package/skills/developing-cpp/steps/03-declaration.md +1 -1
package/README.en.md
CHANGED
|
@@ -19,20 +19,22 @@ The exception material follows the C++ Core Guidelines' Error handling section a
|
|
|
19
19
|
Pinned npm install:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.
|
|
22
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.7
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
The matching Git tag:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.
|
|
28
|
+
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.7
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The desktop app owns the `desktop` profile. If the command-line `add` is rejected, add the plugin in the app settings and use the same pinned npm version. A Git tag install uses the prebuilt `lib/` committed in the repository and runs no build on the target machine; the package has no install script that needs approval.
|
|
32
32
|
|
|
33
33
|
### Settings
|
|
34
34
|
|
|
35
|
-
The plugin page's "C++ Workflow" card is the only entry point and manages file-header tags only: `@file` is always the file name, `@brief` is written by default, and `@author`, `@date`, `@copyright`, `@version` stay off and are written only when they hold a non-empty value; `@param
|
|
35
|
+
The plugin page's "C++ Workflow" card is the only entry point and manages file-header tags only: `@file` is always the file name, `@brief` is written by default, and `@author`, `@date`, `@copyright`, `@version` stay off and are written only when they hold a non-empty value; `@param` is required for every parameter; `@return` is written by default but may be omitted when the condition is self-evident; `@throws` only when the function throws. The preview follows the switches, saving takes effect on the next skill read, and existing sources are never rewritten. Settings navigation no longer duplicates the form.
|
|
36
|
+
|
|
37
|
+
If the page still reports that it is not connected to a configuration form after an upgrade, the window is still running the previous client bundle: restart DeepSeek Harness or select "Reload page" on the plugin page. The page retries the connection while open and switches to the editable form as soon as the host serves it; reinstalling is unnecessary. Discard changes, Restore defaults and Save are separate: discard returns to the last value read from the host, restore fills the draft, and only Save writes it.
|
|
36
38
|
|
|
37
39
|
## Workspace gate
|
|
38
40
|
|
package/README.md
CHANGED
|
@@ -19,20 +19,22 @@
|
|
|
19
19
|
npm 安装钉死版本:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.
|
|
22
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.7
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
同版本的 Git 标签:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.
|
|
28
|
+
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.7
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
|
|
32
32
|
|
|
33
33
|
### 设置
|
|
34
34
|
|
|
35
|
-
插件页的「C++ 工程流程」是唯一入口,只管文件头标签:`@file` 固定为文件名,`@brief` 默认写出,`@author`、`@date`、`@copyright`、`@version` 默认关闭且只在填了非空值时才写出;有参数就要求 `@param
|
|
35
|
+
插件页的「C++ 工程流程」是唯一入口,只管文件头标签:`@file` 固定为文件名,`@brief` 默认写出,`@author`、`@date`、`@copyright`、`@version` 默认关闭且只在填了非空值时才写出;有参数就要求 `@param`,有返回值默认要求 `@return`(成立条件自明时可省),会抛才要求 `@throws`。预览随开关即时更新,保存后下一次读取技能即生效,已有源码不会被改写。设置导航不再重复放一份。
|
|
36
|
+
|
|
37
|
+
升级插件后如果这一页仍显示“暂时没有连到配置表单”,说明当前窗口还挂着旧的前端产物:重启 DeepSeek Harness,或在插件页点“重新载入页面”。页面会在打开后自动重试连接,拿到表单就切换为可编辑,不需要重装插件。保存、撤销修改与恢复默认值分三个按钮:撤销只回到最近一次读到的值,恢复默认值只填草稿,点保存才写盘。
|
|
36
38
|
|
|
37
39
|
## 工作区门禁
|
|
38
40
|
|
package/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 由 scripts/building-client.mjs 生成,请改 src/comment-policy.ts 与 src/client
|
|
1
|
+
// 由 scripts/building-client.mjs 生成,请改 src/comment-policy.ts 与 src/client/*.js。
|
|
2
2
|
window.__ModuleLoader__.load({
|
|
3
3
|
id: "@chengzhao-dev/dsh-cpp-workflow",
|
|
4
4
|
factory: (require) => {
|
|
@@ -79,11 +79,25 @@ const DEFAULT_CONFIG = {
|
|
|
79
79
|
tparam: false,
|
|
80
80
|
retval: false,
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* 宿主把 volatile 字段交给插件时给的是可读写单元(`.get()`),设置页快照给的是普通值;
|
|
84
|
+
* 两种形状都要按同一个值读取。
|
|
85
|
+
*/
|
|
86
|
+
function unwrap(value) {
|
|
87
|
+
if (value !== null &&
|
|
88
|
+
typeof value === 'object' &&
|
|
89
|
+
typeof value.get === 'function') {
|
|
90
|
+
return value.get();
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
82
94
|
function text(value) {
|
|
83
|
-
|
|
95
|
+
const plain = unwrap(value);
|
|
96
|
+
return typeof plain === 'string' ? plain.trim() : '';
|
|
84
97
|
}
|
|
85
98
|
function flag(value, fallback) {
|
|
86
|
-
|
|
99
|
+
const plain = unwrap(value);
|
|
100
|
+
return typeof plain === 'boolean' ? plain : fallback;
|
|
87
101
|
}
|
|
88
102
|
/**
|
|
89
103
|
* 把宿主给的值补齐成完整配置;缺字段、旧版本或手工改坏的配置都回到出厂值。
|
|
@@ -223,14 +237,18 @@ const STRINGS = {
|
|
|
223
237
|
preview: '预览',
|
|
224
238
|
previewHint: '按当前开关生成,文件名用占位符表示。',
|
|
225
239
|
save: '保存',
|
|
226
|
-
reset: '
|
|
240
|
+
reset: '撤销修改',
|
|
241
|
+
defaults: '恢复默认值',
|
|
242
|
+
restored: '已填入内置默认值,点“保存”后生效。',
|
|
227
243
|
saved: '已保存,下一次读取技能时生效。',
|
|
228
|
-
rejected: '
|
|
244
|
+
rejected: '保存被拒绝:配置可能已在别处修改。请先撤销修改,再重新编辑。',
|
|
229
245
|
unsaved: '有未保存的改动',
|
|
230
246
|
loading: '正在读取配置…',
|
|
231
247
|
unavailable: '这个部署没有提供这份配置。',
|
|
232
248
|
unreadable: '当前账户不能修改这份配置。',
|
|
233
|
-
readOnly: '
|
|
249
|
+
readOnly: '这个页面暂时没有连到配置表单,下面的值是内置默认值。重启 DeepSeek Harness 后重新打开本页即可编辑。',
|
|
250
|
+
reload: '重新载入页面',
|
|
251
|
+
noService: '(configForms 服务未连接)',
|
|
234
252
|
fixed: '跟随文件名',
|
|
235
253
|
fixedHint: '值由实际文件决定,不写进配置。',
|
|
236
254
|
emptyValue: '留空则由文件自己写',
|
|
@@ -259,14 +277,18 @@ const STRINGS = {
|
|
|
259
277
|
preview: 'Preview',
|
|
260
278
|
previewHint: 'Generated from the switches above; the file name is a placeholder.',
|
|
261
279
|
save: 'Save',
|
|
262
|
-
reset: '
|
|
280
|
+
reset: 'Discard changes',
|
|
281
|
+
defaults: 'Restore defaults',
|
|
282
|
+
restored: 'Built-in defaults are filled in; select Save to apply them.',
|
|
263
283
|
saved: 'Saved; the next skill read uses it.',
|
|
264
|
-
rejected: 'The host rejected the save: the config changed elsewhere.
|
|
284
|
+
rejected: 'The host rejected the save: the config changed elsewhere. Discard your changes and edit again.',
|
|
265
285
|
unsaved: 'Unsaved changes',
|
|
266
286
|
loading: 'Reading configuration…',
|
|
267
287
|
unavailable: 'This deployment does not serve this configuration.',
|
|
268
288
|
unreadable: 'This account cannot edit the configuration.',
|
|
269
|
-
readOnly: 'This
|
|
289
|
+
readOnly: 'This page is not connected to a configuration form yet; the values below are built-in defaults. Restart DeepSeek Harness, then reopen this page to edit.',
|
|
290
|
+
reload: 'Reload page',
|
|
291
|
+
noService: ' (the configForms service is not connected)',
|
|
270
292
|
fixed: 'Follows the file name',
|
|
271
293
|
fixedHint: 'Derived from the file itself, never stored in the config.',
|
|
272
294
|
emptyValue: 'Leave empty and each file writes its own',
|
|
@@ -343,6 +365,9 @@ const CSS = `
|
|
|
343
365
|
.dcw-status{flex:1 1 200px;margin:0;color:var(--dcw-muted);font-size:12px}
|
|
344
366
|
.dcw-status[data-tone="warn"]{color:var(--dcw-danger)}
|
|
345
367
|
.dcw-status[data-tone="ok"]{color:var(--dsw-alias-state-success-primary,#1a7f37)}
|
|
368
|
+
.dcw-note-code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;opacity:.8;margin-left:6px}
|
|
369
|
+
.dcw-note-action{margin-left:auto;flex:none;padding:3px 10px;border:1px solid currentColor;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:11.5px;cursor:pointer;white-space:nowrap}
|
|
370
|
+
.dcw-note-action:hover{background:var(--dcw-bg1)}
|
|
346
371
|
@media (max-width:640px){.dcw-row{grid-template-columns:1fr;gap:4px}.dcw-row-label{padding-top:0}.dcw-flags{grid-template-columns:1fr}.dcw-flag:nth-child(2){border-top:1px solid var(--dcw-border)}}
|
|
347
372
|
`
|
|
348
373
|
|
|
@@ -468,36 +493,88 @@ function Preview(props) {
|
|
|
468
493
|
function diffOperations(draft, base) {
|
|
469
494
|
const operations = []
|
|
470
495
|
for (const key of Object.keys(DEFAULT_CONFIG)) {
|
|
471
|
-
const next =
|
|
472
|
-
const previous =
|
|
473
|
-
if (next !==
|
|
496
|
+
const next = draft[key]
|
|
497
|
+
const previous = base[key]
|
|
498
|
+
if (next !== null && typeof next === 'object') {
|
|
499
|
+
// 文件头字段整体不是 volatile,宿主只接受叶子路径(brief.enabled / brief.value)。
|
|
500
|
+
const before = previous !== null && typeof previous === 'object' ? previous : {}
|
|
501
|
+
for (const field of Object.keys(next)) {
|
|
502
|
+
if (JSON.stringify(next[field]) !== JSON.stringify(before[field])) {
|
|
503
|
+
operations.push({ op: 'set', path: [key, field], value: next[field] })
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
continue
|
|
507
|
+
}
|
|
508
|
+
if (JSON.stringify(next) !== JSON.stringify(previous)) {
|
|
509
|
+
operations.push({ op: 'set', path: [key], value: next })
|
|
510
|
+
}
|
|
474
511
|
}
|
|
475
512
|
return operations
|
|
476
513
|
}
|
|
477
514
|
|
|
515
|
+
function snapshotOf(form) {
|
|
516
|
+
if (!form) return { status: 'missing', writable: false }
|
|
517
|
+
if (typeof form.getSnapshot === 'function') return form.getSnapshot()
|
|
518
|
+
return form.state || { status: 'missing', writable: false }
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 宿主的 ConfigFormController 是可订阅的外部存储;旧调用方仍可能直接给 form.state。
|
|
522
|
+
function useFormSnapshot(form) {
|
|
523
|
+
const [snapshot, setSnapshot] = React.useState(() => snapshotOf(form))
|
|
524
|
+
React.useEffect(() => {
|
|
525
|
+
if (!form || typeof form.subscribe !== 'function' || typeof form.getSnapshot !== 'function') {
|
|
526
|
+
setSnapshot(snapshotOf(form))
|
|
527
|
+
return undefined
|
|
528
|
+
}
|
|
529
|
+
setSnapshot(form.getSnapshot())
|
|
530
|
+
return form.subscribe(() => setSnapshot(form.getSnapshot()))
|
|
531
|
+
}, [form])
|
|
532
|
+
return snapshot
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// 配置卡片本身:状态机、保存/撤销/恢复默认值,以及组合包条目里的挂点。
|
|
536
|
+
// 分片按 scripts/building-client.mjs 的模块表顺序拼接:shell → text → styles →
|
|
537
|
+
// widgets → form-state → 本文件。分片共享同一个工厂作用域,不要各自 require。
|
|
538
|
+
|
|
539
|
+
// 宿主组合包 cordis.patch.yml 插入的 profile 条目 id;设置服务按它定位表单。
|
|
540
|
+
const ENTRY_ID = 'cpp-workflow'
|
|
541
|
+
// configForms 可能晚于本插件挂载;页面在首次绘制后短暂重试,拿不到再给手动恢复入口。
|
|
542
|
+
const FORM_RETRY_MS = 500
|
|
543
|
+
const MAX_FORM_RETRIES = 20
|
|
544
|
+
|
|
478
545
|
function ConfigPage(props) {
|
|
479
546
|
const s = t()
|
|
480
547
|
ensureStyles()
|
|
481
|
-
|
|
482
|
-
const
|
|
548
|
+
// form 由挂点注入;resolveForm 兜住“服务晚到”的部署,拿到后下一次绘制自动接上。
|
|
549
|
+
const form = props.form || (typeof props.resolveForm === 'function' ? props.resolveForm() : undefined)
|
|
550
|
+
const state = useFormSnapshot(form)
|
|
551
|
+
const initial = React.useMemo(() => normalizeConfig(state.value), [])
|
|
483
552
|
const [draft, setDraft] = React.useState(initial)
|
|
484
553
|
const [base, setBase] = React.useState(initial)
|
|
485
554
|
const [dirty, setDirty] = React.useState(false)
|
|
486
555
|
const [busy, setBusy] = React.useState(false)
|
|
487
556
|
const [message, setMessage] = React.useState('')
|
|
488
557
|
const [tone, setTone] = React.useState('')
|
|
489
|
-
const
|
|
558
|
+
const [attempt, setAttempt] = React.useState(0)
|
|
559
|
+
const revision = state.revision
|
|
490
560
|
// 快照只有 status / writable / revision;旧版本多出来的 mode 字段已不存在,
|
|
491
561
|
// 再按它判断会把所有可写表单误判成只读。
|
|
492
|
-
const ready = Boolean(form &&
|
|
493
|
-
const status =
|
|
562
|
+
const ready = Boolean(form && state.status === 'ready' && state.writable)
|
|
563
|
+
const status = state.status
|
|
564
|
+
const missing = !form
|
|
494
565
|
|
|
495
566
|
React.useEffect(() => {
|
|
496
|
-
if (dirty ||
|
|
497
|
-
const next = normalizeConfig(
|
|
567
|
+
if (dirty || state.status !== 'ready') return
|
|
568
|
+
const next = normalizeConfig(state.value)
|
|
498
569
|
setBase(next)
|
|
499
570
|
setDraft(next)
|
|
500
|
-
}, [revision, status, dirty, form])
|
|
571
|
+
}, [revision, status, dirty, form, state.value])
|
|
572
|
+
|
|
573
|
+
React.useEffect(() => {
|
|
574
|
+
if (form || attempt >= MAX_FORM_RETRIES || typeof setTimeout !== 'function') return undefined
|
|
575
|
+
const timer = setTimeout(() => setAttempt((value) => value + 1), FORM_RETRY_MS)
|
|
576
|
+
return () => clearTimeout(timer)
|
|
577
|
+
}, [form, attempt])
|
|
501
578
|
|
|
502
579
|
const update = (patch) => {
|
|
503
580
|
setDraft((current) => ({ ...current, ...patch }))
|
|
@@ -506,6 +583,7 @@ function ConfigPage(props) {
|
|
|
506
583
|
setTone('')
|
|
507
584
|
}
|
|
508
585
|
|
|
586
|
+
// 撤销:回到最近一次从宿主读到的值,不写盘。
|
|
509
587
|
const reset = () => {
|
|
510
588
|
setDraft(base)
|
|
511
589
|
setDirty(false)
|
|
@@ -513,6 +591,20 @@ function ConfigPage(props) {
|
|
|
513
591
|
setTone('')
|
|
514
592
|
}
|
|
515
593
|
|
|
594
|
+
// 恢复默认值:只填进草稿,点“保存”后才写盘,避免误触直接覆盖已保存的配置。
|
|
595
|
+
const restoreDefaults = () => {
|
|
596
|
+
setDraft(normalizeConfig(DEFAULT_CONFIG))
|
|
597
|
+
setDirty(true)
|
|
598
|
+
setMessage(s.restored)
|
|
599
|
+
setTone('')
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const reload = () => {
|
|
603
|
+
if (typeof window !== 'undefined' && window.location && typeof window.location.reload === 'function') {
|
|
604
|
+
window.location.reload()
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
516
608
|
const save = async () => {
|
|
517
609
|
if (!ready || busy) return
|
|
518
610
|
const operations = diffOperations(draft, base)
|
|
@@ -526,6 +618,9 @@ function ConfigPage(props) {
|
|
|
526
618
|
setMessage(s.saved)
|
|
527
619
|
setTone('ok')
|
|
528
620
|
} else {
|
|
621
|
+
// 被拒时宿主已刷新镜像;把基准对齐到新快照,撤销才不会回到过期值。
|
|
622
|
+
const fresh = snapshotOf(form)
|
|
623
|
+
if (fresh && fresh.status === 'ready') setBase(normalizeConfig(fresh.value))
|
|
529
624
|
setMessage(s.rejected)
|
|
530
625
|
setTone('warn')
|
|
531
626
|
}
|
|
@@ -550,12 +645,22 @@ function ConfigPage(props) {
|
|
|
550
645
|
h('h3', { className: 'dcw-title' }, s.title),
|
|
551
646
|
h('p', { className: 'dcw-lede' }, s.intro),
|
|
552
647
|
),
|
|
553
|
-
|
|
648
|
+
missing
|
|
554
649
|
? h(
|
|
555
650
|
'div',
|
|
556
|
-
{ className: 'dcw-note', role: 'status' },
|
|
651
|
+
{ className: 'dcw-note', role: 'status', 'data-tone': 'warn' },
|
|
557
652
|
h('span', { className: 'dcw-note-dot' }),
|
|
558
|
-
h(
|
|
653
|
+
h(
|
|
654
|
+
'span',
|
|
655
|
+
null,
|
|
656
|
+
s.readOnly,
|
|
657
|
+
props.hasService === false ? h('span', { className: 'dcw-note-code' }, s.noService) : null,
|
|
658
|
+
),
|
|
659
|
+
h(
|
|
660
|
+
'button',
|
|
661
|
+
{ type: 'button', className: 'dcw-note-action', onClick: reload },
|
|
662
|
+
s.reload,
|
|
663
|
+
),
|
|
559
664
|
)
|
|
560
665
|
: null,
|
|
561
666
|
editable && !ready && status !== 'missing'
|
|
@@ -657,35 +762,55 @@ function ConfigPage(props) {
|
|
|
657
762
|
),
|
|
658
763
|
h('button', { type: 'button', className: 'dcw-btn', onClick: reset, disabled: busy || !dirty }, s.reset),
|
|
659
764
|
h(
|
|
660
|
-
'
|
|
661
|
-
{ className: 'dcw-
|
|
662
|
-
|
|
765
|
+
'button',
|
|
766
|
+
{ type: 'button', className: 'dcw-btn', onClick: restoreDefaults, disabled: busy || !ready },
|
|
767
|
+
s.defaults,
|
|
663
768
|
),
|
|
769
|
+
h('p', { className: 'dcw-status', role: 'status', 'data-tone': tone }, message || (dirty ? s.unsaved : '')),
|
|
664
770
|
)
|
|
665
771
|
: null,
|
|
666
772
|
)
|
|
667
773
|
}
|
|
668
774
|
|
|
669
|
-
function page(ownerProps) {
|
|
670
|
-
return h(ConfigPage, ownerProps || {})
|
|
775
|
+
function page(ownerProps, form, resolveForm, hasService) {
|
|
776
|
+
return h(ConfigPage, { ...(ownerProps || {}), form, resolveForm, hasService })
|
|
671
777
|
}
|
|
672
778
|
|
|
673
779
|
function apply(ctx) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
)
|
|
780
|
+
// 每次取表单都重新查询服务:宿主可能在本插件之后才挂载 configForms。
|
|
781
|
+
const resolveForm = () => {
|
|
782
|
+
if (!ctx.configForms || typeof ctx.configForms.get !== 'function') return undefined
|
|
783
|
+
try {
|
|
784
|
+
return ctx.configForms.get(ENTRY_ID)
|
|
785
|
+
} catch {
|
|
786
|
+
return undefined
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const hasService = Boolean(ctx.configForms && typeof ctx.configForms.get === 'function')
|
|
790
|
+
const mount = () =>
|
|
791
|
+
ctx.slots.inject('plugins.bundle.config', () =>
|
|
792
|
+
ctx.slots.register(
|
|
793
|
+
{
|
|
794
|
+
name: 'plugins.bundle.config',
|
|
795
|
+
key: PACKAGE_NAME,
|
|
796
|
+
label: () => t().nav,
|
|
797
|
+
},
|
|
798
|
+
(ownerProps) =>
|
|
799
|
+
ownerProps && ownerProps.view === 'summary'
|
|
800
|
+
? null
|
|
801
|
+
: page(ownerProps, undefined, resolveForm, hasService),
|
|
802
|
+
),
|
|
803
|
+
)
|
|
804
|
+
if (ctx.configForms && typeof ctx.configForms.whileServed === 'function') {
|
|
805
|
+
ctx.effect(() => ctx.configForms.whileServed([ENTRY_ID], mount), 'dsh-cpp-workflow: 配置挂点')
|
|
806
|
+
} else {
|
|
807
|
+
mount()
|
|
808
|
+
}
|
|
684
809
|
}
|
|
685
810
|
|
|
686
811
|
module.exports = {
|
|
687
812
|
name: PACKAGE_NAME,
|
|
688
|
-
inject: ['slots'],
|
|
813
|
+
inject: ['slots', 'configForms'],
|
|
689
814
|
apply,
|
|
690
815
|
ConfigPage,
|
|
691
816
|
diffOperations,
|
package/lib/comment-policy.js
CHANGED
|
@@ -72,11 +72,25 @@ export const DEFAULT_CONFIG = {
|
|
|
72
72
|
tparam: false,
|
|
73
73
|
retval: false,
|
|
74
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* 宿主把 volatile 字段交给插件时给的是可读写单元(`.get()`),设置页快照给的是普通值;
|
|
77
|
+
* 两种形状都要按同一个值读取。
|
|
78
|
+
*/
|
|
79
|
+
function unwrap(value) {
|
|
80
|
+
if (value !== null &&
|
|
81
|
+
typeof value === 'object' &&
|
|
82
|
+
typeof value.get === 'function') {
|
|
83
|
+
return value.get();
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
75
87
|
function text(value) {
|
|
76
|
-
|
|
88
|
+
const plain = unwrap(value);
|
|
89
|
+
return typeof plain === 'string' ? plain.trim() : '';
|
|
77
90
|
}
|
|
78
91
|
function flag(value, fallback) {
|
|
79
|
-
|
|
92
|
+
const plain = unwrap(value);
|
|
93
|
+
return typeof plain === 'boolean' ? plain : fallback;
|
|
80
94
|
}
|
|
81
95
|
/**
|
|
82
96
|
* 把宿主给的值补齐成完整配置;缺字段、旧版本或手工改坏的配置都回到出厂值。
|
package/lib/index.js
CHANGED
|
@@ -223,11 +223,11 @@ async function getSkill(candidate, options = {}, commentConfig = undefined) {
|
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
225
|
export function apply(ctx, config = undefined) {
|
|
226
|
-
//
|
|
227
|
-
|
|
226
|
+
// 配置对象由宿主在 volatile 保存时原地更新,所以每次 get() 都重新读取,
|
|
227
|
+
// 不能在这里缓存成一份快照,否则设置页改了标签开关,技能正文仍是旧值。
|
|
228
228
|
ctx.skills.registerProvider(() => ({
|
|
229
229
|
name: PROVIDER,
|
|
230
230
|
list: listSkills,
|
|
231
|
-
get: (candidate, options) => getSkill(candidate, options,
|
|
231
|
+
get: (candidate, options) => getSkill(candidate, options, normalizeConfig(config)),
|
|
232
232
|
}));
|
|
233
233
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chengzhao-dev/dsh-cpp-workflow",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "写 C++ 源码、配 CMake、用 Clang 构建;头文件用 /// Doxygen 文档注释,函数体用 // 分段说明,文件头标签可在设置里改。 · Write C++ sources, configure CMake, and build with Clang; /// Doxygen on declarations, // section notes in bodies, and configurable file-header tags.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"client": {
|
|
33
33
|
"platform": "web",
|
|
34
34
|
"inject": [
|
|
35
|
-
"@deepseek-ai/dsh-client-ui-slots"
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager"
|
|
36
38
|
]
|
|
37
39
|
},
|
|
38
40
|
"catalog": {
|
|
@@ -67,7 +67,7 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
67
67
|
有参数或有返回值的函数写两段:第一段写结果、失败方式或检查项;第二段按签名把标签写齐。
|
|
68
68
|
|
|
69
69
|
1. 第一段:普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」。
|
|
70
|
-
2. 第二段:每个参数一条 `@param
|
|
70
|
+
2. 第二段:每个参数一条 `@param`;有返回值默认写 `@return`,成立条件已由函数名与返回类型说清时可省略;会抛才写 `@throws`。
|
|
71
71
|
|
|
72
72
|
```cpp
|
|
73
73
|
/// 检查坐标能否落子,不修改棋盘。
|
|
@@ -78,9 +78,9 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
78
78
|
[[nodiscard]] PlaceCellResult CheckPlaceable(CellPosition position, CellType cell_type) const;
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
- 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 只写「什么情况下为 true
|
|
81
|
+
- 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 只写「什么情况下为 true」,再补一句「不检查什么」。`Is`、`Has` 开头的名字与 `bool` 返回类型已经把成立条件说清时可省略 `@return`,不为了形式补一句同义反复。
|
|
82
82
|
- `@param` 每个参数都写,写范围、单位或所有权,不写「索引」这类同义反复;没有参数就不写空的 `@param`。
|
|
83
|
-
- `@return`
|
|
83
|
+
- `@return` 默认写,写返回值怎么读或失败时返回什么;返回类型与函数名已把条件说清时可省略,不要用「见上」或重复第一段。
|
|
84
84
|
- `@throws` 必须与 `references/exceptions.md` 一致;不抛异常的函数不写。
|
|
85
85
|
- 私有函数同样写契约,不能默认调用者知道前置条件;输出参数标 `[out]`,既读又写标 `[in,out]`。
|
|
86
86
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- 头文件只就地定义大约 10 行以内的短函数;更长或需要完整定义的普通函数只在 `.cpp` 定义,模板和 `constexpr` 例外。
|
|
15
15
|
- 失败是调用方的正常结果时,用结果类型、`bool` + 输出参数或 `std::optional` 表达;函数无法完成自己的任务、或构造函数无法建立 invariant 时,才在声明注释里写清抛出的异常类型;越界用 `std::out_of_range`,前置条件被破坏用 `std::logic_error`,只有调用方必须区分时才新增派生类型;Debug/Release 的抛出条件相同。消息句式见 `references/exceptions.md` 的「消息」节。
|
|
16
16
|
- 一次操作有多种必须区分的正常结果时,用 `enum class` 返回(如落子:成功、越界、类型非法、已占),不用 `bool` 把原因压掉;成功还需要附带数据时再考虑 `std::expected` 或结果结构,成功无数据时不用。纯查询谓词仍用 `bool`,不要把 `IsEmpty` 改成三态。
|
|
17
|
-
- 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(例如 `默认构造函数:得到空棋盘、每个格子都是 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param
|
|
17
|
+
- 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(例如 `默认构造函数:得到空棋盘、每个格子都是 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param`,有返回值默认写 `@return`(成立条件自明时可省),会抛才写 `@throws`;构造、析构和 `void` 无参数函数写两段后置条件,不写空标签。数据成员的主注释放在声明上一行并用 `///`,先说明这个成员是什么,再用全角冒号补名字和类型看不出的轴向、哨兵、所有权与改写时机,行尾不写补充;布局定文件头、位置与行尾;排版定符号、宽度与标签;润色删复述。类型注释不罗列成员功能、不写领域尺寸、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
|
|
18
18
|
- `std::format` 用于组合消息;声明里不要承诺 `std::print`(C++23)。
|
|
19
19
|
|
|
20
20
|
## 情况
|