@dsh-plugin/dsh-ui-experience 1.0.0-dev.43487914661 → 1.0.0-dev.43488936935
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 +66 -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,12 +189,13 @@
|
|
|
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();
|
|
198
|
+
const durationBase = /* @__PURE__ */ new Map();
|
|
196
199
|
const unreadable = /* @__PURE__ */ new Set();
|
|
197
200
|
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
198
201
|
let settings;
|
|
@@ -234,7 +237,7 @@
|
|
|
234
237
|
const frames = /* @__PURE__ */ new Map();
|
|
235
238
|
for (const sheet of sheets) {
|
|
236
239
|
walk(sheet.cssRules, (rule) => {
|
|
237
|
-
if (rule.name === void 0 || rule.cssRules === void 0) return;
|
|
240
|
+
if (rule.type !== KEYFRAMES_RULE || rule.name === void 0 || rule.cssRules === void 0) return;
|
|
238
241
|
const properties = frames.get(rule.name) ?? [];
|
|
239
242
|
walk(rule.cssRules, (frame) => {
|
|
240
243
|
const declaration = frame.style;
|
|
@@ -286,7 +289,7 @@
|
|
|
286
289
|
declaration.setProperty(family.timing, nextTimings.join(", "));
|
|
287
290
|
changed = true;
|
|
288
291
|
}
|
|
289
|
-
if (durationChanged
|
|
292
|
+
if (durationChanged) {
|
|
290
293
|
declaration.setProperty(family.duration, nextDurations.join(", "));
|
|
291
294
|
changed = true;
|
|
292
295
|
}
|
|
@@ -302,8 +305,9 @@
|
|
|
302
305
|
const declaration = rule.style;
|
|
303
306
|
if (declaration === void 0 || patches.has(rule)) return;
|
|
304
307
|
const before = declaration.cssText;
|
|
305
|
-
const
|
|
306
|
-
|
|
308
|
+
const movedTransition = rewriteFamily(declaration, TRANSITIONS, frames, active);
|
|
309
|
+
const movedAnimation = rewriteFamily(declaration, ANIMATIONS, frames, active);
|
|
310
|
+
if (!movedTransition && !movedAnimation) return;
|
|
307
311
|
patches.set(rule, { declaration, before, after: declaration.cssText });
|
|
308
312
|
});
|
|
309
313
|
}
|
|
@@ -311,12 +315,23 @@
|
|
|
311
315
|
function applyDurationTokens() {
|
|
312
316
|
const active = settings;
|
|
313
317
|
if (active === void 0 || active.durationScale === 1) return;
|
|
314
|
-
|
|
315
|
-
|
|
318
|
+
const root = document.documentElement;
|
|
319
|
+
const computed = window.getComputedStyle(root);
|
|
320
|
+
for (const token of DURATION_TOKENS) {
|
|
321
|
+
let base = durationBase.get(token);
|
|
322
|
+
if (base === void 0) {
|
|
323
|
+
base = parseDurationMs(computed.getPropertyValue(token).trim());
|
|
324
|
+
if (base === void 0) {
|
|
325
|
+
console.error(`ui-experience: motion cannot read ${token}; durations using it keep their shipped value`);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
durationBase.set(token, base);
|
|
329
|
+
}
|
|
330
|
+
root.style.setProperty(token, `${Math.round(base * active.durationScale)}ms`);
|
|
316
331
|
}
|
|
317
332
|
}
|
|
318
333
|
function clearDurationTokens() {
|
|
319
|
-
for (const
|
|
334
|
+
for (const token of DURATION_TOKENS) document.documentElement.style.removeProperty(token);
|
|
320
335
|
}
|
|
321
336
|
function wantsReduced() {
|
|
322
337
|
const active = settings;
|
|
@@ -324,8 +339,7 @@
|
|
|
324
339
|
if (active.reduced === "always") return true;
|
|
325
340
|
return active.reduced === "system" && media.matches;
|
|
326
341
|
}
|
|
327
|
-
function
|
|
328
|
-
const want = wantsReduced();
|
|
342
|
+
function setReducedSheet(want) {
|
|
329
343
|
if (want === reducedActive) return;
|
|
330
344
|
reducedActive = want;
|
|
331
345
|
reducedSheet?.remove();
|
|
@@ -336,13 +350,25 @@
|
|
|
336
350
|
document.head.appendChild(sheet);
|
|
337
351
|
reducedSheet = sheet;
|
|
338
352
|
}
|
|
353
|
+
function realize() {
|
|
354
|
+
const active = settings;
|
|
355
|
+
if (active === void 0 || active.mode === "off") return;
|
|
356
|
+
const want = wantsReduced();
|
|
357
|
+
setReducedSheet(want);
|
|
358
|
+
if (want) {
|
|
359
|
+
restoreDeclarations();
|
|
360
|
+
clearDurationTokens();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
applyDurationTokens();
|
|
364
|
+
scan();
|
|
365
|
+
observer ??= observeDocument();
|
|
366
|
+
}
|
|
339
367
|
function refresh() {
|
|
340
368
|
if (refreshPending) return;
|
|
341
369
|
refreshPending = true;
|
|
342
370
|
queueMicrotask(() => {
|
|
343
371
|
refreshPending = false;
|
|
344
|
-
const active = settings;
|
|
345
|
-
if (active === void 0 || active.mode === "off" || reducedActive) return;
|
|
346
372
|
scan();
|
|
347
373
|
});
|
|
348
374
|
}
|
|
@@ -351,23 +377,24 @@
|
|
|
351
377
|
refresh();
|
|
352
378
|
});
|
|
353
379
|
instance.observe(document.head, { childList: true, subtree: true });
|
|
380
|
+
instance.observe(document.body, { childList: true, subtree: true });
|
|
354
381
|
return { disconnect: () => {
|
|
355
382
|
instance.disconnect();
|
|
356
383
|
} };
|
|
357
384
|
}
|
|
358
385
|
function unwatchPreference() {
|
|
359
386
|
if (mediaListener === void 0) return;
|
|
360
|
-
media.removeEventListener
|
|
387
|
+
media.removeEventListener("change", mediaListener);
|
|
361
388
|
mediaListener = void 0;
|
|
362
389
|
}
|
|
363
390
|
function watchPreference() {
|
|
364
391
|
if (mediaListener !== void 0) return;
|
|
365
392
|
mediaListener = () => {
|
|
366
|
-
|
|
393
|
+
realize();
|
|
367
394
|
};
|
|
368
|
-
media.addEventListener
|
|
395
|
+
media.addEventListener("change", mediaListener);
|
|
369
396
|
}
|
|
370
|
-
function
|
|
397
|
+
function restoreDeclarations() {
|
|
371
398
|
for (const patch of patches.values()) {
|
|
372
399
|
if (patch.declaration.cssText !== patch.after) {
|
|
373
400
|
console.error("ui-experience: motion restore overwrote a declaration that changed after this layer wrote it");
|
|
@@ -375,11 +402,15 @@
|
|
|
375
402
|
patch.declaration.cssText = patch.before;
|
|
376
403
|
}
|
|
377
404
|
patches.clear();
|
|
405
|
+
}
|
|
406
|
+
function restore() {
|
|
407
|
+
restoreDeclarations();
|
|
378
408
|
clearDurationTokens();
|
|
379
|
-
|
|
380
|
-
reducedSheet = void 0;
|
|
381
|
-
reducedActive = false;
|
|
409
|
+
setReducedSheet(false);
|
|
382
410
|
unwatchPreference();
|
|
411
|
+
observer?.disconnect();
|
|
412
|
+
observer = void 0;
|
|
413
|
+
settings = void 0;
|
|
383
414
|
document.documentElement.removeAttribute(MOTION_ATTRIBUTE);
|
|
384
415
|
}
|
|
385
416
|
return {
|
|
@@ -388,23 +419,13 @@
|
|
|
388
419
|
settings = next;
|
|
389
420
|
if (next.mode === "off") return;
|
|
390
421
|
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
422
|
document.documentElement.setAttribute(MOTION_ATTRIBUTE, next.mode);
|
|
423
|
+
realize();
|
|
400
424
|
},
|
|
401
425
|
refresh,
|
|
402
426
|
restore,
|
|
403
427
|
dispose() {
|
|
404
428
|
restore();
|
|
405
|
-
observer?.disconnect();
|
|
406
|
-
observer = void 0;
|
|
407
|
-
settings = void 0;
|
|
408
429
|
}
|
|
409
430
|
};
|
|
410
431
|
}
|
|
@@ -579,7 +600,7 @@
|
|
|
579
600
|
"palette.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
580
601
|
"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
602
|
"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\
|
|
603
|
+
"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
604
|
"motion.mode": "\u66F2\u7EBF\u65CF",
|
|
584
605
|
"motion.mode-off": "\u5173\u95ED",
|
|
585
606
|
"motion.mode-m3": "M3",
|
|
@@ -587,7 +608,7 @@
|
|
|
587
608
|
"motion.duration": "\u65F6\u957F\u500D\u7387",
|
|
588
609
|
"motion.options": "\u9AD8\u7EA7\u9009\u9879",
|
|
589
610
|
"motion.spatial-split": "\u4F4D\u79FB\u4E0E\u6548\u679C\u4F7F\u7528\u4E0D\u540C\u66F2\u7EBF",
|
|
590
|
-
"motion.spatial-split-hint": "
|
|
611
|
+
"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
612
|
"motion.quantize": "\u65F6\u957F\u5BF9\u9F50\u5230 Material \u6863\u4F4D",
|
|
592
613
|
"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
614
|
"motion.reduced": "\u59CB\u7EC8\u51CF\u5F31\u52A8\u6548",
|
|
@@ -617,7 +638,7 @@
|
|
|
617
638
|
"motion.duration": "Duration scale",
|
|
618
639
|
"motion.options": "Advanced options",
|
|
619
640
|
"motion.spatial-split": "Spatial and effects take different curves",
|
|
620
|
-
"motion.spatial-split-hint": "The
|
|
641
|
+
"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
642
|
"motion.quantize": "Snap durations to the Material ladder",
|
|
622
643
|
"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
644
|
"motion.reduced": "Always reduce motion",
|
|
@@ -1103,12 +1124,13 @@
|
|
|
1103
1124
|
{ value: "m3", label: tr("motion.mode-m3") },
|
|
1104
1125
|
{ value: "m3e", label: tr("motion.mode-m3e") }
|
|
1105
1126
|
];
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1127
|
+
const ladder = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
1128
|
+
if (!ladder.includes(current.durationScale)) ladder.push(current.durationScale);
|
|
1129
|
+
ladder.sort((left, right) => left - right);
|
|
1130
|
+
const scales = ladder.map((value) => ({
|
|
1131
|
+
value: String(value),
|
|
1132
|
+
label: `${value}\xD7`
|
|
1133
|
+
}));
|
|
1112
1134
|
const policy = open ? h(
|
|
1113
1135
|
"div",
|
|
1114
1136
|
{ 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.43488936935",
|
|
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",
|