@chatcode/cco-market 1.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/README.zh.md +143 -0
- package/UPDATE-API-V1.md +132 -0
- package/client/client.js +11534 -0
- package/cordis.patch.yml +5 -0
- package/lib/accelerate.js +184 -0
- package/lib/agents.js +36 -0
- package/lib/backup.js +572 -0
- package/lib/catalog-local-match.js +126 -0
- package/lib/catalog-npm.js +111 -0
- package/lib/changelog.js +242 -0
- package/lib/channels.js +64 -0
- package/lib/check.js +1067 -0
- package/lib/compatibility.js +198 -0
- package/lib/diagnostics.js +49 -0
- package/lib/discovery-compatibility.js +260 -0
- package/lib/dsh-cli.js +949 -0
- package/lib/dsh-install.js +108 -0
- package/lib/gist.js +352 -0
- package/lib/groups.js +97 -0
- package/lib/home-paths.js +42 -0
- package/lib/hot.js +519 -0
- package/lib/http.js +39 -0
- package/lib/index.js +97 -0
- package/lib/install.js +387 -0
- package/lib/log.js +197 -0
- package/lib/ndjson.js +154 -0
- package/lib/net.js +98 -0
- package/lib/order.js +284 -0
- package/lib/patch.js +521 -0
- package/lib/pnpm-compat.js +490 -0
- package/lib/presets.js +278 -0
- package/lib/profile.js +911 -0
- package/lib/region-probe.js +93 -0
- package/lib/regions.js +253 -0
- package/lib/registry.js +204 -0
- package/lib/restart.js +373 -0
- package/lib/routes.js +4372 -0
- package/lib/settings.js +109 -0
- package/lib/skill-market/api.js +119 -0
- package/lib/skill-market/config.js +29 -0
- package/lib/skill-market/installer.js +352 -0
- package/lib/skill-market/runtime.js +13 -0
- package/lib/skill-market/tui.js +119 -0
- package/lib/skill-market/types.js +1 -0
- package/lib/skill-market/web-routes.js +123 -0
- package/lib/snapshot.js +500 -0
- package/lib/source-migration.js +54 -0
- package/lib/sources.js +596 -0
- package/lib/store.js +91 -0
- package/lib/themes.js +102 -0
- package/lib/trial.js +116 -0
- package/lib/types/accelerate.d.ts +85 -0
- package/lib/types/agents.d.ts +22 -0
- package/lib/types/backup.d.ts +131 -0
- package/lib/types/catalog-local-match.d.ts +50 -0
- package/lib/types/catalog-npm.d.ts +47 -0
- package/lib/types/changelog.d.ts +102 -0
- package/lib/types/channels.d.ts +56 -0
- package/lib/types/check.d.ts +266 -0
- package/lib/types/compatibility.d.ts +64 -0
- package/lib/types/diagnostics.d.ts +29 -0
- package/lib/types/discovery-compatibility.d.ts +80 -0
- package/lib/types/dsh-cli.d.ts +324 -0
- package/lib/types/dsh-install.d.ts +46 -0
- package/lib/types/gist.d.ts +53 -0
- package/lib/types/groups.d.ts +34 -0
- package/lib/types/home-paths.d.ts +16 -0
- package/lib/types/hot.d.ts +221 -0
- package/lib/types/http.d.ts +12 -0
- package/lib/types/index.d.ts +14 -0
- package/lib/types/install.d.ts +172 -0
- package/lib/types/log.d.ts +43 -0
- package/lib/types/ndjson.d.ts +52 -0
- package/lib/types/net.d.ts +58 -0
- package/lib/types/order.d.ts +100 -0
- package/lib/types/patch.d.ts +129 -0
- package/lib/types/pnpm-compat.d.ts +93 -0
- package/lib/types/presets.d.ts +90 -0
- package/lib/types/profile.d.ts +253 -0
- package/lib/types/region-probe.d.ts +50 -0
- package/lib/types/regions.d.ts +122 -0
- package/lib/types/registry.d.ts +86 -0
- package/lib/types/restart.d.ts +194 -0
- package/lib/types/routes.d.ts +67 -0
- package/lib/types/settings.d.ts +78 -0
- package/lib/types/skill-market/api.d.ts +10 -0
- package/lib/types/skill-market/config.d.ts +3 -0
- package/lib/types/skill-market/installer.d.ts +23 -0
- package/lib/types/skill-market/runtime.d.ts +10 -0
- package/lib/types/skill-market/tui.d.ts +4 -0
- package/lib/types/skill-market/types.d.ts +71 -0
- package/lib/types/skill-market/web-routes.d.ts +19 -0
- package/lib/types/snapshot.d.ts +93 -0
- package/lib/types/source-migration.d.ts +11 -0
- package/lib/types/sources.d.ts +216 -0
- package/lib/types/store.d.ts +30 -0
- package/lib/types/themes.d.ts +40 -0
- package/lib/types/trial.d.ts +61 -0
- package/lib/types/update-api-v1.d.ts +66 -0
- package/lib/types/updates.d.ts +123 -0
- package/lib/types/verify.d.ts +139 -0
- package/lib/update-api-v1.js +215 -0
- package/lib/updates.js +361 -0
- package/lib/verify.js +453 -0
- package/package.json +121 -0
- package/src/accelerate.ts +213 -0
- package/src/agents.ts +43 -0
- package/src/backup.ts +583 -0
- package/src/catalog-local-match.ts +144 -0
- package/src/catalog-npm.ts +120 -0
- package/src/changelog.ts +282 -0
- package/src/channels.ts +70 -0
- package/src/check.ts +1239 -0
- package/src/client/CommentsModal.tsx +119 -0
- package/src/client/Diagnostics.tsx +907 -0
- package/src/client/ErrorBoundary.tsx +111 -0
- package/src/client/InstallToast.tsx +31 -0
- package/src/client/Market.module.css +830 -0
- package/src/client/MarketSection.tsx +5301 -0
- package/src/client/OperationsPanel.tsx +365 -0
- package/src/client/SettingsCard.tsx +621 -0
- package/src/client/SkillMarket.module.css +21 -0
- package/src/client/SkillMarketSection.tsx +162 -0
- package/src/client/comments.ts +54 -0
- package/src/client/globals.d.ts +13 -0
- package/src/client/index.ts +180 -0
- package/src/client/locales.ts +1112 -0
- package/src/client/market-data.ts +1321 -0
- package/src/client/operations.ts +201 -0
- package/src/client/preset-panel.tsx +263 -0
- package/src/client/primitives.d.ts +140 -0
- package/src/client/self-check.ts +147 -0
- package/src/client/snapshot-panel.tsx +244 -0
- package/src/compatibility.ts +237 -0
- package/src/diagnostics.ts +84 -0
- package/src/discovery-compatibility.ts +315 -0
- package/src/dsh-cli.ts +1126 -0
- package/src/dsh-install.ts +118 -0
- package/src/gist.ts +362 -0
- package/src/groups.ts +111 -0
- package/src/home-paths.ts +53 -0
- package/src/hot.ts +628 -0
- package/src/http.ts +41 -0
- package/src/index.ts +128 -0
- package/src/install.ts +420 -0
- package/src/log.ts +206 -0
- package/src/ndjson.ts +185 -0
- package/src/net.ts +106 -0
- package/src/order.ts +303 -0
- package/src/patch.ts +522 -0
- package/src/pnpm-compat.ts +527 -0
- package/src/presets.ts +344 -0
- package/src/profile.ts +940 -0
- package/src/region-probe.ts +97 -0
- package/src/regions.ts +310 -0
- package/src/registry.ts +250 -0
- package/src/restart.ts +396 -0
- package/src/routes.ts +4506 -0
- package/src/settings.ts +141 -0
- package/src/skill-market/api.ts +130 -0
- package/src/skill-market/config.ts +32 -0
- package/src/skill-market/installer.ts +337 -0
- package/src/skill-market/runtime.ts +14 -0
- package/src/skill-market/tui.ts +157 -0
- package/src/skill-market/types.ts +80 -0
- package/src/skill-market/web-routes.ts +132 -0
- package/src/snapshot.ts +532 -0
- package/src/source-migration.ts +61 -0
- package/src/sources.ts +565 -0
- package/src/store.ts +89 -0
- package/src/themes.ts +125 -0
- package/src/trial.ts +156 -0
- package/src/update-api-v1.ts +277 -0
- package/src/updates.ts +400 -0
- package/src/verify.ts +492 -0
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
/* Market section + install toast styles. Every color resolves through a
|
|
2
|
+
* --dsw-alias-* design token with the original light-mode literal as
|
|
3
|
+
* fallback. Interactive chrome (buttons, menus, dialogs, pills, inputs,
|
|
4
|
+
* disclosures) comes from @deepseek-ai/dsh-client-ui-primitives; this sheet
|
|
5
|
+
* only carries layout, cards, and the few bespoke surfaces without a
|
|
6
|
+
* primitive (progress bar, banners, tabs, swatches). */
|
|
7
|
+
|
|
8
|
+
.root{height:100%;display:flex;flex-direction:column;min-width:0;color:var(--dsw-alias-label-primary,#1f2328);position:relative;container-type:inline-size}
|
|
9
|
+
.head{display:flex;flex-direction:column;gap:12px;padding:4px 4px 6px}
|
|
10
|
+
.title{font-size:16px;line-height:24px;font-weight:500;margin:0}
|
|
11
|
+
.sub{display:flex;align-items:center;gap:8px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0}
|
|
12
|
+
.submitLink{font-size:11px;line-height:18px;color:var(--dsw-alias-label-tertiary,#8b93a1);text-decoration:none;white-space:nowrap}
|
|
13
|
+
.submitLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}
|
|
14
|
+
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end}
|
|
15
|
+
.tab{border:none;background:none;font:inherit;font-size:13px;color:var(--dsw-alias-label-secondary,#6b7280);padding:7px 12px;cursor:pointer;border-bottom:2px solid transparent;white-space:nowrap}
|
|
16
|
+
.tab.on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}
|
|
17
|
+
/* Second-level tabs under "Advanced" — same button styling as .tabs so the
|
|
18
|
+
two read as one family, but no full-width bottom border of its own: that
|
|
19
|
+
border is what visually says "top-level tab row", and having two of them
|
|
20
|
+
stacked read as two unrelated tab bars rather than a parent/child pair. */
|
|
21
|
+
.subTabs{display:flex;gap:2px;align-items:flex-end;margin:-4px 0 4px}
|
|
22
|
+
.banner{display:flex;align-items:center;gap:8px;background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;padding:8px 12px;font-size:12px;margin:0}
|
|
23
|
+
.bannerIcon{flex-shrink:0;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
24
|
+
.bannerHint{display:inline-flex;color:var(--dsw-alias-label-tertiary,#8b93a1);cursor:help}
|
|
25
|
+
/* overflow-anchor:none is load-bearing, not a tidy-up (#395 by @zhangyh-x:
|
|
26
|
+
"展开分类,滑动列表时会闪动"). Chrome anchors the scroll position to a node
|
|
27
|
+
below the viewport and silently rewrites scrollTop whenever content ABOVE
|
|
28
|
+
it changes height. The sticky header does exactly that on purpose: leaving
|
|
29
|
+
the top auto-collapses an expanded category row, which is ~120px. So the
|
|
30
|
+
first small scroll collapsed the row, Chrome moved scrollTop back by the
|
|
31
|
+
same 120px to keep the anchored card still, that put the viewport above
|
|
32
|
+
the sentinel again, the row re-expanded, and the cycle repeated for as
|
|
33
|
+
long as the user kept scrolling. Measured in Chrome at 1020x526 with the
|
|
34
|
+
row open: scrollTop 8 -> 134 -> 24 inside 30ms with the row flipping
|
|
35
|
+
146px <-> 26px each time; parking at scrollTop 20 was yanked back to 0.
|
|
36
|
+
With anchoring off, the same gesture collapses once and scrollTop stays
|
|
37
|
+
where the user put it.
|
|
38
|
+
|
|
39
|
+
Anchoring costs this list nothing to give up: every image it renders is
|
|
40
|
+
sized in CSS (.shot, .cardShot, .av), so nothing above the viewport
|
|
41
|
+
resizes on load, and the banners that can appear live outside this
|
|
42
|
+
scroller entirely. The one thing anchoring had left to compensate for was
|
|
43
|
+
the deliberate collapse — the one adjustment that must not happen. */
|
|
44
|
+
.body{flex:1;overflow-y:auto;padding:12px 4px 24px;overflow-x:hidden;overflow-anchor:none}
|
|
45
|
+
/* Sticky, so the search box and the category row both stay reachable while
|
|
46
|
+
the grid scrolls — they used to stick separately (only .cats did), which
|
|
47
|
+
left the search row to scroll away under .head's fixed title/tabs strip,
|
|
48
|
+
getting sliced off mid-scroll instead of disappearing cleanly. Wrapping
|
|
49
|
+
both in one sticky block makes them move (and stop) together; a second,
|
|
50
|
+
independently-offset sticky element would need its `top` hand-tuned to
|
|
51
|
+
the search row's rendered height, which drifts with locale/font/icon
|
|
52
|
+
changes — this doesn't need that number to exist at all.
|
|
53
|
+
The negative top margin used to pull it 12px up over the search row, and
|
|
54
|
+
with a background and z-index above it that covered the input's lower
|
|
55
|
+
edge — the control looked cut in half at rest, before any scrolling.
|
|
56
|
+
Only the horizontal bleed is wanted; the vertical one was collateral. */
|
|
57
|
+
/* The scroller (.body) carries padding-top:12px, and a sticky child cannot
|
|
58
|
+
rise above its containing block — the parent's CONTENT box. So this row
|
|
59
|
+
stops 12px short of the scrollport and the grid scrolls through the strip
|
|
60
|
+
above it: measured on a real host as cats.top=214 against scroller.top=203.
|
|
61
|
+
Reported twice, first as "a big gap above the sticky row" and then as a
|
|
62
|
+
card clipped into it.
|
|
63
|
+
::before paints that strip in the same background and inherits the row's
|
|
64
|
+
z-index, so the row covers what it cannot reach. Fixing it by removing the
|
|
65
|
+
scroller's padding would re-space all five tabs to repair one. */
|
|
66
|
+
.stickyHead{position:sticky;top:-1px;z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa)}
|
|
67
|
+
.stickyHead::before{content:'';position:absolute;left:0;right:0;bottom:100%;height:14px;background:inherit;pointer-events:none}
|
|
68
|
+
.cats{padding:12px 4px 4px;margin:0 -4px 2px}
|
|
69
|
+
.catsRow{display:flex;gap:8px;align-items:flex-start;position:relative}
|
|
70
|
+
.star{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af);flex:0 0 auto;white-space:nowrap}
|
|
71
|
+
/* Opaque disc behind the outline Button. The host outline fill (incl.
|
|
72
|
+
:hover) is transparent by design — without a surface under it the
|
|
73
|
+
chevron sits on card text. Keep the fill on this wrapper so the
|
|
74
|
+
Button can keep its native hover wash. */
|
|
75
|
+
.top{
|
|
76
|
+
position:absolute;right:18px;bottom:18px;z-index:20;display:inline-flex;
|
|
77
|
+
border-radius:99px;
|
|
78
|
+
background:var(--dsw-alias-bg-layer-1,#fff);
|
|
79
|
+
box-shadow:var(--dsw-shadow-lv1,0 4px 12px rgba(31,35,40,.08));
|
|
80
|
+
}
|
|
81
|
+
.topBtn{width:38px;height:38px;border-radius:99px;padding:0}
|
|
82
|
+
.tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}
|
|
83
|
+
.hostRequirement{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;min-width:0;max-width:100%;padding:1px 6px;font-size:11px;line-height:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
84
|
+
.hostFilterNote{margin-top:6px;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px;line-height:16px}
|
|
85
|
+
.okState{font-size:12px;font-weight:600;color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap}
|
|
86
|
+
.catsWrap{display:flex;gap:6px;flex-wrap:wrap;flex:1;min-width:0;align-items:center}
|
|
87
|
+
.catsCollapsed{max-height:62px;overflow:hidden}
|
|
88
|
+
.catsToggle.catsToggle{height:26px;min-height:26px;padding:0 6px;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
89
|
+
.shots{display:flex;gap:8px;overflow-x:auto;padding:2px 0 6px;margin:6px 0 8px;-webkit-overflow-scrolling:touch;scrollbar-width:thin}
|
|
90
|
+
/* width, not just max-width — a slot with only a height reservation still
|
|
91
|
+
collapses to zero width until the image loads, so the strip snaps wider
|
|
92
|
+
the instant each one arrives instead of holding its place from the start. */
|
|
93
|
+
.shot{width:220px;height:150px;flex:0 0 auto;object-fit:cover;border-radius:8px;border:1px solid var(--dsw-alias-border-default,#e5e7eb);background:var(--dsw-alias-bg-layer-2,#f3f4f6);cursor:pointer}
|
|
94
|
+
.cardShots{display:flex;gap:6px;overflow-x:auto;padding:0 0 2px;margin:0 0 6px;-webkit-overflow-scrolling:touch;scrollbar-width:thin}
|
|
95
|
+
/* A fixed box so the strip holds its place: with width:auto the browser had
|
|
96
|
+
no aspect ratio until the bytes arrived, so each thumbnail rendered at its
|
|
97
|
+
min-width and jumped to height x natural-ratio once decoded.
|
|
98
|
+
`contain`, not `cover`: reserving the space is what fixes the jump, and
|
|
99
|
+
cropping to fill would throw away the uncropped thumbnail this strip was
|
|
100
|
+
built for. The letterbox shows through as the tile's own background. */
|
|
101
|
+
.cardShot{width:132px;height:88px;flex:0 0 auto;object-fit:contain;border-radius:8px;
|
|
102
|
+
border:1px solid var(--dsw-alias-border-default,#e5e7eb);
|
|
103
|
+
background:var(--dsw-alias-bg-layer-2,#f3f4f6);cursor:pointer;display:block}
|
|
104
|
+
/* Full-bleed overlay, not the Modal primitive — Modal is sized chrome
|
|
105
|
+
(title/description/footer) for making a decision, this is just an image
|
|
106
|
+
the user came to look at, no decision to make. */
|
|
107
|
+
/* Above the Settings Modal it opens on top of — that Modal's own z-index
|
|
108
|
+
isn't defined in this package (its CSS module ships with the host app),
|
|
109
|
+
so this has to out-rank any plausible dialog layer, not just tie and win
|
|
110
|
+
on DOM order. */
|
|
111
|
+
.lightbox{position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;cursor:zoom-out}
|
|
112
|
+
.lightboxImg{max-width:90vw;max-height:85vh;object-fit:contain;border-radius:4px;cursor:default}
|
|
113
|
+
.lightboxClose{position:absolute;top:16px;right:16px;width:36px;height:36px;border-radius:99px;border:none;background:rgba(255,255,255,.12);color:#fff;font-size:22px;line-height:1;display:grid;place-items:center;cursor:pointer}
|
|
114
|
+
.lightboxClose:hover{background:rgba(255,255,255,.22)}
|
|
115
|
+
.lightboxNav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;border-radius:99px;border:none;background:rgba(255,255,255,.12);color:#fff;display:grid;place-items:center;cursor:pointer}
|
|
116
|
+
.lightboxNav:hover{background:rgba(255,255,255,.22)}
|
|
117
|
+
.lightboxPrev{left:16px}
|
|
118
|
+
.lightboxNext{right:16px}
|
|
119
|
+
.lightboxDots{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);display:flex;gap:8px}
|
|
120
|
+
.lightboxDot{width:7px;height:7px;border-radius:99px;background:rgba(255,255,255,.4);cursor:pointer}
|
|
121
|
+
.lightboxDotOn{background:#fff}
|
|
122
|
+
.cmd{font-size:11px;line-height:18px;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:6px;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;margin:8px 0 0;word-break:break-all}
|
|
123
|
+
/* flex-wrap so a long warning + trailing link wraps like normal text — the
|
|
124
|
+
deprecated-plugin warning below (.depLine) is the same icon+text+link
|
|
125
|
+
shape and already gets this right; without it the trailing link is the
|
|
126
|
+
one flex item left with no room once the paragraph text wraps, and gets
|
|
127
|
+
squeezed into a vertical stack instead of dropping to its own line. */
|
|
128
|
+
.warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);font-weight:600;font-size:12px;line-height:18px;margin:0;display:flex;align-items:center;gap:4px;flex-wrap:wrap}
|
|
129
|
+
.modalNote{font-size:12px;color:var(--dsw-alias-label-tertiary,#8b93a1);line-height:18px;margin:12px 0 0;display:flex;align-items:center;gap:4px}
|
|
130
|
+
.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:10px}
|
|
131
|
+
/* Masonry: two independent columns, each packing its own cards tight.
|
|
132
|
+
A CSS grid cannot do this — its rows are as tall as their tallest cell, so
|
|
133
|
+
a card with screenshots left a matching hole beside a card without them,
|
|
134
|
+
and `align-items:start` turned that hole into visible dead space rather
|
|
135
|
+
than a stretched card. Column-major `column-count` would pack just as
|
|
136
|
+
well but reorders the list down one column and then the other, which
|
|
137
|
+
destroys the ranking the sort just produced. Dealing the cards
|
|
138
|
+
alternately into two flex columns keeps rank reading left-to-right,
|
|
139
|
+
top-to-bottom, and lets each column close its own gaps. */
|
|
140
|
+
.masonry{display:flex;align-items:flex-start;gap:10px}
|
|
141
|
+
.masonryCol{flex:1;min-width:0;display:flex;flex-direction:column;gap:10px}
|
|
142
|
+
/* `.card` carries `align-self:start`, which under CSS grid means "do not
|
|
143
|
+
stretch VERTICALLY to match a taller row-mate". In a flex COLUMN the same
|
|
144
|
+
declaration addresses the other axis: it stops the card stretching
|
|
145
|
+
HORIZONTALLY, so its width becomes max-content and a card with a long
|
|
146
|
+
name grew to 444px inside a 273px column and covered the neighbouring
|
|
147
|
+
column's buttons. Same property, opposite meaning, silent either way.
|
|
148
|
+
Masonry needs the vertical freedom it already gets from the column stack,
|
|
149
|
+
so cards stretch to the column width here. min-width:0 is the flex
|
|
150
|
+
equivalent of the grid's `minmax(0, 1fr)`: without it a card's
|
|
151
|
+
min-content floor would push the column wide again. */
|
|
152
|
+
/* Two class names, not one: `.card` sets `align-self:start` and is declared
|
|
153
|
+
later in this file, so a single-class selector here would tie on
|
|
154
|
+
specificity and lose to source order. */
|
|
155
|
+
.masonry > .masonryCol > *{align-self:stretch;min-width:0}
|
|
156
|
+
@media (max-width:680px){
|
|
157
|
+
/* Stacked, the cross axis is horizontal and `.masonry`'s align-items keeps
|
|
158
|
+
the column shrink-to-fit — so the single column has to claim the width
|
|
159
|
+
explicitly. Both tabs render through the same component, so this belongs
|
|
160
|
+
here rather than as an inline style on one of them. */
|
|
161
|
+
.masonry{flex-direction:column}
|
|
162
|
+
.masonryCol{width:100%}
|
|
163
|
+
}
|
|
164
|
+
@media (max-width:680px){
|
|
165
|
+
.grid{grid-template-columns:minmax(0,1fr)}
|
|
166
|
+
}
|
|
167
|
+
.swatches{display:flex;gap:0;height:34px;border-radius:8px;overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
168
|
+
.themesGrid{margin-bottom:12px}
|
|
169
|
+
.swatches i{flex:1}
|
|
170
|
+
|
|
171
|
+
/* Themes are a visual choice, not a repository list. Their page therefore
|
|
172
|
+
owns a stable gallery instead of inheriting the generic masonry cards. */
|
|
173
|
+
.themeToolbar{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;padding:0 4px 12px}
|
|
174
|
+
/* border-box, not the sheet's default content-box: the primitives' Input
|
|
175
|
+
renders a span with 8px padding and a 1px border, so `width:100%` resolved
|
|
176
|
+
to the grid cell (438px) and then rendered 18px WIDER than it — straight
|
|
177
|
+
under the filter and full-screen buttons (#305 by @lcthe). The same class
|
|
178
|
+
on the Discover tab never showed it: that row is flex, and a flex item
|
|
179
|
+
shrinks to fit where a grid item does not. */
|
|
180
|
+
.themeSearch{width:100%;min-width:0;box-sizing:border-box}
|
|
181
|
+
.themeToolbarActions{display:flex;align-items:center;justify-content:flex-end;gap:8px}
|
|
182
|
+
.themeFullscreenBtn.themeFullscreenBtn{width:28px;min-width:28px;padding:0}
|
|
183
|
+
.themeResultBar{display:flex;align-items:center;min-height:24px;padding:0 4px 8px;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px;line-height:18px}
|
|
184
|
+
.favoritesSectionHead{margin:16px 0 8px;padding:0 4px;font-size:13px;font-weight:600;line-height:20px;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
185
|
+
.favoritesSectionHead:first-of-type{margin-top:0}
|
|
186
|
+
.favoritesStaleBar{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;margin:0 0 8px;padding:8px 10px;border:1px solid var(--dsw-alias-state-warn-tertiary,rgba(180,83,9,.2));border-radius:8px;background:var(--dsw-alias-state-warn-tertiary,rgba(180,83,9,.06));color:var(--dsw-alias-state-warn-primary,#b45309);font-size:12px;line-height:18px}
|
|
187
|
+
.favoritesStaleOnly{display:flex;flex-direction:column;align-items:flex-start;gap:10px}
|
|
188
|
+
.themeGallery{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));align-items:stretch;gap:10px}
|
|
189
|
+
.themeCard{display:flex;flex-direction:column;min-width:0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);transition:border-color 160ms cubic-bezier(.16,1,.3,1),box-shadow 160ms cubic-bezier(.16,1,.3,1),transform 160ms cubic-bezier(.16,1,.3,1)}
|
|
190
|
+
.themeCard:hover{border-color:var(--dsw-alias-border-l3,#d9dde3);box-shadow:var(--dsw-shadow-lv1,0 4px 12px rgba(31,35,40,.08));transform:translateY(-1px)}
|
|
191
|
+
.themeCover{position:relative;display:block;width:100%;aspect-ratio:16/10;overflow:hidden;padding:0;border:0;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:0;background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280);cursor:zoom-in}
|
|
192
|
+
.themeCover img{display:block;width:100%;height:100%;object-fit:contain;background:var(--dsw-alias-bg-layer-2,#f3f4f6);transition:transform 180ms cubic-bezier(.16,1,.3,1)}
|
|
193
|
+
.themeCover:hover img{transform:scale(1.012)}
|
|
194
|
+
.themeCover:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4f6ef7);outline-offset:-3px}
|
|
195
|
+
.themeCoverEmpty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;cursor:default;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px;line-height:18px}
|
|
196
|
+
.themePreviewAction,.themePreviewCount{position:absolute;bottom:8px;display:inline-flex;align-items:center;height:24px;padding:0 8px;border:1px solid rgba(255,255,255,.34);border-radius:4px;background:rgba(20,24,31,.78);color:#fff;font-size:11px;line-height:16px;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
|
|
197
|
+
.themePreviewAction{right:8px;gap:4px}
|
|
198
|
+
.themePreviewCount{left:8px}
|
|
199
|
+
.themeCardBody{display:flex;flex:1;flex-direction:column;gap:8px;padding:12px}
|
|
200
|
+
.themeCardHead{display:flex;align-items:flex-start;gap:8px;min-width:0}
|
|
201
|
+
.themeIdentity{min-width:0;flex:1}
|
|
202
|
+
.themeStatus,.themeStatusMuted{flex:none;display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:4px;font-size:11px;font-weight:600;white-space:nowrap}
|
|
203
|
+
.themeStatus{background:var(--dsw-alias-state-success-tertiary,rgba(22,163,74,.1));color:var(--dsw-alias-state-success-primary,#15803d)}
|
|
204
|
+
.themeStatusMuted{background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
205
|
+
.themeDescription{display:-webkit-box;min-height:36px;margin:0;overflow:hidden;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px;line-height:18px;-webkit-box-orient:vertical;-webkit-line-clamp:3}
|
|
206
|
+
.themeCardFooter{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;padding-top:4px}
|
|
207
|
+
.themeLifecycle{min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}
|
|
208
|
+
.themeActions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-left:auto}
|
|
209
|
+
|
|
210
|
+
/* The market usually lives inside a settings panel, so viewport media
|
|
211
|
+
queries do not describe the width available to it. Container queries do. */
|
|
212
|
+
@container (max-width:900px){
|
|
213
|
+
.themeGallery{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
214
|
+
}
|
|
215
|
+
@container (max-width:680px){
|
|
216
|
+
.themeGallery{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
217
|
+
}
|
|
218
|
+
@container (max-width:460px){
|
|
219
|
+
.themeGallery{grid-template-columns:minmax(0,1fr)}
|
|
220
|
+
}
|
|
221
|
+
@container (max-width:420px){
|
|
222
|
+
.sub{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:2px 8px}
|
|
223
|
+
.sub > span:first-child{grid-column:1;grid-row:1}
|
|
224
|
+
.sub > .grow{display:none}
|
|
225
|
+
.submitLink{grid-column:1;grid-row:2}
|
|
226
|
+
.exportLogBtn{grid-column:2;grid-row:1 / span 2;flex-shrink:0}
|
|
227
|
+
.themeToolbar{grid-template-columns:minmax(0,1fr)}
|
|
228
|
+
.themeCardHead{flex-wrap:wrap}
|
|
229
|
+
.themeStatus,.themeStatusMuted{order:3}
|
|
230
|
+
}
|
|
231
|
+
@container (max-width:280px){
|
|
232
|
+
.themeCardFooter{align-items:flex-start;flex-direction:column}
|
|
233
|
+
.themeActions{width:100%;justify-content:flex-start;margin-left:0}
|
|
234
|
+
}
|
|
235
|
+
/* The host Settings dialog keeps a fixed navigation rail on phone-sized
|
|
236
|
+
viewports. Once that rail leaves less than a card's usable width, the
|
|
237
|
+
market becomes the only settings destination and keeps the host's own
|
|
238
|
+
close button in the content header. */
|
|
239
|
+
@media (max-width:560px){
|
|
240
|
+
[role="dialog"]:has([data-dsh-market-root]) > nav{display:none}
|
|
241
|
+
[role="dialog"]:has([data-dsh-market-root]) > div{width:100%;min-width:0;flex:1 1 100%}
|
|
242
|
+
}
|
|
243
|
+
/* Explicit immersive mode: the normal Settings width remains the default,
|
|
244
|
+
while the user can opt into a Wallpaper-style multi-column gallery. */
|
|
245
|
+
[role="dialog"]:has([data-dsh-market-fullscreen="true"]){position:fixed;inset:0;width:100vw;height:100vh;max-width:none;max-height:none;border-radius:0}
|
|
246
|
+
@media (prefers-reduced-motion:reduce){
|
|
247
|
+
.themeCard,.themeCover img{transition:none}
|
|
248
|
+
.themeCard:hover{transform:none}
|
|
249
|
+
}
|
|
250
|
+
/* align-self:start so a short card doesn't stretch to match a tall
|
|
251
|
+
row-mate — CSS grid rows are equal-height by default, and a stretched
|
|
252
|
+
card with margin-top:auto on its footer used to fill that leftover
|
|
253
|
+
height as a blank band INSIDE the card, between the description and the
|
|
254
|
+
footer, reading as a layout bug rather than "this one's row-mate had a
|
|
255
|
+
longer description". Un-stretched, any leftover row height sits outside
|
|
256
|
+
the card as ordinary grid gutter instead. */
|
|
257
|
+
.card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:12px;align-self:start}
|
|
258
|
+
.row1{display:flex;align-items:flex-start;gap:10px;min-width:0}
|
|
259
|
+
.cardAction{flex-shrink:0;display:inline-flex;align-items:center}
|
|
260
|
+
.installBtn.installBtn{min-width:64px}
|
|
261
|
+
.av{width:16px;height:16px;border-radius:50%;display:grid;place-items:center;font-weight:700;color:#fff;font-size:9px;flex-shrink:0;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6)}
|
|
262
|
+
/* The plugin name carries the card, so it is the one thing sized up; the
|
|
263
|
+
author sits under it as a signature. Same reading order as a post
|
|
264
|
+
header — you take in who wrote it in one glance and spend the rest on
|
|
265
|
+
what it is. */
|
|
266
|
+
.nm{font-weight:600;font-size:15px;line-height:22px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
267
|
+
/* display:block is load-bearing, not cosmetic. `.nm` clips with
|
|
268
|
+
overflow:hidden + text-overflow:ellipsis, and neither applies to an
|
|
269
|
+
INLINE box — so once the title became an <a> (it links to the repo), a
|
|
270
|
+
long name stopped ellipsizing and overflowed the card instead, landing on
|
|
271
|
+
top of the Install button and swallowing its clicks. Caught by the e2e
|
|
272
|
+
suite as "the name link intercepts pointer events". */
|
|
273
|
+
.nmLink{display:block;color:inherit;text-decoration:none}
|
|
274
|
+
.nmLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}
|
|
275
|
+
/* The title has always opened the repo, but styled `color:inherit` with no
|
|
276
|
+
underline it looked like plain text, so nothing said so until the cursor
|
|
277
|
+
was already on it (#256). The GitHub mark is the standing affordance —
|
|
278
|
+
it names the destination before hover (#365). It rides the title's own
|
|
279
|
+
line rather than taking a row of its own; a second link to the same URL
|
|
280
|
+
would have cost every card ~20px of head, against the density the grid
|
|
281
|
+
was just tuned for. */
|
|
282
|
+
.repoMark{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;margin-left:5px;vertical-align:-1px;display:inline-block}
|
|
283
|
+
.nmLink:hover .repoMark{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
284
|
+
/* flex-wrap so a long owner name pushes the download/star counts to a
|
|
285
|
+
second line instead of squeezing the name itself toward unreadable: an
|
|
286
|
+
ellipsized span's own min-content width is 0 (overflow:hidden zeroes the
|
|
287
|
+
automatic minimum), so without `.owner`'s explicit min-width below, the
|
|
288
|
+
row never wraps — it just keeps shrinking the name past legibility while
|
|
289
|
+
the fixed-width counts stay whole. */
|
|
290
|
+
.byline{display:flex;align-items:center;gap:6px;min-width:0;margin-top:2px;flex-wrap:wrap}
|
|
291
|
+
/* Second line under the byline: the publish date, moved off the crowded
|
|
292
|
+
owner/downloads/stars row for visual balance (reported once downloads
|
|
293
|
+
joined stars on that line). */
|
|
294
|
+
.meta{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af);margin-top:2px}
|
|
295
|
+
.metaInline{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af)}
|
|
296
|
+
/* The author name is the only variable-length part, so it is the only part
|
|
297
|
+
that shrinks; the counts stay whole because a truncated one is not a fact. */
|
|
298
|
+
.owner{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af);flex:0 1 auto;min-width:44px;
|
|
299
|
+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
300
|
+
.desc{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0}
|
|
301
|
+
/* A FIXED 5-line slot (not just a cap): a one-line description reserves the
|
|
302
|
+
same 90px as a five-line one, so every card in a row starts its thumbnail
|
|
303
|
+
strip/footer at the same y — a clamp with no floor still let a short card
|
|
304
|
+
end early and a long one run to five lines, which read as the grid itself
|
|
305
|
+
being uneven (reported on a real host: several cards mid-row landing at
|
|
306
|
+
visibly different heights). The toggle in CardDesc removes this class, so
|
|
307
|
+
nothing here caps the expanded height. */
|
|
308
|
+
/* A ceiling, not a fixed slot. The 90px floor existed to make CSS-grid rows
|
|
309
|
+
line up; masonry packs each column independently, so reserving five lines
|
|
310
|
+
for a one-line description now just wastes the space it was buying. */
|
|
311
|
+
.descClamp{display:-webkit-box;-webkit-line-clamp:5;-webkit-box-orient:vertical;overflow:hidden}
|
|
312
|
+
.descToggle{display:flex;align-items:center;justify-content:center;width:20px;height:16px;margin-top:2px;
|
|
313
|
+
color:var(--dsw-alias-label-tertiary,#8b93a1);background:none;border:none;padding:0;cursor:pointer}
|
|
314
|
+
.descToggle:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
315
|
+
.foot{display:flex;align-items:flex-end;gap:8px;flex-wrap:nowrap}
|
|
316
|
+
.foot .metaInline,.foot .src{white-space:nowrap}
|
|
317
|
+
/* Tags share one flex child so they wrap among themselves instead of
|
|
318
|
+
kicking favorite/comments onto a lonely second row. flex:1 + min-width:0
|
|
319
|
+
leaves room for .footActions on the right of the same line. */
|
|
320
|
+
.footTags{display:flex;flex-wrap:wrap;gap:8px;flex:1 1 0;min-width:0;align-items:center}
|
|
321
|
+
.grow{flex:1}
|
|
322
|
+
.titleRow{display:flex;align-items:center;gap:10px}
|
|
323
|
+
/* Sits beside the heading so a screenshot of the page carries the
|
|
324
|
+
version — most reports arrive as a photo of the screen. Selectable
|
|
325
|
+
(not user-select:none) so it can also be copied into an issue. */
|
|
326
|
+
.version{font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary,#8b93a1);font-variant-numeric:tabular-nums;flex-shrink:0}
|
|
327
|
+
.repoLink{font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary,#8b93a1);text-decoration:none;flex-shrink:0}
|
|
328
|
+
.repoLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
329
|
+
/* The note row (#347). The user's own text reads as theirs — slightly
|
|
330
|
+
stronger than a catalog blurb, which is the point: it is the line they
|
|
331
|
+
wrote for themselves. The original/mine toggle stays quiet text, while
|
|
332
|
+
add/edit is a small outlined action: when both used the description's
|
|
333
|
+
tertiary text treatment, `备注` read like another word in the blurb
|
|
334
|
+
instead of something the user could press (#399). */
|
|
335
|
+
.noteRow{display:flex;align-items:baseline;gap:6px;min-width:0;flex-wrap:wrap}
|
|
336
|
+
/* Below the spec line; #458 can leave this row button-only with no desc
|
|
337
|
+
height, so a little top margin keeps it from hugging the link. */
|
|
338
|
+
.desc.noteRow{margin-top:6px}
|
|
339
|
+
/* The update-preview row (#294) sits right under the note row; without a
|
|
340
|
+
desc line to carry height the two blocks read as one (#458). */
|
|
341
|
+
.desc.noteRow + .noteRow{margin-top:6px}
|
|
342
|
+
.noteEdit + .noteRow{margin-top:6px}
|
|
343
|
+
.noteMine{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
344
|
+
.noteToggle{font:inherit;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#8b93a1);background:0 0;border:none;padding:0;cursor:pointer;flex-shrink:0;white-space:nowrap}
|
|
345
|
+
.noteToggle:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}
|
|
346
|
+
.noteAction{display:inline-flex;align-items:center;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l3,#d9dde3);border-radius:4px;padding:0 5px;font-weight:600;text-decoration:none}
|
|
347
|
+
.noteAction:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:none}
|
|
348
|
+
.noteEdit{display:flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap;margin-top:6px}
|
|
349
|
+
.noteInput{flex:1;min-width:160px}
|
|
350
|
+
.descTight{min-height:0}
|
|
351
|
+
.src{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af);text-decoration:none}
|
|
352
|
+
.src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
353
|
+
.dot{margin-left:5px;vertical-align:2px}
|
|
354
|
+
.act{display:flex;align-items:center;gap:6px;margin-top:6px;font-size:11px;flex-wrap:wrap}
|
|
355
|
+
.actLive{color:var(--dsw-alias-state-success-primary,#16a34a);font-weight:600;display:inline-flex;align-items:center;gap:4px}
|
|
356
|
+
.actWarn{color:var(--dsw-alias-state-warn-primary,#b45309);font-weight:600;display:inline-flex;align-items:center;gap:4px}
|
|
357
|
+
.actBroken{color:var(--dsw-alias-state-error-primary,#dc2626);font-weight:600;display:inline-flex;align-items:center;gap:4px}
|
|
358
|
+
/* DisclosureRow compacted into the activation chip row. */
|
|
359
|
+
.actWhy{margin-top:2px;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
360
|
+
.loading{display:flex;flex-direction:column;align-items:center;gap:12px;padding:48px;color:var(--dsw-alias-label-secondary,#9ca3af);font-size:13px}
|
|
361
|
+
.spin{display:inline-flex;flex-shrink:0;color:var(--dsw-alias-brand-primary,#4f6ef7);animation:sp .8s linear infinite}
|
|
362
|
+
/* Brand loading: the loose block plugs itself into the grid's empty corner
|
|
363
|
+
and springs back out. It is the mark's own story — a plugin arriving —
|
|
364
|
+
rather than a generic rotation, and it stays legible at 22px because the
|
|
365
|
+
moving part travels a full block-width against a still grid.
|
|
366
|
+
`transform-box: fill-box` makes the offsets below user units of THIS rect,
|
|
367
|
+
so they are the same (+1.28, -1.27) the static mark is drawn with. */
|
|
368
|
+
.logoMark{display:inline-flex;flex-shrink:0;color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
369
|
+
.logoPlug{transform-box:fill-box;transform-origin:50% 50%;animation:dshmPlug 1.5s cubic-bezier(.4,0,.2,1) infinite}
|
|
370
|
+
@keyframes dshmPlug{
|
|
371
|
+
0%,12%{transform:rotate(9deg)}
|
|
372
|
+
45%,62%{transform:translate(-1.28px,1.27px) rotate(0)}
|
|
373
|
+
95%,100%{transform:rotate(9deg)}
|
|
374
|
+
}
|
|
375
|
+
/* Motion is the whole signal here, so with it suppressed the block still has
|
|
376
|
+
to say "working" — it breathes in place instead of sitting inert. */
|
|
377
|
+
@media (prefers-reduced-motion: reduce){
|
|
378
|
+
.logoPlug{animation:dshmPlugFade 1.5s ease-in-out infinite}
|
|
379
|
+
.spin{animation:dshmPlugFade 1.5s ease-in-out infinite}
|
|
380
|
+
}
|
|
381
|
+
@keyframes dshmPlugFade{0%,100%{opacity:1}50%{opacity:.35}}
|
|
382
|
+
@keyframes sp{to{transform:rotate(360deg)}}
|
|
383
|
+
.progress{display:flex;align-items:center;flex-wrap:wrap;gap:9px;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:8px 12px;font-size:12px;margin:0;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
384
|
+
.bar{width:100%;height:4px;border-radius:99px;background:var(--dsw-alias-border-l1,#e5e7eb);overflow:hidden}
|
|
385
|
+
.barFill{height:100%;border-radius:99px;background:var(--dsw-alias-brand-primary,#4f6ef7);transition:width .6s ease}
|
|
386
|
+
.barWave{width:30%;animation:dshmSlide 1.2s ease-in-out infinite}
|
|
387
|
+
@keyframes dshmSlide{0%{margin-left:-30%}100%{margin-left:100%}}
|
|
388
|
+
.irow .progress{margin-top:8px}
|
|
389
|
+
.progress code{font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
390
|
+
.empty{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:13px;padding:32px;text-align:center}
|
|
391
|
+
.err{color:var(--dsw-alias-state-error-primary,#dc2626);font-size:12px;margin:8px 0;white-space:pre-wrap;word-break:break-all}
|
|
392
|
+
/* Loader-id clash, rendered ON the card the user pressed Install on rather
|
|
393
|
+
than in the page-level .err banner: that banner is shared by a dozen
|
|
394
|
+
unrelated operations, so a failure reported there loses its connection to
|
|
395
|
+
the card that caused it as soon as a list is longer than a screen. */
|
|
396
|
+
.cardBlocked{border-color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
397
|
+
.conflictHead{display:flex;align-items:flex-start;gap:8px}
|
|
398
|
+
.conflictIcon{flex-shrink:0;margin-top:1px;color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
399
|
+
.conflictTitle{font-size:13px;font-weight:600;line-height:18px;color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
400
|
+
.conflictBody{font-size:12px;line-height:19px;margin:0}
|
|
401
|
+
/* One row per OWNER, not per id: the decision the user makes is which
|
|
402
|
+
plugins to uninstall, so the plugin name leads and the ids it clashes on
|
|
403
|
+
ride along as secondary evidence. */
|
|
404
|
+
.roster{display:flex;flex-direction:column;gap:1px;border-radius:8px;overflow:hidden;
|
|
405
|
+
background:var(--dsw-alias-border-l2,#e5e7eb);border:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
406
|
+
.rosterRow{display:flex;align-items:center;gap:8px;padding:7px 10px;background:var(--dsw-alias-bg-layer-1,#fff)}
|
|
407
|
+
.rosterMain{display:flex;flex-direction:column;min-width:0}
|
|
408
|
+
.rosterName{font-size:12px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
409
|
+
.rosterAuthor{font-size:10.5px;color:var(--dsw-alias-label-secondary,#9ca3af);
|
|
410
|
+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
411
|
+
.rosterTag{margin-left:auto;flex-shrink:0;font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:4px}
|
|
412
|
+
.rosterTagKeep{color:var(--dsw-alias-state-success-primary,#16a34a);background:rgba(22,163,74,.12)}
|
|
413
|
+
.rosterTagDrop{color:var(--dsw-alias-state-error-primary,#dc2626);background:rgba(220,38,38,.12)}
|
|
414
|
+
/* The losing side is dimmed, so the outcome reads before any label does. */
|
|
415
|
+
.rosterRowOut .rosterName{text-decoration:line-through}
|
|
416
|
+
.rosterRowOut{opacity:.62}
|
|
417
|
+
/* Separates the plugin being installed from the ones already there. */
|
|
418
|
+
.rosterSplit{height:1px;background:var(--dsw-alias-border-l2,#e5e7eb)}
|
|
419
|
+
.reassure{display:flex;align-items:center;gap:5px;font-size:11.5px;line-height:17px;
|
|
420
|
+
color:var(--dsw-alias-label-secondary,#6b7280);margin:0}
|
|
421
|
+
.reassureOk{color:var(--dsw-alias-state-success-primary,#16a34a);flex-shrink:0}
|
|
422
|
+
.conflictWhy{margin-top:2px;font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary,#8b93a1);
|
|
423
|
+
font-family:ui-monospace,Menlo,monospace;overflow-wrap:anywhere}
|
|
424
|
+
.conflictWhyText{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC",sans-serif;margin-top:5px}
|
|
425
|
+
/* The clash resolves to a choice between two outcomes, so the options carry
|
|
426
|
+
their own consequence text and the safe one is preselected. */
|
|
427
|
+
.choices{display:flex;flex-direction:column;gap:7px}
|
|
428
|
+
.choice{display:flex;gap:9px;align-items:flex-start;text-align:left;font:inherit;cursor:pointer;
|
|
429
|
+
padding:9px 11px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#fff);
|
|
430
|
+
border:1px solid var(--dsw-alias-border-l3,#d9dde3)}
|
|
431
|
+
.choice:has(input:disabled){cursor:default;opacity:.6}
|
|
432
|
+
.choiceOn{border-color:var(--dsw-alias-brand-primary,#4f6ef7);
|
|
433
|
+
background:var(--dsw-alias-bg-layer-2,#f5f7ff)}
|
|
434
|
+
/* accent-color, not a redrawn control: the platform's own radio is the one
|
|
435
|
+
users recognise, and it keeps focus ring and arrow-key grouping. */
|
|
436
|
+
.choiceRadio{flex-shrink:0;margin:2px 0 0;width:13px;height:13px;
|
|
437
|
+
accent-color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
438
|
+
.choiceMain{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
439
|
+
.choiceTitle{font-size:12px;font-weight:600;line-height:17px}
|
|
440
|
+
.choiceNote{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
441
|
+
.choiceSafe{color:var(--dsw-alias-state-success-primary,#16a34a)}
|
|
442
|
+
|
|
443
|
+
/* --- activity entry + panel ---------------------------------------------
|
|
444
|
+
The entry sits in the tab row, so no amount of paginating, searching or
|
|
445
|
+
tab switching can take a record — or a pending decision — off screen.
|
|
446
|
+
That was the flaw of reporting an operation on the card that started it. */
|
|
447
|
+
/* Status reads as the dot + tag pair the host's own plugin inventory uses,
|
|
448
|
+
so the two surfaces state the same fact the same way. Every occupant of
|
|
449
|
+
the trailing group is one pill height, which is what lets an enabled row
|
|
450
|
+
and a disabled one line up instead of drifting against each other. */
|
|
451
|
+
.stateTag{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;min-height:20px;
|
|
452
|
+
border-radius:5px;padding:1px 7px;font-size:11px;line-height:16px;white-space:nowrap;
|
|
453
|
+
background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
454
|
+
.stateTag[data-on='true']{color:var(--dsw-alias-state-success-primary,#16a34a);
|
|
455
|
+
background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#16a34a) 10%, transparent)}
|
|
456
|
+
.stateDot{width:6px;height:6px;flex:none;border-radius:999px;background:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
457
|
+
.stateDot[data-on='true']{background:var(--dsw-alias-state-success-primary,#16a34a)}
|
|
458
|
+
/* The update slot: a tag when there is nothing to do, a button when there is.
|
|
459
|
+
Bare text here left the slot shorter than its neighbour's button. */
|
|
460
|
+
/* Shrinkable, unlike the buttons beside it: this slot holds the row's only
|
|
461
|
+
variable-length copy ("本地开发链接" / "Local dev link" is three times the
|
|
462
|
+
width of "已是最新"), and as an unshrinkable nowrap tag it pushed Uninstall
|
|
463
|
+
onto a second line (#234 by @Ztyss). The tag is the informational half of
|
|
464
|
+
the row and the buttons are the actionable half, so the tag is what gives
|
|
465
|
+
way — ellipsized, with the full text on hover. */
|
|
466
|
+
.metaTag{display:inline-block;flex-shrink:1;min-width:0;max-width:100%;overflow:hidden;
|
|
467
|
+
text-overflow:ellipsis;min-height:20px;border-radius:5px;
|
|
468
|
+
padding:1px 7px;font-size:11px;line-height:18px;white-space:nowrap;
|
|
469
|
+
background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
470
|
+
.metaTagOk{color:var(--dsw-alias-state-success-primary,#16a34a);
|
|
471
|
+
background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#16a34a) 10%, transparent)}
|
|
472
|
+
/* Local-dev restore in the same status slot as 已是最新 — same pill as
|
|
473
|
+
.metaTag but clickable and never ellipsized. */
|
|
474
|
+
.metaTagAction{font-family:inherit;font-size:11px;line-height:18px;font-weight:inherit;
|
|
475
|
+
cursor:pointer;border:none;flex-shrink:0;padding:1px 7px;border-radius:5px;
|
|
476
|
+
background:var(--dsw-alias-bg-layer-2,#f3f4f6);
|
|
477
|
+
color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
478
|
+
.metaTagAction:hover:not(:disabled){color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
479
|
+
.metaTagAction:disabled{opacity:.5;cursor:default}
|
|
480
|
+
/* Installed-row header: title left, local-dev badge top-right. */
|
|
481
|
+
.irowHead{display:flex;align-items:flex-start;gap:8px;min-width:0}
|
|
482
|
+
.irowHead .irowName{flex:1 1 0;min-width:0}
|
|
483
|
+
.irowDevTag{flex-shrink:0;border-radius:99px;padding:2px 8px;font-size:10px;line-height:14px;font-weight:600;
|
|
484
|
+
letter-spacing:0.02em;white-space:nowrap;cursor:default;user-select:none;
|
|
485
|
+
color:var(--dsw-alias-label-tertiary,#8b93a1);
|
|
486
|
+
background:color-mix(in srgb, var(--dsw-alias-brand-primary,#4f6ef7) 9%, var(--dsw-alias-bg-layer-2,#f3f4f6))}
|
|
487
|
+
.nameLink{color:inherit;text-decoration:none}
|
|
488
|
+
/* Installed-row title only. The version is a fact the row exists to state,
|
|
489
|
+
so it never truncates; the name is the variable-length part and is the
|
|
490
|
+
only thing allowed to shrink and ellipsize (#257). Scoped to this row
|
|
491
|
+
rather than added to `.nm`, which six other surfaces share. */
|
|
492
|
+
/* Wraps rather than truncates (#342, #343). A scoped package name is the
|
|
493
|
+
thing that tells two installed plugins apart — `@deepseek-ai/dsh-client-ui-…`
|
|
494
|
+
and `@dsh-external/dsh-sessi…` are both just prefixes — and an ellipsis
|
|
495
|
+
removes exactly the end that distinguishes them. Wrapping costs a line only
|
|
496
|
+
on the names long enough to need one, and the installed list is a masonry,
|
|
497
|
+
so a taller row does not push its neighbours around.
|
|
498
|
+
`anywhere` rather than `break-word`: these names have long unbroken runs
|
|
499
|
+
between the slash and the dashes, which `break-word` leaves overflowing. */
|
|
500
|
+
.irowName{display:flex;align-items:baseline;gap:6px;min-width:0;overflow:visible;text-overflow:clip;flex-wrap:wrap}
|
|
501
|
+
.irowNameText{min-width:0;overflow-wrap:anywhere;white-space:normal}
|
|
502
|
+
.irowName>.owner{flex:0 0 auto}
|
|
503
|
+
.nameLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}
|
|
504
|
+
/* Lift the entry off the tab row's bottom rule — a pill button flush on the
|
|
505
|
+
divider reads as clipped, especially when export log sits one row above on
|
|
506
|
+
narrow viewports. */
|
|
507
|
+
.opWrap{position:relative;display:inline-flex;flex-shrink:0;margin-bottom:6px}
|
|
508
|
+
.opEntry{display:inline-flex;align-items:center;gap:6px;font:inherit;font-size:12px;
|
|
509
|
+
padding:4px 10px;border-radius:7px;cursor:pointer;position:relative;white-space:nowrap;
|
|
510
|
+
color:var(--dsw-alias-label-primary,#1f2328);
|
|
511
|
+
background:var(--dsw-alias-bg-layer-1,#fff);
|
|
512
|
+
border:1px solid var(--dsw-alias-border-l3,#d9dde3)}
|
|
513
|
+
/* Idle is not news: no border, no fill, nothing competing with the tabs. */
|
|
514
|
+
.opEntryQuiet{border-color:transparent;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
515
|
+
.opEntryAlert{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
516
|
+
.opDot{position:absolute;top:-3px;right:-3px;width:8px;height:8px;border-radius:99px;
|
|
517
|
+
background:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
518
|
+
.opPanel{position:absolute;top:calc(100% + 6px);right:0;z-index:40;width:460px;max-width:86vw;
|
|
519
|
+
max-height:70vh;overflow-y:auto;border-radius:12px;
|
|
520
|
+
background:var(--dsw-alias-bg-layer-1,#fff);
|
|
521
|
+
border:1px solid var(--dsw-alias-border-l3,#d9dde3);
|
|
522
|
+
box-shadow:0 20px 52px rgba(0,0,0,.28)}
|
|
523
|
+
.opHead{display:flex;align-items:center;gap:8px;padding:9px 14px;
|
|
524
|
+
border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
525
|
+
.opPanelTitle{font-size:12.5px;font-weight:600}
|
|
526
|
+
.opCloseBtn.opCloseBtn{padding:0 6px;min-width:0;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
527
|
+
.opAggregate{padding:9px 14px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);
|
|
528
|
+
background:var(--dsw-alias-bg-layer-2,#f7f8fa)}
|
|
529
|
+
.opAggregateTop{font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}
|
|
530
|
+
.opAggregateHint{font-size:10.5px;color:var(--dsw-alias-label-tertiary,#8b93a1);margin-top:4px}
|
|
531
|
+
.opRow{display:flex;align-items:flex-start;gap:9px;padding:9px 14px;
|
|
532
|
+
border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
533
|
+
.opRow:last-child{border-bottom:none}
|
|
534
|
+
.opRowAlert{background:rgba(220,38,38,.06)}
|
|
535
|
+
.opIcon{flex-shrink:0;margin-top:1px;width:14px;display:grid;place-items:center}
|
|
536
|
+
.opQueuedIcon{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px}
|
|
537
|
+
.opMain{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
|
|
538
|
+
.opTop{display:flex;align-items:baseline;gap:6px;min-width:0}
|
|
539
|
+
.opVerb{font-size:12px;color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0}
|
|
540
|
+
.opName{font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
541
|
+
.opStatus{font-size:10.5px;line-height:16px;color:var(--dsw-alias-label-tertiary,#8b93a1);
|
|
542
|
+
overflow-wrap:anywhere}
|
|
543
|
+
.opStatusBad{color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
544
|
+
.opActions{flex-shrink:0;display:flex;align-items:center;gap:6px;margin-top:1px}
|
|
545
|
+
.opDecision{margin-top:8px;padding-top:8px;display:flex;flex-direction:column;gap:8px;
|
|
546
|
+
border-top:1px dashed var(--dsw-alias-border-l2,#e5e7eb)}
|
|
547
|
+
.opDecisionFoot{display:flex;align-items:center;gap:8px}
|
|
548
|
+
.conflictDetailsToggle{display:inline-flex;align-items:center;gap:4px;font:inherit;font-size:11px;
|
|
549
|
+
cursor:pointer;padding:2px 0;border:none;background:none;
|
|
550
|
+
color:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
551
|
+
.conflictDetailsToggle:hover{color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
552
|
+
.opEmpty{padding:30px 14px;text-align:center;font-size:12px;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
553
|
+
.opEmptyHint{font-size:11px;color:var(--dsw-alias-label-tertiary,#8b93a1);margin-top:4px}
|
|
554
|
+
/* The card keeps a terminal marker for an install that ended without
|
|
555
|
+
installing: a card that looks untouched invites pressing Install again. */
|
|
556
|
+
.cardBlockedMark{display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:11px;
|
|
557
|
+
cursor:pointer;padding:4px 9px;border-radius:7px;
|
|
558
|
+
color:var(--dsw-alias-state-error-primary,#dc2626);
|
|
559
|
+
background:rgba(220,38,38,.08);
|
|
560
|
+
border:1px solid var(--dsw-alias-state-error-primary,#dc2626)}
|
|
561
|
+
/* Destructive actions — uninstall, delete group, and the conflict swap.
|
|
562
|
+
Doubled selectors so these beat the primitive's own variant rules, the
|
|
563
|
+
same way .installBtn and .catsToggle do. */
|
|
564
|
+
.dangerBtn.dangerBtn{color:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
565
|
+
.dangerBtn.dangerBtn:hover:not(:disabled){background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}
|
|
566
|
+
.dangerArmed.dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}
|
|
567
|
+
.retryBtn{margin-top:4px}
|
|
568
|
+
.irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);
|
|
569
|
+
border-radius:10px;padding:12px 14px;display:flex;flex-direction:column;gap:10px;min-width:0}
|
|
570
|
+
/* Everything packs left. Pushing the trailing controls right — whether with
|
|
571
|
+
justify-content or an auto margin on the tag — made the switch's position
|
|
572
|
+
depend on whether its neighbour rendered 更新 or 已是最新, so the switch
|
|
573
|
+
sat at a different x on every row. Packed left, state and switch land at
|
|
574
|
+
the same place regardless of what follows them. */
|
|
575
|
+
.irowActions{display:flex;align-items:center;justify-content:flex-start;gap:8px;flex-wrap:wrap;min-width:0}
|
|
576
|
+
/* The status tag and Uninstall travel together: nowrap so they never split
|
|
577
|
+
across lines, min-width:0 so the group can still shrink (the tag inside
|
|
578
|
+
ellipsizes) rather than forcing the whole row wider (#242). */
|
|
579
|
+
.irowTrailing{display:inline-flex;align-items:center;gap:8px;flex-wrap:nowrap;flex-shrink:0;min-width:0}
|
|
580
|
+
.irowTrailing .metaTag{flex-shrink:1;min-width:0}
|
|
581
|
+
.irowTrailing .metaTagAction{flex-shrink:0}
|
|
582
|
+
.irowMissing{filter:grayscale(1);opacity:.5}
|
|
583
|
+
.irow>.src,.irow>.owner{white-space:nowrap;flex-shrink:0}
|
|
584
|
+
.irow button{white-space:nowrap;flex-shrink:0}
|
|
585
|
+
.tabSearchRow{display:flex;padding:0 4px 6px}
|
|
586
|
+
.tabSearch{width:100%}
|
|
587
|
+
/* Buttons sharing the row with a description must keep one line (#26, thanks @yanshuai2002) */
|
|
588
|
+
.spec{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;overflow-wrap:anywhere;min-width:0}
|
|
589
|
+
.specTag{display:inline-flex;align-items:center;height:16px;padding:0 5px;border-radius:4px;font-size:10px;font-weight:600;white-space:nowrap;flex-shrink:0}
|
|
590
|
+
.specTagGit{color:#0b7285;background:rgba(18,163,196,.12)}
|
|
591
|
+
.specTagFile{color:#b07d1b;background:rgba(240,180,41,.14)}
|
|
592
|
+
.backupCheckList .grow{flex:1 1 70%;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
593
|
+
.backupCheckList .spec{flex:0 1 30%;max-width:30%;text-align:right;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
|
594
|
+
/* A row, with room between the two buttons. `.err` sets break-all for long
|
|
595
|
+
pnpm paths, which also broke short button labels mid-word once a button
|
|
596
|
+
sat inside it (#410). */
|
|
597
|
+
.staleAction{margin-top:8px;display:flex;flex-wrap:wrap;gap:8px;align-items:center;word-break:normal}
|
|
598
|
+
.pct{font-weight:600;font-size:11px;flex-shrink:0;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
599
|
+
/* Single-line pager: the host settings dialog's content column is ~556px
|
|
600
|
+
(800px panel − 188px section nav − 48px options padding − 8px body
|
|
601
|
+
padding), so 上一页/下一页 + the numbered window + 第x/y页 + 每页n must
|
|
602
|
+
share ONE row. nowrap is safe because pageItems caps the numbered window
|
|
603
|
+
(five buttons max, 1 and N always present — the pager had no room for the
|
|
604
|
+
«/» skip glyphs: even the compact set measures ~60px over the column in
|
|
605
|
+
English, and a centered overflow clips them at the scroll edge). Compact
|
|
606
|
+
8px paddings (primitives sm uses 10px) keep the row inside the column.
|
|
607
|
+
Below 544px the pager falls back to two centered lines instead of
|
|
608
|
+
squeezing buttons. */
|
|
609
|
+
.pager{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:16px 0 4px;flex-wrap:nowrap}
|
|
610
|
+
.pagerPages{display:flex;align-items:center;justify-content:center;gap:4px;flex-wrap:nowrap;flex:1;min-width:0}
|
|
611
|
+
.pagerMeta{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;flex-shrink:0}
|
|
612
|
+
.pagerPages button,.pagerMeta button{padding:0 8px;min-width:0}
|
|
613
|
+
.pageEllipsis{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:12px;padding:0 1px}
|
|
614
|
+
.pageInfo{font-size:12px;color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap}
|
|
615
|
+
@container (max-width:544px){.pager{flex-wrap:wrap;justify-content:center;gap:8px}.pagerPages{flex-basis:100%}}
|
|
616
|
+
/* #60: per-plugin enable/disable switches, deprecation badges, custom groups */
|
|
617
|
+
.depBadge{border:1px solid var(--dsw-alias-state-warn-primary,#b45309);color:var(--dsw-alias-state-warn-primary,#b45309);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px;font-weight:600;white-space:nowrap;margin-left:6px}
|
|
618
|
+
.deprecate{font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309);background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;padding:8px 10px;margin:0}
|
|
619
|
+
.deprecate a{color:var(--dsw-alias-state-warn-primary,#b45309);text-decoration:underline}
|
|
620
|
+
.deprecate .src{margin-left:8px}
|
|
621
|
+
.depLine{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
622
|
+
.switch{position:relative;width:38px;height:22px;border-radius:99px;border:1px solid var(--dsw-alias-border-l2,#d9dde3);background:var(--dsw-alias-bg-layer-2,#e5e7eb);cursor:pointer;padding:0;flex-shrink:0;transition:background .15s ease,border-color .15s ease}
|
|
623
|
+
.switchOn{background:var(--dsw-alias-state-success-primary,#16a34a);border-color:var(--dsw-alias-state-success-primary,#16a34a)}
|
|
624
|
+
.switchMixed{background:var(--dsw-alias-state-warn-primary,#b45309);border-color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
625
|
+
.switchKnob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:99px;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:left .15s ease}
|
|
626
|
+
.switchOn .switchKnob,.switchMixed .switchKnob{left:18px}
|
|
627
|
+
.switch:disabled{opacity:.5;cursor:default}
|
|
628
|
+
.viewBar{display:flex;gap:2px;align-items:center;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:2px;margin-bottom:12px;width:fit-content}
|
|
629
|
+
.viewBtn{border:none;background:none;font:inherit;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#6b7280);padding:4px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}
|
|
630
|
+
.viewBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
631
|
+
.viewOn{background:var(--dsw-alias-bg-layer-2,#eef0f4);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}
|
|
632
|
+
.groupRow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:12px 14px;margin-bottom:10px}
|
|
633
|
+
.groupHead{display:flex;align-items:center;gap:10px;min-width:0}
|
|
634
|
+
.groupName{font-weight:600;font-size:13px;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
635
|
+
.groupActions{display:flex;align-items:center;gap:6px;flex-shrink:0}
|
|
636
|
+
.groupMembers{display:flex;flex-direction:column;gap:6px;margin-top:10px}
|
|
637
|
+
.groupMember{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,#f7f8fa);font-size:12px;line-height:18px}
|
|
638
|
+
.groupMember .nm{font-size:12px;flex:1;min-width:0}
|
|
639
|
+
.groupAddPanel{display:flex;flex-direction:column;gap:6px;margin-top:10px;padding-top:10px;border-top:1px dashed var(--dsw-alias-border-l2,#e5e7eb)}
|
|
640
|
+
.groupCreate{display:flex;align-items:center;gap:8px;margin-bottom:10px}
|
|
641
|
+
.inlineInput{flex:1;min-width:120px}
|
|
642
|
+
.assignRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
643
|
+
.assignSelect{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);border-radius:6px;padding:3px 6px;font:inherit;font-size:12px;line-height:18px}
|
|
644
|
+
.groupHint{font-size:11px;color:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
645
|
+
.sectAction{margin:14px 2px 8px;font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#6b7280);display:flex;align-items:center;gap:8px}
|
|
646
|
+
|
|
647
|
+
/* #61: profile backup & restore */
|
|
648
|
+
.backupGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}
|
|
649
|
+
.backupCard{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:10px}
|
|
650
|
+
.backupCard h3{font-size:14px;margin:0}
|
|
651
|
+
.backupCard p{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#6b7280);margin:0}
|
|
652
|
+
.backupActions{display:flex;gap:8px;flex-wrap:wrap;position:relative}
|
|
653
|
+
.hiddenFile{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
|
|
654
|
+
.backupInput{width:100%;box-sizing:border-box}
|
|
655
|
+
.backupCheck{display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer}
|
|
656
|
+
.backupWarn{color:var(--dsw-alias-state-warn-primary,#b45309)!important;font-size:12px;line-height:18px;margin:0}
|
|
657
|
+
.backupMessage{grid-column:1/-1;font-size:12px;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
658
|
+
.backupCheckList{display:flex;flex-direction:column;gap:6px;max-height:260px;overflow-y:auto;margin:10px 0 4px;padding-right:4px}
|
|
659
|
+
.backupCheckList .backupCheck{justify-content:space-between;gap:8px}
|
|
660
|
+
|
|
661
|
+
/* #98: Diagnostics tab — profile composition check report.
|
|
662
|
+
The page fills the settings dialog's content height and scrolls INSIDE
|
|
663
|
+
itself (overflow-y:auto), so the outer dialog never overflows and its own
|
|
664
|
+
overflow-y:auto stays silent (no outer scrollbar). */
|
|
665
|
+
.diagPage{display:flex;flex-direction:column;gap:12px;height:100%;min-height:0;overflow-y:auto}
|
|
666
|
+
.diagSummary{display:flex;flex-wrap:wrap;align-items:center;gap:12px;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:10px 14px;font-size:12px}
|
|
667
|
+
.diagSummaryItem{display:inline-flex;align-items:center;gap:6px;color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap}
|
|
668
|
+
.diagSummaryMeta{color:var(--dsw-alias-label-tertiary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
669
|
+
.diagSection{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:8px}
|
|
670
|
+
.diagSection h3{font-size:13px;font-weight:600;margin:0;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
671
|
+
.diagCount{font-size:11px;color:var(--dsw-alias-label-tertiary,#9ca3af);font-weight:400}
|
|
672
|
+
.diagEmpty{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:12px;padding:8px 0}
|
|
673
|
+
.diagBundle{display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--dsw-alias-border-l2,#f0f1f3);padding-top:8px}
|
|
674
|
+
.diagBundle:first-of-type{border-top:none;padding-top:0}
|
|
675
|
+
.diagRow{display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:12px;line-height:18px;min-width:0}
|
|
676
|
+
.diagMeta{display:flex;align-items:baseline;gap:8px;font-size:12px;min-width:0}
|
|
677
|
+
.diagKey{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:11px;flex-shrink:0;min-width:64px}
|
|
678
|
+
.diagVal{font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);font-family:ui-monospace,Menlo,monospace;overflow-wrap:anywhere;min-width:0}
|
|
679
|
+
.diagIndex{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;border-radius:9px;background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;font-weight:600;flex-shrink:0}
|
|
680
|
+
.diagArrow{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:12px;flex-shrink:0}
|
|
681
|
+
.diagBadgeOfficial{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-size:11px;font-weight:600;flex-shrink:0}
|
|
682
|
+
.diagBadgeCommunity{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;flex-shrink:0}
|
|
683
|
+
.diagBadgeShadow{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff;font-size:11px;font-weight:600;flex-shrink:0}
|
|
684
|
+
.diagBadgeWarn{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-state-warn-primary,#b45309);color:#fff;font-size:11px;font-weight:600;flex-shrink:0;white-space:nowrap}
|
|
685
|
+
.diagBadgeInfo{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;flex-shrink:0;white-space:nowrap}
|
|
686
|
+
.diagList{display:flex;flex-direction:column;gap:6px}
|
|
687
|
+
|
|
688
|
+
/* #98 supplement: section overview line, overrides/orphans rows, drag ordering */
|
|
689
|
+
.sectionOverview{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#8b93a1);padding:2px 0 6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
|
|
690
|
+
.diagAlert{color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
691
|
+
.ovRow{display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;padding:6px 10px;min-width:0}
|
|
692
|
+
.ovArrow{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:12px;flex-shrink:0}
|
|
693
|
+
.ovByTag{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-size:11px;font-weight:600;flex-shrink:0;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
694
|
+
.ovFrom{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
695
|
+
.orphRow{display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;padding:6px 10px;min-width:0}
|
|
696
|
+
.orphBadge{display:inline-flex;align-items:center;padding:0 8px;height:18px;border-radius:9px;background:var(--dsw-alias-state-warn-primary,#b45309);color:#fff;font-size:11px;font-weight:600;flex-shrink:0;white-space:nowrap}
|
|
697
|
+
.dragHandle{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;color:var(--dsw-alias-label-tertiary,#9ca3af);cursor:grab;flex-shrink:0;user-select:none;font-size:12px;line-height:20px}
|
|
698
|
+
.dragOver{outline:2px dashed var(--dsw-alias-brand-primary,#4f6ef7);outline-offset:2px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,#f0f2f8)}
|
|
699
|
+
.dragging{opacity:.45;background:var(--dsw-alias-bg-layer-2,#f3f4f6)}
|
|
700
|
+
|
|
701
|
+
/* #98 phase 3: collapsible snapshots & rollback panel */
|
|
702
|
+
.collapseHead{display:flex;align-items:center;gap:8px;border:none;background:none;font:inherit;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);padding:0;cursor:pointer;text-align:left;width:100%}
|
|
703
|
+
.collapseIcon{display:inline-flex;flex-shrink:0;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
704
|
+
.collapseTitle{flex:1;min-width:0}
|
|
705
|
+
.collapseBody{display:flex;flex-direction:column;gap:10px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2,#f0f1f3);margin-top:8px;min-width:0;overflow-wrap:anywhere}
|
|
706
|
+
.orderPanel{display:flex;flex-direction:column;gap:10px;min-width:0}
|
|
707
|
+
.panelNote{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#6b7280);margin:0}
|
|
708
|
+
.panelActions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
709
|
+
.presetList{display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
710
|
+
.presetRow{display:flex;flex-direction:column;align-items:stretch;gap:6px;background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;padding:8px 10px;font-size:12px;line-height:18px;min-width:0;max-width:100%}
|
|
711
|
+
.presetName{font-weight:600;font-size:13px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
712
|
+
.snapList{display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
713
|
+
.snapRow{display:flex;flex-direction:column;align-items:stretch;gap:8px;background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;padding:10px 12px;font-size:12px;line-height:18px}
|
|
714
|
+
.snapMeta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-width:0}
|
|
715
|
+
.snapConfirmText{font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309);margin:0}
|
|
716
|
+
.confirmRow{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
|
|
717
|
+
|
|
718
|
+
/* #98: AI-fix clipboard failure fallback — the built prompt as a selectable
|
|
719
|
+
text block, so the user can still copy it by hand. */
|
|
720
|
+
.fixFallback{display:flex;flex-direction:column;gap:6px;margin:8px 0}
|
|
721
|
+
.fixFallbackText{width:100%;box-sizing:border-box;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:16px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:6px;padding:8px 10px;resize:vertical;white-space:pre-wrap;word-break:break-all}
|
|
722
|
+
/* Plugin-configuration card (dsh >= rc.7). Drawn here rather than imported:
|
|
723
|
+
the section's own cards live in a package whose value import would fail
|
|
724
|
+
the client bundle-purity gate, so a card owns its appearance. Matches the
|
|
725
|
+
chrome of the built-in cards beside it. */
|
|
726
|
+
/* The plugin configuration page dispatches a card and lets the plugin draw
|
|
727
|
+
it ("a plugin that ships a browser half owns its own card"). These mirror
|
|
728
|
+
the host's own PluginCard one for one — same tokens, same sizes — so the
|
|
729
|
+
market's card is indistinguishable from the rows above and below it. An
|
|
730
|
+
earlier version invented a flat, always-open box and stood out badly. */
|
|
731
|
+
.setCard{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-3,#fff);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
|
|
732
|
+
.setCard:hover{border-color:var(--dsw-alias-label-dimmed,#c8ccd4)}
|
|
733
|
+
.setCardOpen{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-color:var(--dsw-alias-label-dimmed,#c8ccd4)}
|
|
734
|
+
.setHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
735
|
+
.setHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4f6ef7);outline-offset:-2px}
|
|
736
|
+
.setHeadText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
737
|
+
.setName{color:var(--dsw-alias-label-primary,#1f2328);font-size:15px;font-weight:600;line-height:1.4}
|
|
738
|
+
.setDesc{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:13px;line-height:1.5}
|
|
739
|
+
.setChevron{color:var(--dsw-alias-label-tertiary,#8b93a1);flex:none;display:inline-flex;transition:transform .16s}
|
|
740
|
+
.setChevronOpen{transform:rotate(180deg)}
|
|
741
|
+
.setBody{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);margin:0 16px;padding-bottom:8px}
|
|
742
|
+
.setRow{display:flex;align-items:center;gap:12px;padding:12px 0}
|
|
743
|
+
.setRow + .setRow{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
744
|
+
.setLabelBox{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}
|
|
745
|
+
.setLabel{font-size:13px;line-height:20px}
|
|
746
|
+
.setHint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
747
|
+
/* The confirm block is deliberately NOT a modal: the consequence copy has to
|
|
748
|
+
stay readable while the checkbox is being decided. */
|
|
749
|
+
.setConfirm{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);display:flex;flex-direction:column;gap:8px;padding:12px 0 4px}
|
|
750
|
+
.setCheck{display:flex;align-items:center;gap:8px;font-size:12px;line-height:18px;cursor:pointer}
|
|
751
|
+
.setActions{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}
|
|
752
|
+
.setInlineActions{display:flex;align-items:center;justify-content:flex-end;gap:6px;flex-wrap:wrap}
|
|
753
|
+
.setProxyEditor{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);display:flex;align-items:flex-end;flex-wrap:wrap;gap:10px;padding:12px 0}
|
|
754
|
+
.setProxyLabel{display:flex;flex:1;min-width:180px;flex-direction:column;gap:5px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
755
|
+
.setProxyInput{box-sizing:border-box;width:100%;min-width:0;border:1px solid var(--dsw-alias-border-l2,#d1d5db);border-radius:7px;background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-primary,#1f2328);font:inherit;font-size:12px;line-height:18px;padding:6px 8px}
|
|
756
|
+
.setProxyInput:focus{border-color:var(--dsw-alias-brand-primary,#4f6ef7);outline:2px solid color-mix(in srgb,var(--dsw-alias-brand-primary,#4f6ef7) 18%,transparent)}
|
|
757
|
+
.setDanger{color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
758
|
+
/* A two-state segmented control rather than a switch: "stable / beta" is a
|
|
759
|
+
choice between two named things, and a toggle would force one of them to
|
|
760
|
+
be the unlabelled "off". */
|
|
761
|
+
.setSeg{display:inline-flex;flex-shrink:0;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:2px;gap:2px}
|
|
762
|
+
.setSegBtn{font:inherit;font-size:12px;line-height:18px;padding:3px 10px;border:none;border-radius:6px;background:0 0;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer}
|
|
763
|
+
.setSegBtn:disabled{cursor:default;opacity:.5}
|
|
764
|
+
.setSegOn{background:var(--dsw-alias-bg-layer-2,#eef0f4);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}
|
|
765
|
+
.setBetaTag{margin-left:6px;padding:0 6px;border-radius:9px;font-size:11px;font-weight:600;line-height:17px;background:var(--dsw-alias-bg-module-platform,#eef0f4);color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
766
|
+
|
|
767
|
+
/* ---------------------------------------------------------- comments */
|
|
768
|
+
/* Card footer entry point. Styled as the quiet sibling of .src, not as a
|
|
769
|
+
button: it competes with Install for attention otherwise, and Install is
|
|
770
|
+
what the card is for. */
|
|
771
|
+
.commentsLink{background:none;border:0;padding:0;cursor:pointer;font-size:11px;line-height:16px;white-space:nowrap;color:var(--dsw-alias-label-secondary,#9ca3af)}
|
|
772
|
+
.commentsLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
773
|
+
.footActions{display:inline-flex;align-items:center;gap:8px;flex-shrink:0}
|
|
774
|
+
.favoriteBtn{background:none;border:0;padding:0;cursor:pointer;display:inline-flex;align-items:center;color:var(--dsw-alias-label-secondary,#9ca3af)}
|
|
775
|
+
.favoriteBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
776
|
+
.favoriteOn{color:var(--dsw-alias-brand-primary,#4f6ef7)}
|
|
777
|
+
.commentsNote{font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary,#9ca3af);margin:0 0 8px}
|
|
778
|
+
.commentsGithubPrompt{padding:10px;margin:0 0 10px;border:1px solid color-mix(in srgb,var(--dsw-alias-brand-primary,#4f6ef7) 24%,transparent);border-radius:8px;background:color-mix(in srgb,var(--dsw-alias-brand-primary,#4f6ef7) 6%,transparent)}
|
|
779
|
+
.commentsGithubHint{font-size:12px;line-height:17px;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}
|
|
780
|
+
.commentsGithubAction{display:inline-flex;align-items:center;justify-content:center;min-height:28px;box-sizing:border-box;padding:5px 10px;border:1px solid var(--dsw-alias-brand-primary,#4f6ef7);border-radius:6px;color:var(--dsw-alias-brand-primary,#4f6ef7);font-size:12px;font-weight:600;line-height:16px;text-decoration:none}
|
|
781
|
+
.commentsGithubAction:hover{background:color-mix(in srgb,var(--dsw-alias-brand-primary,#4f6ef7) 9%,transparent)}
|
|
782
|
+
.commentsStatus{font-size:12px;color:var(--dsw-alias-label-secondary,#9ca3af);margin:0}
|
|
783
|
+
.commentsStatus:empty{display:none}
|
|
784
|
+
.commentsError{font-size:12px;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}
|
|
785
|
+
.commentsFail{display:flex;flex-direction:column;align-items:flex-start;gap:8px;padding:12px 0}
|
|
786
|
+
/* The iframe reports its own height once giscus has the thread. Holding room
|
|
787
|
+
until then keeps the modal from snapping open to full size a beat after the
|
|
788
|
+
reader has already started looking at it. */
|
|
789
|
+
/* The host owns the dialog's width and does not let a plugin change it, so the
|
|
790
|
+
thread is fitted to that width rather than overflowing a box that clips.
|
|
791
|
+
giscus lays out fine this narrow — it is the width it uses on phones. */
|
|
792
|
+
.commentsMount{width:100%;min-height:240px;max-height:56vh;overflow:auto}
|
|
793
|
+
.commentsMount iframe{width:100%}
|
|
794
|
+
/* Update-notes entry + dialog (#294). The entry is a text button, not a
|
|
795
|
+
Button component: it must read as content in the row's flow rather than as
|
|
796
|
+
an action competing with 更新/卸载 on the trailing edge. */
|
|
797
|
+
.notesLink{display:block;background:0 0;border:none;padding:0;margin:2px 0 0;font:inherit;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;text-align:left}
|
|
798
|
+
.notesLink:hover{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
799
|
+
.notesBody{display:flex;flex-direction:column;gap:8px;min-width:0}
|
|
800
|
+
/* 弹窗内的层级:范围行弱化、区块标题小而重、正文有承载面 */
|
|
801
|
+
.notesRange{display:flex;align-items:center;gap:8px;font-size:12px;line-height:18px;margin-bottom:4px}
|
|
802
|
+
.notesArrow{color:var(--dsw-alias-label-tertiary,#8b93a1)}
|
|
803
|
+
.notesMeta{font-size:12px;line-height:18px;letter-spacing:.02em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#6b7280);display:flex;gap:6px;flex-wrap:wrap;align-items:baseline;margin-top:12px}
|
|
804
|
+
.notesPre{margin:0;white-space:pre-wrap;word-break:break-word;font-size:13px;line-height:20px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,#eef0f4);border-radius:8px;max-height:40vh;overflow:auto}
|
|
805
|
+
.notesList{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px;font-size:13px;line-height:20px}
|
|
806
|
+
/* markdown轻渲染的层级:标题、段落、行内代码 */
|
|
807
|
+
.notesRendered{display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
808
|
+
.notesH{font-size:13px;font-weight:650;line-height:20px;margin-top:6px}
|
|
809
|
+
.notesP{font-size:13px;line-height:20px}
|
|
810
|
+
.notesCode{font-family:var(--dsw-alias-font-mono,ui-monospace,monospace);font-size:12px;padding:1px 5px;border-radius:5px;background:var(--dsw-alias-bg-layer-2,#eef0f4)}
|
|
811
|
+
/* 列表行:日期一列弱化对齐,内容一列自适应换行 */
|
|
812
|
+
.notesList{margin:0;padding:0;list-style:none;display:flex;flex-direction:column}
|
|
813
|
+
.notesRow{display:flex;gap:12px;align-items:baseline;padding:6px 0;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
814
|
+
.notesRow:last-child{border-bottom:none}
|
|
815
|
+
.notesDate{flex-shrink:0;color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px;font-variant-numeric:tabular-nums}
|
|
816
|
+
.notesMsg{min-width:0;word-break:break-word;font-size:13px;line-height:20px}
|
|
817
|
+
/* commit行:消息限高两行,短sha右对齐可点 */
|
|
818
|
+
.notesMsg{min-width:0;word-break:break-word;font-size:13px;line-height:20px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
|
|
819
|
+
.notesSha{margin-left:auto;flex-shrink:0;font-size:12px;color:var(--dsw-alias-label-secondary,#6b7280);text-decoration:none;font-variant-numeric:tabular-nums}
|
|
820
|
+
.notesSha:hover{color:var(--dsw-alias-label-primary,#1f2328);text-decoration:underline}
|
|
821
|
+
.notesVer{color:inherit;text-decoration:none;font-weight:600}
|
|
822
|
+
.notesVer:hover{text-decoration:underline}
|
|
823
|
+
|
|
824
|
+
/* Git -> npm source confirmation: make the from/to change readable without turning it into an error banner. */
|
|
825
|
+
.migrationSources{display:flex;flex-direction:column;gap:8px;margin-top:4px}
|
|
826
|
+
.migrationSource{display:flex;flex-direction:column;gap:4px;padding:10px 12px;border:1px solid var(--dsw-alias-border-default,#e5e7eb);border-radius:8px}
|
|
827
|
+
.migrationSource code{font-size:12px;font-family:ui-monospace,Menlo,monospace;overflow-wrap:anywhere}
|
|
828
|
+
.migrationLabel{font-size:11px;color:var(--dsw-alias-label-secondary,#9ca3af)}
|
|
829
|
+
.migrationArrow{text-align:center;color:var(--dsw-alias-label-secondary,#9ca3af)}
|
|
830
|
+
.migrationWarning{display:flex;align-items:flex-start;gap:6px;margin:12px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)}
|