poi-plugin-kai-planner 1.0.0

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 (54) hide show
  1. package/README.md +48 -0
  2. package/index.js +35 -0
  3. package/package.json +25 -0
  4. package/src/app/KaiPlannerApp.js +211 -0
  5. package/src/app/tabs/daily/DailyTab.js +439 -0
  6. package/src/app/tabs/debug/DebugTab.js +554 -0
  7. package/src/app/tabs/wishlist/CreatePlanForm.js +185 -0
  8. package/src/app/tabs/wishlist/WishlistTab.js +704 -0
  9. package/src/app/tabs/wishlist/components/MouseComboBox.js +185 -0
  10. package/src/app/tabs/wishlist/components/WishlistExpandedDetail.js +170 -0
  11. package/src/app/tabs/wishlist/components/WishlistTable.js +253 -0
  12. package/src/core/poi/secretaryName.js +64 -0
  13. package/src/data/indexes/buildArrangementIndex.js +103 -0
  14. package/src/data/loaders/loadStaticData.js +182 -0
  15. package/src/data/static/data_manifest.json +15 -0
  16. package/src/data/static/equip_base_cost.json +5000 -0
  17. package/src/data/static/equipment_upgrade_path.json +3555 -0
  18. package/src/data/static/improvement_arrangement.json +15677 -0
  19. package/src/data/static/improvement_consume_item.json +8933 -0
  20. package/src/data/static/improvement_consume_step.json +9284 -0
  21. package/src/data/static/improvement_upgrade_cost.json +4766 -0
  22. package/src/data/static/improvement_upgrade_target.json +2641 -0
  23. package/src/data/static/material.json +90 -0
  24. package/src/services/common/secretaryDisplay.js +42 -0
  25. package/src/services/daily/buildDailyViewModel.js +402 -0
  26. package/src/services/planner/buildUpgradePath.js +79 -0
  27. package/src/services/planner/calcImproveSteps.js +104 -0
  28. package/src/services/planner/calcRemainingPlan.js +169 -0
  29. package/src/services/planner/calcRoutePlan.js +85 -0
  30. package/src/services/planner/calcUpgradeStep.js +85 -0
  31. package/src/services/planner/detectCurrentPosition.js +57 -0
  32. package/src/services/planner/summarizeShortage.js +76 -0
  33. package/src/services/player/countPlayerEquipByMasterId.js +27 -0
  34. package/src/services/player/getEquipOwnerShip.js +66 -0
  35. package/src/services/player/getPlayerData.js +14 -0
  36. package/src/services/player/getPlayerItemCountByUseitemId.js +45 -0
  37. package/src/services/player/getReduxStateFromEnvWindow.js +12 -0
  38. package/src/services/player/resolveMaterialKeyToUseitemId.js +54 -0
  39. package/src/services/static/indexes/buildConsumeIndexes.js +28 -0
  40. package/src/services/static/indexes/buildPathIndexes.js +29 -0
  41. package/src/services/static/indexes/buildUpgradeIndexes.js +57 -0
  42. package/src/services/static/version/dataSourceConfig.js +25 -0
  43. package/src/services/static/version/dataUpdateManager.js +176 -0
  44. package/src/services/static/version/versionStore.js +205 -0
  45. package/src/services/utils/toInt.js +11 -0
  46. package/src/services/utils/tokyoTime.js +58 -0
  47. package/src/services/wishlist/buildWishlistViewModel.js +56 -0
  48. package/src/services/wishlist/dropdownInteraction.js +30 -0
  49. package/src/services/wishlist/wishlistActions.js +485 -0
  50. package/src/storage/userPlans/fileStore.js +106 -0
  51. package/src/storage/userPlans/localStorageStore.js +50 -0
  52. package/src/storage/userPlans/migrate.js +60 -0
  53. package/src/storage/userPlans/planStore.js +107 -0
  54. package/src/storage/userPlans/storeAdapter.js +77 -0
@@ -0,0 +1,103 @@
1
+ /* src/data/indexes/buildArrangementIndex.js */
2
+ /*
3
+ Build arrangement index from improvement_arrangement rows.
4
+
5
+ Target index shape:
6
+ index[equipId][weekdayKey][routeKindKey] -> Array<row>
7
+
8
+ - weekdayKey: "monday" | ... | "sunday"
9
+ - routeKindKey: "__NULL__" for null/empty route_kind, otherwise exact string
10
+ */
11
+
12
+ const WEEKDAY_KEYS = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
13
+ const NULL_ROUTE = "__NULL__";
14
+
15
+ function normRouteKind(route_kind) {
16
+ if (route_kind === null || route_kind === undefined) return NULL_ROUTE;
17
+ const s = String(route_kind).trim();
18
+ return s === "" ? NULL_ROUTE : s;
19
+ }
20
+
21
+ function ensure(obj, k, initVal) {
22
+ if (!obj[k]) obj[k] = initVal;
23
+ return obj[k];
24
+ }
25
+
26
+ /**
27
+ * @param {Array} rows - improvement_arrangement (already filtered is_deleted=false in loader)
28
+ * @returns {{
29
+ * index: Object,
30
+ * stats: {
31
+ * totalRows: number,
32
+ * equipCount: number,
33
+ * routeKinds: string[],
34
+ * byWeekday: Record<string, number>,
35
+ * }
36
+ * }}
37
+ */
38
+ function buildArrangementIndex(rows) {
39
+ const index = {};
40
+ const byWeekday = {};
41
+ WEEKDAY_KEYS.forEach((k) => (byWeekday[k] = 0));
42
+
43
+ const routeKindSet = new Set();
44
+ let totalRows = 0;
45
+
46
+ for (const r of rows || []) {
47
+ if (!r) continue;
48
+
49
+ const equipId = Number(r.equipment_id);
50
+ if (!Number.isFinite(equipId)) continue;
51
+
52
+ const routeKey = normRouteKind(r.route_kind);
53
+ routeKindSet.add(routeKey);
54
+ totalRows++;
55
+
56
+ const equipBucket = ensure(index, String(equipId), {});
57
+
58
+ for (const wk of WEEKDAY_KEYS) {
59
+ // 只把该 weekday=true 的记录放入索引
60
+ if (!r[wk]) continue;
61
+
62
+ byWeekday[wk]++;
63
+
64
+ const weekdayBucket = ensure(equipBucket, wk, {});
65
+ const routeBucket = ensure(weekdayBucket, routeKey, []);
66
+ routeBucket.push(r);
67
+ }
68
+ }
69
+
70
+ return {
71
+ index,
72
+ stats: {
73
+ totalRows,
74
+ equipCount: Object.keys(index).length,
75
+ routeKinds: Array.from(routeKindSet).sort(),
76
+ byWeekday,
77
+ },
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Query helper: get rows for equipId + weekdayKey + route_kind
83
+ * - route_kind null => match NULL_ROUTE only
84
+ * @param {Object} index
85
+ * @param {number} equipId
86
+ * @param {string} weekdayKey
87
+ * @param {string|null} route_kind
88
+ */
89
+ function queryArrangement(index, equipId, weekdayKey, route_kind) {
90
+ const equipBucket = index && index[String(equipId)];
91
+ if (!equipBucket) return [];
92
+ const weekdayBucket = equipBucket[weekdayKey];
93
+ if (!weekdayBucket) return [];
94
+ const routeKey = normRouteKind(route_kind);
95
+ return weekdayBucket[routeKey] || [];
96
+ }
97
+
98
+ module.exports = {
99
+ WEEKDAY_KEYS,
100
+ NULL_ROUTE,
101
+ buildArrangementIndex,
102
+ queryArrangement,
103
+ };
@@ -0,0 +1,182 @@
1
+ /* src/data/loaders/loadStaticData.js */
2
+ /* Load static JSON tables from /static */
3
+
4
+ function loadJson(name) {
5
+ // During local dev, static lives at src/data/static.
6
+ // Some bundle layouts may place static at projectRoot/static.
7
+ const candidates = [`../static/${name}`, `../../../static/${name}`];
8
+
9
+ // Prefer runtime cached data (downloaded updates), then bundled static files.
10
+ try {
11
+ // eslint-disable-next-line global-require
12
+ const { readCachedJson } = require("../../services/static/version/versionStore");
13
+ const cached = readCachedJson(name);
14
+ if (cached != null) return cached;
15
+ } catch {}
16
+ try {
17
+ for (const p of candidates) {
18
+ try {
19
+ // eslint-disable-next-line import/no-dynamic-require, global-require
20
+ return require(p);
21
+ } catch {}
22
+ }
23
+ return null;
24
+ } catch (e) {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ function toInt(x) {
30
+ const n = Number(x);
31
+ return Number.isFinite(n) ? n : null;
32
+ }
33
+
34
+ function normalizeBool(x) {
35
+ if (typeof x === "boolean") return x;
36
+ if (x === 1 || x === "1") return true;
37
+ if (x === 0 || x === "0") return false;
38
+ return !!x;
39
+ }
40
+
41
+ function loadStaticData() {
42
+ const equipBaseCost2 = loadJson("equip_base_cost.json") || [];
43
+ const improvementArrangement2 = loadJson("improvement_arrangement.json") || [];
44
+ const improvementConsumeStep2 = loadJson("improvement_consume_step.json") || [];
45
+ const improvementConsumeItem2 = loadJson("improvement_consume_item.json") || [];
46
+ const improvementUpgradeTarget2 = loadJson("improvement_upgrade_target.json") || [];
47
+ const improvementUpgradeCost2 = loadJson("improvement_upgrade_cost.json") || [];
48
+ const material2 = loadJson("material.json") || [];
49
+ const equipmentUpgradePath2 = loadJson("equipment_upgrade_path.json") || [];
50
+ const dataManifest = loadJson("data_manifest.json") || {
51
+ data_version: "unknown",
52
+ updated_at: null,
53
+ notes: null,
54
+ files: [],
55
+ };
56
+
57
+ const equipBaseCost = (equipBaseCost2 || []).map((r) => ({
58
+ ...r,
59
+ id: toInt(r.id),
60
+ consume_fuel: toInt(r.consume_fuel) || 0,
61
+ consume_ammo: toInt(r.consume_ammo) || 0,
62
+ consume_steel: toInt(r.consume_steel) || 0,
63
+ consume_bauxite: toInt(r.consume_bauxite) || 0,
64
+ is_deleted: normalizeBool(r.is_deleted),
65
+ }));
66
+
67
+ const improvementArrangement = (improvementArrangement2 || []).map((r) => ({
68
+ ...r,
69
+ equipment_id: toInt(r.equipment_id),
70
+ secretary_id: toInt(r.secretary_id),
71
+ monday: normalizeBool(r.monday),
72
+ tuesday: normalizeBool(r.tuesday),
73
+ wednesday: normalizeBool(r.wednesday),
74
+ thursday: normalizeBool(r.thursday),
75
+ friday: normalizeBool(r.friday),
76
+ saturday: normalizeBool(r.saturday),
77
+ sunday: normalizeBool(r.sunday),
78
+ note: r.note == null ? null : String(r.note),
79
+ is_deleted: normalizeBool(r.is_deleted),
80
+ secretary_variant: r.secretary_variant == null ? null : String(r.secretary_variant),
81
+ route_kind: r.route_kind == null || r.route_kind === "" ? null : String(r.route_kind),
82
+ }));
83
+
84
+ const improvementConsumeStep = (improvementConsumeStep2 || []).map((r) => ({
85
+ ...r,
86
+ id: r.id == null ? null : String(r.id),
87
+ equipment_id: toInt(r.equipment_id),
88
+ step_id: toInt(r.step_id),
89
+ consume_development_min: toInt(r.consume_development_min) || 0,
90
+ consume_development_max: toInt(r.consume_development_max) || 0,
91
+ consume_improvement_min: toInt(r.consume_improvement_min) || 0,
92
+ consume_improvement_max: toInt(r.consume_improvement_max) || 0,
93
+ is_deleted: normalizeBool(r.is_deleted),
94
+ }));
95
+
96
+ const improvementConsumeItem = (improvementConsumeItem2 || []).map((r) => ({
97
+ ...r,
98
+ uuid: r.uuid == null ? null : String(r.uuid),
99
+ step_id: r.step_id == null ? null : String(r.step_id),
100
+ item_equipment_id: r.item_equipment_id == null ? null : toInt(r.item_equipment_id),
101
+ item_material_key: r.item_material_key == null ? null : String(r.item_material_key),
102
+ count: toInt(r.count) || 0,
103
+ is_deleted: normalizeBool(r.is_deleted),
104
+ star_from: r.star_from == null ? null : toInt(r.star_from),
105
+ star_to: r.star_to == null ? null : toInt(r.star_to),
106
+ }));
107
+
108
+ const improvementUpgradeTarget = (improvementUpgradeTarget2 || []).map((r) => ({
109
+ ...r,
110
+ equipment_id: toInt(r.equipment_id),
111
+ upgrade_id: toInt(r.upgrade_id),
112
+ consume_development_min: toInt(r.consume_development_min) || 0,
113
+ consume_development_max: toInt(r.consume_development_max) || 0,
114
+ consume_improvement_min: toInt(r.consume_improvement_min) || 0,
115
+ consume_improvement_max: toInt(r.consume_improvement_max) || 0,
116
+ is_deleted: normalizeBool(r.is_deleted),
117
+ route_kind: r.route_kind == null || r.route_kind === "" ? null : String(r.route_kind),
118
+ }));
119
+
120
+ const improvementUpgradeCost = (improvementUpgradeCost2 || []).map((r) => ({
121
+ ...r,
122
+ id: r.id == null ? null : String(r.id),
123
+ equipment_id: toInt(r.equipment_id),
124
+ upgrade_id: toInt(r.upgrade_id),
125
+ item_equipment_id: r.item_equipment_id == null ? null : toInt(r.item_equipment_id),
126
+ item_material_key: r.item_material_key == null ? null : String(r.item_material_key),
127
+ count: toInt(r.count) || 0,
128
+ is_deleted: normalizeBool(r.is_deleted),
129
+ }));
130
+
131
+ const material = (material2 || []).map((r) => ({
132
+ ...r,
133
+ key: r.key == null ? null : String(r.key),
134
+ name: r.name == null ? "" : String(r.name),
135
+ is_deleted: normalizeBool(r.is_deleted),
136
+ }));
137
+
138
+ // ✅ 新增:materialByKey(后续 material_key -> name/useitemId 都靠它)
139
+ const materialByKey = {};
140
+ for (const r of material) {
141
+ if (!r || r.key == null) continue;
142
+ materialByKey[String(r.key)] = r;
143
+ }
144
+
145
+ const equipmentUpgradePath = (equipmentUpgradePath2 || []).map((r) => {
146
+ let equipmentPath = [];
147
+ if (Array.isArray(r.equipment_path)) {
148
+ equipmentPath = r.equipment_path.map((x) => String(x));
149
+ } else if (typeof r.equipment_path === "string") {
150
+ try {
151
+ const arr = JSON.parse(r.equipment_path);
152
+ if (Array.isArray(arr)) equipmentPath = arr.map((x) => String(x));
153
+ } catch {}
154
+ }
155
+
156
+ return {
157
+ ...r,
158
+ id: r.id == null ? null : String(r.id),
159
+ route_kind: r.route_kind == null || r.route_kind === "" ? null : String(r.route_kind),
160
+ from_equipment_id: toInt(r.from_equipment_id),
161
+ to_equipment_id: toInt(r.to_equipment_id),
162
+ equipment_path: equipmentPath,
163
+ step_count: toInt(r.step_count) || 0,
164
+ is_deleted: normalizeBool(r.is_deleted),
165
+ };
166
+ });
167
+
168
+ return {
169
+ dataManifest,
170
+ equipBaseCost,
171
+ improvementArrangement,
172
+ improvementConsumeStep,
173
+ improvementConsumeItem,
174
+ improvementUpgradeTarget,
175
+ improvementUpgradeCost,
176
+ material,
177
+ materialByKey, // ✅
178
+ equipmentUpgradePath,
179
+ };
180
+ }
181
+
182
+ module.exports = { loadStaticData };
@@ -0,0 +1,15 @@
1
+ {
2
+ "data_version": "2026.02.25",
3
+ "updated_at": "2026-02-25T00:00:00+09:00",
4
+ "notes": "Static dataset bundled with plugin release.",
5
+ "files": [
6
+ "equip_base_cost.json",
7
+ "equipment_upgrade_path.json",
8
+ "improvement_arrangement.json",
9
+ "improvement_consume_item.json",
10
+ "improvement_consume_step.json",
11
+ "improvement_upgrade_cost.json",
12
+ "improvement_upgrade_target.json",
13
+ "material.json"
14
+ ]
15
+ }