dsh-dock 0.9.1 → 0.9.3

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
@@ -3876,6 +3876,13 @@ var PHASE_COLORS = { think: "#2f6fed", write: "#0d9488", code: "#b45309", search
3876
3876
  function phaseColor(p) {
3877
3877
  return PHASE_COLORS[p] || "#2f6fed";
3878
3878
  }
3879
+ function toolLabel(name) {
3880
+ const n = String(name || "").toLowerCase();
3881
+ if (/bash|shell|terminal|cmd|pwsh|powershell|exec/.test(n)) return "\u6267\u884C\u547D\u4EE4";
3882
+ if (/write|edit|patch|apply|create|mkdir|remove|delete/.test(n)) return "\u4FEE\u6539\u6587\u4EF6";
3883
+ if (/web|fetch|browser|navigate|search/.test(n)) return "\u8BBF\u95EE\u7F51\u9875";
3884
+ return "";
3885
+ }
3879
3886
  var END_LABELS = {
3880
3887
  completed: { label: "\u5B8C\u6210", cls: "ok" },
3881
3888
  error: { label: "\u51FA\u9519", cls: "err" },
@@ -4109,12 +4116,12 @@ function RobotScene(props) {
4109
4116
  ] });
4110
4117
  }
4111
4118
  var SOUND_LIBRARY = {
4112
- chime: { name: "\u6E05\u8106\u53CC\u97F3", notes: { done: [[659.25, 0, 0.14], [880, 0.13, 0.24]], err: [[220, 0, 0.16], [164.81, 0.15, 0.3]] } },
4113
- ding: { name: "\u53EE", notes: { done: [[987.77, 0, 0.35]], err: [[246.94, 0, 0.4]] } },
4114
- coin: { name: "\u91D1\u5E01", notes: { done: [[988, 0, 0.08], [1319, 0.08, 0.35], [988, 0, 0.08, "square"], [1319, 0.08, 0.3, "square"]], err: [[196, 0, 0.12], [147, 0.11, 0.35]] } },
4115
- bell: { name: "\u949F\u58F0", notes: { done: [[523.25, 0, 0.5], [659.25, 0.02, 0.45], [783.99, 0.04, 0.4]], err: [[174.61, 0, 0.5], [130.81, 0.05, 0.5]] } },
4116
- pulse: { name: "\u8109\u51B2", notes: { done: [[440, 0, 0.09], [440, 0.14, 0.09], [440, 0.28, 0.16]], err: [[174.61, 0, 0.1], [174.61, 0.14, 0.1], [174.61, 0.28, 0.18]] } },
4117
- arp: { name: "\u7436\u97F3", notes: { done: [[523.25, 0, 0.12], [659.25, 0.09, 0.12], [783.99, 0.18, 0.12], [1046.5, 0.27, 0.3]], err: [[392, 0, 0.12], [329.63, 0.1, 0.12], [261.63, 0.2, 0.12], [196, 0.3, 0.32]] } }
4119
+ chime: { name: "\u6E05\u8106\u53CC\u97F3", notes: { done: [[659.25, 0, 0.14], [880, 0.13, 0.24]], err: [[220, 0, 0.16], [164.81, 0.15, 0.3]], ask: [[587.33, 0, 0.16], [880, 0.19, 0.34]] } },
4120
+ ding: { name: "\u53EE", notes: { done: [[987.77, 0, 0.35]], err: [[246.94, 0, 0.4]], ask: [[783.99, 0, 0.18], [1174.66, 0.2, 0.42]] } },
4121
+ coin: { name: "\u91D1\u5E01", notes: { done: [[988, 0, 0.08], [1319, 0.08, 0.35], [988, 0, 0.08, "square"], [1319, 0.08, 0.3, "square"]], err: [[196, 0, 0.12], [147, 0.11, 0.35]], ask: [[880, 0, 0.09], [1108.73, 0.1, 0.12], [1318.51, 0.22, 0.3]] } },
4122
+ bell: { name: "\u949F\u58F0", notes: { done: [[523.25, 0, 0.5], [659.25, 0.02, 0.45], [783.99, 0.04, 0.4]], err: [[174.61, 0, 0.5], [130.81, 0.05, 0.5]], ask: [[659.25, 0, 0.4], [523.25, 0.05, 0.55]] } },
4123
+ pulse: { name: "\u8109\u51B2", notes: { done: [[440, 0, 0.09], [440, 0.14, 0.09], [440, 0.28, 0.16]], err: [[174.61, 0, 0.1], [174.61, 0.14, 0.1], [174.61, 0.28, 0.18]], ask: [[523.25, 0, 0.09], [523.25, 0.15, 0.09], [659.25, 0.3, 0.22]] } },
4124
+ arp: { name: "\u7436\u97F3", notes: { done: [[523.25, 0, 0.12], [659.25, 0.09, 0.12], [783.99, 0.18, 0.12], [1046.5, 0.27, 0.3]], err: [[392, 0, 0.12], [329.63, 0.1, 0.12], [261.63, 0.2, 0.12], [196, 0.3, 0.32]], ask: [[440, 0, 0.11], [554.37, 0.12, 0.11], [659.25, 0.24, 0.11], [880, 0.36, 0.34]] } }
4118
4125
  };
4119
4126
  var soundCtx = null;
4120
4127
  function playTone(seq) {
@@ -4146,6 +4153,10 @@ function playDoneSound(success, effect) {
4146
4153
  const lib = SOUND_LIBRARY[effect] || SOUND_LIBRARY.chime;
4147
4154
  playTone(success ? lib.notes.done : lib.notes.err);
4148
4155
  }
4156
+ function playAskSound(effect) {
4157
+ const lib = SOUND_LIBRARY[effect] || SOUND_LIBRARY.chime;
4158
+ playTone(lib.notes.ask || lib.notes.done);
4159
+ }
4149
4160
  function previewSound(effect) {
4150
4161
  const lib = SOUND_LIBRARY[effect] || SOUND_LIBRARY.chime;
4151
4162
  playTone(lib.notes.done);
@@ -4168,11 +4179,14 @@ function Toast(props) {
4168
4179
  const timer = setTimeout(() => props.onClose(t.id), 240);
4169
4180
  return () => clearTimeout(timer);
4170
4181
  }, [closing]);
4171
- const markColor = t.kind === "success" ? "var(--dk-ok)" : t.kind === "error" ? "var(--dk-err)" : "var(--dk-warn)";
4172
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-toast" + (closing ? " out" : ""), children: [
4182
+ const markColor = t.kind === "success" ? "var(--dk-ok)" : t.kind === "error" ? "var(--dk-err)" : t.kind === "confirm" ? "var(--dk-warn)" : "var(--dk-warn)";
4183
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-toast" + (t.kind === "confirm" ? " dkan-toast-confirm" : "") + (closing ? " out" : ""), children: [
4173
4184
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-toast-head", children: [
4174
4185
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-toast-mark", style: { background: markColor } }),
4175
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-toast-title", title: t.title, children: t.title }),
4186
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dkan-toast-title", title: t.title, children: [
4187
+ t.kind === "confirm" ? "\u270B " : "",
4188
+ t.title
4189
+ ] }),
4176
4190
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dkan-toast-close", "aria-label": "\u5173\u95ED", onClick: close, children: "\u2715" })
4177
4191
  ] }),
4178
4192
  t.body ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-toast-body", children: t.body }) : null
@@ -4376,6 +4390,24 @@ function visibleRect(el) {
4376
4390
  const r = el.getBoundingClientRect();
4377
4391
  return r.width > 0 && r.height > 0 && r.bottom > 0 && r.top < window.innerHeight ? r : null;
4378
4392
  }
4393
+ function currentSessionTitle() {
4394
+ if (typeof document === "undefined") return "";
4395
+ for (const el of document.querySelectorAll('[role="treeitem"][aria-selected="true"]')) {
4396
+ const r = el.getBoundingClientRect();
4397
+ if (r.width > 0 && r.height > 0 && r.right <= window.innerWidth * 0.35) {
4398
+ const text = (el.textContent || "").trim();
4399
+ if (text) return text;
4400
+ }
4401
+ }
4402
+ return "";
4403
+ }
4404
+ function taskOnScreen(task, titleCache) {
4405
+ const title = task && (task.title || "");
4406
+ if (!title) return true;
4407
+ const current = titleCache !== void 0 ? titleCache : currentSessionTitle();
4408
+ if (!current) return true;
4409
+ return current === title || current.indexOf(title) !== -1 || title.indexOf(current) !== -1;
4410
+ }
4379
4411
  var COMPOSER_SEND_SELECTOR = 'button[aria-label="Send message"],button[aria-label*="\u53D1\u9001"],button[data-testid*="send"],button[data-testid*="submit"],button[type="submit"]';
4380
4412
  function conversationComposer() {
4381
4413
  if (typeof document === "undefined" || typeof window === "undefined") return null;
@@ -4445,7 +4477,6 @@ function useSpaceDocks(enabled) {
4445
4477
  let frame = 0;
4446
4478
  let idle = 0;
4447
4479
  let timer = 0;
4448
- let pollTimer = 0;
4449
4480
  let retries = 0;
4450
4481
  let pending = false;
4451
4482
  let observedComposer = null;
@@ -4501,7 +4532,25 @@ function useSpaceDocks(enabled) {
4501
4532
  }) : null;
4502
4533
  if (mutationObserver && document.body) mutationObserver.observe(document.body, { childList: true, subtree: true });
4503
4534
  schedule(0, true);
4504
- pollTimer = window.setInterval(() => schedule(), 700);
4535
+ let calibrationInterval = 700;
4536
+ let lastActivityAt = Date.now();
4537
+ const markActivity = () => {
4538
+ lastActivityAt = Date.now();
4539
+ if (calibrationInterval !== 700) {
4540
+ calibrationInterval = 700;
4541
+ restartPoll();
4542
+ }
4543
+ };
4544
+ const unsubscribeActivity = animationStore.subscribe(markActivity);
4545
+ let pollTimer = 0;
4546
+ function restartPoll() {
4547
+ if (pollTimer) window.clearInterval(pollTimer);
4548
+ pollTimer = window.setInterval(() => {
4549
+ if (Date.now() - lastActivityAt > 2e4 && calibrationInterval !== 5e3) calibrationInterval = 5e3;
4550
+ schedule();
4551
+ }, calibrationInterval);
4552
+ }
4553
+ restartPoll();
4505
4554
  const onResize = () => {
4506
4555
  if (idle && typeof window.cancelIdleCallback === "function") window.cancelIdleCallback(idle);
4507
4556
  if (timer) window.clearTimeout(timer);
@@ -4517,6 +4566,7 @@ function useSpaceDocks(enabled) {
4517
4566
  if (pollTimer) window.clearInterval(pollTimer);
4518
4567
  if (mutationObserver) mutationObserver.disconnect();
4519
4568
  if (resizeObserver) resizeObserver.disconnect();
4569
+ unsubscribeActivity();
4520
4570
  window.removeEventListener("resize", onResize);
4521
4571
  };
4522
4572
  }, [enabled]);
@@ -4597,13 +4647,14 @@ function MissionDeparture(props) {
4597
4647
  }, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FreighterHull, { units: 3, empty: true }) });
4598
4648
  }
4599
4649
  function ProgrammingStuck(props) {
4600
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-space-stuck", "aria-hidden": "true", children: [
4650
+ const waiting = !!props.waiting;
4651
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-space-stuck" + (waiting ? " waiting" : ""), "aria-hidden": "true", children: [
4601
4652
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FreighterHull, { units: 3, empty: true }),
4602
4653
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dkan-stuck-code", children: [
4603
4654
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", {}),
4604
4655
  `{ }`
4605
4656
  ] }),
4606
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "\u7F16\u7A0B\u7B49\u5F85\u4E2D" }),
4657
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: waiting ? "\u7B49\u5F85\u786E\u8BA4" : "\u7F16\u7A0B\u7B49\u5F85\u4E2D" }),
4607
4658
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: truncate(props.task && props.task.title || "\u5DE5\u5177\u6682\u65E0\u65B0\u54CD\u5E94", 28) })
4608
4659
  ] });
4609
4660
  }
@@ -4725,6 +4776,56 @@ function AnimationOverlay(props) {
4725
4776
  }
4726
4777
  prevActiveRef.current = st2.active.slice();
4727
4778
  }, [snap.status]);
4779
+ const remindedApprovalsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
4780
+ (0, import_react8.useEffect)(() => {
4781
+ const st2 = snap.status;
4782
+ if (!st2 || !st2.active) return;
4783
+ const cfg2 = st2.config || {};
4784
+ const activeIds = new Set(st2.active.map((x) => x.sessionId));
4785
+ for (const [sid, seen] of remindedApprovalsRef.current) {
4786
+ if (!activeIds.has(sid)) remindedApprovalsRef.current.delete(sid);
4787
+ }
4788
+ for (const task of st2.active) {
4789
+ const approvals = Array.isArray(task.approvals) ? task.approvals : [];
4790
+ if (approvals.length === 0) continue;
4791
+ let seen = remindedApprovalsRef.current.get(task.sessionId);
4792
+ if (!seen) {
4793
+ seen = /* @__PURE__ */ new Set();
4794
+ remindedApprovalsRef.current.set(task.sessionId, seen);
4795
+ }
4796
+ const fresh = approvals.filter((a) => a && !seen.has(a.id));
4797
+ if (fresh.length === 0) continue;
4798
+ for (const a of fresh) seen.add(a.id);
4799
+ if (!cfg2.notifyEnabled || cfg2.notifyOnConfirm === false) continue;
4800
+ const toolName = fresh[0].toolName || "";
4801
+ const scene = toolLabel(toolName);
4802
+ const lines = [
4803
+ "\u4EFB\u52A1\uFF1A" + (task.title || "(\u65E0\u6807\u9898)"),
4804
+ "\u5DE5\u5177\uFF1A" + (toolName || "\u672A\u77E5") + (scene ? "\uFF08" + scene + "\uFF09" : "")
4805
+ ];
4806
+ if (fresh[0].reason) lines.push("\u8BF4\u660E\uFF1A" + truncate(fresh[0].reason, 140));
4807
+ lines.push(fresh.length > 1 ? "\u5171 " + fresh.length + " \u9879\u7B49\u5F85\u4F60\u7684\u786E\u8BA4" : "\u8BF7\u5728\u4F1A\u8BDD\u91CC\u786E\u8BA4\u540E\u7EE7\u7EED");
4808
+ const toast = {
4809
+ id: Date.now() + Math.random(),
4810
+ kind: "confirm",
4811
+ title: "\u4EFB\u52A1\u9700\u8981\u786E\u8BA4",
4812
+ body: lines.join("\n"),
4813
+ // 需确认常驻到用户处理(notifyStayMs=0 同款语义),关掉开关的间隙也不会自动消失
4814
+ stayMs: 0
4815
+ };
4816
+ setToasts((prev) => prev.concat([toast]).slice(-4));
4817
+ if (cfg2.soundNotify !== false) playAskSound(cfg2.soundEffect);
4818
+ if (cfg2.systemNotify && typeof document !== "undefined" && document.hidden && typeof Notification !== "undefined" && Notification.permission === "granted") {
4819
+ try {
4820
+ new Notification("dsh \u4EFB\u52A1\u9700\u8981\u786E\u8BA4", {
4821
+ body: (task.title || "(\u65E0\u6807\u9898)") + " \xB7 " + (toolName || "\u5DE5\u5177") + " \u7B49\u5F85\u786E\u8BA4",
4822
+ tag: "dsh-dock-approval-" + task.sessionId
4823
+ });
4824
+ } catch {
4825
+ }
4826
+ }
4827
+ }
4828
+ }, [snap.status]);
4728
4829
  const handleTaskEnd = (task, record, cfg2) => {
4729
4830
  const reason = record && record.endReason || "";
4730
4831
  const success = isSuccessReason(reason);
@@ -4795,6 +4896,18 @@ function AnimationOverlay(props) {
4795
4896
  const animOn = !!(cfg && cfg.animationEnabled && active.length > 0);
4796
4897
  const mode = cfg && cfg.effectMode ? cfg.effectMode : "flow";
4797
4898
  const phase = active.length > 0 ? active.reduce((best, x) => !best || (x.phaseAt || 0) > (best.phaseAt || 0) ? x : best, null).phase : "think";
4899
+ const waitingTask = active.find((x) => Array.isArray(x.approvals) && x.approvals.length > 0) || null;
4900
+ const sessionTitleRef = (0, import_react8.useRef)({ at: 0, title: "" });
4901
+ const currentTitle = (0, import_react8.useMemo)(() => {
4902
+ const c = sessionTitleRef.current;
4903
+ if (typeof window === "undefined") return "";
4904
+ if (Date.now() - c.at < 1e3) return c.title;
4905
+ c.at = Date.now();
4906
+ c.title = currentSessionTitle();
4907
+ return c.title;
4908
+ }, [snap.status]);
4909
+ const onScreenTasks = (0, import_react8.useMemo)(() => active.filter((x) => taskOnScreen(x, currentTitle)), [active, currentTitle]);
4910
+ const waitingTaskOnScreen = onScreenTasks.find((x) => Array.isArray(x.approvals) && x.approvals.length > 0) || null;
4798
4911
  useTicker(ctx, animOn);
4799
4912
  const now = Date.now();
4800
4913
  const elapsed = active.length > 0 ? fmtClock(now - Math.min(...active.map((x) => x.startTime || now))) : "";
@@ -4828,7 +4941,7 @@ function AnimationOverlay(props) {
4828
4941
  const spaceModeOn = !!(cfg && cfg.animationEnabled && mode === "space" && !panelOpen);
4829
4942
  const spaceDocks = useSpaceDocks(spaceModeOn);
4830
4943
  const spaceGalaxies = spaceModeOn && spaceDocks ? spaceGalaxyLayout() : null;
4831
- const stuckTask = active.find((task) => task.phase === "code" && now - (task.lastActivityAt || task.phaseAt || now) >= 18e3) || null;
4944
+ const stuckTask = onScreenTasks.find((task) => task.phase === "code" && now - (task.lastActivityAt || task.phaseAt || now) >= 18e3) || null;
4832
4945
  const robotScaleFromConfig = Math.max(0.85, Math.min(2.2, Number(cfg && cfg.robotScale) || 1.35));
4833
4946
  const speedStyle = { "--dkan-speed": Number(speed).toFixed(2) };
4834
4947
  const saveRobotScale = (0, import_react8.useCallback)((robotScale) => {
@@ -4943,7 +5056,7 @@ function AnimationOverlay(props) {
4943
5056
  ambientOn && mode === "flow" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-line", style: speedStyle, "aria-hidden": "true" }) : null,
4944
5057
  ambientOn && AMBIENT_EFFECT_MODES.has(mode) && (mode !== "space" || spaceGalaxies) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AmbientLayer, { mode, phase, speed: Number(speed).toFixed(2), tasks: active, galaxies: spaceGalaxies }) : null,
4945
5058
  spaceModeOn && spaceDocks && active.length === 0 && deliveries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ReadyFreighter, { dock: spaceDocks.ready }) : null,
4946
- spaceModeOn && stuckTask ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ProgrammingStuck, { task: stuckTask }) : null,
5059
+ spaceModeOn && stuckTask ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ProgrammingStuck, { task: stuckTask, waiting: !!(stuckTask.approvals && stuckTask.approvals.length) }) : null,
4947
5060
  !panelOpen ? departures.map((departure) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MissionDeparture, { departure }, departure.id)) : null,
4948
5061
  ambientOn && mode === "orbit" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-orbit", style: speedStyle, "aria-hidden": "true" }) : null,
4949
5062
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BurstLayer, { bursts }),
@@ -4953,8 +5066,8 @@ function AnimationOverlay(props) {
4953
5066
  className: "dkan-botcard",
4954
5067
  role: "button",
4955
5068
  tabIndex: 0,
4956
- style: botCardStyle,
4957
- title: active.length + " \u4E2A\u4EFB\u52A1 \xB7 " + phaseLabel(phase) + " \xB7 \u70B9\u51FB\u67E5\u770B\u4EFB\u52A1\u52A8\u753B\u9875 \xB7 \u53EF\u62D6\u52A8\u5230\u4EFB\u610F\u4F4D\u7F6E",
5069
+ style: Object.assign({}, botCardStyle, waitingTask ? { "--dkan-phase": "#b45309" } : null),
5070
+ title: active.length + " \u4E2A\u4EFB\u52A1 \xB7 " + (waitingTask ? "\u7B49\u5F85\u786E\u8BA4" : phaseLabel(phase)) + " \xB7 \u70B9\u51FB\u67E5\u770B\u4EFB\u52A1\u52A8\u753B\u9875 \xB7 \u53EF\u62D6\u52A8\u5230\u4EFB\u610F\u4F4D\u7F6E",
4958
5071
  onPointerDown: onBotPointerDown,
4959
5072
  onPointerMove: onBotPointerMove,
4960
5073
  onPointerUp: onBotPointerUp,
@@ -4973,7 +5086,7 @@ function AnimationOverlay(props) {
4973
5086
  " \u4E2A\u4EFB\u52A1",
4974
5087
  elapsed ? " \xB7 " + elapsed : "",
4975
5088
  " \xB7 ",
4976
- phaseLabel(phase)
5089
+ waitingTask ? "\u7B49\u5F85\u786E\u8BA4" : phaseLabel(phase)
4977
5090
  ] }),
4978
5091
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
4979
5092
  "button",
@@ -5004,8 +5117,8 @@ function AnimationOverlay(props) {
5004
5117
  {
5005
5118
  type: "button",
5006
5119
  className: "dkan-badge" + (mode === "breathe" ? " dkan-badge-breathe" : ""),
5007
- style: Object.assign({}, speedStyle, { "--dkan-phase": phaseColor(phase) }),
5008
- title: active.length + " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D \xB7 " + phaseLabel(phase) + " \xB7 \u70B9\u51FB\u67E5\u770B\u4EFB\u52A1\u52A8\u753B\u9875",
5120
+ style: Object.assign({}, speedStyle, { "--dkan-phase": phaseColor(waitingTask ? "code" : phase) }),
5121
+ title: active.length + " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D \xB7 " + (waitingTask ? "\u7B49\u5F85\u786E\u8BA4" : phaseLabel(phase)) + " \xB7 \u70B9\u51FB\u67E5\u770B\u4EFB\u52A1\u52A8\u753B\u9875",
5009
5122
  onClick: () => openPanel("animation"),
5010
5123
  children: [
5011
5124
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dkan-dotwrap", children: [
@@ -5018,7 +5131,7 @@ function AnimationOverlay(props) {
5018
5131
  " \u4E2A\u4EFB\u52A1",
5019
5132
  elapsed ? " \xB7 " + elapsed : "",
5020
5133
  " \xB7 ",
5021
- phaseLabel(phase)
5134
+ waitingTask ? "\u7B49\u5F85\u786E\u8BA4" : phaseLabel(phase)
5022
5135
  ] })
5023
5136
  ]
5024
5137
  }
@@ -5093,10 +5206,11 @@ function ModePreview({ id }) {
5093
5206
  function TaskRow(props) {
5094
5207
  const t = props.t;
5095
5208
  const info = endInfo(t.endReason);
5209
+ const waiting = !props.done && Array.isArray(t.approvals) && t.approvals.length > 0;
5096
5210
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-task", children: [
5097
5211
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-task-head", children: [
5098
5212
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-task-title", title: t.title, children: t.title || "(\u65E0\u6807\u9898)" }),
5099
- props.done ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-tag " + info.cls, children: info.label }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-tag on", children: phaseLabel(t.phase) })
5213
+ props.done ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-tag " + info.cls, children: info.label }) : waiting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-tag warn", children: "\u7B49\u5F85\u786E\u8BA4" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-tag on", children: phaseLabel(t.phase) })
5100
5214
  ] }),
5101
5215
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-task-meta", children: [
5102
5216
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: (t.models && t.models.length ? t.models.join(", ") : "\u672A\u77E5") + (t.provider ? "\uFF08" + t.provider + "\uFF09" : "") }),
@@ -5105,6 +5219,7 @@ function TaskRow(props) {
5105
5219
  t.totalTokens ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u21A7" + fmtCompact2(t.inputTokens) + " \u21A5" + fmtCompact2(t.outputTokens) }) : null,
5106
5220
  props.done && t.endTime ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: fmtTime2(t.endTime) }) : null
5107
5221
  ] }),
5222
+ waiting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-task-err", children: t.approvals.map((a, i) => (a && a.toolName ? a.toolName : "\u672A\u77E5\u5DE5\u5177") + (a && a.reason ? "\uFF1A" + truncate(a.reason, 80) : "")).join("\uFF1B") }) : null,
5108
5223
  props.done && t.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-task-err", children: truncate(t.errorMessage, 120) }) : null
5109
5224
  ] });
5110
5225
  }
@@ -5232,6 +5347,7 @@ function AnimationView(props) {
5232
5347
  if (!cfg) {
5233
5348
  rows.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-note", children: snap.error ? "\u72B6\u6001\u4E0D\u53EF\u7528\uFF1A" + snap.error : snap.loading ? "\u6B63\u5728\u62C9\u53D6\u4EFB\u52A1\u72B6\u6001\u2026" : "\u7B49\u5F85\u4EFB\u52A1\u72B6\u6001" }, "load"));
5234
5349
  } else {
5350
+ const waitingCount2 = active.reduce((n, t) => n + (Array.isArray(t.approvals) ? t.approvals.length : 0), 0);
5235
5351
  rows.push(
5236
5352
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-sec", children: [
5237
5353
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-sec-head", children: [
@@ -5351,6 +5467,19 @@ function AnimationView(props) {
5351
5467
  ),
5352
5468
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-row-sub", children: "\u51FA\u9519 / \u4E2D\u6B62 / \u8FBE\u8F93\u51FA\u4E0A\u9650\u65F6\u901A\u77E5" })
5353
5469
  ] }),
5470
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-row", children: [
5471
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-row-label", children: "\u9700\u786E\u8BA4\u63D0\u9192" }),
5472
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5473
+ "button",
5474
+ {
5475
+ type: "button",
5476
+ className: "dkm-miniswitch" + (cfg.notifyOnConfirm !== false ? " on" : ""),
5477
+ onClick: () => patch({ notifyOnConfirm: cfg.notifyOnConfirm === false }),
5478
+ children: cfg.notifyOnConfirm !== false ? "\u5F00" : "\u5173"
5479
+ }
5480
+ ),
5481
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-row-sub", children: "\u5DE5\u5177\u7B49\u5F85\u4F60\u6279\u51C6\u65F6\u5F39\u5361\u7247\u5E76\u54CD\u786E\u8BA4\u97F3\uFF08\u5E38\u9A7B\u4E0D\u81EA\u52A8\u6D88\u5931\uFF09" })
5482
+ ] }),
5354
5483
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-row", children: [
5355
5484
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-row-label", children: "\u505C\u7559\u65F6\u957F" }),
5356
5485
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
@@ -5393,7 +5522,7 @@ function AnimationView(props) {
5393
5522
  children: cfg.soundNotify !== false ? "\u5F00" : "\u5173"
5394
5523
  }
5395
5524
  ),
5396
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-row-sub", children: "\u4EFB\u52A1\u7ED3\u675F\u65F6\u64AD\u653E\uFF08\u8BD5\u542C\u4E3A\u5148\u64AD\u5B8C\u6210\u97F3\u3001\u540E\u64AD\u5F02\u5E38\u97F3\uFF09" })
5525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-row-sub", children: "\u4EFB\u52A1\u7ED3\u675F/\u9700\u786E\u8BA4\u65F6\u64AD\u653E\uFF08\u8BD5\u542C\u4E3A\u5148\u64AD\u5B8C\u6210\u97F3\u3001\u540E\u64AD\u5F02\u5E38\u97F3\uFF09" })
5397
5526
  ] }),
5398
5527
  cfg.soundNotify !== false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-sounds", children: Object.keys(SOUND_LIBRARY).map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5399
5528
  "button",
@@ -5558,7 +5687,7 @@ function AnimationView(props) {
5558
5687
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-sec", children: [
5559
5688
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-sec-head", children: [
5560
5689
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-sec-title", children: "\u8FD0\u884C\u72B6\u6001" }),
5561
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-sec-sub", children: snap.error ? "\u72B6\u6001\u62C9\u53D6\u5931\u8D25\uFF1A" + snap.error : active.length > 0 ? active.length + " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D" : recent.length > 0 ? "\u7A7A\u95F2 \xB7 \u663E\u793A\u6700\u8FD1\u5B8C\u6210" : "\u7A7A\u95F2 \xB7 \u6682\u65E0\u4EFB\u52A1\u8BB0\u5F55" }),
5690
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dkan-sec-sub", children: snap.error ? "\u72B6\u6001\u62C9\u53D6\u5931\u8D25\uFF1A" + snap.error : waitingCount > 0 ? waitingCount + " \u9879\u7B49\u5F85\u786E\u8BA4 \xB7 " + active.length + " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D" : active.length > 0 ? active.length + " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D" : recent.length > 0 ? "\u7A7A\u95F2 \xB7 \u663E\u793A\u6700\u8FD1\u5B8C\u6210" : "\u7A7A\u95F2 \xB7 \u6682\u65E0\u4EFB\u52A1\u8BB0\u5F55" }),
5562
5691
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dkan-refresh", onClick: () => animationStore.refresh(), children: snap.loading ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0" })
5563
5692
  ] }),
5564
5693
  active.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-tasks", children: active.map((t) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TaskRow, { t }, t.sessionId)) }) : null,
@@ -5630,7 +5759,9 @@ var css2 = [
5630
5759
  ".dkan-delivery-label{position:absolute;left:21px;top:58px;display:flex;align-items:baseline;gap:6px;padding:4px 8px;border:1px solid color-mix(in srgb,#93c5fd 30%,var(--dsw-alias-border-l1));border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 82%,transparent);backdrop-filter:blur(10px);color:var(--dsw-alias-label-secondary);font:10px/1.2 var(--ds-font-family,system-ui,sans-serif);white-space:nowrap;}.dkan-delivery-label b{color:var(--dsw-alias-label-primary);font-weight:650}.dkan-delivery-label span{color:var(--dk-accent);}",
5631
5760
  "@media (max-width:1100px){.dkan-space-system,.dkan-space-runner{display:none}}",
5632
5761
  ".dkan-ready-freighter{position:fixed;width:210px;height:88px;z-index:9994;pointer-events:none;filter:drop-shadow(0 10px 14px rgb(0 0 0 / .18));}.dkan-ready-freighter>span:last-child{position:absolute;left:21px;top:58px;padding:4px 8px;border:1px solid color-mix(in srgb,#93c5fd 28%,var(--dsw-alias-border-l1));border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 80%,transparent);backdrop-filter:blur(10px);color:var(--dsw-alias-label-secondary);font:10px/1.2 var(--ds-font-family,system-ui,sans-serif);white-space:nowrap;}.dkan-departure{position:fixed;left:0;top:0;width:210px;height:88px;z-index:9994;pointer-events:none;will-change:transform,opacity;contain:layout paint style;isolation:isolate;animation:dkan-departure-flight var(--dkan-departure-duration,8600ms) cubic-bezier(.42,0,.85,.45) forwards;}@keyframes dkan-departure-flight{0%{opacity:0;transform:translate3d(var(--dkan-from-x),var(--dkan-from-y),0) scale(1) rotate(var(--dkan-flight-angle,0deg))}6%{opacity:1;transform:translate3d(var(--dkan-from-x),var(--dkan-from-y),0) scale(1) rotate(var(--dkan-flight-angle,0deg))}100%{opacity:0;transform:translate3d(var(--dkan-to-x),var(--dkan-to-y),0) scale(.24) rotate(var(--dkan-flight-angle,0deg))}}",
5633
- ".dkan-space-stuck{position:fixed;left:50%;top:50%;width:244px;height:160px;z-index:9993;pointer-events:none;transform:translate(-50%,-50%);border:1px solid color-mix(in srgb,#f59e0b 48%,var(--dsw-alias-border-l1));border-radius:20px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 88%,transparent);backdrop-filter:blur(16px);box-shadow:0 18px 54px rgb(0 0 0 / .28),0 0 30px color-mix(in srgb,#f59e0b 15%,transparent);}.dkan-space-stuck .dkan-freighter{left:37px;top:11px;transform:scale(.9);transform-origin:top left;}.dkan-stuck-code{position:absolute;left:17px;right:17px;top:60px;height:36px;border-radius:7px;background:linear-gradient(90deg,color-mix(in srgb,#f59e0b 18%,transparent),transparent);color:var(--dk-warn);font:700 14px/36px var(--ds-font-family-code,monospace);letter-spacing:.12em;text-align:center;overflow:hidden;}.dkan-stuck-code::after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,transparent,color-mix(in srgb,#fef3c7 70%,transparent),transparent);transform:translateX(-110%);animation:dkan-stuck-scan 1.25s linear infinite;}.dkan-stuck-code i{display:inline-block;width:7px;height:7px;margin-right:8px;border-radius:50%;background:#f59e0b;box-shadow:0 0 9px #f59e0b;animation:dkan-stuck-pulse .8s ease-in-out infinite alternate;}.dkan-space-stuck strong{position:absolute;left:0;right:0;top:108px;color:var(--dsw-alias-label-primary);font:650 13px/1.2 var(--ds-font-family,system-ui,sans-serif);text-align:center;}.dkan-space-stuck small{position:absolute;left:18px;right:18px;top:128px;color:var(--dsw-alias-label-secondary);font:11px/1.25 var(--ds-font-family,system-ui,sans-serif);text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}@keyframes dkan-stuck-scan{to{transform:translateX(110%)}}@keyframes dkan-stuck-pulse{to{transform:scale(.55);opacity:.45}}",
5762
+ ".dkan-space-stuck{position:fixed;right:20px;top:88px;width:244px;height:160px;z-index:9993;pointer-events:none;border:1px solid color-mix(in srgb,#f59e0b 48%,var(--dsw-alias-border-l1));border-radius:20px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 88%,transparent);backdrop-filter:blur(16px);box-shadow:0 18px 54px rgb(0 0 0 / .28),0 0 30px color-mix(in srgb,#f59e0b 15%,transparent);}.dkan-space-stuck .dkan-freighter{left:37px;top:11px;transform:scale(.9);transform-origin:top left;}.dkan-stuck-code{position:absolute;left:17px;right:17px;top:60px;height:36px;border-radius:7px;background:linear-gradient(90deg,color-mix(in srgb,#f59e0b 18%,transparent),transparent);color:var(--dk-warn);font:700 14px/36px var(--ds-font-family-code,monospace);letter-spacing:.12em;text-align:center;overflow:hidden;}.dkan-stuck-code::after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,transparent,color-mix(in srgb,#fef3c7 70%,transparent),transparent);transform:translateX(-110%);animation:dkan-stuck-scan 1.25s linear infinite;}.dkan-stuck-code i{display:inline-block;width:7px;height:7px;margin-right:8px;border-radius:50%;background:#f59e0b;box-shadow:0 0 9px #f59e0b;animation:dkan-stuck-pulse .8s ease-in-out infinite alternate;}.dkan-space-stuck strong{position:absolute;left:0;right:0;top:108px;color:var(--dsw-alias-label-primary);font:650 13px/1.2 var(--ds-font-family,system-ui,sans-serif);text-align:center;}.dkan-space-stuck small{position:absolute;left:18px;right:18px;top:128px;color:var(--dsw-alias-label-secondary);font:11px/1.25 var(--ds-font-family,system-ui,sans-serif);text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}@keyframes dkan-stuck-scan{to{transform:translateX(110%)}}@keyframes dkan-stuck-pulse{to{transform:scale(.55);opacity:.45}}",
5763
+ // 窄屏(手机/分屏)贴右会溢出:回落到水平居中、屏幕上方,避开正文重心
5764
+ "@media (max-width:760px){.dkan-space-stuck{right:50%;transform:translateX(50%);top:64px;}}",
5634
5765
  // 极光:顶部柔光带呼吸流动(加浓)
5635
5766
  ".dkan-aurora i{position:absolute;top:-40%;left:-20%;width:70%;height:80%;border-radius:50%;filter:blur(50px);opacity:.32;animation:dkan-aurora ease-in-out infinite alternate;}",
5636
5767
  ".dkan-aurora i:nth-child(1){background:#4d9fff;}",
@@ -5719,6 +5850,9 @@ var css2 = [
5719
5850
  ".dkan-toast-close{cursor:pointer;flex:none;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:6px;width:22px;height:22px;font-size:11px;line-height:1;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;}",
5720
5851
  ".dkan-toast-close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
5721
5852
  ".dkan-toast-body{font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.7;white-space:pre-line;word-break:break-word;}",
5853
+ // 需确认卡片:琥珀描边 + 缓慢呼吸光晕,常驻提醒直到用户处理
5854
+ ".dkan-toast-confirm{border-color:color-mix(in srgb,var(--dk-warn) 55%,transparent);animation:dkan-toast-in .32s var(--ds-ease-in-out),dkan-confirm-glow 2.4s ease-in-out 0.4s infinite;}",
5855
+ "@keyframes dkan-confirm-glow{0%,100%{box-shadow:0 10px 36px rgb(0 0 0 / .22),0 0 0 0 color-mix(in srgb,var(--dk-warn) 30%,transparent)}50%{box-shadow:0 10px 36px rgb(0 0 0 / .22),0 0 14px 2px color-mix(in srgb,var(--dk-warn) 32%,transparent)}}",
5722
5856
  // 面板页布局
5723
5857
  ".dkan-root{display:flex;flex-direction:column;gap:10px;}",
5724
5858
  ".dkan-note{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6;}",
@@ -6151,11 +6285,16 @@ function rotations(shape) {
6151
6285
  }
6152
6286
  var PIECES = SHAPES.map((s) => ({ color: s.color, rots: rotations(s) }));
6153
6287
  var TETRIS_GRID = (() => {
6154
- const lines = [];
6288
+ const parts = [];
6155
6289
  let k = 0;
6156
- 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++));
6157
- 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++));
6158
- return lines;
6290
+ for (let j = 0; j < 20; j += 1) {
6291
+ for (let i = 0; i < 10; i += 1) {
6292
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { className: "dgame-tetris-pit", x: i * 100 + 7, y: j * 100 + 7, width: 86, height: 86, rx: 10 }, k++));
6293
+ }
6294
+ }
6295
+ for (let i = 1; i < 10; i += 1) parts.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: i * 100, y1: 0, x2: i * 100, y2: 2e3 }, k++));
6296
+ for (let j = 1; j < 20; j += 1) parts.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: j * 100, x2: 1e3, y2: j * 100 }, k++));
6297
+ return parts;
6159
6298
  })();
6160
6299
  function emptyBoard() {
6161
6300
  return Array.from({ length: ROWS }, () => Array(COLS).fill(null));
@@ -6461,7 +6600,7 @@ function TetrisPreview() {
6461
6600
  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))) }) });
6462
6601
  }
6463
6602
  var tetrisGame = { Game: TetrisGame, Preview: TetrisPreview, css: `
6464
- .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)}}
6603
+ .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{position:relative;z-index:1;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:rgb(96 165 250/.28);stroke-width:3}.dgame-tetris-lines .dgame-tetris-pit{fill:rgb(0 0 0/.42);stroke:rgb(255 255 255/.07);stroke-width:2}@media (max-width:680px){.dgame-tetris{width:100%;gap:8px}.dgame-tetris-board{width:min(100%,170px)}}
6465
6604
  ` };
6466
6605
 
6467
6606
  // features/games/games/sokoban.jsx
@@ -7314,6 +7453,18 @@ var XIANGQI_LINES = (() => {
7314
7453
  }
7315
7454
  return parts;
7316
7455
  })();
7456
+ var CN_NUMS = ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u4E03", "\u516B", "\u4E5D"];
7457
+ function moveNotation(board, fr, fc, tr, tc) {
7458
+ const p = board[fr][fc];
7459
+ if (!p) return "";
7460
+ const red = p.c === "r";
7461
+ const num = (n) => red ? CN_NUMS[n - 1] : String(n);
7462
+ const file = (c) => red ? 9 - c : c + 1;
7463
+ const forward = red ? tr < fr : tr > fr;
7464
+ const verb = fr === tr ? "\u5E73" : forward ? "\u8FDB" : "\u9000";
7465
+ const to = fr === tr || p.t === "h" || p.t === "e" || p.t === "a" ? file(tc) : Math.abs(tr - fr);
7466
+ return pieceChar(p) + num(file(fc)) + verb + num(to);
7467
+ }
7317
7468
  function XiangqiGame(props) {
7318
7469
  const [board, setBoard] = (0, import_react13.useState)(initialBoard);
7319
7470
  const [turn, setTurn] = (0, import_react13.useState)("r");
@@ -7322,6 +7473,9 @@ function XiangqiGame(props) {
7322
7473
  const [over, setOver] = (0, import_react13.useState)(null);
7323
7474
  const [check, setCheck] = (0, import_react13.useState)(false);
7324
7475
  const [last, setLast] = (0, import_react13.useState)(null);
7476
+ const [lastMove, setLastMove] = (0, import_react13.useState)(null);
7477
+ const [moveLog, setMoveLog] = (0, import_react13.useState)([]);
7478
+ const [moving, setMoving] = (0, import_react13.useState)(null);
7325
7479
  const [thinking, setThinking] = (0, import_react13.useState)(false);
7326
7480
  const [history, setHistory] = (0, import_react13.useState)([]);
7327
7481
  const [repKeys, setRepKeys] = (0, import_react13.useState)(() => [posKey(initialBoard())]);
@@ -7337,7 +7491,43 @@ function XiangqiGame(props) {
7337
7491
  thinkingRef.current = thinking;
7338
7492
  const historyRef = (0, import_react13.useRef)(history);
7339
7493
  historyRef.current = history;
7494
+ const movingRef = (0, import_react13.useRef)(false);
7495
+ const pendingRef = (0, import_react13.useRef)(null);
7496
+ const animTimerRef = (0, import_react13.useRef)(null);
7497
+ const animateMove = (0, import_react13.useCallback)((b, fr, fc, tr, tc, after) => {
7498
+ const piece = boardRef.current[fr][fc];
7499
+ const capture = !!boardRef.current[tr][tc];
7500
+ pendingRef.current = { board: b, after };
7501
+ movingRef.current = true;
7502
+ setSel(null);
7503
+ setTargets([]);
7504
+ setMoving({ fr, fc, tr, tc, piece, capture });
7505
+ playSfx(capture ? "capture" : "step");
7506
+ if (animTimerRef.current) clearTimeout(animTimerRef.current);
7507
+ animTimerRef.current = setTimeout(() => {
7508
+ animTimerRef.current = null;
7509
+ const pend = pendingRef.current;
7510
+ pendingRef.current = null;
7511
+ movingRef.current = false;
7512
+ setMoving(null);
7513
+ if (pend) {
7514
+ const text = moveNotation(boardRef.current, fr, fc, tr, tc);
7515
+ boardRef.current = pend.board;
7516
+ setBoard(pend.board);
7517
+ setLastMove({ fr, fc, tr, tc, text });
7518
+ setMoveLog((log) => log.concat({ side: piece.c, text }));
7519
+ setLast([tr, tc]);
7520
+ if (pend.after) pend.after();
7521
+ }
7522
+ }, 260);
7523
+ }, []);
7340
7524
  const restart = (0, import_react13.useCallback)(() => {
7525
+ if (animTimerRef.current) {
7526
+ clearTimeout(animTimerRef.current);
7527
+ animTimerRef.current = null;
7528
+ }
7529
+ pendingRef.current = null;
7530
+ movingRef.current = false;
7341
7531
  const b = initialBoard();
7342
7532
  boardRef.current = b;
7343
7533
  setBoard(b);
@@ -7349,10 +7539,13 @@ function XiangqiGame(props) {
7349
7539
  setLast(null);
7350
7540
  setThinking(false);
7351
7541
  setHistory([]);
7542
+ setLastMove(null);
7543
+ setMoveLog([]);
7544
+ setMoving(null);
7352
7545
  setRepKeys([posKey(b)]);
7353
7546
  }, []);
7354
7547
  const undo2 = (0, import_react13.useCallback)(() => {
7355
- if (thinkingRef.current || overRef.current || turnRef.current !== "r") return;
7548
+ if (thinkingRef.current || overRef.current || turnRef.current !== "r" || movingRef.current) return;
7356
7549
  const h = historyRef.current;
7357
7550
  if (h.length < 2) return;
7358
7551
  const hist = h.slice(0, -2);
@@ -7363,31 +7556,28 @@ function XiangqiGame(props) {
7363
7556
  setSel(null);
7364
7557
  setTargets([]);
7365
7558
  setLast(null);
7559
+ setLastMove(null);
7560
+ setMoveLog((prevLog) => prevLog.slice(0, -2));
7366
7561
  setOver(null);
7367
7562
  setCheck(inCheck(b, "r"));
7368
7563
  setRepKeys(hist.map(posKey).concat(posKey(b)));
7369
7564
  }, []);
7370
7565
  const moveRed = (0, import_react13.useCallback)((tr, tc) => {
7371
- if (turnRef.current !== "r" || overRef.current || !sel) return;
7566
+ if (turnRef.current !== "r" || overRef.current || !sel || movingRef.current) return;
7372
7567
  const fr = sel[0], fc = sel[1];
7373
- if (boardRef.current[tr][tc]) playSfx("capture");
7374
- else playSfx("step");
7375
7568
  const nb = applyMove(boardRef.current, fr, fc, tr, tc);
7376
7569
  setHistory((h) => [...h, boardRef.current]);
7377
- boardRef.current = nb;
7378
7570
  pushKey(nb);
7379
- setBoard(nb);
7380
- setSel(null);
7381
- setTargets([]);
7382
- setLast([tr, tc]);
7383
- setTurn("b");
7384
- if (!hasLegalMoves(nb, "b")) {
7385
- setOver("r");
7386
- setCheck(false);
7387
- } else setCheck(inCheck(nb, "b"));
7388
- }, [sel, pushKey]);
7571
+ animateMove(nb, fr, fc, tr, tc, () => {
7572
+ setTurn("b");
7573
+ if (!hasLegalMoves(nb, "b")) {
7574
+ setOver("r");
7575
+ setCheck(false);
7576
+ } else setCheck(inCheck(nb, "b"));
7577
+ });
7578
+ }, [sel, pushKey, animateMove]);
7389
7579
  const select = (0, import_react13.useCallback)((r, c) => {
7390
- if (overRef.current || turnRef.current !== "r" || thinkingRef.current) return;
7580
+ if (overRef.current || turnRef.current !== "r" || thinkingRef.current || movingRef.current) return;
7391
7581
  const p = boardRef.current[r][c];
7392
7582
  if (p && p.c === "r") {
7393
7583
  setSel([r, c]);
@@ -7403,32 +7593,29 @@ function XiangqiGame(props) {
7403
7593
  setTargets([]);
7404
7594
  }, [sel, targets, moveRed]);
7405
7595
  (0, import_react13.useEffect)(() => {
7406
- if (turn !== "b" || over || props.paused) return;
7596
+ if (turn !== "b" || over || props.paused || movingRef.current) return;
7407
7597
  const t = setTimeout(() => {
7408
7598
  const seenCounts = /* @__PURE__ */ new Map();
7409
7599
  for (const k of repKeys) seenCounts.set(k, (seenCounts.get(k) || 0) + 1);
7410
7600
  const mv = bestMove(boardRef.current, "b", 3, seenCounts);
7411
7601
  if (mv) {
7412
7602
  const [fr, fc, tr, tc] = mv;
7413
- if (boardRef.current[tr][tc]) playSfx("capture");
7414
- else playSfx("step");
7415
7603
  const nb = applyMove(boardRef.current, fr, fc, tr, tc);
7416
7604
  setHistory((h) => [...h, boardRef.current]);
7417
- boardRef.current = nb;
7418
7605
  pushKey(nb);
7419
- setBoard(nb);
7420
- setLast([tr, tc]);
7421
- setTurn("r");
7422
- if (!hasLegalMoves(nb, "r")) {
7423
- setOver("b");
7424
- setCheck(false);
7425
- } else setCheck(inCheck(nb, "r"));
7606
+ animateMove(nb, fr, fc, tr, tc, () => {
7607
+ setTurn("r");
7608
+ if (!hasLegalMoves(nb, "r")) {
7609
+ setOver("b");
7610
+ setCheck(false);
7611
+ } else setCheck(inCheck(nb, "r"));
7612
+ });
7426
7613
  } else setOver("r");
7427
7614
  setThinking(false);
7428
- }, 90);
7615
+ }, 420);
7429
7616
  setThinking(true);
7430
7617
  return () => clearTimeout(t);
7431
- }, [turn, over, props.paused, repKeys, pushKey]);
7618
+ }, [turn, over, props.paused, repKeys, pushKey, animateMove]);
7432
7619
  const onKeyDown = (0, import_react13.useCallback)((event) => {
7433
7620
  if (event.key.toLowerCase() === "r") {
7434
7621
  event.preventDefault();
@@ -7439,7 +7626,7 @@ function XiangqiGame(props) {
7439
7626
  }
7440
7627
  }, [restart, undo2]);
7441
7628
  useGameControls(stageRef, props.paused, onKeyDown);
7442
- 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";
7629
+ const status = over === "r" ? "\u7EA2\u65B9\u80DC \xB7 \u606D\u559C" : over === "b" ? "\u9ED1\u65B9\u80DC \xB7 AI \u8D62" : moving ? moving.piece.c === "r" ? "\u7EA2\u65B9\u8D70\u5B50\u2026" : "AI \u8D70\u5B50\u2026" : 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";
7443
7630
  const prevCheck = (0, import_react13.useRef)(false);
7444
7631
  (0, import_react13.useEffect)(() => {
7445
7632
  if (over === "r") playSfx("win");
@@ -7447,6 +7634,9 @@ function XiangqiGame(props) {
7447
7634
  else if (!thinking && check && !prevCheck.current) playSfx("check");
7448
7635
  prevCheck.current = !thinking && check;
7449
7636
  }, [check, thinking, over]);
7637
+ (0, import_react13.useEffect)(() => () => {
7638
+ if (animTimerRef.current) clearTimeout(animTimerRef.current);
7639
+ }, []);
7450
7640
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "dgame-game", "aria-label": "\u8C61\u68CB", children: [
7451
7641
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dgame-game-head", children: [
7452
7642
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
@@ -7467,26 +7657,47 @@ function XiangqiGame(props) {
7467
7657
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { className: "dgame-xiangqi-rivertext", x: 670, y: 505, children: "\u6F22 \u754C" })
7468
7658
  ] }),
7469
7659
  board.map((row, r) => row.map((p, c) => {
7660
+ const mv = moving;
7661
+ const isFlyFrom = mv && mv.fr === r && mv.fc === c;
7662
+ const isDying = mv && mv.capture && mv.tr === r && mv.tc === c;
7470
7663
  const isSel = sel && sel[0] === r && sel[1] === c;
7471
7664
  const isTargetMark = targets.some(([tr, tc]) => tr === r && tc === c);
7665
+ const isLastMoveFrom = lastMove && lastMove.fr === r && lastMove.fc === c;
7666
+ const isLand = !mv && lastMove && lastMove.tr === r && lastMove.tc === c;
7472
7667
  const isLast = last && last[0] === r && last[1] === c;
7473
7668
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7474
7669
  "button",
7475
7670
  {
7476
7671
  type: "button",
7477
7672
  role: "gridcell",
7478
- className: "dgame-xiangqi-cell" + (isSel ? " sel" : "") + (isTargetMark ? " target" : "") + (isLast ? " last" : ""),
7673
+ className: "dgame-xiangqi-cell" + (isSel ? " sel" : "") + (isTargetMark ? " target" : "") + (isLastMoveFrom ? " from" : "") + (isLand ? " land" : "") + (isLast ? " last" : ""),
7479
7674
  "aria-label": "\u7B2C" + (r + 1) + "\u884C\u7B2C" + (c + 1) + "\u5217" + (p ? pieceChar(p) : " \u7A7A"),
7480
7675
  onClick: () => {
7481
7676
  focusStage(stageRef);
7482
7677
  select(r, c);
7483
7678
  },
7484
- 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
7679
+ children: p && !isFlyFrom ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dgame-xiangqi-pc " + p.c + (isDying ? " dying" : ""), children: pieceChar(p) }) : !p && isTargetMark ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dgame-xiangqi-dot", "aria-hidden": "true" }) : null
7485
7680
  },
7486
7681
  r + "-" + c
7487
7682
  );
7488
- }))
7683
+ })),
7684
+ moving ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7685
+ "span",
7686
+ {
7687
+ className: "dgame-xiangqi-pc dgame-xiangqi-fly " + moving.piece.c,
7688
+ style: {
7689
+ "--fx0": (moving.fc + 0.5) / 9,
7690
+ "--fy0": (moving.fr + 0.5) / 10,
7691
+ "--fx1": (moving.tc + 0.5) / 9,
7692
+ "--fy1": (moving.tr + 0.5) / 10
7693
+ },
7694
+ "aria-hidden": "true",
7695
+ children: pieceChar(moving.piece)
7696
+ },
7697
+ moving.fr + "-" + moving.fc + "-" + moving.tr + "-" + moving.tc + "-" + moving.piece.c
7698
+ ) : null
7489
7699
  ] }),
7700
+ moveLog.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dgame-xiangqi-log", role: "status", "aria-live": "polite", children: moveLog.slice(-4).map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: m.side, children: m.text }, moveLog.length - 4 + i)) }) : null,
7490
7701
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dgame-controls", children: [
7491
7702
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: undo2, children: "\u64A4\u9500" }),
7492
7703
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
@@ -7524,7 +7735,7 @@ function XiangqiPreview() {
7524
7735
  ] }) });
7525
7736
  }
7526
7737
  var xiangqiGame = { Game: XiangqiGame, Preview: XiangqiPreview, css: `
7527
- .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)}
7738
+ .dgame-xiangqi-board{position:relative;display:grid;grid-template-columns:repeat(9,1fr);grid-template-rows:repeat(10,1fr);align-self:center;width:min(100%,min(90vw,340px));aspect-ratio:9/10;padding:13px;border-radius:12px;border:1px solid color-mix(in srgb,#7c4f16 65%,transparent);box-shadow:0 12px 32px rgb(0 0 0/.5),inset 0 1px 0 rgba(255,236,200,.55),inset 0 -3px 9px rgba(90,50,10,.35),inset 3px 0 8px rgba(255,224,170,.28),inset -3px 0 8px rgba(120,70,20,.25);outline:none;background:repeating-linear-gradient(94deg,rgba(124,79,22,.05) 0 2px,transparent 2px 11px),repeating-linear-gradient(87deg,rgba(255,238,196,.05) 0 2px,transparent 2px 14px),radial-gradient(130% 105% at 26% 8%,#f0cd86,#ddb067 48%,#cb9a51 78%,#bb8844)}.dgame-xiangqi-lines{position:absolute;inset:13px;pointer-events:none}.dgame-xiangqi-lines line,.dgame-xiangqi-lines path,.dgame-xiangqi-lines rect{stroke:#66431a;stroke-width:3.4;fill:none;stroke-linecap:square}.dgame-xiangqi-lines rect{stroke-width:7}.dgame-xiangqi-lines .dgame-xiangqi-mark{stroke-width:2.6}.dgame-xiangqi-rivertext{fill:rgba(102,67,26,.7);font-family:KaiTi,"Kaiti SC","STKaiti","FangSong",serif;font-size:46px;letter-spacing:7px;text-anchor:middle;dominant-baseline:central}.dgame-xiangqi-board:focus-visible{box-shadow:0 0 0 3px rgba(0,0,0,.25)}.dgame-xiangqi-cell{position:relative;z-index:1;background:transparent;border:none;cursor:pointer;padding:0;margin:0;outline:none;display:grid;place-items:center;min-width:0;min-height:0}.dgame-xiangqi-cell.target .dgame-xiangqi-dot{width:12px;height:12px;border-radius:50%;background:rgba(34,211,238,.6);box-shadow:0 0 10px rgba(34,211,238,.65),0 0 0 2px rgba(255,255,255,.28)}.dgame-xiangqi-cell.target .dgame-xiangqi-pc{box-shadow:0 0 0 2px rgba(248,113,113,.85),0 0 10px rgba(248,113,113,.5),0 2px 4px rgb(0 0 0/.4),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4)}.dgame-xiangqi-cell.sel .dgame-xiangqi-pc::before{content:'';position:absolute;inset:-3px;border-radius:50%;border:2.5px solid rgba(251,191,36,.95);box-shadow:0 0 12px rgba(251,191,36,.6)}.dgame-xiangqi-cell.from::before{content:'';position:absolute;inset:50% auto auto 50%;width:34%;height:34%;transform:translate(-50%,-50%);border-radius:50%;border:2.5px dashed rgba(102,67,26,.75);pointer-events:none}.dgame-xiangqi-cell.land .dgame-xiangqi-pc{animation:dgame-xq-land .28s cubic-bezier(.2,.85,.3,1.25)}.dgame-xiangqi-cell.last .dgame-xiangqi-pc::after{content:'';position:absolute;right:5%;bottom:5%;width:5px;height:5px;border-radius:50%;background:#ef4444;box-shadow:0 0 4px rgba(239,68,68,.8)}.dgame-xiangqi-pc{position:relative;display:grid;place-items:center;z-index:1;width:min(80%,30px);aspect-ratio:1/1;border-radius:50%;box-sizing:border-box;font-family:KaiTi,"Kaiti SC","STKaiti","FangSong","SimSun",serif;font-size:clamp(12px,1.4vw,15px);font-weight:700;line-height:1;background:radial-gradient(circle at 34% 28%,#fff3d4,#eedaa6 46%,#d8b675 72%,#bd9650);box-shadow:0 2px 4px rgb(0 0 0/.45),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4)}.dgame-xiangqi-pc.dying{animation:dgame-xq-die .26s ease forwards}.dgame-xiangqi-fly{position:absolute;z-index:5;width:calc((100% - 26px)*.0889);aspect-ratio:1/1;border-radius:50%;display:grid;place-items:center;box-sizing:border-box;font-family:KaiTi,"Kaiti SC","STKaiti","FangSong","SimSun",serif;font-size:clamp(12px,1.4vw,15px);font-weight:700;line-height:1;pointer-events:none;background:radial-gradient(circle at 34% 28%,#fff3d4,#eedaa6 46%,#d8b675 72%,#bd9650);box-shadow:0 6px 14px rgb(0 0 0/.5),0 0 0 1.5px rgba(96,62,18,.35),inset 0 0 0 1.5px rgba(96,62,18,.5),inset 0 1px 2px rgba(255,255,255,.95),inset 0 -2px 3px rgba(120,80,20,.4);animation:dgame-xq-hop .26s cubic-bezier(.3,.7,.4,1) forwards}.dgame-xiangqi-fly.r{color:#b91c1c;text-shadow:0 1px 0 rgba(255,255,255,.5)}.dgame-xiangqi-fly.b{color:#1f2937;text-shadow:0 1px 0 rgba(255,255,255,.35)}@keyframes dgame-xq-hop{0%{left:calc(13px + var(--fx0)*(100% - 26px));top:calc(13px + var(--fy0)*(100% - 26px));transform:translate(-50%,-50%) scale(.9);opacity:.85}55%{transform:translate(-50%,-64%) scale(1.16);opacity:1}100%{left:calc(13px + var(--fx1)*(100% - 26px));top:calc(13px + var(--fy1)*(100% - 26px));transform:translate(-50%,-50%) scale(1)}}@keyframes dgame-xq-land{0%{transform:scale(1.18)}100%{transform:scale(1)}}@keyframes dgame-xq-die{to{opacity:0;transform:scale(.55)}}.dgame-xiangqi-log{display:flex;flex-wrap:wrap;justify-content:center;gap:5px 12px;align-self:center;min-height:22px;color:var(--dsw-alias-label-secondary);font-family:KaiTi,"Kaiti SC","STKaiti","FangSong",serif;font-size:13px}.dgame-xiangqi-log span{opacity:.65}.dgame-xiangqi-log span:last-child{opacity:1;font-weight:700}.dgame-xiangqi-log .r{color:#c03434}.dgame-xiangqi-log .b{color:var(--dsw-alias-label-primary)}
7528
7739
  ` };
7529
7740
 
7530
7741
  // features/games/games/snake.jsx
@@ -7858,7 +8069,7 @@ function BreakoutGame(props) {
7858
8069
  if (keys.current.right) g.paddle.x += 5.5;
7859
8070
  g.paddle.x = Math.max(0, Math.min(W - PADDLE_W, g.paddle.x));
7860
8071
  const b = g.ball;
7861
- if (!b.launched) {
8072
+ if (!g.launched) {
7862
8073
  b.x = g.paddle.x + PADDLE_W / 2;
7863
8074
  b.y = g.paddle.y - BALL_R - 1;
7864
8075
  } else {
@@ -7985,7 +8196,10 @@ function BreakoutGame(props) {
7985
8196
  ] })
7986
8197
  ] }),
7987
8198
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-breakout", children: [
7988
- /* @__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" }),
8199
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("canvas", { ref: canvasRef, width: W, height: H, tabIndex: 0, onKeyDown, onKeyUp, onPointerMove, onClick: (e) => {
8200
+ focusStage(canvasRef);
8201
+ launchBall();
8202
+ }, 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\u6216\u70B9\u51FB\u53D1\u7403" }),
7989
8203
  status === "win" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dgame-over", children: [
7990
8204
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("strong", { children: [
7991
8205
  "\u5168\u6D88\uFF01\u8FDB\u5165\u7B2C ",
@@ -8019,7 +8233,7 @@ function BreakoutGame(props) {
8019
8233
  keys.current.left = false;
8020
8234
  game.current && (game.current.paddle.x = Math.max(0, game.current.paddle.x - 12));
8021
8235
  }, children: "\u2190 \u5DE6\u79FB" }),
8022
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 \u7A7A\u683C\u53D1\u7403" }),
8236
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 \u7A7A\u683C/\u70B9\u51FB\u53D1\u7403" }),
8023
8237
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "button", "aria-label": "\u5411\u53F3\u79FB\u52A8", onClick: () => {
8024
8238
  keys.current.right = false;
8025
8239
  game.current && (game.current.paddle.x = Math.min(W - PADDLE_W, game.current.paddle.x + 12));
@@ -8083,7 +8297,7 @@ function RacerGame(props) {
8083
8297
  const reset = (0, import_react16.useCallback)(() => {
8084
8298
  game.current = {
8085
8299
  stars: Array.from({ length: 50 }, () => ({ x: Math.random() * W2, y: Math.random() * H2, a: 0.2 + Math.random() * 0.5 })),
8086
- player: { x: laneX(1), y: H2 - CAR_H - 16 },
8300
+ player: { x: laneX(1), y: H2 - CAR_H - 16, w: CAR_W, h: CAR_H },
8087
8301
  enemies: [],
8088
8302
  coins: [],
8089
8303
  dist: 0,
@@ -8955,7 +9169,7 @@ var GAMES = [
8955
9169
  cat: "classic",
8956
9170
  accent: "#38bdf8",
8957
9171
  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",
8958
- tip: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 \u7A7A\u683C\u53D1\u7403",
9172
+ tip: "\u2190/\u2192 \u6216\u9F20\u6807\u79FB\u52A8 \xB7 \u7A7A\u683C/\u70B9\u51FB\u53D1\u7403",
8959
9173
  Game: breakoutGame.Game,
8960
9174
  Preview: breakoutGame.Preview
8961
9175
  },
@@ -9781,7 +9995,7 @@ var feature8 = {
9781
9995
  };
9782
9996
 
9783
9997
  // src/client.jsx
9784
- var DOCK_VERSION = "0.9.1";
9998
+ var DOCK_VERSION = "0.9.3";
9785
9999
  var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8];
9786
10000
  var PLANNED_FEATURES = [];
9787
10001
  var PLANNED_NOTES = {};