poi-plugin-gimmick-tracker 0.2.0 → 0.2.1

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 (3) hide show
  1. package/README.md +2 -0
  2. package/index.js +157 -70
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -28,6 +28,8 @@ P2点空优1次
28
28
 
29
29
  导入时逐行报告识别结果,无法识别的行会明确指出,不会静默丢弃。条件可删除,重复导入按签名更新次数。
30
30
 
31
+ 解析器对攻略写法尽量鲁棒:中文数字(两次/一回)、省略「胜/点/次」(`P3点A2次`)、乘号计数(`S×2`)、词序变体(`G点2次S胜`、`到达E2点2次`)、`boss/王点/斩杀` 等装饰词、大小写混排、全角字符、繁体(點/勝/達)、一行挤多条。`击破` 按 A 胜理解;`P1boss一次` 这类无字母点的 boss 简写按「P1点击破 A胜」理解并在报告里提示。
32
+
31
33
  ## 数据与隐私
32
34
 
33
35
  - 状态按成员分文件保存在 `<poi AppData>/gimmick-tracker/<memberId>/state-v2.json`,临时文件加 rename 原子写。
package/index.js CHANGED
@@ -62,96 +62,180 @@ function matchesCondition(condition, observation) {
62
62
  }
63
63
 
64
64
  // src/parser.js
65
- var AIR_TEXT_TO_STATE = { \u7A7A\u786E: 1, \u7A7A\u512A: 2, \u7A7A\u4F18: 2, \u7A7A\u5747: 3, \u7A7A\u52A3: 4 };
65
+ var AIR_ALIAS = {
66
+ \u7A7A\u786E: 1,
67
+ \u7A7A\u78BA: 1,
68
+ \u5236\u7A7A\u786E\u4FDD: 1,
69
+ \u5236\u7A7A\u78BA\u4FDD: 1,
70
+ \u5236\u7A7A\u6743\u786E\u4FDD: 1,
71
+ \u5236\u7A7A\u6B0A\u78BA\u4FDD: 1,
72
+ \u7A7A\u512A: 2,
73
+ \u7A7A\u4F18: 2,
74
+ \u822A\u7A7A\u512A\u52E2: 2,
75
+ \u822A\u7A7A\u4F18\u52BF: 2,
76
+ \u7A7A\u5747: 3,
77
+ \u7A7A\u5747\u8861: 3,
78
+ \u7A7A\u52A3: 4
79
+ };
66
80
  var AIR_STATE_TO_TEXT = { 1: "\u7A7A\u786E", 2: "\u7A7A\u4F18", 3: "\u7A7A\u5747", 4: "\u7A7A\u52A3" };
81
+ var CHINESE_DIGIT = { \u4E00: 1, \u4E24: 2, \u4E8C: 2, \u4E09: 3, \u56DB: 4, \u4E94: 5, \u516D: 6, \u4E03: 7, \u516B: 8, \u4E5D: 9, \u5341: 10 };
67
82
  var NODE = "([A-Za-z]{1,2}\\d{0,2})";
68
- var AIR = "(\u7A7A\u786E|\u7A7A\u512A|\u7A7A\u4F18|\u7A7A\u5747|\u7A7A\u52A3)";
69
- var RANK = "(SS|S|A|B|C|D|E)";
70
- var PATTERNS = [
71
- { kind: "node-arrival", re: new RegExp(`^${NODE}\\s*\u70B9\\s*\u5230(\u8FBE|\u9054)\\s*(\\d+)\\s*\u6B21$`, "i"), order: 0 },
72
- { kind: "victory-rank", re: new RegExp(`^${NODE}\\s*\u70B9\\s*${RANK}\\s*(?:\u80DC|\u52DD)?\\s*(\\d+)\\s*\u6B21$`, "i"), order: 1 },
73
- { kind: "node-air-state", re: new RegExp(`^${NODE}\\s*\u70B9\\s*${AIR}\\s*(\\d+)\\s*\u6B21$`, "i"), order: 2 },
74
- { kind: "base-air-defense", re: new RegExp(`^(\u5B88\u5BB6|\u57FA\u5730\u9632\u7A7A|\u5BB6\u91CC)\\s*${AIR}\\s*(\\d+)\\s*\u6B21$`, "i"), order: 3 }
83
+ var DIAN = "[\u70B9\u70B9\u9EDE]?";
84
+ var CNT = "(\\d+|[\u4E00\u4E24\u4E8C\u4E09\u56DB\u4E94\u516D\u4E03\u516B\u4E5D\u5341])";
85
+ var CNT_UNIT = "[\u6B21\u56DE\u51F8]?";
86
+ var RANKC = "(SS|S|A|B|C|D|E)";
87
+ var AIR = "(\u7A7A\u786E|\u7A7A\u512A|\u7A7A\u4F18|\u7A7A\u5747|\u7A7A\u52A3|\u5236\u7A7A\u786E\u4FDD|\u5236\u7A7A\u78BA\u4FDD|\u5236\u7A7A\u6743\u786E\u4FDD|\u5236\u7A7A\u6B0A\u78BA\u4FDD|\u822A\u7A7A\u512A\u52E2|\u822A\u7A7A\u4F18\u52BF)";
88
+ var WIN = "[\u80DC\u52DD]?";
89
+ var KILL = "[\u51FB\u64CA]\u7834";
90
+ var REACH = "\u5230[\u8FBE\u9054]";
91
+ var FORMS = [
92
+ { kind: "base-air-defense", re: new RegExp(`(\u5B88\u5BB6|\u57FA\u5730\u9632\u7A7A\u6218|\u57FA\u5730\u9632\u7A7A|\u5BB6\u91CC|\u9632\u7A7A|\u57FA\u5730)${AIR}${CNT}${CNT_UNIT}`, "i") },
93
+ { kind: "reach-after", re: new RegExp(`${REACH}${NODE}${DIAN}${CNT}${CNT_UNIT}`, "i") },
94
+ { kind: "node-arrival", re: new RegExp(`${NODE}${DIAN}${REACH}${CNT}${CNT_UNIT}`, "i") },
95
+ { kind: "kill", re: new RegExp(`${NODE}${DIAN}${KILL}${CNT}${CNT_UNIT}`, "i") },
96
+ { kind: "victory-rank", re: new RegExp(`${NODE}${DIAN}${RANKC}${WIN}${CNT}${CNT_UNIT}`, "i") },
97
+ { kind: "count-first", re: new RegExp(`${NODE}${DIAN}${CNT}${CNT_UNIT}${RANKC}${WIN}`, "i") },
98
+ { kind: "node-air-state", re: new RegExp(`${NODE}${DIAN}${AIR}${CNT}${CNT_UNIT}`, "i") }
75
99
  ];
100
+ var DECORATION = /(boss|王点|王點|門神|门神|斬殺|斩杀|斬殺線|斩杀线)/gi;
76
101
  function normalizeNodeLabel(value) {
77
102
  return String(value ?? "").trim().toUpperCase();
78
103
  }
104
+ function toHalfWidth(text) {
105
+ let out = "";
106
+ for (const ch of text) {
107
+ const code = ch.codePointAt(0);
108
+ if (code >= 65281 && code <= 65374) out += String.fromCharCode(code - 65248);
109
+ else if (ch === "\u3000") out += " ";
110
+ else out += ch;
111
+ }
112
+ return out;
113
+ }
114
+ function chineseCountToNumber(count) {
115
+ return CHINESE_DIGIT[count] ?? Number(count);
116
+ }
79
117
  function stripAnnotation(text) {
80
118
  const notes = [];
81
- const cleaned = String(text ?? "").replace(/(([^()]*))|\(([^()]*)\)/g, (_m, a, b) => {
82
- const note = (a ?? b ?? "").trim();
119
+ const cleaned = String(text ?? "").replace(/[((【〔「]([^()()【】〔〕「」]*)[))】〕」]/g, (_m, inner) => {
120
+ const note = String(inner ?? "").trim();
83
121
  if (note !== "") notes.push(note);
84
122
  return "";
85
123
  });
86
124
  return { cleaned: cleaned.trim(), notes };
87
125
  }
88
- function parseSegment(raw) {
89
- const segment = raw.trim();
90
- if (segment === "") return null;
126
+ function airStateOf(word) {
127
+ return AIR_ALIAS[word];
128
+ }
129
+ function parseSegmentBody(segment) {
91
130
  const { cleaned, notes } = stripAnnotation(segment);
92
- const text = cleaned.replace(/\s+/g, "");
93
- if (text === "") {
94
- return { ok: false, raw: segment, reason: "\u53BB\u6389\u62EC\u53F7\u6CE8\u8BB0\u540E\u6CA1\u6709\u6761\u4EF6\u5185\u5BB9" };
95
- }
96
- for (const pattern of PATTERNS) {
97
- const match = pattern.re.exec(text);
98
- if (match == null) continue;
99
- if (pattern.kind === "node-arrival") {
100
- return {
131
+ let text = toHalfWidth(cleaned).replace(DECORATION, "").replace(/[×✕**]/g, "").replace(/\s+/g, "");
132
+ if (text === "") return { matches: null, notes };
133
+ const bossSeen = /boss/i.test(segment) || /boss/i.test(text);
134
+ text = text.replace(/([一两二三四五六七八九十])(?=[次回凸]|$)/g, (m) => String(CHINESE_DIGIT[m]));
135
+ const matches = [];
136
+ let rest = text;
137
+ let guard = 0;
138
+ while (rest.length > 0 && guard < 20) {
139
+ guard += 1;
140
+ let matched = null;
141
+ for (const form2 of FORMS) {
142
+ const m2 = form2.re.exec(rest);
143
+ if (m2 != null && (matched == null || m2.index < matched.m.index)) {
144
+ matched = { form: form2, m: m2 };
145
+ }
146
+ }
147
+ if (matched == null && bossSeen) {
148
+ const bare = new RegExp(`^${NODE}${DIAN}${CNT}${CNT_UNIT}`, "i").exec(rest);
149
+ if (bare != null) {
150
+ matches.push({
151
+ ok: true,
152
+ condition: {
153
+ kind: "victory-rank",
154
+ nodeLabel: normalizeNodeLabel(bare[1]),
155
+ requiredRank: "A",
156
+ requiredCount: chineseCountToNumber(bare[2]),
157
+ notes: [...notes, "boss\u2192\u6309\u51FB\u7834(A\u80DC)\u8BA1"]
158
+ }
159
+ });
160
+ rest = rest.slice(bare[0].length);
161
+ continue;
162
+ }
163
+ break;
164
+ }
165
+ if (matched == null) break;
166
+ const { form, m } = matched;
167
+ const count = chineseCountToNumber(form.kind === "count-first" ? m[2] : m[m.length - 1]);
168
+ if (!Number.isInteger(count) || count <= 0) return { matches: [{ ok: false, reason: "\u6B21\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570" }], notes };
169
+ if (form.kind === "base-air-defense") {
170
+ const airState = airStateOf(m[2]);
171
+ if (airState == null) return { matches: [{ ok: false, reason: `\u672A\u77E5\u5236\u7A7A\u8BCD\u300C${m[2]}\u300D` }], notes };
172
+ matches.push({
173
+ ok: true,
174
+ condition: { kind: "base-air-defense", requiredAirState: airState, requiredCount: count, notes }
175
+ });
176
+ } else if (form.kind === "node-arrival" || form.kind === "reach-after") {
177
+ matches.push({
101
178
  ok: true,
102
- raw: segment,
103
179
  condition: {
104
180
  kind: "node-arrival",
105
- nodeLabel: normalizeNodeLabel(match[1]),
106
- requiredCount: Number(match[3]),
181
+ nodeLabel: normalizeNodeLabel(m[1]),
182
+ requiredCount: count,
107
183
  notes
108
184
  }
109
- };
110
- }
111
- if (pattern.kind === "victory-rank") {
112
- return {
185
+ });
186
+ } else if (form.kind === "kill") {
187
+ matches.push({
113
188
  ok: true,
114
- raw: segment,
115
189
  condition: {
116
190
  kind: "victory-rank",
117
- nodeLabel: normalizeNodeLabel(match[1]),
118
- requiredRank: match[2].toUpperCase(),
119
- requiredCount: Number(match[3]),
191
+ nodeLabel: normalizeNodeLabel(m[1]),
192
+ requiredRank: "A",
193
+ requiredCount: count,
194
+ notes: [...notes, "\u51FB\u7834\u2192\u6309A\u80DC\u8BA1"]
195
+ }
196
+ });
197
+ } else if (form.kind === "victory-rank") {
198
+ matches.push({
199
+ ok: true,
200
+ condition: {
201
+ kind: "victory-rank",
202
+ nodeLabel: normalizeNodeLabel(m[1]),
203
+ requiredRank: m[2].toUpperCase(),
204
+ requiredCount: count,
120
205
  notes
121
206
  }
122
- };
123
- }
124
- if (pattern.kind === "node-air-state") {
125
- return {
207
+ });
208
+ } else if (form.kind === "count-first") {
209
+ matches.push({
210
+ ok: true,
211
+ condition: {
212
+ kind: "victory-rank",
213
+ nodeLabel: normalizeNodeLabel(m[1]),
214
+ requiredRank: m[3].toUpperCase(),
215
+ requiredCount: count,
216
+ notes
217
+ }
218
+ });
219
+ } else {
220
+ const airState = airStateOf(m[2]);
221
+ if (airState == null) return { matches: [{ ok: false, reason: `\u672A\u77E5\u5236\u7A7A\u8BCD\u300C${m[2]}\u300D` }], notes };
222
+ matches.push({
126
223
  ok: true,
127
- raw: segment,
128
224
  condition: {
129
225
  kind: "node-air-state",
130
- nodeLabel: normalizeNodeLabel(match[1]),
131
- requiredAirState: AIR_TEXT_TO_STATE[match[2]],
132
- requiredCount: Number(match[3]),
226
+ nodeLabel: normalizeNodeLabel(m[1]),
227
+ requiredAirState: airState,
228
+ requiredCount: count,
133
229
  notes
134
230
  }
135
- };
231
+ });
136
232
  }
137
- return {
138
- ok: true,
139
- raw: segment,
140
- condition: {
141
- kind: "base-air-defense",
142
- requiredAirState: AIR_TEXT_TO_STATE[match[2]],
143
- requiredCount: Number(match[3]),
144
- notes
145
- }
146
- };
233
+ rest = rest.slice(m.index + m[0].length);
147
234
  }
148
- return { ok: false, raw: segment, reason: "\u672A\u8BC6\u522B\u7684\u683C\u5F0F\uFF08\u793A\u4F8B\uFF1AY2\u70B9S\u80DC1\u6B21 / C2\u70B9\u5230\u8FBE1\u6B21 / P2\u70B9\u7A7A\u4F181\u6B21 / \u5B88\u5BB6\u7A7A\u4F183\u6B21\uFF09" };
235
+ return { matches, notes, bossSeen };
149
236
  }
150
237
  var BUCKET_HEADER = /^(解谜|解密|谜题|削甲|装甲|debuff|gimmick)[::]/;
151
238
  var BUCKET_WORDS = { \u89E3\u8C1C: "gimmick", \u89E3\u5BC6: "gimmick", \u8C1C\u9898: "gimmick", gimmick: "gimmick", \u524A\u7532: "debuff", \u88C5\u7532: "debuff", debuff: "debuff" };
152
- function bucketOf(word) {
153
- return BUCKET_WORDS[word] ?? null;
154
- }
155
239
  function parseGuideText(text, bucketDefault = "debuff") {
156
240
  const report = [];
157
241
  const conditions = [];
@@ -161,27 +245,30 @@ function parseGuideText(text, bucketDefault = "debuff") {
161
245
  let work = segment;
162
246
  const header = BUCKET_HEADER.exec(work.replace(/\s+/g, ""));
163
247
  if (header != null) {
164
- bucket = bucketOf(header[1]) ?? bucket;
248
+ bucket = BUCKET_WORDS[header[1]] ?? bucket;
165
249
  work = work.replace(/^\s*[^::]*[::]\s*/, "");
166
250
  if (work === "") {
167
251
  report.push({ ok: true, raw: segment, header: true, bucket });
168
252
  continue;
169
253
  }
170
254
  }
171
- const parsed = parseSegment(work);
172
- if (parsed == null) continue;
173
- if (!parsed.ok) {
174
- report.push(parsed);
255
+ const { matches } = parseSegmentBody(work);
256
+ if (matches == null) continue;
257
+ if (matches.length === 0 || matches.some((match) => !match.ok)) {
258
+ const failure = matches.find((match) => !match.ok);
259
+ report.push({
260
+ ok: false,
261
+ raw: segment,
262
+ reason: failure?.reason ?? "\u672A\u8BC6\u522B\u7684\u5199\u6CD5\uFF08\u793A\u4F8B\uFF1AY2\u70B9S\u80DC1\u6B21 / P3\u70B9A2\u6B21 / \u5B88\u5BB6\u7A7A\u4F183\u6B21\uFF09"
263
+ });
175
264
  continue;
176
265
  }
177
- if (!Number.isInteger(parsed.condition.requiredCount) || parsed.condition.requiredCount <= 0) {
178
- report.push({ ok: false, raw: parsed.raw, reason: "\u6B21\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570" });
179
- continue;
266
+ for (const match of matches) {
267
+ match.condition.bucket = bucket;
268
+ match.condition.raw = segment;
269
+ conditions.push(match.condition);
270
+ report.push({ ok: true, raw: segment, condition: match.condition, multiple: matches.length > 1 });
180
271
  }
181
- parsed.condition.bucket = bucket;
182
- parsed.condition.raw = parsed.raw;
183
- conditions.push(parsed.condition);
184
- report.push({ ok: true, raw: parsed.raw, condition: parsed.condition });
185
272
  }
186
273
  return { conditions, report };
187
274
  }
@@ -1314,9 +1401,9 @@ function createLedgerView(controller2) {
1314
1401
  rows: 4,
1315
1402
  value: guideText,
1316
1403
  onChange: (event) => setGuideText(event.currentTarget.value),
1317
- placeholder: "\u652F\u6301\uFF1AY2\u70B9S\u80DC1\u6B21 / L2\u70B9A\u80DC1\u6B21 / P3\u70B9A2\u6B21 / C2\u70B9\u5230\u8FBE1\u6B21 / P2\u70B9\u7A7A\u4F181\u6B21 / \u5B88\u5BB6\u7A7A\u4F183\u6B21\n\u53EF\u5E26\u6CE8\u8BB0\uFF1AG\u70B9\uFF08P1boss\uFF09S\u80DC1\u6B21\n\u53EF\u7528\u5206\u7EC4\u884C\uFF1A\u89E3\u8C1C\uFF1A \u6216 \u524A\u7532\uFF1A"
1404
+ placeholder: "\u4F8B\u5982\uFF1AY2\u70B9S\u80DC1\u6B21 / L2\u70B9A\u80DC1\u6B21 / P3\u70B9A2\u6B21 / C2\u70B9\u5230\u8FBE1\u6B21 / P2\u70B9\u7A7A\u4F181\u6B21 / \u5B88\u5BB6\u7A7A\u4F183\u6B21\n\u4E5F\u8BA4\uFF1AP1boss\u4E00\u6B21 / \u5B88\u5BB6\u7A7A\u4F18\u4E24\u6B21 / G\u70B92\u6B21S\u80DC / S\u70B9\u51FB\u78342\u56DE / \uFF30\uFF12\u70B9\u7A7A\u512A\uFF11\u6B21\n\u53EF\u5E26\u6CE8\u8BB0\u4E0E\u5206\u7EC4\u884C\uFF1AG\u70B9\uFF08P1boss\uFF09S\u80DC1\u6B21\u3001\u89E3\u8C1C\uFF1A \u6216 \u524A\u7532\uFF1A"
1318
1405
  }
1319
- ), /* @__PURE__ */ import_react.default.createElement("div", { style: styles.actions }, /* @__PURE__ */ import_react.default.createElement(import_core.HTMLSelect, { value: bucket, onChange: (event) => setBucket(event.currentTarget.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "debuff" }, "\u5BFC\u5165\u4E3A\u524A\u7532\u6761\u4EF6"), /* @__PURE__ */ import_react.default.createElement("option", { value: "gimmick" }, "\u5BFC\u5165\u4E3A\u89E3\u8C1C\u6761\u4EF6")), /* @__PURE__ */ import_react.default.createElement(import_core.Button, { intent: "primary", onClick: importGuide, disabled: mapRef == null }, "\u5BFC\u5165\u5230\u5F53\u524D\u6D77\u57DF", mapRef != null ? ` ${mapLabel(mapRef)}` : ""), mapRef == null && /* @__PURE__ */ import_react.default.createElement("span", { style: styles.muted }, "\u7B49\u5F85\u6D77\u57DF\u8BC6\u522B\u2026")), report != null && /* @__PURE__ */ import_react.default.createElement("div", { style: { marginTop: 8 } }, report.error != null ? /* @__PURE__ */ import_react.default.createElement(import_core.Callout, { intent: "danger" }, report.error) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { style: { marginBottom: 4 } }, /* @__PURE__ */ import_react.default.createElement("strong", null, report.mapLabel), "\uFF1A\u8BC6\u522B ", report.importedCount, " \u6761\u3002"), report.report.map((line, index) => /* @__PURE__ */ import_react.default.createElement("div", { key: index, style: { ...styles.reportLine, color: line.ok ? void 0 : "#cd4a3e" } }, line.header ? "\u2699" : line.ok ? "\u2714" : "\u2718", " ", line.raw, line.header ? ` \u2192 \u5206\u7EC4\uFF1A${BUCKETS[line.bucket]}` : "", !line.ok && line.reason != null ? ` \u2014 ${line.reason}` : ""))))), mapState != null && closedRounds.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_core.Card, { style: styles.section, elevation: import_core.Elevation.ZERO }, /* @__PURE__ */ import_react.default.createElement("h3", { style: { marginTop: 0 } }, mapLabel(mapState), " \xB7 \u5386\u53F2\u8F6E\u6B21"), closedRounds.map((round) => /* @__PURE__ */ import_react.default.createElement(RoundSummary, { key: round.id, round, mapState }))), otherMaps.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_core.Card, { style: styles.section, elevation: import_core.Elevation.ZERO }, /* @__PURE__ */ import_react.default.createElement("h3", { style: { marginTop: 0 } }, "\u5176\u4ED6\u6D77\u57DF"), otherMaps.map((ms) => {
1406
+ ), /* @__PURE__ */ import_react.default.createElement("div", { style: styles.actions }, /* @__PURE__ */ import_react.default.createElement(import_core.HTMLSelect, { value: bucket, onChange: (event) => setBucket(event.currentTarget.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "debuff" }, "\u5BFC\u5165\u4E3A\u524A\u7532\u6761\u4EF6"), /* @__PURE__ */ import_react.default.createElement("option", { value: "gimmick" }, "\u5BFC\u5165\u4E3A\u89E3\u8C1C\u6761\u4EF6")), /* @__PURE__ */ import_react.default.createElement(import_core.Button, { intent: "primary", onClick: importGuide, disabled: mapRef == null }, "\u5BFC\u5165\u5230\u5F53\u524D\u6D77\u57DF", mapRef != null ? ` ${mapLabel(mapRef)}` : ""), mapRef == null && /* @__PURE__ */ import_react.default.createElement("span", { style: styles.muted }, "\u7B49\u5F85\u6D77\u57DF\u8BC6\u522B\u2026")), report != null && /* @__PURE__ */ import_react.default.createElement("div", { style: { marginTop: 8 } }, report.error != null ? /* @__PURE__ */ import_react.default.createElement(import_core.Callout, { intent: "danger" }, report.error) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { style: { marginBottom: 4 } }, /* @__PURE__ */ import_react.default.createElement("strong", null, report.mapLabel), "\uFF1A\u8BC6\u522B ", report.importedCount, " \u6761\u3002"), report.report.map((line, index) => /* @__PURE__ */ import_react.default.createElement("div", { key: index, style: { ...styles.reportLine, color: line.ok ? void 0 : "#cd4a3e" } }, line.header ? "\u2699" : line.ok ? "\u2714" : "\u2718", " ", line.raw, line.header ? ` \u2192 \u5206\u7EC4\uFF1A${BUCKETS[line.bucket]}` : "", !line.header && line.ok && line.condition != null ? ` \u2192 ${conditionLabel(line.condition)}` : "", !line.ok && line.reason != null ? ` \u2014 ${line.reason}` : ""))))), mapState != null && closedRounds.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_core.Card, { style: styles.section, elevation: import_core.Elevation.ZERO }, /* @__PURE__ */ import_react.default.createElement("h3", { style: { marginTop: 0 } }, mapLabel(mapState), " \xB7 \u5386\u53F2\u8F6E\u6B21"), closedRounds.map((round) => /* @__PURE__ */ import_react.default.createElement(RoundSummary, { key: round.id, round, mapState }))), otherMaps.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_core.Card, { style: styles.section, elevation: import_core.Elevation.ZERO }, /* @__PURE__ */ import_react.default.createElement("h3", { style: { marginTop: 0 } }, "\u5176\u4ED6\u6D77\u57DF"), otherMaps.map((ms) => {
1320
1407
  const legacy = ms.rounds.find((round) => round.bucket === "legacy");
1321
1408
  const window2 = controller2.legacyWindowFor(ms.mapId);
1322
1409
  return /* @__PURE__ */ import_react.default.createElement("div", { key: ms.mapId, style: { padding: "8px 0", borderBottom: "1px solid rgba(128,128,128,.15)" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, /* @__PURE__ */ import_react.default.createElement(import_core.Tag, { minimal: true }, mapLabel(ms)), /* @__PURE__ */ import_react.default.createElement("span", { style: styles.muted }, "\u6761\u4EF6 ", ms.conditions.length, " \xB7 \u8F6E\u6B21 ", ms.rounds.length, ms.cleared ? " \xB7 \u5DF2\u901A\u5173" : "", legacy != null ? " \xB7 \u542B\u65E7\u6570\u636E\u5F52\u6863" : ""), legacy != null && window2 != null && /* @__PURE__ */ import_react.default.createElement(import_core.Button, { small: true, onClick: () => controller2.replayLegacy(ms.mapId, window2.from, window2.to) }, "\u56DE\u653E\u5386\u53F2\u524A\u7532\u4E00\u8F6E")));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-gimmick-tracker",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "解密·削甲进度:自动对账活动海域解密/削甲条件进度(轮次账本,阶段自动旋转,无人工确认卡)",
5
5
  "main": "index.js",
6
6
  "files": [