poi-plugin-kai-planner 1.0.5 → 1.0.7

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/README.md CHANGED
@@ -1,26 +1,12 @@
1
1
  # poi-plugin-kai-planner
2
2
 
3
- 用于 POI 的改修规划插件,包含“每日改修”和“改修心愿清单”两条主链路。
4
-
5
- ## 当前发布机制(2026-03)
6
-
7
- - 插件代码仓库:本仓库(私有)
8
- - 玩家安装方式:通过 npm 安装 POI 插件包
9
- - 远端静态数据:独立公开仓库 `PlannerRemoteRawData`
10
- - 调试页开关:`package.json -> poiPlugin.enableDebug`,发布建议关闭
11
- - 发布包控制:`package.json.files` + `.npmignore` + `npm run release:check`
12
-
13
- ## 功能概览
3
+ ## 本地数据
14
4
 
15
- - 每日改修:按 JST(GMT+9)展示当天可改修/可进化项目、秘书舰与基础消耗
16
- - 改修心愿清单:创建计划、跟踪实时进度、查看今日下一步、缺口与起点绑定状态
17
- - 心愿清单表格已拆分为两个 tab:
18
- - `改修未完成`:默认 tab,只展示仍有剩余步骤的计划
19
- - `改修已完成`:只展示 `remainingSteps.length === 0` 的计划
20
- - 心愿清单支持行内编辑:优先级、目标星级、起点重绑、展开查看实时缺口
21
- - Debug:可选加载,不影响 Daily/Wishlist 主流程
5
+ - 静态原始数据:`src/data/static/*.json`
6
+ - 运行时静态缓存:`runtime_data/static_data/cache/*.json`
7
+ - 运行时缓存元数据:`runtime_data/static_data/meta.json`
22
8
 
23
- ## 本地数据
9
+ ## 计划存储
24
10
 
25
11
  - 用户计划优先写入:
26
12
  - 若 Electron 可提供 `userData` 目录,则写入 `<userData>/poi-plugin-kai-planner/userPlans/plans.v1.json`
@@ -52,3 +38,6 @@
52
38
  - Daily 与 Wishlist 表格中的装备名称前都会显示装备类型 icon
53
39
  - Wishlist 主表中的优先级改为标签样式展示(P0-P5 固定颜色)
54
40
  - Wishlist 展开明细中的“当前持有”计数会去除改修星数大于 0 的装备以及计划起点装备
41
+ - 页面顶部 tab、上一次数据源更新、检查更新与更新提示统一使用 `14px` 字号
42
+ - Daily 与 Wishlist 主表中的装备类型 icon 统一缩小到 `16x16`
43
+ - Daily 与 Wishlist 主表的折叠箭头统一为:未展开 `▶`、已展开 `▼`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-kai-planner",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "main": "index.js",
5
5
  "author": "aulu",
6
6
  "contributors": ["aulu"],
@@ -1,211 +1,211 @@
1
- // src/app/KaiPlannerApp.js
2
- const React = require("react");
3
- const { DailyTab } = require("./tabs/daily/DailyTab");
4
- const { checkAndUpdateData } = require("../services/static/version/dataUpdateManager");
5
- const { readMeta } = require("../services/static/version/versionStore");
6
-
7
- const placeholderStyle = {
8
- padding: 12,
9
- background: "#111827",
10
- color: "#e5e7eb",
11
- borderRadius: 8,
12
- };
13
-
14
- function pad2(n) {
15
- return String(n).padStart(2, "0");
16
- }
17
-
18
- function formatSimpleTime(iso) {
19
- if (!iso) return "-";
20
- const d = new Date(iso);
21
- if (!Number.isFinite(d.getTime())) return "-";
22
- return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(
23
- d.getMinutes()
24
- )}:${pad2(d.getSeconds())}`;
25
- }
26
-
27
- function readBundledManifestUpdatedAt() {
28
- try {
29
- // eslint-disable-next-line global-require
30
- const m = require("../data/static/data_manifest.json");
31
- if (m && m.updated_at) return String(m.updated_at);
32
- } catch {}
33
- return null;
34
- }
35
-
36
- function readEnableDebugFlag() {
37
- try {
38
- // eslint-disable-next-line global-require
39
- const pkg = require("../../package.json");
40
- const poiPlugin = pkg && pkg.poiPlugin ? pkg.poiPlugin : {};
41
- if (poiPlugin.enableDebug == null) return true;
42
- return !!poiPlugin.enableDebug;
43
- } catch {
44
- return true;
45
- }
46
- }
47
-
48
- class KaiPlannerApp extends React.Component {
49
- constructor(props) {
50
- super(props);
51
- this.state = {
52
- tab: "daily", // daily | wishlist | debug
53
- wishlistLoadError: null,
54
- debugLoadError: null,
55
- checkingUpdate: false,
56
- lastUpdateAtText: "-",
57
- updateTip: "",
58
- };
59
- this._WishlistTab = null;
60
- this._DebugTab = null;
61
- this.enableDebug = readEnableDebugFlag();
62
- this.handleCheckUpdate = this.handleCheckUpdate.bind(this);
63
- }
64
-
65
- componentDidMount() {
66
- this.syncUpdateMeta();
67
- this.handleCheckUpdate(false);
68
- }
69
-
70
- syncUpdateMeta() {
71
- try {
72
- const meta = readMeta();
73
- const fallback = readBundledManifestUpdatedAt();
74
- this.setState({
75
- lastUpdateAtText: formatSimpleTime((meta && meta.lastUpdateAt) || fallback),
76
- });
77
- } catch {}
78
- }
79
-
80
- async handleCheckUpdate(force) {
81
- if (this.state.checkingUpdate) return;
82
- this.setState({ checkingUpdate: true, updateTip: force ? "正在检查..." : "" });
83
- try {
84
- const res = await checkAndUpdateData({ force: !!force });
85
- this.syncUpdateMeta();
86
- if (force) {
87
- if (res && res.ok && res.updated) this.setState({ updateTip: `已更新到数据版本 ${res.remoteVersion}` });
88
- else if (res && res.ok && !res.updated) this.setState({ updateTip: "当前已是最新数据" });
89
- else if (res && res.reason === "data_source_not_configured") this.setState({ updateTip: "未配置远端数据源" });
90
- else this.setState({ updateTip: `检查失败${res && res.error ? `:${res.error}` : ""}` });
91
- }
92
- } catch (e) {
93
- if (force) this.setState({ updateTip: `检查失败:${String(e && (e.stack || e))}` });
94
- } finally {
95
- this.setState({ checkingUpdate: false });
96
- }
97
- }
98
-
99
- render() {
100
- const tab = !this.enableDebug && this.state.tab === "debug" ? "daily" : this.state.tab;
101
-
102
- const tabButtonStyle = (key) => ({
103
- padding: "6px 10px",
104
- borderRadius: 8,
105
- cursor: "pointer",
106
- border: "1px solid #374151",
107
- background: tab === key ? "#1f2937" : "transparent",
108
- color: "#e5e7eb",
109
- marginRight: 8,
110
- fontSize: 12,
111
- });
112
-
113
- let content = null;
114
- if (tab === "daily") {
115
- content = React.createElement(DailyTab, {
116
- envWindow: this.props.envWindow,
117
- envCtx: this.props.envCtx,
118
- });
119
- } else if (tab === "wishlist") {
120
- if (!this._WishlistTab && !this.state.wishlistLoadError) {
121
- try {
122
- // eslint-disable-next-line global-require
123
- this._WishlistTab = require("./tabs/wishlist/WishlistTab");
124
- } catch (e) {
125
- this.setState({ wishlistLoadError: String(e && e.stack ? e.stack : e) });
126
- }
127
- }
128
-
129
- if (this.state.wishlistLoadError) {
130
- content = React.createElement(
131
- "div",
132
- { style: { ...placeholderStyle, whiteSpace: "pre-wrap" } },
133
- "【改修心愿清单】模块加载失败(已隔离,不影响 Daily/Debug)\n\n错误信息:\n" + this.state.wishlistLoadError
134
- );
135
- } else if (this._WishlistTab) {
136
- content = React.createElement(this._WishlistTab, {
137
- envWindow: this.props.envWindow,
138
- envCtx: this.props.envCtx,
139
- });
140
- } else {
141
- content = React.createElement("div", { style: placeholderStyle }, "【改修心愿清单】加载中...");
142
- }
143
- } else if (tab === "debug") {
144
- if (!this._DebugTab && !this.state.debugLoadError) {
145
- try {
146
- // eslint-disable-next-line global-require
147
- this._DebugTab = require("./tabs/debug/DebugTab");
148
- } catch (e) {
149
- this.setState({ debugLoadError: String(e && e.stack ? e.stack : e) });
150
- }
151
- }
152
-
153
- if (this.state.debugLoadError) {
154
- content = React.createElement(
155
- "div",
156
- { style: { ...placeholderStyle, whiteSpace: "pre-wrap" } },
157
- "【Debug】模块加载失败(已隔离,不影响 Daily/Wishlist)\n\n错误信息:\n" + this.state.debugLoadError
158
- );
159
- } else if (this._DebugTab) {
160
- content = React.createElement(this._DebugTab, {
161
- envWindow: this.props.envWindow,
162
- envCtx: this.props.envCtx,
163
- });
164
- } else {
165
- content = React.createElement("div", { style: placeholderStyle }, "【Debug】加载中...");
166
- }
167
- } else {
168
- content = React.createElement("div", { style: placeholderStyle }, "未知标签页");
169
- }
170
-
171
- return React.createElement(
172
- "div",
173
- { style: { padding: 12 } },
174
- React.createElement(
175
- "div",
176
- { style: { marginBottom: 12, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 } },
177
- React.createElement(
178
- "div",
179
- null,
180
- React.createElement("button", { style: tabButtonStyle("daily"), onClick: () => this.setState({ tab: "daily" }) }, "每日改修"),
181
- React.createElement(
182
- "button",
183
- { style: tabButtonStyle("wishlist"), onClick: () => this.setState({ tab: "wishlist" }) },
184
- "改修心愿清单"
185
- ),
186
- this.enableDebug
187
- ? React.createElement(
188
- "button",
189
- { style: tabButtonStyle("debug"), onClick: () => this.setState({ tab: "debug" }) },
190
- "Debug"
191
- )
192
- : null
193
- ),
194
- React.createElement(
195
- "div",
196
- { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", justifyContent: "flex-end" } },
197
- React.createElement("span", { style: { fontSize: 12, opacity: 0.8 } }, `上一次数据源更新:${this.state.lastUpdateAtText}`),
198
- React.createElement(
199
- "button",
200
- { style: tabButtonStyle("__check__"), onClick: () => this.handleCheckUpdate(true), disabled: this.state.checkingUpdate },
201
- this.state.checkingUpdate ? "检查中..." : "检查更新"
202
- ),
203
- this.state.updateTip ? React.createElement("span", { style: { fontSize: 12, opacity: 0.85 } }, this.state.updateTip) : null
204
- )
205
- ),
206
- content
207
- );
208
- }
209
- }
210
-
211
- module.exports = { KaiPlannerApp };
1
+ // src/app/KaiPlannerApp.js
2
+ const React = require("react");
3
+ const { DailyTab } = require("./tabs/daily/DailyTab");
4
+ const { checkAndUpdateData } = require("../services/static/version/dataUpdateManager");
5
+ const { readMeta } = require("../services/static/version/versionStore");
6
+
7
+ const placeholderStyle = {
8
+ padding: 12,
9
+ background: "#111827",
10
+ color: "#e5e7eb",
11
+ borderRadius: 8,
12
+ };
13
+
14
+ function pad2(n) {
15
+ return String(n).padStart(2, "0");
16
+ }
17
+
18
+ function formatSimpleTime(iso) {
19
+ if (!iso) return "-";
20
+ const d = new Date(iso);
21
+ if (!Number.isFinite(d.getTime())) return "-";
22
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(
23
+ d.getMinutes()
24
+ )}:${pad2(d.getSeconds())}`;
25
+ }
26
+
27
+ function readBundledManifestUpdatedAt() {
28
+ try {
29
+ // eslint-disable-next-line global-require
30
+ const m = require("../data/static/data_manifest.json");
31
+ if (m && m.updated_at) return String(m.updated_at);
32
+ } catch {}
33
+ return null;
34
+ }
35
+
36
+ function readEnableDebugFlag() {
37
+ try {
38
+ // eslint-disable-next-line global-require
39
+ const pkg = require("../../package.json");
40
+ const poiPlugin = pkg && pkg.poiPlugin ? pkg.poiPlugin : {};
41
+ if (poiPlugin.enableDebug == null) return true;
42
+ return !!poiPlugin.enableDebug;
43
+ } catch {
44
+ return true;
45
+ }
46
+ }
47
+
48
+ class KaiPlannerApp extends React.Component {
49
+ constructor(props) {
50
+ super(props);
51
+ this.state = {
52
+ tab: "daily", // daily | wishlist | debug
53
+ wishlistLoadError: null,
54
+ debugLoadError: null,
55
+ checkingUpdate: false,
56
+ lastUpdateAtText: "-",
57
+ updateTip: "",
58
+ };
59
+ this._WishlistTab = null;
60
+ this._DebugTab = null;
61
+ this.enableDebug = readEnableDebugFlag();
62
+ this.handleCheckUpdate = this.handleCheckUpdate.bind(this);
63
+ }
64
+
65
+ componentDidMount() {
66
+ this.syncUpdateMeta();
67
+ this.handleCheckUpdate(false);
68
+ }
69
+
70
+ syncUpdateMeta() {
71
+ try {
72
+ const meta = readMeta();
73
+ const fallback = readBundledManifestUpdatedAt();
74
+ this.setState({
75
+ lastUpdateAtText: formatSimpleTime((meta && meta.lastUpdateAt) || fallback),
76
+ });
77
+ } catch {}
78
+ }
79
+
80
+ async handleCheckUpdate(force) {
81
+ if (this.state.checkingUpdate) return;
82
+ this.setState({ checkingUpdate: true, updateTip: force ? "正在检查..." : "" });
83
+ try {
84
+ const res = await checkAndUpdateData({ force: !!force });
85
+ this.syncUpdateMeta();
86
+ if (force) {
87
+ if (res && res.ok && res.updated) this.setState({ updateTip: `已更新到数据版本 ${res.remoteVersion}` });
88
+ else if (res && res.ok && !res.updated) this.setState({ updateTip: "当前已是最新数据" });
89
+ else if (res && res.reason === "data_source_not_configured") this.setState({ updateTip: "未配置远端数据源" });
90
+ else this.setState({ updateTip: `检查失败${res && res.error ? `:${res.error}` : ""}` });
91
+ }
92
+ } catch (e) {
93
+ if (force) this.setState({ updateTip: `检查失败:${String(e && (e.stack || e))}` });
94
+ } finally {
95
+ this.setState({ checkingUpdate: false });
96
+ }
97
+ }
98
+
99
+ render() {
100
+ const tab = !this.enableDebug && this.state.tab === "debug" ? "daily" : this.state.tab;
101
+
102
+ const tabButtonStyle = (key) => ({
103
+ padding: "6px 10px",
104
+ borderRadius: 8,
105
+ cursor: "pointer",
106
+ border: "1px solid #374151",
107
+ background: tab === key ? "#1f2937" : "transparent",
108
+ color: "#e5e7eb",
109
+ marginRight: 8,
110
+ fontSize: 14,
111
+ });
112
+
113
+ let content = null;
114
+ if (tab === "daily") {
115
+ content = React.createElement(DailyTab, {
116
+ envWindow: this.props.envWindow,
117
+ envCtx: this.props.envCtx,
118
+ });
119
+ } else if (tab === "wishlist") {
120
+ if (!this._WishlistTab && !this.state.wishlistLoadError) {
121
+ try {
122
+ // eslint-disable-next-line global-require
123
+ this._WishlistTab = require("./tabs/wishlist/WishlistTab");
124
+ } catch (e) {
125
+ this.setState({ wishlistLoadError: String(e && e.stack ? e.stack : e) });
126
+ }
127
+ }
128
+
129
+ if (this.state.wishlistLoadError) {
130
+ content = React.createElement(
131
+ "div",
132
+ { style: { ...placeholderStyle, whiteSpace: "pre-wrap" } },
133
+ "【改修心愿清单】模块加载失败(已隔离,不影响 Daily/Debug)\n\n错误信息:\n" + this.state.wishlistLoadError
134
+ );
135
+ } else if (this._WishlistTab) {
136
+ content = React.createElement(this._WishlistTab, {
137
+ envWindow: this.props.envWindow,
138
+ envCtx: this.props.envCtx,
139
+ });
140
+ } else {
141
+ content = React.createElement("div", { style: placeholderStyle }, "【改修心愿清单】加载中...");
142
+ }
143
+ } else if (tab === "debug") {
144
+ if (!this._DebugTab && !this.state.debugLoadError) {
145
+ try {
146
+ // eslint-disable-next-line global-require
147
+ this._DebugTab = require("./tabs/debug/DebugTab");
148
+ } catch (e) {
149
+ this.setState({ debugLoadError: String(e && e.stack ? e.stack : e) });
150
+ }
151
+ }
152
+
153
+ if (this.state.debugLoadError) {
154
+ content = React.createElement(
155
+ "div",
156
+ { style: { ...placeholderStyle, whiteSpace: "pre-wrap" } },
157
+ "【Debug】模块加载失败(已隔离,不影响 Daily/Wishlist)\n\n错误信息:\n" + this.state.debugLoadError
158
+ );
159
+ } else if (this._DebugTab) {
160
+ content = React.createElement(this._DebugTab, {
161
+ envWindow: this.props.envWindow,
162
+ envCtx: this.props.envCtx,
163
+ });
164
+ } else {
165
+ content = React.createElement("div", { style: placeholderStyle }, "【Debug】加载中...");
166
+ }
167
+ } else {
168
+ content = React.createElement("div", { style: placeholderStyle }, "未知标签页");
169
+ }
170
+
171
+ return React.createElement(
172
+ "div",
173
+ { style: { padding: 12 } },
174
+ React.createElement(
175
+ "div",
176
+ { style: { marginBottom: 12, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 } },
177
+ React.createElement(
178
+ "div",
179
+ null,
180
+ React.createElement("button", { style: tabButtonStyle("daily"), onClick: () => this.setState({ tab: "daily" }) }, "每日改修"),
181
+ React.createElement(
182
+ "button",
183
+ { style: tabButtonStyle("wishlist"), onClick: () => this.setState({ tab: "wishlist" }) },
184
+ "改修心愿清单"
185
+ ),
186
+ this.enableDebug
187
+ ? React.createElement(
188
+ "button",
189
+ { style: tabButtonStyle("debug"), onClick: () => this.setState({ tab: "debug" }) },
190
+ "Debug"
191
+ )
192
+ : null
193
+ ),
194
+ React.createElement(
195
+ "div",
196
+ { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", justifyContent: "flex-end" } },
197
+ React.createElement("span", { style: { fontSize: 14, opacity: 0.8 } }, `上一次数据源更新:${this.state.lastUpdateAtText}`),
198
+ React.createElement(
199
+ "button",
200
+ { style: tabButtonStyle("__check__"), onClick: () => this.handleCheckUpdate(true), disabled: this.state.checkingUpdate },
201
+ this.state.checkingUpdate ? "检查中..." : "检查更新"
202
+ ),
203
+ this.state.updateTip ? React.createElement("span", { style: { fontSize: 14, opacity: 0.85 } }, this.state.updateTip) : null
204
+ )
205
+ ),
206
+ content
207
+ );
208
+ }
209
+ }
210
+
211
+ module.exports = { KaiPlannerApp };