dsh-plugin-feihualing 1.1.1 → 1.1.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/cordis.yml +1 -1
- package/lib/client.js +70 -108
- package/lib/client.js.map +1 -1
- package/lib/index.js +0 -2
- package/package.json +1 -1
package/cordis.yml
CHANGED
package/lib/client.js
CHANGED
|
@@ -11,7 +11,6 @@ window.__ModuleLoader__.load({
|
|
|
11
11
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
12
|
var __getProtoOf = Object.getPrototypeOf;
|
|
13
13
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
15
14
|
var __copyProps = (to, from, except, desc) => {
|
|
16
15
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
17
16
|
key = keys[i];
|
|
@@ -29,6 +28,7 @@ window.__ModuleLoader__.load({
|
|
|
29
28
|
//#endregion
|
|
30
29
|
let react = require("react");
|
|
31
30
|
react = __toESM(react, 1);
|
|
31
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
32
|
//#region src/client/styles.ts
|
|
33
33
|
/**
|
|
34
34
|
* 客户端半边样式:一次性注入 <style>,全部 fhl-* 类以 data-plugin 根类名限定作用域。
|
|
@@ -484,7 +484,6 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
484
484
|
"人闲桂花落",
|
|
485
485
|
"举杯邀明月,对影成三人",
|
|
486
486
|
"路上行人欲断魂",
|
|
487
|
-
"独在异乡为异客,每逢佳节倍思亲",
|
|
488
487
|
"人面不知何处去",
|
|
489
488
|
"但愿人长久",
|
|
490
489
|
"前不见古人,后不见来者",
|
|
@@ -503,7 +502,6 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
503
502
|
"酒入愁肠,化作相思泪",
|
|
504
503
|
"今朝有酒今朝醉",
|
|
505
504
|
"白日放歌须纵酒",
|
|
506
|
-
"人生得意须尽欢,莫使金樽空对月",
|
|
507
505
|
"明月几时有,把酒问青天"
|
|
508
506
|
]),
|
|
509
507
|
夜: Object.freeze([
|
|
@@ -867,39 +865,6 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
867
865
|
};
|
|
868
866
|
}
|
|
869
867
|
//#endregion
|
|
870
|
-
//#region node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
871
|
-
/**
|
|
872
|
-
* @license React
|
|
873
|
-
* react-jsx-runtime.production.js
|
|
874
|
-
*
|
|
875
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
876
|
-
*
|
|
877
|
-
* This source code is licensed under the MIT license found in the
|
|
878
|
-
* LICENSE file in the root directory of this source tree.
|
|
879
|
-
*/
|
|
880
|
-
var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
881
|
-
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
|
|
882
|
-
function jsxProd(type, config, maybeKey) {
|
|
883
|
-
var key = null;
|
|
884
|
-
void 0 !== maybeKey && (key = "" + maybeKey);
|
|
885
|
-
void 0 !== config.key && (key = "" + config.key);
|
|
886
|
-
if ("key" in config) {
|
|
887
|
-
maybeKey = {};
|
|
888
|
-
for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]);
|
|
889
|
-
} else maybeKey = config;
|
|
890
|
-
config = maybeKey.ref;
|
|
891
|
-
return {
|
|
892
|
-
$$typeof: REACT_ELEMENT_TYPE,
|
|
893
|
-
type,
|
|
894
|
-
key,
|
|
895
|
-
ref: void 0 !== config ? config : null,
|
|
896
|
-
props: maybeKey
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
exports.jsx = jsxProd;
|
|
900
|
-
exports.jsxs = jsxProd;
|
|
901
|
-
}));
|
|
902
|
-
//#endregion
|
|
903
868
|
//#region src/client/panel.tsx
|
|
904
869
|
/**
|
|
905
870
|
* 浮层游戏面板(完整对战 UI)。
|
|
@@ -907,9 +872,6 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
907
872
|
* 关闭面板只隐藏,不丢对局;隐藏期间倒计时暂停(视同暂停)。
|
|
908
873
|
* @module dsh-plugin-feihualing/client/panel
|
|
909
874
|
*/
|
|
910
|
-
var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
911
|
-
module.exports = require_react_jsx_runtime_production();
|
|
912
|
-
})))();
|
|
913
875
|
/** 会话短标识(展示用)。 */
|
|
914
876
|
function shortSession(sessionId) {
|
|
915
877
|
return sessionId.length > 12 ? `${sessionId.slice(0, 10)}…` : sessionId;
|
|
@@ -931,15 +893,15 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
931
893
|
/** 开局设置行(未开局或对局结束后显示)。 */
|
|
932
894
|
function SetupView(props) {
|
|
933
895
|
const { api } = props;
|
|
934
|
-
return /* @__PURE__ */ (0,
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
896
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.default.Fragment, { children: [
|
|
897
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
936
898
|
className: "fhl-row",
|
|
937
|
-
children: [/* @__PURE__ */ (0,
|
|
899
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
938
900
|
className: "fhl-seg-label",
|
|
939
901
|
children: "模式"
|
|
940
|
-
}), /* @__PURE__ */ (0,
|
|
902
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
941
903
|
className: "fhl-seg",
|
|
942
|
-
children: ["simple", "ancient"].map((m) => /* @__PURE__ */ (0,
|
|
904
|
+
children: ["simple", "ancient"].map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
943
905
|
type: "button",
|
|
944
906
|
className: `fhl-seg-item${api.mode === m ? " is-active" : ""}`,
|
|
945
907
|
onClick: () => api.setMode(m),
|
|
@@ -947,18 +909,18 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
947
909
|
}, m))
|
|
948
910
|
})]
|
|
949
911
|
}),
|
|
950
|
-
/* @__PURE__ */ (0,
|
|
912
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
951
913
|
className: "fhl-row",
|
|
952
|
-
children: [/* @__PURE__ */ (0,
|
|
914
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
953
915
|
className: "fhl-seg-label",
|
|
954
916
|
children: "难度"
|
|
955
|
-
}), /* @__PURE__ */ (0,
|
|
917
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
956
918
|
className: "fhl-seg",
|
|
957
919
|
children: [
|
|
958
920
|
"easy",
|
|
959
921
|
"medium",
|
|
960
922
|
"hard"
|
|
961
|
-
].map((d) => /* @__PURE__ */ (0,
|
|
923
|
+
].map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
962
924
|
type: "button",
|
|
963
925
|
className: `fhl-seg-item${api.difficulty === d ? " is-active" : ""}`,
|
|
964
926
|
onClick: () => api.setDifficulty(d),
|
|
@@ -967,7 +929,7 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
967
929
|
}, d))
|
|
968
930
|
})]
|
|
969
931
|
}),
|
|
970
|
-
/* @__PURE__ */ (0,
|
|
932
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
971
933
|
className: "fhl-muted",
|
|
972
934
|
style: {
|
|
973
935
|
fontSize: 12,
|
|
@@ -980,13 +942,13 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
980
942
|
DIFF_HINT[api.difficulty]
|
|
981
943
|
]
|
|
982
944
|
}),
|
|
983
|
-
/* @__PURE__ */ (0,
|
|
945
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
984
946
|
type: "button",
|
|
985
947
|
className: "fhl-btn fhl-btn-primary",
|
|
986
948
|
onClick: api.start,
|
|
987
949
|
children: "🎴 开始对局"
|
|
988
950
|
}),
|
|
989
|
-
/* @__PURE__ */ (0,
|
|
951
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
990
952
|
className: "fhl-muted",
|
|
991
953
|
style: {
|
|
992
954
|
fontSize: 11,
|
|
@@ -1006,14 +968,14 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1006
968
|
function PlayView(props) {
|
|
1007
969
|
const { api } = props;
|
|
1008
970
|
const match = api.match;
|
|
1009
|
-
if (match === null) return /* @__PURE__ */ (0,
|
|
971
|
+
if (match === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, {});
|
|
1010
972
|
const timeLow = api.secondsLeft <= 5;
|
|
1011
973
|
const canAct = match.phase === "playing";
|
|
1012
|
-
return /* @__PURE__ */ (0,
|
|
1013
|
-
/* @__PURE__ */ (0,
|
|
974
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.default.Fragment, { children: [
|
|
975
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1014
976
|
className: "fhl-stage",
|
|
1015
977
|
children: [
|
|
1016
|
-
/* @__PURE__ */ (0,
|
|
978
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1017
979
|
className: "fhl-stage-round",
|
|
1018
980
|
children: [
|
|
1019
981
|
"第 ",
|
|
@@ -1023,11 +985,11 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1023
985
|
" 题"
|
|
1024
986
|
]
|
|
1025
987
|
}),
|
|
1026
|
-
/* @__PURE__ */ (0,
|
|
988
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1027
989
|
className: `fhl-timer${timeLow && canAct ? " is-low" : ""}`,
|
|
1028
990
|
children: [api.secondsLeft, "s"]
|
|
1029
991
|
}),
|
|
1030
|
-
/* @__PURE__ */ (0,
|
|
992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1031
993
|
className: "fhl-stage-tag",
|
|
1032
994
|
children: [
|
|
1033
995
|
MODE_LABEL[match.mode],
|
|
@@ -1037,7 +999,7 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1037
999
|
})
|
|
1038
1000
|
]
|
|
1039
1001
|
}),
|
|
1040
|
-
/* @__PURE__ */ (0,
|
|
1002
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1041
1003
|
className: "fhl-lingzi",
|
|
1042
1004
|
children: [
|
|
1043
1005
|
"「",
|
|
@@ -1045,7 +1007,7 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1045
1007
|
"」"
|
|
1046
1008
|
]
|
|
1047
1009
|
}),
|
|
1048
|
-
match.mode === "ancient" && /* @__PURE__ */ (0,
|
|
1010
|
+
match.mode === "ancient" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1049
1011
|
className: "fhl-muted",
|
|
1050
1012
|
style: {
|
|
1051
1013
|
fontSize: 11,
|
|
@@ -1054,50 +1016,50 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1054
1016
|
},
|
|
1055
1017
|
children: [
|
|
1056
1018
|
"古法:本轮令字须位于第 ",
|
|
1057
|
-
/* @__PURE__ */ (0,
|
|
1019
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
|
|
1058
1020
|
style: { color: "var(--fhl-accent)" },
|
|
1059
1021
|
children: match.requiredPosition
|
|
1060
1022
|
}),
|
|
1061
1023
|
" 字"
|
|
1062
1024
|
]
|
|
1063
1025
|
}),
|
|
1064
|
-
/* @__PURE__ */ (0,
|
|
1026
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1065
1027
|
className: "fhl-card fhl-card-bot",
|
|
1066
|
-
children: [/* @__PURE__ */ (0,
|
|
1028
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1067
1029
|
className: "fhl-card-role",
|
|
1068
1030
|
children: "🤖 对手"
|
|
1069
|
-
}), /* @__PURE__ */ (0,
|
|
1031
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1070
1032
|
className: "fhl-card-line",
|
|
1071
1033
|
children: match.challenge
|
|
1072
1034
|
})]
|
|
1073
1035
|
}),
|
|
1074
|
-
match.lastVerdict !== "" && /* @__PURE__ */ (0,
|
|
1036
|
+
match.lastVerdict !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1075
1037
|
className: `fhl-verdict${api.secondsLeft === 0 || !canAct ? "" : ""}`,
|
|
1076
1038
|
children: match.lastVerdict
|
|
1077
1039
|
}),
|
|
1078
|
-
/* @__PURE__ */ (0,
|
|
1040
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
1079
1041
|
className: "fhl-input-row",
|
|
1080
1042
|
onSubmit: (ev) => {
|
|
1081
1043
|
ev.preventDefault();
|
|
1082
1044
|
api.submit();
|
|
1083
1045
|
},
|
|
1084
|
-
children: [/* @__PURE__ */ (0,
|
|
1046
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1085
1047
|
className: "fhl-input",
|
|
1086
1048
|
value: api.draft,
|
|
1087
1049
|
onChange: (ev) => api.setDraft(ev.target.value),
|
|
1088
1050
|
placeholder: `说一句含「${match.lingzi}」的诗句…`,
|
|
1089
1051
|
disabled: !canAct,
|
|
1090
1052
|
autoComplete: "off"
|
|
1091
|
-
}), /* @__PURE__ */ (0,
|
|
1053
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1092
1054
|
type: "submit",
|
|
1093
1055
|
className: "fhl-btn fhl-btn-primary",
|
|
1094
1056
|
disabled: !canAct,
|
|
1095
1057
|
children: "对诗"
|
|
1096
1058
|
})]
|
|
1097
1059
|
}),
|
|
1098
|
-
/* @__PURE__ */ (0,
|
|
1060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1099
1061
|
className: "fhl-actions",
|
|
1100
|
-
children: [/* @__PURE__ */ (0,
|
|
1062
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1101
1063
|
type: "button",
|
|
1102
1064
|
className: "fhl-btn",
|
|
1103
1065
|
onClick: api.requestHint,
|
|
@@ -1108,7 +1070,7 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1108
1070
|
match.hintsLeft,
|
|
1109
1071
|
")"
|
|
1110
1072
|
]
|
|
1111
|
-
}), /* @__PURE__ */ (0,
|
|
1073
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1112
1074
|
type: "button",
|
|
1113
1075
|
className: "fhl-btn fhl-btn-danger",
|
|
1114
1076
|
onClick: api.concede,
|
|
@@ -1116,28 +1078,28 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1116
1078
|
children: "认输"
|
|
1117
1079
|
})]
|
|
1118
1080
|
}),
|
|
1119
|
-
/* @__PURE__ */ (0,
|
|
1081
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1120
1082
|
className: "fhl-stats",
|
|
1121
1083
|
children: [
|
|
1122
|
-
/* @__PURE__ */ (0,
|
|
1084
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1123
1085
|
className: "fhl-stat",
|
|
1124
|
-
children: [/* @__PURE__ */ (0,
|
|
1086
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: match.score }), " 得分"]
|
|
1125
1087
|
}),
|
|
1126
|
-
/* @__PURE__ */ (0,
|
|
1088
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1127
1089
|
className: "fhl-stat",
|
|
1128
|
-
children: [/* @__PURE__ */ (0,
|
|
1090
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: match.combo }), " 连击"]
|
|
1129
1091
|
}),
|
|
1130
|
-
/* @__PURE__ */ (0,
|
|
1092
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1131
1093
|
className: "fhl-stat",
|
|
1132
|
-
children: [/* @__PURE__ */ (0,
|
|
1094
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [
|
|
1133
1095
|
match.solved,
|
|
1134
1096
|
"/",
|
|
1135
1097
|
7
|
|
1136
1098
|
] }), " 答对"]
|
|
1137
1099
|
}),
|
|
1138
|
-
/* @__PURE__ */ (0,
|
|
1100
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1139
1101
|
className: `fhl-stat${match.failures >= 2 ? " is-warn" : ""}`,
|
|
1140
|
-
children: [/* @__PURE__ */ (0,
|
|
1102
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [
|
|
1141
1103
|
match.failures,
|
|
1142
1104
|
"/",
|
|
1143
1105
|
3
|
|
@@ -1151,21 +1113,21 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1151
1113
|
function EndView(props) {
|
|
1152
1114
|
const { api } = props;
|
|
1153
1115
|
const match = api.match;
|
|
1154
|
-
if (match === null) return /* @__PURE__ */ (0,
|
|
1116
|
+
if (match === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, {});
|
|
1155
1117
|
const won = match.phase === "won";
|
|
1156
|
-
return /* @__PURE__ */ (0,
|
|
1157
|
-
/* @__PURE__ */ (0,
|
|
1118
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.default.Fragment, { children: [
|
|
1119
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1158
1120
|
className: `fhl-result${won ? " is-won" : " is-lost"}`,
|
|
1159
1121
|
children: [
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1122
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1161
1123
|
className: "fhl-result-icon",
|
|
1162
1124
|
children: won ? "🏆" : "😢"
|
|
1163
1125
|
}),
|
|
1164
|
-
/* @__PURE__ */ (0,
|
|
1126
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1165
1127
|
className: "fhl-result-title",
|
|
1166
1128
|
children: won ? "通关!" : "本局惜败"
|
|
1167
1129
|
}),
|
|
1168
|
-
/* @__PURE__ */ (0,
|
|
1130
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1169
1131
|
className: "fhl-muted",
|
|
1170
1132
|
style: {
|
|
1171
1133
|
fontSize: 12,
|
|
@@ -1175,44 +1137,44 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1175
1137
|
})
|
|
1176
1138
|
]
|
|
1177
1139
|
}),
|
|
1178
|
-
/* @__PURE__ */ (0,
|
|
1140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1179
1141
|
className: "fhl-stats",
|
|
1180
1142
|
children: [
|
|
1181
|
-
/* @__PURE__ */ (0,
|
|
1143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1182
1144
|
className: "fhl-stat",
|
|
1183
|
-
children: [/* @__PURE__ */ (0,
|
|
1145
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: match.score }), " 得分"]
|
|
1184
1146
|
}),
|
|
1185
|
-
/* @__PURE__ */ (0,
|
|
1147
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1186
1148
|
className: "fhl-stat",
|
|
1187
|
-
children: [/* @__PURE__ */ (0,
|
|
1149
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: match.solved }), " 答对"]
|
|
1188
1150
|
}),
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1151
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1190
1152
|
className: "fhl-stat",
|
|
1191
|
-
children: [/* @__PURE__ */ (0,
|
|
1153
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: match.failures }), " 超时"]
|
|
1192
1154
|
})
|
|
1193
1155
|
]
|
|
1194
1156
|
}),
|
|
1195
|
-
/* @__PURE__ */ (0,
|
|
1157
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1196
1158
|
className: "fhl-record",
|
|
1197
1159
|
children: [
|
|
1198
|
-
/* @__PURE__ */ (0,
|
|
1160
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
1199
1161
|
"🏅 ",
|
|
1200
1162
|
api.stats.wins,
|
|
1201
1163
|
" 胜 / ",
|
|
1202
1164
|
api.stats.losses,
|
|
1203
1165
|
" 负"
|
|
1204
1166
|
] }),
|
|
1205
|
-
/* @__PURE__ */ (0,
|
|
1206
|
-
/* @__PURE__ */ (0,
|
|
1167
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["🔥 连胜 ", api.stats.streak] }),
|
|
1168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["✨ 最佳 ", api.stats.bestScore] })
|
|
1207
1169
|
]
|
|
1208
1170
|
}),
|
|
1209
|
-
/* @__PURE__ */ (0,
|
|
1171
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1210
1172
|
type: "button",
|
|
1211
1173
|
className: "fhl-btn fhl-btn-primary",
|
|
1212
1174
|
onClick: api.start,
|
|
1213
1175
|
children: "🔄 再来一局"
|
|
1214
1176
|
}),
|
|
1215
|
-
/* @__PURE__ */ (0,
|
|
1177
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1216
1178
|
className: "fhl-muted",
|
|
1217
1179
|
style: {
|
|
1218
1180
|
fontSize: 11,
|
|
@@ -1233,23 +1195,23 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1233
1195
|
function GamePanel(props) {
|
|
1234
1196
|
const { sessionId, onClose, api } = props;
|
|
1235
1197
|
const phase = api.match === null ? "setup" : api.match.phase;
|
|
1236
|
-
return /* @__PURE__ */ (0,
|
|
1198
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1237
1199
|
className: "fhl-root fhl-panel",
|
|
1238
1200
|
"data-plugin": "dsh-plugin-feihualing",
|
|
1239
1201
|
children: [
|
|
1240
|
-
/* @__PURE__ */ (0,
|
|
1202
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1241
1203
|
className: "fhl-panel-header",
|
|
1242
1204
|
children: [
|
|
1243
|
-
/* @__PURE__ */ (0,
|
|
1205
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1244
1206
|
className: "fhl-panel-title",
|
|
1245
1207
|
children: "🎴 飞花令 · 即时对战"
|
|
1246
1208
|
}),
|
|
1247
|
-
/* @__PURE__ */ (0,
|
|
1209
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1248
1210
|
className: "fhl-tag",
|
|
1249
1211
|
title: sessionId,
|
|
1250
1212
|
children: ["会话 ", shortSession(sessionId)]
|
|
1251
1213
|
}),
|
|
1252
|
-
/* @__PURE__ */ (0,
|
|
1214
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1253
1215
|
type: "button",
|
|
1254
1216
|
className: "fhl-panel-close",
|
|
1255
1217
|
onClick: onClose,
|
|
@@ -1258,9 +1220,9 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1258
1220
|
})
|
|
1259
1221
|
]
|
|
1260
1222
|
}),
|
|
1261
|
-
phase === "setup" && /* @__PURE__ */ (0,
|
|
1262
|
-
phase === "playing" && /* @__PURE__ */ (0,
|
|
1263
|
-
(phase === "won" || phase === "lost") && /* @__PURE__ */ (0,
|
|
1223
|
+
phase === "setup" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SetupView, { api }),
|
|
1224
|
+
phase === "playing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlayView, { api }),
|
|
1225
|
+
(phase === "won" || phase === "lost") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EndView, { api })
|
|
1264
1226
|
]
|
|
1265
1227
|
});
|
|
1266
1228
|
}
|
|
@@ -1417,7 +1379,7 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1417
1379
|
const api = useGamePanel(open);
|
|
1418
1380
|
const sessionId = props.sessionId ?? "";
|
|
1419
1381
|
const toggle = react.default.useCallback(() => setOpen((prev) => !prev), []);
|
|
1420
|
-
return /* @__PURE__ */ (0,
|
|
1382
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.default.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1421
1383
|
type: "button",
|
|
1422
1384
|
className: "fhl-toggle",
|
|
1423
1385
|
"data-active": open ? "true" : "false",
|
|
@@ -1425,7 +1387,7 @@ body[data-ds-dark-theme] .fhl-root {
|
|
|
1425
1387
|
title: open ? "收起飞花令(对局保留)" : "飞花令小游戏",
|
|
1426
1388
|
"aria-label": "飞花令小游戏",
|
|
1427
1389
|
children: "🎴"
|
|
1428
|
-
}), open && /* @__PURE__ */ (0,
|
|
1390
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GamePanel, {
|
|
1429
1391
|
sessionId,
|
|
1430
1392
|
onClose: () => setOpen(false),
|
|
1431
1393
|
api
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["React","React","engine.TIME_LIMIT","engine.timeoutRound","engine.createMatch","engine.submitPlayerLine","engine.useHint","engine.concedeMatch","React"],"sources":["../src/client/styles.ts","../src/client/constants.ts","../src/shared/rules.ts","../src/shared/poems.ts","../src/client/game/engine.ts","../node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react-jsx-runtime.production.js","../node_modules/.pnpm/react@19.2.8/node_modules/react/jsx-runtime.js","../src/client/panel.tsx","../src/client/game/stats.ts","../src/client/game/useGame.ts","../src/client/header-button.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * 客户端半边样式:一次性注入 <style>,全部 fhl-* 类以 data-plugin 根类名限定作用域。\n * 主题跟随 DSH:深色模式由 body[data-ds-dark-theme] 标识(官方约定),\n * 浅色/深色各写一套语义配色;不依赖任何宿主私有类名。\n * @module dsh-plugin-feihualing/client/styles\n */\n\nconst CSS = `\n[data-plugin=\"dsh-plugin-feihualing\"].fhl-root, .fhl-root {\n --fhl-bg: #ffffff;\n --fhl-bg-elev: #f4f4f5;\n --fhl-fg: #27272a;\n --fhl-fg-muted: #71717a;\n --fhl-accent: #8b5cf6;\n --fhl-accent-soft: rgba(139, 92, 246, 0.12);\n --fhl-border: rgba(0, 0, 0, 0.1);\n --fhl-ok: #16a34a;\n --fhl-ok-soft: rgba(22, 163, 74, 0.12);\n --fhl-bad: #dc2626;\n --fhl-bad-soft: rgba(220, 38, 38, 0.12);\n font-family: var(--dsh-content-font, ui-sans-serif, system-ui, \"PingFang SC\", \"Microsoft YaHei\", sans-serif);\n box-sizing: border-box;\n}\nbody[data-ds-dark-theme] .fhl-root {\n --fhl-bg: #18181b;\n --fhl-bg-elev: #27272a;\n --fhl-fg: #e4e4e7;\n --fhl-fg-muted: #a1a1aa;\n --fhl-accent: #a78bfa;\n --fhl-accent-soft: rgba(167, 139, 250, 0.16);\n --fhl-border: rgba(255, 255, 255, 0.12);\n --fhl-ok: #4ade80;\n --fhl-ok-soft: rgba(74, 222, 128, 0.16);\n --fhl-bad: #f87171;\n --fhl-bad-soft: rgba(248, 113, 113, 0.16);\n}\n.fhl-root *, .fhl-root *::before, .fhl-root *::after { box-sizing: border-box; }\n\n/* 会话头操作行的小按钮 */\n.fhl-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 24px;\n padding: 0 6px;\n border: 1px solid transparent;\n border-radius: 6px;\n background: transparent;\n color: var(--fhl-fg-muted);\n font-size: 14px;\n line-height: 1;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n}\n.fhl-toggle:hover { background: var(--fhl-accent-soft); color: var(--fhl-fg); }\n.fhl-toggle[data-active=\"true\"] { background: var(--fhl-accent-soft); color: var(--fhl-accent); }\n\n/* 浮层游戏面板(portal 到 body 的独立 surface,自带作用域) */\n.fhl-panel {\n position: fixed;\n top: 64px;\n right: 16px;\n width: 340px;\n max-width: calc(100vw - 24px);\n max-height: calc(100vh - 88px);\n overflow: auto;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n border-radius: 14px;\n background: var(--fhl-bg);\n color: var(--fhl-fg);\n border: 1px solid var(--fhl-border);\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);\n}\n.fhl-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.fhl-panel-title { font-weight: 700; font-size: 14px; letter-spacing: 0.02em; }\n.fhl-panel-close {\n border: none;\n background: transparent;\n color: var(--fhl-fg-muted);\n cursor: pointer;\n font-size: 14px;\n line-height: 1;\n padding: 4px;\n border-radius: 6px;\n}\n.fhl-panel-close:hover { background: var(--fhl-bad-soft); color: var(--fhl-bad); }\n.fhl-muted { color: var(--fhl-fg-muted); }\n.fhl-tag { font-size: 11px; color: var(--fhl-fg-muted); border: 1px solid var(--fhl-border); padding: 1px 6px; border-radius: 999px; }\n\n/* ---- 对战 UI ---- */\n.fhl-row { display: flex; align-items: center; gap: 8px; }\n.fhl-seg-label { font-size: 12px; color: var(--fhl-fg-muted); flex: none; width: 34px; }\n.fhl-seg { display: flex; gap: 4px; flex: 1; }\n.fhl-seg-item {\n flex: 1;\n padding: 5px 0;\n font-size: 12px;\n border: 1px solid var(--fhl-border);\n border-radius: 8px;\n background: transparent;\n color: var(--fhl-fg-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.fhl-seg-item:hover { border-color: var(--fhl-accent); color: var(--fhl-fg); }\n.fhl-seg-item.is-active { background: var(--fhl-accent-soft); border-color: var(--fhl-accent); color: var(--fhl-accent); font-weight: 600; }\n\n.fhl-stage { display: flex; align-items: center; gap: 8px; }\n.fhl-stage-round { font-size: 12px; color: var(--fhl-fg-muted); }\n.fhl-timer {\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 700;\n color: var(--fhl-fg);\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--fhl-bg-elev);\n}\n.fhl-timer.is-low { color: var(--fhl-bad); background: var(--fhl-bad-soft); animation: fhl-blink 1s ease-in-out infinite; }\n@keyframes fhl-blink { 50% { opacity: 0.45; } }\n.fhl-stage-tag { margin-left: auto; font-size: 11px; color: var(--fhl-fg-muted); }\n\n.fhl-lingzi {\n text-align: center;\n font-size: 34px;\n font-weight: 800;\n line-height: 1.1;\n letter-spacing: 0.08em;\n color: var(--fhl-accent);\n}\n\n.fhl-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 10px 12px;\n border-radius: 12px;\n background: var(--fhl-bg-elev);\n border: 1px solid var(--fhl-border);\n}\n.fhl-card-role { font-size: 11px; color: var(--fhl-fg-muted); }\n.fhl-card-line { font-size: 15px; line-height: 1.5; }\n\n.fhl-verdict { font-size: 12px; color: var(--fhl-fg-muted); min-height: 16px; }\n\n.fhl-input-row { display: flex; gap: 6px; }\n.fhl-input {\n flex: 1;\n min-width: 0;\n padding: 7px 10px;\n font-size: 13px;\n border-radius: 9px;\n border: 1px solid var(--fhl-border);\n background: var(--fhl-bg);\n color: var(--fhl-fg);\n outline: none;\n transition: border-color 0.15s ease;\n}\n.fhl-input:focus { border-color: var(--fhl-accent); }\n.fhl-input:disabled { opacity: 0.5; }\n\n.fhl-actions { display: flex; gap: 6px; }\n.fhl-btn {\n flex: 1;\n padding: 7px 10px;\n font-size: 13px;\n border: 1px solid var(--fhl-border);\n border-radius: 9px;\n background: var(--fhl-bg-elev);\n color: var(--fhl-fg);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.fhl-btn:hover:not(:disabled) { border-color: var(--fhl-accent); }\n.fhl-btn:disabled { opacity: 0.45; cursor: default; }\n.fhl-btn-primary { background: var(--fhl-accent-soft); border-color: var(--fhl-accent); color: var(--fhl-accent); font-weight: 600; }\n.fhl-btn-danger { color: var(--fhl-bad); }\n.fhl-btn-danger:hover:not(:disabled) { border-color: var(--fhl-bad); background: var(--fhl-bad-soft); }\n\n.fhl-stats {\n display: flex;\n gap: 6px;\n justify-content: space-between;\n padding-top: 2px;\n border-top: 1px dashed var(--fhl-border);\n}\n.fhl-stat { font-size: 11px; color: var(--fhl-fg-muted); display: inline-flex; gap: 3px; align-items: baseline; }\n.fhl-stat b { color: var(--fhl-fg); font-size: 13px; }\n.fhl-stat.is-warn b { color: var(--fhl-bad); }\n\n.fhl-result { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 0 4px; }\n.fhl-result-icon { font-size: 40px; }\n.fhl-result-title { font-size: 18px; font-weight: 800; }\n.fhl-result.is-won .fhl-result-title { color: var(--fhl-ok); }\n.fhl-result.is-lost .fhl-result-title { color: var(--fhl-bad); }\n\n.fhl-record {\n display: flex;\n justify-content: center;\n gap: 10px;\n font-size: 11px;\n color: var(--fhl-fg-muted);\n}\n`\n\n/** 注入样式表;返回 disposer(卸载时移除 style 节点)。 */\nexport function injectStyles(): () => void {\n const style = document.createElement('style')\n style.setAttribute('data-plugin', 'dsh-plugin-feihualing')\n style.textContent = CSS\n document.head.appendChild(style)\n return () => {\n if (style.parentNode) style.parentNode.removeChild(style)\n }\n}\n","/**\n * 客户端半边共享常量。\n * NAMESPACE 用作插槽注册 id,须与包名(package.json name / cordis.patch.yml)一致。\n * @module dsh-plugin-feihualing/client/constants\n */\n\n/** 插件命名空间(与 cordis.patch.yml 的 id、Loader 行 name 保持一致)。 */\nexport const NAMESPACE = 'dsh-plugin-feihualing'\n\n/** 面板在会话标题操作行中的渲染顺序(靠后,避免挤占内置预设/任务按钮)。 */\nexport const HEADER_ORDER = 90\n","/**\n * 共享判定规则(Host 与 Client 浏览器两半共同使用)。\n * 纯函数、零平台依赖:不允许 import node:* / DOM —— 保证「对话判定」与\n * 「面板即时判定」对同一输入给出完全一致的结果。\n * @module dsh-plugin-feihualing/shared/rules\n */\n\n/** 游戏模式:simple 简易 / ancient 古法严格。 */\nexport type GameMode = 'simple' | 'ancient'\n\n/** 诗句尝试的最小/最大长度(规范化后字符数)。 */\nexport const MIN_LINE_CHARS = 4\nexport const MAX_LINE_CHARS = 48\n\n/** 古法令字位置循环上限(第 1 字 → 第 7 字 → 第 1 字)。 */\nexport const MAX_ANCIENT_POSITION = 7\n\n/**\n * 规范化一行文本:去除空白与常见中英文标点,仅保留汉字/字母/数字。\n * 用于关键词匹配与诗句去重比较。\n */\nexport function normalizeLine(raw: string): string {\n return raw.replace(/[\\s,。!?、;:\"\"''()《》〈〉【】\\[\\]·,.!?;:'\"()\\-—…]+/gu, '')\n}\n\n/** 去掉文本首尾空白(用于展示时压缩用户输入的换行/空格)。 */\nexport function trimDisplay(raw: string): string {\n return raw.trim().replace(/\\s+/gu, ' ').slice(0, MAX_LINE_CHARS)\n}\n\n/** 一次诗句判定的输入(与 GameState 中参与判定的字段一一对应)。 */\nexport interface JudgeInput {\n rawText: string\n lingzi: string\n mode: GameMode\n /** 古法本轮要求令字位置(1 起);simple 模式忽略。 */\n requiredPosition: number\n /** 已使用诗句的规范化集合(去重依据)。 */\n usedNormalized: readonly string[]\n}\n\n/** 判定结果分类。 */\nexport type JudgeKind =\n | 'notPoem' // 规范化后不含令字 → 视为普通聊天,不判定\n | 'length' // 长度不符(4-48 字之外)\n | 'duplicate' // 与已使用诗句重复\n | 'badPosition' // 古法模式位置不符\n | 'valid'\n\n/** 古法位置错误的细分原因(badPosition 时有效)。 */\nexport type BadPositionReason = 'tooShort' | 'mismatch'\n\nexport interface JudgeResult {\n kind: JudgeKind\n /** 规范化文本(valid 时用于登记去重与位置推进)。 */\n normalized: string\n /** 展示用文本(压缩空白、截断)。 */\n display: string\n /** 规范化后长度。 */\n length: number\n /** badPosition 的细分原因;其余 kind 时为 undefined。 */\n badPosReason?: BadPositionReason\n /** badPosition 时本轮要求的位置;其余 kind 时为 undefined。 */\n requiredPosition?: number\n}\n\n/**\n * 诗句判定核心(纯函数,Host/Client 共用)。\n * 判定顺序与对话版完全一致:含令字门槛 → 长度 → 去重 → 古法位置。\n */\nexport function judgePoemAttempt(input: JudgeInput): JudgeResult {\n const normalized = normalizeLine(input.rawText)\n const display = trimDisplay(input.rawText)\n // 不含令字的消息不视为诗句尝试(普通聊天直接忽略)\n if (!normalized.includes(input.lingzi)) {\n return { kind: 'notPoem', normalized, display, length: normalized.length }\n }\n if (normalized.length < MIN_LINE_CHARS || normalized.length > MAX_LINE_CHARS) {\n return { kind: 'length', normalized, display, length: normalized.length }\n }\n if (input.usedNormalized.includes(normalized)) {\n return { kind: 'duplicate', normalized, display, length: normalized.length }\n }\n if (input.mode === 'ancient') {\n const pos = input.requiredPosition\n if (normalized.length < pos) {\n return { kind: 'badPosition', badPosReason: 'tooShort', requiredPosition: pos, normalized, display, length: normalized.length }\n }\n if (normalized[pos - 1] !== input.lingzi) {\n return { kind: 'badPosition', badPosReason: 'mismatch', requiredPosition: pos, normalized, display, length: normalized.length }\n }\n }\n return { kind: 'valid', normalized, display, length: normalized.length }\n}\n","/**\n * 共享诗句库(Host 与 Client 两半共同使用;纯数据,零平台依赖)。\n * 用途:Host —— 提示(hint)与令字抽取;Client —— 内置 AI 对手出句与提示。\n * 判定本身不依赖本库(只查“含令字 + 未重复 + 古法位置”),因此诗句库的\n * 覆盖面只影响“对手出句 / 提示”的花样,不影响规则正确性。\n * 全部为公共领域经典诗句(唐/宋/元/清为主,个别汉魏、民歌与古乐府)。\n * @module dsh-plugin-feihualing/shared/poems\n */\n\n/** 令字 → 含该令字的经典诗句(规范化前原文,展示与去重用;每句必含令字)。 */\nexport const POEM_BANK: Readonly<Record<string, readonly string[]>> = Object.freeze({\n 花: Object.freeze([\n '花间一壶酒',\n '感时花溅泪',\n '人面桃花相映红',\n '霜叶红于二月花',\n '花落知多少',\n '春城无处不飞花',\n '不是花中偏爱菊',\n '黄四娘家花满蹊',\n '竹外桃花三两枝',\n '映日荷花别样红',\n '花重锦官城',\n '乱花渐欲迷人眼',\n ]),\n 月: Object.freeze([\n '床前明月光',\n '举头望明月',\n '海上生明月',\n '月落乌啼霜满天',\n '月是故乡明',\n '明月几时有',\n '二十四桥明月夜',\n '月上柳梢头',\n '露似真珠月似弓',\n '月黑雁飞高',\n '深林人不知,明月来相照',\n '明月松间照',\n '江清月近人',\n '海上明月共潮生',\n '杨柳岸,晓风残月',\n '月有阴晴圆缺',\n ]),\n 风: Object.freeze([\n '夜来风雨声',\n '春风又绿江南岸',\n '风吹草低见牛羊',\n '二月春风似剪刀',\n '春风不度玉门关',\n '长风破浪会有时',\n '随风潜入夜',\n '野火烧不尽,春风吹又生',\n '大风起兮云飞扬',\n '风萧萧兮易水寒',\n '昨夜西风凋碧树',\n '吹面不寒杨柳风',\n '古道西风瘦马',\n '北风卷地白草折',\n ]),\n 雪: Object.freeze([\n '窗含西岭千秋雪',\n '独钓寒江雪',\n '柴门闻犬吠,风雪夜归人',\n '北风吹雁雪纷纷',\n '雪上空留马行处',\n '北风卷地白草折,胡天八月即飞雪',\n '遥知不是雪,为有暗香来',\n '欲渡黄河冰塞川,将登太行雪满山',\n '燕山雪花大如席',\n '大雪满弓刀',\n '青海长云暗雪山',\n ]),\n 山: Object.freeze([\n '白日依山尽',\n '山重水复疑无路',\n '轻舟已过万重山',\n '空山不见人',\n '千山鸟飞绝',\n '会当凌绝顶,一览众山小',\n '相看两不厌,只有敬亭山',\n '远上寒山石径斜',\n '两岸青山相对出',\n '国破山河在',\n '青山遮不住',\n '山外青山楼外楼',\n '只在此山中,云深不知处',\n ]),\n 水: Object.freeze([\n '抽刀断水水更流',\n '桃花潭水深千尺',\n '一江春水向东流',\n '行到水穷处',\n '春来江水绿如蓝',\n '白毛浮绿水',\n '水光潋滟晴方好',\n '竹外桃花三两枝,春江水暖鸭先知',\n '曾经沧海难为水',\n '山重水复疑无路,柳暗花明又一村',\n '问君能有几多愁,恰似一江春水向东流',\n '流水落花春去也',\n ]),\n 云: Object.freeze([\n '黄河远上白云间',\n '只在此山中,云深不知处',\n '白云生处有人家',\n '行到水穷处,坐看云起时',\n '众鸟高飞尽,孤云独去闲',\n '黑云压城城欲摧',\n '白云千载空悠悠',\n '朝辞白帝彩云间',\n '云想衣裳花想容',\n '荡胸生层云',\n '青海长云暗雪山',\n '浮云游子意',\n ]),\n 雨: Object.freeze([\n '清明时节雨纷纷',\n '好雨知时节',\n '天街小雨润如酥',\n '巴山夜雨涨秋池',\n '山色空蒙雨亦奇',\n '夜阑卧听风吹雨',\n '渭城朝雨浥轻尘',\n '寒雨连江夜入吴',\n '斜风细雨不须归',\n '沾衣欲湿杏花雨',\n '空山新雨后',\n ]),\n 春: Object.freeze([\n '春眠不觉晓',\n '春来江水绿如蓝',\n '春城无处不飞花',\n '春蚕到死丝方尽',\n '野火烧不尽,春风吹又生',\n '春色满园关不住',\n '等闲识得东风面,万紫千红总是春',\n '春潮带雨晚来急',\n '谁言寸草心,报得三春晖',\n '国破山河在,城春草木深',\n '春江潮水连海平',\n ]),\n 秋: Object.freeze([\n '空山新雨后,天气晚来秋',\n '湖光秋月两相和',\n '自古逢秋悲寂寥',\n '春种一粒粟,秋收万颗子',\n '银烛秋光冷画屏',\n '解落三秋叶,能开二月花',\n '万里悲秋常作客',\n '塞下秋来风景异',\n '洛阳城里见秋风',\n '秋风萧瑟天气凉',\n ]),\n 人: Object.freeze([\n '人生得意须尽欢',\n '人生自古谁无死',\n '人闲桂花落',\n '举杯邀明月,对影成三人',\n '路上行人欲断魂',\n '独在异乡为异客,每逢佳节倍思亲',\n '人面不知何处去',\n '但愿人长久',\n '前不见古人,后不见来者',\n '故人西辞黄鹤楼',\n '莫愁前路无知己,天下谁人不识君',\n '劝君更尽一杯酒,西出阳关无故人',\n '遥知兄弟登高处,遍插茱萸少一人',\n ]),\n 酒: Object.freeze([\n '花间一壶酒',\n '葡萄美酒夜光杯',\n '借问酒家何处有',\n '劝君更尽一杯酒',\n '对酒当歌',\n '金樽清酒斗十千',\n '酒入愁肠,化作相思泪',\n '今朝有酒今朝醉',\n '白日放歌须纵酒',\n '人生得意须尽欢,莫使金樽空对月',\n '明月几时有,把酒问青天',\n ]),\n 夜: Object.freeze([\n '夜来风雨声',\n '忽如一夜春风来',\n '巴山夜雨涨秋池',\n '夜发清溪向三峡',\n '姑苏城外寒山寺,夜半钟声到客船',\n '东风夜放花千树',\n '夜阑卧听风吹雨',\n '可怜九月初三夜',\n '随风直到夜郎西',\n '烟笼寒水月笼沙,夜泊秦淮近酒家',\n '夜深千帐灯',\n '月黑雁飞高,单于夜遁逃',\n ]),\n 江: Object.freeze([\n '孤帆远影碧空尽,唯见长江天际流',\n '春来江水绿如蓝',\n '春风又绿江南岸',\n '一江春水向东流',\n '天门中断楚江开',\n '寒雨连江夜入吴',\n '竹外桃花三两枝,春江水暖鸭先知',\n '月落乌啼霜满天,江枫渔火对愁眠',\n '江畔何人初见月,江月何年初照人',\n '朝辞白帝彩云间,千里江陵一日还',\n '不尽长江滚滚流',\n '杨柳青青江水平',\n ]),\n 日: Object.freeze([\n '白日依山尽',\n '日照香炉生紫烟',\n '千里江陵一日还',\n '映日荷花别样红',\n '锄禾日当午',\n '日出江花红胜火',\n '海日生残夜',\n '大漠孤烟直,长河落日圆',\n '日暮乡关何处是',\n '浮云游子意,落日故人情',\n '白日放歌须纵酒',\n '闲云潭影日悠悠',\n ]),\n 星: Object.freeze([\n '星垂平野阔',\n '迢迢牵牛星',\n '危楼高百尺,手可摘星辰',\n '微微风簇浪,散作满河星',\n '七八个星天外',\n '天阶夜色凉如水,卧看牵牛织女星',\n '昨夜星辰昨夜风',\n '星汉灿烂,若出其里',\n ]),\n 天: Object.freeze([\n '天街小雨润如酥',\n '黄河之水天上来',\n '飞流直下三千尺,疑是银河落九天',\n '天生我材必有用',\n '天门中断楚江开',\n '江天一色无纤尘',\n '天阶夜色凉如水',\n '天苍苍,野茫茫',\n '接天莲叶无穷碧',\n '蜀道之难,难于上青天',\n '天若有情天亦老',\n ]),\n 柳: Object.freeze([\n '羌笛何须怨杨柳',\n '客舍青青柳色新',\n '两个黄鹂鸣翠柳',\n '柳暗花明又一村',\n '月上柳梢头',\n '春城无处不飞花,寒食东风御柳斜',\n '此夜曲中闻折柳',\n '杨柳岸,晓风残月',\n '枝上柳绵吹又少',\n '最是一年春好处,绝胜烟柳满皇都',\n '芙蓉如面柳如眉',\n '杨柳青青江水平',\n ]),\n 雁: Object.freeze([\n '雁字回时,月满西楼',\n '塞下秋来风景异,衡阳雁去无留意',\n '北风吹雁雪纷纷',\n '征蓬出汉塞,归雁入胡天',\n '月黑雁飞高',\n '雁过也,正伤心,却是旧时相识',\n '秋风起兮白云飞,草木黄落兮雁南归',\n '乡书何处达,归雁洛阳边',\n '何处秋风至,萧萧送雁群',\n ]),\n 烟: Object.freeze([\n '日照香炉生紫烟',\n '大漠孤烟直',\n '烟笼寒水月笼沙',\n '草长莺飞二月天,拂堤杨柳醉春烟',\n '一蓑烟雨任平生',\n '南朝四百八十寺,多少楼台烟雨中',\n '故人西辞黄鹤楼,烟花三月下扬州',\n '日暮乡关何处是,烟波江上使人愁',\n '绿杨烟外晓寒轻',\n '最是一年春好处,绝胜烟柳满皇都',\n ]),\n 楼: Object.freeze([\n '故人西辞黄鹤楼',\n '山外青山楼外楼',\n '小楼昨夜又东风',\n '危楼高百尺',\n '欲穷千里目,更上一层楼',\n '无言独上西楼',\n '近水楼台先得月',\n '昨夜西风凋碧树,独上高楼',\n '楼船夜雪瓜洲渡',\n '雁字回时,月满西楼',\n '多少楼台烟雨中',\n '明月楼高休独倚',\n ]),\n 梦: Object.freeze([\n '夜阑卧听风吹雨,铁马冰河入梦来',\n '梦里不知身是客',\n '人生如梦,一尊还酹江月',\n '庄生晓梦迷蝴蝶',\n '故人入我梦,明我长相忆',\n '夜来幽梦忽还乡',\n '梦回吹角连营',\n '因思杜陵梦,凫雁满回塘',\n ]),\n})\n\n/** 开局/换字时随机抽取的令字池(即诗句库的键)。 */\nexport const LINGZI_POOL: readonly string[] = Object.freeze(Object.keys(POEM_BANK))\n\n/** 常见(好接)令字:简易/中等难度可选池。 */\nconst COMMON_LINGZI: ReadonlySet<string> = new Set([\n '花', '月', '风', '雪', '山', '水', '云', '雨', '春', '秋', '人', '酒', '夜', '江', '日', '星', '天',\n])\n\n/** 简易(常见)令字池。 */\nexport const EASY_LINGZI: readonly string[] = Object.freeze(\n LINGZI_POOL.filter((z) => COMMON_LINGZI.has(z)),\n)\n\n/** 困难(偏门)令字池。 */\nexport const HARD_LINGZI: readonly string[] = Object.freeze(\n LINGZI_POOL.filter((z) => !COMMON_LINGZI.has(z)),\n)\n","/**\n * 内置 AI 对手 · 即时对战引擎(纯逻辑,零平台依赖、不依赖 React)。\n * 与 Host 共用 shared/rules.ts 的判定与 shared/poems.ts 的诗库:\n * - 判定:judgePoemAttempt —— 面板即时判定与对话判定规则完全一致;\n * - 双边去重:对手(bot)出句与玩家诗句都登记进 used 集合,杜绝重复;\n * - 对手出句:从诗库按难度取未使用句;库句不足即判玩家获胜。\n * @module dsh-plugin-feihualing/client/game/engine\n */\n\nimport { judgePoemAttempt, MAX_ANCIENT_POSITION, normalizeLine } from '../../shared/rules.ts'\nimport type { GameMode } from '../../shared/rules.ts'\nimport { EASY_LINGZI, HARD_LINGZI, LINGZI_POOL, POEM_BANK } from '../../shared/poems.ts'\n\n/** 对手难度。 */\nexport type Difficulty = 'easy' | 'medium' | 'hard'\n\n/** 对局阶段。 */\nexport type MatchPhase = 'playing' | 'won' | 'lost'\n\n/** 本局上限题数(每键诗库 ≥8 句,扣除双方占用后仍充足)。 */\nexport const MAX_ROUNDS = 7\n\n/** 失败(超时)上限,达到即判负。 */\nexport const FAIL_LIMIT = 3\n\n/** 每局提示次数。 */\nexport const HINTS_PER_MATCH = 3\n\n/** 每题限时(秒),按难度递减。 */\nexport const TIME_LIMIT: Readonly<Record<Difficulty, number>> = {\n easy: 30,\n medium: 20,\n hard: 15,\n}\n\n/** 基础得分:ancient 更严格故更高。 */\nconst SCORE_BASE: Readonly<Record<GameMode, number>> = { simple: 10, ancient: 15 }\n\n/** 连击加成(每段连续 +2)。 */\nconst COMBO_BONUS = 2\n\n/** 使用提示后本题通过的基础分。 */\nconst HINT_SCORE = 5\n\n/** 对局快照。 */\nexport interface MatchState {\n phase: MatchPhase\n mode: GameMode\n difficulty: Difficulty\n /** 当前令字。 */\n lingzi: string\n /** 当前题号(0 起;玩家每答对一题推进)。 */\n roundIndex: number\n /** 玩家已成功题数(roundIndex 与失败推进的解耦计数)。 */\n solved: number\n /** 失败次数(超时)。 */\n failures: number\n /** 累计得分。 */\n score: number\n /** 当前连击数(连续答对;0 表示断连)。 */\n combo: number\n /** 双方已用诗句(展示原文)。 */\n usedRaw: string[]\n /** 双方已用诗句(规范化,双边去重依据)。 */\n usedNormalized: string[]\n /** 对手当前出的题(玩家要接的句子)。 */\n challenge: string\n /** 本回合是否使用过提示(使用后本题通过只计 HINT_SCORE 且断连击)。 */\n hintUsedThisRound: boolean\n /** 剩余提示次数。 */\n hintsLeft: number\n /** 最近一次判定/事件文案(面板状态条展示)。 */\n lastVerdict: string\n /** 古法模式本轮要求位置(1 起);simple 恒 1。 */\n requiredPosition: number\n startedAt: number\n}\n\n/** 单局设置。 */\nexport interface MatchOptions {\n mode: GameMode\n difficulty: Difficulty\n}\n\n/** 玩家提交的裁决结果。 */\nexport interface SubmitOutcome {\n state: MatchState\n /** 是否本题通过(得分推进)。 */\n ok: boolean\n}\n\n// ---------- 内部小工具 ----------\n\nfunction pick<T>(list: readonly T[]): T {\n return list[Math.floor(Math.random() * list.length)]\n}\n\nfunction clone(state: MatchState): MatchState {\n return {\n ...state,\n usedRaw: [...state.usedRaw],\n usedNormalized: [...state.usedNormalized],\n }\n}\n\n/** 按难度抽令字:hard 用偏门字池,其余用常见字池。 */\nfunction pickLingzi(difficulty: Difficulty): string {\n const pool = difficulty === 'hard' ? HARD_LINGZI : EASY_LINGZI\n return pick(pool.length > 0 ? pool : LINGZI_POOL)\n}\n\n/** 诗库中该令字的全部句子。 */\nfunction bankOf(lingzi: string): readonly string[] {\n return POEM_BANK[lingzi] ?? []\n}\n\n/**\n * 从诗库挑一句未使用的句子(对手出句/提示候选)。\n * @returns 命中返回展示文本,库句被双方用完返回 undefined。\n */\nfunction pickUnused(state: MatchState): string | undefined {\n const candidates = bankOf(state.lingzi).filter(\n (line) => !state.usedNormalized.includes(normalizeLine(line)),\n )\n if (candidates.length === 0) return undefined\n return pick(candidates)\n}\n\n// ---------- 公开 API ----------\n\n/** 创建一局(抽令字 + 对手先手出题)。 */\nexport function createMatch(options: MatchOptions): MatchState {\n const lingzi = pickLingzi(options.difficulty)\n const base: MatchState = {\n phase: 'playing',\n mode: options.mode,\n difficulty: options.difficulty,\n lingzi,\n roundIndex: 0,\n solved: 0,\n failures: 0,\n score: 0,\n combo: 0,\n usedRaw: [],\n usedNormalized: [],\n challenge: '',\n hintUsedThisRound: false,\n hintsLeft: HINTS_PER_MATCH,\n lastVerdict: '',\n requiredPosition: 1,\n startedAt: Date.now(),\n }\n const opener = pickUnused(base)\n if (opener === undefined) {\n // 库为空(理论上不会发生):以空白开局仍可玩\n return { ...base, lastVerdict: `令字「${lingzi}」,请出句` }\n }\n return {\n ...base,\n challenge: opener,\n usedRaw: [opener],\n usedNormalized: [normalizeForUsed(opener)],\n lastVerdict: `令字「${lingzi}」· 对手出题,请对诗`,\n }\n}\n\n/** 仅规范化用于登记的辅助(与判定同源)。 */\nfunction normalizeForUsed(raw: string): string {\n return normalizeLine(raw)\n}\n\n/** 生成\"通过\"时的结算文案与计分(advance = 是否推进题号)。 */\nfunction applySolved(state: MatchState, display: string): MatchState {\n const comboBefore = state.combo\n const hintPenalty = state.hintUsedThisRound\n const gain = hintPenalty\n ? HINT_SCORE\n : SCORE_BASE[state.mode] + comboBefore * COMBO_BONUS\n const next = clone(state)\n next.solved += 1\n next.combo = hintPenalty ? 0 : comboBefore + 1\n next.score += gain\n next.usedRaw.push(display)\n next.usedNormalized.push(normalizeForUsed(display))\n if (next.mode === 'ancient') {\n next.requiredPosition = (next.requiredPosition % MAX_ANCIENT_POSITION) + 1\n }\n next.roundIndex += 1\n next.hintUsedThisRound = false\n const streak = next.combo\n next.lastVerdict = `答对!+${gain}${streak >= 2 ? `(连击 ×${streak})` : ''} · ${next.score} 分`\n return next\n}\n\n/** 推进到下一题(对手换句);无句可出或已到上限即玩家获胜。 */\nfunction advance(state: MatchState, why: string): MatchState {\n if (state.roundIndex >= MAX_ROUNDS) {\n return { ...clone(state), phase: 'won', lastVerdict: `通关!连对 ${state.solved} 题,共 ${state.score} 分(${why})` }\n }\n const nextLine = pickUnused(state)\n if (nextLine === undefined) {\n const won = clone(state)\n won.phase = 'won'\n won.lastVerdict = `对手诗穷,你赢了!共 ${state.score} 分(${why})`\n return won\n }\n const next = clone(state)\n next.challenge = nextLine\n next.usedRaw.push(nextLine)\n next.usedNormalized.push(normalizeForUsed(nextLine))\n next.lastVerdict = `${state.lastVerdict} —— 对手换题`\n return next\n}\n\n/** 玩家提交诗句。 */\nexport function submitPlayerLine(state: MatchState, rawText: string): SubmitOutcome {\n if (state.phase !== 'playing') return { state, ok: false }\n const result = judgePoemAttempt({\n rawText,\n lingzi: state.lingzi,\n mode: state.mode,\n requiredPosition: state.requiredPosition,\n usedNormalized: state.usedNormalized,\n })\n if (result.kind === 'notPoem') {\n return { state: { ...state, lastVerdict: `这句没有令字「${state.lingzi}」,请重新对诗` }, ok: false }\n }\n if (result.kind === 'length') {\n return { state: { ...state, lastVerdict: '诗句长度需在 4-48 字之间' }, ok: false }\n }\n if (result.kind === 'duplicate') {\n return { state: { ...state, lastVerdict: '这句用过了(双方都不能重复),换一句吧' }, ok: false }\n }\n if (result.kind === 'badPosition') {\n const pos = result.requiredPosition ?? state.requiredPosition\n const why = result.badPosReason === 'tooShort'\n ? `句子太短:古法要求令字位于第 ${pos} 字`\n : `位置不对:古法要求第 ${pos} 字是令字「${state.lingzi}」`\n return { state: { ...state, lastVerdict: why }, ok: false }\n }\n // 通过\n const solved = applySolved(state, result.display)\n const advanced = advance(solved, '答对推进')\n // 若本轮因对答通关/对手诗穷而 won,保留\n return { state: advanced, ok: true }\n}\n\n/** 超时(由面板倒计时触发):本题作废,换题;连续失败达上限判负。 */\nexport function timeoutRound(state: MatchState): MatchState {\n if (state.phase !== 'playing') return state\n const next = clone(state)\n next.failures += 1\n next.combo = 0\n next.hintUsedThisRound = false\n if (next.failures >= FAIL_LIMIT) {\n next.phase = 'lost'\n next.lastVerdict = `超时 ${FAIL_LIMIT} 次,本局惜败 · 共答对 ${next.solved} 题 / ${next.score} 分`\n return next\n }\n next.lastVerdict = `超时(第 ${next.failures}/${FAIL_LIMIT} 次)—— 换下一题,加油`\n return advance(next, '超时换题')\n}\n\n/** 认输/放弃。 */\nexport function concedeMatch(state: MatchState): MatchState {\n if (state.phase !== 'playing') return state\n const next = clone(state)\n next.phase = 'lost'\n next.lastVerdict = `你认输了 · 共答对 ${next.solved} 题 / ${next.score} 分`\n return next\n}\n\n/** 提示:消耗 1 次,给出库中一句未用参考诗(可直接跟对,但只计 HINT_SCORE 且断连击)。 */\nexport function useHint(state: MatchState): { state: MatchState; line?: string } {\n if (state.phase !== 'playing' || state.hintsLeft <= 0) return { state }\n const next = clone(state)\n next.hintsLeft -= 1\n next.hintUsedThisRound = true\n const line = pickUnused(next)\n next.lastVerdict = line\n ? `提示(剩 ${next.hintsLeft} 次):参考「${line}」—— 参考句直接对出只 +${HINT_SCORE} 分`\n : `提示(剩 ${next.hintsLeft} 次):想一句含「${state.lingzi}」的诗句吧`\n return { state: next, line }\n}\n\n/** 换令字重开本局(保留设置)。 */\nexport function restartMatch(options: MatchOptions): MatchState {\n return createMatch(options)\n}\n","/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n var key = null;\n void 0 !== maybeKey && (key = \"\" + maybeKey);\n void 0 !== config.key && (key = \"\" + config.key);\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n config = maybeKey.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== config ? config : null,\n props: maybeKey\n };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","/**\n * 浮层游戏面板(完整对战 UI)。\n * 对局状态由 HeaderButton 层持有(useGamePanel),本组件只做展示与事件转发:\n * 关闭面板只隐藏,不丢对局;隐藏期间倒计时暂停(视同暂停)。\n * @module dsh-plugin-feihualing/client/panel\n */\n\nimport React from 'react'\nimport * as engine from './game/engine.ts'\nimport type { GamePanelApi } from './game/useGame.ts'\n\nexport interface GamePanelProps {\n /** 所属会话 id(session 级 header 按钮注入)。 */\n sessionId: string\n /** 关闭面板(仅隐藏,对局保留)。 */\n onClose: () => void\n /** 对局控制面(由父层 useGamePanel 提供)。 */\n api: GamePanelApi\n}\n\n/** 会话短标识(展示用)。 */\nfunction shortSession(sessionId: string): string {\n return sessionId.length > 12 ? `${sessionId.slice(0, 10)}…` : sessionId\n}\n\nconst MODE_LABEL: Record<string, string> = { simple: '简易', ancient: '古法' }\nconst DIFF_LABEL: Record<string, string> = { easy: '轻松', medium: '标准', hard: '困难' }\nconst DIFF_HINT: Record<string, string> = { easy: '常见令字 · 30s', medium: '常见令字 · 20s', hard: '冷门令字 · 15s' }\n\n/** 开局设置行(未开局或对局结束后显示)。 */\nfunction SetupView(props: { api: GamePanelApi }): React.ReactElement {\n const { api } = props\n return (\n <React.Fragment>\n <div className=\"fhl-row\">\n <span className=\"fhl-seg-label\">模式</span>\n <div className=\"fhl-seg\">\n {(['simple', 'ancient'] as const).map((m) => (\n <button\n key={m}\n type=\"button\"\n className={`fhl-seg-item${api.mode === m ? ' is-active' : ''}`}\n onClick={() => api.setMode(m)}\n >\n {MODE_LABEL[m]}\n </button>\n ))}\n </div>\n </div>\n <div className=\"fhl-row\">\n <span className=\"fhl-seg-label\">难度</span>\n <div className=\"fhl-seg\">\n {(['easy', 'medium', 'hard'] as const).map((d) => (\n <button\n key={d}\n type=\"button\"\n className={`fhl-seg-item${api.difficulty === d ? ' is-active' : ''}`}\n onClick={() => api.setDifficulty(d)}\n title={DIFF_HINT[d]}\n >\n {DIFF_LABEL[d]}\n </button>\n ))}\n </div>\n </div>\n <div className=\"fhl-muted\" style={{ fontSize: 12, textAlign: 'center', paddingBottom: 4 }}>\n {api.mode === 'ancient'\n ? '古法:令字须按位置循环(第 1 字 → 第 7 字)'\n : '简易:诗句任意位置含令字即可'}\n {' · '}\n {DIFF_HINT[api.difficulty]}\n </div>\n <button type=\"button\" className=\"fhl-btn fhl-btn-primary\" onClick={api.start}>\n 🎴 开始对局\n </button>\n <div className=\"fhl-muted\" style={{ fontSize: 11, textAlign: 'center' }}>\n 对手出题 · 你限时对诗 · 连对 {engine.MAX_ROUNDS} 题通关 · 超时 {engine.FAIL_LIMIT} 次惜败\n </div>\n </React.Fragment>\n )\n}\n\n/** 进行中对局视图。 */\nfunction PlayView(props: { api: GamePanelApi }): React.ReactElement {\n const { api } = props\n const match = api.match\n if (match === null) return <React.Fragment />\n const timeLow = api.secondsLeft <= 5\n const canAct = match.phase === 'playing'\n return (\n <React.Fragment>\n <div className=\"fhl-stage\">\n <span className=\"fhl-stage-round\">第 {match.roundIndex + 1}/{engine.MAX_ROUNDS} 题</span>\n <span className={`fhl-timer${timeLow && canAct ? ' is-low' : ''}`}>{api.secondsLeft}s</span>\n <span className=\"fhl-stage-tag\">{MODE_LABEL[match.mode]} · {DIFF_LABEL[match.difficulty]}</span>\n </div>\n <div className=\"fhl-lingzi\">「{match.lingzi}」</div>\n {match.mode === 'ancient' && (\n <div className=\"fhl-muted\" style={{ fontSize: 11, textAlign: 'center', marginTop: -4 }}>\n 古法:本轮令字须位于第 <b style={{ color: 'var(--fhl-accent)' }}>{match.requiredPosition}</b> 字\n </div>\n )}\n <div className=\"fhl-card fhl-card-bot\">\n <span className=\"fhl-card-role\">🤖 对手</span>\n <span className=\"fhl-card-line\">{match.challenge}</span>\n </div>\n {match.lastVerdict !== '' && (\n <div className={`fhl-verdict${api.secondsLeft === 0 || !canAct ? '' : ''}`}>{match.lastVerdict}</div>\n )}\n <form\n className=\"fhl-input-row\"\n onSubmit={(ev) => {\n ev.preventDefault()\n api.submit()\n }}\n >\n <input\n className=\"fhl-input\"\n value={api.draft}\n onChange={(ev) => api.setDraft(ev.target.value)}\n placeholder={`说一句含「${match.lingzi}」的诗句…`}\n disabled={!canAct}\n autoComplete=\"off\"\n />\n <button type=\"submit\" className=\"fhl-btn fhl-btn-primary\" disabled={!canAct}>对诗</button>\n </form>\n <div className=\"fhl-actions\">\n <button\n type=\"button\"\n className=\"fhl-btn\"\n onClick={api.requestHint}\n disabled={!canAct || match.hintsLeft <= 0}\n title=\"消耗提示次数,给出参考诗句(参考句直接跟对只得 5 分)\"\n >\n 💡 提示({match.hintsLeft})\n </button>\n <button type=\"button\" className=\"fhl-btn fhl-btn-danger\" onClick={api.concede} disabled={!canAct}>\n 认输\n </button>\n </div>\n <div className=\"fhl-stats\">\n <span className=\"fhl-stat\"><b>{match.score}</b> 得分</span>\n <span className=\"fhl-stat\"><b>{match.combo}</b> 连击</span>\n <span className=\"fhl-stat\"><b>{match.solved}/{engine.MAX_ROUNDS}</b> 答对</span>\n <span className={`fhl-stat${match.failures >= engine.FAIL_LIMIT - 1 ? ' is-warn' : ''}`}>\n <b>{match.failures}/{engine.FAIL_LIMIT}</b> 超时\n </span>\n </div>\n </React.Fragment>\n )\n}\n\n/** 结算视图。 */\nfunction EndView(props: { api: GamePanelApi }): React.ReactElement {\n const { api } = props\n const match = api.match\n if (match === null) return <React.Fragment />\n const won = match.phase === 'won'\n return (\n <React.Fragment>\n <div className={`fhl-result${won ? ' is-won' : ' is-lost'}`}>\n <div className=\"fhl-result-icon\">{won ? '🏆' : '😢'}</div>\n <div className=\"fhl-result-title\">{won ? '通关!' : '本局惜败'}</div>\n <div className=\"fhl-muted\" style={{ fontSize: 12, textAlign: 'center' }}>{match.lastVerdict}</div>\n </div>\n <div className=\"fhl-stats\">\n <span className=\"fhl-stat\"><b>{match.score}</b> 得分</span>\n <span className=\"fhl-stat\"><b>{match.solved}</b> 答对</span>\n <span className=\"fhl-stat\"><b>{match.failures}</b> 超时</span>\n </div>\n <div className=\"fhl-record\">\n <span>🏅 {api.stats.wins} 胜 / {api.stats.losses} 负</span>\n <span>🔥 连胜 {api.stats.streak}</span>\n <span>✨ 最佳 {api.stats.bestScore}</span>\n </div>\n <button type=\"button\" className=\"fhl-btn fhl-btn-primary\" onClick={api.start}>\n 🔄 再来一局\n </button>\n <div className=\"fhl-muted\" style={{ fontSize: 11, textAlign: 'center' }}>\n 令字「{match.lingzi}」· {MODE_LABEL[match.mode]} · {DIFF_LABEL[match.difficulty]}\n </div>\n </React.Fragment>\n )\n}\n\n/** 面板主体:按对局阶段切换视图。 */\nexport function GamePanel(props: GamePanelProps): React.ReactElement {\n const { sessionId, onClose, api } = props\n const phase = api.match === null ? 'setup' : api.match.phase\n return (\n <div className=\"fhl-root fhl-panel\" data-plugin=\"dsh-plugin-feihualing\">\n <div className=\"fhl-panel-header\">\n <span className=\"fhl-panel-title\">🎴 飞花令 · 即时对战</span>\n <span className=\"fhl-tag\" title={sessionId}>会话 {shortSession(sessionId)}</span>\n <button type=\"button\" className=\"fhl-panel-close\" onClick={onClose} title=\"收起(对局保留)\">✕</button>\n </div>\n {phase === 'setup' && <SetupView api={api} />}\n {phase === 'playing' && <PlayView api={api} />}\n {(phase === 'won' || phase === 'lost') && <EndView api={api} />}\n </div>\n )\n}\n","/**\n * 面板对战战绩(localStorage 持久化,本机会话无关、跨刷新保留)。\n * 纯浏览器模块;与 Host 对话模式状态互不干扰(面板是独立即时战)。\n * @module dsh-plugin-feihualing/client/game/stats\n */\n\nimport type { MatchState } from './engine.ts'\n\nexport interface GameStats {\n /** 累计胜局。 */\n wins: number\n /** 累计败局。 */\n losses: number\n /** 历史最佳得分。 */\n bestScore: number\n /** 当前连胜。 */\n streak: number\n /** 历史最长连胜。 */\n bestStreak: number\n /** 累计答对题数。 */\n solvedTotal: number\n}\n\nconst KEY = 'dsh-plugin-feihualing:stats'\n\nfunction empty(): GameStats {\n return { wins: 0, losses: 0, bestScore: 0, streak: 0, bestStreak: 0, solvedTotal: 0 }\n}\n\n/** 读取战绩(损坏/缺失返回空战绩)。 */\nexport function loadStats(): GameStats {\n try {\n const raw = localStorage.getItem(KEY)\n if (raw === null) return empty()\n const parsed = JSON.parse(raw) as Partial<GameStats>\n return {\n wins: typeof parsed.wins === 'number' ? parsed.wins : 0,\n losses: typeof parsed.losses === 'number' ? parsed.losses : 0,\n bestScore: typeof parsed.bestScore === 'number' ? parsed.bestScore : 0,\n streak: typeof parsed.streak === 'number' ? parsed.streak : 0,\n bestStreak: typeof parsed.bestStreak === 'number' ? parsed.bestStreak : 0,\n solvedTotal: typeof parsed.solvedTotal === 'number' ? parsed.solvedTotal : 0,\n }\n } catch {\n return empty()\n }\n}\n\n/** 结算一局并入账(返回并持久化新战绩)。 */\nexport function recordResult(stats: GameStats, match: MatchState): GameStats {\n const won = match.phase === 'won'\n const next: GameStats = {\n ...stats,\n wins: stats.wins + (won ? 1 : 0),\n losses: stats.losses + (won ? 0 : 1),\n bestScore: Math.max(stats.bestScore, match.score),\n streak: won ? stats.streak + 1 : 0,\n bestStreak: Math.max(stats.bestStreak, won ? stats.streak + 1 : stats.bestStreak),\n solvedTotal: stats.solvedTotal + match.solved,\n }\n try {\n localStorage.setItem(KEY, JSON.stringify(next))\n } catch {\n /* 存储失败(隐私模式等)不影响对局 */\n }\n return next\n}\n","/**\n * React 对局 hook:包装 engine 的纯逻辑,管理倒计时与面板动作。\n * 倒计时由 useGamePanel 内部控制(setInterval,无第三方库)。\n * @module dsh-plugin-feihualing/client/game/useGame\n */\n\nimport React from 'react'\nimport * as engine from './engine.ts'\nimport type { GameStats } from './stats.ts'\nimport { loadStats, recordResult } from './stats.ts'\nimport type { GameMode } from '../../shared/rules.ts'\n\n/** 对局控制面(供面板组件使用)。 */\nexport interface GamePanelApi {\n match: engine.MatchState | null\n secondsLeft: number\n mode: GameMode\n difficulty: engine.Difficulty\n draft: string\n stats: GameStats\n setMode(mode: GameMode): void\n setDifficulty(difficulty: engine.Difficulty): void\n setDraft(text: string): void\n start(): void\n submit(): void\n requestHint(): void\n concede(): void\n}\n\n/**\n * 一局对战面板状态。倒计时只在对局进行中且面板激活时跑;\n * 面板隐藏(active=false)视同暂停:计时停住、不超时,重开面板继续。\n * 答题推进/超时换题时按 (phase, roundIndex, challenge) 变化重置计时。\n * @param active 面板是否可见(决定倒计时是否运行)。\n */\nexport function useGamePanel(active: boolean): GamePanelApi {\n const [match, setMatch] = React.useState<engine.MatchState | null>(null)\n const [secondsLeft, setSecondsLeft] = React.useState(0)\n const [mode, setMode] = React.useState<GameMode>('simple')\n const [difficulty, setDifficulty] = React.useState<engine.Difficulty>('medium')\n const [draft, setDraft] = React.useState('')\n const [stats, setStats] = React.useState<GameStats>(() => loadStats())\n // 记录已入账的局(按 startedAt),防止同一局重复累计战绩\n const recordedStartedAt = React.useRef<number | null>(null)\n\n // 结算(won/lost)时入账一次战绩\n React.useEffect(() => {\n if (match === null || (match.phase !== 'won' && match.phase !== 'lost')) return\n if (recordedStartedAt.current === match.startedAt) return\n recordedStartedAt.current = match.startedAt\n setStats((cur) => recordResult(cur, match))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [match])\n\n const roundKey = match === null\n ? ''\n : `${match.phase}:${match.roundIndex}:${match.challenge}`\n\n // 每一“题”(phase/题号/对手句变化)重置倒计时为满额限时\n React.useEffect(() => {\n if (match === null || match.phase !== 'playing') return\n setSecondsLeft(engine.TIME_LIMIT[match.difficulty])\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [roundKey])\n\n // 秒针:仅面板激活且对局进行中时递减;隐藏(active=false)即暂停并保留剩余时间\n React.useEffect(() => {\n if (!active || match === null || match.phase !== 'playing') return\n const timer = setInterval(() => {\n setSecondsLeft((s) => Math.max(0, s - 1))\n }, 1000)\n return () => clearInterval(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active, roundKey])\n\n // 倒计时归零:本题超时\n React.useEffect(() => {\n if (!active || match === null || match.phase !== 'playing') return\n if (secondsLeft > 0) return\n setMatch((cur) => (cur !== null && cur.phase === 'playing' ? engine.timeoutRound(cur) : cur))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [secondsLeft, active])\n\n const start = React.useCallback(() => {\n setMatch(engine.createMatch({ mode, difficulty }))\n setDraft('')\n }, [mode, difficulty])\n\n const submit = React.useCallback(() => {\n const text = draft.trim()\n if (text.length === 0) return\n setMatch((cur) => {\n if (cur === null || cur.phase !== 'playing') return cur\n const outcome = engine.submitPlayerLine(cur, text)\n return outcome.state\n })\n setDraft('')\n }, [draft])\n\n const requestHint = React.useCallback(() => {\n setMatch((cur) => {\n if (cur === null || cur.phase !== 'playing') return cur\n return engine.useHint(cur).state\n })\n }, [])\n\n const concede = React.useCallback(() => {\n setMatch((cur) => (cur === null ? cur : engine.concedeMatch(cur)))\n }, [])\n\n return {\n match,\n secondsLeft,\n mode,\n difficulty,\n draft,\n stats,\n setMode,\n setDifficulty,\n setDraft,\n start,\n submit,\n requestHint,\n concede,\n }\n}\n","/**\n * 会话标题游戏开关按钮(conversation.session.header.actions 插槽,session 级 list)。\n * 点击后直接渲染浮层游戏面板(fixed 定位,无需 react-dom portal——\n * client 半边仅依赖 DSH 模块表确认提供的 react)。\n * 对局状态常驻本组件:关闭面板只隐藏,不丢局;隐藏期间倒计时暂停(见 useGame)。\n * @module dsh-plugin-feihualing/client/header-button\n */\n\nimport React from 'react'\nimport { HEADER_ORDER, NAMESPACE } from './constants.ts'\nimport type { LocalContext } from './types.ts'\nimport { GamePanel } from './panel.tsx'\nimport { useGamePanel } from './game/useGame.ts'\n\n/** 在会话头操作行注册游戏开关按钮。 */\nexport function registerHeaderButton(ctx: LocalContext): void {\n ctx.slots.inject('conversation.session.header.actions', () => ctx.slots.register(\n {\n name: 'conversation.session.header.actions',\n id: NAMESPACE,\n order: HEADER_ORDER,\n inject: (sessionId) => ({ sessionId }),\n },\n HeaderButton,\n ))\n}\n\n/** 会话头游戏开关按钮 + 浮层面板(open 时渲染,fixed 定位于视口右上)。 */\nfunction HeaderButton(props: { sessionId?: string }): React.ReactElement {\n const [open, setOpen] = React.useState(false)\n // 对局状态提升到按钮层:面板开合不影响对局,隐藏 = 暂停计时\n const api = useGamePanel(open)\n const sessionId = props.sessionId ?? ''\n const toggle = React.useCallback(() => setOpen((prev) => !prev), [])\n\n return (\n <React.Fragment>\n <button\n type=\"button\"\n className=\"fhl-toggle\"\n data-active={open ? 'true' : 'false'}\n onClick={toggle}\n title={open ? '收起飞花令(对局保留)' : '飞花令小游戏'}\n aria-label=\"飞花令小游戏\"\n >\n 🎴\n </button>\n {open && <GamePanel sessionId={sessionId} onClose={() => setOpen(false)} api={api} />}\n </React.Fragment>\n )\n}\n","/**\n * 客户端半边入口(dsh-plugin-feihualing/client)。\n * 唯一职责:注入样式并组装各 UI 面注册(当前:会话头游戏开关按钮 + 浮层面板)。\n * 只做纯插槽注册,不受 harness 的 WEB_SETTINGS_NAMESPACES 白名单影响,装完即用。\n * @module dsh-plugin-feihualing/client\n */\n\nimport type { LocalContext } from './types.ts'\nimport { injectStyles } from './styles.ts'\nimport { registerHeaderButton } from './header-button.tsx'\n\n/** 依赖的服务:slots 就绪后本插件才会加载。 */\nexport const inject = ['slots']\n\n/** 客户端插件主体。 */\nexport function apply(ctx: LocalContext): void {\n // 样式表随插件生命周期注入/移除(ctx.effect 由 cordis 客户端运行时提供)\n const effect = (ctx as { effect?: (execute: () => (() => void) | void, label?: string) => void }).effect\n const disposeStyles = injectStyles()\n if (effect) {\n effect(() => disposeStyles, 'dsh-plugin-feihualing: client styles')\n }\n registerHeaderButton(ctx)\n}\n"],"x_google_ignoreList":[5,6],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiNZ,SAAgB,eAA2B;GACzC,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,aAAa,eAAe,uBAAuB;GACzD,MAAM,cAAc;GACpB,SAAS,KAAK,YAAY,KAAK;GAC/B,aAAa;IACX,IAAI,MAAM,YAAY,MAAM,WAAW,YAAY,KAAK;GAC1D;EACF;;;;;;;;;ECzNA,MAAa,YAAY;;;;;ECczB,SAAgB,cAAc,KAAqB;GACjD,OAAO,IAAI,QAAQ,iDAAiD,EAAE;EACxE;;EAGA,SAAgB,YAAY,KAAqB;GAC/C,OAAO,IAAI,KAAK,CAAC,CAAC,QAAQ,SAAS,GAAG,CAAC,CAAC,MAAM,GAAA,EAAiB;EACjE;;;;;EA0CA,SAAgB,iBAAiB,OAAgC;GAC/D,MAAM,aAAa,cAAc,MAAM,OAAO;GAC9C,MAAM,UAAU,YAAY,MAAM,OAAO;GAEzC,IAAI,CAAC,WAAW,SAAS,MAAM,MAAM,GACnC,OAAO;IAAE,MAAM;IAAW;IAAY;IAAS,QAAQ,WAAW;GAAO;GAE3E,IAAI,WAAW,SAAA,KAA2B,WAAW,SAAA,IACnD,OAAO;IAAE,MAAM;IAAU;IAAY;IAAS,QAAQ,WAAW;GAAO;GAE1E,IAAI,MAAM,eAAe,SAAS,UAAU,GAC1C,OAAO;IAAE,MAAM;IAAa;IAAY;IAAS,QAAQ,WAAW;GAAO;GAE7E,IAAI,MAAM,SAAS,WAAW;IAC5B,MAAM,MAAM,MAAM;IAClB,IAAI,WAAW,SAAS,KACtB,OAAO;KAAE,MAAM;KAAe,cAAc;KAAY,kBAAkB;KAAK;KAAY;KAAS,QAAQ,WAAW;IAAO;IAEhI,IAAI,WAAW,MAAM,OAAO,MAAM,QAChC,OAAO;KAAE,MAAM;KAAe,cAAc;KAAY,kBAAkB;KAAK;KAAY;KAAS,QAAQ,WAAW;IAAO;GAElI;GACA,OAAO;IAAE,MAAM;IAAS;IAAY;IAAS,QAAQ,WAAW;GAAO;EACzE;;;;;;;;;;;;ECnFA,MAAa,YAAyD,OAAO,OAAO;GAClF,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;;EAGD,MAAa,cAAiC,OAAO,OAAO,OAAO,KAAK,SAAS,CAAC;;EAGlF,MAAM,gCAAqC,IAAI,IAAI;GACjD;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;EAClF,CAAC;;EAGD,MAAa,cAAiC,OAAO,OACnD,YAAY,QAAQ,MAAM,cAAc,IAAI,CAAC,CAAC,CAChD;;EAGA,MAAa,cAAiC,OAAO,OACnD,YAAY,QAAQ,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC,CACjD;;ECxSA,MAAa,aAAmD;GAC9D,MAAM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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["React","React","engine.TIME_LIMIT","engine.timeoutRound","engine.createMatch","engine.submitPlayerLine","engine.useHint","engine.concedeMatch","React"],"sources":["../src/client/styles.ts","../src/client/constants.ts","../src/shared/rules.ts","../src/shared/poems.ts","../src/client/game/engine.ts","../src/client/panel.tsx","../src/client/game/stats.ts","../src/client/game/useGame.ts","../src/client/header-button.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * 客户端半边样式:一次性注入 <style>,全部 fhl-* 类以 data-plugin 根类名限定作用域。\n * 主题跟随 DSH:深色模式由 body[data-ds-dark-theme] 标识(官方约定),\n * 浅色/深色各写一套语义配色;不依赖任何宿主私有类名。\n * @module dsh-plugin-feihualing/client/styles\n */\n\nconst CSS = `\n[data-plugin=\"dsh-plugin-feihualing\"].fhl-root, .fhl-root {\n --fhl-bg: #ffffff;\n --fhl-bg-elev: #f4f4f5;\n --fhl-fg: #27272a;\n --fhl-fg-muted: #71717a;\n --fhl-accent: #8b5cf6;\n --fhl-accent-soft: rgba(139, 92, 246, 0.12);\n --fhl-border: rgba(0, 0, 0, 0.1);\n --fhl-ok: #16a34a;\n --fhl-ok-soft: rgba(22, 163, 74, 0.12);\n --fhl-bad: #dc2626;\n --fhl-bad-soft: rgba(220, 38, 38, 0.12);\n font-family: var(--dsh-content-font, ui-sans-serif, system-ui, \"PingFang SC\", \"Microsoft YaHei\", sans-serif);\n box-sizing: border-box;\n}\nbody[data-ds-dark-theme] .fhl-root {\n --fhl-bg: #18181b;\n --fhl-bg-elev: #27272a;\n --fhl-fg: #e4e4e7;\n --fhl-fg-muted: #a1a1aa;\n --fhl-accent: #a78bfa;\n --fhl-accent-soft: rgba(167, 139, 250, 0.16);\n --fhl-border: rgba(255, 255, 255, 0.12);\n --fhl-ok: #4ade80;\n --fhl-ok-soft: rgba(74, 222, 128, 0.16);\n --fhl-bad: #f87171;\n --fhl-bad-soft: rgba(248, 113, 113, 0.16);\n}\n.fhl-root *, .fhl-root *::before, .fhl-root *::after { box-sizing: border-box; }\n\n/* 会话头操作行的小按钮 */\n.fhl-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 24px;\n padding: 0 6px;\n border: 1px solid transparent;\n border-radius: 6px;\n background: transparent;\n color: var(--fhl-fg-muted);\n font-size: 14px;\n line-height: 1;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n}\n.fhl-toggle:hover { background: var(--fhl-accent-soft); color: var(--fhl-fg); }\n.fhl-toggle[data-active=\"true\"] { background: var(--fhl-accent-soft); color: var(--fhl-accent); }\n\n/* 浮层游戏面板(portal 到 body 的独立 surface,自带作用域) */\n.fhl-panel {\n position: fixed;\n top: 64px;\n right: 16px;\n width: 340px;\n max-width: calc(100vw - 24px);\n max-height: calc(100vh - 88px);\n overflow: auto;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n border-radius: 14px;\n background: var(--fhl-bg);\n color: var(--fhl-fg);\n border: 1px solid var(--fhl-border);\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);\n}\n.fhl-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.fhl-panel-title { font-weight: 700; font-size: 14px; letter-spacing: 0.02em; }\n.fhl-panel-close {\n border: none;\n background: transparent;\n color: var(--fhl-fg-muted);\n cursor: pointer;\n font-size: 14px;\n line-height: 1;\n padding: 4px;\n border-radius: 6px;\n}\n.fhl-panel-close:hover { background: var(--fhl-bad-soft); color: var(--fhl-bad); }\n.fhl-muted { color: var(--fhl-fg-muted); }\n.fhl-tag { font-size: 11px; color: var(--fhl-fg-muted); border: 1px solid var(--fhl-border); padding: 1px 6px; border-radius: 999px; }\n\n/* ---- 对战 UI ---- */\n.fhl-row { display: flex; align-items: center; gap: 8px; }\n.fhl-seg-label { font-size: 12px; color: var(--fhl-fg-muted); flex: none; width: 34px; }\n.fhl-seg { display: flex; gap: 4px; flex: 1; }\n.fhl-seg-item {\n flex: 1;\n padding: 5px 0;\n font-size: 12px;\n border: 1px solid var(--fhl-border);\n border-radius: 8px;\n background: transparent;\n color: var(--fhl-fg-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.fhl-seg-item:hover { border-color: var(--fhl-accent); color: var(--fhl-fg); }\n.fhl-seg-item.is-active { background: var(--fhl-accent-soft); border-color: var(--fhl-accent); color: var(--fhl-accent); font-weight: 600; }\n\n.fhl-stage { display: flex; align-items: center; gap: 8px; }\n.fhl-stage-round { font-size: 12px; color: var(--fhl-fg-muted); }\n.fhl-timer {\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 700;\n color: var(--fhl-fg);\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--fhl-bg-elev);\n}\n.fhl-timer.is-low { color: var(--fhl-bad); background: var(--fhl-bad-soft); animation: fhl-blink 1s ease-in-out infinite; }\n@keyframes fhl-blink { 50% { opacity: 0.45; } }\n.fhl-stage-tag { margin-left: auto; font-size: 11px; color: var(--fhl-fg-muted); }\n\n.fhl-lingzi {\n text-align: center;\n font-size: 34px;\n font-weight: 800;\n line-height: 1.1;\n letter-spacing: 0.08em;\n color: var(--fhl-accent);\n}\n\n.fhl-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 10px 12px;\n border-radius: 12px;\n background: var(--fhl-bg-elev);\n border: 1px solid var(--fhl-border);\n}\n.fhl-card-role { font-size: 11px; color: var(--fhl-fg-muted); }\n.fhl-card-line { font-size: 15px; line-height: 1.5; }\n\n.fhl-verdict { font-size: 12px; color: var(--fhl-fg-muted); min-height: 16px; }\n\n.fhl-input-row { display: flex; gap: 6px; }\n.fhl-input {\n flex: 1;\n min-width: 0;\n padding: 7px 10px;\n font-size: 13px;\n border-radius: 9px;\n border: 1px solid var(--fhl-border);\n background: var(--fhl-bg);\n color: var(--fhl-fg);\n outline: none;\n transition: border-color 0.15s ease;\n}\n.fhl-input:focus { border-color: var(--fhl-accent); }\n.fhl-input:disabled { opacity: 0.5; }\n\n.fhl-actions { display: flex; gap: 6px; }\n.fhl-btn {\n flex: 1;\n padding: 7px 10px;\n font-size: 13px;\n border: 1px solid var(--fhl-border);\n border-radius: 9px;\n background: var(--fhl-bg-elev);\n color: var(--fhl-fg);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.fhl-btn:hover:not(:disabled) { border-color: var(--fhl-accent); }\n.fhl-btn:disabled { opacity: 0.45; cursor: default; }\n.fhl-btn-primary { background: var(--fhl-accent-soft); border-color: var(--fhl-accent); color: var(--fhl-accent); font-weight: 600; }\n.fhl-btn-danger { color: var(--fhl-bad); }\n.fhl-btn-danger:hover:not(:disabled) { border-color: var(--fhl-bad); background: var(--fhl-bad-soft); }\n\n.fhl-stats {\n display: flex;\n gap: 6px;\n justify-content: space-between;\n padding-top: 2px;\n border-top: 1px dashed var(--fhl-border);\n}\n.fhl-stat { font-size: 11px; color: var(--fhl-fg-muted); display: inline-flex; gap: 3px; align-items: baseline; }\n.fhl-stat b { color: var(--fhl-fg); font-size: 13px; }\n.fhl-stat.is-warn b { color: var(--fhl-bad); }\n\n.fhl-result { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 0 4px; }\n.fhl-result-icon { font-size: 40px; }\n.fhl-result-title { font-size: 18px; font-weight: 800; }\n.fhl-result.is-won .fhl-result-title { color: var(--fhl-ok); }\n.fhl-result.is-lost .fhl-result-title { color: var(--fhl-bad); }\n\n.fhl-record {\n display: flex;\n justify-content: center;\n gap: 10px;\n font-size: 11px;\n color: var(--fhl-fg-muted);\n}\n`\n\n/** 注入样式表;返回 disposer(卸载时移除 style 节点)。 */\nexport function injectStyles(): () => void {\n const style = document.createElement('style')\n style.setAttribute('data-plugin', 'dsh-plugin-feihualing')\n style.textContent = CSS\n document.head.appendChild(style)\n return () => {\n if (style.parentNode) style.parentNode.removeChild(style)\n }\n}\n","/**\n * 客户端半边共享常量。\n * NAMESPACE 用作插槽注册 id,须与包名(package.json name / cordis.patch.yml)一致。\n * @module dsh-plugin-feihualing/client/constants\n */\n\n/** 插件命名空间(与 cordis.patch.yml 的 id、Loader 行 name 保持一致)。 */\nexport const NAMESPACE = 'dsh-plugin-feihualing'\n\n/** 面板在会话标题操作行中的渲染顺序(靠后,避免挤占内置预设/任务按钮)。 */\nexport const HEADER_ORDER = 90\n","/**\n * 共享判定规则(Host 与 Client 浏览器两半共同使用)。\n * 纯函数、零平台依赖:不允许 import node:* / DOM —— 保证「对话判定」与\n * 「面板即时判定」对同一输入给出完全一致的结果。\n * @module dsh-plugin-feihualing/shared/rules\n */\n\n/** 游戏模式:simple 简易 / ancient 古法严格。 */\nexport type GameMode = 'simple' | 'ancient'\n\n/** 诗句尝试的最小/最大长度(规范化后字符数)。 */\nexport const MIN_LINE_CHARS = 4\nexport const MAX_LINE_CHARS = 48\n\n/** 古法令字位置循环上限(第 1 字 → 第 7 字 → 第 1 字)。 */\nexport const MAX_ANCIENT_POSITION = 7\n\n/**\n * 规范化一行文本:去除空白与常见中英文标点,仅保留汉字/字母/数字。\n * 用于关键词匹配与诗句去重比较。\n */\nexport function normalizeLine(raw: string): string {\n return raw.replace(/[\\s,。!?、;:\"\"''()《》〈〉【】\\[\\]·,.!?;:'\"()\\-—…]+/gu, '')\n}\n\n/** 去掉文本首尾空白(用于展示时压缩用户输入的换行/空格)。 */\nexport function trimDisplay(raw: string): string {\n return raw.trim().replace(/\\s+/gu, ' ').slice(0, MAX_LINE_CHARS)\n}\n\n/** 一次诗句判定的输入(与 GameState 中参与判定的字段一一对应)。 */\nexport interface JudgeInput {\n rawText: string\n lingzi: string\n mode: GameMode\n /** 古法本轮要求令字位置(1 起);simple 模式忽略。 */\n requiredPosition: number\n /** 已使用诗句的规范化集合(去重依据)。 */\n usedNormalized: readonly string[]\n}\n\n/** 判定结果分类。 */\nexport type JudgeKind =\n | 'notPoem' // 规范化后不含令字 → 视为普通聊天,不判定\n | 'length' // 长度不符(4-48 字之外)\n | 'duplicate' // 与已使用诗句重复\n | 'badPosition' // 古法模式位置不符\n | 'valid'\n\n/** 古法位置错误的细分原因(badPosition 时有效)。 */\nexport type BadPositionReason = 'tooShort' | 'mismatch'\n\nexport interface JudgeResult {\n kind: JudgeKind\n /** 规范化文本(valid 时用于登记去重与位置推进)。 */\n normalized: string\n /** 展示用文本(压缩空白、截断)。 */\n display: string\n /** 规范化后长度。 */\n length: number\n /** badPosition 的细分原因;其余 kind 时为 undefined。 */\n badPosReason?: BadPositionReason\n /** badPosition 时本轮要求的位置;其余 kind 时为 undefined。 */\n requiredPosition?: number\n}\n\n/**\n * 诗句判定核心(纯函数,Host/Client 共用)。\n * 判定顺序与对话版完全一致:含令字门槛 → 长度 → 去重 → 古法位置。\n */\nexport function judgePoemAttempt(input: JudgeInput): JudgeResult {\n const normalized = normalizeLine(input.rawText)\n const display = trimDisplay(input.rawText)\n // 不含令字的消息不视为诗句尝试(普通聊天直接忽略)\n if (!normalized.includes(input.lingzi)) {\n return { kind: 'notPoem', normalized, display, length: normalized.length }\n }\n if (normalized.length < MIN_LINE_CHARS || normalized.length > MAX_LINE_CHARS) {\n return { kind: 'length', normalized, display, length: normalized.length }\n }\n if (input.usedNormalized.includes(normalized)) {\n return { kind: 'duplicate', normalized, display, length: normalized.length }\n }\n if (input.mode === 'ancient') {\n const pos = input.requiredPosition\n if (normalized.length < pos) {\n return { kind: 'badPosition', badPosReason: 'tooShort', requiredPosition: pos, normalized, display, length: normalized.length }\n }\n if (normalized[pos - 1] !== input.lingzi) {\n return { kind: 'badPosition', badPosReason: 'mismatch', requiredPosition: pos, normalized, display, length: normalized.length }\n }\n }\n return { kind: 'valid', normalized, display, length: normalized.length }\n}\n","/**\n * 共享诗句库(Host 与 Client 两半共同使用;纯数据,零平台依赖)。\n * 用途:Host —— 提示(hint)与令字抽取;Client —— 内置 AI 对手出句与提示。\n * 判定本身不依赖本库(只查“含令字 + 未重复 + 古法位置”),因此诗句库的\n * 覆盖面只影响“对手出句 / 提示”的花样,不影响规则正确性。\n * 全部为公共领域经典诗句(唐/宋/元/清为主,个别汉魏、民歌与古乐府)。\n * @module dsh-plugin-feihualing/shared/poems\n */\n\n/** 令字 → 含该令字的经典诗句(规范化前原文,展示与去重用;每句必含令字)。 */\nexport const POEM_BANK: Readonly<Record<string, readonly string[]>> = Object.freeze({\n 花: Object.freeze([\n '花间一壶酒',\n '感时花溅泪',\n '人面桃花相映红',\n '霜叶红于二月花',\n '花落知多少',\n '春城无处不飞花',\n '不是花中偏爱菊',\n '黄四娘家花满蹊',\n '竹外桃花三两枝',\n '映日荷花别样红',\n '花重锦官城',\n '乱花渐欲迷人眼',\n ]),\n 月: Object.freeze([\n '床前明月光',\n '举头望明月',\n '海上生明月',\n '月落乌啼霜满天',\n '月是故乡明',\n '明月几时有',\n '二十四桥明月夜',\n '月上柳梢头',\n '露似真珠月似弓',\n '月黑雁飞高',\n '深林人不知,明月来相照',\n '明月松间照',\n '江清月近人',\n '海上明月共潮生',\n '杨柳岸,晓风残月',\n '月有阴晴圆缺',\n ]),\n 风: Object.freeze([\n '夜来风雨声',\n '春风又绿江南岸',\n '风吹草低见牛羊',\n '二月春风似剪刀',\n '春风不度玉门关',\n '长风破浪会有时',\n '随风潜入夜',\n '野火烧不尽,春风吹又生',\n '大风起兮云飞扬',\n '风萧萧兮易水寒',\n '昨夜西风凋碧树',\n '吹面不寒杨柳风',\n '古道西风瘦马',\n '北风卷地白草折',\n ]),\n 雪: Object.freeze([\n '窗含西岭千秋雪',\n '独钓寒江雪',\n '柴门闻犬吠,风雪夜归人',\n '北风吹雁雪纷纷',\n '雪上空留马行处',\n '北风卷地白草折,胡天八月即飞雪',\n '遥知不是雪,为有暗香来',\n '欲渡黄河冰塞川,将登太行雪满山',\n '燕山雪花大如席',\n '大雪满弓刀',\n '青海长云暗雪山',\n ]),\n 山: Object.freeze([\n '白日依山尽',\n '山重水复疑无路',\n '轻舟已过万重山',\n '空山不见人',\n '千山鸟飞绝',\n '会当凌绝顶,一览众山小',\n '相看两不厌,只有敬亭山',\n '远上寒山石径斜',\n '两岸青山相对出',\n '国破山河在',\n '青山遮不住',\n '山外青山楼外楼',\n '只在此山中,云深不知处',\n ]),\n 水: Object.freeze([\n '抽刀断水水更流',\n '桃花潭水深千尺',\n '一江春水向东流',\n '行到水穷处',\n '春来江水绿如蓝',\n '白毛浮绿水',\n '水光潋滟晴方好',\n '竹外桃花三两枝,春江水暖鸭先知',\n '曾经沧海难为水',\n '山重水复疑无路,柳暗花明又一村',\n '问君能有几多愁,恰似一江春水向东流',\n '流水落花春去也',\n ]),\n 云: Object.freeze([\n '黄河远上白云间',\n '只在此山中,云深不知处',\n '白云生处有人家',\n '行到水穷处,坐看云起时',\n '众鸟高飞尽,孤云独去闲',\n '黑云压城城欲摧',\n '白云千载空悠悠',\n '朝辞白帝彩云间',\n '云想衣裳花想容',\n '荡胸生层云',\n '青海长云暗雪山',\n '浮云游子意',\n ]),\n 雨: Object.freeze([\n '清明时节雨纷纷',\n '好雨知时节',\n '天街小雨润如酥',\n '巴山夜雨涨秋池',\n '山色空蒙雨亦奇',\n '夜阑卧听风吹雨',\n '渭城朝雨浥轻尘',\n '寒雨连江夜入吴',\n '斜风细雨不须归',\n '沾衣欲湿杏花雨',\n '空山新雨后',\n ]),\n 春: Object.freeze([\n '春眠不觉晓',\n '春来江水绿如蓝',\n '春城无处不飞花',\n '春蚕到死丝方尽',\n '野火烧不尽,春风吹又生',\n '春色满园关不住',\n '等闲识得东风面,万紫千红总是春',\n '春潮带雨晚来急',\n '谁言寸草心,报得三春晖',\n '国破山河在,城春草木深',\n '春江潮水连海平',\n ]),\n 秋: Object.freeze([\n '空山新雨后,天气晚来秋',\n '湖光秋月两相和',\n '自古逢秋悲寂寥',\n '春种一粒粟,秋收万颗子',\n '银烛秋光冷画屏',\n '解落三秋叶,能开二月花',\n '万里悲秋常作客',\n '塞下秋来风景异',\n '洛阳城里见秋风',\n '秋风萧瑟天气凉',\n ]),\n 人: Object.freeze([\n '人生得意须尽欢',\n '人生自古谁无死',\n '人闲桂花落',\n '举杯邀明月,对影成三人',\n '路上行人欲断魂',\n '人面不知何处去',\n '但愿人长久',\n '前不见古人,后不见来者',\n '故人西辞黄鹤楼',\n '莫愁前路无知己,天下谁人不识君',\n '劝君更尽一杯酒,西出阳关无故人',\n '遥知兄弟登高处,遍插茱萸少一人',\n ]),\n 酒: Object.freeze([\n '花间一壶酒',\n '葡萄美酒夜光杯',\n '借问酒家何处有',\n '劝君更尽一杯酒',\n '对酒当歌',\n '金樽清酒斗十千',\n '酒入愁肠,化作相思泪',\n '今朝有酒今朝醉',\n '白日放歌须纵酒',\n '明月几时有,把酒问青天',\n ]),\n 夜: Object.freeze([\n '夜来风雨声',\n '忽如一夜春风来',\n '巴山夜雨涨秋池',\n '夜发清溪向三峡',\n '姑苏城外寒山寺,夜半钟声到客船',\n '东风夜放花千树',\n '夜阑卧听风吹雨',\n '可怜九月初三夜',\n '随风直到夜郎西',\n '烟笼寒水月笼沙,夜泊秦淮近酒家',\n '夜深千帐灯',\n '月黑雁飞高,单于夜遁逃',\n ]),\n 江: Object.freeze([\n '孤帆远影碧空尽,唯见长江天际流',\n '春来江水绿如蓝',\n '春风又绿江南岸',\n '一江春水向东流',\n '天门中断楚江开',\n '寒雨连江夜入吴',\n '竹外桃花三两枝,春江水暖鸭先知',\n '月落乌啼霜满天,江枫渔火对愁眠',\n '江畔何人初见月,江月何年初照人',\n '朝辞白帝彩云间,千里江陵一日还',\n '不尽长江滚滚流',\n '杨柳青青江水平',\n ]),\n 日: Object.freeze([\n '白日依山尽',\n '日照香炉生紫烟',\n '千里江陵一日还',\n '映日荷花别样红',\n '锄禾日当午',\n '日出江花红胜火',\n '海日生残夜',\n '大漠孤烟直,长河落日圆',\n '日暮乡关何处是',\n '浮云游子意,落日故人情',\n '白日放歌须纵酒',\n '闲云潭影日悠悠',\n ]),\n 星: Object.freeze([\n '星垂平野阔',\n '迢迢牵牛星',\n '危楼高百尺,手可摘星辰',\n '微微风簇浪,散作满河星',\n '七八个星天外',\n '天阶夜色凉如水,卧看牵牛织女星',\n '昨夜星辰昨夜风',\n '星汉灿烂,若出其里',\n ]),\n 天: Object.freeze([\n '天街小雨润如酥',\n '黄河之水天上来',\n '飞流直下三千尺,疑是银河落九天',\n '天生我材必有用',\n '天门中断楚江开',\n '江天一色无纤尘',\n '天阶夜色凉如水',\n '天苍苍,野茫茫',\n '接天莲叶无穷碧',\n '蜀道之难,难于上青天',\n '天若有情天亦老',\n ]),\n 柳: Object.freeze([\n '羌笛何须怨杨柳',\n '客舍青青柳色新',\n '两个黄鹂鸣翠柳',\n '柳暗花明又一村',\n '月上柳梢头',\n '春城无处不飞花,寒食东风御柳斜',\n '此夜曲中闻折柳',\n '杨柳岸,晓风残月',\n '枝上柳绵吹又少',\n '最是一年春好处,绝胜烟柳满皇都',\n '芙蓉如面柳如眉',\n '杨柳青青江水平',\n ]),\n 雁: Object.freeze([\n '雁字回时,月满西楼',\n '塞下秋来风景异,衡阳雁去无留意',\n '北风吹雁雪纷纷',\n '征蓬出汉塞,归雁入胡天',\n '月黑雁飞高',\n '雁过也,正伤心,却是旧时相识',\n '秋风起兮白云飞,草木黄落兮雁南归',\n '乡书何处达,归雁洛阳边',\n '何处秋风至,萧萧送雁群',\n ]),\n 烟: Object.freeze([\n '日照香炉生紫烟',\n '大漠孤烟直',\n '烟笼寒水月笼沙',\n '草长莺飞二月天,拂堤杨柳醉春烟',\n '一蓑烟雨任平生',\n '南朝四百八十寺,多少楼台烟雨中',\n '故人西辞黄鹤楼,烟花三月下扬州',\n '日暮乡关何处是,烟波江上使人愁',\n '绿杨烟外晓寒轻',\n '最是一年春好处,绝胜烟柳满皇都',\n ]),\n 楼: Object.freeze([\n '故人西辞黄鹤楼',\n '山外青山楼外楼',\n '小楼昨夜又东风',\n '危楼高百尺',\n '欲穷千里目,更上一层楼',\n '无言独上西楼',\n '近水楼台先得月',\n '昨夜西风凋碧树,独上高楼',\n '楼船夜雪瓜洲渡',\n '雁字回时,月满西楼',\n '多少楼台烟雨中',\n '明月楼高休独倚',\n ]),\n 梦: Object.freeze([\n '夜阑卧听风吹雨,铁马冰河入梦来',\n '梦里不知身是客',\n '人生如梦,一尊还酹江月',\n '庄生晓梦迷蝴蝶',\n '故人入我梦,明我长相忆',\n '夜来幽梦忽还乡',\n '梦回吹角连营',\n '因思杜陵梦,凫雁满回塘',\n ]),\n})\n\n/** 开局/换字时随机抽取的令字池(即诗句库的键)。 */\nexport const LINGZI_POOL: readonly string[] = Object.freeze(Object.keys(POEM_BANK))\n\n/** 常见(好接)令字:简易/中等难度可选池。 */\nconst COMMON_LINGZI: ReadonlySet<string> = new Set([\n '花', '月', '风', '雪', '山', '水', '云', '雨', '春', '秋', '人', '酒', '夜', '江', '日', '星', '天',\n])\n\n/** 简易(常见)令字池。 */\nexport const EASY_LINGZI: readonly string[] = Object.freeze(\n LINGZI_POOL.filter((z) => COMMON_LINGZI.has(z)),\n)\n\n/** 困难(偏门)令字池。 */\nexport const HARD_LINGZI: readonly string[] = Object.freeze(\n LINGZI_POOL.filter((z) => !COMMON_LINGZI.has(z)),\n)\n","/**\n * 内置 AI 对手 · 即时对战引擎(纯逻辑,零平台依赖、不依赖 React)。\n * 与 Host 共用 shared/rules.ts 的判定与 shared/poems.ts 的诗库:\n * - 判定:judgePoemAttempt —— 面板即时判定与对话判定规则完全一致;\n * - 双边去重:对手(bot)出句与玩家诗句都登记进 used 集合,杜绝重复;\n * - 对手出句:从诗库按难度取未使用句;库句不足即判玩家获胜。\n * @module dsh-plugin-feihualing/client/game/engine\n */\n\nimport { judgePoemAttempt, MAX_ANCIENT_POSITION, normalizeLine } from '../../shared/rules.ts'\nimport type { GameMode } from '../../shared/rules.ts'\nimport { EASY_LINGZI, HARD_LINGZI, LINGZI_POOL, POEM_BANK } from '../../shared/poems.ts'\n\n/** 对手难度。 */\nexport type Difficulty = 'easy' | 'medium' | 'hard'\n\n/** 对局阶段。 */\nexport type MatchPhase = 'playing' | 'won' | 'lost'\n\n/** 本局上限题数(每键诗库 ≥8 句,扣除双方占用后仍充足)。 */\nexport const MAX_ROUNDS = 7\n\n/** 失败(超时)上限,达到即判负。 */\nexport const FAIL_LIMIT = 3\n\n/** 每局提示次数。 */\nexport const HINTS_PER_MATCH = 3\n\n/** 每题限时(秒),按难度递减。 */\nexport const TIME_LIMIT: Readonly<Record<Difficulty, number>> = {\n easy: 30,\n medium: 20,\n hard: 15,\n}\n\n/** 基础得分:ancient 更严格故更高。 */\nconst SCORE_BASE: Readonly<Record<GameMode, number>> = { simple: 10, ancient: 15 }\n\n/** 连击加成(每段连续 +2)。 */\nconst COMBO_BONUS = 2\n\n/** 使用提示后本题通过的基础分。 */\nconst HINT_SCORE = 5\n\n/** 对局快照。 */\nexport interface MatchState {\n phase: MatchPhase\n mode: GameMode\n difficulty: Difficulty\n /** 当前令字。 */\n lingzi: string\n /** 当前题号(0 起;玩家每答对一题推进)。 */\n roundIndex: number\n /** 玩家已成功题数(roundIndex 与失败推进的解耦计数)。 */\n solved: number\n /** 失败次数(超时)。 */\n failures: number\n /** 累计得分。 */\n score: number\n /** 当前连击数(连续答对;0 表示断连)。 */\n combo: number\n /** 双方已用诗句(展示原文)。 */\n usedRaw: string[]\n /** 双方已用诗句(规范化,双边去重依据)。 */\n usedNormalized: string[]\n /** 对手当前出的题(玩家要接的句子)。 */\n challenge: string\n /** 本回合是否使用过提示(使用后本题通过只计 HINT_SCORE 且断连击)。 */\n hintUsedThisRound: boolean\n /** 剩余提示次数。 */\n hintsLeft: number\n /** 最近一次判定/事件文案(面板状态条展示)。 */\n lastVerdict: string\n /** 古法模式本轮要求位置(1 起);simple 恒 1。 */\n requiredPosition: number\n startedAt: number\n}\n\n/** 单局设置。 */\nexport interface MatchOptions {\n mode: GameMode\n difficulty: Difficulty\n}\n\n/** 玩家提交的裁决结果。 */\nexport interface SubmitOutcome {\n state: MatchState\n /** 是否本题通过(得分推进)。 */\n ok: boolean\n}\n\n// ---------- 内部小工具 ----------\n\nfunction pick<T>(list: readonly T[]): T {\n return list[Math.floor(Math.random() * list.length)]\n}\n\nfunction clone(state: MatchState): MatchState {\n return {\n ...state,\n usedRaw: [...state.usedRaw],\n usedNormalized: [...state.usedNormalized],\n }\n}\n\n/** 按难度抽令字:hard 用偏门字池,其余用常见字池。 */\nfunction pickLingzi(difficulty: Difficulty): string {\n const pool = difficulty === 'hard' ? HARD_LINGZI : EASY_LINGZI\n return pick(pool.length > 0 ? pool : LINGZI_POOL)\n}\n\n/** 诗库中该令字的全部句子。 */\nfunction bankOf(lingzi: string): readonly string[] {\n return POEM_BANK[lingzi] ?? []\n}\n\n/**\n * 从诗库挑一句未使用的句子(对手出句/提示候选)。\n * @returns 命中返回展示文本,库句被双方用完返回 undefined。\n */\nfunction pickUnused(state: MatchState): string | undefined {\n const candidates = bankOf(state.lingzi).filter(\n (line) => !state.usedNormalized.includes(normalizeLine(line)),\n )\n if (candidates.length === 0) return undefined\n return pick(candidates)\n}\n\n// ---------- 公开 API ----------\n\n/** 创建一局(抽令字 + 对手先手出题)。 */\nexport function createMatch(options: MatchOptions): MatchState {\n const lingzi = pickLingzi(options.difficulty)\n const base: MatchState = {\n phase: 'playing',\n mode: options.mode,\n difficulty: options.difficulty,\n lingzi,\n roundIndex: 0,\n solved: 0,\n failures: 0,\n score: 0,\n combo: 0,\n usedRaw: [],\n usedNormalized: [],\n challenge: '',\n hintUsedThisRound: false,\n hintsLeft: HINTS_PER_MATCH,\n lastVerdict: '',\n requiredPosition: 1,\n startedAt: Date.now(),\n }\n const opener = pickUnused(base)\n if (opener === undefined) {\n // 库为空(理论上不会发生):以空白开局仍可玩\n return { ...base, lastVerdict: `令字「${lingzi}」,请出句` }\n }\n return {\n ...base,\n challenge: opener,\n usedRaw: [opener],\n usedNormalized: [normalizeForUsed(opener)],\n lastVerdict: `令字「${lingzi}」· 对手出题,请对诗`,\n }\n}\n\n/** 仅规范化用于登记的辅助(与判定同源)。 */\nfunction normalizeForUsed(raw: string): string {\n return normalizeLine(raw)\n}\n\n/** 生成\"通过\"时的结算文案与计分(advance = 是否推进题号)。 */\nfunction applySolved(state: MatchState, display: string): MatchState {\n const comboBefore = state.combo\n const hintPenalty = state.hintUsedThisRound\n const gain = hintPenalty\n ? HINT_SCORE\n : SCORE_BASE[state.mode] + comboBefore * COMBO_BONUS\n const next = clone(state)\n next.solved += 1\n next.combo = hintPenalty ? 0 : comboBefore + 1\n next.score += gain\n next.usedRaw.push(display)\n next.usedNormalized.push(normalizeForUsed(display))\n if (next.mode === 'ancient') {\n next.requiredPosition = (next.requiredPosition % MAX_ANCIENT_POSITION) + 1\n }\n next.roundIndex += 1\n next.hintUsedThisRound = false\n const streak = next.combo\n next.lastVerdict = `答对!+${gain}${streak >= 2 ? `(连击 ×${streak})` : ''} · ${next.score} 分`\n return next\n}\n\n/** 推进到下一题(对手换句);无句可出或已到上限即玩家获胜。 */\nfunction advance(state: MatchState, why: string): MatchState {\n if (state.roundIndex >= MAX_ROUNDS) {\n return { ...clone(state), phase: 'won', lastVerdict: `通关!连对 ${state.solved} 题,共 ${state.score} 分(${why})` }\n }\n const nextLine = pickUnused(state)\n if (nextLine === undefined) {\n const won = clone(state)\n won.phase = 'won'\n won.lastVerdict = `对手诗穷,你赢了!共 ${state.score} 分(${why})`\n return won\n }\n const next = clone(state)\n next.challenge = nextLine\n next.usedRaw.push(nextLine)\n next.usedNormalized.push(normalizeForUsed(nextLine))\n next.lastVerdict = `${state.lastVerdict} —— 对手换题`\n return next\n}\n\n/** 玩家提交诗句。 */\nexport function submitPlayerLine(state: MatchState, rawText: string): SubmitOutcome {\n if (state.phase !== 'playing') return { state, ok: false }\n const result = judgePoemAttempt({\n rawText,\n lingzi: state.lingzi,\n mode: state.mode,\n requiredPosition: state.requiredPosition,\n usedNormalized: state.usedNormalized,\n })\n if (result.kind === 'notPoem') {\n return { state: { ...state, lastVerdict: `这句没有令字「${state.lingzi}」,请重新对诗` }, ok: false }\n }\n if (result.kind === 'length') {\n return { state: { ...state, lastVerdict: '诗句长度需在 4-48 字之间' }, ok: false }\n }\n if (result.kind === 'duplicate') {\n return { state: { ...state, lastVerdict: '这句用过了(双方都不能重复),换一句吧' }, ok: false }\n }\n if (result.kind === 'badPosition') {\n const pos = result.requiredPosition ?? state.requiredPosition\n const why = result.badPosReason === 'tooShort'\n ? `句子太短:古法要求令字位于第 ${pos} 字`\n : `位置不对:古法要求第 ${pos} 字是令字「${state.lingzi}」`\n return { state: { ...state, lastVerdict: why }, ok: false }\n }\n // 通过\n const solved = applySolved(state, result.display)\n const advanced = advance(solved, '答对推进')\n // 若本轮因对答通关/对手诗穷而 won,保留\n return { state: advanced, ok: true }\n}\n\n/** 超时(由面板倒计时触发):本题作废,换题;连续失败达上限判负。 */\nexport function timeoutRound(state: MatchState): MatchState {\n if (state.phase !== 'playing') return state\n const next = clone(state)\n next.failures += 1\n next.combo = 0\n next.hintUsedThisRound = false\n if (next.failures >= FAIL_LIMIT) {\n next.phase = 'lost'\n next.lastVerdict = `超时 ${FAIL_LIMIT} 次,本局惜败 · 共答对 ${next.solved} 题 / ${next.score} 分`\n return next\n }\n next.lastVerdict = `超时(第 ${next.failures}/${FAIL_LIMIT} 次)—— 换下一题,加油`\n return advance(next, '超时换题')\n}\n\n/** 认输/放弃。 */\nexport function concedeMatch(state: MatchState): MatchState {\n if (state.phase !== 'playing') return state\n const next = clone(state)\n next.phase = 'lost'\n next.lastVerdict = `你认输了 · 共答对 ${next.solved} 题 / ${next.score} 分`\n return next\n}\n\n/** 提示:消耗 1 次,给出库中一句未用参考诗(可直接跟对,但只计 HINT_SCORE 且断连击)。 */\nexport function useHint(state: MatchState): { state: MatchState; line?: string } {\n if (state.phase !== 'playing' || state.hintsLeft <= 0) return { state }\n const next = clone(state)\n next.hintsLeft -= 1\n next.hintUsedThisRound = true\n const line = pickUnused(next)\n next.lastVerdict = line\n ? `提示(剩 ${next.hintsLeft} 次):参考「${line}」—— 参考句直接对出只 +${HINT_SCORE} 分`\n : `提示(剩 ${next.hintsLeft} 次):想一句含「${state.lingzi}」的诗句吧`\n return { state: next, line }\n}\n\n/** 换令字重开本局(保留设置)。 */\nexport function restartMatch(options: MatchOptions): MatchState {\n return createMatch(options)\n}\n","/**\n * 浮层游戏面板(完整对战 UI)。\n * 对局状态由 HeaderButton 层持有(useGamePanel),本组件只做展示与事件转发:\n * 关闭面板只隐藏,不丢对局;隐藏期间倒计时暂停(视同暂停)。\n * @module dsh-plugin-feihualing/client/panel\n */\n\nimport React from 'react'\nimport * as engine from './game/engine.ts'\nimport type { GamePanelApi } from './game/useGame.ts'\n\nexport interface GamePanelProps {\n /** 所属会话 id(session 级 header 按钮注入)。 */\n sessionId: string\n /** 关闭面板(仅隐藏,对局保留)。 */\n onClose: () => void\n /** 对局控制面(由父层 useGamePanel 提供)。 */\n api: GamePanelApi\n}\n\n/** 会话短标识(展示用)。 */\nfunction shortSession(sessionId: string): string {\n return sessionId.length > 12 ? `${sessionId.slice(0, 10)}…` : sessionId\n}\n\nconst MODE_LABEL: Record<string, string> = { simple: '简易', ancient: '古法' }\nconst DIFF_LABEL: Record<string, string> = { easy: '轻松', medium: '标准', hard: '困难' }\nconst DIFF_HINT: Record<string, string> = { easy: '常见令字 · 30s', medium: '常见令字 · 20s', hard: '冷门令字 · 15s' }\n\n/** 开局设置行(未开局或对局结束后显示)。 */\nfunction SetupView(props: { api: GamePanelApi }): React.ReactElement {\n const { api } = props\n return (\n <React.Fragment>\n <div className=\"fhl-row\">\n <span className=\"fhl-seg-label\">模式</span>\n <div className=\"fhl-seg\">\n {(['simple', 'ancient'] as const).map((m) => (\n <button\n key={m}\n type=\"button\"\n className={`fhl-seg-item${api.mode === m ? ' is-active' : ''}`}\n onClick={() => api.setMode(m)}\n >\n {MODE_LABEL[m]}\n </button>\n ))}\n </div>\n </div>\n <div className=\"fhl-row\">\n <span className=\"fhl-seg-label\">难度</span>\n <div className=\"fhl-seg\">\n {(['easy', 'medium', 'hard'] as const).map((d) => (\n <button\n key={d}\n type=\"button\"\n className={`fhl-seg-item${api.difficulty === d ? ' is-active' : ''}`}\n onClick={() => api.setDifficulty(d)}\n title={DIFF_HINT[d]}\n >\n {DIFF_LABEL[d]}\n </button>\n ))}\n </div>\n </div>\n <div className=\"fhl-muted\" style={{ fontSize: 12, textAlign: 'center', paddingBottom: 4 }}>\n {api.mode === 'ancient'\n ? '古法:令字须按位置循环(第 1 字 → 第 7 字)'\n : '简易:诗句任意位置含令字即可'}\n {' · '}\n {DIFF_HINT[api.difficulty]}\n </div>\n <button type=\"button\" className=\"fhl-btn fhl-btn-primary\" onClick={api.start}>\n 🎴 开始对局\n </button>\n <div className=\"fhl-muted\" style={{ fontSize: 11, textAlign: 'center' }}>\n 对手出题 · 你限时对诗 · 连对 {engine.MAX_ROUNDS} 题通关 · 超时 {engine.FAIL_LIMIT} 次惜败\n </div>\n </React.Fragment>\n )\n}\n\n/** 进行中对局视图。 */\nfunction PlayView(props: { api: GamePanelApi }): React.ReactElement {\n const { api } = props\n const match = api.match\n if (match === null) return <React.Fragment />\n const timeLow = api.secondsLeft <= 5\n const canAct = match.phase === 'playing'\n return (\n <React.Fragment>\n <div className=\"fhl-stage\">\n <span className=\"fhl-stage-round\">第 {match.roundIndex + 1}/{engine.MAX_ROUNDS} 题</span>\n <span className={`fhl-timer${timeLow && canAct ? ' is-low' : ''}`}>{api.secondsLeft}s</span>\n <span className=\"fhl-stage-tag\">{MODE_LABEL[match.mode]} · {DIFF_LABEL[match.difficulty]}</span>\n </div>\n <div className=\"fhl-lingzi\">「{match.lingzi}」</div>\n {match.mode === 'ancient' && (\n <div className=\"fhl-muted\" style={{ fontSize: 11, textAlign: 'center', marginTop: -4 }}>\n 古法:本轮令字须位于第 <b style={{ color: 'var(--fhl-accent)' }}>{match.requiredPosition}</b> 字\n </div>\n )}\n <div className=\"fhl-card fhl-card-bot\">\n <span className=\"fhl-card-role\">🤖 对手</span>\n <span className=\"fhl-card-line\">{match.challenge}</span>\n </div>\n {match.lastVerdict !== '' && (\n <div className={`fhl-verdict${api.secondsLeft === 0 || !canAct ? '' : ''}`}>{match.lastVerdict}</div>\n )}\n <form\n className=\"fhl-input-row\"\n onSubmit={(ev) => {\n ev.preventDefault()\n api.submit()\n }}\n >\n <input\n className=\"fhl-input\"\n value={api.draft}\n onChange={(ev) => api.setDraft(ev.target.value)}\n placeholder={`说一句含「${match.lingzi}」的诗句…`}\n disabled={!canAct}\n autoComplete=\"off\"\n />\n <button type=\"submit\" className=\"fhl-btn fhl-btn-primary\" disabled={!canAct}>对诗</button>\n </form>\n <div className=\"fhl-actions\">\n <button\n type=\"button\"\n className=\"fhl-btn\"\n onClick={api.requestHint}\n disabled={!canAct || match.hintsLeft <= 0}\n title=\"消耗提示次数,给出参考诗句(参考句直接跟对只得 5 分)\"\n >\n 💡 提示({match.hintsLeft})\n </button>\n <button type=\"button\" className=\"fhl-btn fhl-btn-danger\" onClick={api.concede} disabled={!canAct}>\n 认输\n </button>\n </div>\n <div className=\"fhl-stats\">\n <span className=\"fhl-stat\"><b>{match.score}</b> 得分</span>\n <span className=\"fhl-stat\"><b>{match.combo}</b> 连击</span>\n <span className=\"fhl-stat\"><b>{match.solved}/{engine.MAX_ROUNDS}</b> 答对</span>\n <span className={`fhl-stat${match.failures >= engine.FAIL_LIMIT - 1 ? ' is-warn' : ''}`}>\n <b>{match.failures}/{engine.FAIL_LIMIT}</b> 超时\n </span>\n </div>\n </React.Fragment>\n )\n}\n\n/** 结算视图。 */\nfunction EndView(props: { api: GamePanelApi }): React.ReactElement {\n const { api } = props\n const match = api.match\n if (match === null) return <React.Fragment />\n const won = match.phase === 'won'\n return (\n <React.Fragment>\n <div className={`fhl-result${won ? ' is-won' : ' is-lost'}`}>\n <div className=\"fhl-result-icon\">{won ? '🏆' : '😢'}</div>\n <div className=\"fhl-result-title\">{won ? '通关!' : '本局惜败'}</div>\n <div className=\"fhl-muted\" style={{ fontSize: 12, textAlign: 'center' }}>{match.lastVerdict}</div>\n </div>\n <div className=\"fhl-stats\">\n <span className=\"fhl-stat\"><b>{match.score}</b> 得分</span>\n <span className=\"fhl-stat\"><b>{match.solved}</b> 答对</span>\n <span className=\"fhl-stat\"><b>{match.failures}</b> 超时</span>\n </div>\n <div className=\"fhl-record\">\n <span>🏅 {api.stats.wins} 胜 / {api.stats.losses} 负</span>\n <span>🔥 连胜 {api.stats.streak}</span>\n <span>✨ 最佳 {api.stats.bestScore}</span>\n </div>\n <button type=\"button\" className=\"fhl-btn fhl-btn-primary\" onClick={api.start}>\n 🔄 再来一局\n </button>\n <div className=\"fhl-muted\" style={{ fontSize: 11, textAlign: 'center' }}>\n 令字「{match.lingzi}」· {MODE_LABEL[match.mode]} · {DIFF_LABEL[match.difficulty]}\n </div>\n </React.Fragment>\n )\n}\n\n/** 面板主体:按对局阶段切换视图。 */\nexport function GamePanel(props: GamePanelProps): React.ReactElement {\n const { sessionId, onClose, api } = props\n const phase = api.match === null ? 'setup' : api.match.phase\n return (\n <div className=\"fhl-root fhl-panel\" data-plugin=\"dsh-plugin-feihualing\">\n <div className=\"fhl-panel-header\">\n <span className=\"fhl-panel-title\">🎴 飞花令 · 即时对战</span>\n <span className=\"fhl-tag\" title={sessionId}>会话 {shortSession(sessionId)}</span>\n <button type=\"button\" className=\"fhl-panel-close\" onClick={onClose} title=\"收起(对局保留)\">✕</button>\n </div>\n {phase === 'setup' && <SetupView api={api} />}\n {phase === 'playing' && <PlayView api={api} />}\n {(phase === 'won' || phase === 'lost') && <EndView api={api} />}\n </div>\n )\n}\n","/**\n * 面板对战战绩(localStorage 持久化,本机会话无关、跨刷新保留)。\n * 纯浏览器模块;与 Host 对话模式状态互不干扰(面板是独立即时战)。\n * @module dsh-plugin-feihualing/client/game/stats\n */\n\nimport type { MatchState } from './engine.ts'\n\nexport interface GameStats {\n /** 累计胜局。 */\n wins: number\n /** 累计败局。 */\n losses: number\n /** 历史最佳得分。 */\n bestScore: number\n /** 当前连胜。 */\n streak: number\n /** 历史最长连胜。 */\n bestStreak: number\n /** 累计答对题数。 */\n solvedTotal: number\n}\n\nconst KEY = 'dsh-plugin-feihualing:stats'\n\nfunction empty(): GameStats {\n return { wins: 0, losses: 0, bestScore: 0, streak: 0, bestStreak: 0, solvedTotal: 0 }\n}\n\n/** 读取战绩(损坏/缺失返回空战绩)。 */\nexport function loadStats(): GameStats {\n try {\n const raw = localStorage.getItem(KEY)\n if (raw === null) return empty()\n const parsed = JSON.parse(raw) as Partial<GameStats>\n return {\n wins: typeof parsed.wins === 'number' ? parsed.wins : 0,\n losses: typeof parsed.losses === 'number' ? parsed.losses : 0,\n bestScore: typeof parsed.bestScore === 'number' ? parsed.bestScore : 0,\n streak: typeof parsed.streak === 'number' ? parsed.streak : 0,\n bestStreak: typeof parsed.bestStreak === 'number' ? parsed.bestStreak : 0,\n solvedTotal: typeof parsed.solvedTotal === 'number' ? parsed.solvedTotal : 0,\n }\n } catch {\n return empty()\n }\n}\n\n/** 结算一局并入账(返回并持久化新战绩)。 */\nexport function recordResult(stats: GameStats, match: MatchState): GameStats {\n const won = match.phase === 'won'\n const next: GameStats = {\n ...stats,\n wins: stats.wins + (won ? 1 : 0),\n losses: stats.losses + (won ? 0 : 1),\n bestScore: Math.max(stats.bestScore, match.score),\n streak: won ? stats.streak + 1 : 0,\n bestStreak: Math.max(stats.bestStreak, won ? stats.streak + 1 : stats.bestStreak),\n solvedTotal: stats.solvedTotal + match.solved,\n }\n try {\n localStorage.setItem(KEY, JSON.stringify(next))\n } catch {\n /* 存储失败(隐私模式等)不影响对局 */\n }\n return next\n}\n","/**\n * React 对局 hook:包装 engine 的纯逻辑,管理倒计时与面板动作。\n * 倒计时由 useGamePanel 内部控制(setInterval,无第三方库)。\n * @module dsh-plugin-feihualing/client/game/useGame\n */\n\nimport React from 'react'\nimport * as engine from './engine.ts'\nimport type { GameStats } from './stats.ts'\nimport { loadStats, recordResult } from './stats.ts'\nimport type { GameMode } from '../../shared/rules.ts'\n\n/** 对局控制面(供面板组件使用)。 */\nexport interface GamePanelApi {\n match: engine.MatchState | null\n secondsLeft: number\n mode: GameMode\n difficulty: engine.Difficulty\n draft: string\n stats: GameStats\n setMode(mode: GameMode): void\n setDifficulty(difficulty: engine.Difficulty): void\n setDraft(text: string): void\n start(): void\n submit(): void\n requestHint(): void\n concede(): void\n}\n\n/**\n * 一局对战面板状态。倒计时只在对局进行中且面板激活时跑;\n * 面板隐藏(active=false)视同暂停:计时停住、不超时,重开面板继续。\n * 答题推进/超时换题时按 (phase, roundIndex, challenge) 变化重置计时。\n * @param active 面板是否可见(决定倒计时是否运行)。\n */\nexport function useGamePanel(active: boolean): GamePanelApi {\n const [match, setMatch] = React.useState<engine.MatchState | null>(null)\n const [secondsLeft, setSecondsLeft] = React.useState(0)\n const [mode, setMode] = React.useState<GameMode>('simple')\n const [difficulty, setDifficulty] = React.useState<engine.Difficulty>('medium')\n const [draft, setDraft] = React.useState('')\n const [stats, setStats] = React.useState<GameStats>(() => loadStats())\n // 记录已入账的局(按 startedAt),防止同一局重复累计战绩\n const recordedStartedAt = React.useRef<number | null>(null)\n\n // 结算(won/lost)时入账一次战绩\n React.useEffect(() => {\n if (match === null || (match.phase !== 'won' && match.phase !== 'lost')) return\n if (recordedStartedAt.current === match.startedAt) return\n recordedStartedAt.current = match.startedAt\n setStats((cur) => recordResult(cur, match))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [match])\n\n const roundKey = match === null\n ? ''\n : `${match.phase}:${match.roundIndex}:${match.challenge}`\n\n // 每一“题”(phase/题号/对手句变化)重置倒计时为满额限时\n React.useEffect(() => {\n if (match === null || match.phase !== 'playing') return\n setSecondsLeft(engine.TIME_LIMIT[match.difficulty])\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [roundKey])\n\n // 秒针:仅面板激活且对局进行中时递减;隐藏(active=false)即暂停并保留剩余时间\n React.useEffect(() => {\n if (!active || match === null || match.phase !== 'playing') return\n const timer = setInterval(() => {\n setSecondsLeft((s) => Math.max(0, s - 1))\n }, 1000)\n return () => clearInterval(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active, roundKey])\n\n // 倒计时归零:本题超时\n React.useEffect(() => {\n if (!active || match === null || match.phase !== 'playing') return\n if (secondsLeft > 0) return\n setMatch((cur) => (cur !== null && cur.phase === 'playing' ? engine.timeoutRound(cur) : cur))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [secondsLeft, active])\n\n const start = React.useCallback(() => {\n setMatch(engine.createMatch({ mode, difficulty }))\n setDraft('')\n }, [mode, difficulty])\n\n const submit = React.useCallback(() => {\n const text = draft.trim()\n if (text.length === 0) return\n setMatch((cur) => {\n if (cur === null || cur.phase !== 'playing') return cur\n const outcome = engine.submitPlayerLine(cur, text)\n return outcome.state\n })\n setDraft('')\n }, [draft])\n\n const requestHint = React.useCallback(() => {\n setMatch((cur) => {\n if (cur === null || cur.phase !== 'playing') return cur\n return engine.useHint(cur).state\n })\n }, [])\n\n const concede = React.useCallback(() => {\n setMatch((cur) => (cur === null ? cur : engine.concedeMatch(cur)))\n }, [])\n\n return {\n match,\n secondsLeft,\n mode,\n difficulty,\n draft,\n stats,\n setMode,\n setDifficulty,\n setDraft,\n start,\n submit,\n requestHint,\n concede,\n }\n}\n","/**\n * 会话标题游戏开关按钮(conversation.session.header.actions 插槽,session 级 list)。\n * 点击后直接渲染浮层游戏面板(fixed 定位,无需 react-dom portal——\n * client 半边仅依赖 DSH 模块表确认提供的 react)。\n * 对局状态常驻本组件:关闭面板只隐藏,不丢局;隐藏期间倒计时暂停(见 useGame)。\n * @module dsh-plugin-feihualing/client/header-button\n */\n\nimport React from 'react'\nimport { HEADER_ORDER, NAMESPACE } from './constants.ts'\nimport type { LocalContext } from './types.ts'\nimport { GamePanel } from './panel.tsx'\nimport { useGamePanel } from './game/useGame.ts'\n\n/** 在会话头操作行注册游戏开关按钮。 */\nexport function registerHeaderButton(ctx: LocalContext): void {\n ctx.slots.inject('conversation.session.header.actions', () => ctx.slots.register(\n {\n name: 'conversation.session.header.actions',\n id: NAMESPACE,\n order: HEADER_ORDER,\n inject: (sessionId) => ({ sessionId }),\n },\n HeaderButton,\n ))\n}\n\n/** 会话头游戏开关按钮 + 浮层面板(open 时渲染,fixed 定位于视口右上)。 */\nfunction HeaderButton(props: { sessionId?: string }): React.ReactElement {\n const [open, setOpen] = React.useState(false)\n // 对局状态提升到按钮层:面板开合不影响对局,隐藏 = 暂停计时\n const api = useGamePanel(open)\n const sessionId = props.sessionId ?? ''\n const toggle = React.useCallback(() => setOpen((prev) => !prev), [])\n\n return (\n <React.Fragment>\n <button\n type=\"button\"\n className=\"fhl-toggle\"\n data-active={open ? 'true' : 'false'}\n onClick={toggle}\n title={open ? '收起飞花令(对局保留)' : '飞花令小游戏'}\n aria-label=\"飞花令小游戏\"\n >\n 🎴\n </button>\n {open && <GamePanel sessionId={sessionId} onClose={() => setOpen(false)} api={api} />}\n </React.Fragment>\n )\n}\n","/**\n * 客户端半边入口(dsh-plugin-feihualing/client)。\n * 唯一职责:注入样式并组装各 UI 面注册(当前:会话头游戏开关按钮 + 浮层面板)。\n * 只做纯插槽注册,不受 harness 的 WEB_SETTINGS_NAMESPACES 白名单影响,装完即用。\n * @module dsh-plugin-feihualing/client\n */\n\nimport type { LocalContext } from './types.ts'\nimport { injectStyles } from './styles.ts'\nimport { registerHeaderButton } from './header-button.tsx'\n\n/** 依赖的服务:slots 就绪后本插件才会加载。 */\nexport const inject = ['slots']\n\n/** 客户端插件主体。 */\nexport function apply(ctx: LocalContext): void {\n // 样式表随插件生命周期注入/移除(ctx.effect 由 cordis 客户端运行时提供)\n const effect = (ctx as { effect?: (execute: () => (() => void) | void, label?: string) => void }).effect\n const disposeStyles = injectStyles()\n if (effect) {\n effect(() => disposeStyles, 'dsh-plugin-feihualing: client styles')\n }\n registerHeaderButton(ctx)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiNZ,SAAgB,eAA2B;GACzC,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,aAAa,eAAe,uBAAuB;GACzD,MAAM,cAAc;GACpB,SAAS,KAAK,YAAY,KAAK;GAC/B,aAAa;IACX,IAAI,MAAM,YAAY,MAAM,WAAW,YAAY,KAAK;GAC1D;EACF;;;;;;;;;ECzNA,MAAa,YAAY;;;;;ECczB,SAAgB,cAAc,KAAqB;GACjD,OAAO,IAAI,QAAQ,iDAAiD,EAAE;EACxE;;EAGA,SAAgB,YAAY,KAAqB;GAC/C,OAAO,IAAI,KAAK,CAAC,CAAC,QAAQ,SAAS,GAAG,CAAC,CAAC,MAAM,GAAA,EAAiB;EACjE;;;;;EA0CA,SAAgB,iBAAiB,OAAgC;GAC/D,MAAM,aAAa,cAAc,MAAM,OAAO;GAC9C,MAAM,UAAU,YAAY,MAAM,OAAO;GAEzC,IAAI,CAAC,WAAW,SAAS,MAAM,MAAM,GACnC,OAAO;IAAE,MAAM;IAAW;IAAY;IAAS,QAAQ,WAAW;GAAO;GAE3E,IAAI,WAAW,SAAA,KAA2B,WAAW,SAAA,IACnD,OAAO;IAAE,MAAM;IAAU;IAAY;IAAS,QAAQ,WAAW;GAAO;GAE1E,IAAI,MAAM,eAAe,SAAS,UAAU,GAC1C,OAAO;IAAE,MAAM;IAAa;IAAY;IAAS,QAAQ,WAAW;GAAO;GAE7E,IAAI,MAAM,SAAS,WAAW;IAC5B,MAAM,MAAM,MAAM;IAClB,IAAI,WAAW,SAAS,KACtB,OAAO;KAAE,MAAM;KAAe,cAAc;KAAY,kBAAkB;KAAK;KAAY;KAAS,QAAQ,WAAW;IAAO;IAEhI,IAAI,WAAW,MAAM,OAAO,MAAM,QAChC,OAAO;KAAE,MAAM;KAAe,cAAc;KAAY,kBAAkB;KAAK;KAAY;KAAS,QAAQ,WAAW;IAAO;GAElI;GACA,OAAO;IAAE,MAAM;IAAS;IAAY;IAAS,QAAQ,WAAW;GAAO;EACzE;;;;;;;;;;;;ECnFA,MAAa,YAAyD,OAAO,OAAO;GAClF,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,GAAG,OAAO,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;;EAGD,MAAa,cAAiC,OAAO,OAAO,OAAO,KAAK,SAAS,CAAC;;EAGlF,MAAM,gCAAqC,IAAI,IAAI;GACjD;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;GAAK;EAClF,CAAC;;EAGD,MAAa,cAAiC,OAAO,OACnD,YAAY,QAAQ,MAAM,cAAc,IAAI,CAAC,CAAC,CAChD;;EAGA,MAAa,cAAiC,OAAO,OACnD,YAAY,QAAQ,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC,CACjD;;ECtSA,MAAa,aAAmD;GAC9D,MAAM;GACN,QAAQ;GACR,MAAM;EACR;;EAGA,MAAM,aAAiD;GAAE,QAAQ;GAAI,SAAS;EAAG;;EAGjF,MAAM,cAAc;;EAGpB,MAAM,aAAa;EAmDnB,SAAS,KAAQ,MAAuB;GACtC,OAAO,KAAK,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,MAAM;EACpD;EAEA,SAAS,MAAM,OAA+B;GAC5C,OAAO;IACL,GAAG;IACH,SAAS,CAAC,GAAG,MAAM,OAAO;IAC1B,gBAAgB,CAAC,GAAG,MAAM,cAAc;GAC1C;EACF;;EAGA,SAAS,WAAW,YAAgC;GAClD,MAAM,OAAO,eAAe,SAAS,cAAc;GACnD,OAAO,KAAK,KAAK,SAAS,IAAI,OAAO,WAAW;EAClD;;EAGA,SAAS,OAAO,QAAmC;GACjD,OAAO,UAAU,WAAW,CAAC;EAC/B;;;;;EAMA,SAAS,WAAW,OAAuC;GACzD,MAAM,aAAa,OAAO,MAAM,MAAM,CAAC,CAAC,QACrC,SAAS,CAAC,MAAM,eAAe,SAAS,cAAc,IAAI,CAAC,CAC9D;GACA,IAAI,WAAW,WAAW,GAAG,OAAO,KAAA;GACpC,OAAO,KAAK,UAAU;EACxB;;EAKA,SAAgB,YAAY,SAAmC;GAC7D,MAAM,SAAS,WAAW,QAAQ,UAAU;GAC5C,MAAM,OAAmB;IACvB,OAAO;IACP,MAAM,QAAQ;IACd,YAAY,QAAQ;IACpB;IACA,YAAY;IACZ,QAAQ;IACR,UAAU;IACV,OAAO;IACP,OAAO;IACP,SAAS,CAAC;IACV,gBAAgB,CAAC;IACjB,WAAW;IACX,mBAAmB;IACnB,WAAA;IACA,aAAa;IACb,kBAAkB;IAClB,WAAW,KAAK,IAAI;GACtB;GACA,MAAM,SAAS,WAAW,IAAI;GAC9B,IAAI,WAAW,KAAA,GAEb,OAAO;IAAE,GAAG;IAAM,aAAa,MAAM,OAAO;GAAO;GAErD,OAAO;IACL,GAAG;IACH,WAAW;IACX,SAAS,CAAC,MAAM;IAChB,gBAAgB,CAAC,iBAAiB,MAAM,CAAC;IACzC,aAAa,MAAM,OAAO;GAC5B;EACF;;EAGA,SAAS,iBAAiB,KAAqB;GAC7C,OAAO,cAAc,GAAG;EAC1B;;EAGA,SAAS,YAAY,OAAmB,SAA6B;GACnE,MAAM,cAAc,MAAM;GAC1B,MAAM,cAAc,MAAM;GAC1B,MAAM,OAAO,cACT,aACA,WAAW,MAAM,QAAQ,cAAc;GAC3C,MAAM,OAAO,MAAM,KAAK;GACxB,KAAK,UAAU;GACf,KAAK,QAAQ,cAAc,IAAI,cAAc;GAC7C,KAAK,SAAS;GACd,KAAK,QAAQ,KAAK,OAAO;GACzB,KAAK,eAAe,KAAK,iBAAiB,OAAO,CAAC;GAClD,IAAI,KAAK,SAAS,WAChB,KAAK,mBAAoB,KAAK,mBAAA,IAA2C;GAE3E,KAAK,cAAc;GACnB,KAAK,oBAAoB;GACzB,MAAM,SAAS,KAAK;GACpB,KAAK,cAAc,OAAO,OAAO,UAAU,IAAI,QAAQ,OAAO,KAAK,GAAG,KAAK,KAAK,MAAM;GACtF,OAAO;EACT;;EAGA,SAAS,QAAQ,OAAmB,KAAyB;GAC3D,IAAI,MAAM,cAAA,GACR,OAAO;IAAE,GAAG,MAAM,KAAK;IAAG,OAAO;IAAO,aAAa,SAAS,MAAM,OAAO,OAAO,MAAM,MAAM,KAAK,IAAI;GAAG;GAE5G,MAAM,WAAW,WAAW,KAAK;GACjC,IAAI,aAAa,KAAA,GAAW;IAC1B,MAAM,MAAM,MAAM,KAAK;IACvB,IAAI,QAAQ;IACZ,IAAI,cAAc,cAAc,MAAM,MAAM,KAAK,IAAI;IACrD,OAAO;GACT;GACA,MAAM,OAAO,MAAM,KAAK;GACxB,KAAK,YAAY;GACjB,KAAK,QAAQ,KAAK,QAAQ;GAC1B,KAAK,eAAe,KAAK,iBAAiB,QAAQ,CAAC;GACnD,KAAK,cAAc,GAAG,MAAM,YAAY;GACxC,OAAO;EACT;;EAGA,SAAgB,iBAAiB,OAAmB,SAAgC;GAClF,IAAI,MAAM,UAAU,WAAW,OAAO;IAAE;IAAO,IAAI;GAAM;GACzD,MAAM,SAAS,iBAAiB;IAC9B;IACA,QAAQ,MAAM;IACd,MAAM,MAAM;IACZ,kBAAkB,MAAM;IACxB,gBAAgB,MAAM;GACxB,CAAC;GACD,IAAI,OAAO,SAAS,WAClB,OAAO;IAAE,OAAO;KAAE,GAAG;KAAO,aAAa,UAAU,MAAM,OAAO;IAAS;IAAG,IAAI;GAAM;GAExF,IAAI,OAAO,SAAS,UAClB,OAAO;IAAE,OAAO;KAAE,GAAG;KAAO,aAAa;IAAkB;IAAG,IAAI;GAAM;GAE1E,IAAI,OAAO,SAAS,aAClB,OAAO;IAAE,OAAO;KAAE,GAAG;KAAO,aAAa;IAAsB;IAAG,IAAI;GAAM;GAE9E,IAAI,OAAO,SAAS,eAAe;IACjC,MAAM,MAAM,OAAO,oBAAoB,MAAM;IAC7C,MAAM,MAAM,OAAO,iBAAiB,aAChC,kBAAkB,IAAI,MACtB,cAAc,IAAI,QAAQ,MAAM,OAAO;IAC3C,OAAO;KAAE,OAAO;MAAE,GAAG;MAAO,aAAa;KAAI;KAAG,IAAI;IAAM;GAC5D;GAKA,OAAO;IAAE,OAFQ,QADF,YAAY,OAAO,OAAO,OACX,GAAG,MAEV;IAAG,IAAI;GAAK;EACrC;;EAGA,SAAgB,aAAa,OAA+B;GAC1D,IAAI,MAAM,UAAU,WAAW,OAAO;GACtC,MAAM,OAAO,MAAM,KAAK;GACxB,KAAK,YAAY;GACjB,KAAK,QAAQ;GACb,KAAK,oBAAoB;GACzB,IAAI,KAAK,YAAA,GAAwB;IAC/B,KAAK,QAAQ;IACb,KAAK,cAAc,qBAAiC,KAAK,OAAO,OAAO,KAAK,MAAM;IAClF,OAAO;GACT;GACA,KAAK,cAAc,QAAQ,KAAK,SAAS;GACzC,OAAO,QAAQ,MAAM,MAAM;EAC7B;;EAGA,SAAgB,aAAa,OAA+B;GAC1D,IAAI,MAAM,UAAU,WAAW,OAAO;GACtC,MAAM,OAAO,MAAM,KAAK;GACxB,KAAK,QAAQ;GACb,KAAK,cAAc,cAAc,KAAK,OAAO,OAAO,KAAK,MAAM;GAC/D,OAAO;EACT;;EAGA,SAAgB,QAAQ,OAAyD;GAC/E,IAAI,MAAM,UAAU,aAAa,MAAM,aAAa,GAAG,OAAO,EAAE,MAAM;GACtE,MAAM,OAAO,MAAM,KAAK;GACxB,KAAK,aAAa;GAClB,KAAK,oBAAoB;GACzB,MAAM,OAAO,WAAW,IAAI;GAC5B,KAAK,cAAc,OACf,QAAQ,KAAK,UAAU,SAAS,KAAK,gBAAgB,WAAW,MAChE,QAAQ,KAAK,UAAU,WAAW,MAAM,OAAO;GACnD,OAAO;IAAE,OAAO;IAAM;GAAK;EAC7B;;;;;;;;;;ECtQA,SAAS,aAAa,WAA2B;GAC/C,OAAO,UAAU,SAAS,KAAK,GAAG,UAAU,MAAM,GAAG,EAAE,EAAE,KAAK;EAChE;EAEA,MAAM,aAAqC;GAAE,QAAQ;GAAM,SAAS;EAAK;EACzE,MAAM,aAAqC;GAAE,MAAM;GAAM,QAAQ;GAAM,MAAM;EAAK;EAClF,MAAM,YAAoC;GAAE,MAAM;GAAc,QAAQ;GAAc,MAAM;EAAa;;EAGzG,SAAS,UAAU,OAAkD;GACnE,MAAM,EAAE,QAAQ;GAChB,OACE,iBAAA,GAAA,kBAAA,KAAA,CAACA,MAAAA,QAAM,UAAP,EAAA,UAAA;IACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAgB,UAAA;KAAQ,CAAA,GACxC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACX,UAAA,CAAC,UAAU,SAAS,CAAC,CAAW,KAAK,MACrC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAEE,MAAK;OACL,WAAW,eAAe,IAAI,SAAS,IAAI,eAAe;OAC1D,eAAe,IAAI,QAAQ,CAAC;OAE3B,UAAA,WAAW;MACN,GAND,CAMC,CACT;KACE,CAAA,CACF;;IACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAgB,UAAA;KAAQ,CAAA,GACxC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACX,UAAA;OAAC;OAAQ;OAAU;MAAM,CAAC,CAAW,KAAK,MAC1C,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAEE,MAAK;OACL,WAAW,eAAe,IAAI,eAAe,IAAI,eAAe;OAChE,eAAe,IAAI,cAAc,CAAC;OAClC,OAAO,UAAU;OAEhB,UAAA,WAAW;MACN,GAPD,CAOC,CACT;KACE,CAAA,CACF;;IACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAY,OAAO;MAAE,UAAU;MAAI,WAAW;MAAU,eAAe;KAAE;KAAxF,UAAA;MACG,IAAI,SAAS,YACV,+BACA;MACH;MACA,UAAU,IAAI;KACZ;;IACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KAAQ,MAAK;KAAS,WAAU;KAA0B,SAAS,IAAI;KAAO,UAAA;IAEtE,CAAA;IACR,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAY,OAAO;MAAE,UAAU;MAAI,WAAW;KAAS;KAAtE,UAAA;MAAyE;MACpD;MAAkB;MAAW;MAAkB;KAC/D;;GACS,EAAA,CAAA;EAEpB;;EAGA,SAAS,SAAS,OAAkD;GAClE,MAAM,EAAE,QAAQ;GAChB,MAAM,QAAQ,IAAI;GAClB,IAAI,UAAU,MAAM,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAACA,MAAAA,QAAM,UAAP,CAAiB,CAAA;GAC5C,MAAM,UAAU,IAAI,eAAe;GACnC,MAAM,SAAS,MAAM,UAAU;GAC/B,OACE,iBAAA,GAAA,kBAAA,KAAA,CAACA,MAAAA,QAAM,UAAP,EAAA,UAAA;IACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA;QAAkC;QAAG,MAAM,aAAa;QAAE;QAAE;QAAkB;OAAQ;;MACtF,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAW,YAAY,WAAW,SAAS,YAAY;OAA7D,UAAA,CAAoE,IAAI,aAAY,GAAO;;MAC3F,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA;QAAiC,WAAW,MAAM;QAAM;QAAI,WAAW,MAAM;OAAkB;;KAC5F;;IACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MAA4B;MAAE,MAAM;MAAO;KAAM;;IAChD,MAAM,SAAS,aACd,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAY,OAAO;MAAE,UAAU;MAAI,WAAW;MAAU,WAAW;KAAG;KAArF,UAAA;MAAwF;MAC1E,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;OAAG,OAAO,EAAE,OAAO,oBAAoB;OAAI,UAAA,MAAM;MAAoB,CAAA;MAAC;KAC/E;;IAEP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAgB,UAAA;KAAW,CAAA,GAC3C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAiB,UAAA,MAAM;KAAgB,CAAA,CACpD;;IACJ,MAAM,gBAAgB,MACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAW,cAAc,IAAI,gBAAgB,KAAK,CAAC,SAAS,KAAK;KAAO,UAAA,MAAM;IAAiB,CAAA;IAEtG,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;KACE,WAAU;KACV,WAAW,OAAO;MAChB,GAAG,eAAe;MAClB,IAAI,OAAO;KACb;KALF,UAAA,CAOE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MACE,WAAU;MACV,OAAO,IAAI;MACX,WAAW,OAAO,IAAI,SAAS,GAAG,OAAO,KAAK;MAC9C,aAAa,QAAQ,MAAM,OAAO;MAClC,UAAU,CAAC;MACX,cAAa;KACd,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MAAQ,MAAK;MAAS,WAAU;MAA0B,UAAU,CAAC;MAAQ,UAAA;KAAU,CAAA,CACnF;;IACN,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;MACE,MAAK;MACL,WAAU;MACV,SAAS,IAAI;MACb,UAAU,CAAC,UAAU,MAAM,aAAa;MACxC,OAAM;MALR,UAAA;OAMC;OACQ,MAAM;OAAU;MACjB;KACR,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MAAQ,MAAK;MAAS,WAAU;MAAyB,SAAS,IAAI;MAAS,UAAU,CAAC;MAAQ,UAAA;KAE1F,CAAA,CACL;;IACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,MAAM,MAAS,CAAA,GAAC,KAAS;;MACxD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,MAAM,MAAS,CAAA,GAAC,KAAS;;MACxD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA2B,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD,EAAA,UAAA;QAAI,MAAM;QAAO;QAAE;OAAqB,EAAA,CAAA,GAAC,KAAS;;MAC7E,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAW,WAAW,MAAM,YAAY,IAAwB,aAAa;OAAnF,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD,EAAA,UAAA;QAAI,MAAM;QAAS;QAAE;OAAqB,EAAA,CAAA,GAAC,KACvC;;KACH;;GACS,EAAA,CAAA;EAEpB;;EAGA,SAAS,QAAQ,OAAkD;GACjE,MAAM,EAAE,QAAQ;GAChB,MAAM,QAAQ,IAAI;GAClB,IAAI,UAAU,MAAM,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAACA,MAAAA,QAAM,UAAP,CAAiB,CAAA;GAC5C,MAAM,MAAM,MAAM,UAAU;GAC5B,OACE,iBAAA,GAAA,kBAAA,KAAA,CAACA,MAAAA,QAAM,UAAP,EAAA,UAAA;IACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAW,aAAa,MAAM,YAAY;KAA/C,UAAA;MACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OAAmB,UAAA,MAAM,OAAO;MAAU,CAAA;MACzD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OAAoB,UAAA,MAAM,QAAQ;MAAY,CAAA;MAC7D,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OAAY,OAAO;QAAE,UAAU;QAAI,WAAW;OAAS;OAAI,UAAA,MAAM;MAAiB,CAAA;KAC9F;;IACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,MAAM,MAAS,CAAA,GAAC,KAAS;;MACxD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,MAAM,OAAU,CAAA,GAAC,KAAS;;MACzD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,MAAM,SAAY,CAAA,GAAC,KAAS;;KACxD;;IACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA;OAAM;OAAI,IAAI,MAAM;OAAK;OAAM,IAAI,MAAM;OAAO;MAAQ,EAAA,CAAA;MACxD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,UAAO,IAAI,MAAM,MAAa,EAAA,CAAA;MACpC,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,SAAM,IAAI,MAAM,SAAgB,EAAA,CAAA;KACnC;;IACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KAAQ,MAAK;KAAS,WAAU;KAA0B,SAAS,IAAI;KAAO,UAAA;IAEtE,CAAA;IACR,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAY,OAAO;MAAE,UAAU;MAAI,WAAW;KAAS;KAAtE,UAAA;MAAyE;MACnE,MAAM;MAAO;MAAI,WAAW,MAAM;MAAM;MAAI,WAAW,MAAM;KAC9D;;GACS,EAAA,CAAA;EAEpB;;EAGA,SAAgB,UAAU,OAA2C;GACnE,MAAM,EAAE,WAAW,SAAS,QAAQ;GACpC,MAAM,QAAQ,IAAI,UAAU,OAAO,UAAU,IAAI,MAAM;GACvD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAqB,eAAY;IAAhD,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAkB,UAAA;OAAmB,CAAA;OACrD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;QAAM,WAAU;QAAU,OAAO;QAAjC,UAAA,CAA4C,OAAI,aAAa,SAAS,CAAQ;;OAC9E,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAkB,SAAS;QAAS,OAAM;QAAW,UAAA;OAAS,CAAA;MAC3F;;KACJ,UAAU,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD,EAAgB,IAAM,CAAA;KAC3C,UAAU,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD,EAAe,IAAM,CAAA;MAC3C,UAAU,SAAS,UAAU,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD,EAAc,IAAM,CAAA;IAC3D;;EAET;;;EClLA,MAAM,MAAM;EAEZ,SAAS,QAAmB;GAC1B,OAAO;IAAE,MAAM;IAAG,QAAQ;IAAG,WAAW;IAAG,QAAQ;IAAG,YAAY;IAAG,aAAa;GAAE;EACtF;;EAGA,SAAgB,YAAuB;GACrC,IAAI;IACF,MAAM,MAAM,aAAa,QAAQ,GAAG;IACpC,IAAI,QAAQ,MAAM,OAAO,MAAM;IAC/B,MAAM,SAAS,KAAK,MAAM,GAAG;IAC7B,OAAO;KACL,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;KACtD,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;KAC5D,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;KACrE,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;KAC5D,YAAY,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;KACxE,aAAa,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;IAC7E;GACF,QAAQ;IACN,OAAO,MAAM;GACf;EACF;;EAGA,SAAgB,aAAa,OAAkB,OAA8B;GAC3E,MAAM,MAAM,MAAM,UAAU;GAC5B,MAAM,OAAkB;IACtB,GAAG;IACH,MAAM,MAAM,QAAQ,MAAM,IAAI;IAC9B,QAAQ,MAAM,UAAU,MAAM,IAAI;IAClC,WAAW,KAAK,IAAI,MAAM,WAAW,MAAM,KAAK;IAChD,QAAQ,MAAM,MAAM,SAAS,IAAI;IACjC,YAAY,KAAK,IAAI,MAAM,YAAY,MAAM,MAAM,SAAS,IAAI,MAAM,UAAU;IAChF,aAAa,MAAM,cAAc,MAAM;GACzC;GACA,IAAI;IACF,aAAa,QAAQ,KAAK,KAAK,UAAU,IAAI,CAAC;GAChD,QAAQ,CAER;GACA,OAAO;EACT;;;;;;;;;;;;;;EC/BA,SAAgB,aAAa,QAA+B;GAC1D,MAAM,CAAC,OAAO,YAAYC,MAAAA,QAAM,SAAmC,IAAI;GACvE,MAAM,CAAC,aAAa,kBAAkBA,MAAAA,QAAM,SAAS,CAAC;GACtD,MAAM,CAAC,MAAM,WAAWA,MAAAA,QAAM,SAAmB,QAAQ;GACzD,MAAM,CAAC,YAAY,iBAAiBA,MAAAA,QAAM,SAA4B,QAAQ;GAC9E,MAAM,CAAC,OAAO,YAAYA,MAAAA,QAAM,SAAS,EAAE;GAC3C,MAAM,CAAC,OAAO,YAAYA,MAAAA,QAAM,eAA0B,UAAU,CAAC;GAErE,MAAM,oBAAoBA,MAAAA,QAAM,OAAsB,IAAI;GAG1D,MAAA,QAAM,gBAAgB;IACpB,IAAI,UAAU,QAAS,MAAM,UAAU,SAAS,MAAM,UAAU,QAAS;IACzE,IAAI,kBAAkB,YAAY,MAAM,WAAW;IACnD,kBAAkB,UAAU,MAAM;IAClC,UAAU,QAAQ,aAAa,KAAK,KAAK,CAAC;GAE5C,GAAG,CAAC,KAAK,CAAC;GAEV,MAAM,WAAW,UAAU,OACvB,KACA,GAAG,MAAM,MAAM,GAAG,MAAM,WAAW,GAAG,MAAM;GAGhD,MAAA,QAAM,gBAAgB;IACpB,IAAI,UAAU,QAAQ,MAAM,UAAU,WAAW;IACjD,eAAeC,WAAkB,MAAM,WAAW;GAEpD,GAAG,CAAC,QAAQ,CAAC;GAGb,MAAA,QAAM,gBAAgB;IACpB,IAAI,CAAC,UAAU,UAAU,QAAQ,MAAM,UAAU,WAAW;IAC5D,MAAM,QAAQ,kBAAkB;KAC9B,gBAAgB,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1C,GAAG,GAAI;IACP,aAAa,cAAc,KAAK;GAElC,GAAG,CAAC,QAAQ,QAAQ,CAAC;GAGrB,MAAA,QAAM,gBAAgB;IACpB,IAAI,CAAC,UAAU,UAAU,QAAQ,MAAM,UAAU,WAAW;IAC5D,IAAI,cAAc,GAAG;IACrB,UAAU,QAAS,QAAQ,QAAQ,IAAI,UAAU,YAAYC,aAAoB,GAAG,IAAI,GAAI;GAE9F,GAAG,CAAC,aAAa,MAAM,CAAC;GA6BxB,OAAO;IACL;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,OArCYF,MAAAA,QAAM,kBAAkB;KACpC,SAASG,YAAmB;MAAE;MAAM;KAAW,CAAC,CAAC;KACjD,SAAS,EAAE;IACb,GAAG,CAAC,MAAM,UAAU,CAkCd;IACJ,QAjCaH,MAAAA,QAAM,kBAAkB;KACrC,MAAM,OAAO,MAAM,KAAK;KACxB,IAAI,KAAK,WAAW,GAAG;KACvB,UAAU,QAAQ;MAChB,IAAI,QAAQ,QAAQ,IAAI,UAAU,WAAW,OAAO;MAEpD,OADgBI,iBAAwB,KAAK,IAChC,CAAC,CAAC;KACjB,CAAC;KACD,SAAS,EAAE;IACb,GAAG,CAAC,KAAK,CAwBF;IACL,aAvBkBJ,MAAAA,QAAM,kBAAkB;KAC1C,UAAU,QAAQ;MAChB,IAAI,QAAQ,QAAQ,IAAI,UAAU,WAAW,OAAO;MACpD,OAAOK,QAAe,GAAG,CAAC,CAAC;KAC7B,CAAC;IACH,GAAG,CAAC,CAkBQ;IACV,SAjBcL,MAAAA,QAAM,kBAAkB;KACtC,UAAU,QAAS,QAAQ,OAAO,MAAMM,aAAoB,GAAG,CAAE;IACnE,GAAG,CAAC,CAeI;GACR;EACF;;;;;;;;;;;EC9GA,SAAgB,qBAAqB,KAAyB;GAC5D,IAAI,MAAM,OAAO,6CAA6C,IAAI,MAAM,SACtE;IACE,MAAM;IACN,IAAI;IACJ,OAAA;IACA,SAAS,eAAe,EAAE,UAAU;GACtC,GACA,YACF,CAAC;EACH;;EAGA,SAAS,aAAa,OAAmD;GACvE,MAAM,CAAC,MAAM,WAAWC,MAAAA,QAAM,SAAS,KAAK;GAE5C,MAAM,MAAM,aAAa,IAAI;GAC7B,MAAM,YAAY,MAAM,aAAa;GACrC,MAAM,SAASA,MAAAA,QAAM,kBAAkB,SAAS,SAAS,CAAC,IAAI,GAAG,CAAC,CAAC;GAEnE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAACA,MAAAA,QAAM,UAAP,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,eAAa,OAAO,SAAS;IAC7B,SAAS;IACT,OAAO,OAAO,gBAAgB;IAC9B,cAAW;IACZ,UAAA;GAEO,CAAA,GACP,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;IAAsB;IAAW,eAAe,QAAQ,KAAK;IAAQ;GAAM,CAAA,CACtE,EAAA,CAAA;EAEpB;;;;ECtCA,MAAa,SAAS,CAAC,OAAO;;EAG9B,SAAgB,MAAM,KAAyB;GAE7C,MAAM,SAAU,IAAkF;GAClG,MAAM,gBAAgB,aAAa;GACnC,IAAI,QACF,aAAa,eAAe,sCAAsC;GAEpE,qBAAqB,GAAG;EAC1B"}
|
package/lib/index.js
CHANGED
|
@@ -224,7 +224,6 @@ const POEM_BANK = Object.freeze({
|
|
|
224
224
|
"人闲桂花落",
|
|
225
225
|
"举杯邀明月,对影成三人",
|
|
226
226
|
"路上行人欲断魂",
|
|
227
|
-
"独在异乡为异客,每逢佳节倍思亲",
|
|
228
227
|
"人面不知何处去",
|
|
229
228
|
"但愿人长久",
|
|
230
229
|
"前不见古人,后不见来者",
|
|
@@ -243,7 +242,6 @@ const POEM_BANK = Object.freeze({
|
|
|
243
242
|
"酒入愁肠,化作相思泪",
|
|
244
243
|
"今朝有酒今朝醉",
|
|
245
244
|
"白日放歌须纵酒",
|
|
246
|
-
"人生得意须尽欢,莫使金樽空对月",
|
|
247
245
|
"明月几时有,把酒问青天"
|
|
248
246
|
]),
|
|
249
247
|
夜: Object.freeze([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-feihualing",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "DeepSeek Harness 飞花令游戏插件:简易/古法严格双模式,按会话独立维护令字、得分、已用诗句与剩余提示次数;提供 feihualing_start / feihualing_status / feihualing_stop 三个工具与关键词指令,检测到 shell/文件写入类工具调用时自动暂停并保存现场。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|