dsh-theme-endfield 1.2.4 → 1.2.6
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 +2 -0
- package/client.js +206 -6
- package/docs/engineering-notes.md +59 -1
- package/docs/features.md +4 -1
- package/docs/testing.md +3 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ dsh plugin --profile web rm dsh-theme-endfield
|
|
|
68
68
|
- **插件卡片展示文案**改由 `locale/<语言>.json` 的 `meta.title` / `meta.description` 提供;`package.json` 的 `dsh.client.inject` 也不再列 0.2 已删除的 `@deepseek-ai/dsh-client-runtime` 与 `@deepseek-ai/dsh-client-ui-slots`,改为列出 0.2 真正的提供方。
|
|
69
69
|
- **composer seat 的 `will-change: transform`**(0.2.2-rc.1):它会让 seat 成为 `position: fixed` 后代的 containing block,而审批白条正是靠 fixed 铺满视口——白条因此塌成窗口底部的一条线,只剩「等待审批」可见([#41](https://github.com/ymh0000123/dsh-theme-endfield/issues/41))。主题在审批面板挂载期间把该提示降回 `auto`,不改 DOM、也不长期关掉上游的合成提示。
|
|
70
70
|
|
|
71
|
+
- **工作区会话行高亮两侧留缝**:app 原生高亮是零几何结构——每行自带 3px 透明描边框,选中态只换背景色;原生淡色下内缩不明显,主题的强高亮色让未上色的描边框透出栏底。主题照原生的结构只换颜色——把描边框刷成同一个高亮色,高亮贴满行盒,文字、栏距、滚动条、圆角(圆角模式 12px)全部保持 app 原生,不再触碰滚动容器的裁剪边界。
|
|
72
|
+
|
|
71
73
|
细节见 [docs/engineering-notes.md](docs/engineering-notes.md)。
|
|
72
74
|
|
|
73
75
|
### 开关总是「刷新后复位」
|
package/client.js
CHANGED
|
@@ -5413,6 +5413,18 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
5413
5413
|
{ name: '国庆节', from: '10-01', to: '10-07', makeup: ['09-20', '10-10'] },
|
|
5414
5414
|
],
|
|
5415
5415
|
},
|
|
5416
|
+
2027: {
|
|
5417
|
+
notice: '国办发明电〔2026〕10号',
|
|
5418
|
+
spans: [
|
|
5419
|
+
{ name: '元旦', from: '01-01', to: '01-03', makeup: [] },
|
|
5420
|
+
{ name: '春节', from: '02-05', to: '02-13', makeup: ['01-31', '02-14'] },
|
|
5421
|
+
{ name: '清明节', from: '04-03', to: '04-05', makeup: [] },
|
|
5422
|
+
{ name: '劳动节', from: '05-01', to: '05-05', makeup: ['05-08'] },
|
|
5423
|
+
{ name: '端午节', from: '06-09', to: '06-09', makeup: [] },
|
|
5424
|
+
{ name: '中秋节', from: '09-15', to: '09-15', makeup: [] },
|
|
5425
|
+
{ name: '国庆节', from: '10-01', to: '10-07', makeup: ['09-26', '10-09'] },
|
|
5426
|
+
],
|
|
5427
|
+
},
|
|
5416
5428
|
}
|
|
5417
5429
|
|
|
5418
5430
|
/* 'YYYY-MM-DD' -> holiday name, expanded once from the spans above. */
|
|
@@ -6493,6 +6505,42 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6493
6505
|
scrollbar-width: thin;
|
|
6494
6506
|
scrollbar-color: var(--edge-line) transparent;
|
|
6495
6507
|
}
|
|
6508
|
+
/* Issue #43: the app draws the conversation-width drag indicator with a
|
|
6509
|
+
gradient whose two solid stops are var(--dsw-alias-scrollbar-bg-l1). The
|
|
6510
|
+
theme sets that token (and l2) to transparent so it can paint its own
|
|
6511
|
+
scrollbars — and the handle is the ONLY consumer of l1 that is not a
|
|
6512
|
+
scrollbar thumb, so the line is still painted on hover / while dragging,
|
|
6513
|
+
at opacity 1, in transparent. That is the whole bug.
|
|
6514
|
+
|
|
6515
|
+
WHY NOT JUST UN-TRANSPARENT THE TOKEN. The app spends --dsw-alias-
|
|
6516
|
+
scrollbar-bg-l2 in 45 rules and l1 in 3; every one but this gradient is a
|
|
6517
|
+
thumb (--dsh-scrollbar-thumb). Handing the token back a colour would
|
|
6518
|
+
repaint every themed scrollbar to the app's stock grey, i.e. trade one
|
|
6519
|
+
visible regression for another. So the token stays transparent and the
|
|
6520
|
+
handle is repainted on its own.
|
|
6521
|
+
|
|
6522
|
+
COLOUR: --dsw-alias-label-primary, the exact pair the app already uses for
|
|
6523
|
+
its own line (#0f1115 on cream, #f9fafb on near-black — 15.35:1 / 18.11:1
|
|
6524
|
+
against their papers). The theme never remaps that token globally, and
|
|
6525
|
+
reading it means both schemes and both palettes come out right without a
|
|
6526
|
+
second rule or a backing plate.
|
|
6527
|
+
|
|
6528
|
+
GEOMETRY: none. Width, radius, offsets and the pointer-y math stay the
|
|
6529
|
+
app's; only the two colour stops change, so the handle cannot grow into
|
|
6530
|
+
the transcript column.
|
|
6531
|
+
|
|
6532
|
+
ANCHORS: [data-width-handle] is the e2e hook the app declares on both
|
|
6533
|
+
handles; the class is matched as a SUBSTRING because a suffix match dies
|
|
6534
|
+
the day upstream appends a second class (the 0.1.2-rc.1 lesson). */
|
|
6535
|
+
:is([data-width-handle], [class*='_widthHandle']):after {
|
|
6536
|
+
background: linear-gradient(
|
|
6537
|
+
to bottom,
|
|
6538
|
+
transparent calc(var(--dsh-width-handle-pointer-y, 50%) - 36px),
|
|
6539
|
+
var(--dsw-alias-label-primary) calc(var(--dsh-width-handle-pointer-y, 50%) - 8px),
|
|
6540
|
+
var(--dsw-alias-label-primary) calc(var(--dsh-width-handle-pointer-y, 50%) + 8px),
|
|
6541
|
+
transparent calc(var(--dsh-width-handle-pointer-y, 50%) + 36px)
|
|
6542
|
+
);
|
|
6543
|
+
}
|
|
6496
6544
|
/* ---------- Light mode: deepen tertiary/secondary labels for icon visibility ---------- */
|
|
6497
6545
|
body:not([data-ds-dark-theme]) {
|
|
6498
6546
|
--dsw-alias-label-tertiary: #6a6d68;
|
|
@@ -6500,6 +6548,48 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6500
6548
|
--dsw-alias-label-dimmed: #9a9d98;
|
|
6501
6549
|
--edge-btn-muted: #dcddd6;
|
|
6502
6550
|
}
|
|
6551
|
+
/* ---------- light mode: keep the accent as a SIGNAL, not as text ----------
|
|
6552
|
+
Issue #44. The composer's quoted selection takes its colour from
|
|
6553
|
+
--dsw-alias-state-business-primary, i.e. the app's brand blue. The light
|
|
6554
|
+
palette remaps that token to ink (#101110) so buttons, status dots and the
|
|
6555
|
+
rest of the blue chrome read as a darkroom instrument — and the quote goes
|
|
6556
|
+
with it, landing 0.7 L* away from body text at 16.84:1. Nothing is
|
|
6557
|
+
unreadable; the QUOTING ACTION stops being visible, which is the point of
|
|
6558
|
+
the colour.
|
|
6559
|
+
|
|
6560
|
+
WHY NOT FIX THE TOKEN. Button fills, status dots and the focus ring's
|
|
6561
|
+
ancestry all read it; a per-consumer repaint is the only change that does
|
|
6562
|
+
not drag one of them back to blue. So the token stays ink and the quote
|
|
6563
|
+
gets its own two-tone: ink on a 55% accent wash (16.4:1 on 谷地黄, 9.9:1
|
|
6564
|
+
on 武陵青). The signal is carried by HUE — yellow or cyan behind black
|
|
6565
|
+
type — which is exactly the axis the palette owns, and it needs no third
|
|
6566
|
+
colour that would have to be re-measured per palette.
|
|
6567
|
+
|
|
6568
|
+
NO PADDING, deliberately: the excerpt is a single-line ellipsis box inside
|
|
6569
|
+
the composer row, and inline padding here would widen its measured box.
|
|
6570
|
+
This sheet has never reflowed the app, and it does not start for a chip.
|
|
6571
|
+
(radius only rounds the wash's ends.)
|
|
6572
|
+
|
|
6573
|
+
Dark needs no rule: there the token already resolves to the accent
|
|
6574
|
+
(13.97:1), so the quote is signal-yellow text today and stays that way.
|
|
6575
|
+
The anchor is the app's declared attribute on the excerpt itself,
|
|
6576
|
+
data-quote-preview — matching the excerpt rather than a parent also keeps
|
|
6577
|
+
this rule from depending on how deeply the row nests it. */
|
|
6578
|
+
body:not([data-ds-dark-theme]) [data-quote-preview] {
|
|
6579
|
+
color: var(--dsw-alias-label-primary);
|
|
6580
|
+
background: rgba(var(--edge-accent-rgb), 0.55);
|
|
6581
|
+
border-radius: 3px;
|
|
6582
|
+
}
|
|
6583
|
+
/* The same token, the same defect, one row over: the ref chip that carries a
|
|
6584
|
+
file / skill / command name is also business-primary, so on cream it turns
|
|
6585
|
+
into body text and stops reading as a reference. --edge-status-light is the
|
|
6586
|
+
palette's own deep step for accent-as-text on paper (#6b5d00 谷地黄 5.35:1,
|
|
6587
|
+
#006a6a 武陵青 5.22:1) — already measured, already the value the turn-status
|
|
6588
|
+
gradient uses, and it switches with the palette for free. Dark is already
|
|
6589
|
+
the accent (the same 13.97:1 the quote gets), so it is out of scope. */
|
|
6590
|
+
body:not([data-ds-dark-theme]) [data-ref-chip] {
|
|
6591
|
+
color: var(--edge-status-light);
|
|
6592
|
+
}
|
|
6503
6593
|
/* ---------- Neutralize remaining DeepSeek brand blues ---------- */
|
|
6504
6594
|
body {
|
|
6505
6595
|
--dsw-static-deepseek-50: #dcddd6;
|
|
@@ -6591,11 +6681,43 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6591
6681
|
--dsw-specific-sidebar-nav-item-active: rgba(var(--edge-accent-rgb), 0.20);
|
|
6592
6682
|
--dsw-specific-sidebar-nav-item-hover: rgba(var(--edge-accent-rgb), 0.16);
|
|
6593
6683
|
}
|
|
6684
|
+
/* ---------- the two places the accent may NOT be ink (#42, #43) ----------
|
|
6685
|
+
Both are painted by elements the theme does not own, and both used to read
|
|
6686
|
+
--edge-accent directly. That works on the near-black page and fails on
|
|
6687
|
+
cream: #fff500 is 13.97:1 on #202220 and 1.02:1 on the #f2f2ec input card.
|
|
6688
|
+
Pinning the two ANSWERS here, per scheme, keeps the accent token itself
|
|
6689
|
+
untouched (it is still the fill/icon colour everywhere else).
|
|
6690
|
+
--edge-caret ink in light, accent in dark
|
|
6691
|
+
--edge-focus-ring the APP's own deepseek-500/400 pair, as literal hex:
|
|
6692
|
+
a focus ring is a system-level accessibility signal
|
|
6693
|
+
and already clears its bar on both papers (5.0:1 on
|
|
6694
|
+
#e8e8e2, 9.2:1 on #101110). Literal, because the
|
|
6695
|
+
theme remaps --dsw-static-deepseek-500/600 to ink and
|
|
6696
|
+
accent, so reading that token would loop straight
|
|
6697
|
+
back to the accent this rule is escaping. */
|
|
6698
|
+
body {
|
|
6699
|
+
--edge-caret: var(--dsw-alias-label-primary);
|
|
6700
|
+
--edge-focus-ring: #4176e6;
|
|
6701
|
+
}
|
|
6702
|
+
body[data-ds-dark-theme] {
|
|
6703
|
+
--edge-caret: var(--edge-accent);
|
|
6704
|
+
--edge-focus-ring: #7aaaff;
|
|
6705
|
+
}
|
|
6706
|
+
/* Issue #42: the caret was the accent in both schemes. A 1px caret paints
|
|
6707
|
+
fine and is simply invisible at 1.02:1 (谷地黄) / 1.70:1 (武陵青) — the
|
|
6708
|
+
report's word was 「光标不可见」, not 「颜色不好看」. Ink on the card is
|
|
6709
|
+
16.4:1; the dark scheme keeps the accent, which is the one scheme where
|
|
6710
|
+
accent-as-ink already measured 13.97:1. */
|
|
6594
6711
|
input, textarea, [contenteditable='true'] {
|
|
6595
|
-
caret-color: var(--edge-accent);
|
|
6712
|
+
caret-color: var(--edge-caret, var(--edge-accent));
|
|
6596
6713
|
}
|
|
6714
|
+
/* Issue #43 annex: same defect on the keyboard focus ring — 2px of #fff500
|
|
6715
|
+
hugging a #f2f2ec button is 1.02:1, and the !important also suppressed the
|
|
6716
|
+
app's own blue ring, so Tab navigation had no visible focus at all. The
|
|
6717
|
+
geometry (2px, offset 1px) is the theme's and stays; only the colour goes
|
|
6718
|
+
back to the app's, which is what "no theme installed" looks like. */
|
|
6597
6719
|
:focus-visible {
|
|
6598
|
-
outline: 2px solid var(--edge-accent) !important;
|
|
6720
|
+
outline: 2px solid var(--edge-focus-ring, var(--edge-accent)) !important;
|
|
6599
6721
|
outline-offset: 1px;
|
|
6600
6722
|
}
|
|
6601
6723
|
a {
|
|
@@ -6645,6 +6767,7 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6645
6767
|
[class$='_sidebarCol'] [class*='_projectRow']:hover,
|
|
6646
6768
|
[class$='_sidebarCol'] [class*='_sessionRow']:hover,
|
|
6647
6769
|
[class$='_sidebarCol'] [class*='_sessionRow'][class*='_selected'],
|
|
6770
|
+
[class$='_sidebarCol'] [class*='_sessionRow'][class*='_menuOpen'],
|
|
6648
6771
|
[class$='_sidebarCol'] [class*='_searchResultRow']:hover,
|
|
6649
6772
|
[class$='_sidebarCol'] [class*='_searchResultRow'][class*='_selected'] {
|
|
6650
6773
|
background: rgba(var(--edge-accent-rgb), 0.22) !important;
|
|
@@ -6669,10 +6792,50 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6669
6792
|
body[data-ds-dark-theme] [class$='_sidebarCol'] [class*='_projectRow']:hover,
|
|
6670
6793
|
body[data-ds-dark-theme] [class$='_sidebarCol'] [class*='_sessionRow']:hover,
|
|
6671
6794
|
body[data-ds-dark-theme] [class$='_sidebarCol'] [class*='_sessionRow'][class*='_selected'],
|
|
6795
|
+
body[data-ds-dark-theme] [class$='_sidebarCol'] [class*='_sessionRow'][class*='_menuOpen'],
|
|
6672
6796
|
body[data-ds-dark-theme] [class$='_sidebarCol'] [class*='_searchResultRow']:hover,
|
|
6673
6797
|
body[data-ds-dark-theme] [class$='_sidebarCol'] [class*='_searchResultRow'][class*='_selected'] {
|
|
6674
6798
|
background: var(--edge-accent) !important;
|
|
6675
6799
|
}
|
|
6800
|
+
/* ---------- 会话行高亮:沿用 app 原生描边结构,只换颜色 ----------
|
|
6801
|
+
Reversed from the shipped bundle (dsh-client-ui-workspace
|
|
6802
|
+
Rows.module.css, read back from the installed 0.2.2-rc.1 app.asar):
|
|
6803
|
+
the app draws the hover/selected wash with NO geometry of its own —
|
|
6804
|
+
every row carries
|
|
6805
|
+
border-inline: var(--dsh-sidebar-paint-inset) solid transparent;
|
|
6806
|
+
background-clip: padding-box;
|
|
6807
|
+
(3px) and the selected state is literally one declaration,
|
|
6808
|
+
.wnFoaW_sessionRow.wnFoaW_selected { background-color: ... }.
|
|
6809
|
+
The 3px transparent frame is the app's own design: with its faint
|
|
6810
|
+
native wash the inset reads as intentional, but the theme's strong
|
|
6811
|
+
accent lets the unpainted frame show the column background through on
|
|
6812
|
+
both sides (「两边颜色不是填满的」). So the fix stays INSIDE the app's
|
|
6813
|
+
structure and only changes colors: the frame is painted with the same
|
|
6814
|
+
wash as the background (border-inline-color), which fills the row's
|
|
6815
|
+
border box edge to edge. Nothing moves — rail padding, row padding,
|
|
6816
|
+
the scrollbar gutter, the title's 36px line all stay exactly where the
|
|
6817
|
+
app put them — and because the whole wash now lives inside the row's
|
|
6818
|
+
border box, it can never cross the scroll container's clip region:
|
|
6819
|
+
圆角模式 keeps the row's native var(--dsw-radius-md) rounding on both
|
|
6820
|
+
ends, and the square-mode blanket pass squares it, both without ever
|
|
6821
|
+
being cut (the previous full-bleed attempt that re-architected the
|
|
6822
|
+
list's margins/clip was reverted for exactly that fragility).
|
|
6823
|
+
Same three row suffixes the theme already paints; the app's menuOpen
|
|
6824
|
+
state is included so its longhand wash fills the frame too. */
|
|
6825
|
+
[class$='_sidebarCol'] :is([class*='_sessionRow'], [class*='_projectRow'], [class*='_searchResultRow']):is(
|
|
6826
|
+
:hover,
|
|
6827
|
+
[class*='_selected'],
|
|
6828
|
+
[class*='_menuOpen']
|
|
6829
|
+
) {
|
|
6830
|
+
border-inline-color: rgba(var(--edge-accent-rgb), 0.22) !important;
|
|
6831
|
+
}
|
|
6832
|
+
body[data-ds-dark-theme] [class$='_sidebarCol'] :is([class*='_sessionRow'], [class*='_projectRow'], [class*='_searchResultRow']):is(
|
|
6833
|
+
:hover,
|
|
6834
|
+
[class*='_selected'],
|
|
6835
|
+
[class*='_menuOpen']
|
|
6836
|
+
) {
|
|
6837
|
+
border-inline-color: var(--edge-accent) !important;
|
|
6838
|
+
}
|
|
6676
6839
|
/* WHY '_badge' not bare 'badge': app CSS-module badges are '<hash>_badge'
|
|
6677
6840
|
(zf92ZW_badge footer, dlU_AG_badge …), but third-party plugins name
|
|
6678
6841
|
their own widgets without the underscore word boundary —
|
|
@@ -6769,9 +6932,40 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6769
6932
|
(test/approval-band-transform.test.js covers both directions).
|
|
6770
6933
|
Both anchors are listed because the [class$=] hook dies the moment
|
|
6771
6934
|
upstream appends a second class to the seat, and [data-composer-seat]
|
|
6772
|
-
is the e2e hook the app itself declares.
|
|
6773
|
-
|
|
6935
|
+
is the e2e hook the app itself declares.
|
|
6936
|
+
|
|
6937
|
+
DEFENCE IN DEPTH (the 自动授权审查 field report). will-change on the
|
|
6938
|
+
seat is the only containing-block producer measured on the seat's
|
|
6939
|
+
ancestor chain in 0.2.2-rc.1 — but the band is position:fixed, so ANY
|
|
6940
|
+
producer on ANY ancestor between it and the viewport re-creates the
|
|
6941
|
+
whole collapse, and the apps that hit it in the field were one release
|
|
6942
|
+
behind (the report shipped before this rule existed). While a panel is
|
|
6943
|
+
mounted the app is modal-blocked anyway, so the theme releases every
|
|
6944
|
+
containing-block-producing property on the seat AND the structural
|
|
6945
|
+
layers the seat actually nests under, all verified in the extracted
|
|
6946
|
+
bundles and all semantic anchors: the conversation scrollport
|
|
6947
|
+
([data-conversation-scroll]), the conversation root
|
|
6948
|
+
([data-conversation-content]), the frame and center column (the same
|
|
6949
|
+
suffix hooks the contour sheet already relies on). transform:none is
|
|
6950
|
+
the only declaration that could plausibly change a visible pixel
|
|
6951
|
+
elsewhere, and everything it could un-transform is behind the band's
|
|
6952
|
+
own full-viewport mask for exactly as long as the panel exists. */
|
|
6953
|
+
body:has([data-approval-key]) :is(
|
|
6954
|
+
[data-composer-seat],
|
|
6955
|
+
[class$='_composerSeat'],
|
|
6956
|
+
[data-conversation-scroll],
|
|
6957
|
+
[data-conversation-content],
|
|
6958
|
+
[class$='_centerCol'],
|
|
6959
|
+
[class*='_frame']
|
|
6960
|
+
) {
|
|
6774
6961
|
will-change: auto !important;
|
|
6962
|
+
transform: none !important;
|
|
6963
|
+
filter: none !important;
|
|
6964
|
+
backdrop-filter: none !important;
|
|
6965
|
+
perspective: none !important;
|
|
6966
|
+
container-type: normal !important;
|
|
6967
|
+
content-visibility: visible !important;
|
|
6968
|
+
contain: none !important;
|
|
6775
6969
|
}
|
|
6776
6970
|
/* ---------- 审批弹窗:全屏白条 + 上下遮罩(Endfield 寻访确认框) ----------
|
|
6777
6971
|
The shipped panel is a rounded warn-bordered card parked inside the
|
|
@@ -6811,8 +7005,14 @@ function apply(ctx) { // Idempotency: the installed bundle can be applied mor
|
|
|
6811
7005
|
}
|
|
6812
7006
|
/* The band: square, full-bleed, paper. The zero-radius pass in round
|
|
6813
7007
|
mode would restore the app's --dsw-radius-xl here, and a rounded band
|
|
6814
|
-
contradicts the reference — pin it flat with priority.
|
|
6815
|
-
|
|
7008
|
+
contradicts the reference — pin it flat with priority. '_card' as a
|
|
7009
|
+
SUBSTRING, not a suffix: subject-bearing approvals (an agent-team
|
|
7010
|
+
escalation renders its icon + name) ship the card as
|
|
7011
|
+
'<hash>_card <hash>_withSubject', and a $= match dies the moment that
|
|
7012
|
+
second class is appended — the band would keep its mask but lose the
|
|
7013
|
+
paper. The root's ONLY child is the card, so the looser hook cannot
|
|
7014
|
+
reach anything else. */
|
|
7015
|
+
[data-approval-key] > [class*='_card'] {
|
|
6816
7016
|
width: 100%;
|
|
6817
7017
|
max-width: none !important;
|
|
6818
7018
|
box-sizing: border-box;
|
|
@@ -752,7 +752,7 @@ const visual = delta - layoutDelta
|
|
|
752
752
|
|
|
753
753
|
### 日历是数据,不是逻辑
|
|
754
754
|
|
|
755
|
-
`BALANCE_HOLIDAY_NOTICES` 按年抄录国务院的放假安排通知(2026 年 = **国办发明电〔2025〕7号**,https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm ),`from` / `to` 是**含端点的 MM-DD**,`makeup` 存调休日——它不只是给人看的,也是上面那条推理的证据。启动时逐日展开成 `BALANCE_HOLIDAY_DATES`(`'YYYY-MM-DD' → 节日名`),再由此得出 `balanceHolidayName` 与 `balanceDayIsOffPeak`。
|
|
755
|
+
`BALANCE_HOLIDAY_NOTICES` 按年抄录国务院的放假安排通知(2026 年 = **国办发明电〔2025〕7号**,https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm ;2027 年 = **国办发明电〔2026〕10号**,https://www.gov.cn/zhengce/content/202611/content_7085101.htm ),`from` / `to` 是**含端点的 MM-DD**,`makeup` 存调休日——它不只是给人看的,也是上面那条推理的证据。启动时逐日展开成 `BALANCE_HOLIDAY_DATES`(`'YYYY-MM-DD' → 节日名`),再由此得出 `balanceHolidayName` 与 `balanceDayIsOffPeak`。
|
|
756
756
|
|
|
757
757
|
**没有收录的年份回退到普通工作日规则**,也就是一个未录入的节假日会被算成高峰。这是刻意的:宁可把假期显示成高峰(用户读到的只是「现在贵」),也不猜一个假的半价读数。代价是这份数据会随时间失效,而且失效是**静默**的——所以 `check.js` 在「当前北京年份不在表里」时响亮失败,作为每年 11 月(通知发布月)的提醒。
|
|
758
758
|
|
|
@@ -1021,6 +1021,64 @@ body:has([data-approval-key]) :is([data-composer-seat], [class$='_composerSeat']
|
|
|
1021
1021
|
|
|
1022
1022
|
这同时解释了为什么**报告者给的补丁能修好、但会退化**:把 root 改成 `position: relative` + 透明背景,面板回到文档流,功能立刻恢复,但通栏纸带被收进聊天列宽、上下遮罩一并消失——那是「把设计也一起删了」,不是等价修复。修在祖先上,两样都留着。
|
|
1023
1023
|
|
|
1024
|
+
### 十类:兜底按「属性清单」打,不按已知元凶打(自动授权审查的现场报告)
|
|
1025
|
+
|
|
1026
|
+
#41 落地次日,评论区来了第二份白条塌陷报告:开着「自动授权审查」实验性插件,被自动审查拒绝的工具调用要等人工审批,弹窗装上主题后无法显示、无法点击,关主题即恢复。现场截图的形态与 #41 分毫不差——底部只活着一行「● 等待审批」。
|
|
1027
|
+
|
|
1028
|
+
对 0.2.2-rc.1 的安装包逐层核对(ui-approval / ui-conversation / ui-session 的 bundle)后确认:**自动审查走的就是同一条 `approval/request` 瀑布、同一个 composer 座位里的同一个面板**,座位祖先链上除 `will-change` 外也没有第二个元凶。真正能解释现场的是版本:报告者装主题的时间在 v1.2.4 发布之前,手里还是没有 #41 的旧构建。但这条线索指出了 #41 修法的结构性弱点——它只按「已知元凶」释放了 `will-change` 一个属性、座位一个元素,而白条塌陷的充分条件是**任意祖先的任意致产属性**。上游任何一次重构(把 transform 挪到包裹层、给滚动层加 container-type)都会让同一条塌陷回来。
|
|
1029
|
+
|
|
1030
|
+
所以兜底改成按属性清单打:面板在屏期间(`body:has([data-approval-key])`),对座位和它已核实的结构层——`[data-conversation-scroll]`(会话滚动体,JSX 实测属性)、`[data-conversation-content]`(会话根,同)、`_centerCol` / `_frame`(与等高线图纸共用的后缀钩子)——一并释放全部致产属性:`transform`、`filter`、`backdrop-filter`、`perspective`、`container-type`、`content-visibility`、`contain`。这些层在 0.2.2-rc.1 上都没有致产值,规则今天是空操作;代价只存在于「面板在屏」的帧里,而那时应用正被白条遮罩挡着。`test/approval-band-transform.test.js` 为此加了两条白条:座位干净、祖先分别带 `container-type: inline-size` 与 `transform`,配合仍会塌陷的对照组,证明释放真的生效而不是「全页没陷阱」的假阳性。附带修掉一个顺藤摸出来的真钩子:带主体(图标 + 名称)的审批卡类名是 `<hash>_card <hash>_withSubject`,`[class$='_card']` 在第二个类 appended 后静默失配——卡片钩子放宽为 `[class*='_card']`(root 的唯一子元素就是卡,不可能误中)。
|
|
1031
|
+
|
|
1032
|
+
**教训**:防御性规则要么按故障的充分条件枚举(所有致产属性 × 全部结构层),要么干脆别写;只覆盖已知名元的兜底给后来者一种「已经防住了」的错觉,而塌陷的充分条件集合在上游任何一次重构后都会变大。
|
|
1033
|
+
|
|
1034
|
+
### 十一类:高亮不满宽是三层内缩的算术(工作区会话行)
|
|
1035
|
+
|
|
1036
|
+
评论区另一份独立报告:左侧工作区的会话行,选中 / 悬停的强调色洗不满行盒,「两边颜色不是填满的」。对照 0.2.2-rc.1 的 bundle 把行盒到侧栏两缘之间的层全数核出,一共三层:
|
|
1037
|
+
|
|
1038
|
+
1. **行自带 3px 内缩**——`.*_projectRow/. *_sessionRow` 携带 `border-inline: var(--dsh-sidebar-paint-inset) solid transparent`(3px)+ `background-clip: padding-box`,洗色永远画不到描边下面。主题自己的 `background:` 简写会把 clip 重置回 border-box,所以**选中和悬停其实早就贴到了行盒边缘**;没贴边的观感来自下面两层(以及 app 用 longhand 画的 menuOpen 态)。
|
|
1039
|
+
2. **侧栏栏根 8px 内边距**——SidebarRoot 的根节点 `padding: 6px var(--dsh-sidebar-inline-padding)`,行盒从 8px 处才开始。
|
|
1040
|
+
3. **列表右侧滚动条槽**——`.*_list` 的 `margin-right: var(--dsh-session-list-scrollbar-offset)` + `padding-right: calc(var(--dsh-session-list-edge-inset) - var(--dsh-session-list-scrollbar-width) - var(--dsh-session-list-scrollbar-offset))`,行盒右缘距列缘还差 `inline-padding + edge-inset − scrollbar-width`。
|
|
1041
|
+
|
|
1042
|
+
前两版修法都在重排几何、都被打回。第一版把负外边距打在**行**上——几何测试全绿(行盒 rect [0, 232]),现场却是「圆角模式下高亮还是直角」:`getBoundingClientRect` 量不出**裁剪**,会话列表是滚动容器(`overflow-y: auto` 使 `overflow-x` 计算为 auto),在**自己的 padding box** 处裁剪绘制与命中测试,圆角模式(app 原生 12px 圆角)的两端圆弧被齐齐切平,还顺带获得一段横向可拖拽区。第二版把外扩挪到列表上(负 margin + padding 归零 + 全套内层补偿)圆角是保住了,但整座补偿算术(骨架行、空态、显示更多按钮、rail 变体的溢出裁剪、隐藏滚动条)都悬在「裁剪边界恰好等于列缘」这一根独苗上。探针还接连否决了两条「聪明」路:`overflow-clip-margin` 只在元素该轴本身是 `clip` 时才被 Chromium 认(与滚动容器轴混用一律忽略,实测),滚动上移到 listArea 则把坑原样搬到上一层。
|
|
1043
|
+
|
|
1044
|
+
**终版(作者裁定:「直接逆向一下,看下原本是怎么做的,在原本基础上换颜色算了」)回到原生的机制。** 逆向结论:原生高亮**零几何**——每行自带 3px 透明描边框 + `background-clip: padding-box`,选中态就一句 `background-color` 换色;原生淡色下 3px 内缩读作有意的胶囊设计,主题的强高亮色一刷,未上色的描边框把栏底透出来,这才是「两边不满」的显形来源。于是修法留在原生结构内、只动颜色:`border-inline-color` 刷成同一个高亮色,高亮即贴满行盒——栏内边距、滚动条槽、标题线(8+3+9+16=36px)、`--dsh-workspace-indent` 算术全部原样,圆角随原生 border-box 走(圆角模式 12px、方角模式被 blanket pass 归零),绘制全部发生在行盒内、滚动容器裁剪区内,**裁剪问题被结构性地排除而不是被对抗**。app 的 menuOpen 态(longhand 换色)一并纳入主题换色,避免「描边主题色 + 内部原生灰」的割裂。`test/sidebar-row-wash.test.js` 的断言策略是**双 boot 对照**:主题关闭先量原生基线,圆角 / 方角两个主题态再量一次,几何(行左缘 / 行高 / 标题线 / 列表盒与全部 CSS 声明)逐值一致——「换颜色不动几何」的可执行定义;颜色断言描边色 === 背景色,圆角断言 12px 保留 / 归零。
|
|
1045
|
+
|
|
1046
|
+
顺带钉死一条 Blink 怪癖(对照测试差点被它假失配搞挂):**`::-webkit-scrollbar` 的级联不按特异性、不按 `!important`、也不可被后插入的样式表翻转**——页面 `#list::-webkit-scrollbar{width:5px !important}` 输给后注入的主题全局 `::-webkit-scrollbar{width:10px}`,boot 之后再 append 一条 5px 全局规则同样翻不掉(实测 gap 恒 10px)。主题的全局滚动条规则一旦注入就统治文档的全部滚动容器占位,这也解释了真实 app 中列表占位是 10px(app 自己的 `--dsh-session-list-scrollbar-width` 计划值是 5px)——它是主题 v1.2.4 起的既成行为。对照测试因此不跨 boot 比行宽(占位差会假失配),只比滚动条碰不到的左半边,行宽改用列表盒内部一致性断言(行右缘 = 列表 client 右缘 − 右内边距)。
|
|
1047
|
+
|
|
1048
|
+
### 十二类:令牌的消费者不全在主题手里(#42 / #43 / #44)
|
|
1049
|
+
|
|
1050
|
+
同一夜、同一台机器、同一个环境(v1.2.5 + 亮色 + 谷地黄)来的三份报告,读起来是三个 bug:输入框光标看不见、拖动会话宽度没有指示线、引用块和正文同色。实际是一条:**主题重映射了应用令牌,而令牌的消费者里有主题从未接管的元素**。这类缺陷有个共同伪装——**它不表现为「字看不清」,而表现为「东西画了但看不见」**,所以连报告者都要先量完才敢确认不是自己眼花了。
|
|
1051
|
+
|
|
1052
|
+
| 面 | 机制 | 修复前(亮色) | 修复后 |
|
|
1053
|
+
| --- | --- | --- | --- |
|
|
1054
|
+
| 输入光标 | `caret-color: var(--edge-accent)` | `#fff500` 压 `#f2f2ec` **1.02:1** | 墨 `16.84:1`(暗色仍用强调色 13.97:1) |
|
|
1055
|
+
| 宽度指示线 | 手柄渐变的两个实色停靠点取自 `--dsw-alias-scrollbar-bg-l1`,而主题把它设成 `transparent` 自绘滚动条 | 线照常画、`opacity:1`,**整条透明** | 墨 `15.35:1`(暗色纸白 18.11:1),2px 几何未动 |
|
|
1056
|
+
| 键盘焦点环 | `outline: 2px solid var(--edge-accent) !important` | 1.02:1,且把应用原版蓝环一起盖掉 | 应用自己的 `#4176e6` `4.96:1`(暗色 `#7aaaff` 9.02:1) |
|
|
1057
|
+
| 引用块 | 引用取 `--dsw-alias-state-business-primary`,亮色被重映射成墨 | 16.84:1 —— 读得清,**和正文同色**,引用这个动作失去信号 | 墨字压 55% 强调洗底:谷地黄 16.38:1 / 武陵青 12.07:1 |
|
|
1058
|
+
| ref chip | 同一枚令牌,同一次撞色 | 同正文墨 | `#6b5d00` 5.86:1 / `#006a6a` 5.72:1(暗色仍是强调色) |
|
|
1059
|
+
|
|
1060
|
+
**四处都不能在令牌层修**,这是这道题的关键:
|
|
1061
|
+
|
|
1062
|
+
- 滚动条令牌 `l1` 3 个消费者、`l2` 45 个,除这一条手柄渐变外全是滚动条滑块。把颜色还给令牌,等于用「自己刚修好的滚动条」换「一条指示线」。
|
|
1063
|
+
- `--dsw-alias-state-business-primary` 是主按钮填充、回合状态点、焦点环前身共用的入口,改它连带一片。
|
|
1064
|
+
- `--edge-accent` 更不能改:它就是主题本身。
|
|
1065
|
+
|
|
1066
|
+
所以全部改成**逐个消费者重绘**,并且遵守这个仓库的老规矩——**只动颜色,不动几何**:手柄不加宽(应用 2px 就 2px)、引用块不加内边距(摘要是单行省略框,inline padding 会改它的测量宽度)。为此新增两枚只读令牌,把「答案」而不是「规则」放进 scheme 作用域:
|
|
1067
|
+
|
|
1068
|
+
```css
|
|
1069
|
+
body { --edge-caret: var(--dsw-alias-label-primary); --edge-focus-ring: #4176e6 }
|
|
1070
|
+
body[data-ds-dark-theme] { --edge-caret: var(--edge-accent); --edge-focus-ring: #7aaaff }
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
焦点环用**字面 hex** 而不是 `--dsw-static-deepseek-500`,因为主题把这枚令牌重映射成了墨/强调色——顺着令牌读会绕回正在逃离的那个颜色。焦点环也不做「更深的金」:它是系统级可访问性信号,最稳的答案是还给应用。
|
|
1074
|
+
|
|
1075
|
+
锚点一律取应用自己声明的属性:`[data-width-handle]`(手柄的 e2e 钩子)、`[data-composer-quote]` / `[data-quote-preview]`、`[data-ref-chip]`;类名一侧用**子串**(`[class*='_widthHandle']`),因为后缀匹配在上游追加第二个类那天会静默失效——0.1.2-rc.1 的教训。
|
|
1076
|
+
|
|
1077
|
+
`test/light-token-consumers.test.js` 走四个状态(亮·谷地黄 / 亮·武陵青 / 暗·谷地黄 / 主题关闭),把应用侧规则原样搬进夹具,并把焦点环在夹具里设成**品红虚线**——应用的真是蓝色,而修复后的主题也是蓝色,蓝对蓝的断言在规则完全失效时照样通过。每个颜色断言都配一条页面内算出的 WCAG 比值。反向验证也做了:把 `client.js` 换回 HEAD 的构建,测试立刻在 `--edge-caret` 处报红(读回空串),不是自证。
|
|
1078
|
+
|
|
1079
|
+
**教训**:重映射一个令牌之前,先数一遍它的消费者。只要消费者里有一个不是「主题要重绘的那类元素」,这个令牌就已经不是主题的私有财产了——而这类失配不会打字面报错,只会让某个像素安静地消失。
|
|
1080
|
+
|
|
1081
|
+
|
|
1024
1082
|
---
|
|
1025
1083
|
|
|
1026
1084
|
## 验证方法论
|
package/docs/features.md
CHANGED
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
|
|
234
234
|
开启后屏幕顶部悬浮一枚药丸:左边是 DeepSeek API 余额(¥ / $),右边是当前**峰谷时段**——`高峰` / `低谷`、时段剩余与已过百分比、一个圆环时钟,以及开场那帧「品牌 pose」(`/// DEEPSEEK API` + `DeepSeek 当前低谷`)。设置行提供「预览」按钮,按真实开场动画重播一次;没有配置 API Key 时按钮禁用并说明原因。
|
|
235
235
|
|
|
236
|
-
**峰谷规则在本地推算**(Host 不提供排班表):北京时间**周一至周五 9:00-12:00、14:00-18:00 是高峰**,价格为空闲时段的 2 倍。按 DeepSeek 2026-09-19 的「API
|
|
236
|
+
**峰谷规则在本地推算**(Host 不提供排班表):北京时间**周一至周五 9:00-12:00、14:00-18:00 是高峰**,价格为空闲时段的 2 倍。按 DeepSeek 2026-09-19 的「API 峰谷时间补充说明」,**中国法定节假日全天**以及**调休上班的周末**都按空闲计费——后一条天然落在「周末即低谷」上,所以胶囊只要保管一份法定节假日日历(按年抄录国务院通知,2026 / 2027 已收录;未收录的年份退回普通工作日规则,宁可显示高峰也不猜一个假的半价)。
|
|
237
237
|
|
|
238
238
|
倒计时读的是**同价连续段**,而不是「今天还剩多少」:周五 18:00 到周一 09:00 是一整段 63 小时的低谷,七天国庆是一整段 183 小时,所以读数可能是 `剩余47:20:00`。节假日名只出现在开场 pose 的标题里(`DeepSeek 国庆节低谷`),正式行仍只写两三个字——胶囊宽 300px,多一个字就会撑破它。
|
|
239
239
|
|
|
@@ -286,10 +286,13 @@
|
|
|
286
286
|
| 会话头部预设徽章(agent preset,如「创造模式」) | 上游把徽章移进 `_headerActions` 插槽的包裹层里,主题的 `>` 选择器失配,退回默认灰胶囊 | 恢复强调色填充 + 黑字,**尺寸沿用上游**(不撑宽) |
|
|
287
287
|
| 审批弹窗(「等待审批」) | 上游是塞在输入区里的一张圆角描边小卡 | 重绘为**贯穿屏幕左右白条 + 上下遮罩**的居中确认框,按钮改为胶囊(拒绝=深色+✕,允许一次=信号黄+◎),见下 |
|
|
288
288
|
| 完全权限确认框(「确认启用完全权限?」) | 共享 Modal 的居中圆角小卡 | 同款白条 + 上下遮罩,取消/启用胶囊按钮,见下 |
|
|
289
|
+
| 工作区会话行高亮(hover / 选中) | 原生高亮零几何:每行自带 3px 透明描边框(`border-inline` + `background-clip: padding-box`),选中态只换一句 `background-color`;原生淡色下内缩读作有意设计,主题的强高亮色一刷,未上色的描边框把栏底透出来,两侧显缝(「两边颜色不是填满的」) | 在原生结构内只换颜色:`border-inline-color` 刷成同一个高亮色,高亮贴满行盒;栏内边距、滚动条槽、文字线全部保持 app 原生不动,圆角模式保留原生 12px 圆角、方角模式直角;绘制全部在行盒内、滚动容器裁剪区内,圆角不再被切;menuOpen 态一并换色,见下 |
|
|
290
|
+
| 审批白条 containing block 兜底 | #41 只释放了输入区座位的 `will-change`;实测 0.2.2-rc.1 里座位祖先链没有别的元凶,但「自动授权审查」的用户报告证明这类塌陷会真实发生 | 面板挂载期间对座位**及其已核实的结构层**(`[data-conversation-scroll]`、`[data-conversation-content]`、`_centerCol`、`_frame`)一并释放全部产生 containing block 的属性(transform / filter / backdrop-filter / perspective / container-type / content-visibility / contain);附带修复:带主体(图标+名称)的审批卡类名是 `<hash>_card <hash>_withSubject`,卡片钩子从 `$=` 放宽为 `*=` 才能命中 |
|
|
289
291
|
|
|
290
292
|
### 审批弹窗:全屏白条 + 上下遮罩
|
|
291
293
|
|
|
292
294
|
工具越权执行时弹出的 `[data-approval-key]` 面板被重绘成参考图那种形态:**不透明纸色横条从屏幕左缘贯到右缘、垂直居中,页面其余部分只有上下被压暗**——实现上没有加任何 DOM:面板自己的 root 变成 fixed 全屏 flex 层,它的半透明黑底就是遮罩,中间的白条不透明地盖住横带,遮罩自然只剩上下两截。文字居中,「等待审批」保留状态点但去掉警示底色。两个按钮改成终末地胶囊(左取消深灰配白 ✕、右确认信号黄配 ◎,墨字对比度与主题其它实心芯片同一条 AA 约束)。**圆角模式同样保持胶囊**(与余额药丸一致),且所有选择器都钉在语义锚点上(`data-approval-key` + `[class*='_card']` 等 CSS-module 后缀),上游重哈希不会失效——`test/selector-guard.test.js` 钉住这三个钩子。
|
|
295
|
+
**挂载期间的 containing block 兜底(#41 的延伸)**:白条是 `position: fixed`,座位到视口之间的**任何**产生 containing block 的祖先都会把白条重新压扁成一条线(#41 的实测就是输入区座位的 `will-change: transform`)。面板在屏时应用本来就处于模态阻塞,主题因此同时释放座位与已核实结构层上的全部致产属性;`test/approval-band-transform.test.js` 用「座位自身干净、祖先带 container-type / transform」的两个白条夹住这条规则——对照组塌陷、这两条满屏,才证明释放真的生效。
|
|
293
296
|
|
|
294
297
|
### 完全权限确认框:同一形态
|
|
295
298
|
|
package/docs/testing.md
CHANGED
|
@@ -203,12 +203,12 @@ node test/balance-window.test.js # 峰谷窗口算术(纯函数,从 clie
|
|
|
203
203
|
node test/balance-capsule.test.js # 胶囊标记 / CSS / 绘制 / 设置行(静态切片)
|
|
204
204
|
```
|
|
205
205
|
|
|
206
|
-
**`balance-window.test.js`** 把 `const BALANCE_HOLIDAY_NOTICES` 到 `const balancePaintWindow` 之间的源码切出来,在 `vm` 里用 `{ Math, Date }` 求值——切片边界本身就是断言,结构性改动会把切片挪走并响亮失败(旧版起点是 `const BALANCE_PEAK_WINDOWS`,节假日数据加在它之前后必须跟着改)。所有时刻都用 `bj(y, mo, d, h, mi, s) = new Date(Date.UTC(y, mo-1, d, h-8, mi, s))` 造成固定瞬时,因此结果与跑测试的机器时区无关。
|
|
206
|
+
**`balance-window.test.js`** 把 `const BALANCE_HOLIDAY_NOTICES` 到 `const balancePaintWindow` 之间的源码切出来,在 `vm` 里用 `{ Math, Date }` 求值——切片边界本身就是断言,结构性改动会把切片挪走并响亮失败(旧版起点是 `const BALANCE_PEAK_WINDOWS`,节假日数据加在它之前后必须跟着改)。所有时刻都用 `bj(y, mo, d, h, mi, s) = new Date(Date.UTC(y, mo-1, d, h-8, mi, s))` 造成固定瞬时,因此结果与跑测试的机器时区无关。105 项断言分四组:
|
|
207
207
|
|
|
208
208
|
- **工作日**:周四 08:59 / 09:00 / 11:59 / 12:00 / 13:59 / 14:00 / 17:59 / 18:00 的峰谷;12-14 谷段 `totalMs = 2h`;20:00 的谷段一路读到次日 09:00(15h);23:30 倒计时 9h30m。
|
|
209
209
|
- **周末**:周六 00:00 / 12:00 都是低谷,且窗口是 **Fri 18:00 → Mon 09:00 的 63h**(不是按天的 24h);周日 23:00 剩 10h;周一 07:00 剩 2h;周六 09:40 剩 47h20m 且 `elapsedPct = 25`。
|
|
210
|
-
- **法定节假日**:元旦(周四)10:00 是低谷、`holiday = '元旦'`,窗口 12-31 18:00 → 01-05 09:00(111h);春节窗口 255h,02-14 / 02-28 调休仍是低谷、02-24 回到高峰;清明 / 劳动 / 端午 / 中秋各自成对断言「节日当天低谷 + 次日高峰」;国庆(周一)10:00 低谷、窗口 183h、10-07 23:59 剩 9h01m、10-08 00:00 仍是同一段谷、10-08 09:00
|
|
211
|
-
- **表完整性**:展开恰为 33
|
|
210
|
+
- **法定节假日**:元旦(周四)10:00 是低谷、`holiday = '元旦'`,窗口 12-31 18:00 → 01-05 09:00(111h);春节窗口 255h,02-14 / 02-28 调休仍是低谷、02-24 回到高峰;清明 / 劳动 / 端午 / 中秋各自成对断言「节日当天低谷 + 次日高峰」;国庆(周一)10:00 低谷、窗口 183h、10-07 23:59 剩 9h01m、10-08 00:00 仍是同一段谷、10-08 09:00 高峰;2027 年按国办发明电〔2026〕10号 另成一组:元旦 87h、春节 255h(01-31 / 02-14 两个调休周日仍是低谷)、清明 87h、劳动 135h、端午 / 中秋都是单个周三 → 39h 谷、国庆 183h;**2028-01-03 无表可查 → 回退按工作日高峰**,把「未收录年份的代价」钉住。
|
|
211
|
+
- **表完整性**:展开恰为 62 天(2026 的 33 = 3+9+3+5+3+3+7,2027 的 29 = 3+9+3+5+1+1+7)、每条 span 每一天都映射到自己的名字;末尾三个断言直接调用切出来的**真** `balanceFormatCountdown`(旧版是在测试里重写一遍格式化,等于什么都没验)。
|
|
212
212
|
|
|
213
213
|
**`balance-capsule.test.js`** 从安装态标记里切片:胶囊 DOM 顺序、`slotsWritten` 只允许写标记内已存在的属性(例外集 `STATE_ATTRS`)、样式表选择器不得成孤儿、圆环 `conic-gradient` / `mask` / `elapsedPct * 3.6`、药丸 32px / 999px / `min-width: 300px`、**480px → 316px 两级窄屏断点**、开场 pose 448×72、品牌 lockup。本次新增的一条断言把节假日的落点钉死:**`win.holiday` 只被一行读取,且那一行在 brand-title 块内**——胶囊宽 300px、316px 断点由它推出,节假日名一旦爬进「时段剩余」行就会撑宽。窄屏两级也各自钉死:480px 只隐藏 `data-endfield-balance-remain-run`(「时段剩余hh:mm:ss」,手机宽度)、且**不得**触碰 `phase`(高峰/低谷 标签要留下)、必须排在 316px 之前(阶梯,而不是互相遮蔽);同格里还断言 `min-width: 0` 与隐藏规则**写在同一个媒体块里**——地板不让,读数减少后 `margin-left: auto` 会把富余像素全吞成空白,胶囊根本不会变短。
|
|
214
214
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-theme-endfield",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "DeepSeek Harness web theme: the industrial editorial look of the Arknights: Endfield official site — cream paper, ink text, signal-yellow accents, zero radius. Settings persist through the DSH volatile Config + configForms transport (0.2.0-rc.2 and 0.1.7+), with a ctx.settingsScope fallback for older hosts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"test:coverage": "node test/contour-coverage.test.js",
|
|
48
48
|
"test:perf": "node test/contour-perf.test.js",
|
|
49
49
|
"test:loader": "node test/loader-performance.test.js && node test/loader-late-prefs.test.js",
|
|
50
|
-
"test": "node check.js && node selftest.js && node test/import-probe.test.js && node test/font-scope.test.js && node test/palette-contrast.test.js && node test/palette-switch.test.js && node test/menu-surface.test.js dark && node test/menu-surface.test.js light && node test/settings-buttons.test.js && node test/tools/hover-check.js && node test/selector-guard.test.js && node test/preset-chip.test.js && node test/balance-window.test.js && node test/balance-capsule.test.js && node test/credit-capsule.test.js && node test/settings-rows.test.js && node test/settings-durable-hold.test.js && node test/settings-config-forms.test.js && node test/settings-config-fallback.test.js && node test/settings-namespace.test.js && node test/settings-off.test.js && node test/settings-locale.test.js && node test/audio-notify.test.js && node test/audio-attention-watch.test.js && node test/host-no-inject.test.js && node scripts/build-sounds.js --check && node test/thunder-edges.test.js && node test/tools/thunder-shot.js && node test/thunder-dismiss.test.js && node test/contour-render.test.js && node test/loader-performance.test.js && node test/loader-late-prefs.test.js && node test/contour-specks.test.js && node test/watermark-stacking.test.js && node test/contour-smoothness.test.js && node test/contour-cusps.test.js && node test/contour-a11y.test.js && node test/contour-coverage.test.js && node test/contour-perf.test.js && node test/contour-density.test.js && node test/contour-bounds.test.js && node scripts/build-host.js --check && node scripts/build-client.js --check && node scripts/build-contour-worker.js --check && node test/worker-eol.test.js && node test/contour-worker.test.js && node test/contour-worker-budget.test.js && node test/theme-sheet-refresh.test.js && node test/titlebar-probe.test.js && node test/corner-shape.test.js && node test/approval-band-transform.test.js && node test/glass.test.js && node test/contour-trail.test.js && node test/contour-trail-browser.test.js && node test/scroll-anim.test.js && node test/contour-loop-reentry.test.js",
|
|
51
|
-
"test:ci": "node check.js && node selftest.js && node test/import-probe.test.js && node test/font-scope.test.js && node test/palette-contrast.test.js && node test/palette-switch.test.js && node test/menu-surface.test.js dark && node test/menu-surface.test.js light && node test/settings-buttons.test.js && node test/tools/hover-check.js && node test/selector-guard.test.js && node test/preset-chip.test.js && node test/balance-window.test.js && node test/balance-capsule.test.js && node test/credit-capsule.test.js && node test/settings-rows.test.js && node test/settings-durable-hold.test.js && node test/settings-config-forms.test.js && node test/settings-config-fallback.test.js && node test/settings-namespace.test.js && node test/settings-off.test.js && node test/settings-locale.test.js && node test/audio-notify.test.js && node test/audio-attention-watch.test.js && node test/host-no-inject.test.js && node scripts/build-sounds.js --check && node test/thunder-edges.test.js && node test/tools/thunder-shot.js && node test/thunder-dismiss.test.js && node test/contour-render.test.js && node test/loader-performance.test.js && node test/loader-late-prefs.test.js && node test/contour-specks.test.js && node test/watermark-stacking.test.js && node test/contour-smoothness.test.js && node test/contour-cusps.test.js && node test/contour-a11y.test.js && node test/contour-density.test.js && node test/contour-bounds.test.js && node scripts/build-host.js --check && node scripts/build-client.js --check && node scripts/build-contour-worker.js --check && node test/worker-eol.test.js && node test/contour-worker.test.js && node test/contour-worker-budget.test.js && node test/theme-sheet-refresh.test.js && node test/titlebar-probe.test.js && node test/corner-shape.test.js && node test/approval-band-transform.test.js && node test/glass.test.js && node test/contour-trail.test.js && node test/contour-trail-browser.test.js && node test/scroll-anim.test.js && node test/contour-loop-reentry.test.js",
|
|
50
|
+
"test": "node check.js && node selftest.js && node test/import-probe.test.js && node test/font-scope.test.js && node test/palette-contrast.test.js && node test/palette-switch.test.js && node test/menu-surface.test.js dark && node test/menu-surface.test.js light && node test/settings-buttons.test.js && node test/tools/hover-check.js && node test/selector-guard.test.js && node test/preset-chip.test.js && node test/balance-window.test.js && node test/balance-capsule.test.js && node test/credit-capsule.test.js && node test/settings-rows.test.js && node test/settings-durable-hold.test.js && node test/settings-config-forms.test.js && node test/settings-config-fallback.test.js && node test/settings-namespace.test.js && node test/settings-off.test.js && node test/settings-locale.test.js && node test/audio-notify.test.js && node test/audio-attention-watch.test.js && node test/host-no-inject.test.js && node scripts/build-sounds.js --check && node test/thunder-edges.test.js && node test/tools/thunder-shot.js && node test/thunder-dismiss.test.js && node test/contour-render.test.js && node test/loader-performance.test.js && node test/loader-late-prefs.test.js && node test/contour-specks.test.js && node test/watermark-stacking.test.js && node test/contour-smoothness.test.js && node test/contour-cusps.test.js && node test/contour-a11y.test.js && node test/contour-coverage.test.js && node test/contour-perf.test.js && node test/contour-density.test.js && node test/contour-bounds.test.js && node scripts/build-host.js --check && node scripts/build-client.js --check && node scripts/build-contour-worker.js --check && node test/worker-eol.test.js && node test/contour-worker.test.js && node test/contour-worker-budget.test.js && node test/theme-sheet-refresh.test.js && node test/titlebar-probe.test.js && node test/corner-shape.test.js && node test/approval-band-transform.test.js && node test/light-token-consumers.test.js && node test/sidebar-row-wash.test.js && node test/glass.test.js && node test/contour-trail.test.js && node test/contour-trail-browser.test.js && node test/scroll-anim.test.js && node test/contour-loop-reentry.test.js",
|
|
51
|
+
"test:ci": "node check.js && node selftest.js && node test/import-probe.test.js && node test/font-scope.test.js && node test/palette-contrast.test.js && node test/palette-switch.test.js && node test/menu-surface.test.js dark && node test/menu-surface.test.js light && node test/settings-buttons.test.js && node test/tools/hover-check.js && node test/selector-guard.test.js && node test/preset-chip.test.js && node test/balance-window.test.js && node test/balance-capsule.test.js && node test/credit-capsule.test.js && node test/settings-rows.test.js && node test/settings-durable-hold.test.js && node test/settings-config-forms.test.js && node test/settings-config-fallback.test.js && node test/settings-namespace.test.js && node test/settings-off.test.js && node test/settings-locale.test.js && node test/audio-notify.test.js && node test/audio-attention-watch.test.js && node test/host-no-inject.test.js && node scripts/build-sounds.js --check && node test/thunder-edges.test.js && node test/tools/thunder-shot.js && node test/thunder-dismiss.test.js && node test/contour-render.test.js && node test/loader-performance.test.js && node test/loader-late-prefs.test.js && node test/contour-specks.test.js && node test/watermark-stacking.test.js && node test/contour-smoothness.test.js && node test/contour-cusps.test.js && node test/contour-a11y.test.js && node test/contour-density.test.js && node test/contour-bounds.test.js && node scripts/build-host.js --check && node scripts/build-client.js --check && node scripts/build-contour-worker.js --check && node test/worker-eol.test.js && node test/contour-worker.test.js && node test/contour-worker-budget.test.js && node test/theme-sheet-refresh.test.js && node test/titlebar-probe.test.js && node test/corner-shape.test.js && node test/approval-band-transform.test.js && node test/light-token-consumers.test.js && node test/sidebar-row-wash.test.js && node test/glass.test.js && node test/contour-trail.test.js && node test/contour-trail-browser.test.js && node test/scroll-anim.test.js && node test/contour-loop-reentry.test.js",
|
|
52
52
|
"shots": "node test/tools/shoot.js",
|
|
53
53
|
"shots:verify": "node test/tools/shoot.js && node test/tools/verify-shots.js",
|
|
54
54
|
"test:bounds": "node test/contour-bounds.test.js",
|