@dsh-plugin/dsh-ui-experience 1.0.0-dev.43492311898 → 1.0.0-dev.43493974985
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 +24 -8
- package/lib/client.js +62 -89
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -143,17 +143,32 @@ M3 那侧的分离要说明白:Material 3 的 `emphasized` 整体是两段首
|
|
|
143
143
|
|
|
144
144
|
### 滚动
|
|
145
145
|
|
|
146
|
-
`scroll-behavior: smooth` 与脚本调用的 `scrollTo({behavior:'smooth'})` 都不受 CSS 曲线控制——时长和曲线由浏览器定,CSS 读不到,JS
|
|
146
|
+
`scroll-behavior: smooth` 与脚本调用的 `scrollTo({behavior:'smooth'})` 都不受 CSS 曲线控制——时长和曲线由浏览器定,CSS 读不到,JS 也拿不到。勾选「定位滚动使用同一曲线」后,本层接管 `Element.prototype.scrollIntoView`,用脚本滚动把**一次性定位**(轨迹行聚焦、插件卡片居中、提问卡定位)换成本页曲线。
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
#### 为什么只接管 scrollIntoView
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
2. **用效果曲线,不用位移曲线。** 位移曲线带过冲,滚动时过冲意味着内容冲过目标位置再退回来,终点错两次。曲线族照样生效,带过冲的那半永远不用于滚动。
|
|
152
|
-
3. **时长按距离增长。** 240px 以内固定 180ms,之后每像素加 0.2ms,封顶 460ms,再乘时长倍率。滚动的手感预期和元素过渡不同:读者对「滚了多远、停在哪」有精确期待,长距离用固定时长会显得急促。
|
|
150
|
+
`scrollTo` / `scrollBy` 若一并接管,会破坏聊天跟随。跟随控制器先设一个目标,再等 `scrollend` 结算;在 Chrome 156 实测:
|
|
153
151
|
|
|
154
|
-
|
|
152
|
+
| 写入方式 | scroll 事件 | scrollend 事件 |
|
|
153
|
+
| --- | --- | --- |
|
|
154
|
+
| 原生 `behavior:'smooth'` 一次 | 89 | **1** |
|
|
155
|
+
| 逐帧赋值 `scrollTop`(脚本动画) | 21 | **21** |
|
|
156
|
+
|
|
157
|
+
浏览器会为自己发起的每一次瞬时滚动各发一次 `scrollend`,本层无法拦截。控制器把其中第一个读作「读者在中途停下」,于是在动画第一帧就放弃跟随。因此走这条循环的调用一律保持原生。
|
|
158
|
+
|
|
159
|
+
`scrollIntoView` 属于一次性定位:一次手势对应一个终点,没有消费方靠数滚动结束事件推断意图。
|
|
160
|
+
|
|
161
|
+
#### 固定的两条规则
|
|
162
|
+
|
|
163
|
+
1. **读者优先。** 在正在滚动的容器上滚轮或触摸、或在文本框之外按滚动键,脚本滚动立即取消,不混合、不恢复;任何本层没写过的位置变化(拖滚动条、辅助技术滚动、容器在动画中重排)也会在下一帧取消。滚轮落在别的面板上不会打断——那不是这个容器的滚动。
|
|
164
|
+
2. **用效果曲线,不用位移曲线。** 位移曲线带过冲,滚动过冲意味着内容冲过目标位置再退回来,终点错两次。曲线族照样生效,带过冲的那半永远不用于滚动。
|
|
165
|
+
|
|
166
|
+
#### 时长与落点
|
|
155
167
|
|
|
156
|
-
|
|
168
|
+
- 240px 以内固定 180ms,之后每像素加 0.2ms,封顶 460ms;再乘时长倍率,开了「时长对齐」时同样吸附到 Material 档位。
|
|
169
|
+
- 终点由对齐方式、`scroll-margin`、嵌套容器和元素自身盒模型共同决定,自己算就是「滚到错的那一行」的来源。做法是先向浏览器要一次瞬时跳转,读出每个被移动容器的落点,把它们全部放回原位,再动画到读到的落点;跳转与还原在同一个任务内完成,中间位置不会绘制。
|
|
170
|
+
- 返回值:脚本路径返回一个在落点写入(或被取消)后兑现的 Promise,与原生 `scrollTo` 一致。
|
|
171
|
+
- 「始终减弱动效」或系统的减弱动效偏好生效时,平滑定位直接变成瞬时跳转。
|
|
157
172
|
|
|
158
173
|
### 滚动驱动动画不动
|
|
159
174
|
|
|
@@ -205,7 +220,8 @@ DSH 目前只有一处用到(提问卡片标题的溢出遮罩 `--question-cop
|
|
|
205
220
|
- 跨源样式表里的规则无法改写,只会在日志中记录。
|
|
206
221
|
- 本层只改写曲线与时长,不新增、不改写关键帧本身;DSH 没定义的动效不会因此出现。
|
|
207
222
|
- CSS 的 `scroll-behavior: smooth`(全仓一处,dock 标签条)不是 transition/animation,本层不碰;只有减弱动效时随压制表置为 `auto`。
|
|
208
|
-
-
|
|
223
|
+
- 脚本滚动只覆盖 `scrollIntoView`,由「定位滚动使用同一曲线」单独控制,与曲线族无关(关掉曲线族就一并停止接管)。聊天跟随、附件横向滚动、虚拟列表定位都走 `scrollTo`/`scrollBy`,保持原生时序。
|
|
224
|
+
- 接管的定位滚动会按帧产生多次 `scrollend`(实测一次约 28 次)。目前没有消费方在这一族调用上依赖单次 `scrollend`;若将来 DSH 给轨迹面板加上类似的跟随控制器,需要重新评估。
|
|
209
225
|
- 滚动驱动动画(`animation-timeline`)不属于时间轴动画,本层显式跳过,见上一节。
|
|
210
226
|
- 浏览器半在刷新页面时重载,宿主半只在进程重启时重载。两者版本不一致时(刚升级插件但没重启 DSH),M3 动效行会保持无操作并在控制台记录一行原因,配色功能不受影响。
|
|
211
227
|
|
package/lib/client.js
CHANGED
|
@@ -472,6 +472,8 @@
|
|
|
472
472
|
var MAX_MS = 460;
|
|
473
473
|
var FREE_PX = 240;
|
|
474
474
|
var GROWTH_MS_PER_PX = 0.2;
|
|
475
|
+
var MOTION_EPSILON = 0.5;
|
|
476
|
+
var FOREIGN_TOLERANCE = 1;
|
|
475
477
|
var SCROLL_KEYS = /* @__PURE__ */ new Set([
|
|
476
478
|
"ArrowUp",
|
|
477
479
|
"ArrowDown",
|
|
@@ -483,7 +485,7 @@
|
|
|
483
485
|
"End",
|
|
484
486
|
" "
|
|
485
487
|
]);
|
|
486
|
-
var
|
|
488
|
+
var EDITABLE = 'input, textarea, select, [contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"]';
|
|
487
489
|
function scrollDistance(legs) {
|
|
488
490
|
let distance = 0;
|
|
489
491
|
for (const leg of legs) {
|
|
@@ -493,7 +495,8 @@
|
|
|
493
495
|
}
|
|
494
496
|
function scrollDurationMs(distance, settings) {
|
|
495
497
|
const grown = MIN_MS + Math.max(0, distance - FREE_PX) * GROWTH_MS_PER_PX;
|
|
496
|
-
|
|
498
|
+
const scaled = Math.min(MAX_MS, grown) * settings.durationScale;
|
|
499
|
+
return settings.quantize ? quantizeMs(scaled) : scaled;
|
|
497
500
|
}
|
|
498
501
|
function maxima(element) {
|
|
499
502
|
return {
|
|
@@ -501,32 +504,27 @@
|
|
|
501
504
|
left: Math.max(0, element.scrollWidth - element.clientWidth)
|
|
502
505
|
};
|
|
503
506
|
}
|
|
504
|
-
function scrollable(element) {
|
|
505
|
-
const max = maxima(element);
|
|
506
|
-
return max.top > 0 || max.left > 0;
|
|
507
|
-
}
|
|
508
507
|
function containers(element) {
|
|
509
508
|
const out = [];
|
|
510
509
|
let node = element.parentElement;
|
|
511
510
|
while (node !== null) {
|
|
512
|
-
|
|
511
|
+
const max = maxima(node);
|
|
512
|
+
if (max.top > 0 || max.left > 0) out.push(node);
|
|
513
513
|
node = node.parentElement;
|
|
514
514
|
}
|
|
515
515
|
return out;
|
|
516
516
|
}
|
|
517
517
|
function createScrollMotion() {
|
|
518
|
-
const shipped =
|
|
519
|
-
scrollTo: Element.prototype.scrollTo,
|
|
520
|
-
scrollBy: Element.prototype.scrollBy,
|
|
521
|
-
scrollIntoView: Element.prototype.scrollIntoView
|
|
522
|
-
};
|
|
518
|
+
const shipped = Element.prototype.scrollIntoView;
|
|
523
519
|
let settings;
|
|
524
520
|
let run;
|
|
525
521
|
let installed = false;
|
|
526
522
|
function cancel() {
|
|
527
|
-
|
|
528
|
-
|
|
523
|
+
const active = run;
|
|
524
|
+
if (active === void 0) return;
|
|
525
|
+
window.cancelAnimationFrame(active.frame);
|
|
529
526
|
run = void 0;
|
|
527
|
+
active.done();
|
|
530
528
|
}
|
|
531
529
|
function step(now) {
|
|
532
530
|
const active = run;
|
|
@@ -538,7 +536,7 @@
|
|
|
538
536
|
const leg = active.legs[index];
|
|
539
537
|
const written = active.written[index];
|
|
540
538
|
if (leg === void 0 || written === void 0) continue;
|
|
541
|
-
if (Math.abs(leg.element.scrollTop - written.top) >
|
|
539
|
+
if (Math.abs(leg.element.scrollTop - written.top) > FOREIGN_TOLERANCE || Math.abs(leg.element.scrollLeft - written.left) > FOREIGN_TOLERANCE) {
|
|
542
540
|
cancel();
|
|
543
541
|
return;
|
|
544
542
|
}
|
|
@@ -551,77 +549,49 @@
|
|
|
551
549
|
}
|
|
552
550
|
if (progress >= 1) {
|
|
553
551
|
run = void 0;
|
|
552
|
+
active.done();
|
|
554
553
|
return;
|
|
555
554
|
}
|
|
556
555
|
active.frame = window.requestAnimationFrame(step);
|
|
557
556
|
}
|
|
558
|
-
function start(legs) {
|
|
557
|
+
function start(legs, done) {
|
|
559
558
|
cancel();
|
|
560
559
|
const active = settings;
|
|
561
|
-
if (active === void 0)
|
|
560
|
+
if (active === void 0) {
|
|
561
|
+
done();
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
562
564
|
run = {
|
|
563
565
|
legs,
|
|
564
566
|
started: performance.now(),
|
|
565
567
|
duration: scrollDurationMs(scrollDistance(legs), active),
|
|
566
568
|
written: legs.map((leg) => ({ top: leg.element.scrollTop, left: leg.element.scrollLeft })),
|
|
567
|
-
frame: window.requestAnimationFrame(step)
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
function defer(behavior) {
|
|
571
|
-
if (settings === void 0) return true;
|
|
572
|
-
if (behavior !== "smooth") return true;
|
|
573
|
-
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
574
|
-
}
|
|
575
|
-
function legsFor(element, top, left) {
|
|
576
|
-
const max = maxima(element);
|
|
577
|
-
const toTop = top === void 0 ? element.scrollTop : Math.min(max.top, Math.max(0, top));
|
|
578
|
-
const toLeft = left === void 0 ? element.scrollLeft : Math.min(max.left, Math.max(0, left));
|
|
579
|
-
if (Math.abs(toTop - element.scrollTop) < 1 && Math.abs(toLeft - element.scrollLeft) < 1) return void 0;
|
|
580
|
-
return [{ element, fromTop: element.scrollTop, fromLeft: element.scrollLeft, toTop, toLeft }];
|
|
581
|
-
}
|
|
582
|
-
function readOffsets(options, y, base) {
|
|
583
|
-
if (typeof options === "number") {
|
|
584
|
-
return { left: base.left + options, top: y === void 0 ? void 0 : base.top + y };
|
|
585
|
-
}
|
|
586
|
-
if (options === void 0 || options === null) return { top: void 0, left: void 0 };
|
|
587
|
-
return {
|
|
588
|
-
left: options.left === void 0 ? void 0 : base.left + options.left,
|
|
589
|
-
top: options.top === void 0 ? void 0 : base.top + options.top
|
|
569
|
+
frame: window.requestAnimationFrame(step),
|
|
570
|
+
done
|
|
590
571
|
};
|
|
591
572
|
}
|
|
592
|
-
function
|
|
593
|
-
const
|
|
594
|
-
if (
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
const request = readOffsets(options, y, { top: 0, left: 0 });
|
|
600
|
-
const legs = legsFor(this, request.top, request.left);
|
|
601
|
-
if (legs !== void 0) start(legs);
|
|
573
|
+
function wantsInstant() {
|
|
574
|
+
const active = settings;
|
|
575
|
+
if (active === void 0) return true;
|
|
576
|
+
if (active.reduced === "always") return true;
|
|
577
|
+
return active.reduced === "system" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
602
578
|
}
|
|
603
|
-
function
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
579
|
+
function patchedScrollIntoView(...args) {
|
|
580
|
+
const options = args[0];
|
|
581
|
+
const requested = typeof options === "object" && options !== null ? options : void 0;
|
|
582
|
+
const active = settings;
|
|
583
|
+
if (active === void 0 || requested?.behavior !== "smooth") {
|
|
584
|
+
shipped.apply(this, args);
|
|
585
|
+
return void 0;
|
|
609
586
|
}
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
function patchedScrollIntoView(options) {
|
|
615
|
-
const behavior = typeof options === "object" && options !== null ? options.behavior : void 0;
|
|
616
|
-
if (defer(behavior)) {
|
|
617
|
-
;
|
|
618
|
-
shipped.scrollIntoView.call(this, options);
|
|
619
|
-
return;
|
|
587
|
+
const jump = { ...requested, behavior: "instant" };
|
|
588
|
+
if (wantsInstant()) {
|
|
589
|
+
shipped.call(this, jump);
|
|
590
|
+
return void 0;
|
|
620
591
|
}
|
|
621
|
-
const instant = { ...typeof options === "object" && options !== null ? options : {}, behavior: "instant" };
|
|
622
592
|
const tracked = containers(this);
|
|
623
593
|
const before = tracked.map((element) => ({ top: element.scrollTop, left: element.scrollLeft }));
|
|
624
|
-
shipped.
|
|
594
|
+
shipped.call(this, jump);
|
|
625
595
|
const legs = [];
|
|
626
596
|
for (let index = 0; index < tracked.length; index += 1) {
|
|
627
597
|
const element = tracked[index];
|
|
@@ -630,39 +600,43 @@
|
|
|
630
600
|
const landed = { top: element.scrollTop, left: element.scrollLeft };
|
|
631
601
|
element.scrollTop = origin.top;
|
|
632
602
|
element.scrollLeft = origin.left;
|
|
633
|
-
|
|
603
|
+
const moved = Math.abs(landed.top - origin.top) > MOTION_EPSILON || Math.abs(landed.left - origin.left) > MOTION_EPSILON;
|
|
604
|
+
if (!moved) continue;
|
|
634
605
|
legs.push({ element, fromTop: origin.top, fromLeft: origin.left, toTop: landed.top, toLeft: landed.left });
|
|
635
606
|
}
|
|
636
|
-
if (legs.length
|
|
607
|
+
if (legs.length === 0) return void 0;
|
|
608
|
+
return new Promise((resolve) => {
|
|
609
|
+
start(legs, resolve);
|
|
610
|
+
});
|
|
637
611
|
}
|
|
638
612
|
function onTakeover(event) {
|
|
639
|
-
|
|
640
|
-
if (
|
|
613
|
+
const active = run;
|
|
614
|
+
if (active === void 0) return;
|
|
615
|
+
if (event.type === "keydown") {
|
|
616
|
+
if (event.key === void 0 || !SCROLL_KEYS.has(event.key)) return;
|
|
617
|
+
const target2 = event.target;
|
|
618
|
+
if (target2 instanceof Element && target2.closest(EDITABLE) !== null) return;
|
|
641
619
|
cancel();
|
|
642
620
|
return;
|
|
643
621
|
}
|
|
644
|
-
|
|
622
|
+
const target = event.target;
|
|
623
|
+
if (target === null || !active.legs.some((leg) => leg.element.contains(target))) return;
|
|
624
|
+
cancel();
|
|
645
625
|
}
|
|
646
626
|
function install() {
|
|
647
627
|
if (installed) return;
|
|
648
628
|
installed = true;
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
prototype.scrollIntoView = patchedScrollIntoView;
|
|
653
|
-
for (const kind of TAKEOVER_EVENTS) {
|
|
654
|
-
window.addEventListener(kind, onTakeover, true);
|
|
629
|
+
Element.prototype.scrollIntoView = patchedScrollIntoView;
|
|
630
|
+
for (const kind of ["wheel", "touchstart", "keydown"]) {
|
|
631
|
+
window.addEventListener(kind, onTakeover, { capture: true, passive: true });
|
|
655
632
|
}
|
|
656
633
|
}
|
|
657
634
|
function uninstall() {
|
|
658
635
|
cancel();
|
|
659
636
|
if (!installed) return;
|
|
660
637
|
installed = false;
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
prototype.scrollBy = shipped.scrollBy;
|
|
664
|
-
prototype.scrollIntoView = shipped.scrollIntoView;
|
|
665
|
-
for (const kind of TAKEOVER_EVENTS) {
|
|
638
|
+
Element.prototype.scrollIntoView = shipped;
|
|
639
|
+
for (const kind of ["wheel", "touchstart", "keydown"]) {
|
|
666
640
|
window.removeEventListener(kind, onTakeover, true);
|
|
667
641
|
}
|
|
668
642
|
}
|
|
@@ -671,7 +645,6 @@
|
|
|
671
645
|
uninstall();
|
|
672
646
|
settings = next;
|
|
673
647
|
if (!next.scroll || next.mode === "off") return;
|
|
674
|
-
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
675
648
|
install();
|
|
676
649
|
},
|
|
677
650
|
restore: uninstall
|
|
@@ -859,8 +832,8 @@
|
|
|
859
832
|
"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",
|
|
860
833
|
"motion.quantize": "\u65F6\u957F\u5BF9\u9F50\u5230 Material \u6863\u4F4D",
|
|
861
834
|
"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",
|
|
862
|
-
"motion.scroll": "\u6EDA\u52A8\u4F7F\u7528\u540C\u4E00\u66F2\u7EBF",
|
|
863
|
-
"motion.scroll-hint": "\u63A5\u7BA1\
|
|
835
|
+
"motion.scroll": "\u5B9A\u4F4D\u6EDA\u52A8\u4F7F\u7528\u540C\u4E00\u66F2\u7EBF",
|
|
836
|
+
"motion.scroll-hint": "\u63A5\u7BA1 scrollIntoView \u7684\u5E73\u6ED1\u5B9A\u4F4D\uFF08\u8F68\u8FF9\u884C\u805A\u7126\u3001\u63D2\u4EF6\u5361\u7247\u5C45\u4E2D\uFF09\uFF0C\u6539\u7528\u672C\u9875\u66F2\u7EBF\u3002\u8BFB\u8005\u4E00\u65E6\u5728\u5BB9\u5668\u4E0A\u6EDA\u8F6E\u3001\u89E6\u6478\u6216\u6309\u6EDA\u52A8\u952E\uFF0C\u811A\u672C\u6EDA\u52A8\u7ACB\u5373\u505C\u6B62\u5E76\u4EA4\u8FD8\u539F\u751F\u63A7\u5236\u3002\u804A\u5929\u8DDF\u968F\u4E0E\u5404\u7C7B scrollTo \u6EDA\u52A8\u4FDD\u6301\u539F\u751F\uFF1A\u5B83\u4EEC\u9760\u6BCF\u6B21\u6EDA\u52A8\u53EA\u7ED3\u675F\u4E00\u6B21\u7684 scrollend \u5224\u65AD\u610F\u56FE\uFF0C\u800C\u9010\u5E27\u811A\u672C\u6EDA\u52A8\u4F1A\u6BCF\u5E27\u7ED3\u675F\u4E00\u6B21\u3002",
|
|
864
837
|
"motion.reduced": "\u59CB\u7EC8\u51CF\u5F31\u52A8\u6548",
|
|
865
838
|
"motion.reduced-hint": "\u5F00\u542F\u540E\u5FFD\u7565\u7CFB\u7EDF\u7684\u300C\u51CF\u5F31\u52A8\u6001\u6548\u679C\u300D\u504F\u597D\uFF0C\u4E00\u5F8B\u628A\u65F6\u957F\u538B\u5230 1ms\u3002\u5173\u95ED\u65F6\u8DDF\u968F\u7CFB\u7EDF\u3002",
|
|
866
839
|
"motion.system-reduced": "\u7CFB\u7EDF\u5DF2\u8BF7\u6C42\u51CF\u5F31\u52A8\u6548\uFF0C\u672C\u9875\u52A8\u753B\u6B63\u5728\u88AB\u538B\u5236\u3002",
|
|
@@ -891,8 +864,8 @@
|
|
|
891
864
|
"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.",
|
|
892
865
|
"motion.quantize": "Snap durations to the Material ladder",
|
|
893
866
|
"motion.quantize-hint": "Pull rewritten durations onto 50/100/150/200/250/300/350/400/450/500/550/600/700/800/900/1000 ms.",
|
|
894
|
-
"motion.scroll": "
|
|
895
|
-
"motion.scroll-hint": "Take over
|
|
867
|
+
"motion.scroll": "Locating scroll on the same curve",
|
|
868
|
+
"motion.scroll-hint": "Take over the smooth locating jump of scrollIntoView \u2014 focus a trajectory row, centre a plugin card \u2014 and drive it on this page's curve. A wheel, touch, or scroll key over the container stops it at once. Chat follow and other scrollTo scrolling stay native: they read one end-of-scroll event per gesture to infer intent, and frame-by-frame scripted scrolling ends once per frame.",
|
|
896
869
|
"motion.reduced": "Always reduce motion",
|
|
897
870
|
"motion.reduced-hint": "Ignore the system reduce-motion preference and force every duration to 1 ms. Off follows the system.",
|
|
898
871
|
"motion.system-reduced": "The system asked for reduced motion; this page is being held still.",
|
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.43493974985",
|
|
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",
|