dsh-dock 0.9.0 → 0.9.2
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/client.js +212 -119
- package/features/animation/view.jsx +39 -39
- package/features/balance/view.js +19 -17
- package/features/games/games/breakout.jsx +3 -3
- package/features/games/games/racer.jsx +1 -1
- package/features/games/games/tetris.jsx +12 -6
- package/features/games/games/xiangqi.jsx +98 -26
- package/features/games/view.jsx +4 -4
- package/features/mobile-relay/host.js +53 -0
- package/features/mobile-relay/view.jsx +1 -1
- package/features/modelconfig/view.js +4 -4
- package/features/tokenlog/view.jsx +8 -8
- package/package.json +1 -1
- package/src/client.jsx +20 -7
|
@@ -37,6 +37,21 @@ const XIANGQI_LINES = (() => {
|
|
|
37
37
|
return parts;
|
|
38
38
|
})();
|
|
39
39
|
|
|
40
|
+
// 传统棋谱着法(如"炮二平五"/"馬8进7"):红方用中文数字、黑方用阿拉伯数字,各自从己方右侧数起。
|
|
41
|
+
const CN_NUMS = ["一", "二", "三", "四", "五", "六", "七", "八", "九"];
|
|
42
|
+
function moveNotation(board, fr, fc, tr, tc) {
|
|
43
|
+
const p = board[fr][fc];
|
|
44
|
+
if (!p) return "";
|
|
45
|
+
const red = p.c === "r";
|
|
46
|
+
const num = (n) => (red ? CN_NUMS[n - 1] : String(n));
|
|
47
|
+
const file = (c) => (red ? 9 - c : c + 1);
|
|
48
|
+
const forward = red ? tr < fr : tr > fr;
|
|
49
|
+
const verb = fr === tr ? "平" : (forward ? "进" : "退");
|
|
50
|
+
// 直行棋子进/退记步数;斜行棋子(马/象/士)进/退记目标线位。
|
|
51
|
+
const to = fr === tr || p.t === "h" || p.t === "e" || p.t === "a" ? file(tc) : Math.abs(tr - fr);
|
|
52
|
+
return pieceChar(p) + num(file(fc)) + verb + num(to);
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
export function XiangqiGame(props) {
|
|
41
56
|
const [board, setBoard] = useState(initialBoard);
|
|
42
57
|
const [turn, setTurn] = useState("r");
|
|
@@ -45,6 +60,11 @@ export function XiangqiGame(props) {
|
|
|
45
60
|
const [over, setOver] = useState(null); // 'r' | 'b' | 'draw'
|
|
46
61
|
const [check, setCheck] = useState(false);
|
|
47
62
|
const [last, setLast] = useState(null);
|
|
63
|
+
// 上一手的起点/终点/棋谱文本:起点画虚线圈、终点画红点,棋谱行让玩家看清对方走了什么。
|
|
64
|
+
const [lastMove, setLastMove] = useState(null); // { fr, fc, tr, tc, text }
|
|
65
|
+
const [moveLog, setMoveLog] = useState([]); // [{ side, text }] 双方着法流水
|
|
66
|
+
// 跳跃动画:moving = { fr, fc, tr, tc, piece, capture },动画结束后落定为普通棋子。
|
|
67
|
+
const [moving, setMoving] = useState(null);
|
|
48
68
|
const [thinking, setThinking] = useState(false);
|
|
49
69
|
const [history, setHistory] = useState([]);
|
|
50
70
|
// 已出现过的局面指纹:AI 搜索时排除重复局面,杜绝"反复将军无限循环"。
|
|
@@ -57,44 +77,78 @@ export function XiangqiGame(props) {
|
|
|
57
77
|
const overRef = useRef(over); overRef.current = over;
|
|
58
78
|
const thinkingRef = useRef(thinking); thinkingRef.current = thinking;
|
|
59
79
|
const historyRef = useRef(history); historyRef.current = history;
|
|
80
|
+
// 动画期间锁输入与 AI 回合,结束后调用 settle 落定真正的棋盘状态。
|
|
81
|
+
const movingRef = useRef(false);
|
|
82
|
+
const pendingRef = useRef(null);
|
|
83
|
+
|
|
84
|
+
// 起跳动画:先在起点渲染飞行棋子,260ms 后落地并把棋盘推进到新局面。
|
|
85
|
+
const animTimerRef = useRef(null);
|
|
86
|
+
const animateMove = useCallback((b, fr, fc, tr, tc, after) => {
|
|
87
|
+
// 被移动/被吃的棋子须从旧盘(boardRef)读取:b 已是落子后的新盘。
|
|
88
|
+
const piece = boardRef.current[fr][fc];
|
|
89
|
+
const capture = !!boardRef.current[tr][tc];
|
|
90
|
+
pendingRef.current = { board: b, after };
|
|
91
|
+
movingRef.current = true;
|
|
92
|
+
setSel(null); setTargets([]);
|
|
93
|
+
setMoving({ fr, fc, tr, tc, piece, capture });
|
|
94
|
+
playSfx(capture ? "capture" : "step");
|
|
95
|
+
if (animTimerRef.current) clearTimeout(animTimerRef.current);
|
|
96
|
+
animTimerRef.current = setTimeout(() => {
|
|
97
|
+
animTimerRef.current = null;
|
|
98
|
+
const pend = pendingRef.current;
|
|
99
|
+
pendingRef.current = null;
|
|
100
|
+
movingRef.current = false;
|
|
101
|
+
setMoving(null);
|
|
102
|
+
if (pend) {
|
|
103
|
+
const text = moveNotation(boardRef.current, fr, fc, tr, tc);
|
|
104
|
+
boardRef.current = pend.board;
|
|
105
|
+
setBoard(pend.board);
|
|
106
|
+
setLastMove({ fr, fc, tr, tc, text });
|
|
107
|
+
setMoveLog((log) => log.concat({ side: piece.c, text }));
|
|
108
|
+
setLast([tr, tc]);
|
|
109
|
+
if (pend.after) pend.after();
|
|
110
|
+
}
|
|
111
|
+
}, 260);
|
|
112
|
+
}, []);
|
|
60
113
|
|
|
61
114
|
const restart = useCallback(() => {
|
|
115
|
+
if (animTimerRef.current) { clearTimeout(animTimerRef.current); animTimerRef.current = null; }
|
|
116
|
+
pendingRef.current = null; movingRef.current = false;
|
|
62
117
|
const b = initialBoard();
|
|
63
118
|
boardRef.current = b;
|
|
64
119
|
setBoard(b); setTurn("r"); setSel(null); setTargets([]); setOver(null); setCheck(false); setLast(null); setThinking(false); setHistory([]);
|
|
65
|
-
setRepKeys([posKey(b)]);
|
|
120
|
+
setLastMove(null); setMoveLog([]); setMoving(null); setRepKeys([posKey(b)]);
|
|
66
121
|
}, []);
|
|
67
122
|
|
|
68
123
|
const undo = useCallback(() => {
|
|
69
|
-
if (thinkingRef.current || overRef.current || turnRef.current !== "r") return;
|
|
124
|
+
if (thinkingRef.current || overRef.current || turnRef.current !== "r" || movingRef.current) return;
|
|
70
125
|
const h = historyRef.current;
|
|
71
126
|
if (h.length < 2) return;
|
|
72
127
|
const hist = h.slice(0, -2);
|
|
73
128
|
const b = hist.length ? cloneBoard(hist[hist.length - 1]) : initialBoard();
|
|
74
129
|
boardRef.current = b;
|
|
75
|
-
setBoard(b); setHistory(hist); setSel(null); setTargets([]); setLast(null); setOver(null);
|
|
130
|
+
setBoard(b); setHistory(hist); setSel(null); setTargets([]); setLast(null); setLastMove(null); setMoveLog((prevLog) => prevLog.slice(0, -2)); setOver(null);
|
|
76
131
|
setCheck(inCheck(b, "r"));
|
|
77
132
|
// 重建局面指纹(历史各局面 + 当前)。
|
|
78
133
|
setRepKeys(hist.map(posKey).concat(posKey(b)));
|
|
79
134
|
}, []);
|
|
80
135
|
|
|
81
136
|
const moveRed = useCallback((tr, tc) => {
|
|
82
|
-
if (turnRef.current !== "r" || overRef.current || !sel) return;
|
|
137
|
+
if (turnRef.current !== "r" || overRef.current || !sel || movingRef.current) return;
|
|
83
138
|
const fr = sel[0], fc = sel[1];
|
|
84
|
-
if (boardRef.current[tr][tc]) playSfx("capture"); else playSfx("step");
|
|
85
139
|
const nb = applyMove(boardRef.current, fr, fc, tr, tc);
|
|
86
140
|
setHistory((h) => [...h, boardRef.current]);
|
|
87
|
-
boardRef.current = nb;
|
|
88
141
|
pushKey(nb);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
142
|
+
animateMove(nb, fr, fc, tr, tc, () => {
|
|
143
|
+
// 动画落地后再移交回合:让 AI 的 useEffect 依赖到最新 turn 才启动搜索。
|
|
144
|
+
setTurn("b");
|
|
145
|
+
if (!hasLegalMoves(nb, "b")) { setOver("r"); setCheck(false); }
|
|
146
|
+
else setCheck(inCheck(nb, "b"));
|
|
147
|
+
});
|
|
148
|
+
}, [sel, pushKey, animateMove]);
|
|
95
149
|
|
|
96
150
|
const select = useCallback((r, c) => {
|
|
97
|
-
if (overRef.current || turnRef.current !== "r" || thinkingRef.current) return;
|
|
151
|
+
if (overRef.current || turnRef.current !== "r" || thinkingRef.current || movingRef.current) return;
|
|
98
152
|
const p = boardRef.current[r][c];
|
|
99
153
|
if (p && p.c === "r") {
|
|
100
154
|
setSel([r, c]);
|
|
@@ -106,9 +160,9 @@ export function XiangqiGame(props) {
|
|
|
106
160
|
setSel(null); setTargets([]);
|
|
107
161
|
}, [sel, targets, moveRed]);
|
|
108
162
|
|
|
109
|
-
// AI
|
|
163
|
+
// AI 回合(执黑):先想棋(thinking 期间玩家能看到"AI 思考中"),落子走同款跳跃动画。
|
|
110
164
|
useEffect(() => {
|
|
111
|
-
if (turn !== "b" || over || props.paused) return;
|
|
165
|
+
if (turn !== "b" || over || props.paused || movingRef.current) return;
|
|
112
166
|
const t = setTimeout(() => {
|
|
113
167
|
// 局面出现次数表:AI 优先走新局面;全部重复时也倾向去往出现最少的局面。
|
|
114
168
|
const seenCounts = new Map();
|
|
@@ -116,20 +170,20 @@ export function XiangqiGame(props) {
|
|
|
116
170
|
const mv = bestMove(boardRef.current, "b", 3, seenCounts);
|
|
117
171
|
if (mv) {
|
|
118
172
|
const [fr, fc, tr, tc] = mv;
|
|
119
|
-
if (boardRef.current[tr][tc]) playSfx("capture"); else playSfx("step");
|
|
120
173
|
const nb = applyMove(boardRef.current, fr, fc, tr, tc);
|
|
121
174
|
setHistory((h) => [...h, boardRef.current]);
|
|
122
|
-
boardRef.current = nb;
|
|
123
175
|
pushKey(nb);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
176
|
+
animateMove(nb, fr, fc, tr, tc, () => {
|
|
177
|
+
setTurn("r");
|
|
178
|
+
if (!hasLegalMoves(nb, "r")) { setOver("b"); setCheck(false); }
|
|
179
|
+
else setCheck(inCheck(nb, "r"));
|
|
180
|
+
});
|
|
127
181
|
} else setOver("r");
|
|
128
182
|
setThinking(false);
|
|
129
|
-
},
|
|
183
|
+
}, 420);
|
|
130
184
|
setThinking(true);
|
|
131
185
|
return () => clearTimeout(t);
|
|
132
|
-
}, [turn, over, props.paused, repKeys, pushKey]);
|
|
186
|
+
}, [turn, over, props.paused, repKeys, pushKey, animateMove]);
|
|
133
187
|
|
|
134
188
|
const onKeyDown = useCallback((event) => {
|
|
135
189
|
if (event.key.toLowerCase() === "r") { event.preventDefault(); restart(); }
|
|
@@ -137,7 +191,7 @@ export function XiangqiGame(props) {
|
|
|
137
191
|
}, [restart, undo]);
|
|
138
192
|
useGameControls(stageRef, props.paused, onKeyDown);
|
|
139
193
|
|
|
140
|
-
const status = over === "r" ? "红方胜 · 恭喜" : over === "b" ? "黑方胜 · AI 赢" : (thinking ? "AI 思考中…" : (check ? "将军!" : (turn === "r" ? "轮到你(红方)" : "轮到 AI(黑方)")));
|
|
194
|
+
const status = over === "r" ? "红方胜 · 恭喜" : over === "b" ? "黑方胜 · AI 赢" : (moving ? (moving.piece.c === "r" ? "红方走子…" : "AI 走子…") : (thinking ? "AI 思考中…" : (check ? "将军!" : (turn === "r" ? "轮到你(红方)" : "轮到 AI(黑方)"))));
|
|
141
195
|
const prevCheck = useRef(false);
|
|
142
196
|
useEffect(() => {
|
|
143
197
|
if (over === "r") playSfx("win");
|
|
@@ -145,6 +199,8 @@ export function XiangqiGame(props) {
|
|
|
145
199
|
else if (!thinking && check && !prevCheck.current) playSfx("check");
|
|
146
200
|
prevCheck.current = !thinking && check;
|
|
147
201
|
}, [check, thinking, over]);
|
|
202
|
+
// 组件卸载时清掉未落地的动画定时器,避免对已卸载组件 setState。
|
|
203
|
+
useEffect(() => () => { if (animTimerRef.current) clearTimeout(animTimerRef.current); }, []);
|
|
148
204
|
|
|
149
205
|
return <section className="dgame-game" aria-label="象棋">
|
|
150
206
|
<div className="dgame-game-head"><div><h3>中国象棋</h3><p>你执红先行,AI 执黑;吃掉对方将/帅即胜。点击选中红子,再点高亮落点走棋。</p></div><div className="dgame-score"><span>当前</span><strong className="dg-gomoku-status">{status}</strong><button type="button" onClick={undo}>撤销</button><button type="button" onClick={restart}>重开</button></div></div>
|
|
@@ -155,17 +211,33 @@ export function XiangqiGame(props) {
|
|
|
155
211
|
<text className="dgame-xiangqi-rivertext" x={670} y={505}>漢 界</text>
|
|
156
212
|
</svg>
|
|
157
213
|
{board.map((row, r) => row.map((p, c) => {
|
|
214
|
+
const mv = moving;
|
|
215
|
+
// 动画期间:飞行棋子由独立层渲染(起点格不再画它);被吃棋子原地淡出。
|
|
216
|
+
const isFlyFrom = mv && mv.fr === r && mv.fc === c;
|
|
217
|
+
const isDying = mv && mv.capture && mv.tr === r && mv.tc === c;
|
|
158
218
|
const isSel = sel && sel[0] === r && sel[1] === c;
|
|
159
219
|
const isTargetMark = targets.some(([tr, tc]) => tr === r && tc === c);
|
|
220
|
+
const isLastMoveFrom = lastMove && lastMove.fr === r && lastMove.fc === c;
|
|
221
|
+
const isLand = !mv && lastMove && lastMove.tr === r && lastMove.tc === c;
|
|
160
222
|
const isLast = last && last[0] === r && last[1] === c;
|
|
161
223
|
return <button type="button" key={r + "-" + c} role="gridcell"
|
|
162
|
-
className={"dgame-xiangqi-cell" + (isSel ? " sel" : "") + (isTargetMark ? " target" : "") + (isLast ? " last" : "")}
|
|
224
|
+
className={"dgame-xiangqi-cell" + (isSel ? " sel" : "") + (isTargetMark ? " target" : "") + (isLastMoveFrom ? " from" : "") + (isLand ? " land" : "") + (isLast ? " last" : "")}
|
|
163
225
|
aria-label={"第" + (r + 1) + "行第" + (c + 1) + "列" + (p ? pieceChar(p) : " 空")}
|
|
164
226
|
onClick={() => { focusStage(stageRef); select(r, c); }}>
|
|
165
|
-
{p ? <span className={"dgame-xiangqi-pc " + p.c}>{pieceChar(p)}</span> : (isTargetMark ? <span className="dgame-xiangqi-dot" aria-hidden="true" /> : null)}
|
|
227
|
+
{p && !isFlyFrom ? <span className={"dgame-xiangqi-pc " + p.c + (isDying ? " dying" : "")}>{pieceChar(p)}</span> : (!p && isTargetMark ? <span className="dgame-xiangqi-dot" aria-hidden="true" /> : null)}
|
|
166
228
|
</button>;
|
|
167
229
|
}))}
|
|
230
|
+
{moving ? <span key={moving.fr + "-" + moving.fc + "-" + moving.tr + "-" + moving.tc + "-" + moving.piece.c}
|
|
231
|
+
className={"dgame-xiangqi-pc dgame-xiangqi-fly " + moving.piece.c}
|
|
232
|
+
style={{
|
|
233
|
+
"--fx0": (moving.fc + 0.5) / 9, "--fy0": (moving.fr + 0.5) / 10,
|
|
234
|
+
"--fx1": (moving.tc + 0.5) / 9, "--fy1": (moving.tr + 0.5) / 10,
|
|
235
|
+
}}
|
|
236
|
+
aria-hidden="true">{pieceChar(moving.piece)}</span> : null}
|
|
168
237
|
</div>
|
|
238
|
+
{moveLog.length ? <div className="dgame-xiangqi-log" role="status" aria-live="polite">
|
|
239
|
+
{moveLog.slice(-4).map((m, i) => <span key={moveLog.length - 4 + i} className={m.side}>{m.text}</span>)}
|
|
240
|
+
</div> : null}
|
|
169
241
|
<div className="dgame-controls"><button type="button" onClick={undo}>撤销</button><span>{status} · R 重开 · U 撤销</span><button type="button" onClick={restart}>重开</button></div>
|
|
170
242
|
</section>;
|
|
171
243
|
}
|
|
@@ -201,5 +273,5 @@ export function XiangqiPreview() {
|
|
|
201
273
|
}
|
|
202
274
|
|
|
203
275
|
export const xiangqiGame = { Game: XiangqiGame, Preview: XiangqiPreview, css: `
|
|
204
|
-
.dgame-xiangqi-board{position:relative;display:grid;grid-template-columns:repeat(9,1fr);grid-template-rows:repeat(10,1fr);align-self:center;width:min(100%,min(90vw,340px));aspect-ratio:9/10;padding:13px;border-radius:12px;border:1px solid color-mix(in srgb,#7c4f16 65%,transparent);box-shadow:0 12px 32px rgb(0 0 0/.5),inset 0 1px 0 rgba(255,236,200,.55),inset 0 -3px 9px rgba(90,50,10,.35),inset 3px 0 8px rgba(255,224,170,.28),inset -3px 0 8px rgba(120,70,20,.25);outline:none;background:repeating-linear-gradient(94deg,rgba(124,79,22,.05) 0 2px,transparent 2px 11px),repeating-linear-gradient(87deg,rgba(255,238,196,.05) 0 2px,transparent 2px 14px),radial-gradient(130% 105% at 26% 8%,#f0cd86,#ddb067 48%,#cb9a51 78%,#bb8844)}.dgame-xiangqi-lines{position:absolute;inset:13px;pointer-events:none}.dgame-xiangqi-lines line,.dgame-xiangqi-lines path{stroke:#66431a;stroke-width:3.4;fill:none;stroke-linecap:square}.dgame-xiangqi-lines .dgame-xiangqi-mark{stroke-width:2.6}.dgame-xiangqi-rivertext{fill:rgba(102,67,26,.7);font-family:KaiTi,"Kaiti SC","STKaiti","FangSong",serif;font-size:46px;letter-spacing:7px;text-anchor:middle;dominant-baseline:central}.dgame-xiangqi-board:focus-visible{box-shadow:0 0 0 3px rgba(0,0,0,.25)}.dgame-xiangqi-cell{position:relative;z-index:1;background:transparent;border:none;cursor:pointer;padding:0;margin:0;outline:none;display:grid;place-items:center;min-width:0;min-height:0}.dgame-xiangqi-cell.target .dgame-xiangqi-dot{width:12px;height:12px;border-radius:50%;background:rgba(34,211,238,.6);box-shadow:0 0 10px rgba(34,211,238,.65),0 0 0 2px rgba(255,255,255,.28)}.dgame-xiangqi-cell.target .dgame-xiangqi-pc{box-shadow:0 0 0 2px rgba(248,113,113,.85),0 0 10px rgba(248,113,113,.5),0 2px 4px rgb(0 0 0/.4),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4)}.dgame-xiangqi-cell.sel .dgame-xiangqi-pc::before{content:'';position:absolute;inset:-3px;border-radius:50%;border:2.5px solid rgba(251,191,36,.95);box-shadow:0 0 12px rgba(251,191,36,.6)}.dgame-xiangqi-cell.last .dgame-xiangqi-pc::after{content:'';position:absolute;right:5%;bottom:5%;width:5px;height:5px;border-radius:50%;background:#ef4444;box-shadow:0 0 4px rgba(239,68,68,.8)}.dgame-xiangqi-pc{position:relative;display:grid;place-items:center;z-index:1;width:min(80%,30px);aspect-ratio:1/1;border-radius:50%;box-sizing:border-box;font-family:KaiTi,"Kaiti SC","STKaiti","FangSong","SimSun",serif;font-size:clamp(12px,1.4vw,15px);font-weight:700;line-height:1;background:radial-gradient(circle at 34% 28%,#fff3d4,#eedaa6 46%,#d8b675 72%,#bd9650);box-shadow:0 2px 4px rgb(0 0 0/.45),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4)}.dgame-xiangqi-pc.r{color:#b91c1c;text-shadow:0 1px 0 rgba(255,255,255,.5)}.dgame-xiangqi-
|
|
276
|
+
.dgame-xiangqi-board{position:relative;display:grid;grid-template-columns:repeat(9,1fr);grid-template-rows:repeat(10,1fr);align-self:center;width:min(100%,min(90vw,340px));aspect-ratio:9/10;padding:13px;border-radius:12px;border:1px solid color-mix(in srgb,#7c4f16 65%,transparent);box-shadow:0 12px 32px rgb(0 0 0/.5),inset 0 1px 0 rgba(255,236,200,.55),inset 0 -3px 9px rgba(90,50,10,.35),inset 3px 0 8px rgba(255,224,170,.28),inset -3px 0 8px rgba(120,70,20,.25);outline:none;background:repeating-linear-gradient(94deg,rgba(124,79,22,.05) 0 2px,transparent 2px 11px),repeating-linear-gradient(87deg,rgba(255,238,196,.05) 0 2px,transparent 2px 14px),radial-gradient(130% 105% at 26% 8%,#f0cd86,#ddb067 48%,#cb9a51 78%,#bb8844)}.dgame-xiangqi-lines{position:absolute;inset:13px;pointer-events:none}.dgame-xiangqi-lines line,.dgame-xiangqi-lines path,.dgame-xiangqi-lines rect{stroke:#66431a;stroke-width:3.4;fill:none;stroke-linecap:square}.dgame-xiangqi-lines rect{stroke-width:7}.dgame-xiangqi-lines .dgame-xiangqi-mark{stroke-width:2.6}.dgame-xiangqi-rivertext{fill:rgba(102,67,26,.7);font-family:KaiTi,"Kaiti SC","STKaiti","FangSong",serif;font-size:46px;letter-spacing:7px;text-anchor:middle;dominant-baseline:central}.dgame-xiangqi-board:focus-visible{box-shadow:0 0 0 3px rgba(0,0,0,.25)}.dgame-xiangqi-cell{position:relative;z-index:1;background:transparent;border:none;cursor:pointer;padding:0;margin:0;outline:none;display:grid;place-items:center;min-width:0;min-height:0}.dgame-xiangqi-cell.target .dgame-xiangqi-dot{width:12px;height:12px;border-radius:50%;background:rgba(34,211,238,.6);box-shadow:0 0 10px rgba(34,211,238,.65),0 0 0 2px rgba(255,255,255,.28)}.dgame-xiangqi-cell.target .dgame-xiangqi-pc{box-shadow:0 0 0 2px rgba(248,113,113,.85),0 0 10px rgba(248,113,113,.5),0 2px 4px rgb(0 0 0/.4),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4)}.dgame-xiangqi-cell.sel .dgame-xiangqi-pc::before{content:'';position:absolute;inset:-3px;border-radius:50%;border:2.5px solid rgba(251,191,36,.95);box-shadow:0 0 12px rgba(251,191,36,.6)}.dgame-xiangqi-cell.from::before{content:'';position:absolute;inset:50% auto auto 50%;width:34%;height:34%;transform:translate(-50%,-50%);border-radius:50%;border:2.5px dashed rgba(102,67,26,.75);pointer-events:none}.dgame-xiangqi-cell.land .dgame-xiangqi-pc{animation:dgame-xq-land .28s cubic-bezier(.2,.85,.3,1.25)}.dgame-xiangqi-cell.last .dgame-xiangqi-pc::after{content:'';position:absolute;right:5%;bottom:5%;width:5px;height:5px;border-radius:50%;background:#ef4444;box-shadow:0 0 4px rgba(239,68,68,.8)}.dgame-xiangqi-pc{position:relative;display:grid;place-items:center;z-index:1;width:min(80%,30px);aspect-ratio:1/1;border-radius:50%;box-sizing:border-box;font-family:KaiTi,"Kaiti SC","STKaiti","FangSong","SimSun",serif;font-size:clamp(12px,1.4vw,15px);font-weight:700;line-height:1;background:radial-gradient(circle at 34% 28%,#fff3d4,#eedaa6 46%,#d8b675 72%,#bd9650);box-shadow:0 2px 4px rgb(0 0 0/.45),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4)}.dgame-xiangqi-pc.dying{animation:dgame-xq-die .26s ease forwards}.dgame-xiangqi-fly{position:absolute;z-index:5;width:calc((100% - 26px)*.0889);aspect-ratio:1/1;border-radius:50%;display:grid;place-items:center;box-sizing:border-box;font-family:KaiTi,"Kaiti SC","STKaiti","FangSong","SimSun",serif;font-size:clamp(12px,1.4vw,15px);font-weight:700;line-height:1;pointer-events:none;background:radial-gradient(circle at 34% 28%,#fff3d4,#eedaa6 46%,#d8b675 72%,#bd9650);box-shadow:0 6px 14px rgb(0 0 0/.5),0 0 0 1.5px rgba(96,62,18,.35),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4);animation:dgame-xq-hop .26s cubic-bezier(.3,.7,.4,1) forwards}.dgame-xiangqi-fly.r{color:#b91c1c;text-shadow:0 1px 0 rgba(255,255,255,.5)}.dgame-xiangqi-fly.b{color:#1f2937;text-shadow:0 1px 0 rgba(255,255,255,.35)}@keyframes dgame-xq-hop{0%{left:calc(13px + var(--fx0)*(100% - 26px));top:calc(13px + var(--fy0)*(100% - 26px));transform:translate(-50%,-50%) scale(.9);opacity:.85}55%{transform:translate(-50%,-64%) scale(1.16);opacity:1}100%{left:calc(13px + var(--fx1)*(100% - 26px));top:calc(13px + var(--fy1)*(100% - 26px));transform:translate(-50%,-50%) scale(1)}}@keyframes dgame-xq-land{0%{transform:scale(1.18)}100%{transform:scale(1)}}@keyframes dgame-xq-die{to{opacity:0;transform:scale(.55)}}.dgame-xiangqi-log{display:flex;flex-wrap:wrap;justify-content:center;gap:5px 12px;align-self:center;min-height:22px;color:var(--dsw-alias-label-secondary);font-family:KaiTi,"Kaiti SC","STKaiti","FangSong",serif;font-size:13px}.dgame-xiangqi-log span{opacity:.65}.dgame-xiangqi-log span:last-child{opacity:1;font-weight:700}.dgame-xiangqi-log .r{color:#c03434}.dgame-xiangqi-log .b{color:var(--dsw-alias-label-primary)}
|
|
205
277
|
` };
|
package/features/games/view.jsx
CHANGED
|
@@ -210,7 +210,7 @@ const GAMES = [
|
|
|
210
210
|
{
|
|
211
211
|
id: "breakout", name: "打砖块", cat: "classic", accent: "#38bdf8",
|
|
212
212
|
desc: "移动挡板反弹小球,击碎全部砖块;球掉到底部丢一条命。",
|
|
213
|
-
tip: "←/→ 或鼠标移动 ·
|
|
213
|
+
tip: "←/→ 或鼠标移动 · 空格/点击发球",
|
|
214
214
|
Game: breakoutGame.Game, Preview: breakoutGame.Preview,
|
|
215
215
|
},
|
|
216
216
|
{
|
|
@@ -583,12 +583,12 @@ function GamesHomeStat() {
|
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
const baseCss = `
|
|
586
|
-
.dgame{--dgame-accent
|
|
586
|
+
.dgame{--dgame-accent:var(--dk-games-accent,#7c3aed);--dgame-accent-soft:color-mix(in srgb,var(--dgame-accent) 16%,transparent);display:flex;flex-direction:column;gap:14px;max-width:760px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}.dgame h2,.dgame h3,.dgame p{margin:0}.dgame-hero{position:relative;display:flex;justify-content:space-between;gap:18px;overflow:hidden;padding:17px 18px;border:1px solid color-mix(in srgb,var(--dgame-accent) 35%,var(--dsw-alias-border-l1));border-radius:16px;background:radial-gradient(circle at 88% 30%,color-mix(in srgb,#38bdf8 25%,transparent),transparent 28%),radial-gradient(circle at 8% 100%,color-mix(in srgb,#a855f7 24%,transparent),transparent 34%),var(--dsw-alias-bg-layer-1)}.dgame-hero>div{position:relative;z-index:1;display:flex;flex-direction:column;gap:5px;max-width:540px}.dgame-hero h2{font-size:21px;letter-spacing:-.02em}.dgame-hero p,.dgame-foot{color:var(--dsw-alias-label-secondary);font-size:12px}.dgame-eyebrow{display:inline-flex;align-items:center;gap:7px;color:var(--dgame-accent);font-size:11px;font-weight:700}.dgame-eyebrow i,.dgame-task-status>i{width:7px;height:7px;border-radius:50%;background:currentColor;box-shadow:0 0 0 4px currentColor;opacity:.9}.dgame-planet{position:relative;z-index:1;align-self:center;display:block;width:76px;height:76px;flex:none;border-radius:50%;background:radial-gradient(circle at 35% 30%,#e0f2fe 0 4%,#60a5fa 18%,#3730a3 59%,#1e1b4b);box-shadow:0 0 30px color-mix(in srgb,#818cf8 45%,transparent)}.dgame-planet::after{content:'';position:absolute;left:-16px;top:33px;width:106px;height:27px;border:3px solid color-mix(in srgb,#c4b5fd 72%,transparent);border-radius:50%;transform:rotate(-17deg)}.dgame-planet i{position:absolute;right:15px;top:14px;width:9px;height:9px;border-radius:50%;background:#818cf8;box-shadow:-23px 24px 0 5px color-mix(in srgb,#312e81 52%,transparent)}.dgame-planet b{position:absolute;left:-9px;top:9px;width:5px;height:5px;border-radius:50%;background:#fef3c7;box-shadow:58px -20px 0 #fef3c7,76px 43px 0 #bae6fd}.dgame-task-status{display:flex;align-items:center;gap:9px;min-height:40px;padding:9px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:11px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary)}.dgame-task-status>i{color:var(--dsw-alias-label-tertiary);box-shadow:0 0 0 4px color-mix(in srgb,var(--dsw-alias-label-tertiary) 13%,transparent)}.dgame-task-status.live{color:var(--dgame-accent);border-color:color-mix(in srgb,var(--dgame-accent) 36%,var(--dsw-alias-border-l1));background:var(--dgame-accent-soft)}.dgame-task-status.live>i{box-shadow:0 0 0 4px color-mix(in srgb,var(--dgame-accent) 17%,transparent);animation:dgame-pulse 1.8s ease-in-out infinite}.dgame-task-status div{display:flex;flex:1;min-width:0;flex-direction:column}.dgame-task-status strong{font-size:12px}.dgame-task-status span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:var(--dsw-alias-label-secondary)}.dgame-task-status.loading{color:var(--dsw-alias-label-tertiary)}@keyframes dgame-pulse{50%{transform:scale(.72);opacity:.5}}.dgame-launch-note{color:var(--dsw-alias-label-secondary);font-size:12px}.dgame-game{display:flex;flex-direction:column;gap:11px;padding:15px;border:1px solid var(--dsw-alias-border-l1);border-radius:14px;background:var(--dsw-alias-bg-layer-1)}.dgame-game-head{display:flex;justify-content:space-between;gap:12px}.dgame-game-head>div:first-child{display:flex;flex-direction:column;gap:3px}.dgame-game h3{font-size:16px}.dgame-game p{font-size:12px;color:var(--dsw-alias-label-secondary)}.dgame-score{display:flex;align-items:flex-end;gap:9px;flex-direction:column;color:var(--dsw-alias-label-tertiary);font-size:11px;white-space:nowrap}.dgame-score strong{color:var(--dgame-accent);font-size:17px;line-height:1}.dgame-score button{min-height:28px;padding:3px 8px;font-size:11px}.dgame-dash-stage{position:relative;align-self:center;width:min(100%,360px);border:1px solid color-mix(in srgb,var(--dgame-accent) 30%,var(--dsw-alias-border-l1));border-radius:13px;overflow:hidden;outline:none;background:radial-gradient(circle at 50% -16%,color-mix(in srgb,#38bdf8 25%,transparent),transparent 42%),linear-gradient(180deg,#10162d,#17142d)}.dgame-dash-stage:focus-visible{box-shadow:0 0 0 3px var(--dgame-accent-soft)}.dgame-dash-grid{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(5,52px);padding:7px;background:repeating-linear-gradient(90deg,transparent 0 calc(33.333% - 1px),color-mix(in srgb,#c4b5fd 15%,transparent) calc(33.333% - 1px) 33.333%)}.dgame-dash-cell{position:relative;display:grid;place-items:center;border-bottom:1px dashed color-mix(in srgb,#c4b5fd 13%,transparent)}.dgame-rock{font-style:normal;color:#fb7185;text-shadow:0 0 12px #f43f5e;font-size:22px;animation:dgame-rock .55s cubic-bezier(.2,.8,.25,1)}.dgame-pilot{position:relative;color:#a5f3fc;font-size:25px;text-shadow:0 0 14px #38bdf8;animation:dgame-pilot .9s ease-in-out infinite alternate}.dgame-pilot::after{content:'';position:absolute;left:46%;top:19px;width:4px;height:18px;background:linear-gradient(#fef3c7,transparent);transform:translateX(-50%)}@keyframes dgame-rock{from{opacity:0;transform:translateY(-13px) scale(.65)}to{opacity:1;transform:none}}@keyframes dgame-pilot{to{transform:translateY(-2px)}}.dgame-over{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:9px;background:color-mix(in srgb,#111827 74%,transparent);backdrop-filter:blur(2px);color:#fef3c7}.dgame-over strong{font-size:17px}.dgame-over button{padding:7px 12px;color:#fef3c7;background:color-mix(in srgb,#f59e0b 25%,transparent);border-color:#f59e0b}.dgame-controls{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:8px;align-self:center;width:min(100%,360px)}.dgame-controls button{min-height:42px;padding:8px 10px;font-weight:600}.dgame-controls span{text-align:center;color:var(--dsw-alias-label-tertiary);font-size:11px}.dgame-reactor{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;align-self:center;width:min(100%,360px);padding:10px;border-radius:13px;background:linear-gradient(135deg,color-mix(in srgb,#312e81 34%,transparent),color-mix(in srgb,#0f172a 45%,transparent));border:1px solid color-mix(in srgb,var(--dgame-accent) 22%,var(--dsw-alias-border-l1))}.dgame-reactor-cell{position:relative;aspect-ratio:1;border:1px solid color-mix(in srgb,#94a3b8 30%,transparent);border-radius:12px;background:#171a33;cursor:pointer;transition:transform .16s ease,background .22s ease,border-color .22s ease,box-shadow .22s ease}.dgame-reactor-cell:hover{transform:translateY(-2px);border-color:var(--dgame-accent)}.dgame-reactor-cell:focus-visible{outline:2px solid var(--dgame-accent);outline-offset:2px}.dgame-reactor-cell i{position:absolute;inset:23%;border-radius:9px;background:#303252;transition:inherit}.dgame-reactor-cell.on{border-color:#67e8f9;background:color-mix(in srgb,#164e63 70%,#0f172a)}.dgame-reactor-cell.on i{background:radial-gradient(circle at 35% 30%,#ecfeff,#67e8f9 36%,#2563eb 78%);box-shadow:0 0 15px color-mix(in srgb,#22d3ee 65%,transparent)}.dgame-reactor-cell.focus{border-color:#a5f3fc;box-shadow:0 0 0 2px var(--dgame-accent) inset,0 0 14px color-mix(in srgb,#22d3ee 48%,transparent)}.dgame-reactor-cell.focus i{box-shadow:0 0 0 2px color-mix(in srgb,#ecfeff 55%,transparent) inset}.dgame-reactor:focus-visible{outline:2px solid var(--dgame-accent);outline-offset:3px}.dgame-reactor-status{align-self:center;min-height:36px;padding:8px 12px;border-radius:9px;color:var(--dsw-alias-label-secondary);font-size:12px}.dgame-reactor-status.solved{color:var(--dk-ok);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#0d9488) 13%,transparent)}.dgame-foot{padding:0 2px}.dgame-foot::before{content:'✦';margin-right:6px;color:var(--dgame-accent)}
|
|
587
587
|
.dgame .dgame-score button,.dgame .dgame-controls button,.dgame .dgame-over button,.dgwin .dgame-score button,.dgwin .dgame-controls button,.dgwin .dgame-over button{border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;transition:transform .16s ease,border-color .18s ease,background .18s ease,color .18s ease}.dgame .dgame-score button:hover,.dgame .dgame-controls button:hover,.dgame .dgame-over button:hover,.dgwin .dgame-score button:hover,.dgwin .dgame-controls button:hover,.dgwin .dgame-over button:hover{border-color:var(--dgame-accent);color:var(--dsw-alias-label-primary)}.dgame .dgame-score button:active,.dgame .dgame-controls button:active,.dgame .dgame-over button:active,.dgwin .dgame-score button:active,.dgwin .dgame-controls button:active,.dgwin .dgame-over button:active{transform:scale(.97)}
|
|
588
|
-
.dgcov-grid{--dgame-accent
|
|
588
|
+
.dgcov-grid{--dgame-accent:var(--dk-games-accent,#7c3aed);display:flex;gap:10px;flex-wrap:wrap}.dgcov{flex:1;min-width:230px;display:flex;flex-direction:column;gap:8px;padding:11px;border:1px solid var(--dsw-alias-border-l1);border-radius:13px;background:var(--dsw-alias-bg-layer-2);cursor:pointer;font-family:inherit;text-align:left;transition:border-color .16s ease,transform .16s ease}.dgcov:hover{border-color:var(--dgame-accent);transform:translateY(-2px)}.dgcov:focus-visible{outline:2px solid var(--dgame-accent);outline-offset:2px}.dgcov.on{border-color:var(--dgame-accent);box-shadow:0 0 0 1px color-mix(in srgb,var(--dgame-accent) 40%,transparent)}.dgcov-name{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}.dgcov-name i{width:8px;height:8px;border-radius:50%;flex:none}.dgcov-name em{margin-left:auto;font-style:normal;font-size:10px;color:var(--dgame-accent);border:1px solid color-mix(in srgb,var(--dgame-accent) 45%,transparent);border-radius:999px;padding:1px 8px;white-space:nowrap}.dgcov-desc{font-size:11.5px;color:var(--dsw-alias-label-secondary);line-height:1.55}.dgcov-prev{position:relative;display:block;height:96px;border-radius:9px;overflow:hidden;border:1px solid color-mix(in srgb,var(--dgame-accent) 25%,var(--dsw-alias-border-l1))}
|
|
589
589
|
.dgcov-prev-dash{background:radial-gradient(circle at 70% 20%,color-mix(in srgb,#1d4ed8 30%,transparent),transparent 55%),linear-gradient(180deg,#0d1330,#171233)}.dgcov-prev-dash i{position:absolute;top:-22%;font-style:normal;color:#fb7185;text-shadow:0 0 10px #f43f5e;font-size:15px;animation:dgcov-fall 1.5s linear infinite}.dgcov-prev-dash i::after{content:'◆'}.dgcov-prev-dash i:nth-child(1){left:16%}.dgcov-prev-dash i:nth-child(2){left:50%;animation-delay:-.5s}.dgcov-prev-dash i:nth-child(3){left:82%;animation-delay:-1s}.dgcov-prev-dash b{position:absolute;left:50%;bottom:8px;margin-left:-9px;font-size:17px;color:#a5f3fc;text-shadow:0 0 12px #38bdf8;animation:dgcov-dodge 2.4s ease-in-out infinite}.dgcov-prev-dash b::after{content:'▲'}@keyframes dgcov-fall{to{transform:translateY(140px)}}@keyframes dgcov-dodge{0%,100%{transform:translateX(-46px)}25%,75%{transform:translateX(0)}50%{transform:translateX(46px)}}
|
|
590
590
|
.dgcov-prev-reactor{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;padding:14px;background:linear-gradient(135deg,#272463,#12102c)}.dgcov-prev-reactor i{display:block;border-radius:9px;background:#191c38;border:1px solid color-mix(in srgb,#ffffff 11%,transparent);animation:dgcov-reactor 3s ease-in-out infinite}.dgcov-prev-reactor i:nth-child(1){animation-delay:0s}.dgcov-prev-reactor i:nth-child(2){animation-delay:-.33s}.dgcov-prev-reactor i:nth-child(3){animation-delay:-.66s}.dgcov-prev-reactor i:nth-child(4){animation-delay:-1s}.dgcov-prev-reactor i:nth-child(5){animation-delay:-1.33s}.dgcov-prev-reactor i:nth-child(6){animation-delay:-1.66s}.dgcov-prev-reactor i:nth-child(7){animation-delay:-2s}.dgcov-prev-reactor i:nth-child(8){animation-delay:-2.33s}.dgcov-prev-reactor i:nth-child(9){animation-delay:-2.66s}@keyframes dgcov-reactor{0%,20%,100%{background:#191c38;box-shadow:none;border-color:color-mix(in srgb,#ffffff 11%,transparent)}40%,64%{background:radial-gradient(circle at 35% 30%,#ecfeff,#67e8f9 40%,#2563eb 85%);box-shadow:0 0 14px color-mix(in srgb,#22d3ee 55%,transparent);border-color:#67e8f9}}
|
|
591
|
-
.dgfab{--dgame-accent
|
|
591
|
+
.dgfab{--dgame-accent:var(--dk-games-accent,#7c3aed);position:fixed;z-index:9985;display:flex;flex-direction:column;align-items:center;gap:6px;box-sizing:border-box;width:36px;padding:11px 0;border:1px solid color-mix(in srgb,var(--dgame-accent) 42%,var(--dsw-alias-border-l1));background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 82%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--dsw-alias-label-primary);font-family:inherit;cursor:grab;user-select:none;touch-action:none;box-shadow:0 6px 24px rgb(0 0 0 / .18);transition:left .22s ease,top .22s ease,border-color .15s ease,opacity .15s ease}.dgfab:hover{border-color:var(--dgame-accent)}.dgfab-side-left{border-radius:0 12px 12px 0;border-left:none}.dgfab-side-right{border-radius:12px 0 0 12px;border-right:none}.dgfab-drag{transition:none;cursor:grabbing;box-shadow:0 10px 34px rgb(0 0 0 / .3);z-index:9986}.dgfab-hide{display:none}.dgfab-ico{display:inline-flex;color:var(--dgame-accent)}.dgfab-label{writing-mode:vertical-rl;letter-spacing:.22em;font-size:11px;line-height:1;color:var(--dsw-alias-label-secondary)}
|
|
592
592
|
.dgwin{--dgame-accent:#a78bfa;--dgame-accent-soft:color-mix(in srgb,#a78bfa 16%,transparent);--dsw-alias-label-primary:#e7ecff;--dsw-alias-label-secondary:#aab3e8;--dsw-alias-label-tertiary:#7d86c0;--dsw-alias-border-l1:rgb(255 255 255 / .12);--dsw-alias-border-l2:rgb(255 255 255 / .17);--dsw-alias-bg-layer-1:#0d1230;--dsw-alias-bg-layer-2:#131a3d;--dsw-alias-interactive-bg-hover:rgb(255 255 255 / .08);position:fixed;z-index:9993;display:flex;flex-direction:column;border:1px solid rgb(255 255 255 / .14);border-radius:14px;background:radial-gradient(600px 300px at 80% -10%,color-mix(in srgb,#312e81 30%,transparent),transparent 60%),radial-gradient(500px 340px at -10% 110%,color-mix(in srgb,#0ea5e9 14%,transparent),transparent 55%),linear-gradient(180deg,#0b1024,#12102a 60%,#0a0f22);color:var(--dsw-alias-label-primary);box-shadow:0 24px 72px rgb(0 0 0 / .5);animation:dgwin-in .18s ease-out}.dgwin::before{content:'';position:absolute;inset:0;pointer-events:none;background-image:radial-gradient(1px 1px at 12% 22%,rgb(255 255 255 / .35),transparent),radial-gradient(1px 1px at 34% 68%,rgb(255 255 255 / .26),transparent),radial-gradient(1.5px 1.5px at 58% 14%,rgb(199 210 254 / .35),transparent),radial-gradient(1px 1px at 76% 44%,rgb(255 255 255 / .22),transparent),radial-gradient(1.5px 1.5px at 88% 78%,rgb(165 243 252 / .3),transparent),radial-gradient(1px 1px at 22% 88%,rgb(255 255 255 / .18),transparent),radial-gradient(1px 1px at 45% 38%,rgb(255 255 255 / .15),transparent),radial-gradient(1px 1px at 67% 86%,rgb(255 255 255 / .19),transparent)}@keyframes dgwin-in{from{opacity:0;transform:translateY(8px) scale(.985)}}
|
|
593
593
|
.dgwin-min{height:auto!important;border-radius:12px}.dgwin-min .dgwin-body{display:none}.dgwin-min .dgwin-resize{display:none}
|
|
594
594
|
.dgwin-max{border-radius:12px}.dgwin-max .dgwin-body .dgame-game{align-items:stretch}.dgwin-max .dgwin-body .dgame-dash-stage,.dgwin-max .dgwin-body .dgame-tetris,.dgwin-max .dgwin-body .dgame-tetris-board,.dgwin-max .dgwin-body .dgame-snake-stage,.dgwin-max .dgwin-body .dgame-sokoban-stage,.dgwin-max .dgwin-body .dgame-gomoku-board,.dgwin-max .dgwin-body .dgame-xiangqi-board,.dgwin-max .dgwin-body .dgame-tank-stage,.dgwin-max .dgwin-body .dgame-breakout,.dgwin-max .dgwin-body .dgame-racer,.dgwin-max .dgwin-body .dgame-reactor{width:100%;max-width:none}.dgwin-max .dgwin-body .dgame-controls{width:100%;max-width:none}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// 配对状态、在线设备与接力备注只存内存;会话数据始终只有 ~/.dsh 这一份。
|
|
17
17
|
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto'
|
|
18
18
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
19
|
+
import { stat } from 'node:fs/promises'
|
|
19
20
|
import { homedir } from 'node:os'
|
|
20
21
|
import { dirname, join } from 'node:path'
|
|
21
22
|
import yaml from 'js-yaml'
|
|
@@ -193,6 +194,58 @@ export const feature = {
|
|
|
193
194
|
|
|
194
195
|
disposers.push(ctx.inject(['settings'], (sctx) => { settingsCtx = sctx }))
|
|
195
196
|
|
|
197
|
+
// ── 浏览模式目录选择器的 Windows 跨盘补全 ──
|
|
198
|
+
// 官方 browse 后端从主目录起步,只能列出当前盘内的子目录,面包屑在主目录之上
|
|
199
|
+
// 全部折叠——Windows 开着远程访问时,选工作区根本到不了其他磁盘。
|
|
200
|
+
// 这里在运行时包装 browse 能力的 list():主目录层级注入全部盘符入口,盘根目录
|
|
201
|
+
// 注入其他盘符入口,远程浏览即可像资源管理器一样跨盘选工作区。非 Windows 或
|
|
202
|
+
// 非浏览模式(原生对话框本身可跨盘)零改动。
|
|
203
|
+
const DRIVE_SCAN_TTL = 5000
|
|
204
|
+
let driveScan = { at: 0, letters: null }
|
|
205
|
+
function sameWinPath(a, b) {
|
|
206
|
+
return a.replace(/[\\/]+/g, '\\').toLowerCase() === b.replace(/[\\/]+/g, '\\').toLowerCase()
|
|
207
|
+
}
|
|
208
|
+
function winDriveLetterOf(p) {
|
|
209
|
+
return /^[A-Za-z]:[\\/]?$/.test(p) ? p.slice(0, 1).toUpperCase() : null
|
|
210
|
+
}
|
|
211
|
+
async function windowsDrivePaths() {
|
|
212
|
+
if (driveScan.letters && Date.now() - driveScan.at < DRIVE_SCAN_TTL) return driveScan.letters
|
|
213
|
+
const probes = []
|
|
214
|
+
for (const letter of 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') {
|
|
215
|
+
probes.push(stat(letter + ':\\').then(() => letter, () => null))
|
|
216
|
+
}
|
|
217
|
+
const letters = (await Promise.all(probes)).filter(Boolean).sort()
|
|
218
|
+
driveScan = { at: Date.now(), letters }
|
|
219
|
+
return letters
|
|
220
|
+
}
|
|
221
|
+
disposers.push(ctx.inject(['directoryPicker'], (dpCtx) => {
|
|
222
|
+
if (process.platform !== 'win32') return
|
|
223
|
+
const picker = dpCtx.directoryPicker
|
|
224
|
+
if (!picker || typeof picker.capability !== 'function') return
|
|
225
|
+
let cap
|
|
226
|
+
try { cap = picker.capability() } catch { return }
|
|
227
|
+
if (!cap || cap.kind !== 'browse' || typeof cap.list !== 'function' || cap.__dshDockDrives) return
|
|
228
|
+
cap.__dshDockDrives = true
|
|
229
|
+
const origList = cap.list
|
|
230
|
+
cap.list = async (path, signal) => {
|
|
231
|
+
const listing = await origList(path, signal)
|
|
232
|
+
try {
|
|
233
|
+
const target = String((listing && listing.path) || '')
|
|
234
|
+
const atHome = sameWinPath(target, String(listing.home || '\u0000'))
|
|
235
|
+
const driveRoot = winDriveLetterOf(target)
|
|
236
|
+
// 只在主目录与盘根两级注入:这两级是跨盘的必经之地,其余层级保持官方原样
|
|
237
|
+
if (atHome || driveRoot) {
|
|
238
|
+
const letters = await windowsDrivePaths()
|
|
239
|
+
const rows = letters
|
|
240
|
+
.filter((letter) => !driveRoot || letter !== driveRoot)
|
|
241
|
+
.map((letter) => ({ name: letter + ':\\', path: letter + ':\\', hidden: false }))
|
|
242
|
+
if (rows.length) listing.entries = atHome ? rows.concat(listing.entries) : listing.entries.concat(rows)
|
|
243
|
+
}
|
|
244
|
+
} catch { /* 盘符探测失败保持原列表,不让补全拖垮选目录 */ }
|
|
245
|
+
return listing
|
|
246
|
+
}
|
|
247
|
+
}))
|
|
248
|
+
|
|
196
249
|
// ── 远程访问账号(settings 持久化;密码只存加盐哈希) ──
|
|
197
250
|
function hashPassword(salt, password) {
|
|
198
251
|
return createHash('sha256').update(String(salt) + '\u0000' + String(password), 'utf8').digest('hex')
|
|
@@ -169,6 +169,6 @@ export const feature = {
|
|
|
169
169
|
id: 'mobile-relay', name: '远程访问', order: 80, accent: '#38bdf8',
|
|
170
170
|
description: '账号密码登录的远程入口:所有设备访问同一个 DSH,任务进度实时一致', defaultEnabled: true,
|
|
171
171
|
css: `
|
|
172
|
-
.dmr{--dmr-accent:#38bdf8;--dmr-accent-soft:color-mix(in srgb,var(--dmr-accent) 13%,transparent);display:flex;flex-direction:column;gap:16px;max-width:720px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}.dmr h3,.dmr h4,.dmr p{margin:0}.dmr h3{font-size:17px;line-height:1.25;letter-spacing:-.01em}.dmr h4{font-size:13px}.dmr-hero,.dmr-status-head{display:flex;align-items:flex-start;gap:12px}.dmr-hero>div,.dmr-status-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}.dmr-hero p,.dmr-status-head p,.dmr-field small,.dmr-share small,.dmr-overview-card small,.dmr-task small,.dmr-note p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}.dmr-hero-icon{width:42px;height:42px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:14px;background:var(--dmr-accent-soft);color:var(--dmr-accent);border:1px solid color-mix(in srgb,var(--dmr-accent) 35%,var(--dsw-alias-border-l1))}.dmr-steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none}.dmr-steps li{display:flex;gap:9px;padding:11px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px}.dmr-steps li>span{width:21px;height:21px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:50%;font-size:11px;font-weight:700;color:var(--dmr-accent);background:var(--dmr-accent-soft)}.dmr-steps div{display:flex;flex-direction:column;gap:3px;min-width:0}.dmr-steps strong{font-size:12px}.dmr-steps small{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.45}.dmr-field{display:flex;flex-direction:column;gap:6px;font-weight:600}.dmr-field input,.dmr-field select,.dmr-note textarea{box-sizing:border-box;width:100%;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:10px;padding:10px 12px;min-height:44px;font:inherit;outline:none;transition:border-color .18s ease,box-shadow .18s ease}.dmr-field input:focus,.dmr-field select:focus,.dmr-note textarea:focus{border-color:var(--dmr-accent);box-shadow:0 0 0 3px var(--dmr-accent-soft)}.dmr-field small{font-weight:400}.dmr-network-grid{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(150px,.6fr);gap:12px}.dmr-security{padding:12px 13px;border:1px solid color-mix(in srgb,#f59e0b 38%,var(--dsw-alias-border-l1));border-radius:12px;background:color-mix(in srgb,#f59e0b 8%,transparent)}.dmr-security strong{display:block;margin-bottom:4px;color:#f59e0b;font-size:12px}.dmr-security p{font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.55}.dmr-primary,.dmr-secondary,.dmr-text-button,.dmr-icon-button{font:inherit;touch-action:manipulation;cursor:pointer;transition:transform .15s ease,background .18s ease,border-color .18s ease,opacity .18s ease}.dmr-primary,.dmr-secondary{min-height:44px;display:inline-flex;align-items:center;justify-content:center;gap:8px;border-radius:10px;padding:9px 13px;font-weight:600}.dmr-primary{align-self:flex-start;border:1px solid var(--dmr-accent);background:var(--dmr-accent);color:#062238}.dmr-primary:hover{filter:brightness(1.05)}.dmr-secondary{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.dmr-secondary:hover{border-color:var(--dmr-accent);background:var(--dmr-accent-soft)}.dmr-primary:active,.dmr-secondary:active,.dmr-icon-button:active{transform:scale(.98)}.dmr-primary:disabled,.dmr-secondary:disabled,.dmr-text-button:disabled,.dmr-icon-button:disabled{cursor:not-allowed;opacity:.5}.dmr-message{display:flex;align-items:flex-start;gap:7px;padding:10px 12px;border-radius:10px;font-size:12px}.dmr-message.success{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 10%,transparent)}.dmr-message.error{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}.dmr-status-head{justify-content:space-between}.dmr-eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;color:var(--dmr-accent)}.dmr-eyebrow i{width:7px;height:7px;border-radius:50%;background:currentColor;box-shadow:0 0 0 4px var(--dmr-accent-soft)}.dmr-icon-button{display:inline-flex;align-items:center;justify-content:center;flex:none;width:44px;height:44px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;color:var(--dsw-alias-label-secondary)}.dmr-icon-button:hover{color:var(--dsw-alias-state-error-primary);border-color:currentColor}.dmr-share{display:flex;flex-direction:column;gap:9px;padding:13px;border:1px solid color-mix(in srgb,var(--dmr-accent) 32%,var(--dsw-alias-border-l1));border-radius:13px;background:var(--dmr-accent-soft)}.dmr-share-layout{display:grid;grid-template-columns:196px minmax(0,1fr);gap:16px;align-items:center}.dmr-qr-card{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;gap:5px;padding:10px;border-radius:12px;background:#fff;color:#111827;text-align:center}.dmr-qr-card img{display:block;width:176px;height:176px;max-width:100%;object-fit:contain}.dmr-qr-card strong{font-size:12px}.dmr-qr-card small{color:#4b5563;font-size:10px}.dmr-qr-loading{display:grid;place-items:center;width:176px;height:176px;color:#64748b;font-size:12px}.dmr-share-detail{min-width:0;display:flex;flex-direction:column;gap:10px}.dmr-share-detail>div:first-child{display:flex;flex-direction:column;gap:4px}.dmr-link{padding:10px 11px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;word-break:break-all;color:var(--dsw-alias-label-secondary)}.dmr-share-actions{display:flex;gap:8px;flex-wrap:wrap}.dmr-share-actions .dmr-primary{align-self:auto}.dmr-overview{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dmr-overview-card{display:flex;flex-direction:column;gap:3px;padding:12px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.dmr-overview-card>span{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dmr-overview-card strong{font-size:16px;letter-spacing:-.01em}.dmr-section{display:flex;flex-direction:column;gap:9px}.dmr-section-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.dmr-section-head small{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dmr-text-button{border:0;background:transparent;color:var(--dmr-accent);padding:8px;min-height:36px;font-weight:600}.dmr-task-list{display:flex;flex-direction:column;gap:8px}.dmr-task{display:flex;align-items:flex-start;gap:9px;padding:10px 11px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dmr-task>div{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}.dmr-task strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dmr-phase{flex:none;border-radius:999px;padding:3px 8px;font-size:11px;background:var(--dmr-accent-soft);color:var(--dmr-accent)}.dmr-phase.write{color:var(--dsw-alias-state-success-primary)}.dmr-phase.code{color:#f59e0b}.dmr-phase.search{color:#14b8a6}.dmr-empty{padding:18px 12px;text-align:center;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12px}.dmr-note{display:flex;flex-direction:column;gap:9px;padding-top:2px}.dmr-note blockquote{margin:0;padding:10px 12px;border-left:3px solid var(--dmr-accent);border-radius:0 9px 9px 0;background:var(--dmr-accent-soft);white-space:pre-wrap;font-size:12px}.dmr-note textarea{min-height:88px;resize:vertical;line-height:1.5}.dmr-note .dmr-secondary{align-self:flex-start}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media (max-width:680px){.dmr{gap:14px;font-size:14px}.dmr h3{font-size:18px}.dmr-steps{grid-template-columns:1fr;gap:8px}.dmr-steps li{padding:10px}.dmr-steps small,.dmr-hero p,.dmr-status-head p,.dmr-field small,.dmr-share small,.dmr-overview-card small,.dmr-task small,.dmr-note p{font-size:12px}.dmr-network-grid{grid-template-columns:1fr;gap:10px}.dmr-field input,.dmr-field select{font-size:16px}.dmr-primary,.dmr-secondary{width:100%;font-size:14px}.dmr-share-layout{grid-template-columns:1fr;gap:12px}.dmr-qr-card{width:min(240px,100%);margin:0 auto}.dmr-qr-card img,.dmr-qr-loading{width:210px;height:210px}.dmr-share-actions{flex-direction:column}.dmr-share-actions .dmr-primary{width:100%}.dmr-overview{gap:8px}.dmr-overview-card{padding:11px}.dmr-status-head{gap:8px}.dmr-link{font-size:11px}.dmr-note textarea{font-size:16px;min-height:104px}.dmr-note .dmr-secondary{align-self:stretch}.dmr-compact .dmr-hero-icon{width:40px;height:40px;border-radius:13px}}@media (prefers-reduced-motion:reduce){.dmr-primary,.dmr-secondary,.dmr-text-button,.dmr-icon-button,.dmr-field input,.dmr-field select,.dmr-note textarea{transition:none}}.dmr-lan{flex:none;margin-top:6px;padding-top:16px;border-top:1px solid var(--dsw-alias-border-l1)}.dmr-lan-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.dmr-lan-title{min-width:0;display:flex;flex-direction:column;gap:4px}.dmr-lan-title p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}.dmr-lan-badge{flex:none;display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:5px 10px;font-size:11px;font-weight:700;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary)}.dmr-lan-badge.on{border-color:color-mix(in srgb,var(--dmr-accent) 45%,transparent);background:var(--dmr-accent-soft);color:var(--dmr-accent)}.dmr-lan-active,.dmr-lan-idle{display:flex;flex-direction:column;gap:12px}.dmr-lan-active .dmr-share-detail small strong{color:var(--dsw-alias-state-error-primary)}.dmr-danger{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,var(--dsw-alias-border-l2));color:var(--dsw-alias-state-error-primary)}.dmr-danger:hover{border-color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}`,
|
|
172
|
+
.dmr{--dmr-accent:var(--dk-accent,#2f6fed);--dmr-accent-soft:color-mix(in srgb,var(--dmr-accent) 13%,transparent);display:flex;flex-direction:column;gap:16px;max-width:720px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}.dmr h3,.dmr h4,.dmr p{margin:0}.dmr h3{font-size:17px;line-height:1.25;letter-spacing:-.01em}.dmr h4{font-size:13px}.dmr-hero,.dmr-status-head{display:flex;align-items:flex-start;gap:12px}.dmr-hero>div,.dmr-status-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}.dmr-hero p,.dmr-status-head p,.dmr-field small,.dmr-share small,.dmr-overview-card small,.dmr-task small,.dmr-note p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}.dmr-hero-icon{width:42px;height:42px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:14px;background:var(--dmr-accent-soft);color:var(--dmr-accent);border:1px solid color-mix(in srgb,var(--dmr-accent) 35%,var(--dsw-alias-border-l1))}.dmr-steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none}.dmr-steps li{display:flex;gap:9px;padding:11px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px}.dmr-steps li>span{width:21px;height:21px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:50%;font-size:11px;font-weight:700;color:var(--dmr-accent);background:var(--dmr-accent-soft)}.dmr-steps div{display:flex;flex-direction:column;gap:3px;min-width:0}.dmr-steps strong{font-size:12px}.dmr-steps small{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.45}.dmr-field{display:flex;flex-direction:column;gap:6px;font-weight:600}.dmr-field input,.dmr-field select,.dmr-note textarea{box-sizing:border-box;width:100%;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:10px;padding:10px 12px;min-height:44px;font:inherit;outline:none;transition:border-color .18s ease,box-shadow .18s ease}.dmr-field input:focus,.dmr-field select:focus,.dmr-note textarea:focus{border-color:var(--dmr-accent);box-shadow:0 0 0 3px var(--dmr-accent-soft)}.dmr-field small{font-weight:400}.dmr-network-grid{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(150px,.6fr);gap:12px}.dmr-security{padding:12px 13px;border:1px solid color-mix(in srgb,var(--dk-warn) 48%,var(--dsw-alias-border-l1));border-radius:12px;background:color-mix(in srgb,var(--dk-warn) 9%,transparent)}.dmr-security strong{display:block;margin-bottom:4px;color:var(--dk-warn);font-size:12px}.dmr-security p{font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.55}.dmr-primary,.dmr-secondary,.dmr-text-button,.dmr-icon-button{font:inherit;touch-action:manipulation;cursor:pointer;transition:transform .15s ease,background .18s ease,border-color .18s ease,opacity .18s ease}.dmr-primary,.dmr-secondary{min-height:44px;display:inline-flex;align-items:center;justify-content:center;gap:8px;border-radius:10px;padding:9px 13px;font-weight:600}.dmr-primary{align-self:flex-start;border:1px solid var(--dmr-accent);background:var(--dmr-accent);color:#fff}.dmr-primary:hover{filter:brightness(1.05)}.dmr-secondary{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.dmr-secondary:hover{border-color:var(--dmr-accent);background:var(--dmr-accent-soft)}.dmr-primary:active,.dmr-secondary:active,.dmr-icon-button:active{transform:scale(.98)}.dmr-primary:disabled,.dmr-secondary:disabled,.dmr-text-button:disabled,.dmr-icon-button:disabled{cursor:not-allowed;opacity:.5}.dmr-message{display:flex;align-items:flex-start;gap:7px;padding:10px 12px;border-radius:10px;font-size:12px}.dmr-message.success{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 10%,transparent)}.dmr-message.error{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}.dmr-status-head{justify-content:space-between}.dmr-eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;color:var(--dmr-accent)}.dmr-eyebrow i{width:7px;height:7px;border-radius:50%;background:currentColor;box-shadow:0 0 0 4px var(--dmr-accent-soft)}.dmr-icon-button{display:inline-flex;align-items:center;justify-content:center;flex:none;width:44px;height:44px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;color:var(--dsw-alias-label-secondary)}.dmr-icon-button:hover{color:var(--dsw-alias-state-error-primary);border-color:currentColor}.dmr-share{display:flex;flex-direction:column;gap:9px;padding:13px;border:1px solid color-mix(in srgb,var(--dmr-accent) 32%,var(--dsw-alias-border-l1));border-radius:13px;background:var(--dmr-accent-soft)}.dmr-share-layout{display:grid;grid-template-columns:196px minmax(0,1fr);gap:16px;align-items:center}.dmr-qr-card{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;gap:5px;padding:10px;border-radius:12px;background:#fff;color:#111827;text-align:center}.dmr-qr-card img{display:block;width:176px;height:176px;max-width:100%;object-fit:contain}.dmr-qr-card strong{font-size:12px}.dmr-qr-card small{color:#4b5563;font-size:10px}.dmr-qr-loading{display:grid;place-items:center;width:176px;height:176px;color:#64748b;font-size:12px}.dmr-share-detail{min-width:0;display:flex;flex-direction:column;gap:10px}.dmr-share-detail>div:first-child{display:flex;flex-direction:column;gap:4px}.dmr-link{padding:10px 11px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;word-break:break-all;color:var(--dsw-alias-label-secondary)}.dmr-share-actions{display:flex;gap:8px;flex-wrap:wrap}.dmr-share-actions .dmr-primary{align-self:auto}.dmr-overview{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dmr-overview-card{display:flex;flex-direction:column;gap:3px;padding:12px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.dmr-overview-card>span{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dmr-overview-card strong{font-size:16px;letter-spacing:-.01em}.dmr-section{display:flex;flex-direction:column;gap:9px}.dmr-section-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.dmr-section-head small{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dmr-text-button{border:0;background:transparent;color:var(--dmr-accent);padding:8px;min-height:36px;font-weight:600}.dmr-task-list{display:flex;flex-direction:column;gap:8px}.dmr-task{display:flex;align-items:flex-start;gap:9px;padding:10px 11px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dmr-task>div{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}.dmr-task strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dmr-phase{flex:none;border-radius:999px;padding:3px 8px;font-size:11px;background:var(--dmr-accent-soft);color:var(--dmr-accent)}.dmr-phase.write{color:var(--dsw-alias-state-success-primary)}.dmr-phase.code{color:var(--dk-warn)}.dmr-phase.search{color:#0d9488}.dmr-empty{padding:18px 12px;text-align:center;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12px}.dmr-note{display:flex;flex-direction:column;gap:9px;padding-top:2px}.dmr-note blockquote{margin:0;padding:10px 12px;border-left:3px solid var(--dmr-accent);border-radius:0 9px 9px 0;background:var(--dmr-accent-soft);white-space:pre-wrap;font-size:12px}.dmr-note textarea{min-height:88px;resize:vertical;line-height:1.5}.dmr-note .dmr-secondary{align-self:flex-start}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media (max-width:680px){.dmr{gap:14px;font-size:14px}.dmr h3{font-size:18px}.dmr-steps{grid-template-columns:1fr;gap:8px}.dmr-steps li{padding:10px}.dmr-steps small,.dmr-hero p,.dmr-status-head p,.dmr-field small,.dmr-share small,.dmr-overview-card small,.dmr-task small,.dmr-note p{font-size:12px}.dmr-network-grid{grid-template-columns:1fr;gap:10px}.dmr-field input,.dmr-field select{font-size:16px}.dmr-primary,.dmr-secondary{width:100%;font-size:14px}.dmr-share-layout{grid-template-columns:1fr;gap:12px}.dmr-qr-card{width:min(240px,100%);margin:0 auto}.dmr-qr-card img,.dmr-qr-loading{width:210px;height:210px}.dmr-share-actions{flex-direction:column}.dmr-share-actions .dmr-primary{width:100%}.dmr-overview{gap:8px}.dmr-overview-card{padding:11px}.dmr-status-head{gap:8px}.dmr-link{font-size:11px}.dmr-note textarea{font-size:16px;min-height:104px}.dmr-note .dmr-secondary{align-self:stretch}.dmr-compact .dmr-hero-icon{width:40px;height:40px;border-radius:13px}}@media (prefers-reduced-motion:reduce){.dmr-primary,.dmr-secondary,.dmr-text-button,.dmr-icon-button,.dmr-field input,.dmr-field select,.dmr-note textarea{transition:none}}.dmr-lan{flex:none;margin-top:6px;padding-top:16px;border-top:1px solid var(--dsw-alias-border-l1)}.dmr-lan-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.dmr-lan-title{min-width:0;display:flex;flex-direction:column;gap:4px}.dmr-lan-title p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}.dmr-lan-badge{flex:none;display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:5px 10px;font-size:11px;font-weight:700;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary)}.dmr-lan-badge.on{border-color:color-mix(in srgb,var(--dmr-accent) 45%,transparent);background:var(--dmr-accent-soft);color:var(--dmr-accent)}.dmr-lan-active,.dmr-lan-idle{display:flex;flex-direction:column;gap:12px}.dmr-lan-active .dmr-share-detail small strong{color:var(--dsw-alias-state-error-primary)}.dmr-danger{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,var(--dsw-alias-border-l2));color:var(--dsw-alias-state-error-primary)}.dmr-danger:hover{border-color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}`,
|
|
173
173
|
View: MobileRelayView, HomeStat: MobileRelayHomeStat,
|
|
174
174
|
}
|
|
@@ -441,11 +441,11 @@ export const feature = {
|
|
|
441
441
|
description: "编辑各 Provider 模型目录:输入类型(文本/图片 + 标注)与思考强度档位;写回官方配置热生效,会话模型选择器即时可选",
|
|
442
442
|
css: [
|
|
443
443
|
".dkm-note{color:var(--dsw-alias-label-secondary);font-size:12px;}",
|
|
444
|
-
".dkm-warn{color:var(--
|
|
444
|
+
".dkm-warn{color:var(--dk-warn);}",
|
|
445
445
|
".dkm-error{color:var(--dsw-alias-state-error-primary);font-size:12px;}",
|
|
446
446
|
".dkm-chips{display:flex;flex-wrap:wrap;gap:6px;}",
|
|
447
447
|
".dkm-chip{cursor:pointer;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);border-radius:999px;padding:3px 12px;font-family:inherit;font-size:12px;}",
|
|
448
|
-
".dkm-chip.on{color:var(--
|
|
448
|
+
".dkm-chip.on{color:var(--dk-accent);border-color:currentColor;}",
|
|
449
449
|
".dkm-prov{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:10px;padding:10px 12px;display:flex;flex-direction:column;gap:8px;}",
|
|
450
450
|
".dkm-prov-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}",
|
|
451
451
|
".dkm-name{font-weight:600;}",
|
|
@@ -461,7 +461,7 @@ export const feature = {
|
|
|
461
461
|
".dkm-input{box-sizing:border-box;width:100%;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px 8px;font-family:inherit;font-size:12px;}",
|
|
462
462
|
".dkm-checks{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}",
|
|
463
463
|
".dkm-check{display:inline-flex;align-items:center;gap:5px;cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px;}",
|
|
464
|
-
".dkm-check input{accent-color:var(--
|
|
464
|
+
".dkm-check input{accent-color:var(--dk-accent);margin:0;}",
|
|
465
465
|
".dkm-tag{color:var(--dsw-alias-label-tertiary);}",
|
|
466
466
|
".dkm-efforts{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}",
|
|
467
467
|
".dkm-del{margin-left:auto;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:8px;padding:2px 10px;font-family:inherit;font-size:12px;flex:none;}",
|
|
@@ -469,7 +469,7 @@ export const feature = {
|
|
|
469
469
|
".dkm-add{cursor:pointer;border:1px dashed var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);border-radius:10px;padding:6px 14px;font-family:inherit;font-size:12px;align-self:flex-start;}",
|
|
470
470
|
".dkm-add:hover{color:var(--dsw-alias-label-primary);}",
|
|
471
471
|
".dkm-savebar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;border-top:1px solid var(--dsw-alias-border-l1);padding-top:10px;}",
|
|
472
|
-
".dkm-save{cursor:pointer;border:1px solid var(--
|
|
472
|
+
".dkm-save{cursor:pointer;border:1px solid var(--dk-accent);color:var(--dk-accent);background:transparent;border-radius:8px;padding:4px 14px;font-family:inherit;font-size:12px;flex:none;}",
|
|
473
473
|
".dkm-save:hover{background:var(--dsw-alias-interactive-bg-hover);}",
|
|
474
474
|
".dkm-save[disabled]{opacity:.5;cursor:default;}",
|
|
475
475
|
".dkm-msg{font-size:12px;color:var(--dsw-alias-label-secondary);}",
|
|
@@ -106,7 +106,7 @@ const css = `
|
|
|
106
106
|
.dtok-btn{cursor:pointer;border-radius:6px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);padding:4px 12px;font-size:12px;font-family:inherit;flex:none;}
|
|
107
107
|
.dtok-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}
|
|
108
108
|
.dtok-btn[disabled]{opacity:.5;cursor:default;}
|
|
109
|
-
.dtok-btn.primary{background:var(--
|
|
109
|
+
.dtok-btn.primary{background:var(--dk-accent);border-color:var(--dk-accent);color:#fff;}
|
|
110
110
|
.dtok-btn.primary:hover{filter:brightness(1.1);}
|
|
111
111
|
.dtok-body{display:flex;flex-direction:column;gap:8px;min-width:0;}
|
|
112
112
|
.dtok-cards{display:flex;gap:8px;flex-wrap:wrap;}
|
|
@@ -118,22 +118,22 @@ const css = `
|
|
|
118
118
|
.dtok-table{border-collapse:collapse;width:100%;font-size:12px;white-space:nowrap;color:var(--dsw-alias-label-primary);}
|
|
119
119
|
.dtok-table th{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);text-align:left;padding:7px 10px;position:sticky;top:0;z-index:1;border-bottom:1px solid var(--dsw-alias-border-l1);font-weight:600;user-select:none;cursor:pointer;}
|
|
120
120
|
.dtok-table th:hover{color:var(--dsw-alias-label-primary);}
|
|
121
|
-
.dtok-table td{padding:6px 10px;border-bottom:1px solid
|
|
122
|
-
.dtok-table tr:hover td{background:
|
|
121
|
+
.dtok-table td{padding:6px 10px;border-bottom:1px solid var(--dk-tdim);}
|
|
122
|
+
.dtok-table tr:hover td{background:var(--dk-hover-tint);}
|
|
123
123
|
.dtok-empty{text-align:center;color:var(--dsw-alias-label-secondary);padding:32px 0;font-size:13px;}
|
|
124
|
-
.dtok-sid{color:var(--
|
|
124
|
+
.dtok-sid{color:var(--dk-accent);cursor:pointer;text-decoration:underline dotted;}
|
|
125
125
|
.dtok-sid:hover{filter:brightness(1.25);}
|
|
126
126
|
.dtok-code{font-weight:700;font-family:ui-monospace,monospace;}
|
|
127
127
|
.dtok-code.ok{color:var(--dsw-alias-state-success-primary,#4ade80);}
|
|
128
|
-
.dtok-code.warn{color:var(--
|
|
128
|
+
.dtok-code.warn{color:var(--dk-warn);}
|
|
129
129
|
.dtok-code.err{color:var(--dsw-alias-state-error-primary,#f87171);}
|
|
130
130
|
.dtok-code.pend{color:var(--dsw-alias-label-tertiary,#94a3b8);}
|
|
131
|
-
.dtok-detail-link{color:var(--
|
|
131
|
+
.dtok-detail-link{color:var(--dk-accent);cursor:pointer;font-size:11px;}
|
|
132
132
|
.dtok-detail-link:hover{text-decoration:underline;}
|
|
133
133
|
.dtok-detail{position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;}
|
|
134
134
|
.dtok-detail-card{background:var(--dsw-alias-bg-layer-2,#1c212b);border:1px solid var(--dsw-alias-border-l2,#3a4150);border-radius:12px;padding:20px 24px;max-width:640px;width:92%;max-height:80vh;overflow:auto;color:var(--dsw-alias-label-primary,#e8eaf0);}
|
|
135
135
|
.dtok-detail-card h3{margin:0 0 12px;font-size:15px;}
|
|
136
|
-
.dtok-detail-row{display:flex;gap:8px;padding:4px 0;font-size:12px;border-bottom:1px solid
|
|
136
|
+
.dtok-detail-row{display:flex;gap:8px;padding:4px 0;font-size:12px;border-bottom:1px solid var(--dk-tdim);}
|
|
137
137
|
.dtok-detail-row .k{color:var(--dsw-alias-label-secondary,#9aa3b5);min-width:110px;flex-shrink:0;}
|
|
138
138
|
.dtok-detail-row .v{word-break:break-all;}
|
|
139
139
|
.dtok-sort-mark{opacity:0.6;margin-left:3px;}
|
|
@@ -542,7 +542,7 @@ export function TokenLogChip(props) {
|
|
|
542
542
|
return (
|
|
543
543
|
<button type="button" className={"dockchip" + (snap.err && !t ? " err" : "")} title={title} aria-label="会话用量"
|
|
544
544
|
onClick={() => openPanel("tokenlog", { sessionId: sid })}>
|
|
545
|
-
<span className="dockchip-dot" style={{ background: "
|
|
545
|
+
<span className="dockchip-dot" style={{ background: "var(--dk-warn)" }} />
|
|
546
546
|
<span>{label}</span>
|
|
547
547
|
</button>
|
|
548
548
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-dock",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "DeepSeek Harness 功能坞插件(dsh-dock):模块化功能中枢——每个功能一个 features/ 模块(可单独提取成包发布,又可经 dockBridge 装回面板)。含用量记录(LLM 调用记账/费用统计)、模型设置、图片理解代理、模型余额、任务动画(8 种氛围动效 + 桌面伙伴 3D 场景、完成通知与提示音、钉钉/飞书推送,开关独立、配置持久化);侧栏入口 + 可拖拽弹窗(最大化/最小化/缩放)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/src/client.jsx
CHANGED
|
@@ -28,7 +28,7 @@ import { feature as fGames } from "../features/games/view.jsx";
|
|
|
28
28
|
import { feature as fMobileRelay } from "../features/mobile-relay/view.jsx";
|
|
29
29
|
|
|
30
30
|
const name = "dsh-dock";
|
|
31
|
-
const DOCK_VERSION = "0.
|
|
31
|
+
const DOCK_VERSION = "0.9.1";
|
|
32
32
|
|
|
33
33
|
// ---- 内置功能注册表:新功能 = features/<id>/ 加模块 + 这里 import 一行 ----
|
|
34
34
|
const BUILTIN_FEATURES = [fTokenlog, fModelconfig, fHeartbeat, fTheme, fBalance, fAnimation, fGames, fMobileRelay];
|
|
@@ -95,6 +95,18 @@ function allModules() {
|
|
|
95
95
|
// ⚠️ 变量名不得用 CSS:浏览器存在全局 window.CSS 命名空间,bundle 任何作用域解析歧义
|
|
96
96
|
// 都会把标识符解析成该全局对象(无 .join),曾导致插件应用失败、整页启动崩溃。
|
|
97
97
|
const SHELL_CSS = [
|
|
98
|
+
// ---- 主题桥接层(亮色适配的根基)----
|
|
99
|
+
// dsh 宿主没有 --dsw-alias-accent,也没有 --dsw-alias-state-warning-primary(正确名是 state-warn-primary),
|
|
100
|
+
// 旧写法全部落到暗色系回退值(浅蓝/琥珀),亮色白底下看不清。这里在 body 上按宿主主题
|
|
101
|
+
// (body / body[data-ds-dark-theme])声明两套 dock 语义变量,各模块统一引用:
|
|
102
|
+
// --dk-accent 主强调色(亮色用深蓝保证对比度,暗色用亮蓝)
|
|
103
|
+
// --dk-warn 警告色(亮色用深琥珀,暗色用亮琥珀)
|
|
104
|
+
// --dk-ok/--dk-err 成功/错误(亮色取宿主深色值,暗色保持宿主亮色值)
|
|
105
|
+
// --dk-tdim 表格分隔线/弱边框(亮色用中性灰蓝、暗色用白透明)
|
|
106
|
+
"body{--dk-accent:#2f6fed;--dk-accent-soft:color-mix(in srgb,#2f6fed 12%,transparent);--dk-warn:#b45309;--dk-ok:var(--dsw-alias-state-success-primary,#16a34a);--dk-err:var(--dsw-alias-state-error-primary,#dc2626);--dk-tdim:rgba(100,116,139,.22);--dk-hover-tint:rgba(47,111,237,.08);--dk-games-accent:#7c3aed;}",
|
|
107
|
+
"body[data-ds-dark-theme]{--dk-accent:#4d9fff;--dk-accent-soft:color-mix(in srgb,#4d9fff 12%,transparent);--dk-warn:#fbbf24;--dk-ok:var(--dsw-alias-state-success-primary,#34d399);--dk-err:var(--dsw-alias-state-error-primary,#f87171);--dk-tdim:rgba(122,132,152,.14);--dk-hover-tint:rgba(80,110,180,.12);--dk-games-accent:#a78bfa;}",
|
|
108
|
+
// 宿主语义变量兜底别名(部分模块引用了宿主不存在的名字,统一在此补齐,取值随主题自动切换)
|
|
109
|
+
"body{--dsw-alias-accent:var(--dk-accent);--dsw-alias-accent-soft:var(--dk-accent-soft);--dsw-alias-state-warning-primary:var(--dk-warn);}",
|
|
98
110
|
".dock-root{display:flex;flex-direction:column;gap:12px;padding:4px 0;color:var(--dsw-alias-label-primary);font-size:13px;}",
|
|
99
111
|
".dock-intro{color:var(--dsw-alias-label-secondary);line-height:1.6;}",
|
|
100
112
|
".dock-card{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:10px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;}",
|
|
@@ -114,10 +126,11 @@ const SHELL_CSS = [
|
|
|
114
126
|
// 使两按钮上下成列、左右边缘完全对齐(此前右对齐/叠放都会错位)。
|
|
115
127
|
".docke2-btn{box-sizing:border-box;cursor:pointer;display:flex;align-items:center;gap:8px;border:none;background:transparent;color:var(--dsw-alias-label-primary);border-radius:12px;width:calc(100% + 4px);height:42px;margin:4px -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
|
|
116
128
|
".docke2-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
|
|
117
|
-
".docke2-btn.docke2-on{color:var(--
|
|
129
|
+
".docke2-btn.docke2-on{color:var(--dk-accent);background:var(--dk-accent-soft);}",
|
|
118
130
|
".docke2-btn.docke2-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;border-radius:50%;justify-content:center;gap:0;}",
|
|
119
131
|
".docke2-label{white-space:nowrap;overflow:hidden;}",
|
|
120
|
-
|
|
132
|
+
// 遮罩固定用深色半透明:亮色主题下宿主 bg-layer-1 是纯白,白底遮罩会让弹窗与页面完全无层次
|
|
133
|
+
".dockm-backdrop{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;background:rgba(15,17,21,.45);backdrop-filter:blur(4px);pointer-events:auto;animation:dockm-fade .15s var(--ds-ease-in-out);}",
|
|
121
134
|
"@keyframes dockm-fade{from{opacity:0}to{opacity:1}}",
|
|
122
135
|
".dockm-dialog{box-sizing:border-box;position:relative;width:min(1080px,calc(100vw - 32px));height:min(700px,calc(100vh - 32px));display:flex;flex-direction:column;border-radius:16px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:0 20px 64px rgb(0 0 0 / .32);overflow:hidden;animation:dockm-pop .18s var(--ds-ease-in-out);}",
|
|
123
136
|
"@keyframes dockm-pop{from{opacity:0;transform:translateY(10px) scale(.985)}to{opacity:1;transform:none}}",
|
|
@@ -137,7 +150,7 @@ const SHELL_CSS = [
|
|
|
137
150
|
".dockm-nav{flex:none;width:176px;display:flex;flex-direction:column;gap:2px;padding:10px 8px;overflow-y:auto;border-right:1px solid var(--dsw-alias-border-l1);}",
|
|
138
151
|
".dockm-nav-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border:none;background:transparent;border-radius:10px;cursor:pointer;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:13px;text-align:left;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
|
|
139
152
|
".dockm-nav-item:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
|
|
140
|
-
".dockm-nav-item.on{background:var(--
|
|
153
|
+
".dockm-nav-item.on{background:var(--dk-accent-soft);color:var(--dsw-alias-label-primary);font-weight:600;}",
|
|
141
154
|
".dockm-nav-item .dockm-badge{margin-left:auto;}",
|
|
142
155
|
".dockm-dot{width:8px;height:8px;border-radius:50%;flex:none;}",
|
|
143
156
|
".dockm-badge{flex:none;font-size:11px;border-radius:999px;padding:0 8px;color:var(--dsw-alias-label-tertiary);border:1px solid var(--dsw-alias-border-l2);}",
|
|
@@ -153,10 +166,10 @@ const SHELL_CSS = [
|
|
|
153
166
|
".dockm-switch:hover{color:var(--dsw-alias-label-primary);}",
|
|
154
167
|
".dockm-switch.on{color:var(--dsw-alias-state-success-primary);border-color:currentColor;}",
|
|
155
168
|
// 首页总揽(dockh- 前缀):导航首项图标 + 模块卡片网格
|
|
156
|
-
".dockm-navhome{display:inline-flex;align-items:center;color:var(--
|
|
169
|
+
".dockm-navhome{display:inline-flex;align-items:center;color:var(--dk-accent);flex:none;}",
|
|
157
170
|
".dockh-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px;}",
|
|
158
171
|
".dockh-card{display:flex;flex-direction:column;gap:6px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:10px;padding:10px 12px;cursor:pointer;transition:border-color .15s var(--ds-ease-in-out);}",
|
|
159
|
-
".dockh-card:hover,.dockh-card:focus-visible{border-color:var(--
|
|
172
|
+
".dockh-card:hover,.dockh-card:focus-visible{border-color:var(--dk-accent);outline:none;}",
|
|
160
173
|
".dockh-head{display:flex;align-items:center;gap:8px;}",
|
|
161
174
|
".dockh-name{font-weight:600;flex:none;}",
|
|
162
175
|
".dockh-badge{flex:none;font-size:11px;border-radius:999px;padding:0 8px;color:var(--dsw-alias-label-tertiary);border:1px solid var(--dsw-alias-border-l2);}",
|
|
@@ -168,7 +181,7 @@ const SHELL_CSS = [
|
|
|
168
181
|
// iOS 风滑动开关(首页卡片 / 弹层页脚 / 设置页共用的启停控件)
|
|
169
182
|
".dock-sw{flex:none;position:relative;width:34px;height:19px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;padding:0;transition:background .18s var(--ds-ease-in-out),border-color .18s var(--ds-ease-in-out);}",
|
|
170
183
|
".dock-sw::after{content:\"\";position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgb(0 0 0 / .35);transition:transform .18s var(--ds-ease-in-out);}",
|
|
171
|
-
".dock-sw:hover{border-color:var(--
|
|
184
|
+
".dock-sw:hover{border-color:var(--dk-accent);}",
|
|
172
185
|
".dock-sw.on{background:var(--dsw-alias-state-success-primary,#34d399);border-color:transparent;}",
|
|
173
186
|
".dock-sw.on::after{transform:translateX(15px);}",
|
|
174
187
|
// 弹层页脚:文字标签 + 开关 成组
|