@dsh-plugin/dsh-ui-experience 1.0.0-dev.43487914661 → 1.0.0-dev.43488816373
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/lib/client.js +61 -44
- package/lib/index.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,11 +119,13 @@ token 的值按原样写入 CSS 变量,因此 `color-mix(...)` 这类表达式
|
|
|
119
119
|
| 位移(transform、宽高、内外距、gap、圆角…) | `cubic-bezier(0.05, 0.7, 0.1, 1)`(emphasized decelerate) | 声明时长 ≤200ms 用 `cubic-bezier(0.42, 1.67, 0.21, 0.9)`,否则用 `cubic-bezier(0.38, 1.21, 0.22, 1)` |
|
|
120
120
|
| 效果(颜色、透明度、阴影、滤镜…) | `cubic-bezier(0.2, 0, 0, 1)`(standard) | `cubic-bezier(0.34, 0.8, 0.34, 1)` |
|
|
121
121
|
|
|
122
|
-
两族都把「移动」和「效果」分开,原因在 M3E 上最直观:它的两条位移曲线带过冲,峰值分别为 1.092 与 1.014,效果曲线不过冲。物体移动有惯性,颜色过冲则会闪出调色板里不存在的颜色。
|
|
122
|
+
两族都把「移动」和「效果」分开,原因在 M3E 上最直观:它的两条位移曲线带过冲,峰值分别为 1.092 与 1.014,效果曲线不过冲。物体移动有惯性,颜色过冲则会闪出调色板里不存在的颜色。
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
M3 那侧的分离要说明白:Material 3 的 `emphasized` 整体是两段首尾相接的三次贝塞尔(`pathInterpolator(M 0,0 C 0.05,0,0.133333,0.06,0.166666,0.4 C 0.208333,0.82,0.25,1,1,1)`),`cubic-bezier()` 只能表达一段,所以 Material 官方的 Web token 表把 `emphasized` 标为不可用、回退到 `standard`。这里改用官方单列出来的 `emphasized-decelerate`(`cubic-bezier(0.05, 0.7, 0.1, 1)`)承担位移,效果侧用 `standard`——两个都是 Material 自己发布的 token,不是拼出来的近似。
|
|
125
|
+
|
|
126
|
+
本层无法区分入场与出场,因此位移一律用 decelerate;Material 对出场建议用对应的 accelerate。
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
关闭「位移与效果使用不同曲线」后,两族都退化为只用效果曲线。
|
|
127
129
|
|
|
128
130
|
### 为什么是改写 CSSOM,而不是注入覆盖样式表
|
|
129
131
|
|
|
@@ -147,7 +149,9 @@ M3 的 emphasized 整体是一条五次贝塞尔,`cubic-bezier()` 表达不了
|
|
|
147
149
|
|
|
148
150
|
### 时长
|
|
149
151
|
|
|
150
|
-
-
|
|
152
|
+
- 「时长倍率」在界面上提供 0.5× / 0.75× / 1× / 1.25× / 1.5× / 2×。配置里手工写入的其它值(schema 允许 0.5–2)会插入档位表并选中,不会被悄悄折回 1×。
|
|
153
|
+
- 倍率作用于**写死的字面时长**;引用 `var(--ds-transition-duration*)` 的时长不动它本身,改为把这三个自定义属性按同一倍率缩放,效果一致且不用逐条改。缩放前会读取页面当前的计算值,DSH 改了基值也按新基值算。
|
|
154
|
+
- `1ms` 及以下的时长视为「立即」,既不放缩也不吸附——DSH 的减弱动效规则里写的就是 `.01ms`,把它当成待调的时长去放大,本来即时完成的过渡会变成可见的一跳。
|
|
151
155
|
- 无限循环的时长不缩放——1 秒的旋转是设计选定的速度,不是待调的过渡。
|
|
152
156
|
- 「时长对齐到 Material 档位」把结果吸附到 50/100/150/200/250/300/350/400/450/500/550/600/700/800/900/1000ms。
|
|
153
157
|
|
|
@@ -160,8 +164,9 @@ M3 的 emphasized 整体是一条五次贝塞尔,`cubic-bezier()` 表达不了
|
|
|
160
164
|
|
|
161
165
|
### 生效范围
|
|
162
166
|
|
|
163
|
-
- 覆盖页面上的全部样式表,包括其他插件注入的。首次应用时全量扫描,之后用 `MutationObserver` 监视 `head
|
|
164
|
-
-
|
|
167
|
+
- 覆盖页面上的全部样式表,包括其他插件注入的。首次应用时全量扫描,之后用 `MutationObserver` 监视 `head` 与 `body`,新注入的 `<style>` 在下一个微任务内被补上。
|
|
168
|
+
- 通过 `document.adoptedStyleSheets` 挂载的构造式样式表不是 DOM 变更,只在首次应用与下一次 `apply` 时被读取。
|
|
169
|
+
- 跨源样式表读不到 `cssRules`,会在浏览器控制台记录一行并跳过——这类表的规则无法改写,静默跳过会让部分界面停留在原生曲线而无迹可循。(这条诊断走控制台而非 `~/.dsh/ui-experience.log`:后者由宿主半写入,浏览器半够不到文件系统。)
|
|
165
170
|
- 状态会写在 `<html data-dsh-uix-motion="m3|m3e">` 上,便于区分曲线来源。
|
|
166
171
|
|
|
167
172
|
## 边框宽度
|
package/lib/client.js
CHANGED
|
@@ -127,7 +127,6 @@
|
|
|
127
127
|
const match = DURATION.exec(value.trim());
|
|
128
128
|
if (match === null) return void 0;
|
|
129
129
|
const amount = Number(match[1]);
|
|
130
|
-
if (!Number.isFinite(amount) || amount < 0) return void 0;
|
|
131
130
|
return match[2] === "s" ? amount * 1e3 : amount;
|
|
132
131
|
}
|
|
133
132
|
function quantizeMs(ms) {
|
|
@@ -143,11 +142,13 @@
|
|
|
143
142
|
return best;
|
|
144
143
|
}
|
|
145
144
|
var MAX_DURATION_MS = 1e4;
|
|
145
|
+
var NEGLIGIBLE_MS = 1;
|
|
146
146
|
function retime(value, scale, quantize) {
|
|
147
|
+
if (scale === 1 && !quantize) return void 0;
|
|
147
148
|
const original = parseDurationMs(value);
|
|
148
|
-
if (original === void 0 || original
|
|
149
|
+
if (original === void 0 || original <= NEGLIGIBLE_MS) return void 0;
|
|
149
150
|
const scaled = Math.round(original * scale);
|
|
150
|
-
const target = Math.min(MAX_DURATION_MS, Math.max(
|
|
151
|
+
const target = Math.min(MAX_DURATION_MS, Math.max(NEGLIGIBLE_MS, quantize ? quantizeMs(scaled) : scaled));
|
|
151
152
|
if (target === original) return void 0;
|
|
152
153
|
return `${target}ms`;
|
|
153
154
|
}
|
|
@@ -175,6 +176,7 @@
|
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
// src/motion.ts
|
|
179
|
+
var KEYFRAMES_RULE = 7;
|
|
178
180
|
var TRANSITIONS = {
|
|
179
181
|
selector: "transition-property",
|
|
180
182
|
duration: "transition-duration",
|
|
@@ -187,9 +189,9 @@
|
|
|
187
189
|
iterations: "animation-iteration-count"
|
|
188
190
|
};
|
|
189
191
|
var DURATION_TOKENS = [
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
"--ds-transition-duration",
|
|
193
|
+
"--ds-transition-duration-fast",
|
|
194
|
+
"--ds-transition-duration-slow"
|
|
193
195
|
];
|
|
194
196
|
function createMotion() {
|
|
195
197
|
const patches = /* @__PURE__ */ new Map();
|
|
@@ -234,7 +236,7 @@
|
|
|
234
236
|
const frames = /* @__PURE__ */ new Map();
|
|
235
237
|
for (const sheet of sheets) {
|
|
236
238
|
walk(sheet.cssRules, (rule) => {
|
|
237
|
-
if (rule.name === void 0 || rule.cssRules === void 0) return;
|
|
239
|
+
if (rule.type !== KEYFRAMES_RULE || rule.name === void 0 || rule.cssRules === void 0) return;
|
|
238
240
|
const properties = frames.get(rule.name) ?? [];
|
|
239
241
|
walk(rule.cssRules, (frame) => {
|
|
240
242
|
const declaration = frame.style;
|
|
@@ -286,7 +288,7 @@
|
|
|
286
288
|
declaration.setProperty(family.timing, nextTimings.join(", "));
|
|
287
289
|
changed = true;
|
|
288
290
|
}
|
|
289
|
-
if (durationChanged
|
|
291
|
+
if (durationChanged) {
|
|
290
292
|
declaration.setProperty(family.duration, nextDurations.join(", "));
|
|
291
293
|
changed = true;
|
|
292
294
|
}
|
|
@@ -302,8 +304,9 @@
|
|
|
302
304
|
const declaration = rule.style;
|
|
303
305
|
if (declaration === void 0 || patches.has(rule)) return;
|
|
304
306
|
const before = declaration.cssText;
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
+
const movedTransition = rewriteFamily(declaration, TRANSITIONS, frames, active);
|
|
308
|
+
const movedAnimation = rewriteFamily(declaration, ANIMATIONS, frames, active);
|
|
309
|
+
if (!movedTransition && !movedAnimation) return;
|
|
307
310
|
patches.set(rule, { declaration, before, after: declaration.cssText });
|
|
308
311
|
});
|
|
309
312
|
}
|
|
@@ -311,12 +314,19 @@
|
|
|
311
314
|
function applyDurationTokens() {
|
|
312
315
|
const active = settings;
|
|
313
316
|
if (active === void 0 || active.durationScale === 1) return;
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
const root = document.documentElement;
|
|
318
|
+
const computed = window.getComputedStyle(root);
|
|
319
|
+
for (const token of DURATION_TOKENS) {
|
|
320
|
+
const base = parseDurationMs(computed.getPropertyValue(token).trim());
|
|
321
|
+
if (base === void 0) {
|
|
322
|
+
console.error(`ui-experience: motion cannot read ${token}; durations using it keep their shipped value`);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
root.style.setProperty(token, `${Math.round(base * active.durationScale)}ms`);
|
|
316
326
|
}
|
|
317
327
|
}
|
|
318
328
|
function clearDurationTokens() {
|
|
319
|
-
for (const
|
|
329
|
+
for (const token of DURATION_TOKENS) document.documentElement.style.removeProperty(token);
|
|
320
330
|
}
|
|
321
331
|
function wantsReduced() {
|
|
322
332
|
const active = settings;
|
|
@@ -324,8 +334,7 @@
|
|
|
324
334
|
if (active.reduced === "always") return true;
|
|
325
335
|
return active.reduced === "system" && media.matches;
|
|
326
336
|
}
|
|
327
|
-
function
|
|
328
|
-
const want = wantsReduced();
|
|
337
|
+
function setReducedSheet(want) {
|
|
329
338
|
if (want === reducedActive) return;
|
|
330
339
|
reducedActive = want;
|
|
331
340
|
reducedSheet?.remove();
|
|
@@ -336,13 +345,25 @@
|
|
|
336
345
|
document.head.appendChild(sheet);
|
|
337
346
|
reducedSheet = sheet;
|
|
338
347
|
}
|
|
348
|
+
function realize() {
|
|
349
|
+
const active = settings;
|
|
350
|
+
if (active === void 0 || active.mode === "off") return;
|
|
351
|
+
const want = wantsReduced();
|
|
352
|
+
setReducedSheet(want);
|
|
353
|
+
if (want) {
|
|
354
|
+
restoreDeclarations();
|
|
355
|
+
clearDurationTokens();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
applyDurationTokens();
|
|
359
|
+
scan();
|
|
360
|
+
observer ??= observeDocument();
|
|
361
|
+
}
|
|
339
362
|
function refresh() {
|
|
340
363
|
if (refreshPending) return;
|
|
341
364
|
refreshPending = true;
|
|
342
365
|
queueMicrotask(() => {
|
|
343
366
|
refreshPending = false;
|
|
344
|
-
const active = settings;
|
|
345
|
-
if (active === void 0 || active.mode === "off" || reducedActive) return;
|
|
346
367
|
scan();
|
|
347
368
|
});
|
|
348
369
|
}
|
|
@@ -351,23 +372,24 @@
|
|
|
351
372
|
refresh();
|
|
352
373
|
});
|
|
353
374
|
instance.observe(document.head, { childList: true, subtree: true });
|
|
375
|
+
instance.observe(document.body, { childList: true, subtree: true });
|
|
354
376
|
return { disconnect: () => {
|
|
355
377
|
instance.disconnect();
|
|
356
378
|
} };
|
|
357
379
|
}
|
|
358
380
|
function unwatchPreference() {
|
|
359
381
|
if (mediaListener === void 0) return;
|
|
360
|
-
media.removeEventListener
|
|
382
|
+
media.removeEventListener("change", mediaListener);
|
|
361
383
|
mediaListener = void 0;
|
|
362
384
|
}
|
|
363
385
|
function watchPreference() {
|
|
364
386
|
if (mediaListener !== void 0) return;
|
|
365
387
|
mediaListener = () => {
|
|
366
|
-
|
|
388
|
+
realize();
|
|
367
389
|
};
|
|
368
|
-
media.addEventListener
|
|
390
|
+
media.addEventListener("change", mediaListener);
|
|
369
391
|
}
|
|
370
|
-
function
|
|
392
|
+
function restoreDeclarations() {
|
|
371
393
|
for (const patch of patches.values()) {
|
|
372
394
|
if (patch.declaration.cssText !== patch.after) {
|
|
373
395
|
console.error("ui-experience: motion restore overwrote a declaration that changed after this layer wrote it");
|
|
@@ -375,11 +397,15 @@
|
|
|
375
397
|
patch.declaration.cssText = patch.before;
|
|
376
398
|
}
|
|
377
399
|
patches.clear();
|
|
400
|
+
}
|
|
401
|
+
function restore() {
|
|
402
|
+
restoreDeclarations();
|
|
378
403
|
clearDurationTokens();
|
|
379
|
-
|
|
380
|
-
reducedSheet = void 0;
|
|
381
|
-
reducedActive = false;
|
|
404
|
+
setReducedSheet(false);
|
|
382
405
|
unwatchPreference();
|
|
406
|
+
observer?.disconnect();
|
|
407
|
+
observer = void 0;
|
|
408
|
+
settings = void 0;
|
|
383
409
|
document.documentElement.removeAttribute(MOTION_ATTRIBUTE);
|
|
384
410
|
}
|
|
385
411
|
return {
|
|
@@ -388,23 +414,13 @@
|
|
|
388
414
|
settings = next;
|
|
389
415
|
if (next.mode === "off") return;
|
|
390
416
|
watchPreference();
|
|
391
|
-
syncReduced();
|
|
392
|
-
if (reducedActive) {
|
|
393
|
-
document.documentElement.setAttribute(MOTION_ATTRIBUTE, next.mode);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
applyDurationTokens();
|
|
397
|
-
scan();
|
|
398
|
-
observer ??= observeDocument();
|
|
399
417
|
document.documentElement.setAttribute(MOTION_ATTRIBUTE, next.mode);
|
|
418
|
+
realize();
|
|
400
419
|
},
|
|
401
420
|
refresh,
|
|
402
421
|
restore,
|
|
403
422
|
dispose() {
|
|
404
423
|
restore();
|
|
405
|
-
observer?.disconnect();
|
|
406
|
-
observer = void 0;
|
|
407
|
-
settings = void 0;
|
|
408
424
|
}
|
|
409
425
|
};
|
|
410
426
|
}
|
|
@@ -579,7 +595,7 @@
|
|
|
579
595
|
"palette.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
580
596
|
"palette.paths-hint": "\u652F\u6301\u4EE5 ~ \u5F00\u5934\u7684\u8DEF\u5F84\uFF0C\u6216\u7EDD\u5BF9\u8DEF\u5F84\u3002\u989C\u8272\u6587\u4EF6\u63D0\u4F9B\u5B8C\u6574\u6620\u5C04\uFF0C\u4EE4\u724C\u6587\u4EF6\u91CC\u5217\u51FA\u7684 token \u9010\u9879\u8986\u76D6\u3002",
|
|
581
597
|
"motion.title": "M3 \u52A8\u6548",
|
|
582
|
-
"motion.description": "\u6309 Material 3 \u66F2\u7EBF\u6539\u5199\u6574\u4E2A\u754C\u9762\u7684\u8FC7\u6E21\u4E0E\u5173\u952E\u5E27\u52A8\u753B\uFF0C\
|
|
598
|
+
"motion.description": "\u6309 Material 3 \u66F2\u7EBF\u6539\u5199\u6574\u4E2A\u754C\u9762\u7684\u8FC7\u6E21\u4E0E\u5173\u952E\u5E27\u52A8\u753B\uFF0C\u76F4\u63A5\u6539\u5199\u5DF2\u751F\u6548\u7684\u6837\u5F0F\u89C4\u5219",
|
|
583
599
|
"motion.mode": "\u66F2\u7EBF\u65CF",
|
|
584
600
|
"motion.mode-off": "\u5173\u95ED",
|
|
585
601
|
"motion.mode-m3": "M3",
|
|
@@ -587,7 +603,7 @@
|
|
|
587
603
|
"motion.duration": "\u65F6\u957F\u500D\u7387",
|
|
588
604
|
"motion.options": "\u9AD8\u7EA7\u9009\u9879",
|
|
589
605
|
"motion.spatial-split": "\u4F4D\u79FB\u4E0E\u6548\u679C\u4F7F\u7528\u4E0D\u540C\u66F2\u7EBF",
|
|
590
|
-
"motion.spatial-split-hint": "
|
|
606
|
+
"motion.spatial-split-hint": "\u4F4D\u79FB\u66F2\u7EBF\u5E26\u8FC7\u51B2\uFF08\u5FEB\u4F4D\u79FB\u7EA6 9%\uFF0C\u9ED8\u8BA4\u4F4D\u79FB\u7EA6 1.4%\uFF09\uFF0C\u6548\u679C\u66F2\u7EBF\u6CA1\u6709\u3002\u5173\u95ED\u540E\u5168\u90E8\u4F7F\u7528\u6548\u679C\u66F2\u7EBF\u3002",
|
|
591
607
|
"motion.quantize": "\u65F6\u957F\u5BF9\u9F50\u5230 Material \u6863\u4F4D",
|
|
592
608
|
"motion.quantize-hint": "\u628A\u6539\u5199\u540E\u7684\u65F6\u957F\u5438\u9644\u5230 50/100/150/200/250/300/350/400/450/500/550/600/700/800/900/1000ms\u3002",
|
|
593
609
|
"motion.reduced": "\u59CB\u7EC8\u51CF\u5F31\u52A8\u6548",
|
|
@@ -617,7 +633,7 @@
|
|
|
617
633
|
"motion.duration": "Duration scale",
|
|
618
634
|
"motion.options": "Advanced options",
|
|
619
635
|
"motion.spatial-split": "Spatial and effects take different curves",
|
|
620
|
-
"motion.spatial-split-hint": "The
|
|
636
|
+
"motion.spatial-split-hint": "The spatial curves overshoot (about 9% for the fast one, 1.4% for the default); the effects curve does not. Turning this off uses the effects curve everywhere.",
|
|
621
637
|
"motion.quantize": "Snap durations to the Material ladder",
|
|
622
638
|
"motion.quantize-hint": "Pull rewritten durations onto 50/100/150/200/250/300/350/400/450/500/550/600/700/800/900/1000 ms.",
|
|
623
639
|
"motion.reduced": "Always reduce motion",
|
|
@@ -1103,12 +1119,13 @@
|
|
|
1103
1119
|
{ value: "m3", label: tr("motion.mode-m3") },
|
|
1104
1120
|
{ value: "m3e", label: tr("motion.mode-m3e") }
|
|
1105
1121
|
];
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1122
|
+
const ladder = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
1123
|
+
if (!ladder.includes(current.durationScale)) ladder.push(current.durationScale);
|
|
1124
|
+
ladder.sort((left, right) => left - right);
|
|
1125
|
+
const scales = ladder.map((value) => ({
|
|
1126
|
+
value: String(value),
|
|
1127
|
+
label: `${value}\xD7`
|
|
1128
|
+
}));
|
|
1112
1129
|
const policy = open ? h(
|
|
1113
1130
|
"div",
|
|
1114
1131
|
{ className: "dsh-uix-form" },
|
package/lib/index.js
CHANGED
|
@@ -579,9 +579,15 @@ var UiExperienceService = class extends TypertRemoteService {
|
|
|
579
579
|
if (reduced !== void 0 && !MOTION_REDUCED.test(reduced)) {
|
|
580
580
|
throw new Error(`ui-experience: unknown reduced-motion policy ${JSON.stringify(reduced)}`);
|
|
581
581
|
}
|
|
582
|
-
if (scale !== void 0 && (!Number.isFinite(scale) || scale < 0.5 || scale > 2)) {
|
|
582
|
+
if (scale !== void 0 && (typeof scale !== "number" || !Number.isFinite(scale) || scale < 0.5 || scale > 2)) {
|
|
583
583
|
throw new Error(`ui-experience: durationScale ${String(scale)} is outside 0.5\u20132`);
|
|
584
584
|
}
|
|
585
|
+
if (spatialSplit !== void 0 && typeof spatialSplit !== "boolean") {
|
|
586
|
+
throw new Error(`ui-experience: spatialSplit must be a boolean, got ${typeof spatialSplit}`);
|
|
587
|
+
}
|
|
588
|
+
if (quantize !== void 0 && typeof quantize !== "boolean") {
|
|
589
|
+
throw new Error(`ui-experience: quantize must be a boolean, got ${typeof quantize}`);
|
|
590
|
+
}
|
|
585
591
|
await this.persist((current) => ({
|
|
586
592
|
...current,
|
|
587
593
|
...mode === void 0 ? {} : { motion: mode },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plugin/dsh-ui-experience",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.43488816373",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "UI experience enhancements for DeepSeek Harness: follow the system matugen Material You palette as the interface theme, and rewrite interface motion onto Material 3 / Material 3 Expressive curves.",
|
|
6
6
|
"license": "MIT",
|