@sayue_ltr/fleq 1.51.0 → 2.0.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.
- package/CHANGELOG.md +86 -0
- package/README.md +1 -1
- package/dist/config.js +2 -2
- package/dist/dmdata/telegram-parser.js +78 -5
- package/dist/engine/cli/cli-run.js +19 -4
- package/dist/engine/eew/eew-tracker.js +41 -15
- package/dist/engine/monitor/monitor.js +1 -1
- package/dist/engine/notification/notifier.js +5 -3
- package/dist/engine/notification/sound-player.js +232 -35
- package/dist/engine/presentation/processors/process-eew.js +15 -0
- package/dist/engine/presentation/processors/process-message.js +2 -2
- package/dist/engine/startup/update-checker.js +23 -2
- package/dist/engine/template/compiler.js +4 -1
- package/dist/engine/template/field-accessor.js +10 -4
- package/dist/engine/template/filters.js +14 -8
- package/dist/engine/template/parser.js +10 -15
- package/dist/types.js +1 -1
- package/dist/ui/earthquake-formatter.js +5 -0
- package/dist/ui/formatter.js +49 -0
- package/dist/ui/repl-handlers/command-definitions.js +11 -4
- package/dist/ui/repl-handlers/info-handlers.js +97 -41
- package/dist/ui/repl-handlers/settings-handlers.js +19 -15
- package/dist/ui/statistics-formatter.js +65 -15
- package/dist/ui/status-line.js +11 -0
- package/dist/ui/test-samples.js +6 -0
- package/dist/ui/theme.js +9 -0
- package/dist/ui/waiting-tips-eew.js +63 -0
- package/dist/ui/waiting-tips-info-systems.js +81 -0
- package/dist/ui/waiting-tips-seismology.js +97 -0
- package/dist/ui/waiting-tips-tsunami.js +72 -0
- package/dist/ui/waiting-tips-weather.js +189 -0
- package/dist/ui/waiting-tips.js +137 -6
- package/package.json +1 -1
package/dist/types.js
CHANGED
|
@@ -381,6 +381,11 @@ function displayEarthquakeInfo(info) {
|
|
|
381
381
|
buf.push(wl);
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
|
+
// EventID (同一地震の紐付け用、通常モードのみ表示)
|
|
385
|
+
if (info.eventId) {
|
|
386
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
387
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`EventID: ${info.eventId}`), width));
|
|
388
|
+
}
|
|
384
389
|
// フッター
|
|
385
390
|
(0, formatter_1.renderFooter)(level, info.type, info.reportDateTime, info.publishingOffice, width, buf);
|
|
386
391
|
buf.push((0, formatter_1.frameBottom)(level, width));
|
package/dist/ui/formatter.js
CHANGED
|
@@ -67,6 +67,7 @@ exports.collectHighlightSpans = collectHighlightSpans;
|
|
|
67
67
|
exports.highlightAndWrap = highlightAndWrap;
|
|
68
68
|
exports.formatTimestamp = formatTimestamp;
|
|
69
69
|
exports.formatElapsedTime = formatElapsedTime;
|
|
70
|
+
exports.formatUptime = formatUptime;
|
|
70
71
|
exports.intensityColor = intensityColor;
|
|
71
72
|
exports.lgIntensityColor = lgIntensityColor;
|
|
72
73
|
exports.lgIntToNumeric = lgIntToNumeric;
|
|
@@ -619,6 +620,54 @@ function formatElapsedTime(ms) {
|
|
|
619
620
|
const ss = totalSec % 60;
|
|
620
621
|
return `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}:${String(ss).padStart(2, "0")}`;
|
|
621
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* 稼働時間を "DDD:HH:MM:SS" 形式に整形する。
|
|
625
|
+
* 未使用の上位ゼロ桁は dim (gray) 表示にして画面ヤケを軽減する。
|
|
626
|
+
* 日数が 3 桁を超える場合は桁数が増える。
|
|
627
|
+
*/
|
|
628
|
+
function formatUptime(ms) {
|
|
629
|
+
const safeMs = Math.max(0, ms);
|
|
630
|
+
const totalSec = Math.floor(safeMs / 1000);
|
|
631
|
+
const dd = Math.floor(totalSec / 86400);
|
|
632
|
+
const hh = Math.floor((totalSec % 86400) / 3600);
|
|
633
|
+
const mm = Math.floor((totalSec % 3600) / 60);
|
|
634
|
+
const ss = totalSec % 60;
|
|
635
|
+
const ddStr = String(dd).padStart(3, "0");
|
|
636
|
+
const hhStr = String(hh).padStart(2, "0");
|
|
637
|
+
const mmStr = String(mm).padStart(2, "0");
|
|
638
|
+
const ssStr = String(ss).padStart(2, "0");
|
|
639
|
+
const raw = `${ddStr}:${hhStr}:${mmStr}:${ssStr}`;
|
|
640
|
+
// セグメント境界: DDD(0-2) : HH(4-5) : MM(7-8) : SS(10-11)
|
|
641
|
+
// segStarts は dd < 1000 (3桁) の通常ケース用。
|
|
642
|
+
// dd >= 1000 では firstNonZero=0 → activeSegStart=0 で全体 white になるため問題なし。
|
|
643
|
+
const segStarts = [0, 4, 7, 10];
|
|
644
|
+
// 先頭から連続するゼロと区切り文字をスキャン
|
|
645
|
+
let firstNonZero = -1;
|
|
646
|
+
for (let i = 0; i < raw.length; i++) {
|
|
647
|
+
if (raw[i] !== "0" && raw[i] !== ":") {
|
|
648
|
+
firstNonZero = i;
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
// dim 境界を決定
|
|
653
|
+
// - 日部分 (index 0-2): 文字レベルで先頭ゼロをグレーアウト
|
|
654
|
+
// - 時刻部分 (HH/MM/SS): セグメント境界でグレーアウト
|
|
655
|
+
// - 全桁ゼロ: SS セグメントから通常表示
|
|
656
|
+
let dimEnd;
|
|
657
|
+
if (firstNonZero === -1) {
|
|
658
|
+
dimEnd = segStarts[segStarts.length - 1];
|
|
659
|
+
}
|
|
660
|
+
else if (firstNonZero <= 2) {
|
|
661
|
+
dimEnd = firstNonZero;
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
dimEnd = segStarts.filter((s) => s <= firstNonZero).pop();
|
|
665
|
+
}
|
|
666
|
+
if (dimEnd === 0) {
|
|
667
|
+
return chalk_1.default.white(raw);
|
|
668
|
+
}
|
|
669
|
+
return chalk_1.default.gray(raw.slice(0, dimEnd)) + chalk_1.default.white(raw.slice(dimEnd));
|
|
670
|
+
}
|
|
622
671
|
/** 区切り線 (後方互換用、新コードではフレーム関数を使用) */
|
|
623
672
|
function separator(char = "─", len = 60) {
|
|
624
673
|
return chalk_1.default.gray(char.repeat(len));
|
|
@@ -41,11 +41,17 @@ const ops = __importStar(require("./operation-handlers"));
|
|
|
41
41
|
function buildCommandMap(getCtx) {
|
|
42
42
|
return {
|
|
43
43
|
help: {
|
|
44
|
-
description: "
|
|
45
|
-
detail: "
|
|
44
|
+
description: "コマンドの詳細を表示 (例: help notify)",
|
|
45
|
+
detail: "help <command>: コマンドの詳細を表示\n help <command> <sub>: サブコマンドの詳細を表示\n 一覧は commands コマンドで表示できます。",
|
|
46
46
|
category: "info",
|
|
47
47
|
handler: (args) => info.handleHelp(getCtx(), args),
|
|
48
48
|
},
|
|
49
|
+
commands: {
|
|
50
|
+
description: "コマンド一覧を表示 (例: commands settings)",
|
|
51
|
+
detail: "引数なし: 全コマンドをカテゴリ別に一覧表示\n commands <category>: カテゴリで絞り込み (info / status / settings / operation)\n commands <query>: 名前・説明で検索",
|
|
52
|
+
category: "info",
|
|
53
|
+
handler: (args) => info.handleCommands(getCtx(), args),
|
|
54
|
+
},
|
|
49
55
|
"?": {
|
|
50
56
|
description: "help のエイリアス",
|
|
51
57
|
category: "info",
|
|
@@ -185,12 +191,13 @@ function buildCommandMap(getCtx) {
|
|
|
185
191
|
handler: (args) => settings.handleFocus(getCtx(), args),
|
|
186
192
|
},
|
|
187
193
|
clock: {
|
|
188
|
-
description: "プロンプト時計の切替 (例: clock / clock
|
|
189
|
-
detail: "clock:
|
|
194
|
+
description: "プロンプト時計の切替 (例: clock / clock uptime)",
|
|
195
|
+
detail: "clock: 経過時間→現在時刻→稼働時間をトグル切替\n clock elapsed: 経過時間表示 (デフォルト)\n clock now: 現在時刻表示\n clock uptime: 稼働時間表示 (DDD:HH:MM:SS)",
|
|
190
196
|
category: "settings",
|
|
191
197
|
subcommands: {
|
|
192
198
|
elapsed: { description: "経過時間表示 (デフォルト)" },
|
|
193
199
|
now: { description: "現在時刻表示" },
|
|
200
|
+
uptime: { description: "稼働時間表示 (DDD:HH:MM:SS)" },
|
|
194
201
|
},
|
|
195
202
|
handler: (args) => settings.handleClock(getCtx(), args),
|
|
196
203
|
},
|
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.CATEGORY_ALIASES = exports.COMMAND_ALIASES = void 0;
|
|
40
40
|
exports.getCurrentSettingValues = getCurrentSettingValues;
|
|
41
41
|
exports.handleDetail = handleDetail;
|
|
42
|
+
exports.handleCommands = handleCommands;
|
|
42
43
|
exports.handleHelp = handleHelp;
|
|
43
44
|
exports.handleStats = handleStats;
|
|
44
45
|
exports.handleHistory = handleHistory;
|
|
@@ -161,6 +162,7 @@ function printColorGrid(termWidth, items, renderFn) {
|
|
|
161
162
|
}
|
|
162
163
|
/** コマンドのエイリアス (逆引き用) */
|
|
163
164
|
exports.COMMAND_ALIASES = {
|
|
165
|
+
cmds: "commands",
|
|
164
166
|
hist: "history",
|
|
165
167
|
cols: "colors",
|
|
166
168
|
det: "detail",
|
|
@@ -224,8 +226,11 @@ function getCurrentSettingValues(ctx) {
|
|
|
224
226
|
options: "normal / compact",
|
|
225
227
|
},
|
|
226
228
|
clock: {
|
|
227
|
-
current:
|
|
228
|
-
|
|
229
|
+
current: (() => {
|
|
230
|
+
const m = statusLine.getClockMode();
|
|
231
|
+
return m === "clock" ? "現在時刻" : m === "uptime" ? "稼働時間" : "経過時間";
|
|
232
|
+
})(),
|
|
233
|
+
options: "elapsed / now / uptime",
|
|
229
234
|
},
|
|
230
235
|
notify: {
|
|
231
236
|
current: `${onCount}/${totalCount} ON${muteInfo}`,
|
|
@@ -274,6 +279,88 @@ function handleDetail(ctx, args) {
|
|
|
274
279
|
}
|
|
275
280
|
console.log(chalk_1.default.yellow(` 不明なサブコマンド: ${sub}`) + chalk_1.default.gray(" (利用可能: tsunami)"));
|
|
276
281
|
}
|
|
282
|
+
/** カテゴリ名を解決する (日本語ラベルにも対応) */
|
|
283
|
+
function resolveCategory(input) {
|
|
284
|
+
const lower = input.toLowerCase();
|
|
285
|
+
const categories = Object.keys(types_2.CATEGORY_LABELS);
|
|
286
|
+
const exact = categories.find((c) => c === lower);
|
|
287
|
+
if (exact != null)
|
|
288
|
+
return exact;
|
|
289
|
+
for (const cat of categories) {
|
|
290
|
+
if (types_2.CATEGORY_LABELS[cat] === input)
|
|
291
|
+
return cat;
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
function handleCommands(ctx, args) {
|
|
296
|
+
const trimmed = args.trim();
|
|
297
|
+
let filterCategory = null;
|
|
298
|
+
let searchQuery = null;
|
|
299
|
+
if (trimmed.length > 0) {
|
|
300
|
+
filterCategory = resolveCategory(trimmed);
|
|
301
|
+
if (filterCategory == null) {
|
|
302
|
+
searchQuery = trimmed.toLowerCase();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// 検索モード
|
|
306
|
+
if (searchQuery != null) {
|
|
307
|
+
const query = searchQuery;
|
|
308
|
+
const matches = Object.entries(ctx.commands)
|
|
309
|
+
.filter(([name]) => name !== "?" && name !== "exit" && name !== "cmds")
|
|
310
|
+
.filter(([name, entry]) => name.toLowerCase().includes(query) ||
|
|
311
|
+
entry.description.toLowerCase().includes(query))
|
|
312
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
313
|
+
if (matches.length === 0) {
|
|
314
|
+
console.log(chalk_1.default.yellow(` "${trimmed}" に一致するコマンドはありません`));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
console.log();
|
|
318
|
+
console.log(chalk_1.default.cyan.bold(` 検索結果: "${trimmed}"`));
|
|
319
|
+
console.log(chalk_1.default.gray(` help <command> で各コマンドの詳細を表示`));
|
|
320
|
+
console.log();
|
|
321
|
+
for (const [name, entry] of matches) {
|
|
322
|
+
const sub = entry.subcommands != null ? chalk_1.default.cyan("+ ") : " ";
|
|
323
|
+
const alias = Object.entries(exports.COMMAND_ALIASES).find(([, v]) => v === name)?.[0];
|
|
324
|
+
const aliasSuffix = alias != null ? chalk_1.default.gray(` (${alias})`) : "";
|
|
325
|
+
console.log(chalk_1.default.white(` ${sub}${name.padEnd(14)}`) + chalk_1.default.gray(entry.description) + aliasSuffix);
|
|
326
|
+
}
|
|
327
|
+
console.log();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
// 一覧モード
|
|
331
|
+
const currentValues = getCurrentSettingValues(ctx);
|
|
332
|
+
const displayed = new Set();
|
|
333
|
+
const categoryOrder = ["info", "status", "settings", "operation"];
|
|
334
|
+
const categories = filterCategory != null ? [filterCategory] : categoryOrder;
|
|
335
|
+
console.log();
|
|
336
|
+
console.log(chalk_1.default.cyan.bold(" 利用可能なコマンド:"));
|
|
337
|
+
console.log(chalk_1.default.gray(` help <command> で各コマンドの詳細を表示`));
|
|
338
|
+
for (const category of categories) {
|
|
339
|
+
console.log();
|
|
340
|
+
console.log(chalk_1.default.cyan(` [${types_2.CATEGORY_LABELS[category]}]`));
|
|
341
|
+
const commandNames = Object.keys(ctx.commands)
|
|
342
|
+
.filter((name) => name !== "exit" && name !== "?" && name !== "cmds" && ctx.commands[name].category === category)
|
|
343
|
+
.sort();
|
|
344
|
+
for (const name of commandNames) {
|
|
345
|
+
if (displayed.has(name))
|
|
346
|
+
continue;
|
|
347
|
+
displayed.add(name);
|
|
348
|
+
const entry = ctx.commands[name];
|
|
349
|
+
const sub = entry.subcommands != null ? chalk_1.default.cyan("+ ") : " ";
|
|
350
|
+
const setting = currentValues[name];
|
|
351
|
+
const valueSuffix = setting != null
|
|
352
|
+
? chalk_1.default.gray(" [") + chalk_1.default.yellow(setting.current) + chalk_1.default.gray("]")
|
|
353
|
+
: "";
|
|
354
|
+
const alias = Object.entries(exports.COMMAND_ALIASES).find(([, v]) => v === name)?.[0];
|
|
355
|
+
const aliasSuffix = alias != null ? chalk_1.default.gray(` (${alias})`) : "";
|
|
356
|
+
console.log(chalk_1.default.white(` ${sub}${name.padEnd(14)}`) + chalk_1.default.gray(entry.description) + aliasSuffix + valueSuffix);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
console.log();
|
|
360
|
+
console.log(chalk_1.default.cyan(" +") + chalk_1.default.gray(" = サブコマンドあり (help <command> で詳細表示)"));
|
|
361
|
+
console.log(chalk_1.default.gray(" カテゴリ絞り込み: commands <category> / 検索: commands <query>"));
|
|
362
|
+
console.log();
|
|
363
|
+
}
|
|
277
364
|
function handleHelp(ctx, args) {
|
|
278
365
|
const trimmed = args.trim();
|
|
279
366
|
if (trimmed.length > 0) {
|
|
@@ -284,13 +371,15 @@ function handleHelp(ctx, args) {
|
|
|
284
371
|
return;
|
|
285
372
|
}
|
|
286
373
|
if (parts.length > 1 && entry.subcommands) {
|
|
287
|
-
const
|
|
374
|
+
const subInput = parts[1].toLowerCase();
|
|
375
|
+
const subKey = Object.keys(entry.subcommands).find((k) => k.toLowerCase() === subInput);
|
|
376
|
+
const sub = subKey != null ? entry.subcommands[subKey] : undefined;
|
|
288
377
|
if (sub == null) {
|
|
289
378
|
console.log(chalk_1.default.yellow(` 不明なサブコマンド: ${parts[0]} ${parts[1]}`));
|
|
290
379
|
return;
|
|
291
380
|
}
|
|
292
381
|
console.log();
|
|
293
|
-
console.log(chalk_1.default.cyan.bold(` ${parts[0]} ${
|
|
382
|
+
console.log(chalk_1.default.cyan.bold(` ${parts[0]} ${subKey}`) + chalk_1.default.gray(` — ${sub.description}`));
|
|
294
383
|
if (sub.detail) {
|
|
295
384
|
console.log();
|
|
296
385
|
for (const line of sub.detail.split("\n")) {
|
|
@@ -321,45 +410,12 @@ function handleHelp(ctx, args) {
|
|
|
321
410
|
console.log();
|
|
322
411
|
return;
|
|
323
412
|
}
|
|
324
|
-
// help —
|
|
413
|
+
// help — 引数なしはガイド表示
|
|
325
414
|
console.log();
|
|
326
|
-
console.log(chalk_1.default.cyan.bold("
|
|
327
|
-
|
|
328
|
-
const displayed = new Set();
|
|
329
|
-
const categoryOrder = ["info", "status", "settings", "operation"];
|
|
330
|
-
for (const category of categoryOrder) {
|
|
331
|
-
console.log();
|
|
332
|
-
console.log(chalk_1.default.cyan(` [${types_2.CATEGORY_LABELS[category]}]`));
|
|
333
|
-
const commandNames = Object.keys(ctx.commands)
|
|
334
|
-
.filter((name) => name !== "exit" && name !== "?" && ctx.commands[name].category === category)
|
|
335
|
-
.sort();
|
|
336
|
-
for (const name of commandNames) {
|
|
337
|
-
const entry = ctx.commands[name];
|
|
338
|
-
if (displayed.has(entry.description))
|
|
339
|
-
continue;
|
|
340
|
-
displayed.add(entry.description);
|
|
341
|
-
const setting = currentValues[name];
|
|
342
|
-
const valueSuffix = setting != null
|
|
343
|
-
? chalk_1.default.gray(" [") + chalk_1.default.yellow(setting.current) + chalk_1.default.gray("]") +
|
|
344
|
-
(setting.options ? chalk_1.default.gray(` (${setting.options})`) : "")
|
|
345
|
-
: "";
|
|
346
|
-
const alias = Object.entries(exports.COMMAND_ALIASES)
|
|
347
|
-
.find(([, v]) => v === name)?.[0];
|
|
348
|
-
const aliasSuffix = alias != null ? chalk_1.default.gray(` (${alias})`) : "";
|
|
349
|
-
console.log(chalk_1.default.white(` ${name.padEnd(14)}`) + chalk_1.default.gray(entry.description) + aliasSuffix + valueSuffix);
|
|
350
|
-
if (entry.subcommands) {
|
|
351
|
-
const subNames = Object.keys(entry.subcommands).sort();
|
|
352
|
-
for (let i = 0; i < subNames.length; i++) {
|
|
353
|
-
const subName = subNames[i];
|
|
354
|
-
const sub = entry.subcommands[subName];
|
|
355
|
-
const prefix = i < subNames.length - 1 ? "├─" : "└─";
|
|
356
|
-
console.log(chalk_1.default.gray(` ${prefix} `) + chalk_1.default.white(subName.padEnd(10)) + chalk_1.default.gray(sub.description));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
415
|
+
console.log(chalk_1.default.cyan.bold(" help <command>") + chalk_1.default.gray(" — コマンドの詳細を表示"));
|
|
416
|
+
console.log(chalk_1.default.cyan.bold(" commands") + chalk_1.default.gray(" — コマンド一覧を表示"));
|
|
361
417
|
console.log();
|
|
362
|
-
console.log(chalk_1.default.gray("
|
|
418
|
+
console.log(chalk_1.default.gray(" 例: help notify, help test table, commands settings"));
|
|
363
419
|
console.log();
|
|
364
420
|
}
|
|
365
421
|
function handleStats(ctx) {
|
|
@@ -408,31 +408,35 @@ function handleFocus(ctx, args) {
|
|
|
408
408
|
}
|
|
409
409
|
function handleClock(ctx, args) {
|
|
410
410
|
const trimmed = args.trim();
|
|
411
|
+
const labels = {
|
|
412
|
+
clock: "現在時刻",
|
|
413
|
+
elapsed: "経過時間",
|
|
414
|
+
uptime: "稼働時間",
|
|
415
|
+
};
|
|
411
416
|
if (trimmed.length === 0) {
|
|
412
417
|
const current = ctx.statusLine.getClockMode();
|
|
413
|
-
const next = current === "elapsed" ? "clock" : "elapsed";
|
|
418
|
+
const next = current === "elapsed" ? "clock" : current === "clock" ? "uptime" : "elapsed";
|
|
414
419
|
ctx.statusLine.setClockMode(next);
|
|
415
420
|
ctx.config.promptClock = next;
|
|
416
421
|
ctx.updateConfig((c) => { c.promptClock = next; });
|
|
417
|
-
|
|
418
|
-
console.log(` プロンプト時計を ${label} に切り替えました。`);
|
|
422
|
+
console.log(` プロンプト時計を ${labels[next]} に切り替えました。`);
|
|
419
423
|
return;
|
|
420
424
|
}
|
|
421
425
|
const clockLower = trimmed.toLowerCase();
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
ctx.statusLine.setClockMode(
|
|
430
|
-
ctx.config.promptClock =
|
|
431
|
-
ctx.updateConfig((c) => { c.promptClock =
|
|
432
|
-
console.log(
|
|
426
|
+
const modeMap = {
|
|
427
|
+
elapsed: "elapsed",
|
|
428
|
+
now: "clock",
|
|
429
|
+
uptime: "uptime",
|
|
430
|
+
};
|
|
431
|
+
const mode = modeMap[clockLower];
|
|
432
|
+
if (mode) {
|
|
433
|
+
ctx.statusLine.setClockMode(mode);
|
|
434
|
+
ctx.config.promptClock = mode;
|
|
435
|
+
ctx.updateConfig((c) => { c.promptClock = mode; });
|
|
436
|
+
console.log(` プロンプト時計を ${labels[mode]} に変更しました。`);
|
|
433
437
|
}
|
|
434
438
|
else {
|
|
435
|
-
console.log(chalk_1.default.yellow(" elapsed
|
|
439
|
+
console.log(chalk_1.default.yellow(" elapsed, now, uptime のいずれかを指定してください。"));
|
|
436
440
|
}
|
|
437
441
|
}
|
|
438
442
|
function handleTipInterval(ctx, args) {
|
|
@@ -1,8 +1,42 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.formatStatsDuration = formatStatsDuration;
|
|
4
37
|
exports.displayStatistics = displayStatistics;
|
|
5
38
|
const formatter_1 = require("./formatter");
|
|
39
|
+
const theme = __importStar(require("./theme"));
|
|
6
40
|
// ── 定数 ──
|
|
7
41
|
const TYPE_LABELS = {
|
|
8
42
|
VXSE43: "緊急地震速報(警報)",
|
|
@@ -42,6 +76,14 @@ const CATEGORY_LABELS = {
|
|
|
42
76
|
nankaiTrough: "南海トラフ",
|
|
43
77
|
other: "その他",
|
|
44
78
|
};
|
|
79
|
+
const CATEGORY_ROLE = {
|
|
80
|
+
eew: "statsCategoryEew",
|
|
81
|
+
earthquake: "statsCategoryEarthquake",
|
|
82
|
+
tsunami: "statsCategoryTsunami",
|
|
83
|
+
volcano: "statsCategoryVolcano",
|
|
84
|
+
nankaiTrough: "statsCategoryNankaiTrough",
|
|
85
|
+
other: "statsCategoryOther",
|
|
86
|
+
};
|
|
45
87
|
const CATEGORY_ORDER = [
|
|
46
88
|
"eew",
|
|
47
89
|
"earthquake",
|
|
@@ -52,6 +94,13 @@ const CATEGORY_ORDER = [
|
|
|
52
94
|
];
|
|
53
95
|
const INTENSITY_ORDER = ["1", "2", "3", "4", "5-", "5+", "6-", "6+", "7"];
|
|
54
96
|
const FRAME_LEVEL = "info";
|
|
97
|
+
// ── chalk ショートカット ──
|
|
98
|
+
function muted(s) {
|
|
99
|
+
return theme.getRoleChalk("statsMuted")(s);
|
|
100
|
+
}
|
|
101
|
+
function count(s) {
|
|
102
|
+
return theme.getRoleChalk("statsCount")(s);
|
|
103
|
+
}
|
|
55
104
|
// ── 公開関数 ──
|
|
56
105
|
/** 経過時間をミリ秒から日本語の文字列に変換する */
|
|
57
106
|
function formatStatsDuration(ms) {
|
|
@@ -78,7 +127,7 @@ function displayStatistics(snapshot, now) {
|
|
|
78
127
|
const elapsedMs = effectiveNow.getTime() - snapshot.startTime.getTime();
|
|
79
128
|
if (snapshot.totalCount === 0) {
|
|
80
129
|
const title = "統計";
|
|
81
|
-
const msg = "まだ電文を受信していません";
|
|
130
|
+
const msg = muted("まだ電文を受信していません");
|
|
82
131
|
const width = calcWidth([title, msg]);
|
|
83
132
|
console.log((0, formatter_1.frameTop)(FRAME_LEVEL, width));
|
|
84
133
|
console.log((0, formatter_1.frameLine)(FRAME_LEVEL, title, width));
|
|
@@ -133,7 +182,7 @@ function calcWidth(contentLines) {
|
|
|
133
182
|
// frameLine adds 4 chars overhead (│ + space + space + │)
|
|
134
183
|
return Math.max(40, Math.min(200, maxContentWidth + 4));
|
|
135
184
|
}
|
|
136
|
-
/**
|
|
185
|
+
/** 最大震度内訳行を構築する(色付き) */
|
|
137
186
|
function buildIntBreakdownLine(earthquakeMaxIntByEvent) {
|
|
138
187
|
const intCounts = new Map();
|
|
139
188
|
for (const maxInt of earthquakeMaxIntByEvent.values()) {
|
|
@@ -141,12 +190,13 @@ function buildIntBreakdownLine(earthquakeMaxIntByEvent) {
|
|
|
141
190
|
}
|
|
142
191
|
const parts = [];
|
|
143
192
|
for (const intensity of INTENSITY_ORDER) {
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
|
|
193
|
+
const cnt = intCounts.get(intensity);
|
|
194
|
+
if (cnt != null && cnt > 0) {
|
|
195
|
+
const intStyle = (0, formatter_1.intensityColor)(intensity);
|
|
196
|
+
parts.push(`${intStyle(intensity)}${muted(":")}${cnt}`);
|
|
147
197
|
}
|
|
148
198
|
}
|
|
149
|
-
return ` 最大震度内訳 ${parts.join(" ")}`;
|
|
199
|
+
return ` ${muted("最大震度内訳")} ${parts.join(" ")}`;
|
|
150
200
|
}
|
|
151
201
|
/** 全コンテンツ行を構築する (__DIVIDER__ はフレーム区切り線のセンチネル) */
|
|
152
202
|
function buildAllContentLines(snapshot, activeCategories, typesByCategory, elapsedMs, countWidth) {
|
|
@@ -159,21 +209,21 @@ function buildAllContentLines(snapshot, activeCategories, typesByCategory, elaps
|
|
|
159
209
|
hour: "2-digit",
|
|
160
210
|
minute: "2-digit",
|
|
161
211
|
});
|
|
162
|
-
lines.push(
|
|
212
|
+
lines.push(`${muted("開始:")} ${startStr} ${muted("経過:")} ${formatStatsDuration(elapsedMs)} ${muted("合計:")} ${count(String(snapshot.totalCount))}件`);
|
|
163
213
|
// カテゴリセクション
|
|
164
|
-
for (
|
|
165
|
-
const category = activeCategories[i];
|
|
214
|
+
for (const category of activeCategories) {
|
|
166
215
|
lines.push("__DIVIDER__");
|
|
167
216
|
// カテゴリヘッダー
|
|
168
217
|
const types = typesByCategory.get(category) ?? [];
|
|
169
218
|
const categoryCount = types.reduce((sum, t) => sum + (snapshot.countByType.get(t) ?? 0), 0);
|
|
170
219
|
const catLabel = CATEGORY_LABELS[category];
|
|
220
|
+
const catStyle = theme.getRoleChalk(CATEGORY_ROLE[category]);
|
|
171
221
|
let catHeader;
|
|
172
222
|
if (category === "eew") {
|
|
173
|
-
catHeader = `[${catLabel}] ${categoryCount}件 / ${snapshot.eewEventCount}イベント`;
|
|
223
|
+
catHeader = `${catStyle(`[${catLabel}]`)} ${count(String(categoryCount))}件 / ${count(String(snapshot.eewEventCount))}イベント`;
|
|
174
224
|
}
|
|
175
225
|
else {
|
|
176
|
-
catHeader = `[${catLabel}] ${categoryCount}件`;
|
|
226
|
+
catHeader = `${catStyle(`[${catLabel}]`)} ${count(String(categoryCount))}件`;
|
|
177
227
|
}
|
|
178
228
|
lines.push(catHeader);
|
|
179
229
|
// タイプ行
|
|
@@ -190,14 +240,14 @@ function buildAllContentLines(snapshot, activeCategories, typesByCategory, elaps
|
|
|
190
240
|
maxLabelWidth = (0, formatter_1.visualWidth)(label);
|
|
191
241
|
}
|
|
192
242
|
for (const headType of types) {
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
243
|
+
const cnt = snapshot.countByType.get(headType) ?? 0;
|
|
244
|
+
if (cnt === 0)
|
|
195
245
|
continue;
|
|
196
246
|
const label = TYPE_LABELS[headType] ?? headType;
|
|
197
247
|
const typePad = " ".repeat(Math.max(0, maxTypeWidth - (0, formatter_1.visualWidth)(headType)));
|
|
198
248
|
const labelPad = " ".repeat(Math.max(0, maxLabelWidth - (0, formatter_1.visualWidth)(label)));
|
|
199
|
-
const countStr = String(
|
|
200
|
-
lines.push(` ${headType}${typePad} ${label}${labelPad} : ${countStr}`);
|
|
249
|
+
const countStr = String(cnt).padStart(countWidth);
|
|
250
|
+
lines.push(` ${muted(headType)}${typePad} ${label}${labelPad} ${muted(":")} ${count(countStr)}`);
|
|
201
251
|
}
|
|
202
252
|
// 地震カテゴリの場合は最大震度内訳を追加
|
|
203
253
|
if (category === "earthquake" && snapshot.earthquakeMaxIntByEvent.size > 0) {
|
package/dist/ui/status-line.js
CHANGED
|
@@ -45,6 +45,17 @@ class StatusLine {
|
|
|
45
45
|
*/
|
|
46
46
|
buildPrefix(options) {
|
|
47
47
|
const suffix = options?.noSuffix ? "" : chalk_1.default.gray("]> ");
|
|
48
|
+
// uptime モードは接続状態に依存しない
|
|
49
|
+
if (this.clockMode === "uptime") {
|
|
50
|
+
const dot = this.connectedAt == null
|
|
51
|
+
? chalk_1.default.gray("○")
|
|
52
|
+
: this.pulseOn ? chalk_1.default.cyan("●") : chalk_1.default.gray("○");
|
|
53
|
+
return (chalk_1.default.gray("FlEq [") +
|
|
54
|
+
dot +
|
|
55
|
+
chalk_1.default.gray(" ") +
|
|
56
|
+
(0, formatter_1.formatUptime)(process.uptime() * 1000) +
|
|
57
|
+
suffix);
|
|
58
|
+
}
|
|
48
59
|
if (this.connectedAt == null) {
|
|
49
60
|
return (chalk_1.default.gray("FlEq [") + chalk_1.default.gray("○ --:--:--") + suffix);
|
|
50
61
|
}
|
package/dist/ui/test-samples.js
CHANGED
|
@@ -116,6 +116,7 @@ exports.SAMPLE_EARTHQUAKE = {
|
|
|
116
116
|
reportDateTime: "2024/01/01 00:00:00",
|
|
117
117
|
headline: "1日00時00分ころ、地震がありました。",
|
|
118
118
|
publishingOffice: "気象庁",
|
|
119
|
+
eventId: "20240101000000",
|
|
119
120
|
earthquake: {
|
|
120
121
|
originTime: "2024/01/01 00:00:00",
|
|
121
122
|
hypocenterName: "石川県能登地方",
|
|
@@ -280,6 +281,7 @@ const FALLBACK_EARTHQUAKE_WARNING = {
|
|
|
280
281
|
reportDateTime: "2024/01/02 10:00:00",
|
|
281
282
|
headline: "長野県北部で震度4を観測しました。",
|
|
282
283
|
publishingOffice: "気象庁",
|
|
284
|
+
eventId: null,
|
|
283
285
|
earthquake: {
|
|
284
286
|
originTime: "2024/01/02 09:58:00",
|
|
285
287
|
hypocenterName: "長野県北部",
|
|
@@ -305,6 +307,7 @@ const FALLBACK_EARTHQUAKE_CANCEL = {
|
|
|
305
307
|
reportDateTime: "2024/01/02 10:05:00",
|
|
306
308
|
headline: "先ほどの地震情報を取り消します。",
|
|
307
309
|
publishingOffice: "気象庁",
|
|
310
|
+
eventId: null,
|
|
308
311
|
isTest: true,
|
|
309
312
|
};
|
|
310
313
|
const FALLBACK_EARTHQUAKE_ENCHI = {
|
|
@@ -314,6 +317,7 @@ const FALLBACK_EARTHQUAKE_ENCHI = {
|
|
|
314
317
|
reportDateTime: "2024/01/03 08:20:00",
|
|
315
318
|
headline: "日本への津波の影響はありません。",
|
|
316
319
|
publishingOffice: "気象庁",
|
|
320
|
+
eventId: null,
|
|
317
321
|
earthquake: {
|
|
318
322
|
originTime: "2024/01/03 08:10:00",
|
|
319
323
|
hypocenterName: "台湾付近",
|
|
@@ -332,6 +336,7 @@ const FALLBACK_EARTHQUAKE_SHINDO = {
|
|
|
332
336
|
reportDateTime: "2024/01/04 14:00:00",
|
|
333
337
|
headline: "各地の震度に関する情報です。",
|
|
334
338
|
publishingOffice: "気象庁",
|
|
339
|
+
eventId: null,
|
|
335
340
|
intensity: {
|
|
336
341
|
maxInt: "5弱",
|
|
337
342
|
areas: [
|
|
@@ -349,6 +354,7 @@ const FALLBACK_EARTHQUAKE_LG = {
|
|
|
349
354
|
reportDateTime: "2024/01/05 19:30:00",
|
|
350
355
|
headline: "関東地方で長周期地震動階級4を観測しました。",
|
|
351
356
|
publishingOffice: "気象庁",
|
|
357
|
+
eventId: null,
|
|
352
358
|
earthquake: {
|
|
353
359
|
originTime: "2024/01/05 19:27:00",
|
|
354
360
|
hypocenterName: "千葉県北西部",
|
package/dist/ui/theme.js
CHANGED
|
@@ -189,6 +189,15 @@ exports.DEFAULT_ROLES = {
|
|
|
189
189
|
// volcano: バナー
|
|
190
190
|
volcanoAlertBanner: { bg: "vermillion", fg: "#FFFFFF", bold: true },
|
|
191
191
|
volcanoFlashBanner: { bg: "darkRed", fg: "#FFFFFF", bold: true },
|
|
192
|
+
// stats: 統計表示
|
|
193
|
+
statsMuted: "gray",
|
|
194
|
+
statsCount: { fg: "sky", bold: true },
|
|
195
|
+
statsCategoryEew: { fg: "sky", bold: true },
|
|
196
|
+
statsCategoryEarthquake: { fg: "blue", bold: true },
|
|
197
|
+
statsCategoryTsunami: { fg: "blueGreen", bold: true },
|
|
198
|
+
statsCategoryVolcano: { fg: "orange", bold: true },
|
|
199
|
+
statsCategoryNankaiTrough: { fg: "vermillion", bold: true },
|
|
200
|
+
statsCategoryOther: { fg: "gray", bold: true },
|
|
192
201
|
};
|
|
193
202
|
/** ロール名の一覧 */
|
|
194
203
|
const ROLE_NAMES = Object.keys(exports.DEFAULT_ROLES);
|