dsh-auto-open-web 0.1.9 → 0.1.13

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/lib/client.js CHANGED
@@ -1,19 +1,20 @@
1
- // dsh-auto-open-web — Client bundle(设置卡片,与官方 Shell/Agent loop 卡片同构)
1
+ // dsh-auto-open-web — Client bundle(设置卡片;UI 完全自绘,不依赖官方组件/CSS 模板)
2
2
  // 格式:window.__ModuleLoader__.load({ id, factory });factory(require) 的
3
- // require 解析浏览器端冻结模块表(react、@deepseek-ai/dsh-client-ui-primitives、
4
- // @deepseek-ai/dsh-client-runtime)。
3
+ // require 解析浏览器端模块表(react 为壳层静态模块,@deepseek-ai/dsh-client-runtime
4
+ // 为动态包)。**不** require @deepseek-ai/dsh-client-ui-primitives 等官方 UI 包。
5
5
  //
6
- // 架构完全对齐官方 @deepseek-ai/dsh-client-ui-settings-plugins:
7
- // - 数据通道:settingsScope.bind({namespace})(官方 settings 域,dsh ≥0.1.0-rc.7
8
- // 已取消第三方命名空间白名单)
9
- // - 表单:CardForm 移植(FieldSpec format/parse、staged 草稿、plan/save 计划、
10
- // 写后以 user 层落地校验、失败保留草稿)
11
- // - 字段:ValueField 移植(overridden 徽章 + 恢复默认 + invalid 提示;browserPath
12
- // 附带"浏览/测试"按钮槽);checkbox/radio 值型字段自绘但带同款徽章
13
- // - 外壳:PluginCard 移植(折叠/未保存标记/只读提示/放弃/保存/保存失败)
14
- // - 文案:locale 注册(zh/en),与官方同机制
15
- // - 依赖:react/primitives(壳层 seed 词)+ dsh-client-runtime(动态包)
16
- // + 代码级 5 服务,与官方 settings-plugins 一致
6
+ // 依赖边界(与官方数据的对接面,均为公开接口;本插件声明 dsh ≥0.1.0-rc.8):
7
+ // - settingsScope:官方 settings 域(命名空间 `auto-open-web`;第三方命名空间
8
+ // 白名单自 rc.7 起已取消,rc.8 下无限制)
9
+ // - slots:settings.plugin.item 插槽(keyed 注册,rc.7 起要求 options.key)
10
+ // - locale:文案注册(zh/en/ja/ko/fr/de/ru,跟随界面语言)
11
+ // - dsh-client-runtime:createSnapshotStore 快照 store(缺失时内置兜底实现)
12
+ //
13
+ // 视觉(全部自产):
14
+ // - 卡片外壳、字段、按钮、输入框、复选/单选、徽章、图标均为手写实现;
15
+ // - 观感通过官方设计令牌变量(--dsw-alias-* / --dsw-static-*)对齐,浅/深色
16
+ // 自动跟随主题,不复刻官方任何样式表或组件代码;
17
+ // - CSS 经标准注入约定(<style data-plugin / data-plugin-css> 去重)挂载。
17
18
  //
18
19
  // 宿主保留的辅助路由(官方通道无法覆盖):
19
20
  // POST /auto-open-web/pick-browser → 原生"浏览"对话框
@@ -26,22 +27,59 @@ window.__ModuleLoader__.load({
26
27
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
27
28
  let React = require('react')
28
29
 
29
- // ---- 内置 PluginCard.module.css(官方卡片外观,原样注入) ----
30
- var CARD_CSS = '.YyYd_a_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.YyYd_a_card:hover{border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}.YyYd_a_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}.YyYd_a_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.YyYd_a_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.YyYd_a_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.YyYd_a_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.YyYd_a_description{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:13px;line-height:1.5}.YyYd_a_chevron{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));flex:none;transition:transform .16s}.YyYd_a_chevronOpen{transform:rotate(180deg)}.YyYd_a_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.YyYd_a_readOnly{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:12px 0 0;font-size:12px;line-height:1.5}.YyYd_a_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.YyYd_a_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.YyYd_a_failed{min-width:0;color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));flex:1;margin:0;font-size:12px;line-height:1.5}.YyYd_a_discard,.YyYd_a_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.YyYd_a_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.YyYd_a_discard:disabled{opacity:.55;cursor:default}.YyYd_a_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.YyYd_a_save{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-inverse,#fff);font-weight:600}.YyYd_a_save:disabled{opacity:.55;cursor:default}'
31
- var CARD_TAG = 'dsh-auto-open-web/PluginCard.module.css'
32
- // ---- 内置 ValueField 字段布局样式(label/hint 排版,原样注入) ----
33
- var FIELDS_CSS = '.At1oFq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.At1oFq_field+.At1oFq_field{border-top:1px solid var(--dsw-alias-border-l2)}.At1oFq_head{align-items:center;gap:8px;display:flex}.At1oFq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.At1oFq_badges{align-items:center;gap:8px;display:inline-flex}.At1oFq_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.At1oFq_badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}.At1oFq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.At1oFq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.At1oFq_reset:disabled{cursor:default}.At1oFq_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.At1oFq_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.At1oFq_input:disabled{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));cursor:default}.At1oFq_inputInvalid{border-color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));}.At1oFq_invalid{color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));margin:0;font-size:12px;line-height:1.5}.At1oFq_hint{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:0;font-size:12px;line-height:1.5}'
34
- var FIELDS_TAG = 'dsh-auto-open-web/Fields.module.css'
35
- // ---- 极少量自有布局 CSS(仅行排列/复选行,不改变观感) ----
36
- var EXTRA_CSS =
30
+ // ── 自绘样式(观感对齐官方设计令牌;类名全部自有 aow-*) ────────────────
31
+ // 卡片外壳:圆角 12、l2 边框、layer-3 底;悬停/展开态换层,折叠箭头旋转。
32
+ var CARD_CSS =
33
+ '.aow-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}' +
34
+ '.aow-card:hover{border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}' +
35
+ '.aow-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}' +
36
+ '.aow-cardHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}' +
37
+ '.aow-cardHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}' +
38
+ '.aow-cardHeadText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}' +
39
+ '.aow-cardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}' +
40
+ '.aow-cardDescription{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:13px;line-height:1.5}' +
41
+ '.aow-cardChevron{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));flex:none;transition:transform .16s;display:inline-flex}' +
42
+ '.aow-cardChevronOpen{transform:rotate(180deg)}' +
43
+ '.aow-cardBody{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}' +
44
+ '.aow-cardReadOnly{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:12px 0 0;font-size:12px;line-height:1.5}' +
45
+ '.aow-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}' +
46
+ '.aow-cardFooter{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}' +
47
+ '.aow-cardFailed{min-width:0;color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));flex:1;margin:0;font-size:12px;line-height:1.5}' +
48
+ // 按钮:描边(outline)与主色(save)两种,禁用半透明。
49
+ '.aow-btn{appearance:none;font:inherit;cursor:pointer;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;border:1px solid transparent}' +
50
+ '.aow-btn:disabled{opacity:.55;cursor:default}' +
51
+ '.aow-btnOutline{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}' +
52
+ '.aow-btnOutline:hover:not(:disabled){color:var(--dsw-alias-label-primary)}' +
53
+ '.aow-btnPrimary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-inverse,#fff);border-color:var(--dsw-alias-brand-primary);font-weight:600}' +
54
+ '.aow-btnPrimary:hover:not(:disabled){filter:brightness(.95)}'
55
+ // 字段区:label 行 + 控件 + hint;相邻字段以 l2 分隔;徽章/恢复默认同观感。
56
+ var FIELDS_CSS =
57
+ '.aow-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}' +
58
+ '.aow-field+.aow-field{border-top:1px solid var(--dsw-alias-border-l2)}' +
59
+ '.aow-fieldHead{align-items:center;gap:8px;display:flex}' +
60
+ '.aow-fieldLabel{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}' +
61
+ '.aow-badges{align-items:center;gap:8px;display:inline-flex}' +
62
+ '.aow-badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}' +
63
+ '.aow-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}' +
64
+ '.aow-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}' +
65
+ '.aow-reset:disabled{cursor:default}' +
66
+ '.aow-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}' +
67
+ '.aow-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}' +
68
+ '.aow-input:disabled{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));cursor:default}' +
69
+ '.aow-inputInvalid{border-color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary))}' +
70
+ '.aow-invalid{color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));margin:0;font-size:12px;line-height:1.5}' +
71
+ '.aow-hint{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:0;font-size:12px;line-height:1.5}'
72
+ // 布局与杂项:行排列、复选/单选、测试结果状态。
73
+ var CONTROLS_CSS =
37
74
  '.aow-row{display:flex;gap:8px;align-items:center}' +
38
- '.aow-grow{flex:1;min-width:0}' +
39
75
  '.aow-check{display:flex;align-items:center;gap:8px;cursor:pointer}' +
40
76
  '.aow-check input{accent-color:var(--dsw-alias-brand-primary);width:15px;height:15px;margin:0;flex:none}' +
41
77
  '.aow-check input:disabled{cursor:default}' +
42
78
  '.aow-test-ok{color:var(--dsw-alias-state-success-primary);margin:4px 0 0;font-size:12px;line-height:1.5}' +
43
79
  '.aow-test-fail{color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));margin:4px 0 0;font-size:12px;line-height:1.5}'
44
- var EXTRA_TAG = 'dsh-auto-open-web/Extra.module.css'
80
+ var CARD_TAG = 'dsh-auto-open-web/Card.module.css'
81
+ var FIELDS_TAG = 'dsh-auto-open-web/Fields.module.css'
82
+ var CONTROLS_TAG = 'dsh-auto-open-web/Controls.module.css'
45
83
  function injectCss(tagId, css) {
46
84
  if (typeof document === 'undefined') return
47
85
  if (document.querySelector('style[data-plugin-css=' + JSON.stringify(tagId) + ']') !== null) return
@@ -53,29 +91,17 @@ window.__ModuleLoader__.load({
53
91
  }
54
92
  injectCss(CARD_TAG, CARD_CSS)
55
93
  injectCss(FIELDS_TAG, FIELDS_CSS)
56
- injectCss(EXTRA_TAG, EXTRA_CSS)
94
+ injectCss(CONTROLS_TAG, CONTROLS_CSS)
57
95
 
58
- // ---- 官方共享模块(冻结表) ----
59
- // 与官方 settings-plugins 同源:react/primitives 是壳层 seed 词,
60
- // dsh-client-runtime 是动态包(经 /plugins/<pkg>/client.js 提供)。
61
- // 各 require 独立容错:任一可选模块缺失不得拖垮其余模块(尤其 runtime)。
62
- var prim = null
96
+ // ── 共享模块 ─────────────────────────────────────────────────────────
97
+ // react(壳层静态模块)+ dsh-client-runtime(动态包,快照 store)。
98
+ // runtime 缺失或版本不含 createSnapshotStore 时退化为内置最小实现。
63
99
  var runtime = null
64
- try {
65
- prim = require('@deepseek-ai/dsh-client-ui-primitives')
66
- } catch (e) {
67
- console.error('[auto-open-web] primitives require failed:', e !== null && e !== undefined && e.message !== undefined ? e.message : String(e))
68
- }
69
100
  try {
70
101
  runtime = require('@deepseek-ai/dsh-client-runtime')
71
102
  } catch (e) {
72
103
  console.error('[auto-open-web] runtime require failed:', e !== null && e !== undefined && e.message !== undefined ? e.message : String(e))
73
104
  }
74
- var Button = prim !== null && prim.Button !== undefined ? prim.Button : null
75
- var Input = prim !== null && prim.Input !== undefined ? prim.Input : null
76
- var ChevronIcon = prim !== null && prim.IconChevronDownOutline14 !== undefined ? prim.IconChevronDownOutline14 : null
77
- // 快照 store 契约与官方一致:{ getSnapshot, subscribe, set }。runtime 缺失
78
- // 或版本不含 createSnapshotStore 时退化为内置最小实现(卡片仍可用)。
79
105
  var createSnapshotStore = runtime !== null && runtime.createSnapshotStore !== undefined ? runtime.createSnapshotStore : fallbackSnapshotStore
80
106
  function fallbackSnapshotStore(init) {
81
107
  let value = init
@@ -95,18 +121,49 @@ window.__ModuleLoader__.load({
95
121
  }
96
122
  }
97
123
 
98
- function ProxyInput(props) {
99
- if (Input !== null) return React.createElement(Input, props)
100
- const rest = Object.assign({}, props)
101
- delete rest.className
102
- return React.createElement('input', Object.assign({ className: 'At1oFq_input' + (props.className !== undefined ? ' ' + props.className : '') }, rest))
103
- }
104
- function ProxyButton(props) {
105
- if (Button !== null) return React.createElement(Button, props)
106
- const rest = Object.assign({}, props)
107
- delete rest.className
108
- return React.createElement('button', Object.assign({ type: 'button', className: 'YyYd_a_discard' + (props.className !== undefined ? ' ' + props.className : '') }, rest))
124
+ // ── 自绘基础控件 ─────────────────────────────────────────────────────
125
+ /** 14px 折叠箭头(手绘 SVG,currentColor 跟随令牌) */
126
+ function IconChevronDown(props) {
127
+ return React.createElement('svg', {
128
+ width: 14,
129
+ height: 14,
130
+ viewBox: '0 0 14 14',
131
+ fill: 'none',
132
+ 'aria-hidden': true,
133
+ className: props.className
134
+ },
135
+ React.createElement('path', {
136
+ d: 'M3.5 5.25L7 8.75L10.5 5.25',
137
+ stroke: 'currentColor',
138
+ strokeWidth: 1.5,
139
+ strokeLinecap: 'round',
140
+ strokeLinejoin: 'round'
141
+ })
142
+ )
109
143
  }
144
+ /** 按钮:variant = outline | primary。 */
145
+ function Btn(props) {
146
+ const cls = 'aow-btn ' + (props.variant === 'primary' ? 'aow-btnPrimary' : 'aow-btnOutline')
147
+ return React.createElement('button', {
148
+ type: 'button',
149
+ className: cls,
150
+ disabled: props.disabled === true,
151
+ onClick: props.onClick
152
+ }, props.children)
153
+ }
154
+ /** 文本输入框。 */
155
+ function TextInput(props) {
156
+ return React.createElement('input', {
157
+ id: props.id,
158
+ className: 'aow-input' + (props.invalid === true ? ' aow-inputInvalid' : ''),
159
+ type: 'text',
160
+ value: props.value,
161
+ placeholder: props.placeholder !== undefined ? props.placeholder : '',
162
+ disabled: props.disabled === true,
163
+ onChange: (event) => props.onChange(event.target.value)
164
+ })
165
+ }
166
+ /** 复选行(label + 说明文字)。 */
110
167
  function Checkbox(props) {
111
168
  return React.createElement('label', { className: 'aow-check' },
112
169
  React.createElement('input', {
@@ -115,9 +172,10 @@ window.__ModuleLoader__.load({
115
172
  disabled: props.disabled === true,
116
173
  onChange: (event) => props.onChange(event.target.checked)
117
174
  }),
118
- React.createElement('span', { className: 'At1oFq_hint' }, props.children)
175
+ React.createElement('span', { className: 'aow-hint' }, props.children)
119
176
  )
120
177
  }
178
+ /** 单选行。 */
121
179
  function Radio(props) {
122
180
  return React.createElement('label', { className: 'aow-check' },
123
181
  React.createElement('input', {
@@ -127,86 +185,81 @@ window.__ModuleLoader__.load({
127
185
  disabled: props.disabled === true,
128
186
  onChange: () => props.onChange(props.value)
129
187
  }),
130
- React.createElement('span', { className: 'At1oFq_hint' }, props.children)
188
+ React.createElement('span', { className: 'aow-hint' }, props.children)
131
189
  )
132
190
  }
133
- /** 字段头部"已覆盖 + 恢复默认"徽章(官方 ValueField 同款)。 */
134
- function FieldHeadBadges(props) {
135
- if (!props.overridden) return null
136
- return React.createElement('span', { className: 'At1oFq_badges' },
137
- React.createElement('span', { className: 'At1oFq_badge' }, props.overriddenLabel),
138
- React.createElement('button', { type: 'button', className: 'At1oFq_reset', disabled: props.disabled, onClick: props.onReset }, props.resetLabel)
139
- )
191
+ /** 字段头部徽章组:"已覆盖" + "恢复默认"(或仅静态徽章)。 */
192
+ function FieldBadges(props) {
193
+ const children = []
194
+ if (props.static !== undefined && props.static !== null) {
195
+ children.push(React.createElement('span', { className: 'aow-badge' }, props.static))
196
+ }
197
+ if (props.overridden === true) {
198
+ children.push(
199
+ React.createElement('span', { className: 'aow-badge' }, props.overriddenLabel),
200
+ React.createElement('button', { type: 'button', className: 'aow-reset', disabled: props.disabled === true, onClick: props.onReset }, props.resetLabel)
201
+ )
202
+ }
203
+ if (children.length === 0) return null
204
+ return React.createElement('span', { className: 'aow-badges' }, ...children)
140
205
  }
141
206
 
142
- // ---- 官方 PluginCard 移植(settings.plugin.item 卡片外壳) ----
143
- function PluginCard(props) {
207
+ // ── 自绘卡片外壳(观感对齐官方设置卡片) ─────────────────────────────
208
+ // 折叠为卡片本地状态;展开时显示只读提示、控件、页脚(放弃/保存)
209
+ // 命名空间不可用时整卡不渲染(部署未组合本插件时不留痕迹)。
210
+ function Card(props) {
144
211
  const [open, setOpen] = React.useState(false)
145
212
  const { state } = props
146
213
  if (!state.available) return null
147
214
  const title = props.t(props.titleKey)
148
215
  const blocked = !state.dirty || state.invalid || state.saving
149
- return React.createElement('li', { className: 'YyYd_a_card' + (open ? ' YyYd_a_cardOpen' : '') },
216
+ return React.createElement('li', { className: 'aow-card' + (open ? ' aow-cardOpen' : '') },
150
217
  React.createElement('button', {
151
218
  type: 'button',
152
- className: 'YyYd_a_header',
219
+ className: 'aow-cardHeader',
153
220
  'aria-expanded': open,
154
221
  'aria-label': (open ? props.t('collapse') : props.t('expand')) + ': ' + title,
155
222
  onClick: () => setOpen(!open)
156
223
  },
157
- React.createElement('span', { className: 'YyYd_a_headText' },
158
- React.createElement('span', { className: 'YyYd_a_name' }, title),
159
- React.createElement('span', { className: 'YyYd_a_description' }, props.t(props.descriptionKey))
224
+ React.createElement('span', { className: 'aow-cardHeadText' },
225
+ React.createElement('span', { className: 'aow-cardName' }, title),
226
+ React.createElement('span', { className: 'aow-cardDescription' }, props.t(props.descriptionKey))
160
227
  ),
161
- state.dirty ? React.createElement('span', { className: 'YyYd_a_pending' }, props.t('unsaved')) : null,
162
- ChevronIcon !== null
163
- ? React.createElement(ChevronIcon, { className: 'YyYd_a_chevron' + (open ? ' YyYd_a_chevronOpen' : '') })
164
- : React.createElement('span', { className: 'YyYd_a_chevron' + (open ? ' YyYd_a_chevronOpen' : '') }, '▾')
228
+ state.dirty ? React.createElement('span', { className: 'aow-pending' }, props.t('unsaved')) : null,
229
+ React.createElement(IconChevronDown, { className: 'aow-cardChevron' + (open ? ' aow-cardChevronOpen' : '') })
165
230
  ),
166
- open ? React.createElement('div', { className: 'YyYd_a_body' },
167
- !state.writable ? React.createElement('p', { className: 'YyYd_a_readOnly', role: 'status' }, props.t('readOnly')) : null,
231
+ open ? React.createElement('div', { className: 'aow-cardBody' },
232
+ !state.writable ? React.createElement('p', { className: 'aow-cardReadOnly', role: 'status' }, props.t('readOnly')) : null,
168
233
  props.children,
169
- React.createElement('div', { className: 'YyYd_a_footer' },
170
- state.failed ? React.createElement('p', { className: 'YyYd_a_failed', role: 'status' }, props.t('saveFailed')) : null,
171
- React.createElement('button', { type: 'button', className: 'YyYd_a_discard', disabled: !state.dirty || state.saving, onClick: props.onDiscard }, props.t('discard')),
172
- React.createElement('button', { type: 'button', className: 'YyYd_a_save', disabled: blocked, onClick: props.onSave }, state.saving ? props.t('saving') : props.t('save'))
234
+ React.createElement('div', { className: 'aow-cardFooter' },
235
+ state.failed ? React.createElement('p', { className: 'aow-cardFailed', role: 'status' }, props.t('saveFailed')) : null,
236
+ React.createElement(Btn, { disabled: !state.dirty || state.saving, onClick: props.onDiscard }, props.t('discard')),
237
+ React.createElement(Btn, { variant: 'primary', disabled: blocked, onClick: props.onSave }, state.saving ? props.t('saving') : props.t('save'))
173
238
  )
174
239
  ) : null
175
240
  )
176
241
  }
177
242
 
178
- // ---- 官方 ValueField 移植(带 overridden 徽章/重置/invalid;extra 为按钮槽) ----
179
- function ValueField(props) {
180
- return React.createElement('div', { className: 'At1oFq_field' },
181
- React.createElement('div', { className: 'At1oFq_head' },
182
- React.createElement('label', { className: 'At1oFq_label', htmlFor: props.id }, props.label),
183
- props.overridden
184
- ? React.createElement('span', { className: 'At1oFq_badges' },
185
- React.createElement('span', { className: 'At1oFq_badge' }, props.overriddenLabel),
186
- React.createElement('button', { type: 'button', className: 'At1oFq_reset', disabled: props.disabled, onClick: props.onReset }, props.resetLabel)
187
- )
188
- : null
243
+ // ── 自绘字段布局(label + 控件 + hint + 可选页脚) ──────────────────
244
+ function Field(props) {
245
+ const hintText = props.invalid === true ? props.invalidLabel : props.hint
246
+ return React.createElement('div', { className: 'aow-field' },
247
+ React.createElement('div', { className: 'aow-fieldHead' },
248
+ React.createElement('span', { className: 'aow-fieldLabel' }, props.label),
249
+ props.badges !== undefined && props.badges !== null ? props.badges : null
189
250
  ),
190
- React.createElement('div', { className: 'aow-row' },
191
- React.createElement('input', {
192
- id: props.id,
193
- className: props.invalid ? 'At1oFq_input At1oFq_inputInvalid' : 'At1oFq_input',
194
- type: 'text',
195
- value: props.text,
196
- placeholder: props.placeholder !== undefined ? props.placeholder : '',
197
- disabled: props.disabled,
198
- onChange: (event) => props.onEdit(event.target.value)
199
- }),
200
- props.extra !== undefined ? props.extra : null
201
- ),
202
- React.createElement('p', { className: props.invalid ? 'At1oFq_invalid' : 'At1oFq_hint' },
203
- props.invalid ? props.invalidLabel : props.hint
204
- ),
205
- props.footer !== undefined ? props.footer : null
251
+ props.control,
252
+ // hint 为空时不渲染段落(提示文字已内联在控件里,如复选框行)
253
+ hintText !== undefined && hintText !== null && hintText !== ''
254
+ ? React.createElement('p', { className: props.invalid === true ? 'aow-invalid' : 'aow-hint' }, hintText)
255
+ : null,
256
+ props.footer !== undefined && props.footer !== null ? props.footer : null
206
257
  )
207
258
  }
208
259
 
209
- // ---- 官方 CardForm 移植(字段规格 + 草稿计划 + 保存落地校验) ----
260
+ // ── 表单模型(字段规格 + staged 草稿 + 保存计划 + 落地校验) ───────────
261
+ // 与官方数据契约一致:scope 提供 { getSnapshot() → { status, writable,
262
+ // value, base, user }, set/unset };写后以 user 层落地为准。
210
263
  /** 自由文本字段:空草稿 = 清除(恢复默认)。 */
211
264
  function textField(field) {
212
265
  return {
@@ -235,7 +288,7 @@ window.__ModuleLoader__.load({
235
288
  }
236
289
  }
237
290
  /** 表单:staged 草稿 → 保存计划;写后以 user 层落地为准。 */
238
- function CardForm(scope, specs) {
291
+ function FormModel(scope, specs) {
239
292
  this.scope = scope
240
293
  this.specs = new Map(specs.map((spec) => [spec.field, spec]))
241
294
  this.staged = new Map()
@@ -246,14 +299,14 @@ window.__ModuleLoader__.load({
246
299
  this.publish()
247
300
  })
248
301
  }
249
- CardForm.prototype.bind = function (project) {
302
+ FormModel.prototype.bind = function (project) {
250
303
  const store = createSnapshotStore(project())
251
304
  this.listeners.add(() => {
252
305
  store.set(project())
253
306
  })
254
307
  return store
255
308
  }
256
- CardForm.prototype.shell = function () {
309
+ FormModel.prototype.shell = function () {
257
310
  const snapshot = this.scope.getSnapshot()
258
311
  const plan = this.plan()
259
312
  return {
@@ -265,7 +318,7 @@ window.__ModuleLoader__.load({
265
318
  failed: this.failed
266
319
  }
267
320
  }
268
- CardForm.prototype.field = function (field) {
321
+ FormModel.prototype.field = function (field) {
269
322
  const staged = this.staged.get(field)
270
323
  const spec = this.specs.get(field)
271
324
  if (spec === undefined) throw new Error('plugin card has no field ' + field)
@@ -281,7 +334,7 @@ window.__ModuleLoader__.load({
281
334
  invalid: write === undefined
282
335
  }
283
336
  }
284
- CardForm.prototype.actions = function () {
337
+ FormModel.prototype.actions = function () {
285
338
  return {
286
339
  edit: (field, text) => {
287
340
  this.stage(field, { text, clear: false })
@@ -300,7 +353,7 @@ window.__ModuleLoader__.load({
300
353
  }
301
354
  }
302
355
  }
303
- CardForm.prototype.save = function () {
356
+ FormModel.prototype.save = function () {
304
357
  const plan = this.plan()
305
358
  const writes = plan.flatMap((item) => item.run === undefined ? [] : [item.run])
306
359
  if (plan.length === 0 || this.saving || writes.length !== plan.length) return
@@ -321,7 +374,7 @@ window.__ModuleLoader__.load({
321
374
  this.publish()
322
375
  })
323
376
  }
324
- CardForm.prototype.plan = function () {
377
+ FormModel.prototype.plan = function () {
325
378
  const plan = []
326
379
  for (const [field, staged] of this.staged) {
327
380
  const spec = this.specs.get(field)
@@ -337,45 +390,45 @@ window.__ModuleLoader__.load({
337
390
  }
338
391
  return plan
339
392
  }
340
- CardForm.prototype.stage = function (field, edit) {
393
+ FormModel.prototype.stage = function (field, edit) {
341
394
  this.staged.set(field, edit)
342
395
  this.failed = false
343
396
  this.publish()
344
397
  }
345
- CardForm.prototype.clear = async function (field) {
398
+ FormModel.prototype.clear = async function (field) {
346
399
  await this.scope.unset(field)
347
400
  return !this.stored(field)
348
401
  }
349
- CardForm.prototype.store = async function (field, value) {
402
+ FormModel.prototype.store = async function (field, value) {
350
403
  await this.scope.set(field, value)
351
404
  const user = this.userLayer()
352
405
  return user !== undefined && user !== null && user[field] === value
353
406
  }
354
- CardForm.prototype.snapshotOf = function () {
407
+ FormModel.prototype.snapshotOf = function () {
355
408
  return this.scope.getSnapshot()
356
409
  }
357
- CardForm.prototype.sectionValue = function (field) {
410
+ FormModel.prototype.sectionValue = function (field) {
358
411
  const snapshot = this.snapshotOf()
359
412
  return snapshot.value !== undefined && snapshot.value !== null ? snapshot.value[field] : undefined
360
413
  }
361
- CardForm.prototype.baseValue = function (field) {
414
+ FormModel.prototype.baseValue = function (field) {
362
415
  const snapshot = this.snapshotOf()
363
416
  return snapshot.base !== undefined && snapshot.base !== null ? snapshot.base[field] : undefined
364
417
  }
365
- CardForm.prototype.userLayer = function () {
418
+ FormModel.prototype.userLayer = function () {
366
419
  return this.snapshotOf().user
367
420
  }
368
- CardForm.prototype.stored = function (field) {
421
+ FormModel.prototype.stored = function (field) {
369
422
  const user = this.userLayer()
370
423
  return user !== undefined && user !== null && Object.prototype.hasOwnProperty.call(user, field)
371
424
  }
372
- CardForm.prototype.publish = function () {
425
+ FormModel.prototype.publish = function () {
373
426
  for (const listener of this.listeners) listener()
374
427
  }
375
428
 
376
- // ---- 卡片控制器:scope → CardForm → 投影 store + 动作面 ----
429
+ // ── 卡片控制器:scope → FormModel → 投影 store + 动作面 ───────────────
377
430
  function AutoOpenCardController(scope) {
378
- this.form = new CardForm(scope, [boolField('appWindow'), windowKindField('windowKind'), textField('browserPath'), boolField('exitOnWindowClose')])
431
+ this.form = new FormModel(scope, [boolField('appWindow'), windowKindField('windowKind'), textField('browserPath'), boolField('exitOnWindowClose')])
379
432
  this.store = this.form.bind(() => this.projection())
380
433
  }
381
434
  AutoOpenCardController.prototype.projection = function () {
@@ -394,7 +447,7 @@ window.__ModuleLoader__.load({
394
447
  }
395
448
  }
396
449
 
397
- // ---- 卡片组件(官方 BashCard 同构:PluginCard 包字段) ----
450
+ // ── 设置卡片页面(自绘组件拼装) ─────────────────────────────────────
398
451
  function AutoOpenSettingsPage(props) {
399
452
  const { t } = props
400
453
  const state = props.useAutoOpenCard((snapshot) => snapshot)
@@ -439,7 +492,15 @@ window.__ModuleLoader__.load({
439
492
 
440
493
  const browserDisabled = !state.writable || state.windowKind.text !== 'browser'
441
494
 
442
- return React.createElement(PluginCard, {
495
+ const badgesFor = (field) => React.createElement(FieldBadges, {
496
+ overridden: state[field].overridden,
497
+ disabled: !state.writable,
498
+ overriddenLabel: t('overridden'),
499
+ resetLabel: t('reset'),
500
+ onReset: () => props.resetField(field)
501
+ })
502
+
503
+ return React.createElement(Card, {
443
504
  t,
444
505
  titleKey: 'title',
445
506
  descriptionKey: 'description',
@@ -448,40 +509,38 @@ window.__ModuleLoader__.load({
448
509
  onDiscard: props.discard
449
510
  },
450
511
  // ---- 独立应用窗口 ----
451
- React.createElement('div', { className: 'At1oFq_field' },
452
- React.createElement('div', { className: 'At1oFq_head' },
453
- React.createElement('span', { className: 'At1oFq_label' }, t('appWindow')),
454
- React.createElement(FieldHeadBadges, { overridden: state.appWindow.overridden, disabled: !state.writable, overriddenLabel: t('overridden'), resetLabel: t('reset'), onReset: () => props.resetField('appWindow') })
455
- ),
456
- React.createElement(Checkbox, { checked: state.appWindow.text === true, disabled: !state.writable, onChange: (v) => props.edit('appWindow', v) }, t('appWindowHint'))
457
- ),
512
+ React.createElement(Field, {
513
+ label: t('appWindow'),
514
+ badges: badgesFor('appWindow'),
515
+ control: React.createElement(Checkbox, { checked: state.appWindow.text === true, disabled: !state.writable, onChange: (v) => props.edit('appWindow', v) }, t('appWindowHint'))
516
+ }),
458
517
  // ---- 窗口类型 ----
459
- React.createElement('div', { className: 'At1oFq_field' },
460
- React.createElement('div', { className: 'At1oFq_head' },
461
- React.createElement('span', { className: 'At1oFq_label' }, t('windowKind')),
462
- React.createElement(FieldHeadBadges, { overridden: state.windowKind.overridden, disabled: !state.writable, overriddenLabel: t('overridden'), resetLabel: t('reset'), onReset: () => props.resetField('windowKind') })
463
- ),
464
- React.createElement(Radio, { checked: state.windowKind.text === 'webview2', disabled: !state.writable, value: 'webview2', onChange: (v) => props.edit('windowKind', v) }, t('windowKindWebview2')),
465
- React.createElement(Radio, { checked: state.windowKind.text === 'browser', disabled: !state.writable, value: 'browser', onChange: (v) => props.edit('windowKind', v) }, t('windowKindBrowser'))
466
- ),
467
- // ---- 浏览器可执行文件(ValueField + 浏览/测试按钮) ----
468
- React.createElement(ValueField, {
469
- id: 'auto-open-web-browser-path',
518
+ React.createElement(Field, {
519
+ label: t('windowKind'),
520
+ badges: badgesFor('windowKind'),
521
+ control: React.createElement(React.Fragment, null,
522
+ React.createElement(Radio, { checked: state.windowKind.text === 'webview2', disabled: !state.writable, value: 'webview2', onChange: (v) => props.edit('windowKind', v) }, t('windowKindWebview2')),
523
+ React.createElement(Radio, { checked: state.windowKind.text === 'browser', disabled: !state.writable, value: 'browser', onChange: (v) => props.edit('windowKind', v) }, t('windowKindBrowser'))
524
+ )
525
+ }),
526
+ // ---- 浏览器可执行文件(输入 + 浏览/测试按钮) ----
527
+ React.createElement(Field, {
470
528
  label: t('browserPath'),
529
+ badges: badgesFor('browserPath'),
471
530
  hint: t('browserPathHint'),
472
- overridden: state.browserPath.overridden,
473
- overriddenLabel: t('overridden'),
474
- resetLabel: t('reset'),
475
531
  invalid: state.browserPath.invalid,
476
532
  invalidLabel: t('browserPathInvalid'),
477
- disabled: browserDisabled,
478
- text: state.browserPath.text,
479
- placeholder: t('browserPathPlaceholder'),
480
- onEdit: (text) => props.edit('browserPath', text),
481
- onReset: () => props.resetField('browserPath'),
482
- extra: React.createElement(React.Fragment, null,
483
- React.createElement(ProxyButton, { variant: 'outline', size: 'sm', disabled: !state.writable || picking, onClick: browse }, picking ? t('browsePicking') : t('browse')),
484
- React.createElement(ProxyButton, { variant: 'outline', size: 'sm', disabled: testing || state.windowKind.text !== 'browser', onClick: testBrowser }, testing ? t('testRunning') : t('test'))
533
+ control: React.createElement('div', { className: 'aow-row' },
534
+ React.createElement(TextInput, {
535
+ id: 'auto-open-web-browser-path',
536
+ value: state.browserPath.text,
537
+ placeholder: t('browserPathPlaceholder'),
538
+ disabled: browserDisabled,
539
+ invalid: state.browserPath.invalid,
540
+ onChange: (text) => props.edit('browserPath', text)
541
+ }),
542
+ React.createElement(Btn, { disabled: !state.writable || picking, onClick: browse }, picking ? t('browsePicking') : t('browse')),
543
+ React.createElement(Btn, { disabled: testing || state.windowKind.text !== 'browser', onClick: testBrowser }, testing ? t('testRunning') : t('test'))
485
544
  ),
486
545
  footer: testResult !== null
487
546
  ? React.createElement('p', { className: testResult.ok ? 'aow-test-ok' : 'aow-test-fail', role: 'status' },
@@ -490,20 +549,22 @@ window.__ModuleLoader__.load({
490
549
  : null
491
550
  }),
492
551
  // ---- 窗口关闭时退出 DSH(实验性) ----
493
- React.createElement('div', { className: 'At1oFq_field' },
494
- React.createElement('div', { className: 'At1oFq_head' },
495
- React.createElement('span', { className: 'At1oFq_label' }, t('exitOnWindowClose')),
496
- React.createElement('span', { className: 'At1oFq_badges' },
497
- React.createElement('span', { className: 'At1oFq_badge' }, t('experimental'))
498
- ),
499
- React.createElement(FieldHeadBadges, { overridden: state.exitOnWindowClose.overridden, disabled: !state.writable, overriddenLabel: t('overridden'), resetLabel: t('reset'), onReset: () => props.resetField('exitOnWindowClose') })
500
- ),
501
- React.createElement(Checkbox, { checked: state.exitOnWindowClose.text === true, disabled: !state.writable, onChange: (v) => props.edit('exitOnWindowClose', v) }, t('exitOnWindowCloseHint'))
502
- )
552
+ React.createElement(Field, {
553
+ label: t('exitOnWindowClose'),
554
+ badges: React.createElement(FieldBadges, {
555
+ static: t('experimental'),
556
+ overridden: state.exitOnWindowClose.overridden,
557
+ disabled: !state.writable,
558
+ overriddenLabel: t('overridden'),
559
+ resetLabel: t('reset'),
560
+ onReset: () => props.resetField('exitOnWindowClose')
561
+ }),
562
+ control: React.createElement(Checkbox, { checked: state.exitOnWindowClose.text === true, disabled: !state.writable, onChange: (v) => props.edit('exitOnWindowClose', v) }, t('exitOnWindowCloseHint'))
563
+ })
503
564
  )
504
565
  }
505
566
 
506
- // ---- 文案(官方 locale 机制,zh/en 字典) ----
567
+ // ---- 文案(locale 机制,zh/en/ja/ko/fr/de/ru) ----
507
568
  const NS = 'auto-open-web'
508
569
  const en = {
509
570
  title: 'Auto-open web',
@@ -514,7 +575,7 @@ window.__ModuleLoader__.load({
514
575
  expand: 'Show settings', collapse: 'Hide settings',
515
576
  overridden: 'Overridden', reset: 'Reset to default',
516
577
  appWindow: 'Independent app window',
517
- appWindowHint: 'Automatically open an independent app window on start; off opens nothing.',
578
+ appWindowHint: 'Automatically open an independent app window on start; off behaves like the official core — the GUI opens in the default browser instead',
518
579
  windowKind: 'Window kind',
519
580
  windowKindWebview2: 'WebView2 host (own process, DSH taskbar icon, exits with DSH; needs Windows + WebView2 runtime)',
520
581
  windowKindBrowser: 'Browser app window (dedicated --app instance, isolated from the normal browser)',
@@ -537,7 +598,7 @@ window.__ModuleLoader__.load({
537
598
  expand: '展开设置', collapse: '收起设置',
538
599
  overridden: '已覆盖', reset: '恢复默认',
539
600
  appWindow: '独立应用窗口',
540
- appWindowHint: '启动时自动打开独立应用窗口;关闭后不自动打开任何窗口',
601
+ appWindowHint: '启动时自动打开独立应用窗口;关闭后与官方相同:改用系统默认浏览器打开 GUI',
541
602
  windowKind: '窗口类型',
542
603
  windowKindWebview2: 'WebView2 宿主(独立进程,任务栏 DSH 图标,随 DSH 退出;需 Windows + WebView2 运行时)',
543
604
  windowKindBrowser: '浏览器应用窗口(--app 专用实例,不与其他浏览器页面共用进程)',
@@ -560,7 +621,7 @@ window.__ModuleLoader__.load({
560
621
  expand: '設定を表示', collapse: '設定を隠す',
561
622
  overridden: '上書き済み', reset: 'デフォルトに戻す',
562
623
  appWindow: '独立アプリウィンドウ',
563
- appWindowHint: '起動時に独立アプリウィンドウを自動的に開く;オフの場合は何も開かない',
624
+ appWindowHint: '起動時に独立アプリウィンドウを自動的に開く;オフの場合は公式と同じく既定ブラウザで GUI を開く',
564
625
  windowKind: 'ウィンドウの種類',
565
626
  windowKindWebview2: 'WebView2 ホスト(独立プロセス、タスクバーは DSH アイコン、DSH とともに終了;Windows + WebView2 ランタイムが必要)',
566
627
  windowKindBrowser: 'ブラウザアプリウィンドウ(専用 --app インスタンス、通常のブラウザとプロセスを共有しない)',
@@ -583,7 +644,7 @@ window.__ModuleLoader__.load({
583
644
  expand: '설정 표시', collapse: '설정 숨기기',
584
645
  overridden: '재정의됨', reset: '기본값으로 재설정',
585
646
  appWindow: '독립 앱 창',
586
- appWindowHint: '시작 시 독립 앱 창을 자동으로 엽니다. 끄면 아무 창도 열지 않습니다',
647
+ appWindowHint: '시작 시 독립 앱 창을 자동으로 엽니다. 끄면 공식과 동일하게 기본 브라우저로 GUI를 엽니다',
587
648
  windowKind: '창 종류',
588
649
  windowKindWebview2: 'WebView2 호스트(독립 프로세스, 작업 표시줄 DSH 아이콘, DSH와 함께 종료; Windows + WebView2 런타임 필요)',
589
650
  windowKindBrowser: '브라우저 앱 창(전용 --app 인스턴스, 일반 브라우저와 프로세스 공유 안 함)',
@@ -606,7 +667,7 @@ window.__ModuleLoader__.load({
606
667
  expand: 'Afficher les paramètres', collapse: 'Masquer les paramètres',
607
668
  overridden: 'Remplacé', reset: 'Rétablir la valeur par défaut',
608
669
  appWindow: "Fenêtre d'application indépendante",
609
- appWindowHint: "Ouvre automatiquement une fenêtre d'application indépendante au démarrage; désactivé n'ouvre rien",
670
+ appWindowHint: "Ouvre automatiquement une fenêtre d'application indépendante au démarrage; désactivé = comme le cœur officiel, ouverture dans le navigateur par défaut",
610
671
  windowKind: 'Type de fenêtre',
611
672
  windowKindWebview2: 'Hôte WebView2 (processus propre, icône DSH dans la barre des tâches, se ferme avec DSH; nécessite Windows + runtime WebView2)',
612
673
  windowKindBrowser: 'Fenêtre de navigateur (instance --app dédiée, isolée du navigateur normal)',
@@ -629,7 +690,7 @@ window.__ModuleLoader__.load({
629
690
  expand: 'Einstellungen anzeigen', collapse: 'Einstellungen ausblenden',
630
691
  overridden: 'Überschrieben', reset: 'Standard wiederherstellen',
631
692
  appWindow: 'Eigenständiges App-Fenster',
632
- appWindowHint: 'Beim Start automatisch ein eigenständiges App-Fenster öffnen; aus = nichts öffnen',
693
+ appWindowHint: 'Beim Start automatisch ein eigenständiges App-Fenster öffnen; aus = wie offiziell, Öffnen der GUI im Standardbrowser',
633
694
  windowKind: 'Fenstertyp',
634
695
  windowKindWebview2: 'WebView2-Host (eigener Prozess, DSH-Symbol in der Taskleiste, wird mit DSH beendet; benötigt Windows + WebView2-Runtime)',
635
696
  windowKindBrowser: 'Browser-App-Fenster (dedizierte --app-Instanz, getrennt vom normalen Browser)',
@@ -652,7 +713,7 @@ window.__ModuleLoader__.load({
652
713
  expand: 'Показать настройки', collapse: 'Скрыть настройки',
653
714
  overridden: 'Переопределено', reset: 'Вернуть по умолчанию',
654
715
  appWindow: 'Независимое окно приложения',
655
- appWindowHint: 'Автоматически открывать независимое окно приложения при запуске; выкл. — ничего не открывать',
716
+ appWindowHint: 'Автоматически открывать независимое окно приложения при запуске; выкл. — как в официальной версии, открытие GUI в браузере по умолчанию',
656
717
  windowKind: 'Тип окна',
657
718
  windowKindWebview2: 'Хост WebView2 (собственный процесс, значок DSH на панели задач, закрывается вместе с DSH; требуется Windows + среда WebView2)',
658
719
  windowKindBrowser: 'Окно браузера (выделенный экземпляр --app, изолирован от обычного браузера)',
@@ -668,7 +729,7 @@ window.__ModuleLoader__.load({
668
729
  }
669
730
 
670
731
  function apply(ctx) {
671
- // 文案(locale 机制,与官方同构;跟随界面语言)
732
+ // 文案(locale 机制,跟随界面语言)
672
733
  ctx.effect(() => ctx.locale.register(NS, { zh, en, ja, ko, fr, de, ru }), 'auto-open-web: card dictionary')
673
734
  // 数据通道:settingsScope(官方 settings 域)
674
735
  const scope = ctx.settingsScope.bind({ namespace: 'auto-open-web' })
@@ -682,7 +743,7 @@ window.__ModuleLoader__.load({
682
743
  )
683
744
  }
684
745
 
685
- // 代码级服务依赖(cordis):与官方 settings-plugins 完全一致
746
+ // 代码级服务依赖(cordis):数据/机制服务,不含任何 UI
686
747
  var clientInject = ['slots', 'locale', 'connection', 'remote', 'settingsScope']
687
748
 
688
749
  exports.apply = apply