@sayue_ltr/fleq 1.50.0 → 1.51.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.
- package/CHANGELOG.md +103 -0
- package/README.md +47 -5
- package/dist/config.js +35 -2
- package/dist/dmdata/rest-client.js +58 -3
- package/dist/dmdata/telegram-parser.js +37 -59
- package/dist/dmdata/ws-client.js +49 -18
- package/dist/engine/cli/cli-run.js +71 -1
- package/dist/engine/cli/cli.js +12 -0
- package/dist/engine/filter/compile-filter.js +21 -0
- package/dist/engine/filter/compiler.js +188 -0
- package/dist/engine/filter/errors.js +41 -0
- package/dist/engine/filter/field-registry.js +78 -0
- package/dist/engine/filter/index.js +15 -0
- package/dist/engine/filter/parser.js +137 -0
- package/dist/engine/filter/rank-maps.js +34 -0
- package/dist/engine/filter/tokenizer.js +121 -0
- package/dist/engine/filter/type-checker.js +104 -0
- package/dist/engine/filter/types.js +2 -0
- package/dist/engine/filter-template/pipeline-controller.js +73 -0
- package/dist/engine/filter-template/pipeline.js +16 -0
- package/dist/engine/messages/display-callbacks.js +7 -0
- package/dist/engine/messages/message-router.js +114 -182
- package/dist/engine/messages/summary-tracker.js +106 -0
- package/dist/engine/messages/telegram-stats.js +103 -0
- package/dist/engine/messages/volcano-route-handler.js +122 -0
- package/dist/engine/monitor/monitor.js +51 -3
- package/dist/engine/monitor/shutdown.js +1 -0
- package/dist/engine/notification/notifier.js +16 -1
- package/dist/engine/notification/sound-player.js +193 -28
- package/dist/engine/presentation/diff-store.js +158 -0
- package/dist/engine/presentation/diff-types.js +2 -0
- package/dist/engine/presentation/events/from-earthquake.js +53 -0
- package/dist/engine/presentation/events/from-eew.js +72 -0
- package/dist/engine/presentation/events/from-lg-observation.js +58 -0
- package/dist/engine/presentation/events/from-nankai-trough.js +39 -0
- package/dist/engine/presentation/events/from-raw.js +35 -0
- package/dist/engine/presentation/events/from-seismic-text.js +37 -0
- package/dist/engine/presentation/events/from-tsunami.js +51 -0
- package/dist/engine/presentation/events/from-volcano.js +88 -0
- package/dist/engine/presentation/events/to-presentation-event.js +32 -0
- package/dist/engine/presentation/level-helpers.js +118 -0
- package/dist/engine/presentation/processors/process-earthquake.js +36 -0
- package/dist/engine/presentation/processors/process-eew.js +90 -0
- package/dist/engine/presentation/processors/process-lg-observation.js +30 -0
- package/dist/engine/presentation/processors/process-message.js +53 -0
- package/dist/engine/presentation/processors/process-nankai-trough.js +30 -0
- package/dist/engine/presentation/processors/process-raw.js +22 -0
- package/dist/engine/presentation/processors/process-seismic-text.js +30 -0
- package/dist/engine/presentation/processors/process-tsunami.js +42 -0
- package/dist/engine/presentation/processors/process-volcano.js +41 -0
- package/dist/engine/presentation/types.js +2 -0
- package/dist/engine/startup/config-resolver.js +2 -0
- package/dist/engine/template/compile-template.js +18 -0
- package/dist/engine/template/compiler.js +102 -0
- package/dist/engine/template/field-accessor.js +25 -0
- package/dist/engine/template/filters.js +94 -0
- package/dist/engine/template/index.js +5 -0
- package/dist/engine/template/parser.js +190 -0
- package/dist/engine/template/tokenizer.js +96 -0
- package/dist/engine/template/types.js +2 -0
- package/dist/types.js +2 -1
- package/dist/ui/display-adapter.js +60 -0
- package/dist/ui/earthquake-formatter.js +17 -5
- package/dist/ui/eew-formatter.js +25 -10
- package/dist/ui/formatter.js +67 -32
- package/dist/ui/minimap/grid-layout.js +91 -0
- package/dist/ui/minimap/index.js +16 -0
- package/dist/ui/minimap/minimap-renderer.js +277 -0
- package/dist/ui/minimap/pref-mapping.js +82 -0
- package/dist/ui/minimap/types.js +2 -0
- package/dist/ui/night-overlay.js +56 -0
- package/dist/ui/repl-handlers/command-definitions.js +320 -0
- package/dist/ui/repl-handlers/index.js +11 -0
- package/dist/ui/repl-handlers/info-handlers.js +577 -0
- package/dist/ui/repl-handlers/operation-handlers.js +233 -0
- package/dist/ui/repl-handlers/settings-handlers.js +923 -0
- package/dist/ui/repl-handlers/types.js +10 -0
- package/dist/ui/repl.js +81 -1752
- package/dist/ui/statistics-formatter.js +208 -0
- package/dist/ui/status-line.js +69 -0
- package/dist/ui/summary/index.js +5 -0
- package/dist/ui/summary/summary-line.js +18 -0
- package/dist/ui/summary/summary-model.js +31 -0
- package/dist/ui/summary/token-builders.js +317 -0
- package/dist/ui/summary/types.js +2 -0
- package/dist/ui/summary/width-fit.js +41 -0
- package/dist/ui/summary-interval-formatter.js +72 -0
- package/dist/ui/theme.js +34 -5
- package/dist/ui/tip-shuffler.js +81 -0
- package/dist/ui/volcano-formatter.js +15 -13
- package/dist/ui/waiting-tips.js +289 -249
- package/package.json +1 -1
|
@@ -0,0 +1,923 @@
|
|
|
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.handleNotify = handleNotify;
|
|
40
|
+
exports.handleTableWidth = handleTableWidth;
|
|
41
|
+
exports.handleInfoText = handleInfoText;
|
|
42
|
+
exports.handleMode = handleMode;
|
|
43
|
+
exports.handleFilter = handleFilter;
|
|
44
|
+
exports.handleFocus = handleFocus;
|
|
45
|
+
exports.handleClock = handleClock;
|
|
46
|
+
exports.handleTipInterval = handleTipInterval;
|
|
47
|
+
exports.handleNight = handleNight;
|
|
48
|
+
exports.handleSummary = handleSummary;
|
|
49
|
+
exports.handleSound = handleSound;
|
|
50
|
+
exports.handleFold = handleFold;
|
|
51
|
+
exports.handleLimit = handleLimit;
|
|
52
|
+
exports.handleMute = handleMute;
|
|
53
|
+
exports.handleEewLog = handleEewLog;
|
|
54
|
+
exports.handleTheme = handleTheme;
|
|
55
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
56
|
+
const types_1 = require("../../types");
|
|
57
|
+
const config_1 = require("../../config");
|
|
58
|
+
const notifier_1 = require("../../engine/notification/notifier");
|
|
59
|
+
const formatter_1 = require("../formatter");
|
|
60
|
+
const themeModule = __importStar(require("../theme"));
|
|
61
|
+
const filter_1 = require("../../engine/filter");
|
|
62
|
+
const summary_tracker_1 = require("../../engine/messages/summary-tracker");
|
|
63
|
+
const summary_interval_formatter_1 = require("../summary-interval-formatter");
|
|
64
|
+
const info_handlers_1 = require("./info-handlers");
|
|
65
|
+
/** EEW ログ記録項目の表示ラベル */
|
|
66
|
+
const EEW_LOG_FIELD_LABELS = {
|
|
67
|
+
hypocenter: "震源情報",
|
|
68
|
+
originTime: "発生時刻",
|
|
69
|
+
coordinates: "緯度・経度",
|
|
70
|
+
magnitude: "M値・深さ",
|
|
71
|
+
forecastIntensity: "最大予測震度",
|
|
72
|
+
maxLgInt: "最大予測長周期階級",
|
|
73
|
+
forecastAreas: "予測震度地域リスト",
|
|
74
|
+
lgIntensity: "地域別長周期階級",
|
|
75
|
+
isPlum: "PLUM法フラグ",
|
|
76
|
+
hasArrived: "主要動到達フラグ",
|
|
77
|
+
diff: "差分情報",
|
|
78
|
+
maxIntChangeReason: "震度変化理由",
|
|
79
|
+
};
|
|
80
|
+
/** EEW ログ記録項目のグループ定義 */
|
|
81
|
+
const EEW_LOG_FIELD_GROUPS = [
|
|
82
|
+
{ label: "震源", fields: ["hypocenter", "originTime", "coordinates"] },
|
|
83
|
+
{ label: "規模", fields: ["magnitude"] },
|
|
84
|
+
{ label: "変化", fields: ["diff", "maxIntChangeReason"] },
|
|
85
|
+
{ label: "予測概要", fields: ["forecastIntensity", "maxLgInt"] },
|
|
86
|
+
{ label: "予測地域", fields: ["forecastAreas", "lgIntensity", "isPlum", "hasArrived"] },
|
|
87
|
+
];
|
|
88
|
+
/** 省略上限キーの日本語ラベル */
|
|
89
|
+
const TRUNCATION_LABELS = {
|
|
90
|
+
seismicTextLines: "地震テキスト本文",
|
|
91
|
+
nankaiTroughLines: "南海トラフ本文",
|
|
92
|
+
volcanoAlertLines: "火山警報本文",
|
|
93
|
+
volcanoEruptionLines: "火山観測報本文",
|
|
94
|
+
volcanoTextLines: "火山解説情報本文",
|
|
95
|
+
volcanoAshfallQuickLines: "降灰速報(VFVO54)本文",
|
|
96
|
+
volcanoAshfallDetailLines: "降灰詳細(VFVO55)本文",
|
|
97
|
+
volcanoAshfallRegularLines: "降灰定時(VFVO53)本文",
|
|
98
|
+
volcanoPreventionLines: "火山警報 防災事項",
|
|
99
|
+
ashfallAreasQuick: "降灰速報(VFVO54) 地域数",
|
|
100
|
+
ashfallAreasOther: "降灰予報 地域数",
|
|
101
|
+
ashfallPeriodsQuick: "降灰速報(VFVO54) 時間帯数",
|
|
102
|
+
ashfallPeriodsOther: "降灰予報 時間帯数",
|
|
103
|
+
plumeWindSampleRows: "噴煙流向報 風向データ行数",
|
|
104
|
+
tsunamiCompactForecastAreas: "津波compact 予報地域数",
|
|
105
|
+
};
|
|
106
|
+
/** 時間文字列をミリ秒に変換 (例: "30m" → 1800000, "1h" → 3600000, "90s" → 90000) */
|
|
107
|
+
function parseDuration(input) {
|
|
108
|
+
const match = input.match(/^(\d+)(s|m|h)$/);
|
|
109
|
+
if (!match)
|
|
110
|
+
return null;
|
|
111
|
+
const value = parseInt(match[1], 10);
|
|
112
|
+
const unit = match[2];
|
|
113
|
+
switch (unit) {
|
|
114
|
+
case "s": return value * 1000;
|
|
115
|
+
case "m": return value * 60 * 1000;
|
|
116
|
+
case "h": return value * 60 * 60 * 1000;
|
|
117
|
+
default: return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** ミリ秒を人間可読な時間文字列に変換 */
|
|
121
|
+
function formatDuration(ms) {
|
|
122
|
+
const totalSec = Math.ceil(ms / 1000);
|
|
123
|
+
if (totalSec < 60)
|
|
124
|
+
return `${totalSec}秒`;
|
|
125
|
+
const min = Math.floor(totalSec / 60);
|
|
126
|
+
const sec = totalSec % 60;
|
|
127
|
+
if (min < 60)
|
|
128
|
+
return sec > 0 ? `${min}分${sec}秒` : `${min}分`;
|
|
129
|
+
const hour = Math.floor(min / 60);
|
|
130
|
+
const remMin = min % 60;
|
|
131
|
+
return remMin > 0 ? `${hour}時間${remMin}分` : `${hour}時間`;
|
|
132
|
+
}
|
|
133
|
+
/** フィルタのエラー表示 */
|
|
134
|
+
function printFilterError(err) {
|
|
135
|
+
if (err instanceof filter_1.FilterSyntaxError) {
|
|
136
|
+
console.log(chalk_1.default.red(` ${err.format()}`));
|
|
137
|
+
}
|
|
138
|
+
else if (err instanceof filter_1.FilterFieldError) {
|
|
139
|
+
console.log(chalk_1.default.red(` ${err.format()}`));
|
|
140
|
+
}
|
|
141
|
+
else if (err instanceof filter_1.FilterTypeError) {
|
|
142
|
+
console.log(chalk_1.default.red(` ${err.message}`));
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
console.log(chalk_1.default.red(` エラー: ${err instanceof Error ? err.message : err}`));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** カテゴリ名を解決する (case-insensitive + エイリアス) */
|
|
149
|
+
function resolveNotifyCategory(input) {
|
|
150
|
+
const lower = input.toLowerCase();
|
|
151
|
+
for (const cat of Object.keys(notifier_1.NOTIFY_CATEGORY_LABELS)) {
|
|
152
|
+
if (cat.toLowerCase() === lower)
|
|
153
|
+
return cat;
|
|
154
|
+
}
|
|
155
|
+
return info_handlers_1.CATEGORY_ALIASES[lower] ?? null;
|
|
156
|
+
}
|
|
157
|
+
// ── コマンドハンドラ ──
|
|
158
|
+
function handleNotify(ctx, args) {
|
|
159
|
+
const trimmed = args.trim();
|
|
160
|
+
if (trimmed.length === 0) {
|
|
161
|
+
const settings = ctx.notifier.getSettings();
|
|
162
|
+
console.log();
|
|
163
|
+
console.log(chalk_1.default.cyan.bold(" 通知設定:"));
|
|
164
|
+
if (ctx.notifier.isMuted()) {
|
|
165
|
+
const remaining = ctx.notifier.muteRemaining();
|
|
166
|
+
console.log(chalk_1.default.yellow(` (ミュート中: 残り ${formatDuration(remaining)})`));
|
|
167
|
+
}
|
|
168
|
+
console.log();
|
|
169
|
+
for (const [cat, label] of Object.entries(notifier_1.NOTIFY_CATEGORY_LABELS)) {
|
|
170
|
+
const enabled = settings[cat];
|
|
171
|
+
const status = enabled
|
|
172
|
+
? chalk_1.default.green("ON")
|
|
173
|
+
: chalk_1.default.red("OFF");
|
|
174
|
+
const alias = Object.entries(info_handlers_1.CATEGORY_ALIASES)
|
|
175
|
+
.find(([, v]) => v === cat)?.[0];
|
|
176
|
+
const aliasPart = alias != null ? chalk_1.default.gray(` (${alias})`) : "";
|
|
177
|
+
console.log(chalk_1.default.white(` ${cat.padEnd(14)}`) +
|
|
178
|
+
chalk_1.default.gray(`${label} `) +
|
|
179
|
+
status +
|
|
180
|
+
aliasPart);
|
|
181
|
+
}
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(chalk_1.default.gray(" 使い方: notify <category> [on|off] / notify all:on / notify all:off"));
|
|
184
|
+
console.log();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const lower = trimmed.toLowerCase();
|
|
188
|
+
if (lower === "all:on" || lower === "aon") {
|
|
189
|
+
ctx.notifier.setAll(true);
|
|
190
|
+
console.log(chalk_1.default.green(" 全通知を有効にしました"));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (lower === "all:off" || lower === "aoff") {
|
|
194
|
+
ctx.notifier.setAll(false);
|
|
195
|
+
console.log(chalk_1.default.yellow(" 全通知を無効にしました"));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const parts = trimmed.split(/\s+/);
|
|
199
|
+
const cat = resolveNotifyCategory(parts[0]);
|
|
200
|
+
const action = parts[1]?.toLowerCase();
|
|
201
|
+
if (cat == null) {
|
|
202
|
+
console.log(chalk_1.default.yellow(` 不明なカテゴリ: ${parts[0]}`) +
|
|
203
|
+
chalk_1.default.gray(` (有効: ${Object.keys(notifier_1.NOTIFY_CATEGORY_LABELS).join(", ")})`));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
let newState;
|
|
207
|
+
if (action === "on") {
|
|
208
|
+
const settings = ctx.notifier.getSettings();
|
|
209
|
+
if (settings[cat]) {
|
|
210
|
+
console.log(` ${notifier_1.NOTIFY_CATEGORY_LABELS[cat]} (${cat}): 既に ${chalk_1.default.green("ON")} です`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
newState = ctx.notifier.toggleCategory(cat);
|
|
214
|
+
}
|
|
215
|
+
else if (action === "off") {
|
|
216
|
+
const settings = ctx.notifier.getSettings();
|
|
217
|
+
if (!settings[cat]) {
|
|
218
|
+
console.log(` ${notifier_1.NOTIFY_CATEGORY_LABELS[cat]} (${cat}): 既に ${chalk_1.default.red("OFF")} です`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
newState = ctx.notifier.toggleCategory(cat);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
newState = ctx.notifier.toggleCategory(cat);
|
|
225
|
+
}
|
|
226
|
+
const label = notifier_1.NOTIFY_CATEGORY_LABELS[cat];
|
|
227
|
+
const status = newState ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
228
|
+
console.log(` ${label} (${cat}): ${status}`);
|
|
229
|
+
}
|
|
230
|
+
function handleTableWidth(ctx, args) {
|
|
231
|
+
const trimmed = args.trim();
|
|
232
|
+
if (trimmed.length === 0) {
|
|
233
|
+
if (ctx.config.tableWidth == null) {
|
|
234
|
+
const cols = process.stdout.columns ?? 60;
|
|
235
|
+
console.log(` 現在のテーブル幅: auto (ターミナル幅: ${cols})`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
console.log(` 現在のテーブル幅: ${ctx.config.tableWidth} (固定)`);
|
|
239
|
+
}
|
|
240
|
+
console.log(chalk_1.default.gray(" 使い方: tablewidth <40〜200> / tablewidth auto"));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (trimmed.toLowerCase() === "auto") {
|
|
244
|
+
ctx.config.tableWidth = null;
|
|
245
|
+
(0, formatter_1.clearFrameWidth)();
|
|
246
|
+
ctx.updateConfig((c) => { delete c.tableWidth; });
|
|
247
|
+
const cols = process.stdout.columns ?? 60;
|
|
248
|
+
console.log(` テーブル幅を auto に変更しました。(現在のターミナル幅: ${cols})`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const width = Number(trimmed);
|
|
252
|
+
if (isNaN(width) || !Number.isInteger(width) || width < 40 || width > 200) {
|
|
253
|
+
console.log(chalk_1.default.yellow(" tableWidth は 40〜200 の整数、または auto を指定してください。"));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
ctx.config.tableWidth = width;
|
|
257
|
+
(0, formatter_1.setFrameWidth)(width);
|
|
258
|
+
ctx.updateConfig((c) => { c.tableWidth = width; });
|
|
259
|
+
console.log(` テーブル幅を ${width} に変更しました。`);
|
|
260
|
+
}
|
|
261
|
+
function handleInfoText(ctx, args) {
|
|
262
|
+
const trimmed = args.trim();
|
|
263
|
+
if (trimmed.length === 0) {
|
|
264
|
+
const current = ctx.config.infoFullText ? "full (全文表示)" : "short (省略表示)";
|
|
265
|
+
console.log(` お知らせ電文表示: ${current}`);
|
|
266
|
+
console.log(chalk_1.default.gray(" 使い方: infotext full / infotext short"));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const infoLower = trimmed.toLowerCase();
|
|
270
|
+
if (infoLower === "full") {
|
|
271
|
+
ctx.config.infoFullText = true;
|
|
272
|
+
(0, formatter_1.setInfoFullText)(true);
|
|
273
|
+
ctx.updateConfig((c) => { c.infoFullText = true; });
|
|
274
|
+
console.log(" お知らせ電文を全文表示に変更しました。");
|
|
275
|
+
}
|
|
276
|
+
else if (infoLower === "short") {
|
|
277
|
+
ctx.config.infoFullText = false;
|
|
278
|
+
(0, formatter_1.setInfoFullText)(false);
|
|
279
|
+
ctx.updateConfig((c) => { c.infoFullText = false; });
|
|
280
|
+
console.log(" お知らせ電文を省略表示に変更しました。");
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
console.log(chalk_1.default.yellow(" full または short を指定してください。"));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function handleMode(ctx, args) {
|
|
287
|
+
const trimmed = args.trim();
|
|
288
|
+
if (trimmed.length === 0) {
|
|
289
|
+
const current = (0, formatter_1.getDisplayMode)();
|
|
290
|
+
console.log(` 表示モード: ${current}`);
|
|
291
|
+
console.log(chalk_1.default.gray(" 使い方: mode normal / mode compact"));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const modeLower = trimmed.toLowerCase();
|
|
295
|
+
if (modeLower !== "normal" && modeLower !== "compact") {
|
|
296
|
+
console.log(chalk_1.default.yellow(` 無効なモード: ${trimmed}`) + chalk_1.default.gray(" (normal / compact)"));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const mode = modeLower;
|
|
300
|
+
ctx.config.displayMode = mode;
|
|
301
|
+
(0, formatter_1.setDisplayMode)(mode);
|
|
302
|
+
ctx.updateConfig((c) => { c.displayMode = mode; });
|
|
303
|
+
console.log(` 表示モードを ${mode} に変更しました。`);
|
|
304
|
+
}
|
|
305
|
+
function handleFilter(ctx, args) {
|
|
306
|
+
const trimmed = args.trim();
|
|
307
|
+
const ctrl = ctx.pipelineController;
|
|
308
|
+
if (trimmed.length === 0) {
|
|
309
|
+
if (ctrl?.getPipeline().filter == null) {
|
|
310
|
+
console.log(` フィルタ: ${chalk_1.default.gray("無効")}`);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
console.log(` フィルタ: ${chalk_1.default.green("有効")}`);
|
|
314
|
+
console.log(` 式: ${ctrl.getFilterExpr() ?? "(CLI起動時に設定)"}`);
|
|
315
|
+
if (ctx.filterUpdatedAt != null) {
|
|
316
|
+
const ts = ctx.filterUpdatedAt.toLocaleString("ja-JP");
|
|
317
|
+
console.log(` 最終更新: ${ts}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
console.log(chalk_1.default.gray(" 使い方: filter set <expr> / filter clear / filter test <expr>"));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const [sub, ...rest] = trimmed.split(/\s+/);
|
|
324
|
+
const subLower = sub.toLowerCase();
|
|
325
|
+
if (subLower === "clear") {
|
|
326
|
+
ctrl?.clearFilter();
|
|
327
|
+
ctx.filterExpr = null;
|
|
328
|
+
ctx.filterUpdatedAt = null;
|
|
329
|
+
console.log(" フィルタを解除しました。");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (subLower === "test") {
|
|
333
|
+
const expr = rest.join(" ").trim();
|
|
334
|
+
if (expr.length === 0) {
|
|
335
|
+
console.log(chalk_1.default.yellow(" 式を指定してください。") + chalk_1.default.gray(" 例: filter test domain = \"eew\""));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
(0, filter_1.compileFilter)(expr);
|
|
340
|
+
console.log(chalk_1.default.green(" 構文OK") + chalk_1.default.gray(` — ${expr}`));
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
printFilterError(err);
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (subLower === "set") {
|
|
348
|
+
const expr = rest.join(" ").trim();
|
|
349
|
+
if (expr.length === 0) {
|
|
350
|
+
console.log(chalk_1.default.yellow(" 式を指定してください。") + chalk_1.default.gray(" 例: filter set domain = \"eew\""));
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (ctrl == null) {
|
|
354
|
+
console.log(chalk_1.default.yellow(" フィルタパイプラインが利用できません。"));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
ctrl.setFilter(expr);
|
|
359
|
+
ctx.filterExpr = expr;
|
|
360
|
+
ctx.filterUpdatedAt = new Date();
|
|
361
|
+
console.log(chalk_1.default.green(" フィルタを適用しました。") + chalk_1.default.gray(` — ${expr}`));
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
printFilterError(err);
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
console.log(chalk_1.default.yellow(` 不明なサブコマンド: ${sub}`) + chalk_1.default.gray(" (set / clear / test)"));
|
|
369
|
+
}
|
|
370
|
+
function handleFocus(ctx, args) {
|
|
371
|
+
const trimmed = args.trim();
|
|
372
|
+
const ctrl = ctx.pipelineController;
|
|
373
|
+
if (trimmed.length === 0) {
|
|
374
|
+
if (ctrl?.getPipeline().focus == null) {
|
|
375
|
+
console.log(` フォーカス: ${chalk_1.default.gray("無効")}`);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
console.log(` フォーカス: ${chalk_1.default.green("有効")}`);
|
|
379
|
+
console.log(` 式: ${ctrl.getFocusExpr() ?? "(CLI起動時に設定)"}`);
|
|
380
|
+
if (ctx.focusUpdatedAt != null) {
|
|
381
|
+
const ts = ctx.focusUpdatedAt.toLocaleString("ja-JP");
|
|
382
|
+
console.log(` 最終更新: ${ts}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
console.log(chalk_1.default.gray(" 使い方: focus <expr> / focus off"));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (trimmed.toLowerCase() === "off") {
|
|
389
|
+
ctrl?.clearFocus();
|
|
390
|
+
ctx.focusExpr = null;
|
|
391
|
+
ctx.focusUpdatedAt = null;
|
|
392
|
+
console.log(" フォーカスを解除しました。");
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (ctrl == null) {
|
|
396
|
+
console.log(chalk_1.default.yellow(" フォーカスパイプラインが利用できません。"));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
ctrl.setFocus(trimmed);
|
|
401
|
+
ctx.focusExpr = trimmed;
|
|
402
|
+
ctx.focusUpdatedAt = new Date();
|
|
403
|
+
console.log(chalk_1.default.green(" フォーカスを適用しました。") + chalk_1.default.gray(` — ${trimmed}`));
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
printFilterError(err);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function handleClock(ctx, args) {
|
|
410
|
+
const trimmed = args.trim();
|
|
411
|
+
if (trimmed.length === 0) {
|
|
412
|
+
const current = ctx.statusLine.getClockMode();
|
|
413
|
+
const next = current === "elapsed" ? "clock" : "elapsed";
|
|
414
|
+
ctx.statusLine.setClockMode(next);
|
|
415
|
+
ctx.config.promptClock = next;
|
|
416
|
+
ctx.updateConfig((c) => { c.promptClock = next; });
|
|
417
|
+
const label = next === "clock" ? "現在時刻" : "経過時間";
|
|
418
|
+
console.log(` プロンプト時計を ${label} に切り替えました。`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const clockLower = trimmed.toLowerCase();
|
|
422
|
+
if (clockLower === "elapsed") {
|
|
423
|
+
ctx.statusLine.setClockMode("elapsed");
|
|
424
|
+
ctx.config.promptClock = "elapsed";
|
|
425
|
+
ctx.updateConfig((c) => { c.promptClock = "elapsed"; });
|
|
426
|
+
console.log(" プロンプト時計を 経過時間 に変更しました。");
|
|
427
|
+
}
|
|
428
|
+
else if (clockLower === "now") {
|
|
429
|
+
ctx.statusLine.setClockMode("clock");
|
|
430
|
+
ctx.config.promptClock = "clock";
|
|
431
|
+
ctx.updateConfig((c) => { c.promptClock = "clock"; });
|
|
432
|
+
console.log(" プロンプト時計を 現在時刻 に変更しました。");
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
console.log(chalk_1.default.yellow(" elapsed または now を指定してください。"));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function handleTipInterval(ctx, args) {
|
|
439
|
+
const trimmed = args.trim();
|
|
440
|
+
if (trimmed.length === 0) {
|
|
441
|
+
console.log(` 待機中ヒント間隔: ${ctx.config.waitTipIntervalMin}分`);
|
|
442
|
+
console.log(chalk_1.default.gray(" 使い方: tipinterval <0〜1440> (0で無効)"));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const min = Number(trimmed);
|
|
446
|
+
if (isNaN(min) || !Number.isInteger(min) || min < 0 || min > 1440) {
|
|
447
|
+
console.log(chalk_1.default.yellow(" tipinterval は 0〜1440 の整数を指定してください。"));
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
ctx.config.waitTipIntervalMin = min;
|
|
451
|
+
ctx.tipIntervalMs = min * 60 * 1000;
|
|
452
|
+
ctx.resetTipSchedule();
|
|
453
|
+
ctx.updateConfig((c) => { c.waitTipIntervalMin = min; });
|
|
454
|
+
if (min === 0) {
|
|
455
|
+
console.log(" 待機中ヒントを無効化しました。");
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
console.log(` 待機中ヒント間隔を ${min}分 に変更しました。`);
|
|
459
|
+
}
|
|
460
|
+
function handleNight(ctx, args) {
|
|
461
|
+
const trimmed = args.trim();
|
|
462
|
+
if (trimmed.length === 0) {
|
|
463
|
+
const current = themeModule.isNightMode();
|
|
464
|
+
const status = current ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
465
|
+
console.log(` ナイトモード: ${status}`);
|
|
466
|
+
console.log(chalk_1.default.gray(" 使い方: night on / night off"));
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const sub = trimmed.toLowerCase();
|
|
470
|
+
if (sub === "on") {
|
|
471
|
+
themeModule.setNightMode(true);
|
|
472
|
+
ctx.config.nightMode = true;
|
|
473
|
+
console.log(` ナイトモードを ${chalk_1.default.green("ON")} にしました。`);
|
|
474
|
+
}
|
|
475
|
+
else if (sub === "off") {
|
|
476
|
+
themeModule.setNightMode(false);
|
|
477
|
+
ctx.config.nightMode = false;
|
|
478
|
+
console.log(` ナイトモードを ${chalk_1.default.red("OFF")} にしました。`);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
console.log(chalk_1.default.yellow(" on または off を指定してください。"));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function handleSummary(ctx, args) {
|
|
485
|
+
if (ctx.summaryTracker == null) {
|
|
486
|
+
console.log(chalk_1.default.yellow(" 要約トラッカーが利用できません。"));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const trimmed = args.trim();
|
|
490
|
+
const parts = trimmed.split(/\s+/);
|
|
491
|
+
const sub = parts[0]?.toLowerCase() ?? "";
|
|
492
|
+
if (trimmed.length === 0) {
|
|
493
|
+
if (ctx.summaryIntervalMin != null) {
|
|
494
|
+
console.log(` 定期要約: ${chalk_1.default.green("ON")} (${ctx.summaryIntervalMin}分間隔)`);
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
console.log(` 定期要約: ${chalk_1.default.red("OFF")}`);
|
|
498
|
+
}
|
|
499
|
+
console.log(chalk_1.default.gray(" 使い方: summary on [N] / summary off / summary now"));
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (sub === "now") {
|
|
503
|
+
if (ctx.summaryTimerControl != null) {
|
|
504
|
+
ctx.summaryTimerControl.showNow();
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
const snapshot = ctx.summaryTracker.getSnapshot();
|
|
508
|
+
const output = (0, summary_interval_formatter_1.formatSummaryInterval)(snapshot, summary_tracker_1.WINDOW_MINUTES, true);
|
|
509
|
+
console.log(output);
|
|
510
|
+
}
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (sub === "on") {
|
|
514
|
+
const minuteStr = parts[1];
|
|
515
|
+
let minutes = 10;
|
|
516
|
+
if (minuteStr != null) {
|
|
517
|
+
const parsed = parseInt(minuteStr, 10);
|
|
518
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
519
|
+
minutes = parsed;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
ctx.summaryIntervalMin = minutes;
|
|
523
|
+
ctx.config.summaryInterval = minutes;
|
|
524
|
+
ctx.summaryTimerControl?.start(minutes);
|
|
525
|
+
console.log(` 定期要約を ${chalk_1.default.green("ON")} にしました (${minutes}分間隔)。`);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (sub === "off") {
|
|
529
|
+
ctx.summaryIntervalMin = null;
|
|
530
|
+
ctx.config.summaryInterval = null;
|
|
531
|
+
ctx.summaryTimerControl?.stop();
|
|
532
|
+
console.log(` 定期要約を ${chalk_1.default.red("OFF")} にしました。`);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
console.log(chalk_1.default.yellow(" 使い方: summary on [N] / summary off / summary now"));
|
|
536
|
+
}
|
|
537
|
+
function handleSound(ctx, args) {
|
|
538
|
+
const trimmed = args.trim();
|
|
539
|
+
if (trimmed.length === 0) {
|
|
540
|
+
const current = ctx.notifier.getSoundEnabled();
|
|
541
|
+
const status = current ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
542
|
+
console.log(` 通知音: ${status}`);
|
|
543
|
+
console.log(chalk_1.default.gray(" 使い方: sound on / sound off"));
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const soundLower = trimmed.toLowerCase();
|
|
547
|
+
if (soundLower === "on") {
|
|
548
|
+
ctx.notifier.setSoundEnabled(true);
|
|
549
|
+
console.log(` 通知音を ${chalk_1.default.green("ON")} にしました。`);
|
|
550
|
+
}
|
|
551
|
+
else if (soundLower === "off") {
|
|
552
|
+
ctx.notifier.setSoundEnabled(false);
|
|
553
|
+
console.log(` 通知音を ${chalk_1.default.red("OFF")} にしました。`);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
console.log(chalk_1.default.yellow(" on または off を指定してください。"));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function handleFold(ctx, args) {
|
|
560
|
+
const trimmed = args.trim();
|
|
561
|
+
if (trimmed.length === 0) {
|
|
562
|
+
const current = (0, formatter_1.getMaxObservations)();
|
|
563
|
+
if (current == null) {
|
|
564
|
+
console.log(" 観測点表示: 全件表示");
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
console.log(` 観測点表示: 上位 ${current} 件に制限`);
|
|
568
|
+
}
|
|
569
|
+
console.log(chalk_1.default.gray(" 使い方: fold <N> / fold off"));
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (trimmed.toLowerCase() === "off") {
|
|
573
|
+
(0, formatter_1.setMaxObservations)(null);
|
|
574
|
+
ctx.config.maxObservations = null;
|
|
575
|
+
ctx.updateConfig((c) => { delete c.maxObservations; });
|
|
576
|
+
console.log(" 観測点表示を全件表示に戻しました。");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
const n = Number(trimmed);
|
|
580
|
+
if (isNaN(n) || !Number.isInteger(n) || n < 1 || n > 999) {
|
|
581
|
+
console.log(chalk_1.default.yellow(" 1〜999 の整数、または off を指定してください。"));
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
(0, formatter_1.setMaxObservations)(n);
|
|
585
|
+
ctx.config.maxObservations = n;
|
|
586
|
+
ctx.updateConfig((c) => { c.maxObservations = n; });
|
|
587
|
+
console.log(` 観測点表示を上位 ${n} 件に制限しました。`);
|
|
588
|
+
}
|
|
589
|
+
function handleLimit(ctx, args) {
|
|
590
|
+
const trimmed = args.trim();
|
|
591
|
+
if (trimmed.length === 0) {
|
|
592
|
+
const current = (0, formatter_1.getTruncation)();
|
|
593
|
+
const defaults = types_1.DEFAULT_CONFIG.truncation;
|
|
594
|
+
console.log(" 省略表示の上限設定:");
|
|
595
|
+
console.log();
|
|
596
|
+
const linesKeys = [
|
|
597
|
+
"seismicTextLines", "nankaiTroughLines",
|
|
598
|
+
"volcanoAlertLines", "volcanoEruptionLines", "volcanoTextLines",
|
|
599
|
+
"volcanoAshfallQuickLines", "volcanoAshfallDetailLines", "volcanoAshfallRegularLines",
|
|
600
|
+
"volcanoPreventionLines",
|
|
601
|
+
];
|
|
602
|
+
const countKeys = [
|
|
603
|
+
"ashfallAreasQuick", "ashfallAreasOther",
|
|
604
|
+
"ashfallPeriodsQuick", "ashfallPeriodsOther",
|
|
605
|
+
"plumeWindSampleRows", "tsunamiCompactForecastAreas",
|
|
606
|
+
];
|
|
607
|
+
const printGroup = (label, keys) => {
|
|
608
|
+
console.log(chalk_1.default.gray(` [${label}]`));
|
|
609
|
+
for (const key of keys) {
|
|
610
|
+
const val = current[key];
|
|
611
|
+
const def = defaults[key];
|
|
612
|
+
const changed = val !== def;
|
|
613
|
+
const valStr = changed ? chalk_1.default.yellow(String(val)) : String(val);
|
|
614
|
+
const desc = TRUNCATION_LABELS[key];
|
|
615
|
+
console.log(` ${key.padEnd(30)} ${valStr.padStart(changed ? 14 : 4)} ${chalk_1.default.gray(`(default: ${def})`)} ${chalk_1.default.gray(desc)}`);
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
printGroup("本文行数", linesKeys);
|
|
619
|
+
console.log();
|
|
620
|
+
printGroup("件数", countKeys);
|
|
621
|
+
console.log();
|
|
622
|
+
console.log(chalk_1.default.gray(" 使い方: limit <key> <N> / limit <key> default / limit reset"));
|
|
623
|
+
console.log(chalk_1.default.gray(" ※ infotext full 時は本文行数制限は無効になります"));
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (trimmed.toLowerCase() === "reset") {
|
|
627
|
+
const defaults = { ...types_1.DEFAULT_CONFIG.truncation };
|
|
628
|
+
(0, formatter_1.setTruncation)(defaults);
|
|
629
|
+
ctx.config.truncation = defaults;
|
|
630
|
+
ctx.updateConfig((c) => { delete c.truncation; });
|
|
631
|
+
console.log(" 省略上限設定を全てデフォルトに戻しました。");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const parts = trimmed.split(/\s+/);
|
|
635
|
+
if (parts.length < 2) {
|
|
636
|
+
console.log(chalk_1.default.yellow(" 使い方: limit <key> <N> / limit <key> default / limit reset"));
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const [keyStr, valueStr] = parts;
|
|
640
|
+
if (!config_1.VALID_TRUNCATION_KEYS.includes(keyStr)) {
|
|
641
|
+
console.log(chalk_1.default.yellow(` 不明なキー: ${keyStr}`));
|
|
642
|
+
console.log(chalk_1.default.gray(" 有効なキー: limit で一覧表示"));
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
const tKey = keyStr;
|
|
646
|
+
if (valueStr.toLowerCase() === "default") {
|
|
647
|
+
const defaults = types_1.DEFAULT_CONFIG.truncation;
|
|
648
|
+
const newTrunc = { ...(0, formatter_1.getTruncation)(), [tKey]: defaults[tKey] };
|
|
649
|
+
(0, formatter_1.setTruncation)(newTrunc);
|
|
650
|
+
ctx.config.truncation = newTrunc;
|
|
651
|
+
ctx.updateConfig((c) => {
|
|
652
|
+
if (c.truncation != null) {
|
|
653
|
+
delete c.truncation[tKey];
|
|
654
|
+
if (Object.keys(c.truncation).length === 0) {
|
|
655
|
+
delete c.truncation;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
console.log(` ${tKey} をデフォルト (${defaults[tKey]}) に戻しました。`);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const num = Number(valueStr);
|
|
663
|
+
if (isNaN(num) || !Number.isInteger(num) || num < 1 || num > 999) {
|
|
664
|
+
console.log(chalk_1.default.yellow(" 1〜999 の整数、または default を指定してください。"));
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const newTrunc = { ...(0, formatter_1.getTruncation)(), [tKey]: num };
|
|
668
|
+
(0, formatter_1.setTruncation)(newTrunc);
|
|
669
|
+
ctx.config.truncation = newTrunc;
|
|
670
|
+
ctx.updateConfig((c) => {
|
|
671
|
+
if (c.truncation == null)
|
|
672
|
+
c.truncation = {};
|
|
673
|
+
c.truncation[tKey] = num;
|
|
674
|
+
});
|
|
675
|
+
console.log(` ${tKey} を ${num} に変更しました。`);
|
|
676
|
+
}
|
|
677
|
+
function handleMute(ctx, args) {
|
|
678
|
+
const trimmed = args.trim();
|
|
679
|
+
if (trimmed.length === 0) {
|
|
680
|
+
if (ctx.notifier.isMuted()) {
|
|
681
|
+
const remaining = ctx.notifier.muteRemaining();
|
|
682
|
+
console.log(` ミュート中: 残り ${formatDuration(remaining)}`);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
console.log(" ミュートなし");
|
|
686
|
+
}
|
|
687
|
+
console.log(chalk_1.default.gray(" 使い方: mute <duration> (例: 30m, 1h, 90s) / mute off"));
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (trimmed.toLowerCase() === "off") {
|
|
691
|
+
ctx.notifier.unmute();
|
|
692
|
+
console.log(" ミュートを解除しました。");
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const ms = parseDuration(trimmed);
|
|
696
|
+
if (ms == null || ms <= 0) {
|
|
697
|
+
console.log(chalk_1.default.yellow(" 無効な時間指定です。例: 30m, 1h, 90s"));
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
ctx.notifier.mute(ms);
|
|
701
|
+
console.log(` 通知を ${formatDuration(ms)} ミュートしました。`);
|
|
702
|
+
}
|
|
703
|
+
function handleEewLog(ctx, args) {
|
|
704
|
+
const trimmed = args.trim();
|
|
705
|
+
if (trimmed.length === 0) {
|
|
706
|
+
const enabled = ctx.eewLogger.isEnabled();
|
|
707
|
+
const status = enabled ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
708
|
+
console.log();
|
|
709
|
+
console.log(chalk_1.default.cyan.bold(" EEW ログ記録:") + ` ${status}`);
|
|
710
|
+
if (enabled) {
|
|
711
|
+
console.log();
|
|
712
|
+
const fields = ctx.eewLogger.getFields();
|
|
713
|
+
for (const group of EEW_LOG_FIELD_GROUPS) {
|
|
714
|
+
console.log(chalk_1.default.cyan(` [${group.label}]`));
|
|
715
|
+
for (const field of group.fields) {
|
|
716
|
+
const fieldEnabled = fields[field];
|
|
717
|
+
const fieldStatus = fieldEnabled ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
718
|
+
console.log(chalk_1.default.white(` ${field.padEnd(22)}`) +
|
|
719
|
+
chalk_1.default.gray(`${EEW_LOG_FIELD_LABELS[field]} `) +
|
|
720
|
+
fieldStatus);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
console.log();
|
|
725
|
+
console.log(chalk_1.default.gray(" 使い方: eewlog on/off / eewlog fields / eewlog fields <field> [on|off]"));
|
|
726
|
+
console.log();
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
const eewlogLower = trimmed.toLowerCase();
|
|
730
|
+
if (eewlogLower === "on") {
|
|
731
|
+
ctx.eewLogger.setEnabled(true);
|
|
732
|
+
ctx.config.eewLog = true;
|
|
733
|
+
ctx.updateConfig((c) => { c.eewLog = true; });
|
|
734
|
+
console.log(` EEW ログ記録を ${chalk_1.default.green("ON")} にしました。`);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (eewlogLower === "off") {
|
|
738
|
+
ctx.eewLogger.setEnabled(false);
|
|
739
|
+
ctx.config.eewLog = false;
|
|
740
|
+
ctx.updateConfig((c) => { c.eewLog = false; });
|
|
741
|
+
console.log(` EEW ログ記録を ${chalk_1.default.red("OFF")} にしました。`);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (eewlogLower === "fields" || eewlogLower === "fld") {
|
|
745
|
+
const fields = ctx.eewLogger.getFields();
|
|
746
|
+
console.log();
|
|
747
|
+
console.log(chalk_1.default.cyan.bold(" EEW ログ記録項目:"));
|
|
748
|
+
console.log();
|
|
749
|
+
for (const group of EEW_LOG_FIELD_GROUPS) {
|
|
750
|
+
console.log(chalk_1.default.cyan(` [${group.label}]`));
|
|
751
|
+
for (const field of group.fields) {
|
|
752
|
+
const fieldEnabled = fields[field];
|
|
753
|
+
const fieldStatus = fieldEnabled ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
754
|
+
console.log(chalk_1.default.white(` ${field.padEnd(22)}`) +
|
|
755
|
+
chalk_1.default.gray(`${EEW_LOG_FIELD_LABELS[field]} `) +
|
|
756
|
+
fieldStatus);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
console.log();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (eewlogLower.startsWith("fields ") || eewlogLower.startsWith("fld ")) {
|
|
763
|
+
const fieldsPrefixLen = eewlogLower.startsWith("fld ") ? 4 : 7;
|
|
764
|
+
const parts = trimmed.slice(fieldsPrefixLen).trim().split(/\s+/);
|
|
765
|
+
const fieldName = parts[0];
|
|
766
|
+
const action = parts[1]?.toLowerCase();
|
|
767
|
+
if (!config_1.VALID_EEW_LOG_FIELDS.includes(fieldName)) {
|
|
768
|
+
console.log(chalk_1.default.yellow(` 不明な項目: ${parts[0]}`) +
|
|
769
|
+
chalk_1.default.gray(` (有効: ${config_1.VALID_EEW_LOG_FIELDS.join(", ")})`));
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
let newState;
|
|
773
|
+
const fields = ctx.eewLogger.getFields();
|
|
774
|
+
if (action === "on") {
|
|
775
|
+
if (fields[fieldName]) {
|
|
776
|
+
console.log(` ${EEW_LOG_FIELD_LABELS[fieldName]} (${fieldName}): 既に ${chalk_1.default.green("ON")} です`);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
newState = ctx.eewLogger.toggleField(fieldName);
|
|
780
|
+
}
|
|
781
|
+
else if (action === "off") {
|
|
782
|
+
if (!fields[fieldName]) {
|
|
783
|
+
console.log(` ${EEW_LOG_FIELD_LABELS[fieldName]} (${fieldName}): 既に ${chalk_1.default.red("OFF")} です`);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
newState = ctx.eewLogger.toggleField(fieldName);
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
newState = ctx.eewLogger.toggleField(fieldName);
|
|
790
|
+
}
|
|
791
|
+
const label = EEW_LOG_FIELD_LABELS[fieldName];
|
|
792
|
+
const status = newState ? chalk_1.default.green("ON") : chalk_1.default.red("OFF");
|
|
793
|
+
console.log(` ${label} (${fieldName}): ${status}`);
|
|
794
|
+
ctx.updateConfig((c) => { c.eewLogFields = ctx.eewLogger.getFields(); });
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
console.log(chalk_1.default.yellow(" 使い方: eewlog on/off / eewlog fields / eewlog fields <field> [on|off]"));
|
|
798
|
+
}
|
|
799
|
+
function handleTheme(ctx, args) {
|
|
800
|
+
const sub = args.trim().toLowerCase();
|
|
801
|
+
if (sub === "" || sub === "info") {
|
|
802
|
+
const palette = themeModule.getPalette();
|
|
803
|
+
console.log();
|
|
804
|
+
console.log(chalk_1.default.cyan.bold(" カラーテーマ:"));
|
|
805
|
+
console.log();
|
|
806
|
+
const swatches = themeModule.getPaletteNames().map((name) => {
|
|
807
|
+
const rgb = palette[name];
|
|
808
|
+
return chalk_1.default.rgb(rgb[0], rgb[1], rgb[2])("██");
|
|
809
|
+
});
|
|
810
|
+
console.log(` ${swatches.join(" ")}`);
|
|
811
|
+
console.log();
|
|
812
|
+
console.log(chalk_1.default.white(` theme.json: `) + chalk_1.default.gray(themeModule.getThemePath()));
|
|
813
|
+
console.log(chalk_1.default.white(` カスタマイズ: `) + (themeModule.isCustomized() ? chalk_1.default.green("あり") : chalk_1.default.gray("なし (デフォルト)")));
|
|
814
|
+
console.log();
|
|
815
|
+
console.log(chalk_1.default.gray(" サブコマンド: theme path / show / reset / reload / validate"));
|
|
816
|
+
console.log();
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (sub === "path") {
|
|
820
|
+
console.log(` ${themeModule.getThemePath()}`);
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (sub === "show") {
|
|
824
|
+
handleThemeShow();
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (sub === "reset") {
|
|
828
|
+
handleThemeReset(ctx);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (sub === "reload") {
|
|
832
|
+
const warnings = themeModule.reloadTheme();
|
|
833
|
+
if (warnings.length === 0) {
|
|
834
|
+
console.log(chalk_1.default.green(" テーマを再読込しました"));
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
console.log(chalk_1.default.yellow(" テーマを再読込しました (警告あり):"));
|
|
838
|
+
for (const w of warnings) {
|
|
839
|
+
console.log(chalk_1.default.yellow(` ${w}`));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (sub === "validate") {
|
|
845
|
+
const { valid, warnings } = themeModule.validateThemeFile();
|
|
846
|
+
if (valid && warnings.length === 0) {
|
|
847
|
+
console.log(chalk_1.default.green(" theme.json に問題はありません"));
|
|
848
|
+
}
|
|
849
|
+
else if (valid) {
|
|
850
|
+
console.log(chalk_1.default.yellow(" theme.json の検証結果:"));
|
|
851
|
+
for (const w of warnings) {
|
|
852
|
+
console.log(chalk_1.default.yellow(` ${w}`));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
console.log(chalk_1.default.red(" theme.json に問題があります:"));
|
|
857
|
+
for (const w of warnings) {
|
|
858
|
+
console.log(chalk_1.default.red(` ${w}`));
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
console.log(chalk_1.default.yellow(` 不明なサブコマンド: ${args.trim()}`));
|
|
864
|
+
console.log(chalk_1.default.gray(" 使い方: theme / theme path / theme show / theme reset / theme reload / theme validate"));
|
|
865
|
+
}
|
|
866
|
+
function handleThemeShow() {
|
|
867
|
+
const palette = themeModule.getPalette();
|
|
868
|
+
console.log();
|
|
869
|
+
console.log(chalk_1.default.cyan.bold(" パレット:"));
|
|
870
|
+
console.log();
|
|
871
|
+
for (const name of themeModule.getPaletteNames()) {
|
|
872
|
+
const rgb = palette[name];
|
|
873
|
+
const swatch = chalk_1.default.rgb(rgb[0], rgb[1], rgb[2])("██");
|
|
874
|
+
const hex = themeModule.rgbToHex(rgb);
|
|
875
|
+
console.log(` ${swatch} ${chalk_1.default.white(name.padEnd(12))} ${chalk_1.default.gray(hex)}`);
|
|
876
|
+
}
|
|
877
|
+
console.log();
|
|
878
|
+
console.log(chalk_1.default.cyan.bold(" ロール:"));
|
|
879
|
+
console.log();
|
|
880
|
+
const roleNames = themeModule.getRoleNames();
|
|
881
|
+
const maxNameLen = Math.max(...roleNames.map((n) => n.length));
|
|
882
|
+
for (const name of roleNames) {
|
|
883
|
+
const style = themeModule.getRoleChalk(name);
|
|
884
|
+
const resolved = themeModule.getRole(name);
|
|
885
|
+
const parts = [];
|
|
886
|
+
if (resolved.fg)
|
|
887
|
+
parts.push(`fg: ${themeModule.rgbToHex(resolved.fg)}`);
|
|
888
|
+
if (resolved.bg)
|
|
889
|
+
parts.push(`bg: ${themeModule.rgbToHex(resolved.bg)}`);
|
|
890
|
+
if (resolved.bold)
|
|
891
|
+
parts.push("bold");
|
|
892
|
+
const preview = style("Sample");
|
|
893
|
+
console.log(` ${chalk_1.default.white(name.padEnd(maxNameLen + 1))} ${preview} ${chalk_1.default.gray(parts.join(", "))}`);
|
|
894
|
+
}
|
|
895
|
+
console.log();
|
|
896
|
+
}
|
|
897
|
+
function handleThemeReset(ctx) {
|
|
898
|
+
if (!ctx.rl)
|
|
899
|
+
return;
|
|
900
|
+
const rl = ctx.rl;
|
|
901
|
+
rl.question(chalk_1.default.yellow(" デフォルトの theme.json を書き出しますか? (y/N) "), (answer) => {
|
|
902
|
+
if (answer.trim().toLowerCase() === "y") {
|
|
903
|
+
try {
|
|
904
|
+
const warnings = themeModule.resetTheme();
|
|
905
|
+
console.log(chalk_1.default.green(` theme.json を書き出しました: ${themeModule.getThemePath()}`));
|
|
906
|
+
if (warnings.length > 0) {
|
|
907
|
+
for (const w of warnings) {
|
|
908
|
+
console.log(chalk_1.default.yellow(` ${w}`));
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
catch (err) {
|
|
913
|
+
const msg = err instanceof Error ? err.message : "不明なエラー";
|
|
914
|
+
console.log(chalk_1.default.red(` theme.json の書き出しに失敗しました: ${msg}`));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
console.log(chalk_1.default.gray(" キャンセルしました"));
|
|
919
|
+
}
|
|
920
|
+
rl.setPrompt(ctx.buildPromptString());
|
|
921
|
+
rl.prompt();
|
|
922
|
+
});
|
|
923
|
+
}
|