dsh-persona-forge 0.3.0 → 0.3.1
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/CHANGELOG.md +38 -5
- package/README.en.md +21 -18
- package/README.md +7 -8
- package/lib/client.js +175 -123
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -46,12 +46,36 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
46
46
|
|
|
47
47
|
### Changed
|
|
48
48
|
|
|
49
|
+
- **The composer menu is a picker again, not a settings form.** It had grown to
|
|
50
|
+
five equal-weight segmented rows plus 248 characters of permanently visible
|
|
51
|
+
explanation, so picking a character meant scanning past four configuration
|
|
52
|
+
rows every time. It is now two layers: card list, intensity and send mode
|
|
53
|
+
always visible; **rewrite mode and fidelity** — per-session overrides of a
|
|
54
|
+
card's own declaration — folded behind "More settings", shut by default. The
|
|
55
|
+
collapsed row still names anything overridden, so a hidden choice cannot
|
|
56
|
+
surprise the user later. Measured: 5 rows + 248 hint chars → 2 rows + 0
|
|
57
|
+
permanent hint chars.
|
|
58
|
+
|
|
59
|
+
- **Fidelity is only offered when the card may add behavioural constraints.** On
|
|
60
|
+
a voice-only card both options resolve identically, so the row was a control
|
|
61
|
+
that could not do anything, paired with a paragraph explaining why the thing
|
|
62
|
+
you would want was refused. It is now absent on `style` cards.
|
|
63
|
+
|
|
64
|
+
- **The model check moved to the settings page** as a global preference rather
|
|
65
|
+
than a per-session switch in the composer menu. It answers "do I want to pay
|
|
66
|
+
for the second call", which is decided once, not re-asked on the way to every
|
|
67
|
+
send.
|
|
68
|
+
|
|
69
|
+
- "No persona" moved to the end of the card list: it is an exit action, and
|
|
70
|
+
listing it first made it the first thing read in a menu whose point is choosing
|
|
71
|
+
a character.
|
|
72
|
+
|
|
49
73
|
- **The model-based preservation check is now off by default** and switchable
|
|
50
|
-
from the
|
|
51
|
-
or a constraint added in prose — but it costs a second model
|
|
52
|
-
send, and the new local check already proves the common and
|
|
53
|
-
failure for free. `factCheck` in the plugin config now sets that
|
|
54
|
-
only an explicit `true` enables it.
|
|
74
|
+
from the settings page. It is the only thing that can see a requirement
|
|
75
|
+
reworded weaker, or a constraint added in prose — but it costs a second model
|
|
76
|
+
call on **every** send, and the new local check already proves the common and
|
|
77
|
+
most damaging failure for free. `factCheck` in the plugin config now sets that
|
|
78
|
+
default, and only an explicit `true` enables it.
|
|
55
79
|
|
|
56
80
|
- The Chinese README is the primary `README.md` and the English one is
|
|
57
81
|
`README.en.md`, so GitHub, npm and the repository landing page open in Chinese,
|
|
@@ -82,6 +106,15 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
82
106
|
confirmation for an edited bundled card says that its changes are discarded
|
|
83
107
|
and the bundled version returns.
|
|
84
108
|
|
|
109
|
+
- **A cancelled rewrite could not be retried.** `lastRewrite` was written on
|
|
110
|
+
every successful rewrite and never removed, so that the send gesture could
|
|
111
|
+
tell "the rewrite is already in the composer" from "not rewritten yet".
|
|
112
|
+
Restoring the original put the draft text back but left that memory in place,
|
|
113
|
+
so the next send matched "already rewritten" and the plugin silently let the
|
|
114
|
+
raw text through. Restoring is a rewind, not a reviewed result, so it now
|
|
115
|
+
deletes the entry — conditionally on putting back the original, because
|
|
116
|
+
sending the rewrite itself must still count as already rewritten.
|
|
117
|
+
|
|
85
118
|
- **A narrowed fidelity never reached the model.** The route computed the
|
|
86
119
|
narrowed value and reported it in the response, but `buildSystemPrompt` read
|
|
87
120
|
`card.fidelity` directly — so the setting appeared to work and did nothing. The
|
package/README.en.md
CHANGED
|
@@ -126,22 +126,15 @@ you want them gone.
|
|
|
126
126
|
|
|
127
127
|
## Use
|
|
128
128
|
|
|
129
|
-
1. Click 🎭 in the composer tool row and pick a character. **No persona
|
|
130
|
-
same
|
|
131
|
-
your choice is remembered per session.
|
|
132
|
-
2. Set **intensity
|
|
129
|
+
1. Click 🎭 in the composer tool row and pick a character. **No persona**, at the
|
|
130
|
+
end of the same list, turns rewriting off — that is the state the control
|
|
131
|
+
starts in, and your choice is remembered per session.
|
|
132
|
+
2. Set **intensity** and **send mode**:
|
|
133
133
|
- Intensity is a four-rung ladder: **Light · Medium · Strong · Zealot**
|
|
134
134
|
(轻 · 中 · 重 · 狂热). Light adds a touch of flavour; Medium is clear but not
|
|
135
135
|
dominant; Strong gives the voice real weight while keeping the request the
|
|
136
136
|
first thing the reader sees; Zealot is ritual throughout. A card carries
|
|
137
137
|
its own default, and this choice overrides it for your session.
|
|
138
|
-
- **Rewrite mode**: Card default / Model rewrite / Template. A temporary
|
|
139
|
-
switch applies to this session only; if the card has no content for the
|
|
140
|
-
mode you picked (no `style`, or no `template`), the override is ignored and
|
|
141
|
-
falls back to the card's own mode.
|
|
142
|
-
- **Fidelity**: Card default / Voice only. **Narrowing only, never widening**
|
|
143
|
-
— see below.
|
|
144
|
-
- **Model check**: on / off — see *The preservation check* below.
|
|
145
138
|
- **Review first** — the rewrite appears in the comparison panel below the
|
|
146
139
|
composer; you read it and then press Send there. Your draft is untouched
|
|
147
140
|
until you confirm.
|
|
@@ -151,6 +144,12 @@ you want them gone.
|
|
|
151
144
|
3. With a character selected, the composer's normal **Send** button — or
|
|
152
145
|
**Enter** — *is* the rewrite trigger.
|
|
153
146
|
|
|
147
|
+
**More settings**, at the bottom of the menu, is folded shut by default. It holds
|
|
148
|
+
**rewrite mode** and **fidelity** — per-session OVERRIDES of a card's own
|
|
149
|
+
declaration that most people set once and never touch, so they do not take space
|
|
150
|
+
in a menu whose job is choosing a character. When something *is* overridden, the
|
|
151
|
+
collapsed row names it, so a choice you forgot about cannot hide behind the fold.
|
|
152
|
+
|
|
154
153
|
The rewrite runs **once per draft**: after a rewrite lands, sending goes through
|
|
155
154
|
as-is instead of rewriting the output again. Editing the draft re-arms it.
|
|
156
155
|
|
|
@@ -214,10 +213,11 @@ is already proven for free by layer 1, so paying a model call on every send to
|
|
|
214
213
|
re-check it is a waste. Turn it on when the drafts are technical enough to
|
|
215
214
|
warrant it.
|
|
216
215
|
|
|
217
|
-
The
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
216
|
+
The switch lives in **Settings → Persona** ("Enable the model check") and is a
|
|
217
|
+
global preference: it answers "do I want to pay for the second call", which is
|
|
218
|
+
decided once rather than re-asked on the way to every send. With it off the panel
|
|
219
|
+
states plainly that the local check ran and spent no tokens, and says what that
|
|
220
|
+
check does **not** cover — a narrow verdict has to declare its own limits.
|
|
221
221
|
|
|
222
222
|
Both layers are **advisory** and never block a rewrite: they annotate, not stop.
|
|
223
223
|
|
|
@@ -384,9 +384,12 @@ ever narrowed, never widened:**
|
|
|
384
384
|
author's decision, not something to grant with a click on the way to Send. To
|
|
385
385
|
widen, edit the card.
|
|
386
386
|
|
|
387
|
-
The
|
|
388
|
-
|
|
389
|
-
|
|
387
|
+
The control appears under *More settings*, and **only when the card is
|
|
388
|
+
`strategy`**. On a `style` card it is not rendered at all: both options would
|
|
389
|
+
resolve identically there, and showing a control that cannot do anything — plus a
|
|
390
|
+
paragraph explaining why the other one is refused — is worse than showing
|
|
391
|
+
nothing. The server enforces the same rule, so the UI never offers a control it
|
|
392
|
+
would ignore.
|
|
390
393
|
|
|
391
394
|
### `mode: template`
|
|
392
395
|
|
package/README.md
CHANGED
|
@@ -108,19 +108,18 @@ dsh plugin --profile web remove dsh-persona-forge
|
|
|
108
108
|
|
|
109
109
|
## 使用
|
|
110
110
|
|
|
111
|
-
1. 点输入框工具行的
|
|
112
|
-
2.
|
|
111
|
+
1. 点输入框工具行的 🎭,选一个角色。列表最后的 **「无角色」** 可关闭改写 —— 这也是控件的初始状态,选择按会话记住。
|
|
112
|
+
2. 设置**强度和发送方式**:
|
|
113
113
|
- 强度是四档阶梯:**轻 · 中 · 重 · 狂热**(Light · Medium · Strong · Zealot)。轻=只加一点味道;中=明显但不喧宾夺主;重=语气充分,需求依然一眼可见;狂热=通篇仪式化。每张卡有自己的默认值,这里的选择会覆盖它。
|
|
114
|
-
- **改写方式**:跟随卡片(默认)/ 模型改写 / 模板套用。临时切换只影响当前会话;如果卡片没有对应的内容(没有 `style` 或没有 `template`),这个覆盖会被忽略并回退到卡片自己的方式。
|
|
115
|
-
- **保真度**:跟随卡片(默认)/ 只改语气。**只能收窄,不能放宽** —— 见下方说明。
|
|
116
|
-
- **模型校验**:开/关,见下方「技术事实校验」。
|
|
117
114
|
- **先审查** —— 改写结果显示在输入框下方的对比面板里;你看过之后按面板里的发送。**在确认之前,你的草稿一字不动。**
|
|
118
115
|
- **直接发出** —— 改写完成后立即作为你的消息发出。面板会保留显示实际发出的内容,因为已发出的消息无法撤回。
|
|
119
116
|
3. 选了角色之后,输入框**原生的发送按钮、以及 Enter 键**就是改写触发器。
|
|
120
117
|
|
|
118
|
+
菜单底部的 **「更多设置」** 默认收起,里面是**改写方式**与**保真度** —— 它们是对卡片自身声明的**按会话覆盖**,多数人设一次就不会再动,所以不占主菜单。收起时若有覆盖,这一行会**写出改了什么**,不会让一个你忘了的选择藏在折叠后面。
|
|
119
|
+
|
|
121
120
|
**同一份草稿只改写一次**:改写结果落到输入框后,再按发送会原样发出,不会把改写结果再改写一遍。改动草稿会重新触发改写。
|
|
122
121
|
|
|
123
|
-
你的草稿会被保留:**还原原文**
|
|
122
|
+
你的草稿会被保留:**还原原文** 可恢复,且**会撤销"已改写"的记忆** —— 所以撤消后还能再改一次。**仅回填** 只把改写结果放进输入框而不发送。
|
|
124
123
|
|
|
125
124
|
### 发送拦截的原理与它唯一的风险
|
|
126
125
|
|
|
@@ -148,7 +147,7 @@ dsh plugin --profile web remove dsh-persona-forge
|
|
|
148
147
|
|
|
149
148
|
代价是**延迟和 token 都翻倍**,所以默认关闭:最常见、最伤人的失败(丢掉文件名 / 数字 / 版本号)已经被本地检查免费证明了,为它每次发送都付一次模型调用是浪费。草稿偏技术时再开。
|
|
150
149
|
|
|
151
|
-
|
|
150
|
+
开关在**设置 → 角色扮演**(「开启模型校验」),是个全局偏好 —— 它回答的是「我要不要为第二次调用付费」,一次决定即可,不必在每次发送前重新问一遍。关闭时面板会写明「已做本地检查(不耗 token)」,并说清它**没有**覆盖什么 —— 一个窄的结论必须自己交代边界。
|
|
152
151
|
|
|
153
152
|
两层都是**建议性**的,绝不阻断改写:只标注,不拦你。
|
|
154
153
|
|
|
@@ -266,7 +265,7 @@ examplesByIntensity:
|
|
|
266
265
|
- **收窄(允许)**:一张 `strategy` 卡可以临时要求「本次只改语气」。这只是**拿走**它追加约束的权力,所以任何情况下都更安全。
|
|
267
266
|
- **放宽(拒绝)**:一张 `style` 卡**不能**被要求改成 `strategy`。放宽意味着让改写往你的需求里加你没写过的约束 —— 那是卡片作者的决定,不该由「发送前顺手一点」来授权。真要放宽,去编辑那张卡。
|
|
268
267
|
|
|
269
|
-
|
|
268
|
+
这个开关在「更多设置」里,且**只在卡片是 `strategy` 时出现**。`style` 卡上它不显示 —— 那种卡上两个选项效果完全一样,与其摆一个做不了事的控件、再配一段解释为什么不能放宽,不如不显示。服务端强制同一条规则,所以界面上不会给你一个会被忽略的按钮。
|
|
270
269
|
|
|
271
270
|
### `mode: template`
|
|
272
271
|
|
package/lib/client.js
CHANGED
|
@@ -38,8 +38,10 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
'control.busy': '改写中…',
|
|
39
39
|
'control.menu.title': '选择角色',
|
|
40
40
|
'control.menu.empty': '还没有角色卡。把 .yml 文件放进角色卡目录即可添加。',
|
|
41
|
-
'control.menu.manage': '管理角色卡…',
|
|
42
41
|
'control.menu.reveal': '打开角色卡目录',
|
|
42
|
+
'control.more': '更多设置',
|
|
43
|
+
'control.more.open': '展开改写方式、保真度等按会话生效的设置',
|
|
44
|
+
'control.more.custom': '已改:',
|
|
43
45
|
'control.intensity': '强度',
|
|
44
46
|
'control.intensity.light': '轻',
|
|
45
47
|
'control.intensity.medium': '中',
|
|
@@ -60,10 +62,6 @@ window.__ModuleLoader__.load({
|
|
|
60
62
|
'control.fidelity.style': '只改语气',
|
|
61
63
|
'control.fidelity.hint.style': '这张卡声明的是「只改语气」,不能在这里放宽——放宽是改卡的属性,请在设置页改。',
|
|
62
64
|
'control.fidelity.hint.strategy': '这张卡允许追加行为约束。这里可以临时收窄成「只改语气」,但收不回去。',
|
|
63
|
-
'control.modelCheck': '模型校验',
|
|
64
|
-
'control.modelCheck.on': '开',
|
|
65
|
-
'control.modelCheck.off': '关',
|
|
66
|
-
'control.modelCheck.hint': '开启后每次改写会多调用一次模型,检查「需求被悄悄改弱」这类本地检查看不出的问题——代价是延迟和 token 翻倍。关闭时仍会做本地检查(标识符、路径、版本号、数字),不耗 token。',
|
|
67
65
|
'panel.title': '角色改写',
|
|
68
66
|
'panel.loading': '正在按「{name}」改写…',
|
|
69
67
|
'panel.original': '你写的',
|
|
@@ -78,6 +76,7 @@ window.__ModuleLoader__.load({
|
|
|
78
76
|
'panel.sent.hint': '消息已发出,无法撤回。下面是实际发出的内容。',
|
|
79
77
|
'panel.meta': '{provider} / {model} · {ms}ms · 强度 {intensity}',
|
|
80
78
|
'panel.template': '模板模式(未调用模型)',
|
|
79
|
+
'panel.slow': '已经 {seconds} 秒了 —— 模型可能较慢。再等一会儿通常会有结果;若一直不动,可关闭面板后重试。',
|
|
81
80
|
'panel.drift': '技术事实可能被改动',
|
|
82
81
|
'panel.checkUnavailable': '未完成事实校验',
|
|
83
82
|
'panel.checkOk': '技术事实已保留',
|
|
@@ -88,7 +87,6 @@ window.__ModuleLoader__.load({
|
|
|
88
87
|
'panel.stale': '草稿在改写期间有改动——结果基于改写前的文本。',
|
|
89
88
|
'error.empty': '输入框是空的,先写下你的需求。',
|
|
90
89
|
'error.phase': '输入框正忙(提交中),请稍后再试。',
|
|
91
|
-
'error.busy': '已有一个改写在进行中。',
|
|
92
90
|
'error.noCard': '请先选择一个角色。',
|
|
93
91
|
'error.unconfigured': '无法确定模型:先在本会话发一条消息,或设置一个默认模型。',
|
|
94
92
|
'error.timeout': '改写超时,请重试;原输入未改动。',
|
|
@@ -104,7 +102,6 @@ window.__ModuleLoader__.load({
|
|
|
104
102
|
'settings.ai.tip': '新建角色卡时可以让 AI 代填:写一句角色思路,风格和四档范例都会自动写好。',
|
|
105
103
|
'settings.ai.model': '使用模型:{provider} / {model}(设置页没有会话上下文,用的是 harness 默认模型;改写本身跟随当前会话的模型)',
|
|
106
104
|
'settings.ai.desc': '用一句话描述角色的思路,AI 会按同一套规范把表单填好:风格、四档强度的转换范例都写好。生成结果只填入表单,不会自动保存。',
|
|
107
|
-
'settings.ai.briefLabel': '角色思路(一句话或几句话都行)',
|
|
108
105
|
'settings.ai.briefPlaceholder': '例如:一个脾气暴躁但极其可靠的老灯塔守夜人,说话简短、爱用航海比喻',
|
|
109
106
|
'settings.ai.generate': '生成草稿',
|
|
110
107
|
'settings.ai.busy': 'AI 正在撰写角色卡…',
|
|
@@ -126,7 +123,6 @@ window.__ModuleLoader__.load({
|
|
|
126
123
|
'settings.check.none': '所有角色卡都与内置版本一致。',
|
|
127
124
|
'settings.check.some': '有 {n} 张角色卡与内置版本不一致:',
|
|
128
125
|
'settings.check.desc': '点一次即可查出哪些卡偏离了内置版本。只有不一致的卡才显示「还原为内置」。',
|
|
129
|
-
'settings.changed': '改动:',
|
|
130
126
|
'settings.save': '保存',
|
|
131
127
|
'settings.cancel': '取消',
|
|
132
128
|
'settings.saved': '已保存',
|
|
@@ -162,8 +158,10 @@ window.__ModuleLoader__.load({
|
|
|
162
158
|
'control.busy': 'Rewriting…',
|
|
163
159
|
'control.menu.title': 'Choose a character',
|
|
164
160
|
'control.menu.empty': 'No character cards yet. Drop a .yml file into the card directory to add one.',
|
|
165
|
-
'control.menu.manage': 'Manage cards…',
|
|
166
161
|
'control.menu.reveal': 'Open card directory',
|
|
162
|
+
'control.more': 'More settings',
|
|
163
|
+
'control.more.open': 'Show rewrite mode, fidelity and other per-session settings',
|
|
164
|
+
'control.more.custom': 'Changed: ',
|
|
167
165
|
'control.intensity': 'Intensity',
|
|
168
166
|
'control.intensity.light': 'Light',
|
|
169
167
|
'control.intensity.medium': 'Medium',
|
|
@@ -184,10 +182,6 @@ window.__ModuleLoader__.load({
|
|
|
184
182
|
'control.fidelity.style': 'Voice only',
|
|
185
183
|
'control.fidelity.hint.style': 'This card declares voice-only, and it cannot be widened here — widening is a property of the card, so change it in the settings page.',
|
|
186
184
|
'control.fidelity.hint.strategy': 'This card may add behavioural constraints. You can narrow it to voice-only for this session, but not widen it back.',
|
|
187
|
-
'control.modelCheck': 'Model check',
|
|
188
|
-
'control.modelCheck.on': 'On',
|
|
189
|
-
'control.modelCheck.off': 'Off',
|
|
190
|
-
'control.modelCheck.hint': 'On: every rewrite makes one extra model call to catch what the local check cannot see, such as a requirement quietly weakened — at the cost of doubled latency and tokens. Off: the free local check still runs (identifiers, paths, versions, numbers).',
|
|
191
185
|
'panel.title': 'Persona rewrite',
|
|
192
186
|
'panel.loading': 'Rewriting as “{name}”…',
|
|
193
187
|
'panel.original': 'You wrote',
|
|
@@ -202,6 +196,7 @@ window.__ModuleLoader__.load({
|
|
|
202
196
|
'panel.sent.hint': 'The message is already sent and cannot be recalled. This is what was sent.',
|
|
203
197
|
'panel.meta': '{provider} / {model} · {ms}ms · intensity {intensity}',
|
|
204
198
|
'panel.template': 'Template mode (no model call)',
|
|
199
|
+
'panel.slow': '{seconds} s and counting — the model may just be slow. It usually returns; if it never does, close the panel and retry.',
|
|
205
200
|
'panel.drift': 'Technical facts may have changed',
|
|
206
201
|
'panel.checkUnavailable': 'Preservation check did not complete',
|
|
207
202
|
'panel.checkOk': 'Technical facts preserved',
|
|
@@ -212,7 +207,6 @@ window.__ModuleLoader__.load({
|
|
|
212
207
|
'panel.stale': 'The draft changed while rewriting — the result is based on the earlier text.',
|
|
213
208
|
'error.empty': 'The input box is empty — write your request first.',
|
|
214
209
|
'error.phase': 'The input box is busy (submitting) — try again shortly.',
|
|
215
|
-
'error.busy': 'A rewrite is already running.',
|
|
216
210
|
'error.noCard': 'Choose a character first.',
|
|
217
211
|
'error.unconfigured': 'No model resolved: send a message in this session first, or set a default model.',
|
|
218
212
|
'error.timeout': 'The rewrite timed out. Retry; your draft is untouched.',
|
|
@@ -228,7 +222,6 @@ window.__ModuleLoader__.load({
|
|
|
228
222
|
'settings.ai.tip': 'You can have the AI draft a card for you: describe the character in a sentence and the style text plus all four intensity example sets are written for you.',
|
|
229
223
|
'settings.ai.model': 'Model: {provider} / {model} (the settings page has no session context, so this is the harness default; the rewrite itself follows the current session\'s model)',
|
|
230
224
|
'settings.ai.desc': 'Describe the character in a sentence and the AI fills the form for you: the style text and conversion examples for all four intensity levels. The result only fills the form — it is never saved automatically.',
|
|
231
|
-
'settings.ai.briefLabel': 'Character idea (a sentence or a few)',
|
|
232
225
|
'settings.ai.briefPlaceholder': 'e.g. a grumpy but utterly reliable old lighthouse keeper who speaks in short sentences and nautical metaphors',
|
|
233
226
|
'settings.ai.generate': 'Generate draft',
|
|
234
227
|
'settings.ai.busy': 'Writing the character card…',
|
|
@@ -250,7 +243,6 @@ window.__ModuleLoader__.load({
|
|
|
250
243
|
'settings.check.none': 'Every character card matches its bundled version.',
|
|
251
244
|
'settings.check.some': '{n} card(s) differ from the bundled version:',
|
|
252
245
|
'settings.check.desc': 'Finds which cards have drifted from the bundled version. Only a drifted card shows a Restore bundled action.',
|
|
253
|
-
'settings.changed': 'Changed:',
|
|
254
246
|
'settings.save': 'Save',
|
|
255
247
|
'settings.cancel': 'Cancel',
|
|
256
248
|
'settings.saved': 'Saved',
|
|
@@ -333,6 +325,15 @@ window.__ModuleLoader__.load({
|
|
|
333
325
|
.pf-menu{position:fixed;z-index:1100;box-sizing:border-box;width:max-content;min-width:300px;max-width:min(460px,100vw - 24px);max-height:min(70vh,560px);overflow-y:auto;padding:4px;border:0;border-radius:var(--dsw-radius-lg);background:var(--dsw-menu-surface-fill,var(--dsw-alias-bg-overlay));-webkit-backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%));backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%));color:var(--dsw-alias-label-primary);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2)}
|
|
334
326
|
.pf-menu-title{padding:6px 8px 4px;font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary)}
|
|
335
327
|
.pf-menu-sep{height:.5px;margin:3px 2px;background:var(--dsw-alias-border-l2)}
|
|
328
|
+
/* The fold for advanced settings: a quiet row, not another equal-weight control. */
|
|
329
|
+
.pf-more{color:var(--dsw-alias-label-secondary)}
|
|
330
|
+
.pf-more-body{padding:2px 8px 8px;display:grid;gap:6px}
|
|
331
|
+
.pf-pref-row{display:flex;align-items:center;gap:6px;cursor:pointer}
|
|
332
|
+
.pf-pref-row input{margin:0}
|
|
333
|
+
/* The live counter during a rewrite: a muted suffix so the wait reads as
|
|
334
|
+
* progress rather than a freeze. */
|
|
335
|
+
.pf-elapsed{margin-left:8px;padding:0 6px;border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
336
|
+
|
|
336
337
|
.pf-item{display:flex;align-items:center;gap:6px;width:100%;min-height:34px;padding:6px 8px;border:none;border-radius:var(--dsw-radius-md);background:transparent;color:var(--dsw-alias-label-primary);text-align:left;font:inherit;font-size:13px;line-height:20px;cursor:pointer}
|
|
337
338
|
.pf-item:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
338
339
|
.pf-item:focus-visible{background:var(--dsw-alias-interactive-bg-hover);outline:none}
|
|
@@ -500,8 +501,6 @@ window.__ModuleLoader__.load({
|
|
|
500
501
|
const intensityKey = (key) => `persona-forge:intensity:${key}`
|
|
501
502
|
const modeKey = (key) => `persona-forge:mode:${key}`
|
|
502
503
|
const fidelityKey = (key) => `persona-forge:fidelity:${key}`
|
|
503
|
-
const modelCheckKey = (key) => `persona-forge:modelCheck:${key}`
|
|
504
|
-
|
|
505
504
|
/** Effective mode for a card: the session's pick when the card can honour it, else the card's. */
|
|
506
505
|
function effectiveModeOf(card, override) {
|
|
507
506
|
if (card === null) return 'llm'
|
|
@@ -674,6 +673,7 @@ window.__ModuleLoader__.load({
|
|
|
674
673
|
panel: {
|
|
675
674
|
sessionKey: options.sessionKey,
|
|
676
675
|
phase: 'loading',
|
|
676
|
+
startedAt: Date.now(),
|
|
677
677
|
cardId: options.cardId,
|
|
678
678
|
cardName: options.cardName,
|
|
679
679
|
original: options.original,
|
|
@@ -754,14 +754,10 @@ window.__ModuleLoader__.load({
|
|
|
754
754
|
// stored as "unset" until the user actually picks something, so following
|
|
755
755
|
// the card stays the default rather than an explicit choice.
|
|
756
756
|
const [modeOverride, setModeOverride] = useState(() => readLocal(modeKey(sessionKey)))
|
|
757
|
+
// Advanced rows start folded: the menu's job is choosing a character, and
|
|
758
|
+
// these overrides are set once and then forgotten.
|
|
759
|
+
const [advancedOpen, setAdvancedOpen] = useState(false)
|
|
757
760
|
const [fidelityOverride, setFidelityOverride] = useState(() => readLocal(fidelityKey(sessionKey)))
|
|
758
|
-
// Tri-state: undefined = follow the host config, true/false = this
|
|
759
|
-
// session's explicit choice. Stored as a string because localStorage has
|
|
760
|
-
// no booleans.
|
|
761
|
-
const [modelCheckOverride, setModelCheckOverride] = useState(() => {
|
|
762
|
-
const stored = readLocal(modelCheckKey(sessionKey))
|
|
763
|
-
return stored === 'on' ? true : stored === 'off' ? false : undefined
|
|
764
|
-
})
|
|
765
761
|
|
|
766
762
|
useEffect(() => {
|
|
767
763
|
if (shared.catalog === null && shared.catalogError === null) {
|
|
@@ -777,7 +773,20 @@ window.__ModuleLoader__.load({
|
|
|
777
773
|
const effectiveIntensity = intensity ?? selected?.intensity ?? 'medium'
|
|
778
774
|
const effectiveMode = effectiveModeOf(selected, modeOverride)
|
|
779
775
|
const effectiveFidelity = effectiveFidelityOf(selected, fidelityOverride)
|
|
780
|
-
|
|
776
|
+
// The model check is a one-time decision about cost, so it is a GLOBAL
|
|
777
|
+
// preference (set in the settings page), not a per-session override.
|
|
778
|
+
// One source of truth: the host config. A client-side copy would be a
|
|
779
|
+
// second setting for the same thing, and the model check is off by
|
|
780
|
+
// default because it is paid for on every send.
|
|
781
|
+
const effectiveModelCheck = shared.factCheck
|
|
782
|
+
|
|
783
|
+
// What the fold would reveal, in one line. A folded-away override the user
|
|
784
|
+
// forgot about is a surprise waiting to happen, so the collapsed row still
|
|
785
|
+
// names anything that is not following the card.
|
|
786
|
+
const advancedSummary = [
|
|
787
|
+
modeOverride === 'llm' ? t('control.mode.llm') : modeOverride === 'template' ? t('control.mode.template') : '',
|
|
788
|
+
fidelityOverride === 'style' ? t('control.fidelity.style') : '',
|
|
789
|
+
].filter((part) => part !== '').join(' · ')
|
|
781
790
|
|
|
782
791
|
// The send-gesture listener is attached once per session but must read
|
|
783
792
|
// CURRENT values. A ref carries them across renders without re-attaching
|
|
@@ -827,11 +836,6 @@ window.__ModuleLoader__.load({
|
|
|
827
836
|
writeLocal(fidelityKey(sessionKey), fidelity)
|
|
828
837
|
}, [sessionKey])
|
|
829
838
|
|
|
830
|
-
const setModelCheck = useCallback((enabled) => {
|
|
831
|
-
setModelCheckOverride(enabled)
|
|
832
|
-
writeLocal(modelCheckKey(sessionKey), enabled ? 'on' : 'off')
|
|
833
|
-
}, [sessionKey])
|
|
834
|
-
|
|
835
839
|
// Position the menu above the button, clamped to the viewport.
|
|
836
840
|
useEffect(() => {
|
|
837
841
|
if (!open) return
|
|
@@ -1028,26 +1032,6 @@ window.__ModuleLoader__.load({
|
|
|
1028
1032
|
style: { left: `${menuPos.left}px`, bottom: `${menuPos.bottom}px`, width: `${menuPos.width}px` },
|
|
1029
1033
|
},
|
|
1030
1034
|
h('div', { className: 'pf-menu-title' }, t('control.menu.title')),
|
|
1031
|
-
// "No persona" is a real choice, listed like any card, so the
|
|
1032
|
-
// control can always be turned back off.
|
|
1033
|
-
h(
|
|
1034
|
-
'button',
|
|
1035
|
-
{
|
|
1036
|
-
type: 'button',
|
|
1037
|
-
role: 'menuitemradio',
|
|
1038
|
-
'aria-checked': isOff ? 'true' : 'false',
|
|
1039
|
-
className: `pf-item${isOff ? ' is-selected' : ''}`,
|
|
1040
|
-
onClick: () => pickCard(OFF),
|
|
1041
|
-
},
|
|
1042
|
-
h('span', { className: 'pf-item-icon', 'aria-hidden': true }, '🚫'),
|
|
1043
|
-
h(
|
|
1044
|
-
'span',
|
|
1045
|
-
{ className: 'pf-item-body' },
|
|
1046
|
-
h('span', { className: 'pf-item-name' }, t('control.none')),
|
|
1047
|
-
h('span', { className: 'pf-item-desc' }, t('control.none.desc')),
|
|
1048
|
-
),
|
|
1049
|
-
isOff ? h('span', { className: 'pf-check', 'aria-hidden': true }, '✓') : null,
|
|
1050
|
-
),
|
|
1051
1035
|
cards.length === 0
|
|
1052
1036
|
? h('div', { className: 'pf-hint' }, shared.catalogError ?? t('control.menu.empty'))
|
|
1053
1037
|
: cards.map((card) => h(
|
|
@@ -1071,6 +1055,28 @@ window.__ModuleLoader__.load({
|
|
|
1071
1055
|
),
|
|
1072
1056
|
card.id === cardId ? h('span', { className: 'pf-check', 'aria-hidden': true }, '✓') : null,
|
|
1073
1057
|
)),
|
|
1058
|
+
// "No persona" is a real choice, listed like any card so the
|
|
1059
|
+
// control can always be turned back off — but LAST: it is an exit
|
|
1060
|
+
// action, and putting it above the cards made it the first thing
|
|
1061
|
+
// a user reads in a menu whose whole point is picking a character.
|
|
1062
|
+
h(
|
|
1063
|
+
'button',
|
|
1064
|
+
{
|
|
1065
|
+
type: 'button',
|
|
1066
|
+
role: 'menuitemradio',
|
|
1067
|
+
'aria-checked': isOff ? 'true' : 'false',
|
|
1068
|
+
className: `pf-item${isOff ? ' is-selected' : ''}`,
|
|
1069
|
+
onClick: () => pickCard(OFF),
|
|
1070
|
+
},
|
|
1071
|
+
h('span', { className: 'pf-item-icon', 'aria-hidden': true }, '🚫'),
|
|
1072
|
+
h(
|
|
1073
|
+
'span',
|
|
1074
|
+
{ className: 'pf-item-body' },
|
|
1075
|
+
h('span', { className: 'pf-item-name' }, t('control.none')),
|
|
1076
|
+
h('span', { className: 'pf-item-desc' }, t('control.none.desc')),
|
|
1077
|
+
),
|
|
1078
|
+
isOff ? h('span', { className: 'pf-check', 'aria-hidden': true }, '✓') : null,
|
|
1079
|
+
),
|
|
1074
1080
|
h('div', { className: 'pf-menu-sep' }),
|
|
1075
1081
|
h(
|
|
1076
1082
|
'div',
|
|
@@ -1112,80 +1118,90 @@ window.__ModuleLoader__.load({
|
|
|
1112
1118
|
),
|
|
1113
1119
|
),
|
|
1114
1120
|
h('div', { className: 'pf-hint' }, t(`control.sendMode.hint.${effectiveSendMode}`)),
|
|
1115
|
-
//
|
|
1116
|
-
//
|
|
1117
|
-
//
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
'span',
|
|
1124
|
-
{ className: 'pf-seg' },
|
|
1125
|
-
[['', t('control.mode.card')], ['llm', t('control.mode.llm')], ['template', t('control.mode.template')]].map(([value, label]) => h(
|
|
1126
|
-
'button',
|
|
1127
|
-
{
|
|
1128
|
-
key: value === '' ? 'card' : value,
|
|
1129
|
-
type: 'button',
|
|
1130
|
-
'aria-pressed': (modeOverride ?? '') === value ? 'true' : 'false',
|
|
1131
|
-
className: (modeOverride ?? '') === value ? 'is-on' : '',
|
|
1132
|
-
onClick: () => setMode(value === '' ? undefined : value),
|
|
1133
|
-
},
|
|
1134
|
-
label,
|
|
1135
|
-
)),
|
|
1136
|
-
),
|
|
1137
|
-
),
|
|
1138
|
-
h('div', { className: 'pf-hint' }, t('control.mode.hint')),
|
|
1139
|
-
h(
|
|
1140
|
-
'div',
|
|
1141
|
-
{ className: 'pf-row' },
|
|
1142
|
-
h('span', { className: 'pf-row-label' }, t('control.fidelity')),
|
|
1143
|
-
h(
|
|
1144
|
-
'span',
|
|
1145
|
-
{ className: 'pf-seg' },
|
|
1146
|
-
[['', t('control.fidelity.card')], ['style', t('control.fidelity.style')]].map(([value, label]) => h(
|
|
1147
|
-
'button',
|
|
1148
|
-
{
|
|
1149
|
-
key: value === '' ? 'card' : value,
|
|
1150
|
-
type: 'button',
|
|
1151
|
-
'aria-pressed': (fidelityOverride ?? '') === value ? 'true' : 'false',
|
|
1152
|
-
className: (fidelityOverride ?? '') === value ? 'is-on' : '',
|
|
1153
|
-
onClick: () => setFidelity(value === '' ? undefined : value),
|
|
1154
|
-
},
|
|
1155
|
-
label,
|
|
1156
|
-
)),
|
|
1157
|
-
),
|
|
1158
|
-
),
|
|
1159
|
-
// A voice-only card can never be widened from here, so the hint
|
|
1160
|
-
// says which card-level rule is in force rather than offering a
|
|
1161
|
-
// control that would be ignored.
|
|
1162
|
-
h('div', { className: 'pf-hint' }, selected?.fidelity === 'strategy'
|
|
1163
|
-
? t('control.fidelity.hint.strategy')
|
|
1164
|
-
: t('control.fidelity.hint.style')),
|
|
1165
|
-
// The model preservation check costs a second call per send, so
|
|
1166
|
-
// it is a visible switch rather than a hidden config file edit.
|
|
1167
|
-
// The free local check runs either way and cannot be turned off.
|
|
1121
|
+
// Advanced settings, folded away.
|
|
1122
|
+
//
|
|
1123
|
+
// Rewrite mode and fidelity are per-session OVERRIDES of a card's
|
|
1124
|
+
// own declaration, so almost everyone leaves them alone. Shown
|
|
1125
|
+
// flat, they gave the menu five equal-weight control rows and made
|
|
1126
|
+
// it read as a settings form instead of a character picker. The
|
|
1127
|
+
// folded row still reports what is overridden, so a choice the
|
|
1128
|
+
// user forgot about cannot hide behind the fold.
|
|
1168
1129
|
h(
|
|
1169
|
-
'
|
|
1170
|
-
{
|
|
1171
|
-
|
|
1130
|
+
'button',
|
|
1131
|
+
{
|
|
1132
|
+
type: 'button',
|
|
1133
|
+
className: 'pf-item pf-more',
|
|
1134
|
+
'aria-expanded': advancedOpen ? 'true' : 'false',
|
|
1135
|
+
title: t('control.more.open'),
|
|
1136
|
+
onClick: () => setAdvancedOpen((value) => !value),
|
|
1137
|
+
},
|
|
1138
|
+
h('span', { className: 'pf-item-icon', 'aria-hidden': true }, advancedOpen ? '▾' : '▸'),
|
|
1172
1139
|
h(
|
|
1173
1140
|
'span',
|
|
1174
|
-
{ className: 'pf-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
{
|
|
1178
|
-
|
|
1179
|
-
type: 'button',
|
|
1180
|
-
'aria-pressed': effectiveModelCheck === value ? 'true' : 'false',
|
|
1181
|
-
className: effectiveModelCheck === value ? 'is-on' : '',
|
|
1182
|
-
onClick: () => setModelCheck(value),
|
|
1183
|
-
},
|
|
1184
|
-
label,
|
|
1185
|
-
)),
|
|
1141
|
+
{ className: 'pf-item-body' },
|
|
1142
|
+
h('span', { className: 'pf-item-name' }, t('control.more')),
|
|
1143
|
+
advancedSummary !== ''
|
|
1144
|
+
? h('span', { className: 'pf-item-desc' }, `${t('control.more.custom')}${advancedSummary}`)
|
|
1145
|
+
: null,
|
|
1186
1146
|
),
|
|
1187
1147
|
),
|
|
1188
|
-
|
|
1148
|
+
advancedOpen
|
|
1149
|
+
? h(
|
|
1150
|
+
'div',
|
|
1151
|
+
{ className: 'pf-more-body' },
|
|
1152
|
+
h(
|
|
1153
|
+
'div',
|
|
1154
|
+
{ className: 'pf-row' },
|
|
1155
|
+
h('span', { className: 'pf-row-label' }, t('control.mode')),
|
|
1156
|
+
h(
|
|
1157
|
+
'span',
|
|
1158
|
+
{ className: 'pf-seg' },
|
|
1159
|
+
[['', t('control.mode.card')], ['llm', t('control.mode.llm')], ['template', t('control.mode.template')]].map(([value, label]) => h(
|
|
1160
|
+
'button',
|
|
1161
|
+
{
|
|
1162
|
+
key: value === '' ? 'card' : value,
|
|
1163
|
+
type: 'button',
|
|
1164
|
+
'aria-pressed': (modeOverride ?? '') === value ? 'true' : 'false',
|
|
1165
|
+
className: (modeOverride ?? '') === value ? 'is-on' : '',
|
|
1166
|
+
onClick: () => setMode(value === '' ? undefined : value),
|
|
1167
|
+
},
|
|
1168
|
+
label,
|
|
1169
|
+
)),
|
|
1170
|
+
),
|
|
1171
|
+
),
|
|
1172
|
+
h('div', { className: 'pf-hint' }, t('control.mode.hint')),
|
|
1173
|
+
// Only a card that may add behavioural constraints has
|
|
1174
|
+
// anything to narrow. On a voice-only card both options
|
|
1175
|
+
// resolve to the same thing, and offering that choice --
|
|
1176
|
+
// with a paragraph explaining why the other one is refused --
|
|
1177
|
+
// is a control that cannot do anything.
|
|
1178
|
+
selected?.fidelity === 'strategy'
|
|
1179
|
+
? [
|
|
1180
|
+
h(
|
|
1181
|
+
'div',
|
|
1182
|
+
{ className: 'pf-row' },
|
|
1183
|
+
h('span', { className: 'pf-row-label' }, t('control.fidelity')),
|
|
1184
|
+
h(
|
|
1185
|
+
'span',
|
|
1186
|
+
{ className: 'pf-seg' },
|
|
1187
|
+
[['', t('control.fidelity.card')], ['style', t('control.fidelity.style')]].map(([value, label]) => h(
|
|
1188
|
+
'button',
|
|
1189
|
+
{
|
|
1190
|
+
key: value === '' ? 'card' : value,
|
|
1191
|
+
type: 'button',
|
|
1192
|
+
'aria-pressed': (fidelityOverride ?? '') === value ? 'true' : 'false',
|
|
1193
|
+
className: (fidelityOverride ?? '') === value ? 'is-on' : '',
|
|
1194
|
+
onClick: () => setFidelity(value === '' ? undefined : value),
|
|
1195
|
+
},
|
|
1196
|
+
label,
|
|
1197
|
+
)),
|
|
1198
|
+
),
|
|
1199
|
+
),
|
|
1200
|
+
h('div', { className: 'pf-hint' }, t('control.fidelity.hint.strategy')),
|
|
1201
|
+
]
|
|
1202
|
+
: null,
|
|
1203
|
+
)
|
|
1204
|
+
: null,
|
|
1189
1205
|
h('div', { className: 'pf-menu-sep' }),
|
|
1190
1206
|
h(
|
|
1191
1207
|
'button',
|
|
@@ -1218,6 +1234,23 @@ window.__ModuleLoader__.load({
|
|
|
1218
1234
|
const panel = shared.panel
|
|
1219
1235
|
const inputActions = props.inputActions
|
|
1220
1236
|
const setDraft = typeof inputActions?.setDraft === 'function' ? inputActions.setDraft : null
|
|
1237
|
+
|
|
1238
|
+
// Seconds since the in-flight rewrite started, ticking once a second.
|
|
1239
|
+
//
|
|
1240
|
+
// Without this the panel shows "改写中" with no sense of progress, so a
|
|
1241
|
+
// rewrite waiting on a slow model reads as FROZEN — the user cannot tell
|
|
1242
|
+
// "working" from "hung", and reports it as stuck. A live counter answers
|
|
1243
|
+
// that for nothing, and makes the host's own timeout (120s) legible
|
|
1244
|
+
// instead of arbitrary.
|
|
1245
|
+
const [tick, setTick] = useState(0)
|
|
1246
|
+
useEffect(() => {
|
|
1247
|
+
if (panel === null || panel.phase !== 'loading') return
|
|
1248
|
+
const started = typeof panel.startedAt === 'number' ? panel.startedAt : Date.now()
|
|
1249
|
+
setTick(Math.round((Date.now() - started) / 1000))
|
|
1250
|
+
const timer = window.setInterval(() => setTick(Math.round((Date.now() - started) / 1000)), 1000)
|
|
1251
|
+
return () => window.clearInterval(timer)
|
|
1252
|
+
}, [panel === null, panel?.phase, panel?.startedAt])
|
|
1253
|
+
const elapsedSeconds = panel !== null && panel.phase === 'loading' ? Math.max(0, tick) : null
|
|
1221
1254
|
const submit = typeof inputActions?.submit === 'function' ? inputActions.submit : null
|
|
1222
1255
|
const owned = panel !== null && panel.sessionKey === sessionKey ? panel : null
|
|
1223
1256
|
// Guards the direct-send effect against firing twice for one result.
|
|
@@ -1250,6 +1283,12 @@ window.__ModuleLoader__.load({
|
|
|
1250
1283
|
/** Replace the composer content, then optionally submit it. */
|
|
1251
1284
|
const apply = (text, thenSubmit) => {
|
|
1252
1285
|
if (setDraft === null) return
|
|
1286
|
+
// Putting the ORIGINAL back has to forget that a rewrite ever happened.
|
|
1287
|
+
// Without this, `lastRewrite` still records `original: A`, so the next
|
|
1288
|
+
// send on the restored draft A matches "already rewritten" and the
|
|
1289
|
+
// plugin silently does nothing — a rewrite you cancelled would be
|
|
1290
|
+
// impossible to retry. Restoring is a rewind, not a reviewed result.
|
|
1291
|
+
if (text === panel.original) lastRewrite.delete(sessionKey)
|
|
1253
1292
|
setDraft(text)
|
|
1254
1293
|
if (thenSubmit !== true || submit === null) {
|
|
1255
1294
|
closePanel()
|
|
@@ -1281,6 +1320,7 @@ window.__ModuleLoader__.load({
|
|
|
1281
1320
|
)
|
|
1282
1321
|
|
|
1283
1322
|
if (panel.phase === 'loading') {
|
|
1323
|
+
const seconds = elapsedSeconds ?? 0
|
|
1284
1324
|
return h(
|
|
1285
1325
|
'div',
|
|
1286
1326
|
{ className: 'pf-panel' },
|
|
@@ -1288,7 +1328,17 @@ window.__ModuleLoader__.load({
|
|
|
1288
1328
|
h(
|
|
1289
1329
|
'div',
|
|
1290
1330
|
{ className: 'pf-panel-body' },
|
|
1291
|
-
h(
|
|
1331
|
+
h(
|
|
1332
|
+
'div',
|
|
1333
|
+
{ className: 'pf-meta' },
|
|
1334
|
+
t('panel.loading', { name: panel.cardName ?? panel.cardId }),
|
|
1335
|
+
h('span', { className: 'pf-elapsed', 'aria-live': 'off' }, `${seconds}s`),
|
|
1336
|
+
),
|
|
1337
|
+
// Past half the host's own timeout, say so instead of leaving the
|
|
1338
|
+
// user to wonder whether anything is still happening.
|
|
1339
|
+
seconds >= 45
|
|
1340
|
+
? h('div', { className: 'pf-note is-muted', style: { marginTop: '8px' } }, t('panel.slow', { seconds }))
|
|
1341
|
+
: null,
|
|
1292
1342
|
),
|
|
1293
1343
|
)
|
|
1294
1344
|
}
|
|
@@ -1673,9 +1723,11 @@ window.__ModuleLoader__.load({
|
|
|
1673
1723
|
// new-card flow; this line is what tells a user it exists, so they do
|
|
1674
1724
|
// not have to press a button to find out.
|
|
1675
1725
|
h('div', { className: 'pf-small', style: { margin: '0 0 10px' } }, t('settings.ai.tip')),
|
|
1676
|
-
// The check
|
|
1726
|
+
// The check RESULT is real feedback. The instruction line is not
|
|
1727
|
+
// rendered: the button's tooltip already explains the action, and
|
|
1728
|
+
// repeating it on the page is the same text twice.
|
|
1677
1729
|
drift === null
|
|
1678
|
-
?
|
|
1730
|
+
? null
|
|
1679
1731
|
: drift === undefined
|
|
1680
1732
|
? h('div', { className: 'pf-note is-muted', style: { marginBottom: '10px' } }, t('settings.check.busy'))
|
|
1681
1733
|
: drift.length === 0
|
package/package.json
CHANGED