@sayue_ltr/fleq 1.49.2

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 (52) hide show
  1. package/CHANGELOG.md +898 -0
  2. package/LICENSE +21 -0
  3. package/README.md +535 -0
  4. package/assets/icons/.gitkeep +0 -0
  5. package/assets/sounds/.gitkeep +0 -0
  6. package/assets/sounds/cancel.mp3 +0 -0
  7. package/assets/sounds/critical.mp3 +0 -0
  8. package/assets/sounds/info.mp3 +0 -0
  9. package/assets/sounds/normal.mp3 +0 -0
  10. package/assets/sounds/warning.mp3 +0 -0
  11. package/dist/config.js +638 -0
  12. package/dist/dmdata/connection-manager.js +2 -0
  13. package/dist/dmdata/endpoint-selector.js +185 -0
  14. package/dist/dmdata/multi-connection-manager.js +158 -0
  15. package/dist/dmdata/rest-client.js +281 -0
  16. package/dist/dmdata/telegram-parser.js +704 -0
  17. package/dist/dmdata/volcano-parser.js +647 -0
  18. package/dist/dmdata/ws-client.js +336 -0
  19. package/dist/engine/cli/cli-init.js +266 -0
  20. package/dist/engine/cli/cli-run.js +121 -0
  21. package/dist/engine/cli/cli.js +121 -0
  22. package/dist/engine/eew/eew-logger.js +355 -0
  23. package/dist/engine/eew/eew-tracker.js +229 -0
  24. package/dist/engine/messages/message-router.js +261 -0
  25. package/dist/engine/messages/tsunami-state.js +96 -0
  26. package/dist/engine/messages/volcano-state.js +131 -0
  27. package/dist/engine/messages/volcano-vfvo53-aggregator.js +173 -0
  28. package/dist/engine/monitor/monitor.js +118 -0
  29. package/dist/engine/monitor/repl-coordinator.js +63 -0
  30. package/dist/engine/monitor/shutdown.js +114 -0
  31. package/dist/engine/notification/node-notifier-loader.js +19 -0
  32. package/dist/engine/notification/notifier.js +338 -0
  33. package/dist/engine/notification/sound-player.js +230 -0
  34. package/dist/engine/notification/volcano-presentation.js +166 -0
  35. package/dist/engine/startup/config-resolver.js +139 -0
  36. package/dist/engine/startup/tsunami-initializer.js +91 -0
  37. package/dist/engine/startup/update-checker.js +229 -0
  38. package/dist/engine/startup/volcano-initializer.js +89 -0
  39. package/dist/index.js +24 -0
  40. package/dist/logger.js +95 -0
  41. package/dist/types.js +61 -0
  42. package/dist/ui/earthquake-formatter.js +871 -0
  43. package/dist/ui/eew-formatter.js +335 -0
  44. package/dist/ui/formatter.js +689 -0
  45. package/dist/ui/repl.js +2059 -0
  46. package/dist/ui/test-samples.js +880 -0
  47. package/dist/ui/theme.js +516 -0
  48. package/dist/ui/volcano-formatter.js +667 -0
  49. package/dist/ui/waiting-tips.js +227 -0
  50. package/dist/utils/intensity.js +13 -0
  51. package/dist/utils/secrets.js +14 -0
  52. package/package.json +69 -0
@@ -0,0 +1,516 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.DEFAULT_ROLES = exports.DEFAULT_PALETTE = void 0;
40
+ exports.hexToRgb = hexToRgb;
41
+ exports.rgbToHex = rgbToHex;
42
+ exports.resolveTheme = resolveTheme;
43
+ exports.getThemePath = getThemePath;
44
+ exports.loadTheme = loadTheme;
45
+ exports.loadThemeFromPath = loadThemeFromPath;
46
+ exports.reloadTheme = reloadTheme;
47
+ exports.resetTheme = resetTheme;
48
+ exports.validateThemeFile = validateThemeFile;
49
+ exports.getPalette = getPalette;
50
+ exports.getRole = getRole;
51
+ exports.getRoleChalk = getRoleChalk;
52
+ exports.isCustomized = isCustomized;
53
+ exports.getResolvedTheme = getResolvedTheme;
54
+ exports.getRoleNames = getRoleNames;
55
+ exports.getPaletteNames = getPaletteNames;
56
+ exports.generateDefaultThemeJson = generateDefaultThemeJson;
57
+ const fs = __importStar(require("fs"));
58
+ const path = __importStar(require("path"));
59
+ const chalk_1 = __importDefault(require("chalk"));
60
+ const config_1 = require("../config");
61
+ /** RoleStyleDef の型ガード */
62
+ function isRoleStyleDef(value) {
63
+ if (typeof value === "string")
64
+ return true;
65
+ if (typeof value !== "object" || value == null || Array.isArray(value))
66
+ return false;
67
+ const v = value;
68
+ return ((v.fg == null || typeof v.fg === "string") &&
69
+ (v.bg == null || typeof v.bg === "string") &&
70
+ (v.bold == null || typeof v.bold === "boolean"));
71
+ }
72
+ // ── デフォルトパレット ──
73
+ /** CUD 推奨色のデフォルト RGB 値 */
74
+ exports.DEFAULT_PALETTE = {
75
+ gray: [132, 145, 158],
76
+ sky: [86, 180, 233],
77
+ blue: [0, 114, 178],
78
+ blueGreen: [0, 158, 115],
79
+ yellow: [240, 228, 66],
80
+ orange: [230, 159, 0],
81
+ vermillion: [213, 94, 0],
82
+ raspberry: [204, 121, 167],
83
+ darkRed: [122, 30, 0],
84
+ };
85
+ /** パレット色名の一覧 */
86
+ const PALETTE_NAMES = [
87
+ "gray",
88
+ "sky",
89
+ "blue",
90
+ "blueGreen",
91
+ "yellow",
92
+ "orange",
93
+ "vermillion",
94
+ "raspberry",
95
+ "darkRed",
96
+ ];
97
+ // ── デフォルトロール ──
98
+ /** セマンティックロール定義 */
99
+ exports.DEFAULT_ROLES = {
100
+ // frame
101
+ frameCritical: "vermillion",
102
+ frameWarning: "orange",
103
+ frameNormal: "sky",
104
+ frameInfo: "gray",
105
+ frameCancel: "raspberry",
106
+ // intensity
107
+ intensity1: "gray",
108
+ intensity2: "sky",
109
+ intensity3: "blue",
110
+ intensity4: "blueGreen",
111
+ intensity5Lower: "yellow",
112
+ intensity5Upper: "orange",
113
+ intensity6Lower: { fg: "vermillion", bold: true },
114
+ intensity6Upper: { bg: "vermillion", fg: "#000000", bold: true },
115
+ intensity7: { bg: "darkRed", fg: "#FFFFFF", bold: true },
116
+ // lgIntensity
117
+ lgInt0: "gray",
118
+ lgInt1: "sky",
119
+ lgInt2: "yellow",
120
+ lgInt3: "orange",
121
+ lgInt4: { bg: "vermillion", fg: "#000000", bold: true },
122
+ // magnitude
123
+ magnitudeLow: "yellow",
124
+ magnitudeHigh: { fg: "vermillion", bold: true },
125
+ magnitudeMax: { bg: "darkRed", fg: "#FFFFFF", bold: true },
126
+ // tsunami
127
+ tsunamiNone: "blueGreen",
128
+ tsunamiAdvisory: "orange",
129
+ tsunamiWarning: { fg: "vermillion", bold: true },
130
+ tsunamiMajor: { bg: "darkRed", fg: "#FFFFFF", bold: true },
131
+ // eew
132
+ eewWarningBanner: { bg: "darkRed", fg: "#FFFFFF", bold: true },
133
+ eewForecastBanner: { bg: "yellow", fg: "#000000", bold: true },
134
+ eewCancelBanner: { bg: "raspberry", fg: "#000000", bold: true },
135
+ plumLabel: "raspberry",
136
+ arrivedLabel: "vermillion",
137
+ cancelText: "raspberry",
138
+ // common
139
+ testBadge: { bg: "raspberry", fg: "#FFFFFF", bold: true },
140
+ hypocenter: { fg: "yellow", bold: true },
141
+ concurrent: "orange",
142
+ nextAdvisory: "sky",
143
+ warningComment: "orange",
144
+ detailUri: "sky",
145
+ textMuted: "gray",
146
+ // tsunami banner
147
+ tsunamiAdvisoryBanner: { bg: "yellow", fg: "#000000", bold: true },
148
+ tsunamiWarningBanner: { bg: "vermillion", fg: "#FFFFFF", bold: true },
149
+ tsunamiMajorBanner: { bg: "darkRed", fg: "#FFFFFF", bold: true },
150
+ tsunamiMajorBannerDecor: { bg: "#FFFFFF", fg: "#000000", bold: true },
151
+ // nankai trough
152
+ nankaiCriticalBanner: { bg: "darkRed", fg: "#FFFFFF", bold: true },
153
+ nankaiWarningBanner: { bg: "orange", fg: "#000000", bold: true },
154
+ nankaiSerialCritical: { fg: "vermillion", bold: true },
155
+ nankaiSerialWarning: { fg: "orange", bold: true },
156
+ // eew banner palette (additional colors)
157
+ eewWarningBanner1: { bg: "vermillion", fg: "#FFFFFF", bold: true },
158
+ eewWarningBanner2: { bg: "vermillion", fg: "#000000", bold: true },
159
+ eewWarningBanner3: { bg: "orange", fg: "#000000", bold: true },
160
+ eewWarningBanner4: { bg: "raspberry", fg: "#000000", bold: true },
161
+ eewForecastBanner1: { bg: "orange", fg: "#000000", bold: true },
162
+ eewForecastBanner2: { bg: "sky", fg: "#000000", bold: true },
163
+ eewForecastBanner3: { bg: "blueGreen", fg: "#000000", bold: true },
164
+ eewForecastBanner4: { bg: "gray", fg: "#FFFFFF", bold: true },
165
+ // plum decor
166
+ plumDecorWarning: { bg: "blue", fg: "#FFFFFF", bold: true },
167
+ plumDecorForecast: { bg: "sky", fg: "#000000", bold: true },
168
+ // raw header
169
+ rawHeaderLabel: "sky",
170
+ // volcano: 噴火警戒レベル (5段階)
171
+ volcanoLevel1: "blueGreen",
172
+ volcanoLevel2: { fg: "blue", bold: true },
173
+ volcanoLevel3: { fg: "yellow", bold: true },
174
+ volcanoLevel4: { fg: "vermillion", bold: true },
175
+ volcanoLevel5: { bg: "darkRed", fg: "#FFFFFF", bold: true },
176
+ // volcano: 噴火現象 (4種)
177
+ volcanoPhenomenonExplosion: { fg: "vermillion", bold: true },
178
+ volcanoPhenomenonEruption: { fg: "orange", bold: true },
179
+ volcanoPhenomenonFrequent: { bg: "vermillion", fg: "#FFFFFF", bold: true },
180
+ volcanoPhenomenonPossible: { fg: "sky", bold: true },
181
+ // volcano: 降灰量 (4段階)
182
+ volcanoAshfallLight: "sky",
183
+ volcanoAshfallModerate: { fg: "yellow", bold: true },
184
+ volcanoAshfallHeavy: { fg: "orange", bold: true },
185
+ volcanoAshfallBallistic: { fg: "vermillion", bold: true },
186
+ // volcano: バナー
187
+ volcanoAlertBanner: { bg: "vermillion", fg: "#FFFFFF", bold: true },
188
+ volcanoFlashBanner: { bg: "darkRed", fg: "#FFFFFF", bold: true },
189
+ };
190
+ /** ロール名の一覧 */
191
+ const ROLE_NAMES = Object.keys(exports.DEFAULT_ROLES);
192
+ // ── ユーティリティ関数 ──
193
+ /** HEX文字列を RGB タプルに変換。不正なら null */
194
+ function hexToRgb(hex) {
195
+ const m = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/.exec(hex);
196
+ if (!m)
197
+ return null;
198
+ return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)];
199
+ }
200
+ /** RGB タプルを HEX 文字列に変換 */
201
+ function rgbToHex(rgb) {
202
+ const r = rgb[0].toString(16).padStart(2, "0").toUpperCase();
203
+ const g = rgb[1].toString(16).padStart(2, "0").toUpperCase();
204
+ const b = rgb[2].toString(16).padStart(2, "0").toUpperCase();
205
+ return `#${r}${g}${b}`;
206
+ }
207
+ // ── テーマ解決 ──
208
+ /** 色参照(パレット名 or HEX)を解決する */
209
+ function resolveColorRef(ref, palette) {
210
+ // パレット名参照
211
+ if (PALETTE_NAMES.includes(ref)) {
212
+ return { rgb: palette[ref] };
213
+ }
214
+ // HEX値
215
+ if (ref.startsWith("#")) {
216
+ const rgb = hexToRgb(ref);
217
+ if (rgb)
218
+ return { rgb };
219
+ return { rgb: null, warning: `不正なHEX値: "${ref}"` };
220
+ }
221
+ return { rgb: null, warning: `不明な色参照: "${ref}" (パレット名または #RRGGBB を指定してください)` };
222
+ }
223
+ /** RoleStyleDef を解決して ResolvedStyle にする */
224
+ function resolveRoleStyle(def, palette) {
225
+ const warnings = [];
226
+ if (typeof def === "string") {
227
+ // 文字列 → 前景色のみ
228
+ const { rgb, warning } = resolveColorRef(def, palette);
229
+ if (warning)
230
+ warnings.push(warning);
231
+ return {
232
+ style: { fg: rgb ?? undefined, bold: false },
233
+ warnings,
234
+ };
235
+ }
236
+ // オブジェクト
237
+ let fg;
238
+ let bg;
239
+ if (def.fg != null) {
240
+ const result = resolveColorRef(def.fg, palette);
241
+ if (result.warning)
242
+ warnings.push(`fg: ${result.warning}`);
243
+ fg = result.rgb ?? undefined;
244
+ }
245
+ if (def.bg != null) {
246
+ const result = resolveColorRef(def.bg, palette);
247
+ if (result.warning)
248
+ warnings.push(`bg: ${result.warning}`);
249
+ bg = result.rgb ?? undefined;
250
+ }
251
+ return {
252
+ style: { fg, bg, bold: def.bold ?? false },
253
+ warnings,
254
+ };
255
+ }
256
+ /** パース済みオブジェクトを ThemeFile として安全に変換する */
257
+ function sanitizeThemeInput(parsed) {
258
+ const warnings = [];
259
+ const themeFile = {};
260
+ if ("palette" in parsed) {
261
+ if (typeof parsed.palette === "object" && parsed.palette != null && !Array.isArray(parsed.palette)) {
262
+ themeFile.palette = parsed.palette;
263
+ }
264
+ else {
265
+ warnings.push("palette はオブジェクトである必要があります。無視します。");
266
+ }
267
+ }
268
+ if ("roles" in parsed) {
269
+ if (typeof parsed.roles === "object" && parsed.roles != null && !Array.isArray(parsed.roles)) {
270
+ themeFile.roles = parsed.roles;
271
+ }
272
+ else {
273
+ warnings.push("roles はオブジェクトである必要があります。無視します。");
274
+ }
275
+ }
276
+ return { themeFile, warnings };
277
+ }
278
+ /** 純粋関数: ThemeFile → ResolvedTheme + 警告リスト */
279
+ function resolveTheme(raw, defaults) {
280
+ const warnings = [];
281
+ // パレット解決
282
+ const palette = { ...defaults.palette };
283
+ if (raw.palette) {
284
+ for (const [key, value] of Object.entries(raw.palette)) {
285
+ if (!PALETTE_NAMES.includes(key)) {
286
+ warnings.push(`palette: 未知のキー "${key}" を無視しました`);
287
+ continue;
288
+ }
289
+ if (typeof value !== "string") {
290
+ warnings.push(`palette.${key}: 値は文字列(HEX)である必要があります`);
291
+ continue;
292
+ }
293
+ const rgb = hexToRgb(value);
294
+ if (!rgb) {
295
+ warnings.push(`palette.${key}: 不正なHEX値 "${value}"`);
296
+ continue;
297
+ }
298
+ palette[key] = rgb;
299
+ }
300
+ }
301
+ // ロール解決
302
+ const roles = {};
303
+ for (const roleName of ROLE_NAMES) {
304
+ const rawRole = raw.roles?.[roleName];
305
+ if (rawRole != null && !isRoleStyleDef(rawRole)) {
306
+ warnings.push(`roles.${roleName}: 不正な値の形式です (文字列またはオブジェクト{fg?,bg?,bold?}を指定してください)`);
307
+ const fallback = resolveRoleStyle(defaults.roles[roleName], palette);
308
+ roles[roleName] = fallback.style;
309
+ continue;
310
+ }
311
+ const def = rawRole ?? defaults.roles[roleName];
312
+ const { style, warnings: roleWarnings } = resolveRoleStyle(def, palette);
313
+ for (const w of roleWarnings) {
314
+ warnings.push(`roles.${roleName}: ${w}`);
315
+ }
316
+ // 解決失敗時はデフォルトにフォールバック
317
+ if (rawRole != null && roleWarnings.length > 0) {
318
+ const fallback = resolveRoleStyle(defaults.roles[roleName], palette);
319
+ roles[roleName] = fallback.style;
320
+ }
321
+ else {
322
+ roles[roleName] = style;
323
+ }
324
+ }
325
+ // 未知の roles キーをチェック
326
+ if (raw.roles) {
327
+ for (const key of Object.keys(raw.roles)) {
328
+ if (!ROLE_NAMES.includes(key)) {
329
+ warnings.push(`roles: 未知のキー "${key}" を無視しました`);
330
+ }
331
+ }
332
+ }
333
+ return {
334
+ theme: { palette, roles: roles },
335
+ warnings,
336
+ };
337
+ }
338
+ // ── デフォルトテーマの事前解決 ──
339
+ /** テーマオブジェクトを再帰的にフリーズする */
340
+ function deepFreezeTheme(theme) {
341
+ Object.freeze(theme.palette);
342
+ for (const rgb of Object.values(theme.palette))
343
+ Object.freeze(rgb);
344
+ Object.freeze(theme.roles);
345
+ for (const style of Object.values(theme.roles))
346
+ Object.freeze(style);
347
+ Object.freeze(theme);
348
+ return theme;
349
+ }
350
+ function buildDefaultResolvedTheme() {
351
+ const { theme } = resolveTheme({}, { palette: exports.DEFAULT_PALETTE, roles: exports.DEFAULT_ROLES });
352
+ return deepFreezeTheme(theme);
353
+ }
354
+ // ── モジュール状態 ──
355
+ let currentTheme = buildDefaultResolvedTheme();
356
+ // ── パス解決 ──
357
+ /** theme.json のパスを返す */
358
+ function getThemePath() {
359
+ return path.join((0, config_1.getConfigDir)(), "theme.json");
360
+ }
361
+ // ── テーマ読込 ──
362
+ /** ファイルからテーマを読み込み、キャッシュを更新する。警告リストを返す。 */
363
+ function loadTheme() {
364
+ return loadThemeFromPath(getThemePath());
365
+ }
366
+ /** パス指定でテーマを読み込む (テスト用) */
367
+ function loadThemeFromPath(themePath) {
368
+ chalkCache.clear();
369
+ if (!fs.existsSync(themePath)) {
370
+ currentTheme = buildDefaultResolvedTheme();
371
+ return [];
372
+ }
373
+ try {
374
+ const raw = fs.readFileSync(themePath, "utf-8");
375
+ const parsed = JSON.parse(raw);
376
+ if (typeof parsed !== "object" || parsed == null || Array.isArray(parsed)) {
377
+ currentTheme = buildDefaultResolvedTheme();
378
+ return ["theme.json の形式が不正です。デフォルトテーマを使用します。"];
379
+ }
380
+ const { themeFile, warnings: sanitizeWarnings } = sanitizeThemeInput(parsed);
381
+ const { theme, warnings } = resolveTheme(themeFile, {
382
+ palette: exports.DEFAULT_PALETTE,
383
+ roles: exports.DEFAULT_ROLES,
384
+ });
385
+ currentTheme = deepFreezeTheme(theme);
386
+ return [...sanitizeWarnings, ...warnings];
387
+ }
388
+ catch (err) {
389
+ currentTheme = buildDefaultResolvedTheme();
390
+ if (err instanceof SyntaxError) {
391
+ return ["theme.json のJSONパースに失敗しました。デフォルトテーマを使用します。"];
392
+ }
393
+ if (err instanceof Error) {
394
+ return [`theme.json の読み込みに失敗しました: ${err.message}`];
395
+ }
396
+ return ["theme.json の読み込みに失敗しました。"];
397
+ }
398
+ }
399
+ /** テーマを再読込する */
400
+ function reloadTheme() {
401
+ return loadTheme();
402
+ }
403
+ /** デフォルト theme.json を書き出し、リロードする */
404
+ function resetTheme() {
405
+ const themePath = getThemePath();
406
+ const dir = path.dirname(themePath);
407
+ try {
408
+ if (!fs.existsSync(dir)) {
409
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
410
+ }
411
+ fs.writeFileSync(themePath, generateDefaultThemeJson(), {
412
+ encoding: "utf-8",
413
+ mode: 0o600,
414
+ });
415
+ }
416
+ catch (err) {
417
+ if (err instanceof Error) {
418
+ return [`theme.json の書き出しに失敗しました: ${err.message}`];
419
+ }
420
+ return ["theme.json の書き出しに失敗しました。"];
421
+ }
422
+ return loadThemeFromPath(themePath);
423
+ }
424
+ /** theme.json を検証し、問題点を返す */
425
+ function validateThemeFile() {
426
+ const themePath = getThemePath();
427
+ if (!fs.existsSync(themePath)) {
428
+ return { valid: true, warnings: ["theme.json が見つかりません (デフォルトテーマを使用中)"] };
429
+ }
430
+ try {
431
+ const raw = fs.readFileSync(themePath, "utf-8");
432
+ const parsed = JSON.parse(raw);
433
+ if (typeof parsed !== "object" || parsed == null || Array.isArray(parsed)) {
434
+ return { valid: false, warnings: ["theme.json の形式が不正です (オブジェクトである必要があります)"] };
435
+ }
436
+ const { themeFile, warnings: sanitizeWarnings } = sanitizeThemeInput(parsed);
437
+ const { warnings } = resolveTheme(themeFile, {
438
+ palette: exports.DEFAULT_PALETTE,
439
+ roles: exports.DEFAULT_ROLES,
440
+ });
441
+ const allWarnings = [...sanitizeWarnings, ...warnings];
442
+ return { valid: allWarnings.length === 0, warnings: allWarnings };
443
+ }
444
+ catch (err) {
445
+ if (err instanceof SyntaxError) {
446
+ return { valid: false, warnings: [`JSONパースエラー: ${err.message}`] };
447
+ }
448
+ if (err instanceof Error) {
449
+ return { valid: false, warnings: [`読み込みエラー: ${err.message}`] };
450
+ }
451
+ return { valid: false, warnings: ["不明なエラー"] };
452
+ }
453
+ }
454
+ // ── テーマアクセサ ──
455
+ /** 解決済みパレットを返す */
456
+ function getPalette() {
457
+ return currentTheme.palette;
458
+ }
459
+ /** 解決済みロールスタイルを返す */
460
+ function getRole(name) {
461
+ return currentTheme.roles[name];
462
+ }
463
+ let chalkCache = new Map();
464
+ /** ResolvedStyle → chalk.Chalk に変換して返す (キャッシュ付き) */
465
+ function getRoleChalk(name) {
466
+ const key = `${chalk_1.default.level}:${name}`;
467
+ const cached = chalkCache.get(key);
468
+ if (cached)
469
+ return cached;
470
+ const built = styleToChalk(currentTheme.roles[name]);
471
+ chalkCache.set(key, built);
472
+ return built;
473
+ }
474
+ /** ResolvedStyle を chalk.Chalk に変換する */
475
+ function styleToChalk(style) {
476
+ let c = chalk_1.default;
477
+ if (style.bg) {
478
+ c = c.bgRgb(style.bg[0], style.bg[1], style.bg[2]);
479
+ }
480
+ if (style.fg) {
481
+ c = c.rgb(style.fg[0], style.fg[1], style.fg[2]);
482
+ }
483
+ if (style.bold) {
484
+ c = c.bold;
485
+ }
486
+ return c;
487
+ }
488
+ /** theme.json がカスタマイズされているか (ファイルが存在するか) */
489
+ function isCustomized() {
490
+ return fs.existsSync(getThemePath());
491
+ }
492
+ /** 解決済みテーマ全体を返す */
493
+ function getResolvedTheme() {
494
+ return currentTheme;
495
+ }
496
+ /** 全ロール名の一覧を返す */
497
+ function getRoleNames() {
498
+ return [...ROLE_NAMES];
499
+ }
500
+ /** 全パレット色名の一覧を返す */
501
+ function getPaletteNames() {
502
+ return [...PALETTE_NAMES];
503
+ }
504
+ // ── デフォルト theme.json 生成 ──
505
+ /** デフォルト theme.json の内容を JSON 文字列で返す */
506
+ function generateDefaultThemeJson() {
507
+ const paletteObj = {};
508
+ for (const name of PALETTE_NAMES) {
509
+ paletteObj[name] = rgbToHex(exports.DEFAULT_PALETTE[name]);
510
+ }
511
+ const rolesObj = {};
512
+ for (const name of ROLE_NAMES) {
513
+ rolesObj[name] = exports.DEFAULT_ROLES[name];
514
+ }
515
+ return JSON.stringify({ palette: paletteObj, roles: rolesObj }, null, 2) + "\n";
516
+ }