dsh-dock 0.7.1 → 0.8.0

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 CHANGED
@@ -41,7 +41,7 @@ __export(client_exports, {
41
41
  inject: () => inject
42
42
  });
43
43
  module.exports = __toCommonJS(client_exports);
44
- var import_react9 = __toESM(require("react"), 1);
44
+ var import_react19 = __toESM(require("react"), 1);
45
45
 
46
46
  // src/shared.js
47
47
  var import_react = __toESM(require("react"), 1);
@@ -2330,16 +2330,16 @@ function composerDockPoint(side = "right") {
2330
2330
  '[contenteditable="true"][data-slot*="conversation.input"]'
2331
2331
  ];
2332
2332
  const seen = /* @__PURE__ */ new Set();
2333
- const candidates = [];
2333
+ const candidates2 = [];
2334
2334
  for (const selector of selectors) {
2335
2335
  for (const el of document.querySelectorAll(selector)) {
2336
2336
  if (seen.has(el)) continue;
2337
2337
  seen.add(el);
2338
2338
  const r2 = el.getBoundingClientRect();
2339
- if (r2.width > 80 && r2.height > 16 && r2.bottom > window.innerHeight * 0.48) candidates.push({ el, r: r2 });
2339
+ if (r2.width > 80 && r2.height > 16 && r2.bottom > window.innerHeight * 0.48) candidates2.push({ el, r: r2 });
2340
2340
  }
2341
2341
  }
2342
- const best = candidates.sort((a, b) => b.r.bottom - a.r.bottom)[0];
2342
+ const best = candidates2.sort((a, b) => b.r.bottom - a.r.bottom)[0];
2343
2343
  if (!best) return fallbackComposerDockPoint(side);
2344
2344
  const r = best.r;
2345
2345
  const x = side === "ready" ? Math.max(r.left + 16, r.right - 210) : fallbackComposerDockPoint(side).x;
@@ -3920,9 +3920,3462 @@ var feature6 = {
3920
3920
  Overlay: AnimationOverlay
3921
3921
  };
3922
3922
 
3923
+ // features/games/view.jsx
3924
+ var import_react18 = require("react");
3925
+
3926
+ // features/games/games/shared.jsx
3927
+ var import_react9 = require("react");
3928
+ function isTypingTarget(event) {
3929
+ const tag = event.target && event.target.tagName || "";
3930
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
3931
+ if (event.target && event.target.isContentEditable) return true;
3932
+ return false;
3933
+ }
3934
+ function useGameControls(stageRef, paused, onKeyDown, onKeyUp) {
3935
+ const pausedRef = (0, import_react9.useRef)(!!paused);
3936
+ pausedRef.current = !!paused;
3937
+ (0, import_react9.useEffect)(() => {
3938
+ if (pausedRef.current) return;
3939
+ const el = stageRef && stageRef.current;
3940
+ if (!el) return;
3941
+ const t = setTimeout(() => {
3942
+ try {
3943
+ el.focus({ preventScroll: true });
3944
+ } catch {
3945
+ }
3946
+ }, 60);
3947
+ return () => clearTimeout(t);
3948
+ }, [paused]);
3949
+ (0, import_react9.useEffect)(() => {
3950
+ const inStage = (event) => {
3951
+ const el = stageRef && stageRef.current;
3952
+ return !!el && (el === event.target || el.contains(event.target));
3953
+ };
3954
+ const handleDown = (event) => {
3955
+ if (pausedRef.current) return;
3956
+ if (isTypingTarget(event)) return;
3957
+ if (inStage(event)) return;
3958
+ if (typeof onKeyDown === "function") onKeyDown(event);
3959
+ };
3960
+ const handleUp = (event) => {
3961
+ if (pausedRef.current) return;
3962
+ if (isTypingTarget(event)) return;
3963
+ if (inStage(event)) return;
3964
+ if (typeof onKeyUp === "function") onKeyUp(event);
3965
+ };
3966
+ window.addEventListener("keydown", handleDown);
3967
+ window.addEventListener("keyup", handleUp);
3968
+ return () => {
3969
+ window.removeEventListener("keydown", handleDown);
3970
+ window.removeEventListener("keyup", handleUp);
3971
+ };
3972
+ }, [onKeyDown, onKeyUp]);
3973
+ }
3974
+ function focusStage(stageRef) {
3975
+ if (stageRef && stageRef.current) {
3976
+ try {
3977
+ stageRef.current.focus({ preventScroll: true });
3978
+ } catch {
3979
+ }
3980
+ }
3981
+ }
3982
+ var _ctx = null;
3983
+ function audioCtx() {
3984
+ if (typeof window === "undefined") return null;
3985
+ const AC = window.AudioContext || window.webkitAudioContext;
3986
+ if (!AC) return null;
3987
+ try {
3988
+ if (!_ctx) _ctx = new AC();
3989
+ if (_ctx.state === "suspended") _ctx.resume().catch(() => {
3990
+ });
3991
+ return _ctx;
3992
+ } catch {
3993
+ return null;
3994
+ }
3995
+ }
3996
+ var SFX_DEFS = {
3997
+ move: { f: 240, t: "square", d: 0.035, g: 0.07 },
3998
+ rotate: { f: 340, t: "square", d: 0.05, g: 0.08 },
3999
+ lock: { f: 150, t: "triangle", d: 0.08, g: 0.15 },
4000
+ step: { f: 190, t: "triangle", d: 0.03, g: 0.06 },
4001
+ push: { f: 115, t: "triangle", d: 0.07, g: 0.14 },
4002
+ stone: { f: 520, t: "triangle", d: 0.05, g: 0.13, f2: 430 },
4003
+ capture: { f: 175, t: "square", d: 0.09, g: 0.15, f2: 105 },
4004
+ check: { t: "square", g: 0.12, d: 0.24, seq: [[660, 0], [660, 0.14]] },
4005
+ eat: { f: 620, t: "square", d: 0.06, g: 0.11, f2: 920 },
4006
+ bounce: { f: 420, t: "square", d: 0.03, g: 0.07 },
4007
+ brick: { f: 740, t: "square", d: 0.05, g: 0.1, f2: 500 },
4008
+ clear: { t: "sine", g: 0.15, d: 0.28, seq: [[523, 0], [659, 0.09], [784, 0.18], [1047, 0.27]] },
4009
+ win: { t: "sine", g: 0.14, d: 0.34, seq: [[523, 0], [659, 0.11], [784, 0.22], [1047, 0.33], [784, 0.47], [1047, 0.56]] },
4010
+ over: { t: "sawtooth", g: 0.11, d: 0.38, seq: [[392, 0], [311, 0.13], [233, 0.27], [185, 0.44]] }
4011
+ };
4012
+ function playSfx(name) {
4013
+ const def = SFX_DEFS[name];
4014
+ const ac = def && audioCtx();
4015
+ if (!ac) return;
4016
+ const t0 = ac.currentTime;
4017
+ const events = def.seq || [[def.f, 0]];
4018
+ for (const [freq, dt] of events) {
4019
+ let osc, gain;
4020
+ try {
4021
+ osc = ac.createOscillator();
4022
+ gain = ac.createGain();
4023
+ osc.type = def.t || "square";
4024
+ osc.frequency.setValueAtTime(freq, t0 + dt);
4025
+ if (def.f2) osc.frequency.exponentialRampToValueAtTime(Math.max(30, def.f2), t0 + dt + def.d);
4026
+ gain.gain.setValueAtTime(1e-4, t0 + dt);
4027
+ gain.gain.exponentialRampToValueAtTime(def.g, t0 + dt + 8e-3);
4028
+ gain.gain.exponentialRampToValueAtTime(1e-4, t0 + dt + def.d);
4029
+ osc.connect(gain).connect(ac.destination);
4030
+ osc.start(t0 + dt);
4031
+ osc.stop(t0 + dt + def.d + 0.03);
4032
+ } catch {
4033
+ if (osc) try {
4034
+ osc.stop();
4035
+ } catch {
4036
+ }
4037
+ }
4038
+ }
4039
+ }
4040
+
4041
+ // features/games/games/tetris.jsx
4042
+ var import_react10 = require("react");
4043
+ var import_jsx_runtime3 = require("react/jsx-runtime");
4044
+ var COLS = 10;
4045
+ var ROWS = 20;
4046
+ var SHAPES = [
4047
+ { color: "#22d3ee", mat: [[1, 1, 1, 1]] },
4048
+ { color: "#facc15", mat: [[1, 1], [1, 1]] },
4049
+ { color: "#c084fc", mat: [[0, 1, 0], [1, 1, 1]] },
4050
+ { color: "#4ade80", mat: [[0, 1, 1], [1, 1, 0]] },
4051
+ { color: "#f87171", mat: [[1, 1, 0], [0, 1, 1]] },
4052
+ { color: "#60a5fa", mat: [[1, 0, 0], [1, 1, 1]] },
4053
+ { color: "#fb923c", mat: [[0, 0, 1], [1, 1, 1]] }
4054
+ ];
4055
+ function rotateCW(mat) {
4056
+ return mat[0].map((_, col) => mat.map((row) => row[col]).reverse());
4057
+ }
4058
+ function rotations(shape) {
4059
+ const list = [shape.mat];
4060
+ let cur = shape.mat;
4061
+ for (let i = 0; i < 3; i += 1) {
4062
+ cur = rotateCW(cur);
4063
+ if (!list.some((m) => JSON.stringify(m) === JSON.stringify(cur))) list.push(cur);
4064
+ }
4065
+ return list;
4066
+ }
4067
+ var PIECES = SHAPES.map((s) => ({ color: s.color, rots: rotations(s) }));
4068
+ var TETRIS_GRID = (() => {
4069
+ const lines = [];
4070
+ let k = 0;
4071
+ for (let i = 1; i < 10; i += 1) lines.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: i * 100, y1: 0, x2: i * 100, y2: 2e3 }, k++));
4072
+ for (let j = 1; j < 20; j += 1) lines.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: j * 100, x2: 1e3, y2: j * 100 }, k++));
4073
+ return lines;
4074
+ })();
4075
+ function emptyBoard() {
4076
+ return Array.from({ length: ROWS }, () => Array(COLS).fill(null));
4077
+ }
4078
+ function collides(board, mat, x, y) {
4079
+ for (let r = 0; r < mat.length; r += 1) {
4080
+ for (let c = 0; c < mat[r].length; c += 1) {
4081
+ if (!mat[r][c]) continue;
4082
+ const bx = x + c, by = y + r;
4083
+ if (bx < 0 || bx >= COLS || by >= ROWS || by >= 0 && board[by][bx]) return true;
4084
+ }
4085
+ }
4086
+ return false;
4087
+ }
4088
+ function merge(board, mat, x, y, color) {
4089
+ const next = board.map((row) => row.slice());
4090
+ for (let r = 0; r < mat.length; r += 1) {
4091
+ for (let c = 0; c < mat[r].length; c += 1) {
4092
+ if (mat[r][c] && y + r >= 0) next[y + r][x + c] = color;
4093
+ }
4094
+ }
4095
+ return next;
4096
+ }
4097
+ function clearLines(board) {
4098
+ const kept = board.filter((row) => row.some((cell) => !cell));
4099
+ const cleared = ROWS - kept.length;
4100
+ while (kept.length < ROWS) kept.unshift(Array(COLS).fill(null));
4101
+ return { board: kept, cleared };
4102
+ }
4103
+ function seedBag() {
4104
+ const bag = SHAPES.map((_, i) => i);
4105
+ for (let i = bag.length - 1; i > 0; i -= 1) {
4106
+ const j = Math.floor(Math.random() * (i + 1));
4107
+ [bag[i], bag[j]] = [bag[j], bag[i]];
4108
+ }
4109
+ return bag;
4110
+ }
4111
+ var PIECE_SEQ = 0;
4112
+ function makePiece(type, x, y) {
4113
+ const p = PIECES[type];
4114
+ return { id: ++PIECE_SEQ, type, rot: 0, x, y: y != null ? y : -1, mat: p.rots[0], color: p.color };
4115
+ }
4116
+ function initial() {
4117
+ const bag = seedBag();
4118
+ const piece = makePiece(bag.pop(), 3);
4119
+ const next = bag.pop();
4120
+ return { board: emptyBoard(), piece, next, bag, score: 0, lines: 0, level: 1, over: false };
4121
+ }
4122
+ function spawnFrom(state) {
4123
+ let bag = state.bag && state.bag.length ? state.bag.slice() : seedBag();
4124
+ const type = state.next != null ? state.next : bag.pop();
4125
+ let next = bag.length ? bag.pop() : null;
4126
+ if (next == null) {
4127
+ bag = seedBag();
4128
+ next = bag.pop();
4129
+ }
4130
+ const piece = makePiece(type, 3);
4131
+ const over = collides(state.board, piece.mat, piece.x, piece.y) || state.over;
4132
+ return Object.assign({}, state, { bag, next, piece, over });
4133
+ }
4134
+ function advance(state) {
4135
+ if (state.over) return state;
4136
+ const p = state.piece;
4137
+ if (!collides(state.board, p.mat, p.x, p.y + 1)) {
4138
+ return Object.assign({}, state, { piece: Object.assign({}, p, { y: p.y + 1 }), score: state.score + 1 });
4139
+ }
4140
+ const merged = merge(state.board, p.mat, p.x, p.y, p.color);
4141
+ playSfx("lock");
4142
+ const cleared = clearLines(merged);
4143
+ if (cleared.cleared) playSfx(cleared.cleared >= 4 ? "win" : "clear");
4144
+ const lines = state.lines + cleared.cleared;
4145
+ const level = Math.floor(lines / 10) + 1;
4146
+ const base = [0, 100, 300, 500, 800][cleared.cleared] || 800;
4147
+ const scored = Object.assign({}, state, {
4148
+ board: cleared.board,
4149
+ lines,
4150
+ level,
4151
+ score: state.score + base * state.level
4152
+ });
4153
+ return spawnFrom(scored);
4154
+ }
4155
+ function hardDrop(state) {
4156
+ if (state.over) return state;
4157
+ let y = state.piece.y, dist = 0;
4158
+ while (!collides(state.board, state.piece.mat, state.piece.x, y + 1)) {
4159
+ y += 1;
4160
+ dist += 1;
4161
+ }
4162
+ const p = Object.assign({}, state.piece, { y });
4163
+ const merged = merge(state.board, p.mat, p.x, p.y, p.color);
4164
+ playSfx("lock");
4165
+ const cleared = clearLines(merged);
4166
+ if (cleared.cleared) playSfx(cleared.cleared >= 4 ? "win" : "clear");
4167
+ const lines = state.lines + cleared.cleared;
4168
+ const level = Math.floor(lines / 10) + 1;
4169
+ const base = [0, 100, 300, 500, 800][cleared.cleared] || 800;
4170
+ const scored = Object.assign({}, state, {
4171
+ board: cleared.board,
4172
+ lines,
4173
+ level,
4174
+ score: state.score + base * state.level + dist * 2
4175
+ });
4176
+ return spawnFrom(scored);
4177
+ }
4178
+ var KEY_ACTIONS = {
4179
+ "ArrowLeft": "left",
4180
+ "a": "left",
4181
+ "A": "left",
4182
+ "ArrowRight": "right",
4183
+ "d": "right",
4184
+ "D": "right",
4185
+ // 下键 = 快速下滑(一格一格连降,松开即停;空格/回车才是直落到底)。
4186
+ "ArrowDown": "down",
4187
+ "s": "down",
4188
+ "S": "down",
4189
+ "ArrowUp": "cw",
4190
+ "x": "cw",
4191
+ "X": "cw",
4192
+ "z": "ccw",
4193
+ "Z": "ccw",
4194
+ " ": "drop",
4195
+ "Enter": "drop"
4196
+ };
4197
+ function TetrisGame(props) {
4198
+ const [state, setState] = (0, import_react10.useState)(initial);
4199
+ const [manualPause, setManualPause] = (0, import_react10.useState)(false);
4200
+ const pausedRef = (0, import_react10.useRef)(!!(props && props.paused));
4201
+ pausedRef.current = !!(props && props.paused) || manualPause;
4202
+ const stageRef = (0, import_react10.useRef)(null);
4203
+ const act = (0, import_react10.useCallback)((action) => setState((s) => {
4204
+ if (pausedRef.current) return s;
4205
+ if (action === "left") {
4206
+ playSfx("move");
4207
+ const x2 = s.piece.x - 1;
4208
+ if (collides(s.board, s.piece.mat, x2, s.piece.y)) return s;
4209
+ return Object.assign({}, s, { piece: Object.assign({}, s.piece, { x: x2 }) });
4210
+ }
4211
+ if (action === "right") {
4212
+ playSfx("move");
4213
+ const x2 = s.piece.x + 1;
4214
+ if (collides(s.board, s.piece.mat, x2, s.piece.y)) return s;
4215
+ return Object.assign({}, s, { piece: Object.assign({}, s.piece, { x: x2 }) });
4216
+ }
4217
+ if (action === "down") return advance(s);
4218
+ if (action === "drop") return hardDrop(s);
4219
+ if (action === "cw" || action === "ccw") {
4220
+ playSfx("rotate");
4221
+ return rotatePiece(s, action === "cw" ? 1 : -1);
4222
+ }
4223
+ return s;
4224
+ }), []);
4225
+ const softRef = (0, import_react10.useRef)(null);
4226
+ const stateRef = (0, import_react10.useRef)(state);
4227
+ stateRef.current = state;
4228
+ const holdIdRef = (0, import_react10.useRef)(null);
4229
+ const stopSoft = (0, import_react10.useCallback)(() => {
4230
+ if (softRef.current) {
4231
+ clearInterval(softRef.current);
4232
+ softRef.current = null;
4233
+ }
4234
+ }, []);
4235
+ (0, import_react10.useEffect)(() => {
4236
+ if (state.over || manualPause || props.paused) stopSoft();
4237
+ return stopSoft;
4238
+ }, [state.over, manualPause, props.paused, stopSoft]);
4239
+ const onKeyDown = (0, import_react10.useCallback)((event) => {
4240
+ const action = KEY_ACTIONS[event.key];
4241
+ if (action) {
4242
+ event.preventDefault();
4243
+ if (action === "down") {
4244
+ const pieceId = stateRef.current.piece && stateRef.current.piece.id;
4245
+ act("down");
4246
+ if (!softRef.current && !pausedRef.current) {
4247
+ holdIdRef.current = pieceId;
4248
+ softRef.current = setInterval(() => {
4249
+ const cur = stateRef.current;
4250
+ if (pausedRef.current || cur.over || !cur.piece || cur.piece.id !== holdIdRef.current) {
4251
+ stopSoft();
4252
+ return;
4253
+ }
4254
+ act("down");
4255
+ }, 45);
4256
+ }
4257
+ return;
4258
+ }
4259
+ act(action);
4260
+ return;
4261
+ }
4262
+ if (event.key.toLowerCase() === "p") {
4263
+ event.preventDefault();
4264
+ setManualPause((p) => !p);
4265
+ } else if (event.key.toLowerCase() === "r") {
4266
+ event.preventDefault();
4267
+ setState(initial());
4268
+ setManualPause(false);
4269
+ }
4270
+ }, [act, stopSoft]);
4271
+ const onKeyUp = (0, import_react10.useCallback)((event) => {
4272
+ if (KEY_ACTIONS[event.key] === "down") stopSoft();
4273
+ }, [stopSoft]);
4274
+ (0, import_react10.useEffect)(() => {
4275
+ if (state.over) playSfx("over");
4276
+ }, [state.over]);
4277
+ (0, import_react10.useEffect)(() => {
4278
+ if (pausedRef.current || state.over) return;
4279
+ const speed = Math.max(80, 720 - (state.level - 1) * 70);
4280
+ const timer = setInterval(() => act("down"), speed);
4281
+ return () => clearInterval(timer);
4282
+ }, [state.level, state.over, act, props.paused, manualPause]);
4283
+ useGameControls(stageRef, pausedRef.current, onKeyDown, onKeyUp);
4284
+ const display = state.board.map((row) => row.slice());
4285
+ if (!state.over) {
4286
+ const m = state.piece.mat;
4287
+ for (let r = 0; r < m.length; r += 1) {
4288
+ for (let c = 0; c < m[r].length; c += 1) {
4289
+ if (m[r][c]) {
4290
+ const by = state.piece.y + r, bx = state.piece.x + c;
4291
+ if (by >= 0 && by < ROWS && bx >= 0 && bx < COLS) display[by][bx] = state.piece.color;
4292
+ }
4293
+ }
4294
+ }
4295
+ }
4296
+ const nextType = state.next != null ? state.next : 0;
4297
+ const nextMat = PIECES[nextType].rots[0];
4298
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "dgame-game", "aria-label": "\u4FC4\u7F57\u65AF\u65B9\u5757", children: [
4299
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-game-head", children: [
4300
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
4301
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: "\u4FC4\u7F57\u65AF\u65B9\u5757" }),
4302
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: "\u62FC\u6EE1\u4E00\u884C\u5373\u6D88\u9664\uFF0C\u901F\u5EA6\u968F\u7B49\u7EA7\u52A0\u5FEB\uFF1B\u65B9\u5757\u5806\u5230\u9876\u7AEF\u5C31\u7ED3\u675F\u3002" })
4303
+ ] }),
4304
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-score", children: [
4305
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
4306
+ "\u5F97\u5206 ",
4307
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: state.score })
4308
+ ] }),
4309
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
4310
+ "\u7B49\u7EA7 ",
4311
+ state.level,
4312
+ " \xB7 \u6D88\u884C ",
4313
+ state.lines
4314
+ ] })
4315
+ ] })
4316
+ ] }),
4317
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-tetris", children: [
4318
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-tetris-board", ref: stageRef, tabIndex: 0, onKeyDown, onKeyUp, onBlur: stopSoft, onClick: () => focusStage(stageRef), "aria-label": "\u4FC4\u7F57\u65AF\u65B9\u5757\u6E38\u620F\u533A\u57DF\uFF0C\u4F7F\u7528\u65B9\u5411\u952E\u4E0E\u7A7A\u683C\u63A7\u5236", children: [
4319
+ display.map((row, rr) => row.map((cell, cc) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "dgame-tetris-cell", style: cell ? { background: cell, boxShadow: "0 0 8px " + cell + "aa, inset 0 0 4px rgba(255,255,255,.5)" } : void 0 }, rr + "-" + cc))),
4320
+ state.over ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-over", children: [
4321
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "\u65B9\u5757\u5806\u5230\u9876\u4E86" }),
4322
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => setState(initial()), children: "\u518D\u6765\u4E00\u5C40" })
4323
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "dgame-tetris-lines", viewBox: "0 0 1000 2000", preserveAspectRatio: "none", "aria-hidden": "true", children: TETRIS_GRID }),
4324
+ !state.over && (props.paused || manualPause) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-over", children: [
4325
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "\u5DF2\u6682\u505C" }),
4326
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => setManualPause(false), children: "\u7EE7\u7EED\uFF08P\uFF09" })
4327
+ ] }) : null
4328
+ ] }),
4329
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-tetris-side", children: [
4330
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-tetris-next", children: [
4331
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u4E0B\u4E00\u4E2A" }),
4332
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dgame-tetris-nextbox", children: nextMat.map((row, r) => row.map((cell, c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "dgame-tetris-cell", style: cell ? { background: PIECES[nextType].color } : void 0 }, r + "-" + c))) })
4333
+ ] }),
4334
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-tetris-keys", children: [
4335
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: "\u64CD\u4F5C" }),
4336
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u2190/\u2192 \u79FB\u52A8 \xB7 \u2193 \u5FEB\u901F\u4E0B\u6ED1 \xB7 \u2191/X \u65CB\u8F6C \xB7 Z \u53CD\u5411 \xB7 \u7A7A\u683C \u76F4\u843D \xB7 P \u6682\u505C \xB7 R \u91CD\u5F00" })
4337
+ ] })
4338
+ ] })
4339
+ ] }),
4340
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dgame-controls", children: [
4341
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "aria-label": "\u5DE6\u79FB", onClick: () => act("left"), children: "\u2190 \u5DE6\u79FB" }),
4342
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u65B9\u5411\u952E / X\u3001Z\u3001\u7A7A\u683C\u3001P" }),
4343
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", "aria-label": "\u53F3\u79FB", onClick: () => act("right"), children: [
4344
+ "\u53F3\u79FB ",
4345
+ "->"
4346
+ ] })
4347
+ ] })
4348
+ ] });
4349
+ }
4350
+ function rotatePiece(state, dir) {
4351
+ const rots = PIECES[state.piece.type].rots;
4352
+ const len = rots.length;
4353
+ const nextRot = ((state.piece.rot + dir) % len + len) % len;
4354
+ const mat = rots[nextRot];
4355
+ const kicks = [0, -1, 1, -2, 2, -1 - (state.piece.y < 0 ? 1 : 0), 1 + (state.piece.y < 0 ? 1 : 0), -1, 1];
4356
+ for (const kx of kicks) {
4357
+ if (!collides(state.board, mat, state.piece.x + kx, state.piece.y)) {
4358
+ return Object.assign({}, state, { piece: Object.assign({}, state.piece, { rot: nextRot, x: state.piece.x + kx, mat }) });
4359
+ }
4360
+ }
4361
+ return state;
4362
+ }
4363
+ function TetrisPreview() {
4364
+ const COLORS2 = { I: "I", O: "O", T: "T", S: "S", Z: "Z", J: "J", L: "L" };
4365
+ const ROWS3 = [
4366
+ "..........",
4367
+ "....TT....",
4368
+ "..........",
4369
+ ".......Z..",
4370
+ "..OO.S.Z..",
4371
+ "SS..LSOZZ.",
4372
+ "JJSLLSOZZI",
4373
+ "JJSLLOSOII"
4374
+ ];
4375
+ const isFall = (r) => r === 1;
4376
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dgcov-prev dgcov-prev-tetris", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dgp-tet-grid", children: ROWS3.flatMap((row, r) => row.split("").map((ch, c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: COLORS2[ch] ? "c" + COLORS2[ch] + (isFall(r) ? " fall" : "") : "" }, r + "-" + c))) }) });
4377
+ }
4378
+ var tetrisGame = { Game: TetrisGame, Preview: TetrisPreview, css: `
4379
+ .dgame-tetris{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-start;align-self:center;width:fit-content;max-width:100%;margin-inline:auto;justify-content:center}.dgame-tetris-board{position:relative;flex:none;display:grid;grid-template-columns:repeat(10,1fr);grid-template-rows:repeat(20,1fr);width:min(100%,188px);aspect-ratio:10/20;border:1px solid color-mix(in srgb,rgb(34 211 238) 35%,var(--dsw-alias-border-l1));border-radius:8px;overflow:hidden;outline:none;background:linear-gradient(180deg,#0c1024,#131a3d);padding:0;box-shadow:inset 0 0 30px rgb(0 0 0/.45)}.dgame-tetris-board:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,rgb(34 211 238) 40%,transparent)}.dgame-tetris-cell{display:block;background:transparent;border-radius:2px;margin:1px}.dgame-tetris-nextbox{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(2,1fr);width:60px;height:32px}.dgame-tetris-side{flex:1 1 96px;display:flex;flex-direction:column;gap:9px;min-width:0;max-width:220px;color:var(--dsw-alias-label-tertiary);font-size:11px}.dgame-tetris-side b{color:var(--dsw-alias-label-secondary);font-size:12px}.dgame-tetris-side span{font-size:11px}.dgame-tetris-nextbox .dgame-tetris-cell{margin:1px}.dgame-tetris-keys span{display:block;line-height:1.6;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.dgame-tetris-next{display:flex;flex-direction:column;gap:5px;align-items:center}.dgame-tetris-next span{color:var(--dsw-alias-label-secondary)}.dgame-tetris-lines{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}.dgame-tetris-lines line{stroke:#60a5fa;stroke-opacity:.12;stroke-width:2}@media (max-width:680px){.dgame-tetris{width:100%;gap:8px}.dgame-tetris-board{width:min(100%,170px)}}
4380
+ ` };
4381
+
4382
+ // features/games/games/sokoban.jsx
4383
+ var import_react11 = require("react");
4384
+ var import_jsx_runtime4 = require("react/jsx-runtime");
4385
+ var LEVELS = [
4386
+ ["######", "# #", "#@$ .#", "# #", "######"],
4387
+ ["######", "# @ #", "# $ #", "# . #", "######"],
4388
+ ["#######", "# @ #", "# $$ #", "# .. #", "#######"],
4389
+ ["######", "#@ #", "# $ #", "# . #", "# #", "######"],
4390
+ ["#######", "# @ #", "# $ #", "# $ .#", "# . #", "#######"],
4391
+ ["########", "# #", "# $ $ #", "# . . #", "# @ #", "########"],
4392
+ // —— 扩充关:由易到难(已用求解器验证全部可解)——
4393
+ ["########", "# ## #", "#@ $ . #", "# ## #", "########"],
4394
+ ["########", "# @ #", "# $$$ #", "# ... #", "# #", "########"],
4395
+ ["########", "#@ #", "# $ $ #", "# # #", "# . #. #", "########"],
4396
+ ["########", "# @ #", "# $$ #", "# ..# #", "# #", "########"],
4397
+ ["########", "#@ #", "# $ $ #", "# #", "# .# . #", "########"],
4398
+ ["#########", "# #", "# $ $ $ #", "# ... #", "# @ #", "# #", "#########"],
4399
+ ["#########", "# @ #", "# $#$$ #", "# ... #", "# ## #", "#########"],
4400
+ ["#########", "#@ # #", "# $ $ #", "# ### #", "# . . #", "#########"],
4401
+ ["#########", "# ###", "# $$ # #", "# .. #@ #", "# $$ # #", "# .. #", "#########"]
4402
+ ];
4403
+ function cloneGrid(grid) {
4404
+ return grid.map((row) => row.slice());
4405
+ }
4406
+ function parse(rows) {
4407
+ return rows.map((row) => row.split(""));
4408
+ }
4409
+ function findPlayer(grid) {
4410
+ for (let r = 0; r < grid.length; r += 1) {
4411
+ for (let c = 0; c < grid[r].length; c += 1) {
4412
+ if (grid[r][c] === "@" || grid[r][c] === "+") return { r, c };
4413
+ }
4414
+ }
4415
+ return { r: 0, c: 0 };
4416
+ }
4417
+ function isTarget(ch) {
4418
+ return ch === "." || ch === "*" || ch === "+";
4419
+ }
4420
+ function isBox(ch) {
4421
+ return ch === "$" || ch === "*";
4422
+ }
4423
+ function won(grid) {
4424
+ for (const row of grid) for (const ch of row) if (ch === "$") return false;
4425
+ return true;
4426
+ }
4427
+ function makeInitial(levelIndex) {
4428
+ const grid = parse(LEVELS[levelIndex]);
4429
+ return { grid, moves: 0, pushes: 0, level: levelIndex, wonNew: false, history: [] };
4430
+ }
4431
+ function step(state, dr, dc) {
4432
+ if (state.wonNew) return state;
4433
+ const grid = cloneGrid(state.grid);
4434
+ const p = findPlayer(grid);
4435
+ const nr = p.r + dr, nc = p.c + dc;
4436
+ if (nr < 0 || nr >= grid.length || nc < 0 || nc >= grid[0].length) return state;
4437
+ const target = grid[nr][nc];
4438
+ if (target === "#") return state;
4439
+ grid[p.r][p.c] = isTarget(grid[p.r][p.c]) ? "." : " ";
4440
+ if (isBox(target)) {
4441
+ const br = nr + dr, bc = nc + dc;
4442
+ if (br < 0 || br >= grid.length || bc < 0 || bc >= grid[0].length) return state;
4443
+ const beyond = grid[br][bc];
4444
+ if (beyond === "#" || isBox(beyond)) return state;
4445
+ playSfx("push");
4446
+ grid[br][bc] = isTarget(beyond) ? "*" : "$";
4447
+ grid[nr][nc] = isTarget(target) ? "+" : "@";
4448
+ return Object.assign({}, state, {
4449
+ grid,
4450
+ moves: state.moves + 1,
4451
+ pushes: state.pushes + 1,
4452
+ wonNew: won(grid),
4453
+ history: state.history.concat([{ grid: state.grid, moves: state.moves, pushes: state.pushes }])
4454
+ });
4455
+ }
4456
+ grid[nr][nc] = isTarget(target) ? "+" : "@";
4457
+ playSfx("step");
4458
+ return Object.assign({}, state, {
4459
+ grid,
4460
+ moves: state.moves + 1,
4461
+ wonNew: won(grid),
4462
+ history: state.history.concat([{ grid: state.grid, moves: state.moves, pushes: state.pushes }])
4463
+ });
4464
+ }
4465
+ function undo(state) {
4466
+ if (!state.history.length) return state;
4467
+ const prev = state.history[state.history.length - 1];
4468
+ return Object.assign({}, state, {
4469
+ grid: cloneGrid(prev.grid),
4470
+ moves: prev.moves,
4471
+ pushes: prev.pushes,
4472
+ history: state.history.slice(0, -1),
4473
+ wonNew: false
4474
+ });
4475
+ }
4476
+ var DIRS = { ArrowUp: [-1, 0], ArrowDown: [1, 0], ArrowLeft: [0, -1], ArrowRight: [0, 1], w: [-1, 0], s: [1, 0], a: [0, -1], d: [0, 1], W: [-1, 0], S: [1, 0], A: [0, -1], D: [0, 1] };
4477
+ function SokobanGame(props) {
4478
+ const [level, setLevel] = (0, import_react11.useState)(0);
4479
+ const [state, setState] = (0, import_react11.useState)(() => makeInitial(0));
4480
+ const stageRef = (0, import_react11.useRef)(null);
4481
+ const levelRef = (0, import_react11.useRef)(level);
4482
+ levelRef.current = level;
4483
+ const move = (0, import_react11.useCallback)((dr, dc) => setState((s) => step(s, dr, dc)), []);
4484
+ const prevWon = (0, import_react11.useRef)(false);
4485
+ (0, import_react11.useEffect)(() => {
4486
+ if (state.wonNew && !prevWon.current) playSfx("win");
4487
+ prevWon.current = state.wonNew;
4488
+ }, [state.wonNew]);
4489
+ const nextLevel = (0, import_react11.useCallback)(() => {
4490
+ const n = (levelRef.current + 1) % LEVELS.length;
4491
+ setLevel(n);
4492
+ setState(makeInitial(n));
4493
+ }, []);
4494
+ const pickLevel = (0, import_react11.useCallback)((i) => {
4495
+ setLevel(i);
4496
+ setState(makeInitial(i));
4497
+ }, []);
4498
+ const reset = (0, import_react11.useCallback)(() => setState(makeInitial(levelRef.current)), []);
4499
+ const onKeyDown = (0, import_react11.useCallback)((event) => {
4500
+ const d = DIRS[event.key];
4501
+ if (d) {
4502
+ event.preventDefault();
4503
+ move(d[0], d[1]);
4504
+ return;
4505
+ }
4506
+ if (event.key.toLowerCase() === "u") {
4507
+ event.preventDefault();
4508
+ setState((s) => undo(s));
4509
+ } else if (event.key.toLowerCase() === "r") {
4510
+ event.preventDefault();
4511
+ reset();
4512
+ } else if (event.key === "Enter" && state.wonNew) {
4513
+ event.preventDefault();
4514
+ nextLevel();
4515
+ }
4516
+ }, [move, state.wonNew, reset, nextLevel]);
4517
+ useGameControls(stageRef, props.paused, onKeyDown);
4518
+ const grid = state.grid;
4519
+ const rows = grid.length, cols = grid[0].length;
4520
+ const width = "min(" + cols * 26 + "px, 100%, calc((100vh - 250px) * " + cols + " / " + rows + "))";
4521
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "dgame-game", "aria-label": "\u63A8\u7BB1\u5B50", children: [
4522
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dgame-game-head", children: [
4523
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
4524
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: "\u63A8\u7BB1\u5B50" }),
4525
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "\u7528\u65B9\u5411\u952E\u628A\u7BB1\u5B50\u63A8\u5230\u76EE\u6807\u683C\u5B50\u4E0A\uFF1B\u5168\u90E8\u5230\u4F4D\u5373\u8FC7\u5173\uFF08U \u64A4\u9500\u3001R \u91CD\u7F6E\uFF09\u3002" })
4526
+ ] }),
4527
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dgame-score", children: [
4528
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
4529
+ "\u6B65\u6570 ",
4530
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: state.moves })
4531
+ ] }),
4532
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
4533
+ "\u63A8\u7BB1 ",
4534
+ state.pushes
4535
+ ] })
4536
+ ] })
4537
+ ] }),
4538
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dgame-sokoban-levels", role: "tablist", "aria-label": "\u9009\u62E9\u5173\u5361", children: [
4539
+ LEVELS.map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", role: "tab", "aria-selected": i === level, className: i === level ? "on" : "", onClick: () => pickLevel(i), children: i + 1 }) }, i)),
4540
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: () => setState((s) => undo(s)), children: "\u64A4\u9500" }),
4541
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: reset, children: "\u91CD\u7F6E" })
4542
+ ] }),
4543
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dgame-sokoban-stage", ref: stageRef, tabIndex: 0, onKeyDown, onClick: () => focusStage(stageRef), style: { width, gridTemplateColumns: "repeat(" + cols + ",1fr)", gridTemplateRows: "repeat(" + rows + ",1fr)", aspectRatio: cols + " / " + rows }, "aria-label": "\u63A8\u7BB1\u5B50\u6E38\u620F\u533A\u57DF\uFF0C\u4F7F\u7528\u65B9\u5411\u952E\u79FB\u52A8\uFF0CU \u64A4\u9500\uFF0CR \u91CD\u7F6E", children: [
4544
+ grid.map((row, r) => row.map((ch, c) => {
4545
+ let cls = "dgame-soko-cell";
4546
+ if (ch === "#") cls += " wall";
4547
+ else if (ch === "." || ch === "*" || ch === "+") cls += " target";
4548
+ if (ch === "$" || ch === "*") cls += " box";
4549
+ if (ch === "@" || ch === "+") cls += " player";
4550
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("i", { className: cls, "aria-hidden": "true" }, r + "-" + c);
4551
+ })),
4552
+ state.wonNew ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dgame-over", children: [
4553
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("strong", { children: [
4554
+ "\u5168\u641E\u5B9A\uFF01\u7B2C ",
4555
+ level + 1,
4556
+ " \u5173\u901A\u8FC7"
4557
+ ] }),
4558
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: nextLevel, children: level + 1 < LEVELS.length ? "\u4E0B\u4E00\u5173" : "\u91CD\u65B0\u5F00\u59CB" })
4559
+ ] }) : null
4560
+ ] }),
4561
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dgame-controls", children: [
4562
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "\u5411\u4E0A", onClick: () => move(-1, 0), children: "\u2191" }),
4563
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u65B9\u5411\u952E / WASD \xB7 U \u64A4\u9500 \xB7 R \u91CD\u7F6E" }),
4564
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "\u5411\u4E0B", onClick: () => move(1, 0), children: "\u2193" })
4565
+ ] })
4566
+ ] });
4567
+ }
4568
+ function SokobanPreview() {
4569
+ const ROWS3 = [
4570
+ "#######",
4571
+ "# #",
4572
+ "# .$@ #",
4573
+ "# .* #",
4574
+ "#######"
4575
+ ];
4576
+ const cls = { "#": "wall", " ": "floor", ".": "target", "$": "box", "*": "boxT", "@": "player", "+": "player" };
4577
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dgcov-prev dgcov-prev-soko", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dgp-soko-grid", children: ROWS3.flatMap((row, r) => row.split("").map((ch, c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("i", { className: "dgp-soko-" + cls[ch] }, r + "-" + c))) }) });
4578
+ }
4579
+ var sokobanGame = { Game: SokobanGame, Preview: SokobanPreview, css: `
4580
+ .dgame-sokoban-stage{position:relative;display:grid;align-self:center;gap:0;padding:6px;border:1px solid color-mix(in srgb,rgb(250 204 21) 32%,var(--dsw-alias-border-l1));border-radius:8px;background:linear-gradient(180deg,#181420,#14101f);outline:none;box-shadow:inset 0 0 26px rgb(0 0 0/.4)}.dgame-sokoban-stage:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,rgb(250 204 21) 30%,transparent)}.dgame-soko-cell{position:relative;display:block;min-width:0;min-height:0}.dgame-soko-cell.wall{background:linear-gradient(180deg,#3a3350,#241f3a);box-shadow:inset 0 1px 0 rgba(255,255,255,.12),inset 0 -1px 0 rgba(0,0,0,.4)}.dgame-soko-cell.target::before{content:'';position:absolute;inset:30%;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fde68a,#f59e0b 70%);box-shadow:0 0 8px color-mix(in srgb,#f59e0b 55%,transparent);opacity:.9}.dgame-soko-cell.box::after{content:'';position:absolute;inset:12%;border-radius:5px;background:linear-gradient(135deg,#d4a24a,#8a5a1c);box-shadow:0 2px 5px rgb(0 0 0/.45),inset 0 1px 0 rgba(255,255,255,.4);border:1px solid color-mix(in srgb,#fde68a 45%,transparent)}.dgame-soko-cell.box.target::after{background:linear-gradient(135deg,#34d399,#0f766e);border-color:#a7f3d0}.dgame-soko-cell.player::before{content:'';position:absolute;inset:18%;border-radius:50%;background:radial-gradient(circle at 35% 30%,#e0f2fe,#38bdf8 70%);box-shadow:0 0 9px color-mix(in srgb,#38bdf8 70%,transparent)}.dgame-sokoban-levels{display:flex;gap:6px;align-items:center;align-self:center;flex-wrap:wrap;width:min(100%,340px);justify-content:center}.dgame-sokoban-levels button{min-width:30px;height:28px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:11px;cursor:pointer;transition:all .15s ease}.dgame-sokoban-levels button.on{color:#fde68a;border-color:color-mix(in srgb,#f59e0b 60%,transparent);background:color-mix(in srgb,#f59e0b 18%,transparent)}.dgame-sokoban-levels button:hover{border-color:var(--dgame-accent);color:var(--dsw-alias-label-primary)}
4581
+ ` };
4582
+
4583
+ // features/games/games/gomoku.jsx
4584
+ var import_react12 = require("react");
4585
+ var import_jsx_runtime5 = require("react/jsx-runtime");
4586
+ var GOMOKU_LINES = (() => {
4587
+ const P = (n) => n * 100 + 50;
4588
+ const parts = [/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: P(0), y: P(0), width: 1400, height: 1400, strokeWidth: 6.5, fill: "none" }, "frame")];
4589
+ let k = 0;
4590
+ for (let i = 1; i <= 13; i += 1) {
4591
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: P(i), y1: P(0), x2: P(i), y2: P(14) }, k++));
4592
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: P(0), y1: P(i), x2: P(14), y2: P(i) }, k++));
4593
+ }
4594
+ for (const [r, c] of [[3, 3], [3, 11], [7, 7], [11, 3], [11, 11]]) {
4595
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "dgame-gomoku-star", cx: P(c), cy: P(r), r: 15 }, k++));
4596
+ }
4597
+ return parts;
4598
+ })();
4599
+ var SIZE = 15;
4600
+ var DIRS2 = [[0, 1], [1, 0], [1, 1], [1, -1]];
4601
+ function emptyBoard2() {
4602
+ return Array.from({ length: SIZE }, () => Array(SIZE).fill(null));
4603
+ }
4604
+ function inBoard(r, c) {
4605
+ return r >= 0 && r < SIZE && c >= 0 && c < SIZE;
4606
+ }
4607
+ function lineScore(board, r, c, color) {
4608
+ let total = 0;
4609
+ for (const [dr, dc] of DIRS2) {
4610
+ let count = 1;
4611
+ let rr = r + dr, cc = c + dc;
4612
+ let openEnds = 0;
4613
+ while (inBoard(rr, cc) && board[rr][cc] === color) {
4614
+ count += 1;
4615
+ rr += dr;
4616
+ cc += dc;
4617
+ }
4618
+ if (inBoard(rr, cc) && board[rr][cc] === null) openEnds += 1;
4619
+ rr = r - dr;
4620
+ cc = c - dc;
4621
+ while (inBoard(rr, cc) && board[rr][cc] === color) {
4622
+ count += 1;
4623
+ rr -= dr;
4624
+ cc -= dc;
4625
+ }
4626
+ if (inBoard(rr, cc) && board[rr][cc] === null) openEnds += 1;
4627
+ total += patternScore(count, openEnds);
4628
+ }
4629
+ return total;
4630
+ }
4631
+ function patternScore(count, openEnds) {
4632
+ if (count >= 5) return 1e7;
4633
+ if (openEnds === 0) return 0;
4634
+ if (count === 4) return openEnds === 2 ? 1e6 : 1e5;
4635
+ if (count === 3) return openEnds === 2 ? 1e4 : 1e3;
4636
+ if (count === 2) return openEnds === 2 ? 200 : 20;
4637
+ if (count === 1) return openEnds === 2 ? 10 : 1;
4638
+ return 0;
4639
+ }
4640
+ function winLine(board, r, c, color) {
4641
+ for (const [dr, dc] of DIRS2) {
4642
+ const cells = [[r, c]];
4643
+ let rr = r + dr, cc = c + dc;
4644
+ while (inBoard(rr, cc) && board[rr][cc] === color) {
4645
+ cells.push([rr, cc]);
4646
+ rr += dr;
4647
+ cc += dc;
4648
+ }
4649
+ rr = r - dr;
4650
+ cc = c - dc;
4651
+ while (inBoard(rr, cc) && board[rr][cc] === color) {
4652
+ cells.push([rr, cc]);
4653
+ rr -= dr;
4654
+ cc -= dc;
4655
+ }
4656
+ if (cells.length >= 5) return cells;
4657
+ }
4658
+ return null;
4659
+ }
4660
+ function boardFull(board) {
4661
+ for (const row of board) for (const cell of row) if (!cell) return false;
4662
+ return true;
4663
+ }
4664
+ function candidates(board) {
4665
+ const set = [];
4666
+ const seen = /* @__PURE__ */ new Set();
4667
+ let hasStone = false;
4668
+ for (let r = 0; r < SIZE; r += 1) {
4669
+ for (let c = 0; c < SIZE; c += 1) {
4670
+ if (!board[r][c]) continue;
4671
+ hasStone = true;
4672
+ for (let dr = -2; dr <= 2; dr += 1) {
4673
+ for (let dc = -2; dc <= 2; dc += 1) {
4674
+ const rr = r + dr, cc = c + dc;
4675
+ if (inBoard(rr, cc) && !board[rr][cc] && !seen.has(rr * SIZE + cc)) {
4676
+ seen.add(rr * SIZE + cc);
4677
+ set.push([rr, cc]);
4678
+ }
4679
+ }
4680
+ }
4681
+ }
4682
+ }
4683
+ if (!hasStone) return [[7, 7], [7, 8], [8, 7]];
4684
+ return set;
4685
+ }
4686
+ function aiMove(board) {
4687
+ const cands = candidates(board);
4688
+ let best = null, bestScore = -1;
4689
+ for (const [r, c] of cands) {
4690
+ const attack = lineScore(board, r, c, "w");
4691
+ const defend = lineScore(board, r, c, "b");
4692
+ const score = attack + defend * 0.92;
4693
+ if (score > bestScore) {
4694
+ bestScore = score;
4695
+ best = [r, c];
4696
+ }
4697
+ }
4698
+ return best || [7, 7];
4699
+ }
4700
+ function GomokuGame(props) {
4701
+ const [board, setBoard] = (0, import_react12.useState)(emptyBoard2);
4702
+ const [turn, setTurn] = (0, import_react12.useState)("b");
4703
+ const [winner, setWinner] = (0, import_react12.useState)(null);
4704
+ const [winCells, setWinCells] = (0, import_react12.useState)(null);
4705
+ const [showResult, setShowResult] = (0, import_react12.useState)(false);
4706
+ const [last, setLast] = (0, import_react12.useState)(null);
4707
+ const [history, setHistory] = (0, import_react12.useState)([]);
4708
+ const [thinking, setThinking] = (0, import_react12.useState)(false);
4709
+ const boardRef = (0, import_react12.useRef)(board);
4710
+ const stageRef = (0, import_react12.useRef)(null);
4711
+ boardRef.current = board;
4712
+ const turnRef = (0, import_react12.useRef)(turn);
4713
+ turnRef.current = turn;
4714
+ const winRef = (0, import_react12.useRef)(winner);
4715
+ winRef.current = winner;
4716
+ const finish = (0, import_react12.useCallback)((b, r, c, color) => {
4717
+ const line = winLine(b, r, c, color);
4718
+ if (line) {
4719
+ setWinner(color);
4720
+ setWinCells(line);
4721
+ setShowResult(true);
4722
+ playSfx(color === "b" ? "win" : "over");
4723
+ } else if (boardFull(b)) {
4724
+ setWinner("draw");
4725
+ setWinCells(null);
4726
+ setShowResult(true);
4727
+ playSfx("clear");
4728
+ } else playSfx("stone");
4729
+ }, []);
4730
+ const place = (0, import_react12.useCallback)((r, c, color) => {
4731
+ if (boardRef.current[r][c]) return false;
4732
+ const next = boardRef.current.map((row) => row.slice());
4733
+ next[r][c] = color;
4734
+ setBoard(next);
4735
+ boardRef.current = next;
4736
+ setLast({ r, c });
4737
+ setHistory((h) => h.concat([{ r, c, color }]));
4738
+ finish(next, r, c, color);
4739
+ return true;
4740
+ }, [finish]);
4741
+ (0, import_react12.useEffect)(() => {
4742
+ if (turn !== "w" || winner || props.paused) return;
4743
+ setThinking(true);
4744
+ const t = setTimeout(() => {
4745
+ const [r, c] = aiMove(boardRef.current);
4746
+ place(r, c, "w");
4747
+ setTurn("b");
4748
+ setThinking(false);
4749
+ }, 260);
4750
+ return () => clearTimeout(t);
4751
+ }, [turn, winner, props.paused, place]);
4752
+ const onCell = (0, import_react12.useCallback)((r, c) => {
4753
+ if (winRef.current || turnRef.current !== "b" || thinking || boardRef.current[r][c]) return;
4754
+ place(r, c, "b");
4755
+ setTurn("w");
4756
+ }, [thinking, place]);
4757
+ const reset = (0, import_react12.useCallback)(() => {
4758
+ setBoard(emptyBoard2());
4759
+ setTurn("b");
4760
+ setWinner(null);
4761
+ setWinCells(null);
4762
+ setShowResult(false);
4763
+ setLast(null);
4764
+ setHistory([]);
4765
+ setThinking(false);
4766
+ }, []);
4767
+ const undo2 = (0, import_react12.useCallback)(() => {
4768
+ if (thinking || history.length < 2) return;
4769
+ const h = history.slice(0, -2);
4770
+ const next = emptyBoard2();
4771
+ for (const m of h) next[m.r][m.c] = m.color;
4772
+ setBoard(next);
4773
+ boardRef.current = next;
4774
+ setHistory(h);
4775
+ setTurn("b");
4776
+ setWinner(null);
4777
+ setWinCells(null);
4778
+ setShowResult(false);
4779
+ setLast(h.length ? h[h.length - 1] : null);
4780
+ }, [history, thinking]);
4781
+ const onKeyDown = (0, import_react12.useCallback)((event) => {
4782
+ if (event.key.toLowerCase() === "r") {
4783
+ event.preventDefault();
4784
+ reset();
4785
+ } else if (event.key.toLowerCase() === "u") {
4786
+ event.preventDefault();
4787
+ undo2();
4788
+ }
4789
+ }, [reset, undo2]);
4790
+ useGameControls(stageRef, props.paused, onKeyDown);
4791
+ const statusText = winner === "b" ? "\u4F60\u8D62\u4E86 \xB7 \u4E94\u5B50\u8FDE\u73E0" : winner === "w" ? "AI \u8D62\u4E86 \xB7 \u518D\u8BD5\u4E00\u6B21" : winner === "draw" ? "\u548C\u68CB \xB7 \u68CB\u76D8\u5DF2\u6EE1" : turn === "b" ? "\u8F6E\u5230\u4F60\u843D\u5B50\uFF08\u9ED1\uFF09" : "AI \u601D\u8003\u4E2D\u2026\uFF08\u767D\uFF09";
4792
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "dgame-game", "aria-label": "\u4E94\u5B50\u68CB", children: [
4793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dgame-game-head", children: [
4794
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
4795
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { children: "\u4E94\u5B50\u68CB" }),
4796
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "\u4F60\u6267\u9ED1\u5148\u624B\uFF0CAI \u6267\u767D\uFF1B\u5148\u5728\u6A2A\u7AD6\u659C\u4EFB\u4E00\u7EBF\u8FDE\u6210\u4E94\u5B50\u8005\u80DC\u3002" })
4797
+ ] }),
4798
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dgame-score", children: [
4799
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u56DE\u5408" }),
4800
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { className: "dg-gomoku-status", children: statusText }),
4801
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: undo2, children: "\u64A4\u9500" }),
4802
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: reset, children: "\u91CD\u5F00" })
4803
+ ] })
4804
+ ] }),
4805
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dgame-gomoku-board", ref: stageRef, tabIndex: 0, onKeyDown, onClick: () => focusStage(stageRef), role: "grid", "aria-label": "\u5341\u4E94\u8DEF\u4E94\u5B50\u68CB\u68CB\u76D8", children: [
4806
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "dgame-gomoku-lines", viewBox: "0 0 1500 1500", preserveAspectRatio: "none", "aria-hidden": "true", children: GOMOKU_LINES }),
4807
+ board.map((row, r) => row.map((cell, c) => {
4808
+ const isWin = winCells && winCells.some(([wr, wc]) => wr === r && wc === c);
4809
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4810
+ "button",
4811
+ {
4812
+ type: "button",
4813
+ role: "gridcell",
4814
+ className: "dgame-gomoku-cell" + (cell ? " filled " + cell : "") + (last && last.r === r && last.c === c ? " last" : "") + (isWin ? " wincell" : ""),
4815
+ "aria-label": "\u7B2C" + (r + 1) + "\u884C\u7B2C" + (c + 1) + "\u5217" + (cell === "b" ? " \u9ED1\u5B50" : cell === "w" ? " \u767D\u5B50" : " \u7A7A\u683C"),
4816
+ onClick: () => {
4817
+ focusStage(stageRef);
4818
+ onCell(r, c);
4819
+ },
4820
+ children: cell ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("i", { className: "dgame-gomoku-stone " + (cell === "b" ? "black" : "white") }) : null
4821
+ },
4822
+ r + "-" + c
4823
+ );
4824
+ })),
4825
+ winner && showResult ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dgame-gomoku-result", role: "status", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dgame-gomoku-result-card " + winner, children: [
4826
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dgame-gomoku-result-icon", "aria-hidden": "true", children: winner === "b" ? "\u{1F389}" : winner === "w" ? "\u{1F916}" : "\u{1F91D}" }),
4827
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: winner === "b" ? "\u4F60\u8D62\u4E86\uFF01" : winner === "w" ? "AI \u8D62\u4E86" : "\u548C\u68CB" }),
4828
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: winner === "b" ? "\u4E94\u5B50\u8FDE\u73E0\uFF0C\u6F02\u4EAE\uFF01" : winner === "w" ? "\u518D\u63A5\u518D\u5389\uFF0C\u5377\u571F\u91CD\u6765\u3002" : "\u68CB\u76D8\u5DF2\u6EE1\uFF0C\u5E73\u5206\u79CB\u8272\u3002" }),
4829
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dgame-gomoku-result-actions", children: [
4830
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: (e) => {
4831
+ e.stopPropagation();
4832
+ setShowResult(false);
4833
+ }, children: "\u67E5\u770B\u68CB\u76D8" }),
4834
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: (e) => {
4835
+ e.stopPropagation();
4836
+ reset();
4837
+ }, children: "\u518D\u6765\u4E00\u5C40" })
4838
+ ] })
4839
+ ] }) }) : null
4840
+ ] }),
4841
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dgame-controls", children: [
4842
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: undo2, children: "\u64A4\u9500" }),
4843
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: statusText }),
4844
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: reset, children: "\u91CD\u5F00" })
4845
+ ] })
4846
+ ] });
4847
+ }
4848
+ function GomokuPreview() {
4849
+ const P = (n) => n * 100 + 50;
4850
+ const stones = [[3, 3, "b"], [6, 3, "w"], [4, 4, "w"], [5, 5, "b"], [2, 6, "w"], [6, 6, "b"]];
4851
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dgcov-prev dgcov-prev-gomoku", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { viewBox: "0 0 900 900", preserveAspectRatio: "xMidYMid slice", children: [
4852
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: P(0), y: P(0), width: 800, height: 800, fill: "none", strokeWidth: 11 }),
4853
+ [1, 2, 3, 4, 5, 6, 7].map((i) => [
4854
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: P(i), y1: P(0), x2: P(i), y2: P(8) }, "v" + i),
4855
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: P(0), y1: P(i), x2: P(8), y2: P(i) }, "h" + i)
4856
+ ]),
4857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "dgp-star", cx: P(4), cy: P(4), r: 16 }),
4858
+ stones.map(([r, c, col], i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "dgp-stone-" + col, cx: P(c), cy: P(r), r: 40 }, i))
4859
+ ] }) });
4860
+ }
4861
+ var gomokuGame = { Game: GomokuGame, Preview: GomokuPreview, css: `
4862
+ .dgame-gomoku-board{position:relative;display:grid;grid-template-columns:repeat(15,1fr);grid-template-rows:repeat(15,1fr);align-self:center;width:min(100%,min(90vw,330px));aspect-ratio:1/1;padding:12px;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(120% 110% at 30% 10%,#edc97f,#dcae62 52%,#cc9a52 80%,#bb8844)}.dgame-gomoku-lines{position:absolute;inset:12px;pointer-events:none}.dgame-gomoku-lines line{stroke:#66431a;stroke-width:3;fill:none}.dgame-gomoku-star{fill:#66431a}.dgame-gomoku-cell{position:relative;z-index:1;background:transparent;border:none;cursor:pointer;padding:0;margin:0;outline:none;min-width:0;min-height:0}.dgame-gomoku-cell:focus-visible::after{content:'';position:absolute;inset:8%;border:2px solid rgba(255,255,255,.7);border-radius:50%}.dgame-gomoku-stone{position:absolute;inset:10%;border-radius:50%;display:block}.dgame-gomoku-stone.black{background:radial-gradient(circle at 34% 30%,#4b5563,#111827 68%);box-shadow:0 2px 5px rgb(0 0 0/.5),inset 0 1px 1px rgba(255,255,255,.25)}.dgame-gomoku-stone.white{background:radial-gradient(circle at 34% 30%,#ffffff,#cbd5e1 70%);box-shadow:0 2px 5px rgb(0 0 0/.4),inset 0 1px 1px rgba(255,255,255,.9)}.dgame-gomoku-cell.last .dgame-gomoku-stone::after{content:'';position:absolute;inset:32%;border-radius:50%;background:rgba(255,80,80,.9)}.dg-gomoku-status{font-size:12px;color:var(--dsw-alias-label-tertiary)}
4863
+ .dgame-gomoku-cell.wincell .dgame-gomoku-stone{animation:dg-gomoku-win 1s ease-in-out infinite;z-index:1}
4864
+ @keyframes dg-gomoku-win{0%,100%{box-shadow:0 2px 5px rgb(0 0 0/.5),0 0 0 0 rgba(255,214,102,.85)}50%{box-shadow:0 2px 5px rgb(0 0 0/.5),0 0 0 6px rgba(255,214,102,0)}}
4865
+ .dgame-gomoku-result{position:absolute;inset:-1px;z-index:5;display:grid;place-items:center;background:rgba(10,10,20,.55);backdrop-filter:blur(2px);border-radius:8px;animation:dg-gomoku-fade .25s ease-out}
4866
+ .dgame-gomoku-result-card{display:flex;flex-direction:column;align-items:center;gap:6px;padding:18px 26px;border-radius:14px;background:linear-gradient(160deg,#241f38,#171427);border:1px solid color-mix(in srgb,var(--dgame-accent,#a78bfa) 45%,transparent);box-shadow:0 12px 40px rgb(0 0 0/.55),0 0 0 4px rgba(167,139,250,.08);animation:dg-gomoku-pop .35s cubic-bezier(.22,1.6,.36,1);text-align:center}
4867
+ .dgame-gomoku-result-card strong{font-size:18px;color:#fff}
4868
+ .dgame-gomoku-result-card.b strong{color:#fbbf24}.dgame-gomoku-result-card.w strong{color:#e5e7eb}.dgame-gomoku-result-card.draw strong{color:#cbd5e1}
4869
+ .dgame-gomoku-result-icon{font-size:28px;line-height:1;animation:dg-gomoku-bounce 1.2s ease-in-out infinite}
4870
+ .dgame-gomoku-result-card p{margin:0;font-size:12px;color:var(--dsw-alias-label-tertiary,#9ca3af)}
4871
+ .dgame-gomoku-result-actions{display:flex;gap:10px;margin-top:6px}
4872
+ .dgame-gomoku-result-actions button{padding:6px 14px;border-radius:8px;border:1px solid color-mix(in srgb,var(--dgame-accent,#a78bfa) 40%,transparent);background:rgba(167,139,250,.14);color:#ede9fe;font-size:13px;cursor:pointer}
4873
+ .dgame-gomoku-result-actions button:hover{background:rgba(167,139,250,.28)}
4874
+ @keyframes dg-gomoku-fade{from{opacity:0}to{opacity:1}}
4875
+ @keyframes dg-gomoku-pop{from{opacity:0;transform:scale(.6)}to{opacity:1;transform:scale(1)}}
4876
+ @keyframes dg-gomoku-bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
4877
+ ` };
4878
+
4879
+ // features/games/games/xiangqi.jsx
4880
+ var import_react13 = require("react");
4881
+
4882
+ // features/games/xiangqi-core.js
4883
+ var ROWS2 = 10;
4884
+ var COLS2 = 9;
4885
+ function pieceChar(p) {
4886
+ const map = {
4887
+ g: { r: "\u5E25", b: "\u5C07" },
4888
+ a: { r: "\u4ED5", b: "\u58EB" },
4889
+ e: { r: "\u76F8", b: "\u8C61" },
4890
+ h: { r: "\u99AC", b: "\u99AC" },
4891
+ k: { r: "\u8ECA", b: "\u8ECA" },
4892
+ c: { r: "\u70AE", b: "\u70AE" },
4893
+ p: { r: "\u5175", b: "\u5352" }
4894
+ };
4895
+ return map[p.t] && map[p.t][p.c];
4896
+ }
4897
+ var VALUE = { g: 1e5, k: 900, c: 450, h: 400, e: 200, a: 200, p: 100 };
4898
+ function initialBoard() {
4899
+ const b = Array.from({ length: ROWS2 }, () => Array(COLS2).fill(null));
4900
+ const back = ["k", "h", "e", "a", "g", "a", "e", "h", "k"];
4901
+ back.forEach((t, c) => {
4902
+ b[0][c] = { t, c: "b" };
4903
+ });
4904
+ b[2][1] = { t: "c", c: "b" };
4905
+ b[2][7] = { t: "c", c: "b" };
4906
+ [0, 2, 4, 6, 8].forEach((c) => {
4907
+ b[3][c] = { t: "p", c: "b" };
4908
+ });
4909
+ back.forEach((t, c) => {
4910
+ b[9][c] = { t, c: "r" };
4911
+ });
4912
+ b[7][1] = { t: "c", c: "r" };
4913
+ b[7][7] = { t: "c", c: "r" };
4914
+ [0, 2, 4, 6, 8].forEach((c) => {
4915
+ b[6][c] = { t: "p", c: "r" };
4916
+ });
4917
+ return b;
4918
+ }
4919
+ function cloneBoard(b) {
4920
+ return b.map((row) => row.slice());
4921
+ }
4922
+ function posKey(board) {
4923
+ let s = "";
4924
+ for (const row of board) for (const p of row) s += p ? p.t + p.c : ".";
4925
+ return s;
4926
+ }
4927
+ function inBounds(r, c) {
4928
+ return r >= 0 && r < ROWS2 && c >= 0 && c < COLS2;
4929
+ }
4930
+ function inPalace(r, c, color) {
4931
+ if (c < 3 || c > 5) return false;
4932
+ return color === "r" ? r >= 7 && r <= 9 : r >= 0 && r <= 2;
4933
+ }
4934
+ function ownSide(r, color) {
4935
+ return color === "r" ? r >= 5 : r <= 4;
4936
+ }
4937
+ function crossedRiver(r, color) {
4938
+ return color === "r" ? r <= 4 : r >= 5;
4939
+ }
4940
+ function countBetween(board, r, c, tr, tc, dr, dc) {
4941
+ let n = 0, rr = r + dr, cc = c + dc;
4942
+ while (!(rr === tr && cc === tc)) {
4943
+ if (rr < 0 || rr >= ROWS2 || cc < 0 || cc >= COLS2) return -1;
4944
+ if (board[rr][cc]) n += 1;
4945
+ rr += dr;
4946
+ cc += dc;
4947
+ }
4948
+ return n;
4949
+ }
4950
+ function attacks(board, r, c, tr, tc) {
4951
+ const p = board[r][c];
4952
+ if (!p) return false;
4953
+ const dr = Math.sign(tr - r), dc = Math.sign(tc - c);
4954
+ const t = board[tr][tc];
4955
+ if (p.t === "k") {
4956
+ if (r !== tr && c !== tc) return false;
4957
+ return countBetween(board, r, c, tr, tc, dr, dc) === 0;
4958
+ }
4959
+ if (p.t === "c") {
4960
+ if (r !== tr && c !== tc) return false;
4961
+ if (dr === 0 && dc === 0) return false;
4962
+ return countBetween(board, r, c, tr, tc, dr, dc) === 1 && !!t;
4963
+ }
4964
+ if (p.t === "h") {
4965
+ const adr = Math.abs(tr - r), adc = Math.abs(tc - c);
4966
+ if (!(adr === 2 && adc === 1 || adr === 1 && adc === 2)) return false;
4967
+ const legR = adr === 2 ? r + dr : r;
4968
+ const legC = adc === 2 ? c + dc : c;
4969
+ return !board[legR][legC];
4970
+ }
4971
+ if (p.t === "g") {
4972
+ return Math.abs(tr - r) + Math.abs(tc - c) === 1 && inPalace(tr, tc, p.c);
4973
+ }
4974
+ if (p.t === "a") {
4975
+ return Math.abs(tr - r) === 1 && Math.abs(tc - c) === 1 && inPalace(tr, tc, p.c);
4976
+ }
4977
+ if (p.t === "e") {
4978
+ const adr = Math.abs(tr - r), adc = Math.abs(tc - c);
4979
+ if (!(adr === 2 && adc === 2)) return false;
4980
+ if (!ownSide(tr, p.c)) return false;
4981
+ const eyeR = r + dr, eyeC = c + dc;
4982
+ return !board[eyeR][eyeC];
4983
+ }
4984
+ if (p.t === "p") {
4985
+ const forward = p.c === "r" ? r - 1 : r + 1;
4986
+ if (tr === forward && tc === c) return true;
4987
+ if (crossedRiver(r, p.c) && tr === r && Math.abs(tc - c) === 1) return true;
4988
+ return false;
4989
+ }
4990
+ return false;
4991
+ }
4992
+ function findGeneral(board, color) {
4993
+ for (let r = 0; r < ROWS2; r += 1) {
4994
+ for (let c = 0; c < COLS2; c += 1) {
4995
+ const p = board[r][c];
4996
+ if (p && p.t === "g" && p.c === color) return { r, c };
4997
+ }
4998
+ }
4999
+ return null;
5000
+ }
5001
+ function pseudoMoves(board, r, c) {
5002
+ const p = board[r][c];
5003
+ if (!p) return [];
5004
+ const out = [];
5005
+ const push = (nr, nc) => {
5006
+ if (inBounds(nr, nc)) out.push([nr, nc]);
5007
+ };
5008
+ const clear = (nr, nc) => !board[nr][nc];
5009
+ const enemy = (nr, nc) => {
5010
+ const q = board[nr][nc];
5011
+ return q && q.c !== p.c;
5012
+ };
5013
+ if (p.t === "g") {
5014
+ [[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(([dr, dc]) => {
5015
+ const nr = r + dr, nc = c + dc;
5016
+ if (inPalace(nr, nc, p.c) && (clear(nr, nc) || enemy(nr, nc))) push(nr, nc);
5017
+ });
5018
+ }
5019
+ if (p.t === "a") {
5020
+ [[-1, -1], [-1, 1], [1, -1], [1, 1]].forEach(([dr, dc]) => {
5021
+ const nr = r + dr, nc = c + dc;
5022
+ if (inPalace(nr, nc, p.c) && (clear(nr, nc) || enemy(nr, nc))) push(nr, nc);
5023
+ });
5024
+ }
5025
+ if (p.t === "e") {
5026
+ [[-2, -2], [-2, 2], [2, -2], [2, 2]].forEach(([dr, dc]) => {
5027
+ const nr = r + dr, nc = c + dc, er = r + dr / 2, ec = c + dc / 2;
5028
+ if (inBounds(nr, nc) && ownSide(nr, p.c) && !board[er][ec] && (clear(nr, nc) || enemy(nr, nc))) push(nr, nc);
5029
+ });
5030
+ }
5031
+ if (p.t === "h") {
5032
+ [[-2, -1], [-2, 1], [2, -1], [2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2]].forEach(([dr, dc]) => {
5033
+ const nr = r + dr, nc = c + dc;
5034
+ const legR = Math.abs(dr) === 2 ? r + dr / 2 : r;
5035
+ const legC = Math.abs(dc) === 2 ? c + dc / 2 : c;
5036
+ if (inBounds(nr, nc) && !board[legR][legC] && (clear(nr, nc) || enemy(nr, nc))) push(nr, nc);
5037
+ });
5038
+ }
5039
+ if (p.t === "k" || p.t === "c") {
5040
+ [[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(([dr, dc]) => {
5041
+ let rr = r + dr, cc = c + dc, jumped = false;
5042
+ while (inBounds(rr, cc)) {
5043
+ const q = board[rr][cc];
5044
+ if (p.t === "k") {
5045
+ if (!q) push(rr, cc);
5046
+ else {
5047
+ if (q.c !== p.c) push(rr, cc);
5048
+ break;
5049
+ }
5050
+ } else {
5051
+ if (!q && !jumped) push(rr, cc);
5052
+ else if (q && !jumped) jumped = true;
5053
+ else if (q && jumped) {
5054
+ if (q.c !== p.c) push(rr, cc);
5055
+ break;
5056
+ }
5057
+ }
5058
+ rr += dr;
5059
+ cc += dc;
5060
+ }
5061
+ });
5062
+ }
5063
+ if (p.t === "p") {
5064
+ const forward = p.c === "r" ? -1 : 1;
5065
+ [[forward, 0], [0, -1], [0, 1]].forEach(([dr, dc]) => {
5066
+ const nr = r + dr, nc = c + dc;
5067
+ if (!inBounds(nr, nc) || dc !== 0 && !crossedRiver(r, p.c)) return;
5068
+ if (clear(nr, nc) || enemy(nr, nc)) push(nr, nc);
5069
+ });
5070
+ }
5071
+ return out;
5072
+ }
5073
+ function applyMove(board, fr, fc, tr, tc) {
5074
+ const nb = cloneBoard(board);
5075
+ nb[tr][tc] = nb[fr][fc];
5076
+ nb[fr][fc] = null;
5077
+ return nb;
5078
+ }
5079
+ function inCheck(board, color) {
5080
+ const g = findGeneral(board, color);
5081
+ if (!g) return true;
5082
+ const enemy = color === "r" ? "b" : "r";
5083
+ for (let r = 0; r < ROWS2; r += 1) {
5084
+ const p = board[r][g.c];
5085
+ if (p && p.t === "g" && p.c === enemy) {
5086
+ let clear = true;
5087
+ const lo = Math.min(r, g.r), hi = Math.max(r, g.r);
5088
+ for (let i = lo + 1; i < hi; i += 1) if (board[i][g.c]) {
5089
+ clear = false;
5090
+ break;
5091
+ }
5092
+ if (clear) return true;
5093
+ }
5094
+ }
5095
+ for (let r = 0; r < ROWS2; r += 1) {
5096
+ for (let c = 0; c < COLS2; c += 1) {
5097
+ const p = board[r][c];
5098
+ if (p && p.c === enemy && attacks(board, r, c, g.r, g.c)) return true;
5099
+ }
5100
+ }
5101
+ return false;
5102
+ }
5103
+ function legalMoves(board, color) {
5104
+ const out = [];
5105
+ for (let r = 0; r < ROWS2; r += 1) {
5106
+ for (let c = 0; c < COLS2; c += 1) {
5107
+ const p = board[r][c];
5108
+ if (!p || p.c !== color) continue;
5109
+ for (const [nr, nc] of pseudoMoves(board, r, c)) {
5110
+ const nb = applyMove(board, r, c, nr, nc);
5111
+ if (!inCheck(nb, color)) out.push([r, c, nr, nc]);
5112
+ }
5113
+ }
5114
+ }
5115
+ return out;
5116
+ }
5117
+ function hasLegalMoves(board, color) {
5118
+ return legalMoves(board, color).length > 0;
5119
+ }
5120
+ function evaluatePosition(board) {
5121
+ let score = 0;
5122
+ for (let r = 0; r < ROWS2; r += 1) {
5123
+ for (let c = 0; c < COLS2; c += 1) {
5124
+ const p = board[r][c];
5125
+ if (!p) continue;
5126
+ let v = VALUE[p.t];
5127
+ if (p.t === "p") v += crossedRiver(r, p.c) ? 50 : 0;
5128
+ score += p.c === "b" ? v : -v;
5129
+ }
5130
+ }
5131
+ return score;
5132
+ }
5133
+ function evalFrom(board, color) {
5134
+ const e = evaluatePosition(board);
5135
+ return color === "b" ? e : -e;
5136
+ }
5137
+ function negamax(board, color, depth, alpha, beta) {
5138
+ const moves = legalMoves(board, color);
5139
+ if (moves.length === 0) {
5140
+ return color === "b" ? -1e6 + (10 - depth) : 1e6 - (10 - depth);
5141
+ }
5142
+ if (depth === 0) return evalFrom(board, color);
5143
+ const enemy = color === "r" ? "b" : "r";
5144
+ const ordered = orderMoves(board, moves);
5145
+ let best = -Infinity;
5146
+ for (const m of ordered) {
5147
+ const nb = applyMove(board, m[0], m[1], m[2], m[3]);
5148
+ const score = -negamax(nb, enemy, depth - 1, -beta, -alpha);
5149
+ if (score > best) best = score;
5150
+ if (best > alpha) alpha = best;
5151
+ if (alpha >= beta) break;
5152
+ }
5153
+ return best;
5154
+ }
5155
+ function orderMoves(board, moves) {
5156
+ const scored = moves.map((m) => {
5157
+ let s = 0;
5158
+ const target = board[m[2]][m[3]];
5159
+ if (target) s += VALUE[target.t] * 10;
5160
+ return { m, s };
5161
+ });
5162
+ scored.sort((a, b) => b.s - a.s);
5163
+ return scored.map((o) => o.m);
5164
+ }
5165
+ function bestMove(board, color, depth, avoid) {
5166
+ const moves = legalMoves(board, color);
5167
+ if (!moves.length) return null;
5168
+ const enemy = color === "r" ? "b" : "r";
5169
+ const ordered = orderMoves(board, moves);
5170
+ let pool;
5171
+ if (avoid && avoid.size) {
5172
+ const seenCount = (m) => avoid.get(posKey(applyMove(board, m[0], m[1], m[2], m[3]))) || 0;
5173
+ const minSeen = Math.min(...ordered.map(seenCount));
5174
+ pool = minSeen === 0 ? ordered.filter((m) => seenCount(m) === 0).map((m) => ({ m, pen: 0 })) : ordered.map((m) => ({ m, pen: seenCount(m) * 60 })).sort((a, b) => a.pen - b.pen);
5175
+ } else {
5176
+ pool = ordered.map((m) => ({ m, pen: 0 }));
5177
+ }
5178
+ if (depth <= 1) {
5179
+ return pickAmongBest(pool.map(({ m, pen }) => ({ m, s: evalFrom(applyMove(board, m[0], m[1], m[2], m[3]), color) - pen })));
5180
+ }
5181
+ let bestScore = -Infinity;
5182
+ let alpha = -Infinity;
5183
+ const scored = [];
5184
+ for (const { m, pen } of pool) {
5185
+ const nb = applyMove(board, m[0], m[1], m[2], m[3]);
5186
+ const s = -negamax(nb, enemy, depth - 1, -Infinity, -alpha) - pen;
5187
+ scored.push({ m, s });
5188
+ if (s > bestScore) {
5189
+ bestScore = s;
5190
+ alpha = Math.max(alpha, bestScore - TIE_EPSILON);
5191
+ }
5192
+ }
5193
+ return pickAmongBest(scored);
5194
+ }
5195
+ var TIE_EPSILON = 12;
5196
+ function pickAmongBest(scored) {
5197
+ let bestScore = -Infinity;
5198
+ for (const o of scored) if (o.s > bestScore) bestScore = o.s;
5199
+ const ties = scored.filter((o) => o.s >= bestScore - TIE_EPSILON);
5200
+ return ties[Math.floor(Math.random() * ties.length)].m;
5201
+ }
5202
+
5203
+ // features/games/games/xiangqi.jsx
5204
+ var import_jsx_runtime6 = require("react/jsx-runtime");
5205
+ var XIANGQI_LINES = (() => {
5206
+ const P = (n) => n * 100 + 50;
5207
+ const parts = [];
5208
+ let k = 0;
5209
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: P(0), y: P(0), width: 800, height: 900, strokeWidth: 7, fill: "none" }, "frame"));
5210
+ for (let r = 1; r <= 8; r += 1) parts.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(0), y1: P(r), x2: P(8), y2: P(r) }, k++));
5211
+ for (let c = 1; c <= 7; c += 1) {
5212
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(c), y1: P(0), x2: P(c), y2: P(4) }, k++));
5213
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(c), y1: P(5), x2: P(c), y2: P(9) }, k++));
5214
+ }
5215
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: `M${P(3)} ${P(0)}L${P(5)} ${P(2)}M${P(5)} ${P(0)}L${P(3)} ${P(2)}M${P(3)} ${P(7)}L${P(5)} ${P(9)}M${P(5)} ${P(7)}L${P(3)} ${P(9)}` }, k++));
5216
+ const t = 14, o = 16;
5217
+ const marks = [[1, 2], [7, 2], [1, 7], [7, 7], [0, 3], [2, 3], [4, 3], [6, 3], [8, 3], [0, 6], [2, 6], [4, 6], [6, 6], [8, 6]];
5218
+ for (const [c, r] of marks) {
5219
+ const seg = [];
5220
+ if (c > 0) seg.push(
5221
+ `M${P(c) - o - t} ${P(r) - o}H${P(c) - o}V${P(r) - o - t}`,
5222
+ `M${P(c) - o - t} ${P(r) + o}H${P(c) - o}V${P(r) + o + t}`
5223
+ );
5224
+ if (c < 8) seg.push(
5225
+ `M${P(c) + o} ${P(r) - o}H${P(c) + o + t}V${P(r) - o - t}`,
5226
+ `M${P(c) + o} ${P(r) + o}H${P(c) + o + t}V${P(r) + o + t}`
5227
+ );
5228
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { className: "dgame-xiangqi-mark", strokeWidth: 2.6, d: seg.join("") }, k++));
5229
+ }
5230
+ return parts;
5231
+ })();
5232
+ function XiangqiGame(props) {
5233
+ const [board, setBoard] = (0, import_react13.useState)(initialBoard);
5234
+ const [turn, setTurn] = (0, import_react13.useState)("r");
5235
+ const [sel, setSel] = (0, import_react13.useState)(null);
5236
+ const [targets, setTargets] = (0, import_react13.useState)([]);
5237
+ const [over, setOver] = (0, import_react13.useState)(null);
5238
+ const [check, setCheck] = (0, import_react13.useState)(false);
5239
+ const [last, setLast] = (0, import_react13.useState)(null);
5240
+ const [thinking, setThinking] = (0, import_react13.useState)(false);
5241
+ const [history, setHistory] = (0, import_react13.useState)([]);
5242
+ const [repKeys, setRepKeys] = (0, import_react13.useState)(() => [posKey(initialBoard())]);
5243
+ const pushKey = (0, import_react13.useCallback)((b) => setRepKeys((ks) => ks[ks.length - 1] === posKey(b) ? ks : ks.concat(posKey(b))), []);
5244
+ const stageRef = (0, import_react13.useRef)(null);
5245
+ const boardRef = (0, import_react13.useRef)(board);
5246
+ boardRef.current = board;
5247
+ const turnRef = (0, import_react13.useRef)(turn);
5248
+ turnRef.current = turn;
5249
+ const overRef = (0, import_react13.useRef)(over);
5250
+ overRef.current = over;
5251
+ const thinkingRef = (0, import_react13.useRef)(thinking);
5252
+ thinkingRef.current = thinking;
5253
+ const historyRef = (0, import_react13.useRef)(history);
5254
+ historyRef.current = history;
5255
+ const restart = (0, import_react13.useCallback)(() => {
5256
+ const b = initialBoard();
5257
+ boardRef.current = b;
5258
+ setBoard(b);
5259
+ setTurn("r");
5260
+ setSel(null);
5261
+ setTargets([]);
5262
+ setOver(null);
5263
+ setCheck(false);
5264
+ setLast(null);
5265
+ setThinking(false);
5266
+ setHistory([]);
5267
+ setRepKeys([posKey(b)]);
5268
+ }, []);
5269
+ const undo2 = (0, import_react13.useCallback)(() => {
5270
+ if (thinkingRef.current || overRef.current || turnRef.current !== "r") return;
5271
+ const h = historyRef.current;
5272
+ if (h.length < 2) return;
5273
+ const hist = h.slice(0, -2);
5274
+ const b = hist.length ? cloneBoard(hist[hist.length - 1]) : initialBoard();
5275
+ boardRef.current = b;
5276
+ setBoard(b);
5277
+ setHistory(hist);
5278
+ setSel(null);
5279
+ setTargets([]);
5280
+ setLast(null);
5281
+ setOver(null);
5282
+ setCheck(inCheck(b, "r"));
5283
+ setRepKeys(hist.map(posKey).concat(posKey(b)));
5284
+ }, []);
5285
+ const moveRed = (0, import_react13.useCallback)((tr, tc) => {
5286
+ if (turnRef.current !== "r" || overRef.current || !sel) return;
5287
+ const fr = sel[0], fc = sel[1];
5288
+ if (boardRef.current[tr][tc]) playSfx("capture");
5289
+ else playSfx("step");
5290
+ const nb = applyMove(boardRef.current, fr, fc, tr, tc);
5291
+ setHistory((h) => [...h, boardRef.current]);
5292
+ boardRef.current = nb;
5293
+ pushKey(nb);
5294
+ setBoard(nb);
5295
+ setSel(null);
5296
+ setTargets([]);
5297
+ setLast([tr, tc]);
5298
+ setTurn("b");
5299
+ if (!hasLegalMoves(nb, "b")) {
5300
+ setOver("r");
5301
+ setCheck(false);
5302
+ } else setCheck(inCheck(nb, "b"));
5303
+ }, [sel, pushKey]);
5304
+ const select = (0, import_react13.useCallback)((r, c) => {
5305
+ if (overRef.current || turnRef.current !== "r" || thinkingRef.current) return;
5306
+ const p = boardRef.current[r][c];
5307
+ if (p && p.c === "r") {
5308
+ setSel([r, c]);
5309
+ const list = legalMoves(boardRef.current, "r").filter((m) => m[0] === r && m[1] === c).map((m) => [m[2], m[3]]);
5310
+ setTargets(list);
5311
+ return;
5312
+ }
5313
+ if (sel && targets.some(([tr, tc]) => tr === r && tc === c)) {
5314
+ moveRed(r, c);
5315
+ return;
5316
+ }
5317
+ setSel(null);
5318
+ setTargets([]);
5319
+ }, [sel, targets, moveRed]);
5320
+ (0, import_react13.useEffect)(() => {
5321
+ if (turn !== "b" || over || props.paused) return;
5322
+ const t = setTimeout(() => {
5323
+ const seenCounts = /* @__PURE__ */ new Map();
5324
+ for (const k of repKeys) seenCounts.set(k, (seenCounts.get(k) || 0) + 1);
5325
+ const mv = bestMove(boardRef.current, "b", 3, seenCounts);
5326
+ if (mv) {
5327
+ const [fr, fc, tr, tc] = mv;
5328
+ if (boardRef.current[tr][tc]) playSfx("capture");
5329
+ else playSfx("step");
5330
+ const nb = applyMove(boardRef.current, fr, fc, tr, tc);
5331
+ setHistory((h) => [...h, boardRef.current]);
5332
+ boardRef.current = nb;
5333
+ pushKey(nb);
5334
+ setBoard(nb);
5335
+ setLast([tr, tc]);
5336
+ setTurn("r");
5337
+ if (!hasLegalMoves(nb, "r")) {
5338
+ setOver("b");
5339
+ setCheck(false);
5340
+ } else setCheck(inCheck(nb, "r"));
5341
+ } else setOver("r");
5342
+ setThinking(false);
5343
+ }, 90);
5344
+ setThinking(true);
5345
+ return () => clearTimeout(t);
5346
+ }, [turn, over, props.paused, repKeys, pushKey]);
5347
+ const onKeyDown = (0, import_react13.useCallback)((event) => {
5348
+ if (event.key.toLowerCase() === "r") {
5349
+ event.preventDefault();
5350
+ restart();
5351
+ } else if (event.key.toLowerCase() === "u") {
5352
+ event.preventDefault();
5353
+ undo2();
5354
+ }
5355
+ }, [restart, undo2]);
5356
+ useGameControls(stageRef, props.paused, onKeyDown);
5357
+ const status = over === "r" ? "\u7EA2\u65B9\u80DC \xB7 \u606D\u559C" : over === "b" ? "\u9ED1\u65B9\u80DC \xB7 AI \u8D62" : thinking ? "AI \u601D\u8003\u4E2D\u2026" : check ? "\u5C06\u519B\uFF01" : turn === "r" ? "\u8F6E\u5230\u4F60\uFF08\u7EA2\u65B9\uFF09" : "\u8F6E\u5230 AI\uFF08\u9ED1\u65B9\uFF09";
5358
+ const prevCheck = (0, import_react13.useRef)(false);
5359
+ (0, import_react13.useEffect)(() => {
5360
+ if (over === "r") playSfx("win");
5361
+ else if (over === "b") playSfx("over");
5362
+ else if (!thinking && check && !prevCheck.current) playSfx("check");
5363
+ prevCheck.current = !thinking && check;
5364
+ }, [check, thinking, over]);
5365
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "dgame-game", "aria-label": "\u8C61\u68CB", children: [
5366
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dgame-game-head", children: [
5367
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
5368
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: "\u4E2D\u56FD\u8C61\u68CB" }),
5369
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "\u4F60\u6267\u7EA2\u5148\u884C\uFF0CAI \u6267\u9ED1\uFF1B\u5403\u6389\u5BF9\u65B9\u5C06/\u5E05\u5373\u80DC\u3002\u70B9\u51FB\u9009\u4E2D\u7EA2\u5B50\uFF0C\u518D\u70B9\u9AD8\u4EAE\u843D\u70B9\u8D70\u68CB\u3002" })
5370
+ ] }),
5371
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dgame-score", children: [
5372
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u5F53\u524D" }),
5373
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { className: "dg-gomoku-status", children: status }),
5374
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: undo2, children: "\u64A4\u9500" }),
5375
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: restart, children: "\u91CD\u5F00" })
5376
+ ] })
5377
+ ] }),
5378
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dgame-xiangqi-board", ref: stageRef, tabIndex: 0, onKeyDown, onClick: () => focusStage(stageRef), role: "grid", "aria-label": "\u4E2D\u56FD\u8C61\u68CB\u68CB\u76D8", children: [
5379
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { className: "dgame-xiangqi-lines", viewBox: "0 0 900 1000", preserveAspectRatio: "none", "aria-hidden": "true", children: [
5380
+ XIANGQI_LINES,
5381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { className: "dgame-xiangqi-rivertext", x: 230, y: 505, children: "\u695A \u6CB3" }),
5382
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { className: "dgame-xiangqi-rivertext", x: 670, y: 505, children: "\u6F22 \u754C" })
5383
+ ] }),
5384
+ board.map((row, r) => row.map((p, c) => {
5385
+ const isSel = sel && sel[0] === r && sel[1] === c;
5386
+ const isTargetMark = targets.some(([tr, tc]) => tr === r && tc === c);
5387
+ const isLast = last && last[0] === r && last[1] === c;
5388
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5389
+ "button",
5390
+ {
5391
+ type: "button",
5392
+ role: "gridcell",
5393
+ className: "dgame-xiangqi-cell" + (isSel ? " sel" : "") + (isTargetMark ? " target" : "") + (isLast ? " last" : ""),
5394
+ "aria-label": "\u7B2C" + (r + 1) + "\u884C\u7B2C" + (c + 1) + "\u5217" + (p ? pieceChar(p) : " \u7A7A"),
5395
+ onClick: () => {
5396
+ focusStage(stageRef);
5397
+ select(r, c);
5398
+ },
5399
+ children: p ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dgame-xiangqi-pc " + p.c, children: pieceChar(p) }) : isTargetMark ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dgame-xiangqi-dot", "aria-hidden": "true" }) : null
5400
+ },
5401
+ r + "-" + c
5402
+ );
5403
+ }))
5404
+ ] }),
5405
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dgame-controls", children: [
5406
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: undo2, children: "\u64A4\u9500" }),
5407
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5408
+ status,
5409
+ " \xB7 R \u91CD\u5F00 \xB7 U \u64A4\u9500"
5410
+ ] }),
5411
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: restart, children: "\u91CD\u5F00" })
5412
+ ] })
5413
+ ] });
5414
+ }
5415
+ function XiangqiPreview() {
5416
+ const P = (n) => n * 100 + 50;
5417
+ const pc = (c, r, ch, col) => [
5418
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: P(c), cy: P(r), r: 44, className: "dgp-xq-pc-" + col }, "pc" + c + r),
5419
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: P(c), y: P(r) + 2, className: "dgp-xq-tx-" + col, children: ch }, "tx" + c + r)
5420
+ ];
5421
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dgcov-prev dgcov-prev-xiangqi", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { viewBox: "0 0 900 1000", preserveAspectRatio: "xMidYMid slice", children: [
5422
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: P(0), y: P(0), width: 800, height: 900, fill: "none", strokeWidth: 11 }),
5423
+ [1, 2, 3, 4, 5, 6, 7, 8].map((r) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(0), y1: P(r), x2: P(8), y2: P(r) }, "h" + r)),
5424
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(0), y1: P(0), x2: P(0), y2: P(9) }),
5425
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(8), y1: P(0), x2: P(8), y2: P(9) }),
5426
+ [1, 2, 3, 4, 5, 6, 7].map((c) => [
5427
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(c), y1: P(0), x2: P(c), y2: P(4) }, "vt" + c),
5428
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: P(c), y1: P(5), x2: P(c), y2: P(9) }, "vb" + c)
5429
+ ]),
5430
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: `M${P(3)} ${P(0)}L${P(5)} ${P(2)}M${P(5)} ${P(0)}L${P(3)} ${P(2)}M${P(3)} ${P(7)}L${P(5)} ${P(9)}M${P(5)} ${P(7)}L${P(3)} ${P(9)}` }),
5431
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { className: "dgp-xq-river", x: 235, y: 500, children: "\u695A\u6CB3" }),
5432
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { className: "dgp-xq-river", x: 665, y: 500, children: "\u6F22\u754C" }),
5433
+ pc(4, 0, "\u5C07", "b"),
5434
+ pc(1, 2, "\u70AE", "b"),
5435
+ pc(5, 3, "\u5352", "b"),
5436
+ pc(3, 6, "\u5175", "r"),
5437
+ pc(7, 7, "\u70AE", "r"),
5438
+ pc(4, 9, "\u5E25", "r")
5439
+ ] }) });
5440
+ }
5441
+ var xiangqiGame = { Game: XiangqiGame, Preview: XiangqiPreview, css: `
5442
+ .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-pc.b{color:#1f2937;text-shadow:0 1px 0 rgba(255,255,255,.35)}
5443
+ ` };
5444
+
5445
+ // features/games/games/snake.jsx
5446
+ var import_react14 = require("react");
5447
+ var import_jsx_runtime7 = require("react/jsx-runtime");
5448
+ var SIZE2 = 16;
5449
+ var DIRS3 = { ArrowUp: [-1, 0], ArrowDown: [1, 0], ArrowLeft: [0, -1], ArrowRight: [0, 1], w: [-1, 0], s: [1, 0], a: [0, -1], d: [0, 1], W: [-1, 0], S: [1, 0], A: [0, -1], D: [0, 1] };
5450
+ var OPPOSITE = { "-1,0": [1, 0], "1,0": [-1, 0], "0,1": [0, -1], "0,-1": [0, 1] };
5451
+ var SPEED_MODES = {
5452
+ slow: { label: "\u6162\u901F", stepMs: 4, base: 320, min: 180 },
5453
+ normal: { label: "\u4E2D\u901F", stepMs: 3, base: 220, min: 120 },
5454
+ fast: { label: "\u5FEB\u901F", stepMs: 2, base: 140, min: 80 }
5455
+ };
5456
+ function loadSpeedMode() {
5457
+ try {
5458
+ return SPEED_MODES[localStorage.getItem("dsh-dock.snake.speed")] ? localStorage.getItem("dsh-dock.snake.speed") : "slow";
5459
+ } catch {
5460
+ return "slow";
5461
+ }
5462
+ }
5463
+ function randFood(snake) {
5464
+ while (true) {
5465
+ const r = Math.floor(Math.random() * SIZE2), c = Math.floor(Math.random() * SIZE2);
5466
+ if (!snake.some((s) => s.r === r && s.c === c)) return { r, c };
5467
+ }
5468
+ }
5469
+ function initial2() {
5470
+ const snake = [{ r: 7, c: 5 }, { r: 7, c: 4 }, { r: 7, c: 3 }];
5471
+ return { snake, dir: [0, 1], food: randFood(snake), score: 0, over: false, started: false };
5472
+ }
5473
+ function step2(state, dir) {
5474
+ const head = state.snake[0];
5475
+ const nr = head.r + dir[0], nc = head.c + dir[1];
5476
+ const body = state.snake.slice(0, -1);
5477
+ if (nr < 0 || nr >= SIZE2 || nc < 0 || nc >= SIZE2 || body.some((s) => s.r === nr && s.c === nc)) {
5478
+ return Object.assign({}, state, { over: true });
5479
+ }
5480
+ const ate = state.food.r === nr && state.food.c === nc;
5481
+ let snake = [{ r: nr, c: nc }, ...state.snake];
5482
+ if (ate) {
5483
+ playSfx("eat");
5484
+ const food = randFood(snake);
5485
+ return Object.assign({}, state, { snake, food, score: state.score + 1 });
5486
+ }
5487
+ snake.pop();
5488
+ return Object.assign({}, state, { snake });
5489
+ }
5490
+ function SnakeGame(props) {
5491
+ const [state, setState] = (0, import_react14.useState)(initial2);
5492
+ const [paused, setPaused] = (0, import_react14.useState)(false);
5493
+ const [speedMode, setSpeedMode] = (0, import_react14.useState)(loadSpeedMode);
5494
+ const stageRef = (0, import_react14.useRef)(null);
5495
+ const dirRef = (0, import_react14.useRef)(state.dir);
5496
+ const overRef = (0, import_react14.useRef)(state.over);
5497
+ overRef.current = state.over;
5498
+ const pausedRef = (0, import_react14.useRef)(!!(props && props.paused) || paused);
5499
+ pausedRef.current = !!(props && props.paused) || paused;
5500
+ const start = (0, import_react14.useCallback)(() => {
5501
+ const init = initial2();
5502
+ init.started = true;
5503
+ dirRef.current = init.dir;
5504
+ setState(init);
5505
+ setPaused(false);
5506
+ }, []);
5507
+ const begin = (0, import_react14.useCallback)(() => {
5508
+ setState((s) => s.started || s.over ? s : Object.assign({}, s, { started: true }));
5509
+ }, []);
5510
+ const changeDir = (0, import_react14.useCallback)((nv) => {
5511
+ if (overRef.current || pausedRef.current) return;
5512
+ const cur = dirRef.current;
5513
+ const opp = OPPOSITE[cur[0] + "," + cur[1]];
5514
+ if (opp && opp[0] === nv[0] && opp[1] === nv[1]) return;
5515
+ dirRef.current = nv;
5516
+ begin();
5517
+ }, [begin]);
5518
+ (0, import_react14.useEffect)(() => {
5519
+ if (state.over) playSfx("over");
5520
+ }, [state.over]);
5521
+ (0, import_react14.useEffect)(() => {
5522
+ if (!state.started || state.over || pausedRef.current) return;
5523
+ const mode = SPEED_MODES[speedMode] || SPEED_MODES.slow;
5524
+ const speed = Math.max(mode.min, mode.base - state.score * mode.stepMs);
5525
+ const timer = setInterval(() => setState((s) => s.over || !s.started || pausedRef.current ? s : step2(s, dirRef.current)), speed);
5526
+ return () => clearInterval(timer);
5527
+ }, [state.started, state.over, state.score, props.paused, paused, speedMode]);
5528
+ const pickSpeed = (0, import_react14.useCallback)((m) => {
5529
+ setSpeedMode(m);
5530
+ try {
5531
+ localStorage.setItem("dsh-dock.snake.speed", m);
5532
+ } catch {
5533
+ }
5534
+ }, []);
5535
+ const onKeyDown = (0, import_react14.useCallback)((event) => {
5536
+ const d = DIRS3[event.key];
5537
+ if (d) {
5538
+ event.preventDefault();
5539
+ changeDir(d);
5540
+ return;
5541
+ }
5542
+ if (event.key === "Enter" || event.key === " ") {
5543
+ event.preventDefault();
5544
+ if (overRef.current || !state.started || pausedRef.current) start();
5545
+ else setPaused((p) => !p);
5546
+ return;
5547
+ }
5548
+ if (event.key.toLowerCase() === "p") {
5549
+ event.preventDefault();
5550
+ setPaused((p) => !p);
5551
+ } else if (event.key.toLowerCase() === "r") {
5552
+ event.preventDefault();
5553
+ start();
5554
+ }
5555
+ }, [changeDir, start, state.started]);
5556
+ useGameControls(stageRef, pausedRef.current, onKeyDown);
5557
+ const cells = [];
5558
+ for (let r = 0; r < SIZE2; r += 1) {
5559
+ for (let c = 0; c < SIZE2; c += 1) {
5560
+ const idx = state.snake.findIndex((s) => s.r === r && s.c === c);
5561
+ const isFood = state.food.r === r && state.food.c === c;
5562
+ let cls = "dgame-snake-cell";
5563
+ if (idx >= 0) cls += " bob" + (idx === 0 ? " head" : idx < 4 ? " body2" : "");
5564
+ if (isFood) cls += " food";
5565
+ cells.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { className: cls, "aria-hidden": "true" }, r + "-" + c));
5566
+ }
5567
+ }
5568
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "dgame-game", "aria-label": "\u8D2A\u5403\u86C7", children: [
5569
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-game-head", children: [
5570
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
5571
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "\u8D2A\u5403\u86C7" }),
5572
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "\u56DE\u8F66\u5F00\u59CB/\u6682\u505C\uFF0C\u65B9\u5411\u952E\u63A7\u5236\u79FB\u52A8\uFF1B\u5403\u5230\u98DF\u7269\u53D8\u957F\u5E76\u7F13\u6162\u63D0\u901F\uFF0C\u649E\u5899\u6216\u54AC\u5230\u81EA\u5DF1\u5C31\u7ED3\u675F\u3002" })
5573
+ ] }),
5574
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-score", children: [
5575
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5576
+ "\u957F\u5EA6 ",
5577
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: state.score + 3 })
5578
+ ] }),
5579
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5580
+ "\u5F97\u5206 ",
5581
+ state.score
5582
+ ] })
5583
+ ] })
5584
+ ] }),
5585
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-snake-speeds", role: "radiogroup", "aria-label": "\u6E38\u620F\u901F\u5EA6", children: [
5586
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "\u901F\u5EA6" }),
5587
+ Object.entries(SPEED_MODES).map(([m, cfg]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", role: "radio", "aria-checked": speedMode === m, className: speedMode === m ? "on" : "", onClick: () => pickSpeed(m), children: cfg.label }, m))
5588
+ ] }),
5589
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-snake-stage", ref: stageRef, tabIndex: 0, onKeyDown, onClick: () => focusStage(stageRef), "aria-label": "\u8D2A\u5403\u86C7\u6E38\u620F\u533A\u57DF\uFF0C\u4F7F\u7528\u65B9\u5411\u952E\u63A7\u5236\u79FB\u52A8\uFF0C\u56DE\u8F66\u5F00\u59CB", children: [
5590
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dgame-snake-grid", children: cells }),
5591
+ state.over ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-over", children: [
5592
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("strong", { children: [
5593
+ "\u649E\u4E0A\u4E86 \xB7 \u5F97 ",
5594
+ state.score,
5595
+ " \u5206"
5596
+ ] }),
5597
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: start, children: "\u91CD\u65B0\u5F00\u59CB" })
5598
+ ] }) : null,
5599
+ !state.started && !state.over ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-over", children: [
5600
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "\u6309 Enter \u6216\u65B9\u5411\u952E\u5F00\u59CB" }),
5601
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: start, children: "\u5F00\u59CB\u6E38\u620F" })
5602
+ ] }) : null,
5603
+ state.started && !state.over && pausedRef.current ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-over", children: [
5604
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "\u5DF2\u6682\u505C" }),
5605
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: () => setPaused(false), children: "\u7EE7\u7EED\uFF08P\uFF09" })
5606
+ ] }) : null
5607
+ ] }),
5608
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dgame-controls", children: [
5609
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "aria-label": "\u5411\u4E0A", onClick: () => changeDir([-1, 0]), children: "\u2191" }),
5610
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "\u65B9\u5411\u952E / WASD \xB7 \u56DE\u8F66 \u5F00\u59CB/\u6682\u505C \xB7 R \u91CD\u5F00" }),
5611
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "aria-label": "\u5411\u4E0B", onClick: () => changeDir([1, 0]), children: "\u2193" })
5612
+ ] })
5613
+ ] });
5614
+ }
5615
+ function SnakePreview() {
5616
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dgcov-prev dgcov-prev-snake", "aria-hidden": "true", children: [
5617
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { style: { left: "12%", top: "62%" } }),
5618
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { style: { left: "24%", top: "62%" } }),
5619
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { style: { left: "36%", top: "62%" } }),
5620
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { style: { left: "48%", top: "62%" } }),
5621
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", { style: { left: "60%", top: "62%" } }),
5622
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", {})
5623
+ ] });
5624
+ }
5625
+ var snakeGame = { Game: SnakeGame, Preview: SnakePreview, css: `
5626
+ .dgame-snake-speeds{display:flex;gap:6px;align-items:center;align-self:center;justify-content:center}
5627
+ .dgame-snake-speeds span{font-size:11px;color:var(--dsw-alias-label-tertiary)}
5628
+ .dgame-snake-speeds button{min-width:44px;height:26px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:11px;cursor:pointer;transition:all .15s ease}
5629
+ .dgame-snake-speeds button.on{color:#bbf7d0;border-color:color-mix(in srgb,#4ade80 55%,transparent);background:color-mix(in srgb,#4ade80 16%,transparent)}
5630
+ .dgame-snake-speeds button:hover{border-color:#4ade80;color:var(--dsw-alias-label-primary)}
5631
+ .dgame-snake-stage{position:relative;align-self:center;width:min(100%,300px);aspect-ratio:1/1;border:1px solid color-mix(in srgb,rgb(74 222 128) 32%,var(--dsw-alias-border-l1));border-radius:10px;overflow:hidden;outline:none;background:radial-gradient(circle at 50% -10%,color-mix(in srgb,#166534 30%,transparent),transparent 55%),#0a1420}.dgame-snake-stage:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,rgb(74 222 128) 35%,transparent)}.dgame-snake-grid{display:grid;grid-template-columns:repeat(16,1fr);grid-template-rows:repeat(16,1fr);width:100%;height:100%}.dgame-snake-cell{position:relative}.dgame-snake-cell.food::after{content:'';position:absolute;inset:22%;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fecaca,#ef4444 70%);box-shadow:0 0 9px color-mix(in srgb,#ef4444 70%,transparent)}.dgame-snake-cell.bob::after{content:'';position:absolute;inset:20%;border-radius:26%;background:linear-gradient(135deg,#4ade80,#16a34a)}.dgame-snake-cell.head::after{background:linear-gradient(135deg,#bbf7d0,#22c55e);border-radius:30% 30% 28% 28%;box-shadow:0 1px 6px color-mix(in srgb,#22c55e 60%,transparent)}.dgame-snake-cell.head::before{content:'';position:absolute;left:30%;top:30%;width:9%;height:9%;border-radius:50%;background:#0b1b12}.dgame-snake-cell.body2::after{background:linear-gradient(135deg,#34d399,#0d9f6e)}
5632
+ ` };
5633
+
5634
+ // features/games/games/breakout.jsx
5635
+ var import_react15 = require("react");
5636
+ var import_jsx_runtime8 = require("react/jsx-runtime");
5637
+ var W = 320;
5638
+ var H = 360;
5639
+ var BRICK_COLS = 8;
5640
+ var BRICK_ROWS = 5;
5641
+ var BRICK_GAP = 4;
5642
+ var BRICK_TOP = 22;
5643
+ var BRICK_H = 18;
5644
+ var PADDLE_W = 60;
5645
+ var PADDLE_H = 11;
5646
+ var PADDLE_BOTTOM = 20;
5647
+ var BALL_R = 5.5;
5648
+ var SPEED = 3.4;
5649
+ var COLORS = ["#f87171", "#fb923c", "#facc15", "#4ade80", "#38bdf8"];
5650
+ function makeBricks(level) {
5651
+ const bricks = [];
5652
+ const bw = (W - BRICK_GAP * (BRICK_COLS + 1)) / BRICK_COLS;
5653
+ for (let r = 0; r < BRICK_ROWS; r += 1) {
5654
+ for (let c = 0; c < BRICK_COLS; c += 1) {
5655
+ bricks.push({
5656
+ x: BRICK_GAP + c * (bw + BRICK_GAP),
5657
+ y: BRICK_TOP + r * (BRICK_H + BRICK_GAP),
5658
+ w: bw,
5659
+ h: BRICK_H,
5660
+ color: COLORS[(r + level) % COLORS.length],
5661
+ alive: true
5662
+ });
5663
+ }
5664
+ }
5665
+ return bricks;
5666
+ }
5667
+ function draw(ctx, g) {
5668
+ ctx.clearRect(0, 0, W, H);
5669
+ ctx.fillStyle = "#0b1024";
5670
+ ctx.fillRect(0, 0, W, H);
5671
+ ctx.fillStyle = "#1b2bb0";
5672
+ for (const s of g.stars) {
5673
+ ctx.globalAlpha = s.a;
5674
+ ctx.fillRect(s.x, s.y, 1.5, 1.5);
5675
+ }
5676
+ ctx.globalAlpha = 1;
5677
+ for (const b of g.bricks) {
5678
+ if (!b.alive) continue;
5679
+ ctx.fillStyle = b.color;
5680
+ ctx.beginPath();
5681
+ roundRect(ctx, b.x, b.y, b.w, b.h, 4);
5682
+ ctx.fill();
5683
+ ctx.fillStyle = "rgba(255,255,255,.25)";
5684
+ roundRect(ctx, b.x, b.y, b.w, b.h / 2.4, 4);
5685
+ ctx.fill();
5686
+ }
5687
+ ctx.fillStyle = "#a5f3fc";
5688
+ roundRect(ctx, g.paddle.x, g.paddle.y, PADDLE_W, PADDLE_H, 5);
5689
+ ctx.fill();
5690
+ ctx.fillStyle = "#fef3c7";
5691
+ ctx.beginPath();
5692
+ ctx.arc(g.ball.x, g.ball.y, BALL_R, 0, Math.PI * 2);
5693
+ ctx.fill();
5694
+ ctx.fillStyle = "rgba(254,243,199,.5)";
5695
+ ctx.beginPath();
5696
+ ctx.arc(g.ball.x, g.ball.y, BALL_R + 2.5, 0, Math.PI * 2);
5697
+ ctx.fill();
5698
+ }
5699
+ function roundRect(ctx, x, y, w, h, r) {
5700
+ ctx.beginPath();
5701
+ ctx.moveTo(x + r, y);
5702
+ ctx.arcTo(x + w, y, x + w, y + h, r);
5703
+ ctx.arcTo(x + w, y + h, x, y + h, r);
5704
+ ctx.arcTo(x, y + h, x, y, r);
5705
+ ctx.arcTo(x, y, x + w, y, r);
5706
+ ctx.closePath();
5707
+ }
5708
+ function collideRect(x, y, r, bx, by, bw, bh) {
5709
+ const cx = Math.max(bx, Math.min(x, bx + bw));
5710
+ const cy = Math.max(by, Math.min(y, by + bh));
5711
+ const dx = x - cx, dy = y - cy;
5712
+ return dx * dx + dy * dy < r * r;
5713
+ }
5714
+ function BreakoutGame(props) {
5715
+ const canvasRef = (0, import_react15.useRef)(null);
5716
+ const [score, setScore] = (0, import_react15.useState)(0);
5717
+ const [lives, setLives] = (0, import_react15.useState)(3);
5718
+ const [level, setLevel] = (0, import_react15.useState)(1);
5719
+ const [status, setStatus] = (0, import_react15.useState)("play");
5720
+ const keys = (0, import_react15.useRef)({});
5721
+ const pausedRef = (0, import_react15.useRef)(!!(props && props.paused));
5722
+ pausedRef.current = !!(props && props.paused);
5723
+ const game = (0, import_react15.useRef)(null);
5724
+ const scoreRef = (0, import_react15.useRef)(0);
5725
+ scoreRef.current = score;
5726
+ const statusRef = (0, import_react15.useRef)("play");
5727
+ statusRef.current = status;
5728
+ const livesRef = (0, import_react15.useRef)(3);
5729
+ livesRef.current = lives;
5730
+ const start = (0, import_react15.useCallback)((lvl) => {
5731
+ const bricks = makeBricks(lvl);
5732
+ game.current = {
5733
+ stars: Array.from({ length: 46 }, () => ({ x: Math.random() * W, y: Math.random() * H, a: 0.25 + Math.random() * 0.5 })),
5734
+ bricks,
5735
+ paddle: { x: (W - PADDLE_W) / 2, y: H - PADDLE_BOTTOM - PADDLE_H },
5736
+ ball: { x: W / 2, y: H - PADDLE_BOTTOM - PADDLE_H - BALL_R - 1, vx: (Math.random() > 0.5 ? 1 : -1) * SPEED, vy: -SPEED },
5737
+ speed: SPEED * (1 + (lvl - 1) * 0.09)
5738
+ };
5739
+ }, []);
5740
+ (0, import_react15.useEffect)(() => {
5741
+ start(1);
5742
+ setScore(0);
5743
+ setLives(3);
5744
+ setStatus("play");
5745
+ }, [start]);
5746
+ const launchBall = (0, import_react15.useCallback)(() => {
5747
+ const g = game.current;
5748
+ if (!g) return;
5749
+ g.ball.vx = (Math.random() > 0.5 ? 1 : -1) * SPEED;
5750
+ g.ball.vy = -SPEED;
5751
+ g.launched = true;
5752
+ }, []);
5753
+ const resetBall = (0, import_react15.useCallback)(() => {
5754
+ const g = game.current;
5755
+ if (g) {
5756
+ g.ball.x = W / 2;
5757
+ g.ball.y = H - PADDLE_BOTTOM - PADDLE_H - BALL_R - 1;
5758
+ g.ball.vx = 0;
5759
+ g.ball.vy = 0;
5760
+ g.launched = false;
5761
+ }
5762
+ }, []);
5763
+ (0, import_react15.useEffect)(() => {
5764
+ const frame = () => {
5765
+ const g = game.current;
5766
+ const ctx = canvasRef.current && canvasRef.current.getContext("2d");
5767
+ if (!g || !ctx) return;
5768
+ if (pausedRef.current || statusRef.current !== "play") {
5769
+ draw(ctx, g);
5770
+ return;
5771
+ }
5772
+ if (keys.current.left) g.paddle.x -= 5.5;
5773
+ if (keys.current.right) g.paddle.x += 5.5;
5774
+ g.paddle.x = Math.max(0, Math.min(W - PADDLE_W, g.paddle.x));
5775
+ const b = g.ball;
5776
+ if (!b.launched) {
5777
+ b.x = g.paddle.x + PADDLE_W / 2;
5778
+ b.y = g.paddle.y - BALL_R - 1;
5779
+ } else {
5780
+ b.x += b.vx;
5781
+ b.y += b.vy;
5782
+ if (b.x - BALL_R < 0) {
5783
+ b.x = BALL_R;
5784
+ b.vx = Math.abs(b.vx);
5785
+ }
5786
+ if (b.x + BALL_R > W) {
5787
+ b.x = W - BALL_R;
5788
+ b.vx = -Math.abs(b.vx);
5789
+ }
5790
+ if (b.y - BALL_R < 0) {
5791
+ b.y = BALL_R;
5792
+ b.vy = Math.abs(b.vy);
5793
+ }
5794
+ if (b.vy > 0 && b.y + BALL_R >= g.paddle.y && b.y - BALL_R <= g.paddle.y + PADDLE_H && b.x >= g.paddle.x - BALL_R && b.x <= g.paddle.x + PADDLE_W + BALL_R) {
5795
+ playSfx("bounce");
5796
+ const rel = (b.x - (g.paddle.x + PADDLE_W / 2)) / (PADDLE_W / 2);
5797
+ const ang = rel * 0.6;
5798
+ const sp = Math.hypot(b.vx, b.vy);
5799
+ b.vx = sp * Math.sin(ang);
5800
+ b.vy = -Math.abs(sp * Math.cos(ang));
5801
+ b.y = g.paddle.y - BALL_R - 1;
5802
+ }
5803
+ const maxSpeed = g.speed * 1.6;
5804
+ for (const br of g.bricks) {
5805
+ if (!br.alive) continue;
5806
+ if (collideRect(b.x, b.y, BALL_R, br.x, br.y, br.w, br.h)) {
5807
+ br.alive = false;
5808
+ playSfx("brick");
5809
+ const cx = br.x + br.w / 2, cy = br.y + br.h / 2;
5810
+ const dx = b.x - cx, dy = b.y - cy;
5811
+ if (Math.abs(dx / br.w) > Math.abs(dy / br.h)) {
5812
+ b.vx = -b.vx;
5813
+ b.x += b.vx * 2;
5814
+ } else {
5815
+ b.vy = -b.vy;
5816
+ b.y += b.vy * 2;
5817
+ }
5818
+ setScore((s) => s + 10);
5819
+ scoreRef.current += 10;
5820
+ const remain = g.bricks.some((x) => x.alive);
5821
+ if (!remain) {
5822
+ playSfx("win");
5823
+ setStatus("win");
5824
+ setLevel((l) => l + 1);
5825
+ if (statusRef.current !== "over") statusRef.current = "win";
5826
+ }
5827
+ break;
5828
+ }
5829
+ }
5830
+ if (b.y - BALL_R > H) {
5831
+ const nl = livesRef.current - 1;
5832
+ livesRef.current = nl;
5833
+ setLives(nl);
5834
+ playSfx(nl <= 0 ? "over" : "step");
5835
+ if (nl <= 0) {
5836
+ setStatus("over");
5837
+ statusRef.current = "over";
5838
+ } else resetBall();
5839
+ }
5840
+ }
5841
+ draw(ctx, g);
5842
+ };
5843
+ let raf = requestAnimationFrame(function loop() {
5844
+ frame();
5845
+ raf = requestAnimationFrame(loop);
5846
+ });
5847
+ return () => cancelAnimationFrame(raf);
5848
+ }, [resetBall]);
5849
+ const onKeyDown = (0, import_react15.useCallback)((event) => {
5850
+ if (pausedRef.current) return;
5851
+ if (event.key === "ArrowLeft") {
5852
+ event.preventDefault();
5853
+ keys.current.left = true;
5854
+ } else if (event.key === "ArrowRight") {
5855
+ event.preventDefault();
5856
+ keys.current.right = true;
5857
+ } else if (event.key === " ") {
5858
+ event.preventDefault();
5859
+ launchBall();
5860
+ } else if (event.key.toLowerCase() === "r") {
5861
+ event.preventDefault();
5862
+ start(1);
5863
+ setScore(0);
5864
+ setLives(3);
5865
+ setStatus("play");
5866
+ statusRef.current = "play";
5867
+ livesRef.current = 3;
5868
+ }
5869
+ }, [launchBall, start]);
5870
+ const onKeyUp = (0, import_react15.useCallback)((event) => {
5871
+ if (event.key === "ArrowLeft") keys.current.left = false;
5872
+ else if (event.key === "ArrowRight") keys.current.right = false;
5873
+ }, []);
5874
+ const onPointerMove = (0, import_react15.useCallback)((e) => {
5875
+ const g = game.current, cv = canvasRef.current;
5876
+ if (!g || !cv) return;
5877
+ const rect2 = cv.getBoundingClientRect();
5878
+ const scale = rect2.width / W;
5879
+ g.paddle.x = Math.max(0, Math.min(W - PADDLE_W, (e.clientX - rect2.left) / scale - PADDLE_W / 2));
5880
+ }, []);
5881
+ useGameControls(canvasRef, pausedRef.current, onKeyDown, onKeyUp);
5882
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "dgame-game", "aria-label": "\u6253\u7816\u5757", children: [
5883
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-game-head", children: [
5884
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
5885
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "\u6253\u7816\u5757" }),
5886
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "\u79FB\u52A8\u6321\u677F\u53CD\u5F39\u5C0F\u7403\uFF0C\u51FB\u788E\u6240\u6709\u7816\u5757\uFF1B\u7403\u843D\u5230\u5E95\u90E8\u6263\u4E00\u6761\u547D\u3002" })
5887
+ ] }),
5888
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-score", children: [
5889
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
5890
+ "\u5F97\u5206 ",
5891
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: score })
5892
+ ] }),
5893
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
5894
+ "\u547D ",
5895
+ lives,
5896
+ " \xB7 \u7B2C ",
5897
+ level,
5898
+ " \u5173"
5899
+ ] })
5900
+ ] })
5901
+ ] }),
5902
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-breakout", children: [
5903
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("canvas", { ref: canvasRef, width: W, height: H, tabIndex: 0, onKeyDown, onKeyUp, onPointerMove, onClick: () => focusStage(canvasRef), className: "dgame-breakout-canvas", "aria-label": "\u6253\u7816\u5757\u6E38\u620F\u533A\u57DF\uFF0C\u5DE6\u53F3\u65B9\u5411\u952E\u6216\u9F20\u6807\u79FB\u52A8\u6321\u677F\uFF0C\u7A7A\u683C\u53D1\u7403" }),
5904
+ status === "win" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-over", children: [
5905
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("strong", { children: [
5906
+ "\u5168\u6D88\uFF01\u8FDB\u5165\u7B2C ",
5907
+ level,
5908
+ " \u5173"
5909
+ ] }),
5910
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => {
5911
+ start(level);
5912
+ setStatus("play");
5913
+ statusRef.current = "play";
5914
+ }, children: "\u4E0B\u4E00\u5173" })
5915
+ ] }) : null,
5916
+ status === "over" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-over", children: [
5917
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("strong", { children: [
5918
+ "\u7403\u6389\u5149\u4E86 \xB7 \u5F97 ",
5919
+ score,
5920
+ " \u5206"
5921
+ ] }),
5922
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => {
5923
+ start(1);
5924
+ setScore(0);
5925
+ setLives(3);
5926
+ setStatus("play");
5927
+ statusRef.current = "play";
5928
+ livesRef.current = 3;
5929
+ }, children: "\u91CD\u65B0\u5F00\u59CB" })
5930
+ ] }) : null
5931
+ ] }),
5932
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-controls", children: [
5933
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-label": "\u5411\u5DE6\u79FB\u52A8", onClick: () => {
5934
+ keys.current.left = false;
5935
+ game.current && (game.current.paddle.x = Math.max(0, game.current.paddle.x - 12));
5936
+ }, children: "\u2190 \u5DE6\u79FB" }),
5937
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 \u7A7A\u683C\u53D1\u7403" }),
5938
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "button", "aria-label": "\u5411\u53F3\u79FB\u52A8", onClick: () => {
5939
+ keys.current.right = false;
5940
+ game.current && (game.current.paddle.x = Math.min(W - PADDLE_W, game.current.paddle.x + 12));
5941
+ }, children: [
5942
+ "\u53F3\u79FB ",
5943
+ "->"
5944
+ ] })
5945
+ ] })
5946
+ ] });
5947
+ }
5948
+ function BreakoutPreview() {
5949
+ const cols = ["#f87171", "#fb923c", "#facc15", "#4ade80", "#38bdf8", "#c084fc"];
5950
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "dgcov-prev dgcov-prev-breakout", "aria-hidden": "true", children: [
5951
+ Array.from({ length: 12 }, (_, i) => {
5952
+ const row = Math.floor(i / 6), c = i % 6;
5953
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("i", { style: { left: 5 + c * 15 + "%", top: 10 + row * 12 + "%", background: cols[i % cols.length] } }, i);
5954
+ }),
5955
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", {})
5956
+ ] });
5957
+ }
5958
+ var breakoutGame = { Game: BreakoutGame, Preview: BreakoutPreview, css: `
5959
+ .dgame-breakout{position:relative;align-self:center;width:min(100%,320px);margin:0 auto}.dgame-breakout-canvas{display:block;width:100%;aspect-ratio:320/360;border:1px solid color-mix(in srgb,rgb(56 189 248) 32%,var(--dsw-alias-border-l1));border-radius:10px;outline:none;background:#0b1024;box-shadow:0 4px 18px rgb(0 0 0/.4)}.dgame-breakout-canvas:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,rgb(56 189 248) 35%,transparent)}.dgame-breakout .dgame-over{position:absolute;inset:0;border-radius:10px}
5960
+ ` };
5961
+
5962
+ // features/games/games/racer.jsx
5963
+ var import_react16 = require("react");
5964
+ var import_jsx_runtime9 = require("react/jsx-runtime");
5965
+ var W2 = 300;
5966
+ var H2 = 420;
5967
+ var ROAD_W = 232;
5968
+ var ROAD_X = (W2 - ROAD_W) / 2;
5969
+ var CAR_W = 34;
5970
+ var CAR_H = 52;
5971
+ var CAR_COLORS = ["#f87171", "#fb923c", "#c084fc", "#38bdf8", "#4ade80", "#fde047"];
5972
+ function laneX(lane) {
5973
+ return ROAD_X + ROAD_W * (lane + 0.5) / 3 - CAR_W / 2;
5974
+ }
5975
+ function overlap(a, b) {
5976
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
5977
+ }
5978
+ function rect(ctx, x, y, w, h, r, fill) {
5979
+ ctx.fillStyle = fill;
5980
+ ctx.beginPath();
5981
+ ctx.moveTo(x + r, y);
5982
+ ctx.arcTo(x + w, y, x + w, y + h, r);
5983
+ ctx.arcTo(x + w, y + h, x, y + h, r);
5984
+ ctx.arcTo(x, y + h, x, y, r);
5985
+ ctx.arcTo(x, y, x + w, y, r);
5986
+ ctx.closePath();
5987
+ ctx.fill();
5988
+ }
5989
+ function RacerGame(props) {
5990
+ const canvasRef = (0, import_react16.useRef)(null);
5991
+ const [status, setStatus] = (0, import_react16.useState)("play");
5992
+ const game = (0, import_react16.useRef)(null);
5993
+ const keys = (0, import_react16.useRef)({});
5994
+ const statusRef = (0, import_react16.useRef)("play");
5995
+ statusRef.current = status;
5996
+ const pausedRef = (0, import_react16.useRef)(!!(props && props.paused));
5997
+ pausedRef.current = !!(props && props.paused);
5998
+ const reset = (0, import_react16.useCallback)(() => {
5999
+ game.current = {
6000
+ stars: Array.from({ length: 50 }, () => ({ x: Math.random() * W2, y: Math.random() * H2, a: 0.2 + Math.random() * 0.5 })),
6001
+ player: { x: laneX(1), y: H2 - CAR_H - 16 },
6002
+ enemies: [],
6003
+ coins: [],
6004
+ dist: 0,
6005
+ coinsGot: 0,
6006
+ spawn: 0,
6007
+ coinSpawn: 0,
6008
+ speed: 3.4,
6009
+ over: false
6010
+ };
6011
+ }, []);
6012
+ (0, import_react16.useEffect)(() => {
6013
+ reset();
6014
+ setStatus("play");
6015
+ }, [reset]);
6016
+ const launch = (0, import_react16.useCallback)(() => {
6017
+ reset();
6018
+ setStatus("play");
6019
+ }, [reset]);
6020
+ (0, import_react16.useEffect)(() => {
6021
+ const draw2 = () => {
6022
+ const g = game.current, ctx = canvasRef.current && canvasRef.current.getContext("2d");
6023
+ if (!g || !ctx) return;
6024
+ ctx.fillStyle = "#0b1024";
6025
+ ctx.fillRect(0, 0, W2, H2);
6026
+ ctx.fillStyle = "#1b2bb0";
6027
+ for (const s of g.stars) {
6028
+ ctx.globalAlpha = s.a;
6029
+ ctx.fillRect(s.x, s.y, 1.5, 1.5);
6030
+ }
6031
+ ctx.globalAlpha = 1;
6032
+ ctx.fillStyle = "#20243a";
6033
+ ctx.fillRect(ROAD_X, 0, ROAD_W, H2);
6034
+ ctx.fillStyle = "rgba(255,255,255,.09)";
6035
+ ctx.fillRect(ROAD_X + 6, 0, 4, H2);
6036
+ ctx.fillRect(ROAD_X + ROAD_W - 10, 0, 4, H2);
6037
+ ctx.fillStyle = "rgba(255,255,255,.16)";
6038
+ for (let i = 1; i < 3; i += 1) {
6039
+ const lx = ROAD_X + ROAD_W * i / 3 - 1;
6040
+ for (let yy = g.dist % 60; yy < H2; yy += 60) ctx.fillRect(lx, yy, 2, 26);
6041
+ }
6042
+ if (pausedRef.current || g.over) {
6043
+ drawHUD(ctx, g);
6044
+ return;
6045
+ }
6046
+ if (keys.current.left) g.player.x -= 5;
6047
+ if (keys.current.right) g.player.x += 5;
6048
+ g.player.x = Math.max(ROAD_X + 2, Math.min(ROAD_X + ROAD_W - CAR_W - 2, g.player.x));
6049
+ g.dist += g.speed * 0.16;
6050
+ g.spawn += 1;
6051
+ if (g.spawn > Math.max(20, 56 - g.dist / 600)) {
6052
+ g.spawn = 0;
6053
+ const lane = Math.floor(Math.random() * 3);
6054
+ const v = g.speed * (1.1 + Math.random() * 0.6);
6055
+ g.enemies.push({ x: laneX(lane), y: -CAR_H - Math.random() * 60, w: CAR_W, h: CAR_H, v, c: CAR_COLORS[Math.floor(Math.random() * CAR_COLORS.length)] });
6056
+ }
6057
+ g.coinSpawn += 1;
6058
+ if (g.coinSpawn > Math.max(30, 70 - g.dist / 500)) {
6059
+ g.coinSpawn = 0;
6060
+ g.coins.push({ x: laneX(Math.floor(Math.random() * 3)), y: -30, r: 11, got: false });
6061
+ }
6062
+ for (const e of g.enemies) e.y += e.v;
6063
+ for (const c of g.coins) c.y += g.speed;
6064
+ g.enemies = g.enemies.filter((e) => e.y < H2 + 20);
6065
+ g.coins = g.coins.filter((c) => c.y < H2 + 20);
6066
+ for (const e of g.enemies) {
6067
+ if (overlap(g.player, e)) {
6068
+ g.over = true;
6069
+ setStatus("over");
6070
+ statusRef.current = "over";
6071
+ }
6072
+ }
6073
+ for (const c of g.coins) {
6074
+ if (!c.got && Math.abs(c.x + c.r - (g.player.x + CAR_W / 2)) < CAR_W / 2 + 8 && Math.abs(c.y - (g.player.y + CAR_H / 2)) < CAR_H / 2 + 10) {
6075
+ c.got = true;
6076
+ g.coinsGot += 1;
6077
+ }
6078
+ }
6079
+ for (const e of g.enemies) {
6080
+ rect(ctx, e.x, e.y, e.w, e.h, 7, e.c);
6081
+ ctx.fillStyle = "rgba(255,255,255,.25)";
6082
+ ctx.fillRect(e.x + 6, e.y + 7, e.w - 12, 12);
6083
+ }
6084
+ ctx.fillStyle = "#fde047";
6085
+ for (const c of g.coins) {
6086
+ ctx.beginPath();
6087
+ ctx.arc(c.x + c.r, c.y, c.r, 0, Math.PI * 2);
6088
+ ctx.fill();
6089
+ ctx.fillStyle = "rgba(120,53,15,.4)";
6090
+ ctx.beginPath();
6091
+ ctx.arc(c.x + c.r, c.y, c.r * 0.5, 0, Math.PI * 2);
6092
+ ctx.fill();
6093
+ ctx.fillStyle = "#fde047";
6094
+ }
6095
+ drawCar(ctx, g.player.x, g.player.y, "#22d3ee");
6096
+ drawHUD(ctx, g);
6097
+ };
6098
+ const drawHUD = (ctx, g) => {
6099
+ ctx.fillStyle = "#e7ecff";
6100
+ ctx.font = "bold 13px system-ui, sans-serif";
6101
+ ctx.textBaseline = "top";
6102
+ ctx.fillText("\u8DDD\u79BB " + Math.floor(g.dist / 60) + " m", 10, 10);
6103
+ ctx.fillStyle = "#fde047";
6104
+ ctx.fillText("\u91D1\u5E01 \xD7 " + g.coinsGot, W2 - 70, 10);
6105
+ };
6106
+ function drawCar(ctx, x, y, c) {
6107
+ rect(ctx, x, y, CAR_W, CAR_H, 9, c);
6108
+ ctx.fillStyle = "rgba(255,255,255,.3)";
6109
+ rect(ctx, x + 5, y + 6, CAR_W - 10, 13, 4, "rgba(255,255,255,.3)");
6110
+ ctx.fillStyle = "rgba(15,23,42,.5)";
6111
+ rect(ctx, x + 5, y + CAR_H - 16, CAR_W - 10, 9, 3, "rgba(15,23,42,.5)");
6112
+ ctx.fillStyle = "rgba(255,255,255,.45)";
6113
+ rect(ctx, x + 3, y + CAR_H - 4, 6, 5, 2, "#fde047");
6114
+ rect(ctx, x + CAR_W - 9, y + CAR_H - 4, 6, 5, 2, "#fde047");
6115
+ }
6116
+ let raf = requestAnimationFrame(function loop() {
6117
+ draw2();
6118
+ raf = requestAnimationFrame(loop);
6119
+ });
6120
+ return () => cancelAnimationFrame(raf);
6121
+ }, []);
6122
+ const onKeyDown = (0, import_react16.useCallback)((event) => {
6123
+ if (pausedRef.current) return;
6124
+ if (event.key === "ArrowLeft") {
6125
+ event.preventDefault();
6126
+ keys.current.left = true;
6127
+ } else if (event.key === "ArrowRight") {
6128
+ event.preventDefault();
6129
+ keys.current.right = true;
6130
+ } else if (event.key.toLowerCase() === "r") {
6131
+ event.preventDefault();
6132
+ launch();
6133
+ }
6134
+ }, [launch]);
6135
+ const onKeyUp = (0, import_react16.useCallback)((event) => {
6136
+ if (event.key === "ArrowLeft") keys.current.left = false;
6137
+ else if (event.key === "ArrowRight") keys.current.right = false;
6138
+ }, []);
6139
+ const onPointerMove = (0, import_react16.useCallback)((e) => {
6140
+ const g = game.current, cv = canvasRef.current;
6141
+ if (!g || !cv) return;
6142
+ const rect0 = cv.getBoundingClientRect();
6143
+ const scale = rect0.width / W2;
6144
+ g.player.x = Math.max(ROAD_X + 2, Math.min(ROAD_X + ROAD_W - CAR_W - 2, (e.clientX - rect0.left) / scale - CAR_W / 2));
6145
+ }, []);
6146
+ useGameControls(canvasRef, pausedRef.current, onKeyDown, onKeyUp);
6147
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "dgame-game", "aria-label": "\u6781\u901F\u8D5B\u8F66", children: [
6148
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dgame-game-head", children: [
6149
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
6150
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u6781\u901F\u8D5B\u8F66" }),
6151
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: "\u5DE6\u53F3\u79FB\u52A8\u907F\u5F00\u8FCE\u9762\u6765\u8F66\u3001\u6536\u96C6\u91D1\u5E01\uFF0C\u8D8A\u5F00\u8D8A\u5FEB\uFF1B\u649E\u8F66\u5373\u7ED3\u675F\u3002\u8DDD\u79BB\u4E0E\u91D1\u5E01\u5B9E\u65F6\u663E\u793A\u5728\u6E38\u620F\u753B\u9762\u5DE6\u4E0A/\u53F3\u4E0A\u3002" })
6152
+ ] }),
6153
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dgame-score", children: [
6154
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "\u64CD\u4F5C" }),
6155
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "\u2190/\u2192 \u6216\u9F20\u6807" })
6156
+ ] })
6157
+ ] }),
6158
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dgame-racer", children: [
6159
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("canvas", { ref: canvasRef, width: W2, height: H2, tabIndex: 0, onKeyDown, onKeyUp, onPointerMove, onClick: () => focusStage(canvasRef), className: "dgame-racer-canvas", "aria-label": "\u6781\u901F\u8D5B\u8F66\u6E38\u620F\u533A\u57DF\uFF0C\u5DE6\u53F3\u65B9\u5411\u952E\u6216\u9F20\u6807\u63A7\u5236\u65B9\u5411" }),
6160
+ status === "over" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dgame-over", children: [
6161
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("strong", { children: [
6162
+ "\u649E\u8F66\u4E86 \xB7 \u884C\u9A76 ",
6163
+ game.current && Math.floor(game.current.dist / 60) || 0,
6164
+ " \u7C73"
6165
+ ] }),
6166
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: launch, children: "\u91CD\u65B0\u51FA\u53D1" })
6167
+ ] }) : null
6168
+ ] }),
6169
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dgame-controls", children: [
6170
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-label": "\u5411\u5DE6", onClick: () => {
6171
+ keys.current.left = false;
6172
+ if (game.current) game.current.player.x = Math.max(ROAD_X + 2, game.current.player.x - 14);
6173
+ }, children: "\u2190 \u5DE6\u79FB" }),
6174
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 R \u91CD\u5F00" }),
6175
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { type: "button", "aria-label": "\u5411\u53F3", onClick: () => {
6176
+ keys.current.right = false;
6177
+ if (game.current) game.current.player.x = Math.min(ROAD_X + ROAD_W - CAR_W - 2, game.current.player.x + 14);
6178
+ }, children: [
6179
+ "\u53F3\u79FB ",
6180
+ "->"
6181
+ ] })
6182
+ ] })
6183
+ ] });
6184
+ }
6185
+ function RacerPreview() {
6186
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "dgcov-prev dgcov-prev-racer", "aria-hidden": "true", children: [
6187
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { style: { left: "20%", top: "8%" } }),
6188
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { style: { left: "55%", top: "22%" } }),
6189
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { style: { left: "20%", top: "42%" } }),
6190
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { style: { left: "64%", top: "58%" } }),
6191
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("i", { style: { left: "38%", top: "74%" } }),
6192
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("b", {})
6193
+ ] });
6194
+ }
6195
+ var racerGame = { Game: RacerGame, Preview: RacerPreview, css: `
6196
+ .dgame-racer{position:relative;align-self:center;width:min(100%,300px);margin:0 auto}.dgame-racer-canvas{display:block;width:100%;aspect-ratio:300/420;border:1px solid color-mix(in srgb,rgb(34 211 238) 32%,var(--dsw-alias-border-l1));border-radius:10px;outline:none;background:#0b1024;box-shadow:0 4px 18px rgb(0 0 0/.4)}.dgame-racer-canvas:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,rgb(34 211 238) 35%,transparent)}.dgame-racer .dgame-over{position:absolute;inset:0;border-radius:10px}.dgame-racer-stat{color:var(--dsw-alias-label-tertiary)}
6197
+ ` };
6198
+
6199
+ // features/games/games/tank.jsx
6200
+ var import_react17 = require("react");
6201
+
6202
+ // features/games/tank-core.js
6203
+ var GRID = 15;
6204
+ var EMPTY = 0;
6205
+ var STEEL = 1;
6206
+ var BRICK = 2;
6207
+ var BASE = 3;
6208
+ var DIRS4 = { up: [-1, 0], down: [1, 0], left: [0, -1], right: [0, 1] };
6209
+ var DIR_KEYS = Object.keys(DIRS4);
6210
+ var SPAWNS = [{ r: 0, c: 0 }, { r: 0, c: 7 }, { r: 0, c: 14 }];
6211
+ function cloneGrid2(g) {
6212
+ return g.map((row) => row.slice());
6213
+ }
6214
+ function inBounds2(r, c) {
6215
+ return r >= 0 && r < GRID && c >= 0 && c < GRID;
6216
+ }
6217
+ function cellBlockedForTank(grid, r, c) {
6218
+ return !inBounds2(r, c) || grid[r][c] !== EMPTY;
6219
+ }
6220
+ function makeGrid() {
6221
+ const g = Array.from({ length: GRID }, () => Array(GRID).fill(EMPTY));
6222
+ const set = (r, c, v) => {
6223
+ if (r >= 0 && r < GRID && c >= 0 && c < GRID) g[r][c] = v;
6224
+ };
6225
+ for (let r = 2; r <= 5; r += 1) {
6226
+ set(r, 3, STEEL);
6227
+ set(r, 11, STEEL);
6228
+ set(r, 7, STEEL);
6229
+ }
6230
+ for (let c = 4; c <= 10; c += 1) set(8, c, BRICK);
6231
+ set(9, 5, BRICK);
6232
+ set(9, 6, BRICK);
6233
+ set(9, 8, BRICK);
6234
+ set(9, 9, BRICK);
6235
+ set(9, 10, BRICK);
6236
+ set(13, 7, BASE);
6237
+ set(12, 6, BRICK);
6238
+ set(12, 7, BRICK);
6239
+ set(12, 8, BRICK);
6240
+ set(13, 6, BRICK);
6241
+ set(13, 8, BRICK);
6242
+ return g;
6243
+ }
6244
+ function findBase(grid) {
6245
+ for (let r = 0; r < GRID; r += 1) for (let c = 0; c < GRID; c += 1) if (grid[r][c] === BASE) return { r, c };
6246
+ return { r: 13, c: 7 };
6247
+ }
6248
+ function playerSpawn() {
6249
+ return { r: 11, c: 7, dir: "up", alive: true, lives: 3, cooldown: 0 };
6250
+ }
6251
+ function initialState() {
6252
+ return {
6253
+ grid: makeGrid(),
6254
+ player: playerSpawn(),
6255
+ enemies: [],
6256
+ bullets: [],
6257
+ score: 0,
6258
+ wave: 1,
6259
+ remaining: 4,
6260
+ active: 2,
6261
+ spawnTick: 0,
6262
+ over: false,
6263
+ win: false,
6264
+ respawnTicks: 0
6265
+ };
6266
+ }
6267
+ function spawnBullet(r, c, dir, from) {
6268
+ const [dr, dc] = DIRS4[dir];
6269
+ return { r: r + dr, c: c + dc, dr, dc, from };
6270
+ }
6271
+ function spawnOne(grid, enemies) {
6272
+ for (const s of SPAWNS) {
6273
+ if (grid[s.r][s.c] === EMPTY && !enemies.some((e) => e.r === s.r && e.c === s.c)) {
6274
+ return { r: s.r, c: s.c, dir: "down", hp: 1, cooldown: 3, moveTick: 0 };
6275
+ }
6276
+ }
6277
+ return null;
6278
+ }
6279
+ function step3(state, input) {
6280
+ if (state.over || state.win) return state;
6281
+ const grid = cloneGrid2(state.grid);
6282
+ let player = Object.assign({}, state.player);
6283
+ let enemies = state.enemies.map((e) => Object.assign({}, e));
6284
+ let bullets = state.bullets.map((b) => Object.assign({}, b));
6285
+ let remaining = state.remaining, score = state.score, over = state.over;
6286
+ let respawnTicks = state.respawnTicks;
6287
+ let spawnTick = state.spawnTick + 1;
6288
+ if (player.alive && input.dir && DIRS4[input.dir]) {
6289
+ const [dr, dc] = DIRS4[input.dir];
6290
+ player.dir = input.dir;
6291
+ const nr = player.r + dr, nc = player.c + dc;
6292
+ if (!cellBlockedForTank(grid, nr, nc)) {
6293
+ player.r = nr;
6294
+ player.c = nc;
6295
+ }
6296
+ }
6297
+ if (player.alive && input.shoot && player.cooldown <= 0) {
6298
+ bullets.push(spawnBullet(player.r, player.c, player.dir, "p"));
6299
+ player.cooldown = 9;
6300
+ }
6301
+ player.cooldown = Math.max(0, player.cooldown - 1);
6302
+ for (let i = 0; i < enemies.length; i += 1) {
6303
+ const e = enemies[i];
6304
+ e.cooldown -= 1;
6305
+ e.moveTick += 1;
6306
+ if (e.moveTick % 12 === 0) {
6307
+ const [dr, dc] = DIRS4[e.dir];
6308
+ const nr = e.r + dr, nc = e.c + dc;
6309
+ if (cellBlockedForTank(grid, nr, nc) || enemies.some((o, oi) => oi !== i && o.r === nr && o.c === nc)) {
6310
+ const opts = DIR_KEYS.filter((d) => {
6311
+ const [ddr, ddc] = DIRS4[d];
6312
+ const tr = e.r + ddr, tc = e.c + ddc;
6313
+ return !cellBlockedForTank(grid, tr, tc) && !enemies.some((o, oi) => oi !== i && o.r === tr && o.c === tc);
6314
+ });
6315
+ if (opts.length) e.dir = opts[Math.floor(Math.random() * opts.length)];
6316
+ } else {
6317
+ e.r = nr;
6318
+ e.c = nc;
6319
+ }
6320
+ }
6321
+ if (e.cooldown <= 0) {
6322
+ bullets.push(spawnBullet(e.r, e.c, e.dir, "e"));
6323
+ e.cooldown = 9 + Math.floor(Math.random() * 9);
6324
+ }
6325
+ }
6326
+ let baseHit = false, playerHit = false;
6327
+ const nextBullets = [];
6328
+ const base = findBase(grid);
6329
+ for (const b of bullets) {
6330
+ const nr = b.r + b.dr, nc = b.c + b.dc;
6331
+ if (!inBounds2(nr, nc)) continue;
6332
+ const cell = grid[nr][nc];
6333
+ if (cell === STEEL) continue;
6334
+ if (cell === BRICK) {
6335
+ grid[nr][nc] = EMPTY;
6336
+ continue;
6337
+ }
6338
+ if (cell === BASE) {
6339
+ if (b.from === "e") baseHit = true;
6340
+ continue;
6341
+ }
6342
+ if (b.from === "p") {
6343
+ const idx = enemies.findIndex((e) => e.r === nr && e.c === nc);
6344
+ if (idx >= 0) {
6345
+ enemies.splice(idx, 1);
6346
+ score += 100;
6347
+ continue;
6348
+ }
6349
+ } else if (player.alive && player.r === nr && player.c === nc) {
6350
+ player.lives -= 1;
6351
+ if (player.lives <= 0) over = true;
6352
+ else {
6353
+ player.alive = false;
6354
+ respawnTicks = 24;
6355
+ }
6356
+ continue;
6357
+ }
6358
+ b.r = nr;
6359
+ b.c = nc;
6360
+ nextBullets.push(b);
6361
+ }
6362
+ if (remaining > 0 && enemies.length < state.active && spawnTick % 9 === 0) {
6363
+ const fresh = spawnOne(grid, enemies);
6364
+ if (fresh) {
6365
+ enemies.push(fresh);
6366
+ remaining -= 1;
6367
+ }
6368
+ }
6369
+ if (!player.alive && !over) {
6370
+ if (respawnTicks <= 0) {
6371
+ player.alive = true;
6372
+ player.r = 11;
6373
+ player.c = 7;
6374
+ player.dir = "up";
6375
+ player.cooldown = 0;
6376
+ } else respawnTicks -= 1;
6377
+ }
6378
+ const win = remaining <= 0 && enemies.length === 0;
6379
+ return Object.assign({}, state, {
6380
+ grid,
6381
+ player,
6382
+ enemies,
6383
+ bullets: nextBullets,
6384
+ score,
6385
+ remaining,
6386
+ over: over || baseHit,
6387
+ win,
6388
+ spawnTick,
6389
+ respawnTicks
6390
+ });
6391
+ }
6392
+ function nextWave(state) {
6393
+ return Object.assign({}, state, {
6394
+ wave: state.wave + 1,
6395
+ remaining: Math.min(3 + state.wave, 8),
6396
+ active: Math.min(2 + Math.floor(state.wave / 2), 4),
6397
+ enemies: [],
6398
+ win: false,
6399
+ spawnTick: 0
6400
+ });
6401
+ }
6402
+
6403
+ // features/games/games/tank.jsx
6404
+ var import_jsx_runtime10 = require("react/jsx-runtime");
6405
+ var DIR_ANGLE = { up: 0, right: 90, down: 180, left: 270 };
6406
+ var ENEMY_COLORS = ["#f87171", "#fb923c", "#c084fc", "#4ade80", "#38bdf8"];
6407
+ function TankGame(props) {
6408
+ const [snap, setSnap] = (0, import_react17.useState)(initialState);
6409
+ const [status, setStatus] = (0, import_react17.useState)("play");
6410
+ const snapRef = (0, import_react17.useRef)(snap);
6411
+ snapRef.current = snap;
6412
+ const keys = (0, import_react17.useRef)({ dir: null, shoot: false });
6413
+ const pausedRef = (0, import_react17.useRef)(!!(props && props.paused));
6414
+ pausedRef.current = !!(props && props.paused);
6415
+ const statusRef = (0, import_react17.useRef)("play");
6416
+ statusRef.current = status;
6417
+ const boardRef = (0, import_react17.useRef)(null);
6418
+ const start = (0, import_react17.useCallback)(() => {
6419
+ setSnap(initialState());
6420
+ setStatus("play");
6421
+ statusRef.current = "play";
6422
+ }, []);
6423
+ const nextLvl = (0, import_react17.useCallback)(() => {
6424
+ setSnap((s) => nextWave(s));
6425
+ setStatus("play");
6426
+ statusRef.current = "play";
6427
+ }, []);
6428
+ (0, import_react17.useEffect)(() => {
6429
+ const timer = setInterval(() => {
6430
+ if (pausedRef.current) return;
6431
+ setSnap((s) => {
6432
+ if (s.over || s.win) return s;
6433
+ const input = { dir: keys.current.dir, shoot: keys.current.shoot };
6434
+ const ns = step3(s, input);
6435
+ if (ns.over && statusRef.current === "play") {
6436
+ statusRef.current = "over";
6437
+ setStatus("over");
6438
+ } else if (ns.win && statusRef.current === "play") {
6439
+ statusRef.current = "wave";
6440
+ setStatus("wave");
6441
+ }
6442
+ return ns;
6443
+ });
6444
+ }, 120);
6445
+ return () => clearInterval(timer);
6446
+ }, []);
6447
+ const stageRef = (0, import_react17.useRef)(null);
6448
+ const onKeyDown = (0, import_react17.useCallback)((event) => {
6449
+ if (pausedRef.current) return;
6450
+ const map = { ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right", w: "up", s: "down", a: "left", d: "right", W: "up", S: "down", A: "left", D: "right" };
6451
+ const dir = map[event.key];
6452
+ if (dir) {
6453
+ event.preventDefault();
6454
+ keys.current.dir = dir;
6455
+ return;
6456
+ }
6457
+ if (event.key === " ") {
6458
+ event.preventDefault();
6459
+ keys.current.shoot = true;
6460
+ } else if (event.key.toLowerCase() === "r") {
6461
+ event.preventDefault();
6462
+ start();
6463
+ }
6464
+ }, [start]);
6465
+ const onKeyUp = (0, import_react17.useCallback)((event) => {
6466
+ const map = { ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right", w: "up", s: "down", a: "left", d: "right", W: "up", S: "down", A: "left", D: "right" };
6467
+ const dir = map[event.key];
6468
+ if (dir && keys.current.dir === dir) keys.current.dir = null;
6469
+ if (event.key === " ") keys.current.shoot = false;
6470
+ }, []);
6471
+ useGameControls(stageRef, pausedRef.current, onKeyDown, onKeyUp);
6472
+ const grid = snap.grid;
6473
+ const player = snap.player;
6474
+ const enemies = snap.enemies;
6475
+ const bullets = snap.bullets;
6476
+ let playerPos = null;
6477
+ if (player && player.alive) playerPos = { r: player.r, c: player.c };
6478
+ const enemyMap = {};
6479
+ for (const e of enemies) enemyMap[e.r + "-" + e.c] = e;
6480
+ const bulletMap = {};
6481
+ for (const b of bullets) bulletMap[b.r + "-" + b.c] = b;
6482
+ const basePos = findBase(grid);
6483
+ const cells = [];
6484
+ for (let r = 0; r < GRID; r += 1) {
6485
+ for (let c = 0; c < GRID; c += 1) {
6486
+ const key = r + "-" + c;
6487
+ let cls = "dgame-tank-cell";
6488
+ let inner = null;
6489
+ if (grid[r][c] === STEEL) cls += " steel";
6490
+ else if (grid[r][c] === BRICK) cls += " brick";
6491
+ else if (grid[r][c] === BASE) cls += " base";
6492
+ if (playerPos && playerPos.r === r && playerPos.c === c) {
6493
+ cls += " pw";
6494
+ inner = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("i", { className: "dgame-tank-body pw", style: { transform: "rotate(" + DIR_ANGLE[player.dir] + "deg)" } });
6495
+ } else if (enemyMap[key]) {
6496
+ const e = enemyMap[key];
6497
+ cls += " en";
6498
+ inner = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("i", { className: "dgame-tank-body en", style: { background: ENEMY_COLORS[(e.r + e.c) % ENEMY_COLORS.length], transform: "rotate(" + DIR_ANGLE[e.dir] + "deg)" } });
6499
+ }
6500
+ if (bullets && bulletMap[key]) cls += " bullet";
6501
+ cells.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: cls, children: [
6502
+ inner,
6503
+ bulletMap[key] ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("i", { className: "dgame-tank-bullet", "aria-hidden": "true" }) : null,
6504
+ grid[r][c] === BASE ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("b", { className: "dgame-tank-flag", "aria-hidden": "true", children: "\u2691" }) : null
6505
+ ] }, key));
6506
+ }
6507
+ }
6508
+ const statusText = status === "over" ? "\u57FA\u5730\u5931\u5B88 \xB7 \u6E38\u620F\u7ED3\u675F" : status === "wave" ? "\u7B2C " + snap.wave + " \u6CE2\u6E05\u7A7A\uFF01\u8FDB\u5165\u4E0B\u4E00\u6CE2" : "\u7B2C " + snap.wave + " \u6CE2 \xB7 \u5F85\u6B7C\u706D " + (snap.remaining + snap.enemies.length) + " \u8F86 \xB7 \u751F\u547D \xD7 " + (player && player.lives);
6509
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "dgame-game", "aria-label": "\u5766\u514B\u5927\u6218", children: [
6510
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dgame-game-head", children: [
6511
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
6512
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { children: "\u5766\u514B\u5927\u6218" }),
6513
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: "\u65B9\u5411\u952E\u79FB\u52A8\uFF0C\u7A7A\u683C\u5F00\u70AE\uFF1B\u51FB\u6BC1\u654C\u65B9\u5766\u514B\uFF0C\u522B\u8BA9\u654C\u519B\u6467\u6BC1\u4E2D\u592E\u57FA\u5730\u3002" })
6514
+ ] }),
6515
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dgame-score", children: [
6516
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6517
+ "\u5F97\u5206 ",
6518
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: snap.score })
6519
+ ] }),
6520
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6521
+ "\u751F\u547D \xD7 ",
6522
+ player && player.lives
6523
+ ] })
6524
+ ] })
6525
+ ] }),
6526
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dgame-tank-stage", ref: stageRef, tabIndex: 0, role: "grid", onKeyDown, onKeyUp, onClick: () => focusStage(stageRef), "aria-label": "\u5766\u514B\u5927\u6218\u6E38\u620F\u533A\u57DF\uFF0C\u65B9\u5411\u952E\u79FB\u52A8\uFF0C\u7A7A\u683C\u5F00\u70AE", children: [
6527
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dgame-tank-grid", children: cells }),
6528
+ status === "over" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dgame-over", children: [
6529
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("strong", { children: [
6530
+ "\u80DC\u8D25\u5DF2\u5206 \xB7 \u5F97 ",
6531
+ snap.score,
6532
+ " \u5206"
6533
+ ] }),
6534
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: start, children: "\u91CD\u65B0\u5F00\u59CB" })
6535
+ ] }) : null,
6536
+ status === "wave" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dgame-over", children: [
6537
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("strong", { children: [
6538
+ "\u7B2C ",
6539
+ snap.wave,
6540
+ " \u6CE2\u6E05\u7A7A\uFF01"
6541
+ ] }),
6542
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: nextLvl, children: "\u4E0B\u4E00\u6CE2" })
6543
+ ] }) : null
6544
+ ] }),
6545
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dgame-controls dgame-tank-controls", children: [
6546
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-label": "\u5411\u4E0A", onClick: () => {
6547
+ keys.current.dir = "up";
6548
+ setTimeout(() => {
6549
+ if (keys.current.dir === "up") keys.current.dir = null;
6550
+ }, 90);
6551
+ }, children: "\u2191" }),
6552
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "\u65B9\u5411\u952E / WASD \xB7 \u7A7A\u683C\u5F00\u70AE \xB7 R \u91CD\u5F00" }),
6553
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "dgame-tank-fire", "aria-label": "\u5F00\u70AE", onClick: () => {
6554
+ keys.current.shoot = true;
6555
+ setTimeout(() => {
6556
+ keys.current.shoot = false;
6557
+ }, 90);
6558
+ }, children: "\u5F00\u70AE" })
6559
+ ] })
6560
+ ] });
6561
+ }
6562
+ function TankPreview() {
6563
+ const ROWS3 = [
6564
+ "..B..B..",
6565
+ "S.....E.",
6566
+ "..B.B...",
6567
+ "B..S...B",
6568
+ "...P..F."
6569
+ ];
6570
+ const cls = { ".": "void", B: "brick", S: "steel", P: "pw", E: "en", F: "base" };
6571
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dgcov-prev dgcov-prev-tank", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dgp-tank-grid", children: ROWS3.flatMap((row, r) => row.split("").map((ch, c) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("i", { className: "dgp-tank-" + cls[ch] }, r + "-" + c))) }) });
6572
+ }
6573
+ var tankGame = { Game: TankGame, Preview: TankPreview, css: `
6574
+ .dgame-tank-stage{position:relative;align-self:center;width:min(100%,320px);aspect-ratio:1/1;border:1px solid color-mix(in srgb,rgb(250 204 21) 32%,var(--dsw-alias-border-l1));border-radius:8px;overflow:hidden;outline:none;background:linear-gradient(180deg,#1a1425,#100d1c);box-shadow:inset 0 0 26px rgb(0 0 0/.5)}.dgame-tank-stage:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,rgb(250 204 21) 30%,transparent)}.dgame-tank-grid{display:grid;grid-template-columns:repeat(15,1fr);grid-template-rows:repeat(15,1fr);width:100%;height:100%;gap:0}.dgame-tank-cell{position:relative;background:rgba(255,255,255,.03);min-width:0;min-height:0}.dgame-tank-cell.steel{background:linear-gradient(180deg,#94a3b8,#64748b)}.dgame-tank-cell.brick{background:repeating-linear-gradient(45deg,#a3672a 0 55%,#8a5a1c 55% 100%);box-shadow:inset 0 0 0 1px rgba(0,0,0,.25)}.dgame-tank-cell.base{background:#fbbf24}.dgame-tank-cell.bullet::before{content:'';position:absolute;inset:38%;background:#fef3c7;box-shadow:0 0 5px #fef3c7;z-index:4}.dgame-tank-flag{position:absolute;inset:0;display:grid;place-items:center;color:#7c2d12;font-size:18px;z-index:2}.dgame-tank-body{position:absolute;inset:16%;display:block;border-radius:16%;z-index:3;transition:transform .1s ease}.dgame-tank-body.pw{background:linear-gradient(135deg,#fde047,#eab308);box-shadow:0 0 7px color-mix(in srgb,#eab308 55%,transparent)}.dgame-tank-body.en{background:#f87171;box-shadow:0 0 5px color-mix(in srgb,#f87171 40%,transparent)}.dgame-tank-body::before{content:'';position:absolute;left:38%;top:-30%;width:24%;height:64%;background:rgba(255,255,255,.5);border-radius:3px}.dgame-tank-controls{grid-template-columns:1fr auto 1fr}
6575
+ ` };
6576
+
6577
+ // features/games/view.jsx
6578
+ var import_jsx_runtime11 = require("react/jsx-runtime");
6579
+ var LANES = 3;
6580
+ var REACTOR_NEIGHBORS = [[0, 1, 3], [1, 0, 2, 4], [2, 1, 5], [3, 0, 4, 6], [4, 1, 3, 5, 7], [5, 2, 4, 8], [6, 3, 7], [7, 4, 6, 8], [8, 5, 7]];
6581
+ var FAB_POS_KEY = "dsh-dock/games/fab/v1";
6582
+ var WIN_GEOM_KEY = "dsh-dock/games/win/v1";
6583
+ var FAB_W = 36;
6584
+ var FAB_H = 120;
6585
+ var WIN_MIN_W = 360;
6586
+ var WIN_MIN_H = 340;
6587
+ function phaseLabel2(phase) {
6588
+ return { think: "\u601D\u8003\u4E2D", write: "\u8F93\u51FA\u4E2D", code: "\u7F16\u5199\u4EE3\u7801", search: "\u67E5\u8D44\u6599" }[phase] || "\u5904\u7406\u4E2D";
6589
+ }
6590
+ function elapsedText(ms) {
6591
+ const sec = Math.max(0, Math.floor((Number(ms) || 0) / 1e3));
6592
+ const min = Math.floor(sec / 60);
6593
+ return min > 0 ? min + " \u5206 " + String(sec % 60).padStart(2, "0") + " \u79D2" : sec + " \u79D2";
6594
+ }
6595
+ function makeReactor() {
6596
+ let cells = Array(9).fill(true);
6597
+ const seed = [4, 0, 8, 1, 7, 3];
6598
+ for (const index of seed) {
6599
+ for (const target of REACTOR_NEIGHBORS[index]) cells[target] = !cells[target];
6600
+ }
6601
+ return cells;
6602
+ }
6603
+ function initialDash() {
6604
+ return { lane: 1, rocks: [], score: 0, shield: 3, running: true, nextId: 1 };
6605
+ }
6606
+ function useTaskPulse() {
6607
+ const [status, setStatus] = (0, import_react18.useState)({ loading: true, active: [], error: false });
6608
+ const refresh = (0, import_react18.useCallback)(() => fetch("/dsh-dock/animation/status", {
6609
+ method: "POST",
6610
+ headers: { "content-type": "application/json" },
6611
+ body: "{}"
6612
+ }).then((res) => res.json()).then((payload) => {
6613
+ if (!payload || !payload.ok) throw new Error("status unavailable");
6614
+ setStatus({ loading: false, active: Array.isArray(payload.data && payload.data.active) ? payload.data.active : [], error: false });
6615
+ }).catch(() => setStatus((current) => ({ loading: false, active: current.active || [], error: true }))), []);
6616
+ (0, import_react18.useEffect)(() => {
6617
+ refresh();
6618
+ const timer = setInterval(refresh, 2600);
6619
+ return () => clearInterval(timer);
6620
+ }, [refresh]);
6621
+ return status;
6622
+ }
6623
+ function DashGame(props) {
6624
+ const [dash, setDash] = (0, import_react18.useState)(initialDash);
6625
+ const pausedRef = (0, import_react18.useRef)(!!(props && props.paused));
6626
+ pausedRef.current = !!(props && props.paused);
6627
+ const move = (0, import_react18.useCallback)((delta) => setDash((current) => Object.assign({}, current, {
6628
+ lane: Math.max(0, Math.min(LANES - 1, current.lane + delta))
6629
+ })), []);
6630
+ const stageRef = (0, import_react18.useRef)(null);
6631
+ const onKeyDown = (0, import_react18.useCallback)((event) => {
6632
+ if (event.key === "ArrowLeft" || event.key.toLowerCase() === "a") {
6633
+ event.preventDefault();
6634
+ move(-1);
6635
+ } else if (event.key === "ArrowRight" || event.key.toLowerCase() === "d") {
6636
+ event.preventDefault();
6637
+ move(1);
6638
+ } else if (!dash.running && (event.key === "Enter" || event.key === " ")) {
6639
+ event.preventDefault();
6640
+ setDash(initialDash());
6641
+ }
6642
+ }, [move, dash.running]);
6643
+ useGameControls(stageRef, props.paused, onKeyDown);
6644
+ (0, import_react18.useEffect)(() => {
6645
+ const timer = setInterval(() => {
6646
+ if (pausedRef.current || typeof document !== "undefined" && document.hidden) return;
6647
+ setDash((current) => {
6648
+ if (!current.running) return current;
6649
+ let gained = 0;
6650
+ let hit = false;
6651
+ const rocks = [];
6652
+ for (const rock of current.rocks) {
6653
+ const next = Object.assign({}, rock, { row: rock.row + 1 });
6654
+ if (next.row >= 4) {
6655
+ if (next.lane === current.lane) hit = true;
6656
+ else gained += 1;
6657
+ } else rocks.push(next);
6658
+ }
6659
+ const shield = hit ? current.shield - 1 : current.shield;
6660
+ if (Math.random() > 0.32) rocks.push({ id: current.nextId, lane: Math.floor(Math.random() * LANES), row: 0 });
6661
+ return {
6662
+ lane: current.lane,
6663
+ rocks,
6664
+ score: current.score + gained,
6665
+ shield,
6666
+ running: shield > 0,
6667
+ nextId: current.nextId + 1
6668
+ };
6669
+ });
6670
+ }, 620);
6671
+ return () => clearInterval(timer);
6672
+ }, []);
6673
+ const cells = [];
6674
+ for (let row = 0; row < 5; row += 1) {
6675
+ for (let lane = 0; lane < LANES; lane += 1) {
6676
+ const rock = dash.rocks.find((item) => item.row === row && item.lane === lane);
6677
+ const pilot = row === 4 && lane === dash.lane;
6678
+ cells.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgame-dash-cell", children: [
6679
+ rock ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", { className: "dgame-rock", children: "\u25C6" }) : null,
6680
+ pilot ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { className: "dgame-pilot", children: "\u25B2" }) : null
6681
+ ] }, row + "-" + lane));
6682
+ }
6683
+ }
6684
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "dgame-game", "aria-label": "\u661F\u9645\u8EB2\u907F\u5C0F\u6E38\u620F", children: [
6685
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-game-head", children: [
6686
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
6687
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { children: "\u661F\u9645\u8EB2\u907F" }),
6688
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: "\u5DE6\u53F3\u79FB\u52A8\u8865\u7ED9\u8247\uFF0C\u907F\u5F00\u6B63\u5728\u5760\u843D\u7684\u9668\u77F3\u3002" })
6689
+ ] }),
6690
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-score", children: [
6691
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
6692
+ "\u5F97\u5206 ",
6693
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: dash.score })
6694
+ ] }),
6695
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "aria-label": "\u62A4\u76FE " + dash.shield + " \u683C", children: [
6696
+ "\u62A4\u76FE ",
6697
+ "\u2726".repeat(Math.max(0, dash.shield)),
6698
+ "\xB7".repeat(Math.max(0, 3 - dash.shield))
6699
+ ] })
6700
+ ] })
6701
+ ] }),
6702
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-dash-stage", ref: stageRef, tabIndex: 0, onKeyDown, onClick: () => focusStage(stageRef), "aria-label": "\u661F\u9645\u8EB2\u907F\u6E38\u620F\u533A\u57DF\uFF0C\u4F7F\u7528\u5DE6\u53F3\u65B9\u5411\u952E\u6216\u4E0B\u65B9\u6309\u94AE\u79FB\u52A8", children: [
6703
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgame-dash-grid", children: cells }),
6704
+ !dash.running ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-over", children: [
6705
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: "\u62A4\u76FE\u8017\u5C3D" }),
6706
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", onClick: () => setDash(initialDash()), children: "\u91CD\u65B0\u51FA\u53D1" })
6707
+ ] }) : null
6708
+ ] }),
6709
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-controls", children: [
6710
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-label": "\u5411\u5DE6\u79FB\u52A8", onClick: () => move(-1), children: "\u2190 \u5DE6\u79FB" }),
6711
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: "\u65B9\u5411\u952E / A\u3001D \xB7 \u7A7A\u683C\u91CD\u5F00" }),
6712
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", "aria-label": "\u5411\u53F3\u79FB\u52A8", onClick: () => move(1), children: [
6713
+ "\u53F3\u79FB ",
6714
+ "->"
6715
+ ] })
6716
+ ] }),
6717
+ " "
6718
+ ] });
6719
+ }
6720
+ function ReactorGame(props) {
6721
+ const [cells, setCells] = (0, import_react18.useState)(makeReactor);
6722
+ const [moves, setMoves] = (0, import_react18.useState)(0);
6723
+ const [cursor, setCursor] = (0, import_react18.useState)(4);
6724
+ const gridRef = (0, import_react18.useRef)(null);
6725
+ const solved = cells.every(Boolean);
6726
+ const toggle = (index) => {
6727
+ if (solved) return;
6728
+ setCells((current) => current.map((on, target) => REACTOR_NEIGHBORS[index].includes(target) ? !on : on));
6729
+ setMoves((current) => current + 1);
6730
+ };
6731
+ const reset = () => {
6732
+ setCells(makeReactor());
6733
+ setMoves(0);
6734
+ };
6735
+ const moveCursor = (dRow, dCol) => setCursor((c) => {
6736
+ const row = Math.min(2, Math.max(0, Math.floor(c / 3) + dRow));
6737
+ const col = Math.min(2, Math.max(0, c % 3 + dCol));
6738
+ return row * 3 + col;
6739
+ });
6740
+ const onKeyDown = (event) => {
6741
+ const key = event.key;
6742
+ if (key === "ArrowLeft") {
6743
+ event.preventDefault();
6744
+ moveCursor(0, -1);
6745
+ } else if (key === "ArrowRight") {
6746
+ event.preventDefault();
6747
+ moveCursor(0, 1);
6748
+ } else if (key === "ArrowUp") {
6749
+ event.preventDefault();
6750
+ moveCursor(-1, 0);
6751
+ } else if (key === "ArrowDown") {
6752
+ event.preventDefault();
6753
+ moveCursor(1, 0);
6754
+ } else if (key === "Enter" || key === " ") {
6755
+ event.preventDefault();
6756
+ toggle(cursor);
6757
+ } else if (key.toLowerCase() === "r") {
6758
+ event.preventDefault();
6759
+ reset();
6760
+ }
6761
+ };
6762
+ useGameControls(gridRef, props.paused, onKeyDown);
6763
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "dgame-game", "aria-label": "\u53CD\u5E94\u5806\u70B9\u4EAE\u5C0F\u6E38\u620F", children: [
6764
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-game-head", children: [
6765
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
6766
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { children: "\u53CD\u5E94\u5806\u70B9\u4EAE" }),
6767
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: "\u70B9\u4E00\u4E0B\u4F1A\u5207\u6362\u76F8\u90BB\u6A21\u5757\uFF0C\u628A\u4E5D\u4E2A\u6A21\u5757\u5168\u90E8\u70B9\u4EAE\uFF1B\u952E\u76D8\uFF1A\u65B9\u5411\u952E\u9009\u683C\u3001Enter/\u7A7A\u683C \u70B9\u4EAE\u3001R \u91CD\u7F6E\u3002" })
6768
+ ] }),
6769
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-score", children: [
6770
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
6771
+ "\u64CD\u4F5C ",
6772
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: moves })
6773
+ ] }),
6774
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", onClick: reset, children: "\u91CD\u7F6E" })
6775
+ ] })
6776
+ ] }),
6777
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgame-reactor", ref: gridRef, tabIndex: 0, role: "group", "aria-label": "\u4E5D\u5BAB\u683C\u53CD\u5E94\u5806", onKeyDown, onClick: () => focusStage(gridRef), children: cells.map((on, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", tabIndex: -1, className: "dgame-reactor-cell" + (on ? " on" : "") + (cursor === index ? " focus" : ""), "aria-current": cursor === index ? "true" : void 0, "aria-label": (on ? "\u5DF2\u70B9\u4EAE" : "\u672A\u70B9\u4EAE") + "\u6A21\u5757 " + (index + 1), onClick: () => {
6778
+ toggle(index);
6779
+ setCursor(index);
6780
+ focusStage(gridRef);
6781
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}) }, index)) }),
6782
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgame-reactor-status" + (solved ? " solved" : ""), children: solved ? "\u53CD\u5E94\u5806\u7A33\u5B9A\u8FD0\u884C \xB7 \u505A\u5F97\u6F02\u4EAE\uFF08R \u91CD\u65B0\u5F00\u59CB\uFF09" : "\u8BA9\u6240\u6709\u6A21\u5757\u53D1\u5149\uFF0C\u7ED9\u4EFB\u52A1\u4E00\u70B9\u80FD\u91CF" })
6783
+ ] });
6784
+ }
6785
+ function DashCover() {
6786
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgcov-prev dgcov-prev-dash", "aria-hidden": "true", children: [
6787
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
6788
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
6789
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
6790
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", {})
6791
+ ] });
6792
+ }
6793
+ function ReactorCover() {
6794
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dgcov-prev dgcov-prev-reactor", "aria-hidden": "true", children: Array.from({ length: 9 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}, i)) });
6795
+ }
6796
+ var GAMES = [
6797
+ {
6798
+ id: "dash",
6799
+ name: "\u661F\u9645\u8EB2\u907F",
6800
+ cat: "casual",
6801
+ accent: "#38bdf8",
6802
+ desc: "\u5DE6\u53F3\u79FB\u52A8\u8865\u7ED9\u8247\uFF0C\u907F\u5F00\u5760\u843D\u7684\u9668\u77F3\uFF1B\u62A4\u76FE\u8017\u5C3D\u524D\u80FD\u62FF\u591A\u5C11\u5206\uFF1F",
6803
+ tip: "\u2190/\u2192 \u6216 A\u3001D \u79FB\u52A8 \xB7 \u7A7A\u683C\u91CD\u5F00",
6804
+ Game: DashGame,
6805
+ Preview: DashCover
6806
+ },
6807
+ {
6808
+ id: "reactor",
6809
+ name: "\u53CD\u5E94\u5806\u70B9\u4EAE",
6810
+ cat: "casual",
6811
+ accent: "#a78bfa",
6812
+ desc: "\u70B9\u4E00\u4E0B\u4F1A\u7FFB\u8F6C\u76F8\u90BB\u6A21\u5757\uFF0C\u628A\u4E5D\u4E2A\u6A21\u5757\u5168\u90E8\u70B9\u4EAE\uFF0C\u8BA9\u53CD\u5E94\u5806\u7A33\u5B9A\u8FD0\u884C\uFF1B\u952E\u76D8\u5168\u7A0B\u53EF\u73A9\u3002",
6813
+ tip: "\u65B9\u5411\u952E\u9009\u683C \xB7 Enter/\u7A7A\u683C \u70B9\u4EAE \xB7 R \u91CD\u7F6E",
6814
+ Game: ReactorGame,
6815
+ Preview: ReactorCover
6816
+ },
6817
+ {
6818
+ id: "gomoku",
6819
+ name: "\u4E94\u5B50\u68CB",
6820
+ cat: "board",
6821
+ accent: "#38bdf8",
6822
+ desc: "\u4F60\u6267\u9ED1\u5148\u624B\u3001AI \u6267\u767D\uFF1B\u5148\u5728\u6A2A\u7AD6\u659C\u4EFB\u4E00\u65B9\u5411\u8FDE\u6210\u4E94\u5B50\u8005\u80DC\u3002",
6823
+ tip: "\u70B9\u51FB\u7A7A\u4F4D\u843D\u5B50 \xB7 \u64A4\u9500 / \u91CD\u5F00",
6824
+ Game: gomokuGame.Game,
6825
+ Preview: gomokuGame.Preview
6826
+ },
6827
+ {
6828
+ id: "xiangqi",
6829
+ name: "\u4E2D\u56FD\u8C61\u68CB",
6830
+ cat: "board",
6831
+ accent: "#f87171",
6832
+ desc: "\u4F60\u6267\u7EA2\u5148\u884C\u3001AI \u6267\u9ED1\uFF1B\u5403\u5149\u5BF9\u65B9\u5C06/\u5E05\u5373\u80DC\uFF0C\u522B\u9001\u738B\u88AB\u5C06\u3002",
6833
+ tip: "\u9009\u4E2D\u7EA2\u5B50\u518D\u70B9\u843D\u70B9 \xB7 R \u91CD\u5F00 \xB7 U \u64A4\u9500",
6834
+ Game: xiangqiGame.Game,
6835
+ Preview: xiangqiGame.Preview
6836
+ },
6837
+ {
6838
+ id: "tetris",
6839
+ name: "\u4FC4\u7F57\u65AF\u65B9\u5757",
6840
+ cat: "classic",
6841
+ accent: "#22d3ee",
6842
+ desc: "\u7ECF\u5178\u673A\u53F0\u7B2C\u4E00\u6B3E\uFF1A\u62FC\u6EE1\u4E00\u884C\u6D88\u9664\u5F97\u5206\uFF0C\u901F\u5EA6\u968F\u7B49\u7EA7\u52A0\u5FEB\u3002",
6843
+ tip: "\u2190/\u2192 \u79FB\u52A8 \xB7 \u2193 \u4E0B\u843D \xB7 \u2191/X \u65CB\u8F6C \xB7 \u7A7A\u683C \u76F4\u843D \xB7 P \u6682\u505C",
6844
+ Game: tetrisGame.Game,
6845
+ Preview: tetrisGame.Preview
6846
+ },
6847
+ {
6848
+ id: "sokoban",
6849
+ name: "\u63A8\u7BB1\u5B50",
6850
+ cat: "classic",
6851
+ accent: "#facc15",
6852
+ desc: "\u628A\u7BB1\u5B50\u5168\u90E8\u63A8\u5230\u76EE\u6807\u70B9\u4E0A\u901A\u5173\uFF1B\u516D\u5173\u96BE\u5EA6\u9012\u589E\u3002",
6853
+ tip: "\u65B9\u5411\u952E\u79FB\u52A8 \xB7 U \u64A4\u9500 \xB7 R \u91CD\u7F6E \xB7 \u6570\u5B57\u5207\u5173",
6854
+ Game: sokobanGame.Game,
6855
+ Preview: sokobanGame.Preview
6856
+ },
6857
+ {
6858
+ id: "snake",
6859
+ name: "\u8D2A\u5403\u86C7",
6860
+ cat: "classic",
6861
+ accent: "#4ade80",
6862
+ desc: "\u5403\u5230\u98DF\u7269\u53D8\u957F\u5E76\u52A0\u901F\uFF1B\u649E\u5899\u6216\u54AC\u5230\u81EA\u5DF1\u5C31\u7ED3\u675F\u3002",
6863
+ tip: "\u65B9\u5411\u952E / WASD \u79FB\u52A8 \xB7 P \u6682\u505C \xB7 R \u91CD\u5F00",
6864
+ Game: snakeGame.Game,
6865
+ Preview: snakeGame.Preview
6866
+ },
6867
+ {
6868
+ id: "breakout",
6869
+ name: "\u6253\u7816\u5757",
6870
+ cat: "classic",
6871
+ accent: "#38bdf8",
6872
+ desc: "\u79FB\u52A8\u6321\u677F\u53CD\u5F39\u5C0F\u7403\uFF0C\u51FB\u788E\u5168\u90E8\u7816\u5757\uFF1B\u7403\u6389\u5230\u5E95\u90E8\u4E22\u4E00\u6761\u547D\u3002",
6873
+ tip: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 \u7A7A\u683C\u53D1\u7403",
6874
+ Game: breakoutGame.Game,
6875
+ Preview: breakoutGame.Preview
6876
+ },
6877
+ {
6878
+ id: "racer",
6879
+ name: "\u6781\u901F\u8D5B\u8F66",
6880
+ cat: "classic",
6881
+ accent: "#fb923c",
6882
+ desc: "\u907F\u5F00\u8FCE\u9762\u6765\u8F66\u3001\u6536\u96C6\u91D1\u5E01\uFF0C\u8D8A\u5F00\u8D8A\u5FEB\uFF1B\u649E\u8F66\u5373\u7ED3\u675F\u3002",
6883
+ tip: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 R \u91CD\u5F00",
6884
+ Game: racerGame.Game,
6885
+ Preview: racerGame.Preview
6886
+ },
6887
+ {
6888
+ id: "tank",
6889
+ name: "\u5766\u514B\u5927\u6218",
6890
+ cat: "classic",
6891
+ accent: "#fde047",
6892
+ desc: "\u51FB\u6BC1\u654C\u65B9\u5766\u514B\u3001\u5B88\u4F4F\u4E2D\u592E\u57FA\u5730\uFF1B\u6E05\u7A7A\u4E00\u6CE2\u8FDB\u5165\u4E0B\u4E00\u6CE2\u3002",
6893
+ tip: "\u65B9\u5411\u952E\u79FB\u52A8 \xB7 \u7A7A\u683C\u5F00\u70AE \xB7 R \u91CD\u5F00",
6894
+ Game: tankGame.Game,
6895
+ Preview: tankGame.Preview
6896
+ }
6897
+ ];
6898
+ var gamesBus = {
6899
+ state: { open: false, game: null, picker: false },
6900
+ listeners: /* @__PURE__ */ new Set(),
6901
+ subscribe(fn) {
6902
+ this.listeners.add(fn);
6903
+ return () => {
6904
+ this.listeners.delete(fn);
6905
+ };
6906
+ },
6907
+ emit() {
6908
+ for (const fn of this.listeners) fn();
6909
+ },
6910
+ launch(id) {
6911
+ if (!GAMES.some((g) => g.id === id)) return;
6912
+ this.state = { open: true, game: id, picker: false };
6913
+ this.emit();
6914
+ },
6915
+ // 快捷入口点击:上次玩过的直接续玩,否则先展开选择层。
6916
+ entry() {
6917
+ this.state = { open: true, game: this.state.game, picker: this.state.game ? false : true };
6918
+ this.emit();
6919
+ },
6920
+ togglePicker() {
6921
+ this.state = Object.assign({}, this.state, { picker: !this.state.picker });
6922
+ this.emit();
6923
+ },
6924
+ closePicker() {
6925
+ this.state = Object.assign({}, this.state, { picker: false });
6926
+ this.emit();
6927
+ },
6928
+ close() {
6929
+ this.state = { open: false, game: this.state.game, picker: false };
6930
+ this.emit();
6931
+ }
6932
+ };
6933
+ function useGameWin() {
6934
+ const [state, setState] = (0, import_react18.useState)(gamesBus.state);
6935
+ (0, import_react18.useEffect)(() => gamesBus.subscribe(() => setState(gamesBus.state)), []);
6936
+ return state;
6937
+ }
6938
+ var CATS = [
6939
+ { id: "board", label: "\u68CB\u76D8\u5BF9\u6218", desc: "\u4F60\u4E0E AI \u5BF9\u5F08" },
6940
+ { id: "classic", label: "\u7ECF\u5178\u673A\u53F0", desc: "\u4FC4\u7F57\u65AF\u65B9\u5757\u6E38\u620F\u673A\u91CC\u7684\u6000\u65E7\u6E38\u620F" },
6941
+ { id: "casual", label: "\u4F11\u95F2\u76CA\u667A", desc: "\u7B49\u4EFB\u52A1\u65F6\u987A\u624B\u6765\u4E00\u628A" }
6942
+ ];
6943
+ function GameCoverGrid(props) {
6944
+ const currentId = props && props.currentId;
6945
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgcov-groups", children: CATS.map((cat) => {
6946
+ const list = GAMES.filter((g) => (g.cat || "casual") === cat.id);
6947
+ if (!list.length) return null;
6948
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgcov-section", children: [
6949
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgcov-sechead", children: [
6950
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: cat.label }),
6951
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: cat.desc }),
6952
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("em", { children: [
6953
+ list.length,
6954
+ " \u6B3E"
6955
+ ] })
6956
+ ] }),
6957
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgcov-grid", role: "list", children: list.map((g) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
6958
+ "button",
6959
+ {
6960
+ type: "button",
6961
+ role: "listitem",
6962
+ className: "dgcov" + (currentId === g.id ? " on" : ""),
6963
+ onClick: () => props.onLaunch(g.id),
6964
+ children: [
6965
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(g.Preview, {}),
6966
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgcov-name", children: [
6967
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", { style: { background: g.accent } }),
6968
+ g.name,
6969
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("em", { children: currentId === g.id ? "\u6B63\u5728\u73A9" : "\u5F00\u73A9" })
6970
+ ] }),
6971
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dgcov-desc", children: g.desc })
6972
+ ]
6973
+ },
6974
+ g.id
6975
+ )) })
6976
+ ] }, cat.id);
6977
+ }) });
6978
+ }
6979
+ function TaskPill({ status }) {
6980
+ if (status.loading) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dgwin-task", children: "\u6B63\u5728\u8BFB\u53D6\u4EFB\u52A1\u72B6\u6001\u2026" });
6981
+ const task = status.active[0];
6982
+ if (!task) return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgwin-task idle", children: [
6983
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
6984
+ "\u4EFB\u52A1\u7A7A\u95F2 \xB7 \u6162\u6162\u73A9"
6985
+ ] });
6986
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgwin-task live", children: [
6987
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
6988
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("b", { children: [
6989
+ status.active.length,
6990
+ " \u4E2A\u4EFB\u52A1"
6991
+ ] }),
6992
+ phaseLabel2(task.phase),
6993
+ " \xB7 ",
6994
+ elapsedText(task.elapsed)
6995
+ ] });
6996
+ }
6997
+ function readFabSide() {
6998
+ try {
6999
+ const p = JSON.parse(localStorage.getItem(FAB_POS_KEY) || "null");
7000
+ return p && (p.side === "left" || p.side === "right") ? p.side : "left";
7001
+ } catch {
7002
+ return "left";
7003
+ }
7004
+ }
7005
+ function defaultWinGeom() {
7006
+ const w = Math.min(470, window.innerWidth - 16);
7007
+ const h = Math.min(600, window.innerHeight - 16);
7008
+ const x = readFabSide() === "right" ? Math.max(10, window.innerWidth - w - 14) : Math.min(Math.max(10, sidebarRightEdge() + 10), Math.max(10, window.innerWidth - w - 14));
7009
+ return { mode: "normal", x, y: Math.max(10, Math.round((window.innerHeight - h) / 2) - 10), w, h };
7010
+ }
7011
+ function restoreWinGeom() {
7012
+ try {
7013
+ const g = JSON.parse(localStorage.getItem(WIN_GEOM_KEY) || "null");
7014
+ if (g && [g.x, g.y, g.w, g.h].every((v) => typeof v === "number")) {
7015
+ const w = Math.max(WIN_MIN_W, Math.min(g.w, window.innerWidth - 16));
7016
+ const h = Math.max(WIN_MIN_H, Math.min(g.h, window.innerHeight - 16));
7017
+ return {
7018
+ mode: "normal",
7019
+ x: Math.min(Math.max(8, g.x), Math.max(8, window.innerWidth - w - 8)),
7020
+ y: Math.min(Math.max(8, g.y), Math.max(8, window.innerHeight - h - 8)),
7021
+ w,
7022
+ h
7023
+ };
7024
+ }
7025
+ } catch {
7026
+ }
7027
+ return defaultWinGeom();
7028
+ }
7029
+ function findSidebarCol() {
7030
+ if (typeof document === "undefined") return null;
7031
+ const overlay = document.querySelector("[data-shell-overlay]");
7032
+ const frame = overlay && overlay.parentElement;
7033
+ if (!frame) return null;
7034
+ return frame.children[0] || null;
7035
+ }
7036
+ function sidebarRightEdge() {
7037
+ const col = findSidebarCol();
7038
+ if (col) {
7039
+ const rect2 = col.getBoundingClientRect();
7040
+ if (rect2 && typeof rect2.right === "number") return rect2.right;
7041
+ }
7042
+ return 280;
7043
+ }
7044
+ function snapFabX(side) {
7045
+ if (typeof window === "undefined") return 0;
7046
+ return side === "right" ? Math.max(0, window.innerWidth - FAB_W) : sidebarRightEdge();
7047
+ }
7048
+ function GameWindow(props) {
7049
+ const game = props.game;
7050
+ const status = useTaskPulse();
7051
+ const [win, setWin] = (0, import_react18.useState)(() => typeof window === "undefined" ? { mode: "normal", x: 14, y: 14, w: 470, h: 600 } : restoreWinGeom());
7052
+ const winRef = (0, import_react18.useRef)(win);
7053
+ winRef.current = win;
7054
+ const pickerRef = (0, import_react18.useRef)(props.picker);
7055
+ pickerRef.current = props.picker;
7056
+ (0, import_react18.useEffect)(() => {
7057
+ const onKey = (e) => {
7058
+ if (e.key !== "Escape") return;
7059
+ if (pickerRef.current) gamesBus.closePicker();
7060
+ else gamesBus.close();
7061
+ };
7062
+ window.addEventListener("keydown", onKey);
7063
+ return () => window.removeEventListener("keydown", onKey);
7064
+ }, []);
7065
+ function persistGeom(s) {
7066
+ try {
7067
+ localStorage.setItem(WIN_GEOM_KEY, JSON.stringify({ x: s.x, y: s.y, w: s.w, h: s.h }));
7068
+ } catch {
7069
+ }
7070
+ }
7071
+ function beginDrag(e, kind) {
7072
+ if (e.button !== 0 || typeof window === "undefined") return;
7073
+ if (kind === "move" && (winRef.current.mode === "max" || e.target && e.target.closest && e.target.closest("button"))) return;
7074
+ const startX = e.clientX, startY = e.clientY;
7075
+ const origin = Object.assign({}, winRef.current);
7076
+ const onMove = (ev) => {
7077
+ const dx = ev.clientX - startX, dy = ev.clientY - startY;
7078
+ if (kind === "move") setWin((s) => Object.assign({}, s, {
7079
+ x: Math.min(Math.max(8, origin.x + dx), Math.max(8, window.innerWidth - origin.w - 8)),
7080
+ y: Math.min(Math.max(8, origin.y + dy), Math.max(8, window.innerHeight - origin.h - 8))
7081
+ }));
7082
+ else setWin((s) => Object.assign({}, s, {
7083
+ // 窗口高度随内容自适应,缩放只调宽度;游戏板按比例跟随缩放。
7084
+ w: Math.max(WIN_MIN_W, Math.min(origin.w + dx, window.innerWidth - 16))
7085
+ }));
7086
+ };
7087
+ const onUp = () => {
7088
+ window.removeEventListener("pointermove", onMove);
7089
+ window.removeEventListener("pointerup", onUp);
7090
+ setWin((s) => {
7091
+ persistGeom(s);
7092
+ return s;
7093
+ });
7094
+ };
7095
+ window.addEventListener("pointermove", onMove);
7096
+ window.addEventListener("pointerup", onUp);
7097
+ }
7098
+ const Game = game && game.Game;
7099
+ const style = win.mode === "max" ? { left: 10, top: 10, right: 10, bottom: 10, width: "auto", height: "auto" } : { left: win.x, top: win.y, width: win.w, height: props.picker ? "min(76vh, 640px)" : "auto" };
7100
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgwin dgwin-" + win.mode, role: "dialog", "aria-label": "\u6E38\u620F\u7A97\u53E3\uFF1A" + (game ? game.name : "\u8DA3\u5473\u6E38\u620F"), style, children: [
7101
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7102
+ "header",
7103
+ {
7104
+ className: "dgwin-bar",
7105
+ onPointerDown: (e) => beginDrag(e, "move"),
7106
+ onDoubleClick: () => setWin((s) => Object.assign({}, s, { mode: s.mode === "max" ? "normal" : "max" })),
7107
+ children: [
7108
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dgwin-ico", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(GamepadIcon, { size: 15 }) }),
7109
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgwin-title", children: [
7110
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: game ? game.name : "\u8DA3\u5473\u6E38\u620F" }),
7111
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: game ? game.desc : "\u9009\u4E00\u6B3E\u5C0F\u6E38\u620F\uFF0C\u8FB9\u7B49\u8FB9\u73A9" })
7112
+ ] }),
7113
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TaskPill, { status }),
7114
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgwin-ctrls", children: [
7115
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "dgwin-btn", title: props.picker ? "\u6536\u8D77\u9009\u62E9\u5C42\uFF0C\u56DE\u5230\u6E38\u620F" : "\u5C55\u5F00\u9009\u62E9\u5C42\uFF0C\u6362\u4E00\u6B3E\u6E38\u620F", onClick: () => gamesBus.togglePicker(), children: props.picker ? "\u8FD4\u56DE" : "\u6362\u6E38\u620F" }),
7116
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "dgwin-sq", title: win.mode === "min" ? "\u8FD8\u539F" : "\u6700\u5C0F\u5316", "aria-label": win.mode === "min" ? "\u8FD8\u539F\u7A97\u53E3" : "\u6700\u5C0F\u5316\u7A97\u53E3", onClick: () => setWin((s) => Object.assign({}, s, { mode: s.mode === "min" ? "normal" : "min" })), children: "\u2581" }),
7117
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "dgwin-sq", title: win.mode === "max" ? "\u8FD8\u539F" : "\u6700\u5927\u5316", "aria-label": win.mode === "max" ? "\u8FD8\u539F\u7A97\u53E3" : "\u6700\u5927\u5316\u7A97\u53E3", onClick: () => setWin((s) => Object.assign({}, s, { mode: s.mode === "max" ? "normal" : "max" })), children: win.mode === "max" ? "\u2750" : "\u25A2" }),
7118
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "dgwin-sq", title: "\u5173\u95ED\uFF08Esc\uFF09", "aria-label": "\u5173\u95ED\u6E38\u620F\u7A97\u53E3", onClick: () => gamesBus.close(), children: "\u2715" })
7119
+ ] })
7120
+ ]
7121
+ }
7122
+ ),
7123
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgwin-body", children: [
7124
+ Game ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Game, { paused: props.picker || win.mode === "min" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgwin-empty", children: "\u4ECE\u300C\u6362\u6E38\u620F\u300D\u91CC\u6311\u4E00\u6B3E\u5F00\u59CB\u73A9\u3002" }),
7125
+ game ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("footer", { className: "dgwin-tip", children: [
7126
+ game.tip,
7127
+ " \xB7 Esc \u5173\u95ED\u7A97\u53E3"
7128
+ ] }) : null,
7129
+ props.picker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgwin-picker", role: "dialog", "aria-label": "\u9009\u62E9\u5C0F\u6E38\u620F", children: [
7130
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgwin-picker-head", children: [
7131
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7132
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: "\u9009\u62E9\u5C0F\u6E38\u620F" }),
7133
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: "\u70B9\u51FB\u5C01\u9762\u5F00\u73A9\uFF1B\u7A97\u53E3\u4F4D\u7F6E\u548C\u5927\u5C0F\u968F\u4F60\u8C03" })
7134
+ ] }),
7135
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "dgwin-picker-close", "aria-label": "\u5173\u95ED\u9009\u62E9\u5C42", title: "\u5173\u95ED", onClick: () => gamesBus.closePicker(), children: "\u2715" })
7136
+ ] }),
7137
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(GameCoverGrid, { currentId: game && game.id, onLaunch: (id) => gamesBus.launch(id) })
7138
+ ] }) : null
7139
+ ] }),
7140
+ win.mode === "normal" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgwin-resize", title: "\u62D6\u52A8\u8C03\u6574\u7A97\u53E3\u5927\u5C0F", "aria-hidden": "true", onPointerDown: (e) => beginDrag(e, "size") }) : null
7141
+ ] });
7142
+ }
7143
+ function GameFab(props) {
7144
+ const [pos, setPos] = (0, import_react18.useState)(null);
7145
+ const [dragging, setDragging] = (0, import_react18.useState)(false);
7146
+ const dragRef = (0, import_react18.useRef)(null);
7147
+ const clickBlockRef = (0, import_react18.useRef)(false);
7148
+ const sideRef = (0, import_react18.useRef)("left");
7149
+ (0, import_react18.useEffect)(() => {
7150
+ if (typeof window === "undefined") return;
7151
+ let next = { side: "left", y: Math.round(window.innerHeight * 0.34) };
7152
+ try {
7153
+ const p = JSON.parse(localStorage.getItem(FAB_POS_KEY) || "null");
7154
+ if (p && (p.side === "left" || p.side === "right") && typeof p.y === "number") next = { side: p.side, y: p.y };
7155
+ } catch {
7156
+ }
7157
+ sideRef.current = next.side;
7158
+ setPos({
7159
+ side: next.side,
7160
+ x: snapFabX(next.side),
7161
+ y: Math.min(Math.max(8, next.y), Math.max(8, window.innerHeight - FAB_H - 8))
7162
+ });
7163
+ }, []);
7164
+ (0, import_react18.useEffect)(() => {
7165
+ if (typeof window === "undefined") return;
7166
+ const update = () => {
7167
+ if (dragRef.current) return;
7168
+ setPos((cur) => cur ? Object.assign({}, cur, { x: snapFabX(cur.side) }) : cur);
7169
+ };
7170
+ const resizeTarget = findSidebarCol();
7171
+ let ro = null;
7172
+ if (typeof ResizeObserver !== "undefined" && resizeTarget) {
7173
+ ro = new ResizeObserver(update);
7174
+ ro.observe(resizeTarget);
7175
+ }
7176
+ window.addEventListener("resize", update);
7177
+ return () => {
7178
+ window.removeEventListener("resize", update);
7179
+ if (ro) ro.disconnect();
7180
+ };
7181
+ }, []);
7182
+ const onPointerDown = (e) => {
7183
+ if (e.button !== 0) return;
7184
+ const r = e.currentTarget.getBoundingClientRect();
7185
+ dragRef.current = { sx: e.clientX, sy: e.clientY, ox: r.left, oy: r.top, w: r.width || FAB_W, h: r.height || FAB_H, moved: false };
7186
+ try {
7187
+ e.currentTarget.setPointerCapture(e.pointerId);
7188
+ } catch {
7189
+ }
7190
+ };
7191
+ const onPointerMove = (e) => {
7192
+ const d = dragRef.current;
7193
+ if (!d || typeof window === "undefined") return;
7194
+ const dx = e.clientX - d.sx, dy = e.clientY - d.sy;
7195
+ if (!d.moved && Math.hypot(dx, dy) < 4) return;
7196
+ d.moved = true;
7197
+ setDragging(true);
7198
+ const side = d.ox + dx + d.w / 2 < window.innerWidth / 2 ? "left" : "right";
7199
+ sideRef.current = side;
7200
+ setPos({
7201
+ side,
7202
+ x: Math.min(Math.max(0, d.ox + dx), Math.max(0, window.innerWidth - d.w)),
7203
+ y: Math.min(Math.max(8, d.oy + dy), Math.max(8, window.innerHeight - d.h - 8))
7204
+ });
7205
+ };
7206
+ const onPointerUp = (e) => {
7207
+ const d = dragRef.current;
7208
+ dragRef.current = null;
7209
+ setDragging(false);
7210
+ if (!d || !d.moved) return;
7211
+ clickBlockRef.current = true;
7212
+ setTimeout(() => {
7213
+ clickBlockRef.current = false;
7214
+ }, 0);
7215
+ const dx = e.clientX - d.sx, dy = e.clientY - d.sy;
7216
+ const side = d.ox + dx + d.w / 2 < window.innerWidth / 2 ? "left" : "right";
7217
+ sideRef.current = side;
7218
+ const x = snapFabX(side);
7219
+ const y = Math.min(Math.max(8, d.oy + dy), Math.max(8, window.innerHeight - d.h - 8));
7220
+ setPos({ side, x, y });
7221
+ try {
7222
+ localStorage.setItem(FAB_POS_KEY, JSON.stringify({ side, y }));
7223
+ } catch {
7224
+ }
7225
+ };
7226
+ if (!pos) return null;
7227
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7228
+ "button",
7229
+ {
7230
+ type: "button",
7231
+ className: "dgfab dgfab-side-" + pos.side + (dragging ? " dgfab-drag" : "") + (props.hidden ? " dgfab-hide" : ""),
7232
+ style: { left: pos.x + "px", top: pos.y + "px" },
7233
+ title: "\u8DA3\u5473\u6E38\u620F \xB7 \u70B9\u51FB\u5F00\u73A9\uFF1B\u6309\u4F4F\u53EF\u62D6\u5230\u4FA7\u680F\u53F3\u7F18\u6216\u5C4F\u5E55\u53F3\u4FA7\u3001\u4E0A\u4E0B\u632A\u4F4D",
7234
+ "aria-label": "\u8DA3\u5473\u6E38\u620F\u5FEB\u6377\u5165\u53E3\uFF0C\u53EF\u62D6\u52A8\u6362\u8FB9\u4E0E\u4E0A\u4E0B\u632A\u4F4D",
7235
+ onPointerDown,
7236
+ onPointerMove,
7237
+ onPointerUp,
7238
+ onClick: () => {
7239
+ if (!clickBlockRef.current) gamesBus.entry();
7240
+ },
7241
+ children: [
7242
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dgfab-ico", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(GamepadIcon, { size: 16 }) }),
7243
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dgfab-label", children: "\u8DA3\u5473\u6E38\u620F" })
7244
+ ]
7245
+ }
7246
+ );
7247
+ }
7248
+ function GamepadIcon(props) {
7249
+ const size = props && props.size || 16;
7250
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.3", "aria-hidden": "true", children: [
7251
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "1.6", y: "4.6", width: "12.8", height: "6.8", rx: "3.2" }),
7252
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4.7 6.8v2.6M3.4 8.1h2.6" }),
7253
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "10.4", cy: "6.9", r: ".95", fill: "currentColor", stroke: "none" }),
7254
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "12.2", cy: "8.7", r: ".95", fill: "currentColor", stroke: "none" })
7255
+ ] });
7256
+ }
7257
+ function GamesOverlay() {
7258
+ const winState = useGameWin();
7259
+ const [panelOpen, setPanelOpen2] = (0, import_react18.useState)(panelNav.open);
7260
+ (0, import_react18.useEffect)(() => {
7261
+ setPanelOpen2(panelNav.open);
7262
+ return subscribePanel(() => setPanelOpen2(panelNav.open));
7263
+ }, []);
7264
+ const game = winState.game ? GAMES.find((g) => g.id === winState.game) : null;
7265
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
7266
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(GameFab, { hidden: winState.open || panelOpen }),
7267
+ winState.open ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(GameWindow, { game, picker: winState.picker }) : null
7268
+ ] });
7269
+ }
7270
+ function TaskStatus({ status }) {
7271
+ const task = status.active[0];
7272
+ if (status.loading) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgame-task-status loading", children: "\u6B63\u5728\u8BFB\u53D6\u4EFB\u52A1\u72B6\u6001\u2026" });
7273
+ if (!task) return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-task-status", children: [
7274
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
7275
+ " \u4EFB\u52A1\u6682\u65F6\u7A7A\u95F2 \xB7 \u6E38\u620F\u968F\u65F6\u53EF\u73A9"
7276
+ ] });
7277
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dgame-task-status live", children: [
7278
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
7279
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7280
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("strong", { children: [
7281
+ status.active.length,
7282
+ " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D \xB7 ",
7283
+ phaseLabel2(task.phase)
7284
+ ] }),
7285
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { title: task.title, children: [
7286
+ task.title || "\u5F53\u524D\u4EFB\u52A1",
7287
+ " \xB7 \u5DF2\u8FD0\u884C ",
7288
+ elapsedText(task.elapsed)
7289
+ ] })
7290
+ ] })
7291
+ ] });
7292
+ }
7293
+ function GamesView() {
7294
+ const status = useTaskPulse();
7295
+ const winState = useGameWin();
7296
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "dgame", children: [
7297
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("header", { className: "dgame-hero", children: [
7298
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7299
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgame-eyebrow", children: [
7300
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
7301
+ " \u7B49\u5F85\u4E5F\u53EF\u4EE5\u5F88\u597D\u73A9"
7302
+ ] }),
7303
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { children: "\u8DA3\u5473\u6E38\u620F" }),
7304
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: "\u4EFB\u52A1\u5728\u540E\u53F0\u7EE7\u7EED\u6267\u884C\uFF1B\u6E38\u620F\u5728\u5C40\u90E8\u6D6E\u52A8\u7A97\u53E3\u91CC\u8FD0\u884C\uFF0C\u4E0D\u94FA\u6EE1\u5168\u5C4F\u3001\u4E0D\u906E\u6321\u4F1A\u8BDD\u5185\u5BB9\uFF0C\u4E5F\u4E0D\u5F71\u54CD\u6587\u4EF6\u6216\u6B63\u5728\u8FD0\u884C\u7684\u667A\u80FD\u4F53\u3002" })
7305
+ ] }),
7306
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "dgame-planet", "aria-hidden": "true", children: [
7307
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("i", {}),
7308
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", {})
7309
+ ] })
7310
+ ] }),
7311
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TaskStatus, { status }),
7312
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dgame-launch-note", children: "\u70B9\u51FB\u5C01\u9762\u5728\u6D6E\u52A8\u7A97\u53E3\u5F00\u73A9\uFF1A\u7A97\u53E3\u53EF\u62D6\u52A8\u3001\u53EF\u8C03\u5927\u5C0F\u3001\u53EF\u6700\u5927\u5316\u6700\u5C0F\u5316\uFF1B\u5C4F\u5E55\u4FA7\u8FB9\u7684\u300C\u8DA3\u5473\u6E38\u620F\u300D\u78C1\u5438\u5165\u53E3\u8D34\u5728\u5DE6\u4FA7\u8FB9\u680F\u53F3\u7F18\uFF08\u4E0D\u6321\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF09\uFF0C\u53EF\u4E0A\u4E0B\u632A\u4F4D\u3001\u5DE6\u53F3\u6362\u8FB9\uFF0C\u4F4D\u7F6E\u90FD\u4F1A\u8BB0\u4F4F\uFF1B\u5341\u6B3E\u5C0F\u6E38\u620F\u90FD\u652F\u6301\u952E\u76D8\uFF08\u80FD\u914D\u6309\u94AE\u7684\u4E5F\u914D\u4E86\u6309\u94AE\uFF09\u64CD\u4F5C\u3002" }),
7313
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(GameCoverGrid, { currentId: winState.open ? winState.game : null, onLaunch: (id) => {
7314
+ gamesBus.launch(id);
7315
+ setPanelOpen(false);
7316
+ } }),
7317
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("footer", { className: "dgame-foot", children: "\u6E38\u620F\u4E2D\u6309 Esc \u5173\u95ED\u7A97\u53E3\uFF1B\u6E38\u620F\u4E0D\u53D1\u9001\u4EFB\u4F55\u6D88\u606F\uFF0C\u4E5F\u4E0D\u6539\u52A8\u5DE5\u4F5C\u533A\u3002\u65B9\u5411\u952E / WASD\u3001Enter\u3001\u7A7A\u683C\u3001R\u3001U\u3001P \u7B49\u952E\u968F\u6E38\u620F\u53EF\u7528\uFF1B\u4E94\u5B50\u68CB\u3001\u8C61\u68CB\u652F\u6301\u64A4\u9500/\u91CD\u5F00\u3002" })
7318
+ ] });
7319
+ }
7320
+ function GamesHomeStat() {
7321
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: "\u5341\u6B3E\u6D6E\u52A8\u7A97\u53E3\u5C0F\u6E38\u620F \xB7 \u4E94\u5B50\u68CB/\u8C61\u68CB/\u4FC4\u7F57\u65AF\u65B9\u5757/\u63A8\u7BB1\u5B50\u2026\u4EFB\u52A1\u7B49\u5F85\u4E0D\u65E0\u804A" });
7322
+ }
7323
+ var baseCss = `
7324
+ .dgame{--dgame-accent:#a78bfa;--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:#a7f3d0;background:color-mix(in srgb,#34d399 13%,transparent)}.dgame-foot{padding:0 2px}.dgame-foot::before{content:'\u2726';margin-right:6px;color:var(--dgame-accent)}
7325
+ .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)}
7326
+ .dgcov-grid{--dgame-accent:#a78bfa;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))}
7327
+ .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:'\u25C6'}.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:'\u25B2'}@keyframes dgcov-fall{to{transform:translateY(140px)}}@keyframes dgcov-dodge{0%,100%{transform:translateX(-46px)}25%,75%{transform:translateX(0)}50%{transform:translateX(46px)}}
7328
+ .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}}
7329
+ .dgfab{--dgame-accent:#a78bfa;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)}
7330
+ .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)}}
7331
+ .dgwin-min{height:auto!important;border-radius:12px}.dgwin-min .dgwin-body{display:none}.dgwin-min .dgwin-resize{display:none}
7332
+ .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}
7333
+ .dgwin-bar{flex:none;display:flex;align-items:center;gap:9px;padding:10px 12px;border-bottom:1px solid rgb(255 255 255 / .09);background:rgb(7 11 28 / .72);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#cdd6ff;cursor:move;user-select:none;touch-action:none;position:relative;z-index:2}.dgwin-ico{display:inline-flex;color:#a78bfa}.dgwin-title{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}.dgwin-title b{font-size:13px}.dgwin-title span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:#8f98ce}.dgwin-task{display:inline-flex;align-items:center;gap:7px;padding:5px 11px;border-radius:999px;border:1px solid rgb(255 255 255 / .13);background:rgb(255 255 255 / .05);font-size:11px;color:#aab3e8;white-space:nowrap}.dgwin-task i{width:7px;height:7px;border-radius:50%;background:#6b7499;flex:none}.dgwin-task.live{color:#c4f5e4;border-color:color-mix(in srgb,#34d399 33%,transparent);background:color-mix(in srgb,#34d399 8%,transparent)}.dgwin-task.live i{background:#34d399;box-shadow:0 0 0 3px color-mix(in srgb,#34d399 14%,transparent);animation:dgame-pulse 1.8s ease-in-out infinite}.dgwin-ctrls{display:flex;align-items:center;gap:6px;flex:none}.dgwin-btn{cursor:pointer;border:1px solid rgb(255 255 255 / .17);background:rgb(255 255 255 / .07);color:#e6ebff;border-radius:8px;padding:5px 10px;font:inherit;font-size:11px;transition:background .15s ease,border-color .15s ease}.dgwin-btn:hover{background:rgb(255 255 255 / .13);border-color:rgb(255 255 255 / .28)}.dgwin-sq{cursor:pointer;border:1px solid transparent;background:transparent;color:#a9b2e2;border-radius:8px;width:28px;height:26px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:background .15s ease,color .15s ease}.dgwin-sq:hover{background:rgb(255 255 255 / .12);color:#fff}
7334
+ /* \u5C0F\u7A97\u5185\u5BB9\u81EA\u9002\u5E94\uFF1A\u9AD8\u5EA6\u968F\u5185\u5BB9\uFF08\u4E0D\u88C1\u5207\u3001\u4E0D\u6EDA\u52A8\uFF09\uFF0C\u6E38\u620F\u677F\u6309\u5C4F\u5E55\u53EF\u7528\u9AD8\u5EA6\u7B49\u6BD4\u7F29\u653E */
7335
+ .dgwin-normal{max-height:calc(100vh - 12px)}
7336
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-snake-stage,.dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-tank-stage{width:min(100%,430px,calc(100vh - 240px))}
7337
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-gomoku-board{width:min(100%,390px,calc(100vh - 245px))}
7338
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-xiangqi-board{width:min(100%,370px,calc((100vh - 240px) * .9))}
7339
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-breakout{width:min(100%,410px,calc((100vh - 235px) * .89))}
7340
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-tetris-board{width:min(100%,215px,calc((100vh - 270px) / 2))}
7341
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-dash-stage{width:min(100%,430px,calc((100vh - 215px) * 1.24))}
7342
+ .dgwin:not(.dgwin-max):not(.dgwin-min) .dgame-racer{width:min(100%,400px,calc((100vh - 235px) * .71))}
7343
+ .dgwin-body{flex:1;min-height:0;position:relative;z-index:1;overflow:hidden;display:flex;flex-direction:column;gap:10px;padding:13px}.dgwin-body .dgame-game{border-color:transparent;background:transparent;padding:0}.dgwin-empty{display:grid;place-items:center;min-height:220px;color:#8f98ce;font-size:12px}.dgwin-tip{flex:none;text-align:center;color:#8b93c9;font-size:11px;padding:0 4px 2px}
7344
+ .dgwin-picker{position:absolute;inset:0;z-index:4;display:flex;flex-direction:column;gap:10px;padding:13px;background:rgb(10 14 32 / .86);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);overflow-y:auto;animation:dgentry-in .15s ease-out}.dgwin-picker-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.dgwin-picker-head>div{display:flex;flex-direction:column;gap:3px}.dgwin-picker-head b{font-size:14px}.dgwin-picker-head span{font-size:11px;color:#aab3e8}.dgwin-picker-close{cursor:pointer;border:none;background:transparent;color:#7d86c0;border-radius:8px;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;font-size:14px;flex:none}.dgwin-picker-close:hover{background:rgb(255 255 255 / .08);color:#e7ecff}.dgwin-picker .dgcov-grid{flex-direction:column;--dsw-alias-label-primary:#e7ecff;--dsw-alias-label-secondary:#aab3e8;--dsw-alias-border-l1:rgb(255 255 255 / .12)}.dgwin-picker .dgcov{min-width:0;background:rgb(255 255 255 / .04)}
7345
+ .dgwin-resize{position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:nwse-resize;z-index:3;touch-action:none}.dgwin-resize::after{content:'';position:absolute;right:4px;bottom:4px;width:8px;height:8px;border-right:2px solid rgb(255 255 255 / .3);border-bottom:2px solid rgb(255 255 255 / .3);border-radius:0 0 3px 0}
7346
+ @keyframes dgentry-in{from{opacity:0}}
7347
+ @media (max-width:680px){.dgame{gap:12px;font-size:14px}.dgame-hero{padding:15px}.dgame-hero h2{font-size:20px}.dgame-planet{width:58px;height:58px}.dgame-planet::after{left:-12px;top:25px;width:80px;height:20px}.dgame-game{padding:12px}.dgame-dash-grid{grid-template-rows:repeat(5,48px)}.dgame-controls{grid-template-columns:1fr 1fr}.dgame-controls span{grid-column:1 / -1;grid-row:2}.dgame-reactor{gap:8px}.dgcov-grid{flex-direction:column}.dgcov{min-width:0}.dgfab{width:32px}.dgwin-title span,.dgwin-task{display:none}.dgwin-bar{gap:7px;padding:8px 10px}.dgwin-btn{padding:6px 8px}.dgwin-body{padding:10px}}
7348
+ @media (prefers-reduced-motion:reduce){.dgame *,.dgcov *,.dgfab,.dgwin,.dgwin-picker *{animation:none!important;transition:none!important}}
7349
+ `;
7350
+ var dgcovGroupCss = `
7351
+ .dgcov-groups{display:flex;flex-direction:column;gap:16px}.dgcov-section{display:flex;flex-direction:column;gap:8px}.dgcov-sechead{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-secondary);font-size:12px}.dgcov-sechead b{font-size:13px;color:var(--dsw-alias-label-primary);font-weight:600}.dgcov-sechead span{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dgcov-sechead em{margin-left:auto;font-style:normal;font-size:10px;color:var(--dgame-accent);border:1px solid color-mix(in srgb,var(--dgame-accent) 40%,transparent);border-radius:999px;padding:1px 8px;white-space:nowrap}
7352
+ .dgcov-prev-gomoku{background:repeating-linear-gradient(94deg,rgba(124,79,22,.06) 0 2px,transparent 2px 11px),radial-gradient(120% 120% at 30% 10%,#edc97f,#dcae62 55%,#c89a52)}.dgcov-prev-gomoku svg{position:absolute;inset:0;width:100%;height:100%}.dgcov-prev-gomoku line,.dgcov-prev-gomoku rect{stroke:#66431a;stroke-width:3;fill:none}.dgcov-prev-gomoku .dgp-star{fill:#66431a}.dgcov-prev-gomoku .dgp-stone-b{fill:radial-gradient(circle at 34% 30%,#4b5563,#111827 70%);fill:#1f2937;stroke:#0b1220;stroke-width:3}.dgcov-prev-gomoku .dgp-stone-w{fill:#fff;stroke:#94a3b8;stroke-width:3}
7353
+ .dgcov-prev-xiangqi{background:repeating-linear-gradient(94deg,rgba(124,79,22,.06) 0 2px,transparent 2px 11px),radial-gradient(120% 120% at 30% 10%,#f0cd86,#ddb067 55%,#c89a52)}.dgcov-prev-xiangqi svg{position:absolute;inset:0;width:100%;height:100%}.dgcov-prev-xiangqi line,.dgcov-prev-xiangqi rect,.dgcov-prev-xiangqi path{stroke:#66431a;stroke-width:3;fill:none}.dgcov-prev-xiangqi .dgp-xq-river{fill:rgba(102,67,26,.65);font-family:KaiTi,"Kaiti SC","STKaiti",serif;font-size:62px;text-anchor:middle;letter-spacing:8px}.dgcov-prev-xiangqi .dgp-xq-pc-b{fill:#f3e0ae;stroke:#8a5a1c;stroke-width:4}.dgcov-prev-xiangqi .dgp-xq-pc-r{fill:#f3e0ae;stroke:#b91c1c;stroke-width:4}.dgcov-prev-xiangqi .dgp-xq-tx-b{fill:#1f2937;font-size:52px;font-weight:700;text-anchor:middle;dominant-baseline:central;font-family:KaiTi,"Kaiti SC","STKaiti",serif}.dgcov-prev-xiangqi .dgp-xq-tx-r{fill:#b91c1c;font-size:52px;font-weight:700;text-anchor:middle;dominant-baseline:central;font-family:KaiTi,"Kaiti SC","STKaiti",serif}
7354
+ .dgcov-prev-tetris{display:grid;place-items:center;background:linear-gradient(180deg,#0c1024,#131a3d)}.dgp-tet-grid{display:grid;grid-template-columns:repeat(10,10px);grid-auto-rows:10px;gap:1px;padding:7px;border:1px solid rgba(34,211,238,.3);border-radius:7px;background:rgba(12,16,36,.6)}.dgp-tet-grid i{border-radius:2px;background:rgba(255,255,255,.045);box-shadow:inset 0 0 2px rgba(255,255,255,.07)}.dgp-tet-grid i.cI{background:#22d3ee;box-shadow:0 0 6px rgba(34,211,238,.6)}.dgp-tet-grid i.cO{background:#facc15;box-shadow:0 0 6px rgba(250,204,21,.55)}.dgp-tet-grid i.cT{background:#c084fc;box-shadow:0 0 6px rgba(192,132,252,.55)}.dgp-tet-grid i.cS{background:#4ade80;box-shadow:0 0 6px rgba(74,222,128,.55)}.dgp-tet-grid i.cZ{background:#f87171;box-shadow:0 0 6px rgba(248,113,113,.55)}.dgp-tet-grid i.cJ{background:#60a5fa;box-shadow:0 0 6px rgba(96,165,250,.55)}.dgp-tet-grid i.cL{background:#fb923c;box-shadow:0 0 6px rgba(251,146,60,.55)}.dgp-tet-grid i.fall{animation:dgp-tetbob 1.1s ease-in-out infinite}@keyframes dgp-tetbob{0%,100%{transform:translateY(0)}50%{transform:translateY(4px)}}
7355
+ .dgcov-prev-soko{display:grid;place-items:center;background:linear-gradient(180deg,#181420,#14101f)}.dgp-soko-grid{display:grid;grid-template-columns:repeat(7,17px);grid-auto-rows:17px;gap:2px}.dgp-soko-grid i{display:block;border-radius:3px;position:relative}.dgp-soko-floor{background:rgba(255,255,255,.04)}.dgp-soko-wall{background:linear-gradient(180deg,#3a3350,#241f3a);box-shadow:inset 0 1px 0 rgba(255,255,255,.12)}.dgp-soko-box{background:linear-gradient(135deg,#d4a24a,#8a5a1c);box-shadow:inset 0 1px 0 rgba(255,255,255,.4)}.dgp-soko-boxT{background:linear-gradient(135deg,#34d399,#0f766e)}.dgp-soko-target{background:rgba(255,255,255,.04)}.dgp-soko-target::after{content:'';position:absolute;inset:32%;border-radius:50%;background:#f59e0b}.dgp-soko-player{background:radial-gradient(circle at 35% 30%,#e0f2fe,#38bdf8 70%);border-radius:50%!important}
7356
+ .dgcov-prev-snake{background:linear-gradient(180deg,#0a1420,#123320)}.dgcov-prev-snake i{position:absolute;width:12px;height:12px;border-radius:26%;background:linear-gradient(135deg,#4ade80,#16a34a);transform:translate(-50%,-50%)}.dgcov-prev-snake i:first-child{background:linear-gradient(135deg,#bbf7d0,#22c55e)}.dgcov-prev-snake i:nth-child(2){background:linear-gradient(135deg,#34d399,#0d9f6e)}.dgcov-prev-snake b{position:absolute;width:13px;height:13px;border-radius:50%;background:#ef4444;box-shadow:0 0 8px #ef4444;transform:translate(-50%,-50%);animation:dgcov-snakefood 1s ease-in-out infinite}@keyframes dgcov-snakefood{50%{transform:translate(-50%,-50%) scale(1.3)}}
7357
+ .dgcov-prev-breakout{background:linear-gradient(180deg,#0b1024,#14203a)}.dgcov-prev-breakout i{position:absolute;width:12px;height:5px;border-radius:3px}.dgcov-prev-breakout b{position:absolute;left:50%;bottom:10px;width:34px;height:8px;border-radius:4px;background:#a5f3fc;transform:translateX(-50%)}
7358
+ .dgcov-prev-racer{background:linear-gradient(180deg,#0b1024,#1a0f2a)}.dgcov-prev-racer i{position:absolute;width:14px;height:22px;border-radius:5px;transform:translate(-50%,-50%);background:#f87171}.dgcov-prev-racer i:nth-child(even){background:#fb923c}.dgcov-prev-racer b{position:absolute;left:50%;bottom:8px;width:18px;height:30px;border-radius:6px;background:#22d3ee;transform:translateX(-50%)}
7359
+ .dgcov-prev-tank{display:grid;place-items:center;background:linear-gradient(180deg,#1a1425,#100d1c)}.dgp-tank-grid{display:grid;grid-template-columns:repeat(8,18px);grid-auto-rows:18px;gap:2px}.dgp-tank-grid i{display:block;border-radius:3px}.dgp-tank-void{background:rgba(255,255,255,.035)}.dgp-tank-brick{background:repeating-linear-gradient(45deg,#a3672a 0 55%,#8a5a1c 55% 100%);box-shadow:inset 0 0 0 1px rgba(0,0,0,.25)}.dgp-tank-steel{background:linear-gradient(180deg,#94a3b8,#64748b)}.dgp-tank-pw{background:linear-gradient(135deg,#fde047,#eab308);box-shadow:0 0 7px rgba(234,179,8,.55);border-radius:5px}.dgp-tank-en{background:#f87171;box-shadow:0 0 5px rgba(248,113,113,.4);border-radius:5px}.dgp-tank-base{background:#fbbf24;border-radius:50%}
7360
+
7361
+ `;
7362
+ var css3 = [baseCss, tetrisGame.css, sokobanGame.css, gomokuGame.css, xiangqiGame.css, snakeGame.css, breakoutGame.css, racerGame.css, tankGame.css, dgcovGroupCss].filter(Boolean).join("\n");
7363
+ var feature7 = {
7364
+ id: "games",
7365
+ name: "\u8DA3\u5473\u6E38\u620F",
7366
+ order: 135,
7367
+ accent: "#a78bfa",
7368
+ description: "\u4EFB\u52A1\u7B49\u5F85\u65F6\u53EF\u73A9\u7684\u5341\u6B3E\u5C0F\u6E38\u620F\uFF1A\u4E94\u5B50\u68CB\u3001\u4E2D\u56FD\u8C61\u68CB\uFF08\u4F60 vs AI\uFF09\u3001\u4FC4\u7F57\u65AF\u65B9\u5757\u3001\u63A8\u7BB1\u5B50\u3001\u8D2A\u5403\u86C7\u3001\u6253\u7816\u5757\u3001\u6781\u901F\u8D5B\u8F66\u3001\u5766\u514B\u5927\u6218\u3001\u661F\u9645\u8EB2\u907F\u3001\u53CD\u5E94\u5806\u70B9\u4EAE\u3002\u6D6E\u52A8\u7A97\u53E3\u8FD0\u884C\uFF08\u53EF\u62D6\u52A8\u3001\u53EF\u8C03\u5927\u5C0F\u3001\u53EF\u6700\u5927\u5316\u6700\u5C0F\u5316\uFF0C\u4E0D\u906E\u6321\u4F1A\u8BDD\uFF09\uFF0C\u5C4F\u5E55\u4FA7\u8FB9\u78C1\u5438\u5FEB\u6377\u5165\u53E3\u8D34\u5728\u5DE6\u4FA7\u8FB9\u680F\u53F3\u7F18\uFF08\u4E0D\u6321\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF09\uFF0C\u53EF\u4E0A\u4E0B\u632A\u4F4D\u3001\u5DE6\u53F3\u6362\u8FB9\uFF1B\u5747\u652F\u6301\u952E\u76D8",
7369
+ defaultEnabled: true,
7370
+ css: css3,
7371
+ View: GamesView,
7372
+ HomeStat: GamesHomeStat,
7373
+ Overlay: GamesOverlay
7374
+ };
7375
+
3923
7376
  // src/client.jsx
3924
- var DOCK_VERSION = "0.7.1";
3925
- var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6];
7377
+ var DOCK_VERSION = "0.8.0";
7378
+ var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7];
3926
7379
  var PLANNED_FEATURES = [];
3927
7380
  var PLANNED_NOTES = {};
3928
7381
  var externalDefs = [];
@@ -4124,8 +7577,8 @@ function scheduleInitialCss() {
4124
7577
  }
4125
7578
  }
4126
7579
  function useExternalVersion() {
4127
- const [, bump] = import_react9.default.useReducer((n) => n + 1, 0);
4128
- import_react9.default.useEffect(() => {
7580
+ const [, bump] = import_react19.default.useReducer((n) => n + 1, 0);
7581
+ import_react19.default.useEffect(() => {
4129
7582
  externalListeners.add(bump);
4130
7583
  return () => {
4131
7584
  externalListeners.delete(bump);
@@ -4135,44 +7588,44 @@ function useExternalVersion() {
4135
7588
  var lastGeom = { x: null, y: null, w: null, h: null };
4136
7589
  function DockIcon(props) {
4137
7590
  const size = props && props.size || 16;
4138
- return import_react9.default.createElement(
7591
+ return import_react19.default.createElement(
4139
7592
  "svg",
4140
7593
  { width: size, height: size, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.3, "aria-hidden": true },
4141
- import_react9.default.createElement("rect", { x: 2.5, y: 2.5, width: 4.4, height: 4.4, rx: 1.2 }),
4142
- import_react9.default.createElement("rect", { x: 9, y: 2.5, width: 4.4, height: 4.4, rx: 1.2 }),
4143
- import_react9.default.createElement("rect", { x: 2.5, y: 9, width: 4.4, height: 4.4, rx: 1.2 }),
4144
- import_react9.default.createElement("rect", { x: 9, y: 9, width: 4.4, height: 4.4, rx: 1.2 })
7594
+ import_react19.default.createElement("rect", { x: 2.5, y: 2.5, width: 4.4, height: 4.4, rx: 1.2 }),
7595
+ import_react19.default.createElement("rect", { x: 9, y: 2.5, width: 4.4, height: 4.4, rx: 1.2 }),
7596
+ import_react19.default.createElement("rect", { x: 2.5, y: 9, width: 4.4, height: 4.4, rx: 1.2 }),
7597
+ import_react19.default.createElement("rect", { x: 9, y: 9, width: 4.4, height: 4.4, rx: 1.2 })
4145
7598
  );
4146
7599
  }
4147
7600
  function DockEntry(props) {
4148
7601
  const wide = !!props.wide;
4149
- const [open, setOpen] = import_react9.default.useState(panelNav.open);
4150
- import_react9.default.useEffect(() => subscribePanel(() => setOpen(panelNav.open)), []);
4151
- return import_react9.default.createElement("button", {
7602
+ const [open, setOpen] = import_react19.default.useState(panelNav.open);
7603
+ import_react19.default.useEffect(() => subscribePanel(() => setOpen(panelNav.open)), []);
7604
+ return import_react19.default.createElement("button", {
4152
7605
  type: "button",
4153
7606
  className: "docke2-btn" + (open ? " docke2-on" : "") + (wide ? "" : " docke2-rail"),
4154
7607
  title: "\u529F\u80FD\u575E",
4155
7608
  "aria-label": "\u529F\u80FD\u575E",
4156
7609
  "aria-expanded": open,
4157
7610
  onClick: () => setPanelOpen(!open)
4158
- }, import_react9.default.createElement(DockIcon, { size: wide ? 16 : 18 }), wide ? import_react9.default.createElement("span", { className: "docke2-label" }, "\u529F\u80FD\u575E") : null);
7611
+ }, import_react19.default.createElement(DockIcon, { size: wide ? 16 : 18 }), wide ? import_react19.default.createElement("span", { className: "docke2-label" }, "\u529F\u80FD\u575E") : null);
4159
7612
  }
4160
7613
  function DockModal() {
4161
- const [nav, setNav] = import_react9.default.useState({ open: panelNav.open, active: panelNav.active, params: panelNav.params });
4162
- import_react9.default.useEffect(() => subscribePanel(() => setNav({ open: panelNav.open, active: panelNav.active, params: panelNav.params })), []);
7614
+ const [nav, setNav] = import_react19.default.useState({ open: panelNav.open, active: panelNav.active, params: panelNav.params });
7615
+ import_react19.default.useEffect(() => subscribePanel(() => setNav({ open: panelNav.open, active: panelNav.active, params: panelNav.params })), []);
4163
7616
  const open = nav.open || typeof document === "undefined";
4164
7617
  const active = nav.active;
4165
7618
  const setActive = navigatePanel;
4166
7619
  const navParams = nav.params;
4167
- const [, force] = import_react9.default.useReducer((n) => n + 1, 0);
7620
+ const [, force] = import_react19.default.useReducer((n) => n + 1, 0);
4168
7621
  useExternalVersion();
4169
- const [win, setWin] = import_react9.default.useState(() => ({ mode: "normal", x: null, y: null, w: null, h: null }));
4170
- const dlgRef = import_react9.default.useRef(null);
4171
- const contentRef = import_react9.default.useRef(null);
4172
- import_react9.default.useEffect(() => {
7622
+ const [win, setWin] = import_react19.default.useState(() => ({ mode: "normal", x: null, y: null, w: null, h: null }));
7623
+ const dlgRef = import_react19.default.useRef(null);
7624
+ const contentRef = import_react19.default.useRef(null);
7625
+ import_react19.default.useEffect(() => {
4173
7626
  if (lastGeom.w) setWin({ mode: "normal", x: lastGeom.x, y: lastGeom.y, w: lastGeom.w, h: lastGeom.h });
4174
7627
  }, []);
4175
- import_react9.default.useEffect(() => {
7628
+ import_react19.default.useEffect(() => {
4176
7629
  if (open && contentRef.current) contentRef.current.scrollTop = 0;
4177
7630
  }, [active, open]);
4178
7631
  function beginDrag(e, type) {
@@ -4180,9 +7633,9 @@ function DockModal() {
4180
7633
  if (type === "move" && e.target && e.target.closest && e.target.closest("button,select,input")) return;
4181
7634
  const node = dlgRef.current;
4182
7635
  if (!node || typeof window === "undefined") return;
4183
- const rect = node.getBoundingClientRect();
7636
+ const rect2 = node.getBoundingClientRect();
4184
7637
  const startX = e.clientX, startY = e.clientY;
4185
- const origin = { left: rect.left, top: rect.top, w: rect.width, h: rect.height };
7638
+ const origin = { left: rect2.left, top: rect2.top, w: rect2.width, h: rect2.height };
4186
7639
  const clampX = (v) => Math.min(Math.max(8, v), window.innerWidth - origin.w - 8);
4187
7640
  const clampY = (v) => Math.min(Math.max(8, v), window.innerHeight - origin.h - 8);
4188
7641
  const onMove = (ev) => {
@@ -4210,13 +7663,13 @@ function DockModal() {
4210
7663
  const mod = isHome ? null : MODULES.find((m) => m.id === active) || MODULES[0];
4211
7664
  const st = mod ? stateOf(mod.id) : null;
4212
7665
  const View = mod ? mod.View : null;
4213
- const viewNode = mod && View ? import_react9.default.createElement(
7666
+ const viewNode = mod && View ? import_react19.default.createElement(
4214
7667
  "div",
4215
7668
  { className: "dockm-view" },
4216
- import_react9.default.createElement(
4217
- mod.external ? FeatureBoundary : import_react9.default.Fragment,
7669
+ import_react19.default.createElement(
7670
+ mod.external ? FeatureBoundary : import_react19.default.Fragment,
4218
7671
  null,
4219
- import_react9.default.createElement(View, { ctx: ctxRef.current, feature: mod, params: navParams })
7672
+ import_react19.default.createElement(View, { ctx: ctxRef.current, feature: mod, params: navParams })
4220
7673
  )
4221
7674
  ) : null;
4222
7675
  const enabledCount = MODULES.filter((m) => {
@@ -4230,10 +7683,10 @@ function DockModal() {
4230
7683
  width: win.w != null ? win.w : void 0,
4231
7684
  height: win.mode === "min" ? "auto" : win.h != null ? win.h : void 0
4232
7685
  } : null;
4233
- return import_react9.default.createElement(
7686
+ return import_react19.default.createElement(
4234
7687
  "div",
4235
7688
  { className: "dockm-backdrop", onClick: () => setPanelOpen(false) },
4236
- import_react9.default.createElement(
7689
+ import_react19.default.createElement(
4237
7690
  "div",
4238
7691
  {
4239
7692
  className: "dockm-dialog" + (win.mode === "max" ? " dockm-max" : "") + (win.mode === "min" ? " dockm-min" : ""),
@@ -4241,41 +7694,41 @@ function DockModal() {
4241
7694
  ref: dlgRef,
4242
7695
  onClick: (e) => e.stopPropagation()
4243
7696
  },
4244
- import_react9.default.createElement(
7697
+ import_react19.default.createElement(
4245
7698
  "div",
4246
7699
  {
4247
7700
  className: "dockm-head",
4248
7701
  onPointerDown: (e) => beginDrag(e, "move"),
4249
7702
  onDoubleClick: () => setWin((s) => Object.assign({}, s, { mode: s.mode === "max" ? "normal" : "max" }))
4250
7703
  },
4251
- import_react9.default.createElement(DockIcon, null),
4252
- import_react9.default.createElement("span", { className: "dockm-title" }, "\u529F\u80FD\u575E"),
4253
- import_react9.default.createElement("span", { className: "dockm-sub" }, "dsh-dock \xB7 \u4E5F\u53EF\u5728 \u8BBE\u7F6E \u2192 \u529F\u80FD\u575E \u6253\u5F00\u7BA1\u7406\u9875"),
4254
- import_react9.default.createElement(
7704
+ import_react19.default.createElement(DockIcon, null),
7705
+ import_react19.default.createElement("span", { className: "dockm-title" }, "\u529F\u80FD\u575E"),
7706
+ import_react19.default.createElement("span", { className: "dockm-sub" }, "dsh-dock \xB7 \u4E5F\u53EF\u5728 \u8BBE\u7F6E \u2192 \u529F\u80FD\u575E \u6253\u5F00\u7BA1\u7406\u9875"),
7707
+ import_react19.default.createElement(
4255
7708
  "span",
4256
7709
  { className: "dockm-ctrls" },
4257
- import_react9.default.createElement("button", {
7710
+ import_react19.default.createElement("button", {
4258
7711
  type: "button",
4259
7712
  className: "dockm-win",
4260
7713
  title: win.mode === "min" ? "\u8FD8\u539F" : "\u6700\u5C0F\u5316",
4261
7714
  onClick: () => setWin((s) => Object.assign({}, s, { mode: s.mode === "min" ? "normal" : "min" }))
4262
7715
  }, "\u2581"),
4263
- import_react9.default.createElement("button", {
7716
+ import_react19.default.createElement("button", {
4264
7717
  type: "button",
4265
7718
  className: "dockm-win",
4266
7719
  title: win.mode === "max" ? "\u8FD8\u539F" : "\u6700\u5927\u5316",
4267
7720
  onClick: () => setWin((s) => Object.assign({}, s, { mode: s.mode === "max" ? "normal" : "max" }))
4268
7721
  }, win.mode === "max" ? "\u2750" : "\u25A2"),
4269
- import_react9.default.createElement("button", { type: "button", className: "dockm-close", "aria-label": "\u5173\u95ED", title: "\u5173\u95ED", onClick: () => setPanelOpen(false) }, "\u2715")
7722
+ import_react19.default.createElement("button", { type: "button", className: "dockm-close", "aria-label": "\u5173\u95ED", title: "\u5173\u95ED", onClick: () => setPanelOpen(false) }, "\u2715")
4270
7723
  )
4271
7724
  ),
4272
- import_react9.default.createElement(
7725
+ import_react19.default.createElement(
4273
7726
  "div",
4274
7727
  { className: "dockm-body" },
4275
- import_react9.default.createElement(
7728
+ import_react19.default.createElement(
4276
7729
  "nav",
4277
7730
  { className: "dockm-nav", "aria-label": "\u529F\u80FD\u6A21\u5757" },
4278
- import_react9.default.createElement(
7731
+ import_react19.default.createElement(
4279
7732
  "button",
4280
7733
  {
4281
7734
  type: "button",
@@ -4283,10 +7736,10 @@ function DockModal() {
4283
7736
  className: "dockm-nav-item" + (isHome ? " on" : ""),
4284
7737
  onClick: () => setActive("home")
4285
7738
  },
4286
- import_react9.default.createElement("span", { className: "dockm-navhome" }, import_react9.default.createElement(DockIcon, null)),
4287
- import_react9.default.createElement("span", null, "\u9996\u9875")
7739
+ import_react19.default.createElement("span", { className: "dockm-navhome" }, import_react19.default.createElement(DockIcon, null)),
7740
+ import_react19.default.createElement("span", null, "\u9996\u9875")
4288
7741
  ),
4289
- MODULES.map((m) => import_react9.default.createElement(
7742
+ MODULES.map((m) => import_react19.default.createElement(
4290
7743
  "button",
4291
7744
  {
4292
7745
  type: "button",
@@ -4294,42 +7747,42 @@ function DockModal() {
4294
7747
  className: "dockm-nav-item" + (m.id === active ? " on" : ""),
4295
7748
  onClick: () => setActive(m.id)
4296
7749
  },
4297
- import_react9.default.createElement("span", { className: "dockm-dot", style: { background: m.accent } }),
4298
- import_react9.default.createElement("span", null, m.name),
4299
- m.planned ? import_react9.default.createElement("span", { className: "dockm-badge" }, "\u89C4\u5212\u4E2D") : null,
4300
- m.external ? import_react9.default.createElement("span", { className: "dockm-badge" }, "\u5916\u90E8") : null
7750
+ import_react19.default.createElement("span", { className: "dockm-dot", style: { background: m.accent } }),
7751
+ import_react19.default.createElement("span", null, m.name),
7752
+ m.planned ? import_react19.default.createElement("span", { className: "dockm-badge" }, "\u89C4\u5212\u4E2D") : null,
7753
+ m.external ? import_react19.default.createElement("span", { className: "dockm-badge" }, "\u5916\u90E8") : null
4301
7754
  ))
4302
7755
  ),
4303
- import_react9.default.createElement(
7756
+ import_react19.default.createElement(
4304
7757
  "div",
4305
7758
  { className: "dockm-content", ref: contentRef },
4306
- import_react9.default.createElement(
7759
+ import_react19.default.createElement(
4307
7760
  "div",
4308
7761
  { className: "dockm-content-head" },
4309
- import_react9.default.createElement(
7762
+ import_react19.default.createElement(
4310
7763
  "div",
4311
7764
  { className: "dockm-name" },
4312
- isHome ? import_react9.default.createElement("span", { className: "dockm-navhome" }, import_react9.default.createElement(DockIcon, null)) : import_react9.default.createElement("span", { className: "dockm-dot", style: { background: mod.accent } }),
7765
+ isHome ? import_react19.default.createElement("span", { className: "dockm-navhome" }, import_react19.default.createElement(DockIcon, null)) : import_react19.default.createElement("span", { className: "dockm-dot", style: { background: mod.accent } }),
4313
7766
  isHome ? "\u9996\u9875" : mod.name,
4314
- !isHome && mod.planned ? import_react9.default.createElement("span", { className: "dockm-badge" }, "\u89C4\u5212\u4E2D") : null,
4315
- !isHome && mod.external ? import_react9.default.createElement("span", { className: "dockm-badge" }, "\u5916\u90E8\u5305" + (mod.package ? " \xB7 " + mod.package : "")) : null
7767
+ !isHome && mod.planned ? import_react19.default.createElement("span", { className: "dockm-badge" }, "\u89C4\u5212\u4E2D") : null,
7768
+ !isHome && mod.external ? import_react19.default.createElement("span", { className: "dockm-badge" }, "\u5916\u90E8\u5305" + (mod.package ? " \xB7 " + mod.package : "")) : null
4316
7769
  ),
4317
- import_react9.default.createElement(
7770
+ import_react19.default.createElement(
4318
7771
  "div",
4319
7772
  { className: "dockm-desc" },
4320
7773
  isHome ? "\u6240\u6709\u5B50\u529F\u80FD\u603B\u63FD\uFF1A\u8FD0\u884C\u72B6\u6001\u3001\u6982\u8981\u4E0E\u5FEB\u6377\u5F00\u5173\uFF0C\u70B9\u51FB\u5361\u7247\u8FDB\u5165\u5BF9\u5E94\u529F\u80FD\u9875\u3002" : mod.description
4321
7774
  )
4322
7775
  ),
4323
- isHome ? import_react9.default.createElement("div", { className: "dockm-view" }, import_react9.default.createElement(HomeView, { ctx: ctxRef.current, onOpen: setActive, onToggle: force })) : mod.planned ? import_react9.default.createElement("div", { className: "dockm-note" }, PLANNED_NOTES[mod.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : st && st.enabled && viewNode ? viewNode : st && st.error ? import_react9.default.createElement("div", { className: "dockm-note dockm-err" }, "\u529F\u80FD\u51FA\u9519\uFF1A" + st.error) : import_react9.default.createElement("div", { className: "dockm-note" }, "\u8BE5\u529F\u80FD\u5F53\u524D\u4E3A\u505C\u7528\u72B6\u6001\uFF08\u5F00\u5173\u5DF2\u6301\u4E45\u5316\uFF0C\u91CD\u542F\u540E\u4FDD\u6301\uFF09"),
4324
- import_react9.default.createElement(
7776
+ isHome ? import_react19.default.createElement("div", { className: "dockm-view" }, import_react19.default.createElement(HomeView, { ctx: ctxRef.current, onOpen: setActive, onToggle: force })) : mod.planned ? import_react19.default.createElement("div", { className: "dockm-note" }, PLANNED_NOTES[mod.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : st && st.enabled && viewNode ? viewNode : st && st.error ? import_react19.default.createElement("div", { className: "dockm-note dockm-err" }, "\u529F\u80FD\u51FA\u9519\uFF1A" + st.error) : import_react19.default.createElement("div", { className: "dockm-note" }, "\u8BE5\u529F\u80FD\u5F53\u524D\u4E3A\u505C\u7528\u72B6\u6001\uFF08\u5F00\u5173\u5DF2\u6301\u4E45\u5316\uFF0C\u91CD\u542F\u540E\u4FDD\u6301\uFF09"),
7777
+ import_react19.default.createElement(
4325
7778
  "div",
4326
7779
  { className: "dockm-foot" },
4327
- import_react9.default.createElement("span", null, isHome ? "\u529F\u80FD\u575E v" + DOCK_VERSION + " \xB7 \u5171 " + MODULES.length + " \u4E2A\u529F\u80FD\u6A21\u5757\uFF0C" + enabledCount + " \u4E2A\u5DF2\u542F\u7528" : "\u529F\u80FD\u575E v" + DOCK_VERSION + " \xB7 \u65B0\u529F\u80FD\u6309\u8DEF\u7EBF\u56FE\u8FFD\u52A0"),
4328
- !isHome && mod && !mod.planned && st ? import_react9.default.createElement(
7780
+ import_react19.default.createElement("span", null, isHome ? "\u529F\u80FD\u575E v" + DOCK_VERSION + " \xB7 \u5171 " + MODULES.length + " \u4E2A\u529F\u80FD\u6A21\u5757\uFF0C" + enabledCount + " \u4E2A\u5DF2\u542F\u7528" : "\u529F\u80FD\u575E v" + DOCK_VERSION + " \xB7 \u65B0\u529F\u80FD\u6309\u8DEF\u7EBF\u56FE\u8FFD\u52A0"),
7781
+ !isHome && mod && !mod.planned && st ? import_react19.default.createElement(
4329
7782
  "span",
4330
7783
  { className: "dockm-foot-sw" },
4331
- import_react9.default.createElement("span", { className: "dockm-foot-swlabel" + (st.enabled ? " on" : "") }, st.enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u505C\u7528"),
4332
- import_react9.default.createElement("button", {
7784
+ import_react19.default.createElement("span", { className: "dockm-foot-swlabel" + (st.enabled ? " on" : "") }, st.enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u505C\u7528"),
7785
+ import_react19.default.createElement("button", {
4333
7786
  type: "button",
4334
7787
  className: "dock-sw" + (st.enabled ? " on" : ""),
4335
7788
  role: "switch",
@@ -4342,11 +7795,11 @@ function DockModal() {
4342
7795
  }
4343
7796
  })
4344
7797
  ) : null,
4345
- !isHome && mod && typeof mod.Chip === "function" ? import_react9.default.createElement(
7798
+ !isHome && mod && typeof mod.Chip === "function" ? import_react19.default.createElement(
4346
7799
  "span",
4347
7800
  { className: "dockm-foot-sw" },
4348
- import_react9.default.createElement("span", { className: "dockm-foot-swlabel" + (chipShown(mod.id) ? " on" : "") }, "\u4F1A\u8BDD\u9875\u5C0F\u63A7\u4EF6"),
4349
- import_react9.default.createElement("button", {
7801
+ import_react19.default.createElement("span", { className: "dockm-foot-swlabel" + (chipShown(mod.id) ? " on" : "") }, "\u4F1A\u8BDD\u9875\u5C0F\u63A7\u4EF6"),
7802
+ import_react19.default.createElement("button", {
4350
7803
  type: "button",
4351
7804
  className: "dock-sw" + (chipShown(mod.id) ? " on" : ""),
4352
7805
  role: "switch",
@@ -4362,30 +7815,30 @@ function DockModal() {
4362
7815
  )
4363
7816
  )
4364
7817
  ),
4365
- win.mode === "normal" ? import_react9.default.createElement("div", { className: "dockm-resize", onPointerDown: (e) => beginDrag(e, "size") }) : null
7818
+ win.mode === "normal" ? import_react19.default.createElement("div", { className: "dockm-resize", onPointerDown: (e) => beginDrag(e, "size") }) : null
4366
7819
  )
4367
7820
  );
4368
7821
  }
4369
7822
  function HomeView(props) {
4370
7823
  const ctx = props && props.ctx;
4371
- const [, force] = import_react9.default.useReducer((n) => n + 1, 0);
7824
+ const [, force] = import_react19.default.useReducer((n) => n + 1, 0);
4372
7825
  useExternalVersion();
4373
7826
  const open = (id) => {
4374
7827
  if (props && typeof props.onOpen === "function") props.onOpen(id);
4375
7828
  };
4376
- return import_react9.default.createElement(
7829
+ return import_react19.default.createElement(
4377
7830
  "div",
4378
7831
  { className: "dockh-grid" },
4379
7832
  allModules().map((m) => {
4380
7833
  const st = stateOf(m.id);
4381
7834
  const enabled = !!(st && st.enabled);
4382
7835
  const Stat = m.HomeStat;
4383
- const statNode = m.planned ? import_react9.default.createElement("span", null, PLANNED_NOTES[m.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : enabled && Stat ? import_react9.default.createElement(
4384
- m.external ? FeatureBoundary : import_react9.default.Fragment,
7836
+ const statNode = m.planned ? import_react19.default.createElement("span", null, PLANNED_NOTES[m.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : enabled && Stat ? import_react19.default.createElement(
7837
+ m.external ? FeatureBoundary : import_react19.default.Fragment,
4385
7838
  null,
4386
- import_react9.default.createElement(Stat, { ctx })
4387
- ) : import_react9.default.createElement("span", null, "\u5DF2\u505C\u7528\uFF0C\u542F\u7528\u540E\u5728\u6B64\u5C55\u793A\u8FD0\u884C\u6982\u8981");
4388
- return import_react9.default.createElement(
7839
+ import_react19.default.createElement(Stat, { ctx })
7840
+ ) : import_react19.default.createElement("span", null, "\u5DF2\u505C\u7528\uFF0C\u542F\u7528\u540E\u5728\u6B64\u5C55\u793A\u8FD0\u884C\u6982\u8981");
7841
+ return import_react19.default.createElement(
4389
7842
  "div",
4390
7843
  {
4391
7844
  key: m.id,
@@ -4400,21 +7853,21 @@ function HomeView(props) {
4400
7853
  }
4401
7854
  }
4402
7855
  },
4403
- import_react9.default.createElement(
7856
+ import_react19.default.createElement(
4404
7857
  "div",
4405
7858
  { className: "dockh-head" },
4406
- import_react9.default.createElement("span", { className: "dockm-dot", style: { background: m.accent } }),
4407
- import_react9.default.createElement("span", { className: "dockh-name" }, m.name),
4408
- m.external ? import_react9.default.createElement("span", { className: "dockh-badge", title: m.package || void 0 }, "\u5916\u90E8") : null,
7859
+ import_react19.default.createElement("span", { className: "dockm-dot", style: { background: m.accent } }),
7860
+ import_react19.default.createElement("span", { className: "dockh-name" }, m.name),
7861
+ m.external ? import_react19.default.createElement("span", { className: "dockh-badge", title: m.package || void 0 }, "\u5916\u90E8") : null,
4409
7862
  // 状态标识:圆点 + 文字(纯展示,与开关视觉区分)
4410
- import_react9.default.createElement(
7863
+ import_react19.default.createElement(
4411
7864
  "span",
4412
7865
  { className: "dockh-status" + (m.planned ? " plan" : enabled ? "" : " off") },
4413
- import_react9.default.createElement("span", { className: "dockh-sdot" }),
4414
- import_react9.default.createElement("span", null, m.planned ? "\u89C4\u5212\u4E2D" : enabled ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u505C\u7528")
7866
+ import_react19.default.createElement("span", { className: "dockh-sdot" }),
7867
+ import_react19.default.createElement("span", null, m.planned ? "\u89C4\u5212\u4E2D" : enabled ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u505C\u7528")
4415
7868
  ),
4416
7869
  // 启停开关(规划中的功能不显示)
4417
- m.planned ? null : import_react9.default.createElement("button", {
7870
+ m.planned ? null : import_react19.default.createElement("button", {
4418
7871
  type: "button",
4419
7872
  className: "dock-sw" + (enabled ? " on" : ""),
4420
7873
  role: "switch",
@@ -4429,12 +7882,12 @@ function HomeView(props) {
4429
7882
  }
4430
7883
  })
4431
7884
  ),
4432
- import_react9.default.createElement("div", { className: "dockh-desc" }, m.description),
4433
- import_react9.default.createElement("div", { className: "dockh-stat" }, statNode),
4434
- import_react9.default.createElement(
7885
+ import_react19.default.createElement("div", { className: "dockh-desc" }, m.description),
7886
+ import_react19.default.createElement("div", { className: "dockh-stat" }, statNode),
7887
+ import_react19.default.createElement(
4435
7888
  "div",
4436
7889
  { className: "dockh-foot" },
4437
- import_react9.default.createElement("span", { className: "dockh-go" }, "\u67E5\u770B\u8BE6\u60C5 \u2192")
7890
+ import_react19.default.createElement("span", { className: "dockh-go" }, "\u67E5\u770B\u8BE6\u60C5 \u2192")
4438
7891
  )
4439
7892
  );
4440
7893
  })
@@ -4442,16 +7895,16 @@ function HomeView(props) {
4442
7895
  }
4443
7896
  function DockPanel() {
4444
7897
  const ctx = ctxRef.current;
4445
- const [, force] = import_react9.default.useReducer((n) => n + 1, 0);
7898
+ const [, force] = import_react19.default.useReducer((n) => n + 1, 0);
4446
7899
  useExternalVersion();
4447
7900
  const toggle = (id) => {
4448
7901
  toggleFeature(id);
4449
7902
  force();
4450
7903
  };
4451
- return import_react9.default.createElement(
7904
+ return import_react19.default.createElement(
4452
7905
  "div",
4453
7906
  { className: "dock-root" },
4454
- import_react9.default.createElement(
7907
+ import_react19.default.createElement(
4455
7908
  "div",
4456
7909
  { className: "dock-intro" },
4457
7910
  "\u529F\u80FD\u575E\uFF08dsh-dock\uFF09\xB7 \u6240\u6709\u5C0F\u529F\u80FD\u96C6\u4E2D\u5728\u8FD9\u4E00\u4E2A\u9762\u677F\u91CC\u7BA1\u7406\u3002v0.4.0 \u8D77\u6BCF\u4E2A\u529F\u80FD\u662F\u72EC\u7ACB\u6A21\u5757\uFF08features/<id>/\uFF09\uFF0C",
@@ -4460,25 +7913,25 @@ function DockPanel() {
4460
7913
  allModules().map((f) => {
4461
7914
  const st = stateOf(f.id);
4462
7915
  const View = f.View;
4463
- const viewNode = !f.planned && st.enabled && View ? import_react9.default.createElement(
7916
+ const viewNode = !f.planned && st.enabled && View ? import_react19.default.createElement(
4464
7917
  "div",
4465
7918
  { className: "dock-body" },
4466
- import_react9.default.createElement(
4467
- f.external ? FeatureBoundary : import_react9.default.Fragment,
7919
+ import_react19.default.createElement(
7920
+ f.external ? FeatureBoundary : import_react19.default.Fragment,
4468
7921
  null,
4469
- import_react9.default.createElement(View, { ctx, feature: f })
7922
+ import_react19.default.createElement(View, { ctx, feature: f })
4470
7923
  )
4471
7924
  ) : null;
4472
- return import_react9.default.createElement(
7925
+ return import_react19.default.createElement(
4473
7926
  "div",
4474
7927
  { className: "dock-card", key: f.id },
4475
- import_react9.default.createElement(
7928
+ import_react19.default.createElement(
4476
7929
  "div",
4477
7930
  { className: "dock-card-head" },
4478
- import_react9.default.createElement("span", { className: "dock-dot" + (st.error ? " err" : st.enabled ? " on" : "") }),
4479
- import_react9.default.createElement("span", { className: "dock-name" }, f.name),
4480
- import_react9.default.createElement("span", { className: "dock-desc" }, f.description + (f.external ? "\uFF08\u6765\u81EA\u5916\u90E8\u5305" + (f.package ? " " + f.package : "") + "\uFF09" : "")),
4481
- f.planned ? import_react9.default.createElement("span", { className: "dock-badge" }, "\u89C4\u5212\u4E2D") : import_react9.default.createElement("button", {
7931
+ import_react19.default.createElement("span", { className: "dock-dot" + (st.error ? " err" : st.enabled ? " on" : "") }),
7932
+ import_react19.default.createElement("span", { className: "dock-name" }, f.name),
7933
+ import_react19.default.createElement("span", { className: "dock-desc" }, f.description + (f.external ? "\uFF08\u6765\u81EA\u5916\u90E8\u5305" + (f.package ? " " + f.package : "") + "\uFF09" : "")),
7934
+ f.planned ? import_react19.default.createElement("span", { className: "dock-badge" }, "\u89C4\u5212\u4E2D") : import_react19.default.createElement("button", {
4482
7935
  type: "button",
4483
7936
  className: "dock-sw" + (st.enabled ? " on" : ""),
4484
7937
  role: "switch",
@@ -4487,11 +7940,11 @@ function DockPanel() {
4487
7940
  title: st.enabled ? "\u505C\u7528\u300C" + f.name + "\u300D" : "\u542F\u7528\u300C" + f.name + "\u300D",
4488
7941
  onClick: () => toggle(f.id)
4489
7942
  }),
4490
- !f.planned && typeof f.Chip === "function" ? import_react9.default.createElement(
7943
+ !f.planned && typeof f.Chip === "function" ? import_react19.default.createElement(
4491
7944
  "span",
4492
7945
  { className: "dockm-foot-sw" },
4493
- import_react9.default.createElement("span", { className: "dockm-foot-swlabel" + (chipShown(f.id) ? " on" : "") }, "\u4F1A\u8BDD\u9875\u5C0F\u63A7\u4EF6"),
4494
- import_react9.default.createElement("button", {
7946
+ import_react19.default.createElement("span", { className: "dockm-foot-swlabel" + (chipShown(f.id) ? " on" : "") }, "\u4F1A\u8BDD\u9875\u5C0F\u63A7\u4EF6"),
7947
+ import_react19.default.createElement("button", {
4495
7948
  type: "button",
4496
7949
  className: "dock-sw" + (chipShown(f.id) ? " on" : ""),
4497
7950
  role: "switch",
@@ -4505,19 +7958,19 @@ function DockPanel() {
4505
7958
  })
4506
7959
  ) : null
4507
7960
  ),
4508
- f.planned ? import_react9.default.createElement("div", { className: "dock-body" }, PLANNED_NOTES[f.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : st.error ? import_react9.default.createElement("div", { className: "dock-body dockm-err" }, "\u529F\u80FD\u51FA\u9519\uFF1A" + st.error) : null,
7961
+ f.planned ? import_react19.default.createElement("div", { className: "dock-body" }, PLANNED_NOTES[f.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : st.error ? import_react19.default.createElement("div", { className: "dock-body dockm-err" }, "\u529F\u80FD\u51FA\u9519\uFF1A" + st.error) : null,
4509
7962
  viewNode
4510
7963
  );
4511
7964
  })
4512
7965
  );
4513
7966
  }
4514
7967
  function DockChips(props) {
4515
- const [, force] = import_react9.default.useReducer((n) => n + 1, 0);
4516
- import_react9.default.useEffect(() => subscribeFeatureState(() => force()), []);
7968
+ const [, force] = import_react19.default.useReducer((n) => n + 1, 0);
7969
+ import_react19.default.useEffect(() => subscribeFeatureState(() => force()), []);
4517
7970
  const items = [];
4518
7971
  for (const f of allModules()) {
4519
7972
  if (f.planned || !stateOf(f.id).enabled || !chipShown(f.id) || typeof f.Chip !== "function") continue;
4520
- items.push(import_react9.default.createElement(f.Chip, {
7973
+ items.push(import_react19.default.createElement(f.Chip, {
4521
7974
  key: f.id,
4522
7975
  ctx: props.ctx,
4523
7976
  feature: f,
@@ -4527,23 +7980,23 @@ function DockChips(props) {
4527
7980
  }));
4528
7981
  }
4529
7982
  if (items.length === 0) return null;
4530
- return import_react9.default.createElement("div", { className: "dockchip-row" }, items);
7983
+ return import_react19.default.createElement("div", { className: "dockchip-row" }, items);
4531
7984
  }
4532
7985
  function FeatureOverlays() {
4533
- const [, force] = import_react9.default.useReducer((n) => n + 1, 0);
7986
+ const [, force] = import_react19.default.useReducer((n) => n + 1, 0);
4534
7987
  useExternalVersion();
4535
- import_react9.default.useEffect(() => subscribeFeatureState(() => force()), []);
7988
+ import_react19.default.useEffect(() => subscribeFeatureState(() => force()), []);
4536
7989
  const items = [];
4537
7990
  for (const f of allModules()) {
4538
7991
  if (f.planned || !stateOf(f.id).enabled || typeof f.Overlay !== "function") continue;
4539
- items.push(import_react9.default.createElement(
7992
+ items.push(import_react19.default.createElement(
4540
7993
  FeatureBoundary,
4541
7994
  { key: f.id },
4542
- import_react9.default.createElement(f.Overlay, { ctx: ctxRef.current, feature: f })
7995
+ import_react19.default.createElement(f.Overlay, { ctx: ctxRef.current, feature: f })
4543
7996
  ));
4544
7997
  }
4545
7998
  if (items.length === 0) return null;
4546
- return import_react9.default.createElement(import_react9.default.Fragment, null, items);
7999
+ return import_react19.default.createElement(import_react19.default.Fragment, null, items);
4547
8000
  }
4548
8001
  var ctxRef = { current: null };
4549
8002
  function apply(ctx) {
@@ -4554,23 +8007,23 @@ function apply(ctx) {
4554
8007
  if (slots === void 0) return;
4555
8008
  slots.inject("sidebar.footer.action", () => slots.register(
4556
8009
  { name: "sidebar.footer.action", id: "dsh-dock", order: 1, label: "\u529F\u80FD\u575E" },
4557
- (props) => import_react9.default.createElement(DockEntry, props)
8010
+ (props) => import_react19.default.createElement(DockEntry, props)
4558
8011
  ));
4559
8012
  slots.inject("shell.overlay", () => slots.register(
4560
8013
  { name: "shell.overlay", id: "dsh-dock-panel", order: 21, label: "\u529F\u80FD\u575E\u9762\u677F" },
4561
- () => import_react9.default.createElement(DockModal, null)
8014
+ () => import_react19.default.createElement(DockModal, null)
4562
8015
  ));
4563
8016
  slots.inject("shell.overlay", () => slots.register(
4564
8017
  { name: "shell.overlay", id: "dsh-dock-feature-overlays", order: 22, label: "\u529F\u80FD\u575E\u5168\u5C40\u6D6E\u5C42" },
4565
- () => import_react9.default.createElement(FeatureOverlays, null)
8018
+ () => import_react19.default.createElement(FeatureOverlays, null)
4566
8019
  ));
4567
8020
  slots.inject("settings.section", () => slots.register(
4568
8021
  { name: "settings.section", id: "dsh-dock", order: 90, label: "\u529F\u80FD\u575E" },
4569
- () => import_react9.default.createElement(DockPanel, null)
8022
+ () => import_react19.default.createElement(DockPanel, null)
4570
8023
  ));
4571
8024
  slots.inject("conversation.input.left", () => slots.register(
4572
8025
  { name: "conversation.input.left", id: "dsh-dock-chips", order: 10, label: "\u529F\u80FD\u575E" },
4573
- (zone) => import_react9.default.createElement(DockChips, Object.assign({}, zone, { ctx: ctxRef.current }))
8026
+ (zone) => import_react19.default.createElement(DockChips, Object.assign({}, zone, { ctx: ctxRef.current }))
4574
8027
  ));
4575
8028
  }
4576
8029
  var inject = ["timer"];