sa2kit 1.6.91 → 1.6.96

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.
Files changed (117) hide show
  1. package/dist/client-BlkUL2To.d.ts +26 -0
  2. package/dist/client-DpMIhrlS.d.mts +26 -0
  3. package/dist/huarongdao/index.d.mts +8 -0
  4. package/dist/huarongdao/index.d.ts +8 -0
  5. package/dist/huarongdao/index.js +360 -0
  6. package/dist/huarongdao/index.js.map +1 -0
  7. package/dist/huarongdao/index.mjs +338 -0
  8. package/dist/huarongdao/index.mjs.map +1 -0
  9. package/dist/huarongdao/logic/index.d.mts +11 -0
  10. package/dist/huarongdao/logic/index.d.ts +11 -0
  11. package/dist/huarongdao/logic/index.js +89 -0
  12. package/dist/huarongdao/logic/index.js.map +1 -0
  13. package/dist/huarongdao/logic/index.mjs +81 -0
  14. package/dist/huarongdao/logic/index.mjs.map +1 -0
  15. package/dist/huarongdao/routes/index.d.mts +38 -0
  16. package/dist/huarongdao/routes/index.d.ts +38 -0
  17. package/dist/huarongdao/routes/index.js +114 -0
  18. package/dist/huarongdao/routes/index.js.map +1 -0
  19. package/dist/huarongdao/routes/index.mjs +108 -0
  20. package/dist/huarongdao/routes/index.mjs.map +1 -0
  21. package/dist/huarongdao/server/index.d.mts +14 -0
  22. package/dist/huarongdao/server/index.d.ts +14 -0
  23. package/dist/huarongdao/server/index.js +60 -0
  24. package/dist/huarongdao/server/index.js.map +1 -0
  25. package/dist/huarongdao/server/index.mjs +57 -0
  26. package/dist/huarongdao/server/index.mjs.map +1 -0
  27. package/dist/huarongdao/service/index.d.mts +31 -0
  28. package/dist/huarongdao/service/index.d.ts +31 -0
  29. package/dist/huarongdao/service/index.js +45 -0
  30. package/dist/huarongdao/service/index.js.map +1 -0
  31. package/dist/huarongdao/service/index.mjs +42 -0
  32. package/dist/huarongdao/service/index.mjs.map +1 -0
  33. package/dist/huarongdao/types/index.d.mts +46 -0
  34. package/dist/huarongdao/types/index.d.ts +46 -0
  35. package/dist/huarongdao/types/index.js +4 -0
  36. package/dist/huarongdao/types/index.js.map +1 -0
  37. package/dist/huarongdao/types/index.mjs +3 -0
  38. package/dist/huarongdao/types/index.mjs.map +1 -0
  39. package/dist/huarongdao/ui/web/index.d.mts +3 -0
  40. package/dist/huarongdao/ui/web/index.d.ts +3 -0
  41. package/dist/huarongdao/ui/web/index.js +237 -0
  42. package/dist/huarongdao/ui/web/index.js.map +1 -0
  43. package/dist/huarongdao/ui/web/index.mjs +229 -0
  44. package/dist/huarongdao/ui/web/index.mjs.map +1 -0
  45. package/dist/index-B48rcsqv.d.ts +27 -0
  46. package/dist/index-BNqJdwX4.d.ts +37 -0
  47. package/dist/index-C7yh6b5Q.d.mts +17 -0
  48. package/dist/index-CDapUIT5.d.mts +51 -0
  49. package/dist/index-Cv9jlnNz.d.ts +17 -0
  50. package/dist/index-D3UbkUai.d.ts +51 -0
  51. package/dist/index-DOtQI_mz.d.mts +37 -0
  52. package/dist/index-Da2X78GE.d.mts +27 -0
  53. package/dist/index.d.mts +18 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.js +1707 -79
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.mjs +1675 -82
  58. package/dist/index.mjs.map +1 -1
  59. package/dist/mikuContest/index.d.mts +13 -0
  60. package/dist/mikuContest/index.d.ts +13 -0
  61. package/dist/mikuContest/index.js +1310 -0
  62. package/dist/mikuContest/index.js.map +1 -0
  63. package/dist/mikuContest/index.mjs +1253 -0
  64. package/dist/mikuContest/index.mjs.map +1 -0
  65. package/dist/mikuContest/logic/index.d.mts +32 -0
  66. package/dist/mikuContest/logic/index.d.ts +32 -0
  67. package/dist/mikuContest/logic/index.js +511 -0
  68. package/dist/mikuContest/logic/index.js.map +1 -0
  69. package/dist/mikuContest/logic/index.mjs +483 -0
  70. package/dist/mikuContest/logic/index.mjs.map +1 -0
  71. package/dist/mikuContest/routes/index.d.mts +80 -0
  72. package/dist/mikuContest/routes/index.d.ts +80 -0
  73. package/dist/mikuContest/routes/index.js +821 -0
  74. package/dist/mikuContest/routes/index.js.map +1 -0
  75. package/dist/mikuContest/routes/index.mjs +791 -0
  76. package/dist/mikuContest/routes/index.mjs.map +1 -0
  77. package/dist/mikuContest/server/index.d.mts +766 -0
  78. package/dist/mikuContest/server/index.d.ts +766 -0
  79. package/dist/mikuContest/server/index.js +705 -0
  80. package/dist/mikuContest/server/index.js.map +1 -0
  81. package/dist/mikuContest/server/index.mjs +672 -0
  82. package/dist/mikuContest/server/index.mjs.map +1 -0
  83. package/dist/mikuContest/service/index.d.mts +30 -0
  84. package/dist/mikuContest/service/index.d.ts +30 -0
  85. package/dist/mikuContest/service/index.js +139 -0
  86. package/dist/mikuContest/service/index.js.map +1 -0
  87. package/dist/mikuContest/service/index.mjs +135 -0
  88. package/dist/mikuContest/service/index.mjs.map +1 -0
  89. package/dist/mikuContest/types/index.d.mts +179 -0
  90. package/dist/mikuContest/types/index.d.ts +179 -0
  91. package/dist/mikuContest/types/index.js +4 -0
  92. package/dist/mikuContest/types/index.js.map +1 -0
  93. package/dist/mikuContest/types/index.mjs +3 -0
  94. package/dist/mikuContest/types/index.mjs.map +1 -0
  95. package/dist/mikuContest/ui/miniapp/index.d.mts +3 -0
  96. package/dist/mikuContest/ui/miniapp/index.d.ts +3 -0
  97. package/dist/mikuContest/ui/miniapp/index.js +566 -0
  98. package/dist/mikuContest/ui/miniapp/index.js.map +1 -0
  99. package/dist/mikuContest/ui/miniapp/index.mjs +540 -0
  100. package/dist/mikuContest/ui/miniapp/index.mjs.map +1 -0
  101. package/dist/mikuContest/ui/web/index.d.mts +4 -0
  102. package/dist/mikuContest/ui/web/index.d.ts +4 -0
  103. package/dist/mikuContest/ui/web/index.js +353 -0
  104. package/dist/mikuContest/ui/web/index.js.map +1 -0
  105. package/dist/mikuContest/ui/web/index.mjs +343 -0
  106. package/dist/mikuContest/ui/web/index.mjs.map +1 -0
  107. package/dist/qqbot/server/index.d.mts +126 -1
  108. package/dist/qqbot/server/index.d.ts +126 -1
  109. package/dist/qqbot/server/index.js +250 -0
  110. package/dist/qqbot/server/index.js.map +1 -1
  111. package/dist/qqbot/server/index.mjs +246 -1
  112. package/dist/qqbot/server/index.mjs.map +1 -1
  113. package/dist/service-D7DM1wW-.d.ts +38 -0
  114. package/dist/service-DPr2rlvH.d.mts +38 -0
  115. package/dist/types-BS7Xz09b.d.mts +14 -0
  116. package/dist/types-k4koMp4m.d.ts +14 -0
  117. package/package.json +76 -1
@@ -0,0 +1,26 @@
1
+ import { MikuContestStateSnapshot, MikuContestConfig, CreateMikuSubmissionInput, MikuSubmission, MikuSubmissionFilter, ReviewMikuSubmissionInput, VoteMikuSubmissionInput, SetMikuVoterRestrictionInput, MikuVoterRestriction, ResetMikuVotesInput } from './mikuContest/types/index.js';
2
+
3
+ interface MikuContestApiClient {
4
+ getSnapshot(): Promise<MikuContestStateSnapshot>;
5
+ updateContestConfig(patch: Partial<MikuContestConfig>): Promise<MikuContestConfig>;
6
+ createSubmission(input: CreateMikuSubmissionInput, mode?: 'web' | 'miniapp'): Promise<MikuSubmission>;
7
+ listSubmissions(filter?: MikuSubmissionFilter): Promise<MikuSubmission[]>;
8
+ reviewSubmission(input: ReviewMikuSubmissionInput): Promise<MikuSubmission>;
9
+ vote(input: VoteMikuSubmissionInput): Promise<MikuSubmission>;
10
+ setVoterRestriction(input: SetMikuVoterRestrictionInput): Promise<MikuVoterRestriction>;
11
+ resetVotes(input: ResetMikuVotesInput): Promise<{
12
+ removedVotes: number;
13
+ affectedSubmissions: string[];
14
+ }>;
15
+ exportSubmissions(filter?: MikuSubmissionFilter): Promise<ArrayBuffer>;
16
+ }
17
+ type HttpMethod = 'GET' | 'POST' | 'PATCH';
18
+ interface Requester {
19
+ <T>(url: string, options?: {
20
+ method?: HttpMethod;
21
+ body?: unknown;
22
+ }): Promise<T>;
23
+ }
24
+ declare const createMikuContestApiClient: (basePath: string, requester: Requester) => MikuContestApiClient;
25
+
26
+ export { type HttpMethod as H, type MikuContestApiClient as M, type Requester as R, createMikuContestApiClient as c };
@@ -0,0 +1,26 @@
1
+ import { MikuContestStateSnapshot, MikuContestConfig, CreateMikuSubmissionInput, MikuSubmission, MikuSubmissionFilter, ReviewMikuSubmissionInput, VoteMikuSubmissionInput, SetMikuVoterRestrictionInput, MikuVoterRestriction, ResetMikuVotesInput } from './mikuContest/types/index.mjs';
2
+
3
+ interface MikuContestApiClient {
4
+ getSnapshot(): Promise<MikuContestStateSnapshot>;
5
+ updateContestConfig(patch: Partial<MikuContestConfig>): Promise<MikuContestConfig>;
6
+ createSubmission(input: CreateMikuSubmissionInput, mode?: 'web' | 'miniapp'): Promise<MikuSubmission>;
7
+ listSubmissions(filter?: MikuSubmissionFilter): Promise<MikuSubmission[]>;
8
+ reviewSubmission(input: ReviewMikuSubmissionInput): Promise<MikuSubmission>;
9
+ vote(input: VoteMikuSubmissionInput): Promise<MikuSubmission>;
10
+ setVoterRestriction(input: SetMikuVoterRestrictionInput): Promise<MikuVoterRestriction>;
11
+ resetVotes(input: ResetMikuVotesInput): Promise<{
12
+ removedVotes: number;
13
+ affectedSubmissions: string[];
14
+ }>;
15
+ exportSubmissions(filter?: MikuSubmissionFilter): Promise<ArrayBuffer>;
16
+ }
17
+ type HttpMethod = 'GET' | 'POST' | 'PATCH';
18
+ interface Requester {
19
+ <T>(url: string, options?: {
20
+ method?: HttpMethod;
21
+ body?: unknown;
22
+ }): Promise<T>;
23
+ }
24
+ declare const createMikuContestApiClient: (basePath: string, requester: Requester) => MikuContestApiClient;
25
+
26
+ export { type HttpMethod as H, type MikuContestApiClient as M, type Requester as R, createMikuContestApiClient as c };
@@ -0,0 +1,8 @@
1
+ export { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoGameState, HuarongdaoStateSnapshot, PuzzleStatus } from './types/index.mjs';
2
+ export { buildSolvedTiles, canMove, inversionCount, isSolvable, isSolved, moveTile, shuffleSolvable } from './logic/index.mjs';
3
+ export { HttpMethod, HuarongdaoWebClientOptions, Requester, createHuarongdaoApiClient, createHuarongdaoWebClient } from './service/index.mjs';
4
+ export { HuarongdaoService, createHuarongdaoService } from './server/index.mjs';
5
+ export { HuarongdaoRouteConfig, createCreateConfigHandler, createDeleteConfigHandler, createGetSnapshotHandler, createListConfigsHandler, createUpdateConfigHandler } from './routes/index.mjs';
6
+ export { i as webUI } from '../index-Da2X78GE.mjs';
7
+ import 'next/server';
8
+ import 'react';
@@ -0,0 +1,8 @@
1
+ export { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoGameState, HuarongdaoStateSnapshot, PuzzleStatus } from './types/index.js';
2
+ export { buildSolvedTiles, canMove, inversionCount, isSolvable, isSolved, moveTile, shuffleSolvable } from './logic/index.js';
3
+ export { HttpMethod, HuarongdaoWebClientOptions, Requester, createHuarongdaoApiClient, createHuarongdaoWebClient } from './service/index.js';
4
+ export { HuarongdaoService, createHuarongdaoService } from './server/index.js';
5
+ export { HuarongdaoRouteConfig, createCreateConfigHandler, createDeleteConfigHandler, createGetSnapshotHandler, createListConfigsHandler, createUpdateConfigHandler } from './routes/index.js';
6
+ export { i as webUI } from '../index-B48rcsqv.js';
7
+ import 'next/server';
8
+ import 'react';
@@ -0,0 +1,360 @@
1
+ 'use strict';
2
+
3
+ var server = require('next/server');
4
+ var React3 = require('react');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+
16
+ // src/huarongdao/logic/game.ts
17
+ var clone = (v) => [...v];
18
+ var buildSolvedTiles = (rows, cols) => {
19
+ const total = rows * cols;
20
+ return Array.from({ length: total }, (_, i) => (i + 1) % total);
21
+ };
22
+ var isSolved = (tiles) => {
23
+ return tiles.every((v, i, arr) => v === (i + 1) % arr.length);
24
+ };
25
+ var canMove = (tiles, rows, cols, tileIndex) => {
26
+ const blank = tiles.indexOf(0);
27
+ if (blank < 0 || tileIndex < 0 || tileIndex >= tiles.length) return false;
28
+ const br = Math.floor(blank / cols);
29
+ const bc = blank % cols;
30
+ const tr = Math.floor(tileIndex / cols);
31
+ const tc = tileIndex % cols;
32
+ return Math.abs(br - tr) + Math.abs(bc - tc) === 1;
33
+ };
34
+ var moveTile = (state, tileIndex) => {
35
+ if (!canMove(state.tiles, state.rows, state.cols, tileIndex)) return state;
36
+ const nextTiles = clone(state.tiles);
37
+ const blank = nextTiles.indexOf(0);
38
+ const blankValue = nextTiles[blank];
39
+ const tileValue = nextTiles[tileIndex];
40
+ if (blankValue === void 0 || tileValue === void 0) return state;
41
+ nextTiles[blank] = tileValue;
42
+ nextTiles[tileIndex] = blankValue;
43
+ const solved = isSolved(nextTiles);
44
+ return {
45
+ ...state,
46
+ tiles: nextTiles,
47
+ moveCount: state.moveCount + 1,
48
+ isSolved: solved,
49
+ finishedAt: solved ? Date.now() : void 0
50
+ };
51
+ };
52
+ var inversionCount = (tiles) => {
53
+ const arr = tiles.filter((n) => n !== 0);
54
+ let cnt = 0;
55
+ for (let i = 0; i < arr.length; i += 1) {
56
+ for (let j = i + 1; j < arr.length; j += 1) {
57
+ if ((arr[i] ?? 0) > (arr[j] ?? 0)) cnt += 1;
58
+ }
59
+ }
60
+ return cnt;
61
+ };
62
+ var isSolvable = (tiles, rows, cols) => {
63
+ const inv = inversionCount(tiles);
64
+ if (cols % 2 === 1) return inv % 2 === 0;
65
+ const blankRowFromBottom = rows - Math.floor(tiles.indexOf(0) / cols);
66
+ return blankRowFromBottom % 2 === 0 !== (inv % 2 === 0);
67
+ };
68
+ var shuffleSolvable = (rows, cols, steps = 80) => {
69
+ let tiles = buildSolvedTiles(rows, cols);
70
+ for (let i = 0; i < steps; i += 1) {
71
+ const blank = tiles.indexOf(0);
72
+ const br = Math.floor(blank / cols);
73
+ const bc = blank % cols;
74
+ const candidate = [];
75
+ const dirs = [
76
+ [1, 0],
77
+ [-1, 0],
78
+ [0, 1],
79
+ [0, -1]
80
+ ];
81
+ dirs.forEach(([dr, dc]) => {
82
+ const nr = br + dr;
83
+ const nc = bc + dc;
84
+ if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) candidate.push(nr * cols + nc);
85
+ });
86
+ const idx = candidate[Math.floor(Math.random() * candidate.length)] ?? blank;
87
+ const next = clone(tiles);
88
+ [next[blank], next[idx]] = [next[idx] ?? 0, next[blank] ?? 0];
89
+ tiles = next;
90
+ }
91
+ return tiles;
92
+ };
93
+
94
+ // src/huarongdao/service/api/client.ts
95
+ var unwrap = (result) => {
96
+ if (!result.success || result.data === void 0) throw new Error(result.error || "\u8BF7\u6C42\u5931\u8D25");
97
+ return result.data;
98
+ };
99
+ var createHuarongdaoApiClient = (basePath, requester2) => ({
100
+ async getSnapshot() {
101
+ return unwrap(await requester2(`${basePath}/snapshot`, { method: "GET" }));
102
+ },
103
+ async listConfigs() {
104
+ return unwrap(await requester2(`${basePath}/configs`, { method: "GET" }));
105
+ },
106
+ async createConfig(input) {
107
+ return unwrap(await requester2(`${basePath}/configs`, { method: "POST", body: input }));
108
+ },
109
+ async updateConfig(id2, patch) {
110
+ return unwrap(await requester2(`${basePath}/configs`, { method: "PATCH", body: { id: id2, patch } }));
111
+ },
112
+ async deleteConfig(id2) {
113
+ return unwrap(await requester2(`${basePath}/configs`, { method: "DELETE", body: { id: id2 } }));
114
+ }
115
+ });
116
+
117
+ // src/huarongdao/service/web/client.ts
118
+ var requester = (options) => {
119
+ const baseUrl = options.baseUrl || "";
120
+ return async (url, req) => {
121
+ const res = await fetch(`${baseUrl}${url}`, {
122
+ method: req?.method || "GET",
123
+ headers: { "Content-Type": "application/json", ...options.headers || {} },
124
+ body: req?.body ? JSON.stringify(req.body) : void 0
125
+ });
126
+ return await res.json();
127
+ };
128
+ };
129
+ var createHuarongdaoWebClient = (options = {}) => {
130
+ return createHuarongdaoApiClient(options.basePath || "/api/huarongdao", requester(options));
131
+ };
132
+
133
+ // src/huarongdao/server/service.ts
134
+ var id = () => `puzzle_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
135
+ var HuarongdaoService = class {
136
+ constructor() {
137
+ this.configs = /* @__PURE__ */ new Map();
138
+ }
139
+ listConfigs() {
140
+ return [...this.configs.values()];
141
+ }
142
+ getBySlug(slug) {
143
+ return [...this.configs.values()].find((c) => c.slug === slug) || null;
144
+ }
145
+ createConfig(input) {
146
+ const now = (/* @__PURE__ */ new Date()).toISOString();
147
+ const next = {
148
+ id: id(),
149
+ slug: input.slug,
150
+ name: input.name,
151
+ description: input.description,
152
+ status: "draft",
153
+ rows: input.rows,
154
+ cols: input.cols,
155
+ sourceImageUrl: input.sourceImageUrl,
156
+ showReference: input.showReference ?? true,
157
+ shuffleSteps: input.shuffleSteps ?? 80,
158
+ timeLimitSec: input.timeLimitSec,
159
+ startMode: input.startMode ?? "random-solvable",
160
+ initialTiles: input.initialTiles,
161
+ createdAt: now,
162
+ updatedAt: now
163
+ };
164
+ this.configs.set(next.id, next);
165
+ return next;
166
+ }
167
+ updateConfig(id2, patch) {
168
+ const cur = this.configs.get(id2);
169
+ if (!cur) throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728");
170
+ const next = { ...cur, ...patch, id: cur.id, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
171
+ this.configs.set(id2, next);
172
+ return next;
173
+ }
174
+ deleteConfig(id2) {
175
+ this.configs.delete(id2);
176
+ }
177
+ getSnapshot() {
178
+ const configs = this.listConfigs();
179
+ return {
180
+ configs,
181
+ activeConfig: configs.find((c) => c.status === "active")
182
+ };
183
+ }
184
+ };
185
+ var createHuarongdaoService = () => new HuarongdaoService();
186
+ var resolveService = (config) => config?.service || createHuarongdaoService();
187
+ var createGetSnapshotHandler = (config) => {
188
+ const service = resolveService(config);
189
+ return async (_req) => server.NextResponse.json({ success: true, data: service.getSnapshot() });
190
+ };
191
+ var createListConfigsHandler = (config) => {
192
+ const service = resolveService(config);
193
+ return async (_req) => server.NextResponse.json({ success: true, data: service.listConfigs() });
194
+ };
195
+ var createCreateConfigHandler = (config) => {
196
+ const service = resolveService(config);
197
+ return async (req) => {
198
+ try {
199
+ const body = await req.json();
200
+ const data = service.createConfig(body);
201
+ return server.NextResponse.json({ success: true, data });
202
+ } catch (e) {
203
+ return server.NextResponse.json({ success: false, error: e.message }, { status: 400 });
204
+ }
205
+ };
206
+ };
207
+ var createUpdateConfigHandler = (config) => {
208
+ const service = resolveService(config);
209
+ return async (req) => {
210
+ try {
211
+ const body = await req.json();
212
+ const data = service.updateConfig(body.id, body.patch || {});
213
+ return server.NextResponse.json({ success: true, data });
214
+ } catch (e) {
215
+ return server.NextResponse.json({ success: false, error: e.message }, { status: 400 });
216
+ }
217
+ };
218
+ };
219
+ var createDeleteConfigHandler = (config) => {
220
+ const service = resolveService(config);
221
+ return async (req) => {
222
+ try {
223
+ const body = await req.json();
224
+ service.deleteConfig(body.id);
225
+ return server.NextResponse.json({ success: true, data: true });
226
+ } catch (e) {
227
+ return server.NextResponse.json({ success: false, error: e.message }, { status: 400 });
228
+ }
229
+ };
230
+ };
231
+
232
+ // src/huarongdao/ui/web/index.ts
233
+ var web_exports = {};
234
+ __export(web_exports, {
235
+ HuarongdaoBoard: () => HuarongdaoBoard_default,
236
+ HuarongdaoConfigPage: () => HuarongdaoConfigPage_default,
237
+ HuarongdaoGamePage: () => HuarongdaoGamePage_default
238
+ });
239
+ var HuarongdaoBoard = ({ tiles, rows, cols, imageUrl, onClickTile }) => {
240
+ const total = rows * cols;
241
+ return /* @__PURE__ */ React3__default.default.createElement(
242
+ "div",
243
+ {
244
+ style: {
245
+ display: "grid",
246
+ gridTemplateColumns: `repeat(${cols}, 96px)`,
247
+ gridTemplateRows: `repeat(${rows}, 96px)`,
248
+ gap: 6
249
+ }
250
+ },
251
+ tiles.map((tile, index) => {
252
+ if (tile === 0) return /* @__PURE__ */ React3__default.default.createElement("div", { key: `blank-${index}`, style: { background: "#ddd" } });
253
+ const pieceIndex = tile - 1;
254
+ const pr = Math.floor(pieceIndex / cols);
255
+ const pc = pieceIndex % cols;
256
+ return /* @__PURE__ */ React3__default.default.createElement(
257
+ "button",
258
+ {
259
+ key: `${tile}-${index}`,
260
+ onClick: () => onClickTile(index),
261
+ style: {
262
+ border: "1px solid #666",
263
+ cursor: "pointer",
264
+ backgroundImage: `url(${imageUrl})`,
265
+ backgroundSize: `${cols * 96}px ${rows * 96}px`,
266
+ backgroundPosition: `${-pc * 96}px ${-pr * 96}px`
267
+ }
268
+ }
269
+ );
270
+ }),
271
+ tiles.length !== total ? /* @__PURE__ */ React3__default.default.createElement("div", null, "tiles invalid") : null
272
+ );
273
+ };
274
+ var HuarongdaoBoard_default = HuarongdaoBoard;
275
+ var HuarongdaoGamePage = ({ config }) => {
276
+ const initial = React3.useMemo(() => {
277
+ const tiles = config.startMode === "custom-layout" && config.initialTiles?.length === config.rows * config.cols ? config.initialTiles : shuffleSolvable(config.rows, config.cols, config.shuffleSteps);
278
+ return {
279
+ tiles,
280
+ rows: config.rows,
281
+ cols: config.cols,
282
+ moveCount: 0,
283
+ startedAt: Date.now(),
284
+ isSolved: false
285
+ };
286
+ }, [config]);
287
+ const [state, setState] = React3.useState(initial);
288
+ const reset = () => {
289
+ setState({
290
+ ...state,
291
+ tiles: shuffleSolvable(config.rows, config.cols, config.shuffleSteps),
292
+ moveCount: 0,
293
+ startedAt: Date.now(),
294
+ finishedAt: void 0,
295
+ isSolved: false
296
+ });
297
+ };
298
+ const solveNow = () => {
299
+ setState((prev) => ({ ...prev, tiles: buildSolvedTiles(config.rows, config.cols), isSolved: true, finishedAt: Date.now() }));
300
+ };
301
+ const durationSec = Math.floor(((state.finishedAt || Date.now()) - state.startedAt) / 1e3);
302
+ return /* @__PURE__ */ React3__default.default.createElement("section", null, /* @__PURE__ */ React3__default.default.createElement("h2", null, config.name), /* @__PURE__ */ React3__default.default.createElement("p", null, "\u6B65\u6570\uFF1A", state.moveCount, " \uFF5C \u7528\u65F6\uFF1A", durationSec, "s ", state.isSolved ? "\uFF5C\u5DF2\u901A\u5173 \u{1F389}" : ""), /* @__PURE__ */ React3__default.default.createElement(
303
+ HuarongdaoBoard_default,
304
+ {
305
+ tiles: state.tiles,
306
+ rows: state.rows,
307
+ cols: state.cols,
308
+ imageUrl: config.sourceImageUrl,
309
+ onClickTile: (idx) => setState((prev) => moveTile(prev, idx))
310
+ }
311
+ ), /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginTop: 12 } }, /* @__PURE__ */ React3__default.default.createElement("button", { onClick: reset }, "\u91CD\u65B0\u6253\u4E71"), /* @__PURE__ */ React3__default.default.createElement("button", { onClick: solveNow }, "\u4E00\u952E\u5B8C\u6210(\u6D4B\u8BD5)")), config.showReference ? /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginTop: 12 } }, /* @__PURE__ */ React3__default.default.createElement("div", null, "\u53C2\u8003\u56FE\uFF1A"), /* @__PURE__ */ React3__default.default.createElement("img", { src: config.sourceImageUrl, alt: "reference", style: { width: 180, border: "1px solid #ccc" } })) : null);
312
+ };
313
+ var HuarongdaoGamePage_default = HuarongdaoGamePage;
314
+ var HuarongdaoConfigPage = () => {
315
+ const service = React3.useMemo(() => createHuarongdaoService(), []);
316
+ const [version, setVersion] = React3.useState(0);
317
+ const [name, setName] = React3.useState("\u793A\u4F8B\u534E\u5BB9\u9053");
318
+ const [slug, setSlug] = React3.useState("sample-huarongdao");
319
+ const [imageUrl, setImageUrl] = React3.useState("https://i.imgur.com/6z7Qw6M.png");
320
+ const [rows, setRows] = React3.useState(3);
321
+ const [cols, setCols] = React3.useState(3);
322
+ const list = React3.useMemo(() => {
323
+ return service.listConfigs();
324
+ }, [service, version]);
325
+ const create = () => {
326
+ const input = {
327
+ name,
328
+ slug,
329
+ sourceImageUrl: imageUrl,
330
+ rows,
331
+ cols,
332
+ showReference: true,
333
+ shuffleSteps: 80
334
+ };
335
+ service.createConfig(input);
336
+ setVersion((v) => v + 1);
337
+ };
338
+ return /* @__PURE__ */ React3__default.default.createElement("section", null, /* @__PURE__ */ React3__default.default.createElement("h2", null, "\u534E\u5BB9\u9053\u540E\u53F0\u914D\u7F6E\u9875"), /* @__PURE__ */ React3__default.default.createElement("p", null, "\u652F\u6301\u591A\u5957\u914D\u7F6E\u521B\u5EFA\u3001\u53C2\u6570\u5316\u7BA1\u7406\uFF08V1 \u9AA8\u67B6\uFF09"), /* @__PURE__ */ React3__default.default.createElement("div", null, /* @__PURE__ */ React3__default.default.createElement("input", { value: name, onChange: (e) => setName(e.target.value), placeholder: "name" }), /* @__PURE__ */ React3__default.default.createElement("input", { value: slug, onChange: (e) => setSlug(e.target.value), placeholder: "slug" }), /* @__PURE__ */ React3__default.default.createElement("input", { value: imageUrl, onChange: (e) => setImageUrl(e.target.value), placeholder: "image url" }), /* @__PURE__ */ React3__default.default.createElement("input", { type: "number", value: rows, onChange: (e) => setRows(Number(e.target.value) || 3) }), /* @__PURE__ */ React3__default.default.createElement("input", { type: "number", value: cols, onChange: (e) => setCols(Number(e.target.value) || 3) }), /* @__PURE__ */ React3__default.default.createElement("button", { onClick: create }, "\u521B\u5EFA\u914D\u7F6E")), /* @__PURE__ */ React3__default.default.createElement("ul", null, list.map((item) => /* @__PURE__ */ React3__default.default.createElement("li", { key: item.id }, item.name, " (", item.slug, ") - ", item.rows, "x", item.cols, " - ", item.status))));
339
+ };
340
+ var HuarongdaoConfigPage_default = HuarongdaoConfigPage;
341
+
342
+ exports.HuarongdaoService = HuarongdaoService;
343
+ exports.buildSolvedTiles = buildSolvedTiles;
344
+ exports.canMove = canMove;
345
+ exports.createCreateConfigHandler = createCreateConfigHandler;
346
+ exports.createDeleteConfigHandler = createDeleteConfigHandler;
347
+ exports.createGetSnapshotHandler = createGetSnapshotHandler;
348
+ exports.createHuarongdaoApiClient = createHuarongdaoApiClient;
349
+ exports.createHuarongdaoService = createHuarongdaoService;
350
+ exports.createHuarongdaoWebClient = createHuarongdaoWebClient;
351
+ exports.createListConfigsHandler = createListConfigsHandler;
352
+ exports.createUpdateConfigHandler = createUpdateConfigHandler;
353
+ exports.inversionCount = inversionCount;
354
+ exports.isSolvable = isSolvable;
355
+ exports.isSolved = isSolved;
356
+ exports.moveTile = moveTile;
357
+ exports.shuffleSolvable = shuffleSolvable;
358
+ exports.webUI = web_exports;
359
+ //# sourceMappingURL=index.js.map
360
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/huarongdao/logic/game.ts","../../src/huarongdao/service/api/client.ts","../../src/huarongdao/service/web/client.ts","../../src/huarongdao/server/service.ts","../../src/huarongdao/routes/index.ts","../../src/huarongdao/ui/web/index.ts","../../src/huarongdao/ui/web/components/HuarongdaoBoard.tsx","../../src/huarongdao/ui/web/pages/HuarongdaoGamePage.tsx","../../src/huarongdao/ui/web/pages/HuarongdaoConfigPage.tsx"],"names":["requester","id","NextResponse","React","useMemo","useState"],"mappings":"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","file":"index.js","sourcesContent":["import type { HuarongdaoGameState } from '../types';\n\nconst clone = <T>(v: T[]): T[] => [...v];\n\nexport const buildSolvedTiles = (rows: number, cols: number): number[] => {\n const total = rows * cols;\n return Array.from({ length: total }, (_, i) => (i + 1) % total);\n};\n\nexport const isSolved = (tiles: number[]): boolean => {\n return tiles.every((v, i, arr) => v === (i + 1) % arr.length);\n};\n\nexport const canMove = (tiles: number[], rows: number, cols: number, tileIndex: number): boolean => {\n const blank = tiles.indexOf(0);\n if (blank < 0 || tileIndex < 0 || tileIndex >= tiles.length) return false;\n const br = Math.floor(blank / cols);\n const bc = blank % cols;\n const tr = Math.floor(tileIndex / cols);\n const tc = tileIndex % cols;\n return Math.abs(br - tr) + Math.abs(bc - tc) === 1;\n};\n\nexport const moveTile = (state: HuarongdaoGameState, tileIndex: number): HuarongdaoGameState => {\n if (!canMove(state.tiles, state.rows, state.cols, tileIndex)) return state;\n const nextTiles = clone(state.tiles);\n const blank = nextTiles.indexOf(0);\n const blankValue = nextTiles[blank];\n const tileValue = nextTiles[tileIndex];\n if (blankValue === undefined || tileValue === undefined) return state;\n nextTiles[blank] = tileValue;\n nextTiles[tileIndex] = blankValue;\n const solved = isSolved(nextTiles);\n return {\n ...state,\n tiles: nextTiles,\n moveCount: state.moveCount + 1,\n isSolved: solved,\n finishedAt: solved ? Date.now() : undefined,\n };\n};\n\nexport const inversionCount = (tiles: number[]): number => {\n const arr = tiles.filter((n) => n !== 0);\n let cnt = 0;\n for (let i = 0; i < arr.length; i += 1) {\n for (let j = i + 1; j < arr.length; j += 1) {\n if ((arr[i] ?? 0) > (arr[j] ?? 0)) cnt += 1;\n }\n }\n return cnt;\n};\n\nexport const isSolvable = (tiles: number[], rows: number, cols: number): boolean => {\n const inv = inversionCount(tiles);\n if (cols % 2 === 1) return inv % 2 === 0;\n const blankRowFromBottom = rows - Math.floor(tiles.indexOf(0) / cols);\n return (blankRowFromBottom % 2 === 0) !== (inv % 2 === 0);\n};\n\nexport const shuffleSolvable = (rows: number, cols: number, steps = 80): number[] => {\n let tiles = buildSolvedTiles(rows, cols);\n for (let i = 0; i < steps; i += 1) {\n const blank = tiles.indexOf(0);\n const br = Math.floor(blank / cols);\n const bc = blank % cols;\n const candidate: number[] = [];\n const dirs: Array<[number, number]> = [\n [1, 0],\n [-1, 0],\n [0, 1],\n [0, -1],\n ];\n dirs.forEach(([dr, dc]) => {\n const nr = br + dr;\n const nc = bc + dc;\n if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) candidate.push(nr * cols + nc);\n });\n const idx = candidate[Math.floor(Math.random() * candidate.length)] ?? blank;\n const next = clone(tiles);\n [next[blank], next[idx]] = [next[idx] ?? 0, next[blank] ?? 0];\n tiles = next;\n }\n return tiles;\n};\n","import type { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoStateSnapshot } from '../../types';\n\nexport type HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';\nexport interface Requester {\n <T>(url: string, options?: { method?: HttpMethod; body?: unknown }): Promise<T>;\n}\n\ninterface ApiEnvelope<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\nconst unwrap = <T>(result: ApiEnvelope<T>): T => {\n if (!result.success || result.data === undefined) throw new Error(result.error || '请求失败');\n return result.data;\n};\n\nexport const createHuarongdaoApiClient = (basePath: string, requester: Requester) => ({\n async getSnapshot(): Promise<HuarongdaoStateSnapshot> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoStateSnapshot>>(`${basePath}/snapshot`, { method: 'GET' }));\n },\n async listConfigs(): Promise<HuarongdaoConfig[]> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoConfig[]>>(`${basePath}/configs`, { method: 'GET' }));\n },\n async createConfig(input: CreateHuarongdaoConfigInput): Promise<HuarongdaoConfig> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoConfig>>(`${basePath}/configs`, { method: 'POST', body: input }));\n },\n async updateConfig(id: string, patch: Partial<HuarongdaoConfig>): Promise<HuarongdaoConfig> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoConfig>>(`${basePath}/configs`, { method: 'PATCH', body: { id, patch } }));\n },\n async deleteConfig(id: string): Promise<boolean> {\n return unwrap(await requester<ApiEnvelope<boolean>>(`${basePath}/configs`, { method: 'DELETE', body: { id } }));\n },\n});\n","import { createHuarongdaoApiClient, type Requester } from '../api';\n\nexport interface HuarongdaoWebClientOptions {\n baseUrl?: string;\n basePath?: string;\n headers?: Record<string, string>;\n}\n\nconst requester = (options: HuarongdaoWebClientOptions): Requester => {\n const baseUrl = options.baseUrl || '';\n return async <T>(url: string, req?: { method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'; body?: unknown }): Promise<T> => {\n const res = await fetch(`${baseUrl}${url}`, {\n method: req?.method || 'GET',\n headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },\n body: req?.body ? JSON.stringify(req.body) : undefined,\n });\n return (await res.json()) as T;\n };\n};\n\nexport const createHuarongdaoWebClient = (options: HuarongdaoWebClientOptions = {}) => {\n return createHuarongdaoApiClient(options.basePath || '/api/huarongdao', requester(options));\n};\n","import type { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoStateSnapshot } from '../types';\n\nconst id = () => `puzzle_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n\nexport class HuarongdaoService {\n private readonly configs = new Map<string, HuarongdaoConfig>();\n\n listConfigs(): HuarongdaoConfig[] {\n return [...this.configs.values()];\n }\n\n getBySlug(slug: string): HuarongdaoConfig | null {\n return [...this.configs.values()].find((c) => c.slug === slug) || null;\n }\n\n createConfig(input: CreateHuarongdaoConfigInput): HuarongdaoConfig {\n const now = new Date().toISOString();\n const next: HuarongdaoConfig = {\n id: id(),\n slug: input.slug,\n name: input.name,\n description: input.description,\n status: 'draft',\n rows: input.rows,\n cols: input.cols,\n sourceImageUrl: input.sourceImageUrl,\n showReference: input.showReference ?? true,\n shuffleSteps: input.shuffleSteps ?? 80,\n timeLimitSec: input.timeLimitSec,\n startMode: input.startMode ?? 'random-solvable',\n initialTiles: input.initialTiles,\n createdAt: now,\n updatedAt: now,\n };\n this.configs.set(next.id, next);\n return next;\n }\n\n updateConfig(id: string, patch: Partial<HuarongdaoConfig>): HuarongdaoConfig {\n const cur = this.configs.get(id);\n if (!cur) throw new Error('配置不存在');\n const next: HuarongdaoConfig = { ...cur, ...patch, id: cur.id, updatedAt: new Date().toISOString() };\n this.configs.set(id, next);\n return next;\n }\n\n deleteConfig(id: string): void {\n this.configs.delete(id);\n }\n\n getSnapshot(): HuarongdaoStateSnapshot {\n const configs = this.listConfigs();\n return {\n configs,\n activeConfig: configs.find((c) => c.status === 'active'),\n };\n }\n}\n\nexport const createHuarongdaoService = () => new HuarongdaoService();\n","import { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport { createHuarongdaoService } from '../server';\nimport type { CreateHuarongdaoConfigInput, HuarongdaoConfig } from '../types';\n\nexport interface HuarongdaoRouteConfig {\n service?: ReturnType<typeof createHuarongdaoService>;\n}\n\nconst resolveService = (config?: HuarongdaoRouteConfig) => config?.service || createHuarongdaoService();\n\nexport const createGetSnapshotHandler = (config?: HuarongdaoRouteConfig) => {\n const service = resolveService(config);\n return async (_req: NextRequest) => NextResponse.json({ success: true, data: service.getSnapshot() });\n};\n\nexport const createListConfigsHandler = (config?: HuarongdaoRouteConfig) => {\n const service = resolveService(config);\n return async (_req: NextRequest) => NextResponse.json({ success: true, data: service.listConfigs() });\n};\n\nexport const createCreateConfigHandler = (config?: HuarongdaoRouteConfig) => {\n const service = resolveService(config);\n return async (req: NextRequest) => {\n try {\n const body = (await req.json()) as CreateHuarongdaoConfigInput;\n const data = service.createConfig(body);\n return NextResponse.json({ success: true, data });\n } catch (e) {\n return NextResponse.json({ success: false, error: (e as Error).message }, { status: 400 });\n }\n };\n};\n\nexport const createUpdateConfigHandler = (config?: HuarongdaoRouteConfig) => {\n const service = resolveService(config);\n return async (req: NextRequest) => {\n try {\n const body = (await req.json()) as { id: string; patch: Partial<HuarongdaoConfig> };\n const data = service.updateConfig(body.id, body.patch || {});\n return NextResponse.json({ success: true, data });\n } catch (e) {\n return NextResponse.json({ success: false, error: (e as Error).message }, { status: 400 });\n }\n };\n};\n\nexport const createDeleteConfigHandler = (config?: HuarongdaoRouteConfig) => {\n const service = resolveService(config);\n return async (req: NextRequest) => {\n try {\n const body = (await req.json()) as { id: string };\n service.deleteConfig(body.id);\n return NextResponse.json({ success: true, data: true });\n } catch (e) {\n return NextResponse.json({ success: false, error: (e as Error).message }, { status: 400 });\n }\n };\n};\n","export { default as HuarongdaoBoard } from './components/HuarongdaoBoard';\nexport { default as HuarongdaoGamePage } from './pages/HuarongdaoGamePage';\nexport { default as HuarongdaoConfigPage } from './pages/HuarongdaoConfigPage';\n","import React from 'react';\n\nexport interface HuarongdaoBoardProps {\n tiles: number[];\n rows: number;\n cols: number;\n imageUrl: string;\n onClickTile: (index: number) => void;\n}\n\nconst HuarongdaoBoard: React.FC<HuarongdaoBoardProps> = ({ tiles, rows, cols, imageUrl, onClickTile }) => {\n const total = rows * cols;\n return (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: `repeat(${cols}, 96px)`,\n gridTemplateRows: `repeat(${rows}, 96px)`,\n gap: 6,\n }}\n >\n {tiles.map((tile, index) => {\n if (tile === 0) return <div key={`blank-${index}`} style={{ background: '#ddd' }} />;\n const pieceIndex = tile - 1;\n const pr = Math.floor(pieceIndex / cols);\n const pc = pieceIndex % cols;\n return (\n <button\n key={`${tile}-${index}`}\n onClick={() => onClickTile(index)}\n style={{\n border: '1px solid #666',\n cursor: 'pointer',\n backgroundImage: `url(${imageUrl})`,\n backgroundSize: `${cols * 96}px ${rows * 96}px`,\n backgroundPosition: `${-pc * 96}px ${-pr * 96}px`,\n }}\n />\n );\n })}\n {tiles.length !== total ? <div>tiles invalid</div> : null}\n </div>\n );\n};\n\nexport default HuarongdaoBoard;\n","import React, { useMemo, useState } from 'react';\nimport { buildSolvedTiles, moveTile, shuffleSolvable } from '../../../logic';\nimport type { HuarongdaoConfig, HuarongdaoGameState } from '../../../types';\nimport HuarongdaoBoard from '../components/HuarongdaoBoard';\n\nexport interface HuarongdaoGamePageProps {\n config: HuarongdaoConfig;\n}\n\nconst HuarongdaoGamePage: React.FC<HuarongdaoGamePageProps> = ({ config }) => {\n const initial = useMemo<HuarongdaoGameState>(() => {\n const tiles = config.startMode === 'custom-layout' && config.initialTiles?.length === config.rows * config.cols\n ? config.initialTiles\n : shuffleSolvable(config.rows, config.cols, config.shuffleSteps);\n return {\n tiles,\n rows: config.rows,\n cols: config.cols,\n moveCount: 0,\n startedAt: Date.now(),\n isSolved: false,\n };\n }, [config]);\n\n const [state, setState] = useState(initial);\n\n const reset = () => {\n setState({\n ...state,\n tiles: shuffleSolvable(config.rows, config.cols, config.shuffleSteps),\n moveCount: 0,\n startedAt: Date.now(),\n finishedAt: undefined,\n isSolved: false,\n });\n };\n\n const solveNow = () => {\n setState((prev) => ({ ...prev, tiles: buildSolvedTiles(config.rows, config.cols), isSolved: true, finishedAt: Date.now() }));\n };\n\n const durationSec = Math.floor(((state.finishedAt || Date.now()) - state.startedAt) / 1000);\n\n return (\n <section>\n <h2>{config.name}</h2>\n <p>步数:{state.moveCount} | 用时:{durationSec}s {state.isSolved ? '|已通关 🎉' : ''}</p>\n <HuarongdaoBoard\n tiles={state.tiles}\n rows={state.rows}\n cols={state.cols}\n imageUrl={config.sourceImageUrl}\n onClickTile={(idx) => setState((prev) => moveTile(prev, idx))}\n />\n <div style={{ marginTop: 12 }}>\n <button onClick={reset}>重新打乱</button>\n <button onClick={solveNow}>一键完成(测试)</button>\n </div>\n {config.showReference ? (\n <div style={{ marginTop: 12 }}>\n <div>参考图:</div>\n <img src={config.sourceImageUrl} alt=\"reference\" style={{ width: 180, border: '1px solid #ccc' }} />\n </div>\n ) : null}\n </section>\n );\n};\n\nexport default HuarongdaoGamePage;\n","import React, { useMemo, useState } from 'react';\nimport { createHuarongdaoService } from '../../../server';\nimport type { CreateHuarongdaoConfigInput } from '../../../types';\n\nconst HuarongdaoConfigPage: React.FC = () => {\n const service = useMemo(() => createHuarongdaoService(), []);\n const [version, setVersion] = useState(0);\n const [name, setName] = useState('示例华容道');\n const [slug, setSlug] = useState('sample-huarongdao');\n const [imageUrl, setImageUrl] = useState('https://i.imgur.com/6z7Qw6M.png');\n const [rows, setRows] = useState(3);\n const [cols, setCols] = useState(3);\n\n const list = useMemo(() => {\n void version;\n return service.listConfigs();\n }, [service, version]);\n\n const create = () => {\n const input: CreateHuarongdaoConfigInput = {\n name,\n slug,\n sourceImageUrl: imageUrl,\n rows,\n cols,\n showReference: true,\n shuffleSteps: 80,\n };\n service.createConfig(input);\n setVersion((v) => v + 1);\n };\n\n return (\n <section>\n <h2>华容道后台配置页</h2>\n <p>支持多套配置创建、参数化管理(V1 骨架)</p>\n <div>\n <input value={name} onChange={(e) => setName(e.target.value)} placeholder=\"name\" />\n <input value={slug} onChange={(e) => setSlug(e.target.value)} placeholder=\"slug\" />\n <input value={imageUrl} onChange={(e) => setImageUrl(e.target.value)} placeholder=\"image url\" />\n <input type=\"number\" value={rows} onChange={(e) => setRows(Number(e.target.value) || 3)} />\n <input type=\"number\" value={cols} onChange={(e) => setCols(Number(e.target.value) || 3)} />\n <button onClick={create}>创建配置</button>\n </div>\n <ul>\n {list.map((item) => (\n <li key={item.id}>{item.name} ({item.slug}) - {item.rows}x{item.cols} - {item.status}</li>\n ))}\n </ul>\n </section>\n );\n};\n\nexport default HuarongdaoConfigPage;\n"]}