@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.
- package/CHANGELOG.md +898 -0
- package/LICENSE +21 -0
- package/README.md +535 -0
- package/assets/icons/.gitkeep +0 -0
- package/assets/sounds/.gitkeep +0 -0
- package/assets/sounds/cancel.mp3 +0 -0
- package/assets/sounds/critical.mp3 +0 -0
- package/assets/sounds/info.mp3 +0 -0
- package/assets/sounds/normal.mp3 +0 -0
- package/assets/sounds/warning.mp3 +0 -0
- package/dist/config.js +638 -0
- package/dist/dmdata/connection-manager.js +2 -0
- package/dist/dmdata/endpoint-selector.js +185 -0
- package/dist/dmdata/multi-connection-manager.js +158 -0
- package/dist/dmdata/rest-client.js +281 -0
- package/dist/dmdata/telegram-parser.js +704 -0
- package/dist/dmdata/volcano-parser.js +647 -0
- package/dist/dmdata/ws-client.js +336 -0
- package/dist/engine/cli/cli-init.js +266 -0
- package/dist/engine/cli/cli-run.js +121 -0
- package/dist/engine/cli/cli.js +121 -0
- package/dist/engine/eew/eew-logger.js +355 -0
- package/dist/engine/eew/eew-tracker.js +229 -0
- package/dist/engine/messages/message-router.js +261 -0
- package/dist/engine/messages/tsunami-state.js +96 -0
- package/dist/engine/messages/volcano-state.js +131 -0
- package/dist/engine/messages/volcano-vfvo53-aggregator.js +173 -0
- package/dist/engine/monitor/monitor.js +118 -0
- package/dist/engine/monitor/repl-coordinator.js +63 -0
- package/dist/engine/monitor/shutdown.js +114 -0
- package/dist/engine/notification/node-notifier-loader.js +19 -0
- package/dist/engine/notification/notifier.js +338 -0
- package/dist/engine/notification/sound-player.js +230 -0
- package/dist/engine/notification/volcano-presentation.js +166 -0
- package/dist/engine/startup/config-resolver.js +139 -0
- package/dist/engine/startup/tsunami-initializer.js +91 -0
- package/dist/engine/startup/update-checker.js +229 -0
- package/dist/engine/startup/volcano-initializer.js +89 -0
- package/dist/index.js +24 -0
- package/dist/logger.js +95 -0
- package/dist/types.js +61 -0
- package/dist/ui/earthquake-formatter.js +871 -0
- package/dist/ui/eew-formatter.js +335 -0
- package/dist/ui/formatter.js +689 -0
- package/dist/ui/repl.js +2059 -0
- package/dist/ui/test-samples.js +880 -0
- package/dist/ui/theme.js +516 -0
- package/dist/ui/volcano-formatter.js +667 -0
- package/dist/ui/waiting-tips.js +227 -0
- package/dist/utils/intensity.js +13 -0
- package/dist/utils/secrets.js +14 -0
- package/package.json +69 -0
|
@@ -0,0 +1,871 @@
|
|
|
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.typeLabel = typeLabel;
|
|
40
|
+
exports.displayEarthquakeInfo = displayEarthquakeInfo;
|
|
41
|
+
exports.displayTsunamiInfo = displayTsunamiInfo;
|
|
42
|
+
exports.displaySeismicTextInfo = displaySeismicTextInfo;
|
|
43
|
+
exports.displayNankaiTroughInfo = displayNankaiTroughInfo;
|
|
44
|
+
exports.displayLgObservationInfo = displayLgObservationInfo;
|
|
45
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
+
const theme = __importStar(require("./theme"));
|
|
47
|
+
const formatter_1 = require("./formatter");
|
|
48
|
+
// ── テーブル幅閾値 ──
|
|
49
|
+
/** テーブル幅がこの値以上のとき、津波情報をカラム区切りテーブルで表示 */
|
|
50
|
+
const WIDE_TABLE_THRESHOLD = 80;
|
|
51
|
+
// ── 電文タイプラベル ──
|
|
52
|
+
/** 電文タイプの日本語名 */
|
|
53
|
+
function typeLabel(type) {
|
|
54
|
+
const map = {
|
|
55
|
+
VXSE51: "震度速報",
|
|
56
|
+
VXSE52: "震源に関する情報",
|
|
57
|
+
VXSE53: "震源・震度に関する情報",
|
|
58
|
+
VXSE56: "地震の活動状況等に関する情報",
|
|
59
|
+
VXSE60: "地震回数に関する情報",
|
|
60
|
+
VXSE61: "顕著な地震の震源要素更新のお知らせ",
|
|
61
|
+
VTSE41: "津波警報・注意報・予報",
|
|
62
|
+
VTSE51: "津波情報",
|
|
63
|
+
VTSE52: "沖合の津波情報",
|
|
64
|
+
VXSE43: "緊急地震速報(警報)",
|
|
65
|
+
VXSE44: "緊急地震速報(予報)",
|
|
66
|
+
VXSE45: "緊急地震速報(地震動予報)",
|
|
67
|
+
VXSE62: "長周期地震動に関する観測情報",
|
|
68
|
+
VZSE40: "地震・津波に関するお知らせ",
|
|
69
|
+
VYSE50: "南海トラフ地震臨時情報",
|
|
70
|
+
VYSE51: "南海トラフ地震関連解説情報(臨時)",
|
|
71
|
+
VYSE52: "南海トラフ地震関連解説情報(定例)",
|
|
72
|
+
VYSE60: "北海道・三陸沖後発地震注意情報",
|
|
73
|
+
};
|
|
74
|
+
return map[type] || type;
|
|
75
|
+
}
|
|
76
|
+
// ── 地震情報ヘルパー ──
|
|
77
|
+
/** 地震情報のフレームレベルを決定 */
|
|
78
|
+
function earthquakeFrameLevel(info) {
|
|
79
|
+
if (info.infoType === "取消")
|
|
80
|
+
return "cancel";
|
|
81
|
+
if (info.intensity) {
|
|
82
|
+
const num = (0, formatter_1.intensityToNumeric)(info.intensity.maxInt);
|
|
83
|
+
if (num >= 7)
|
|
84
|
+
return "critical"; // 6弱以上
|
|
85
|
+
if (num >= 4)
|
|
86
|
+
return "warning"; // 4以上
|
|
87
|
+
}
|
|
88
|
+
return "normal";
|
|
89
|
+
}
|
|
90
|
+
/** 津波情報の短縮テキスト */
|
|
91
|
+
function tsunamiShort(info) {
|
|
92
|
+
if (!info.tsunami)
|
|
93
|
+
return "";
|
|
94
|
+
const t = info.tsunami.text;
|
|
95
|
+
if (t.includes("心配はありません") || t.includes("心配なし"))
|
|
96
|
+
return theme.getRoleChalk("tsunamiNone")("津波なし");
|
|
97
|
+
if (t.includes("注意"))
|
|
98
|
+
return theme.getRoleChalk("tsunamiAdvisory")("津波注意");
|
|
99
|
+
if (t.includes("警報"))
|
|
100
|
+
return theme.getRoleChalk("tsunamiWarning")("津波警報");
|
|
101
|
+
return chalk_1.default.white(t.length > 10 ? t.substring(0, 10) + "…" : t);
|
|
102
|
+
}
|
|
103
|
+
// ── ハイライトルール ──
|
|
104
|
+
/** 南海トラフ共通ルール */
|
|
105
|
+
const NANKAI_COMMON_RULES = [
|
|
106
|
+
{ source: "巨大地震警戒", flags: "", style: () => theme.getRoleChalk("nankaiSerialCritical") },
|
|
107
|
+
{ source: "大規模地震", flags: "", style: () => theme.getRoleChalk("nankaiSerialCritical") },
|
|
108
|
+
{ source: "巨大地震注意|後発地震注意情報|後発地震への注意", flags: "", style: () => theme.getRoleChalk("nankaiSerialWarning") },
|
|
109
|
+
{ source: "調査中|調査を開始", flags: "", style: () => theme.getRoleChalk("nankaiSerialWarning") },
|
|
110
|
+
{ source: "モーメントマグニチュード[(Mw)0-90-9..クラス以上]*|マグニチュード[(M)0-90-9..クラス以上]*|Mw[0-90-9]+", flags: "", style: () => chalk_1.default.bold.white },
|
|
111
|
+
{ source: "防災対応をとってください|今後の情報に注意してください|身の安全を守る行動", flags: "", style: () => theme.getRoleChalk("nextAdvisory") },
|
|
112
|
+
{ source: "相対的に高まっている", flags: "", style: () => theme.getRoleChalk("warningComment") },
|
|
113
|
+
{ source: "調査終了", flags: "", style: () => theme.getRoleChalk("textMuted") },
|
|
114
|
+
];
|
|
115
|
+
/** VYSE52 追加ルール */
|
|
116
|
+
const NANKAI_VYSE52_EXTRA_RULES = [
|
|
117
|
+
{ source: "特段の変化は観測されていません", flags: "", style: () => theme.getRoleChalk("textMuted") },
|
|
118
|
+
{ source: "短期的ゆっくりすべり|長期的ゆっくりすべり", flags: "", style: () => chalk_1.default.bold.white },
|
|
119
|
+
];
|
|
120
|
+
/** テキスト系ルール */
|
|
121
|
+
const SEISMIC_TEXT_RULES = [
|
|
122
|
+
{ source: "活発", flags: "", style: () => theme.getRoleChalk("warningComment") },
|
|
123
|
+
{ source: "最大マグニチュード[0-90-9M..]+程度|マグニチュード[0-90-9..]+", flags: "", style: () => theme.getRoleChalk("warningComment") },
|
|
124
|
+
{ source: "最大震度[0-90-9][弱強]?|震度[0-90-9][弱強]?を観測", flags: "", style: () => theme.getRoleChalk("warningComment") },
|
|
125
|
+
{ source: "防災上の留意事項|見通し", flags: "", style: () => chalk_1.default.bold.white },
|
|
126
|
+
];
|
|
127
|
+
/** 電文種別に応じた南海トラフルールを返す */
|
|
128
|
+
function getNankaiRules(type) {
|
|
129
|
+
if (type === "VYSE52") {
|
|
130
|
+
return [...NANKAI_COMMON_RULES, ...NANKAI_VYSE52_EXTRA_RULES];
|
|
131
|
+
}
|
|
132
|
+
return NANKAI_COMMON_RULES;
|
|
133
|
+
}
|
|
134
|
+
/** 電文種別に応じたテキスト系ルールを返す */
|
|
135
|
+
function getSeismicTextRules(_type) {
|
|
136
|
+
return SEISMIC_TEXT_RULES;
|
|
137
|
+
}
|
|
138
|
+
// ── 津波情報ヘルパー ──
|
|
139
|
+
/** 津波情報のフレームレベルを決定 */
|
|
140
|
+
function tsunamiFrameLevel(info) {
|
|
141
|
+
if (info.infoType === "取消")
|
|
142
|
+
return "cancel";
|
|
143
|
+
const kinds = (info.forecast || []).map((f) => f.kind);
|
|
144
|
+
if (kinds.some((kind) => kind.includes("大津波警報")))
|
|
145
|
+
return "critical";
|
|
146
|
+
if (kinds.some((kind) => kind.includes("津波警報")))
|
|
147
|
+
return "warning";
|
|
148
|
+
return "normal";
|
|
149
|
+
}
|
|
150
|
+
/** 津波電文のバナーラベルを forecast の kind から決定する */
|
|
151
|
+
function tsunamiBannerLabel(info) {
|
|
152
|
+
// 取消や forecast がない場合は電文タイプラベルをそのまま使う
|
|
153
|
+
if (info.infoType === "取消" || !info.forecast || info.forecast.length === 0) {
|
|
154
|
+
return typeLabel(info.type);
|
|
155
|
+
}
|
|
156
|
+
const kinds = info.forecast.map((f) => f.kind);
|
|
157
|
+
const hasMajor = kinds.some((k) => k.includes("大津波警報"));
|
|
158
|
+
const hasWarning = kinds.some((k) => k.includes("津波警報") && !k.includes("大津波警報"));
|
|
159
|
+
const hasAdvisory = kinds.some((k) => k.includes("津波注意報"));
|
|
160
|
+
const hasForecast = kinds.some((k) => k.includes("津波予報"));
|
|
161
|
+
const parts = [];
|
|
162
|
+
if (hasMajor)
|
|
163
|
+
parts.push("大津波警報");
|
|
164
|
+
if (hasWarning)
|
|
165
|
+
parts.push("津波警報");
|
|
166
|
+
if (hasAdvisory)
|
|
167
|
+
parts.push("津波注意報");
|
|
168
|
+
if (hasForecast)
|
|
169
|
+
parts.push("津波予報");
|
|
170
|
+
return parts.length > 0 ? parts.join("・") : typeLabel(info.type);
|
|
171
|
+
}
|
|
172
|
+
/** 津波種別の表示順 */
|
|
173
|
+
function tsunamiKindRank(kind) {
|
|
174
|
+
if (kind.includes("大津波警報"))
|
|
175
|
+
return 0;
|
|
176
|
+
if (kind.includes("津波警報"))
|
|
177
|
+
return 1;
|
|
178
|
+
if (kind.includes("津波注意報"))
|
|
179
|
+
return 2;
|
|
180
|
+
if (kind.includes("津波予報"))
|
|
181
|
+
return 3;
|
|
182
|
+
return 99;
|
|
183
|
+
}
|
|
184
|
+
/** 時刻文字列なら整形し、そうでなければそのまま返す */
|
|
185
|
+
function prettyTimeOrText(value) {
|
|
186
|
+
const time = new Date(value).getTime();
|
|
187
|
+
if (Number.isNaN(time)) {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
return (0, formatter_1.formatTimestamp)(value);
|
|
191
|
+
}
|
|
192
|
+
// ── 南海トラフ・長周期ヘルパー ──
|
|
193
|
+
/** 南海トラフ関連情報のフレームレベルを決定 */
|
|
194
|
+
function nankaiTroughFrameLevel(info) {
|
|
195
|
+
if (info.infoType === "取消")
|
|
196
|
+
return "cancel";
|
|
197
|
+
if (!info.infoSerial) {
|
|
198
|
+
// VYSE60 (InfoSerial なし) → warning
|
|
199
|
+
return "warning";
|
|
200
|
+
}
|
|
201
|
+
const code = info.infoSerial.code;
|
|
202
|
+
if (code === "120")
|
|
203
|
+
return "critical"; // 巨大地震警戒
|
|
204
|
+
if (code === "130")
|
|
205
|
+
return "warning"; // 巨大地震注意
|
|
206
|
+
if (code === "111" || code === "112" || code === "113")
|
|
207
|
+
return "warning"; // 調査中
|
|
208
|
+
if (code === "210" || code === "219")
|
|
209
|
+
return "warning"; // 臨時解説
|
|
210
|
+
if (code === "190" || code === "200")
|
|
211
|
+
return "info"; // 調査終了 / 定例解説
|
|
212
|
+
return "warning";
|
|
213
|
+
}
|
|
214
|
+
/** 長周期地震動観測情報のフレームレベルを決定 */
|
|
215
|
+
function lgObservationFrameLevel(info) {
|
|
216
|
+
if (info.infoType === "取消")
|
|
217
|
+
return "cancel";
|
|
218
|
+
if (info.maxLgInt) {
|
|
219
|
+
const num = (0, formatter_1.lgIntToNumeric)(info.maxLgInt);
|
|
220
|
+
if (num >= 4)
|
|
221
|
+
return "critical";
|
|
222
|
+
if (num >= 3)
|
|
223
|
+
return "warning";
|
|
224
|
+
if (num >= 2)
|
|
225
|
+
return "normal";
|
|
226
|
+
}
|
|
227
|
+
return "info";
|
|
228
|
+
}
|
|
229
|
+
// ══════════════════════════════════════════════
|
|
230
|
+
// 表示関数
|
|
231
|
+
// ══════════════════════════════════════════════
|
|
232
|
+
/** 地震情報を整形して表示 */
|
|
233
|
+
function displayEarthquakeInfo(info) {
|
|
234
|
+
const level = earthquakeFrameLevel(info);
|
|
235
|
+
const label = typeLabel(info.type);
|
|
236
|
+
const width = (0, formatter_1.getFrameWidth)();
|
|
237
|
+
// コンパクトモード: 1行サマリー
|
|
238
|
+
if ((0, formatter_1.getDisplayMode)() === "compact") {
|
|
239
|
+
const parts = [];
|
|
240
|
+
parts.push(formatter_1.SEVERITY_LABELS[level]);
|
|
241
|
+
parts.push(label);
|
|
242
|
+
if (info.earthquake) {
|
|
243
|
+
parts.push(info.earthquake.hypocenterName);
|
|
244
|
+
parts.push(`M${info.earthquake.magnitude}`);
|
|
245
|
+
}
|
|
246
|
+
if (info.intensity)
|
|
247
|
+
parts.push(`震度${info.intensity.maxInt}`);
|
|
248
|
+
const ts = tsunamiShort(info);
|
|
249
|
+
if (ts)
|
|
250
|
+
parts.push((0, formatter_1.stripAnsi)(ts));
|
|
251
|
+
const color = (0, formatter_1.frameColor)(level);
|
|
252
|
+
console.log(color(parts.join(" ")));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const buf = (0, formatter_1.createRenderBuffer)();
|
|
256
|
+
buf.pushEmpty();
|
|
257
|
+
buf.push((0, formatter_1.frameTop)(level, width));
|
|
258
|
+
// テスト電文
|
|
259
|
+
if (info.isTest) {
|
|
260
|
+
buf.push((0, formatter_1.frameLine)(level, theme.getRoleChalk("testBadge")(" テスト電文 "), width));
|
|
261
|
+
}
|
|
262
|
+
// タイトル行 (severity ラベル付き)
|
|
263
|
+
const titleContent = chalk_1.default.bold(`${label}`) + chalk_1.default.gray(` ${info.infoType}`) + chalk_1.default.gray(` ${formatter_1.SEVERITY_LABELS[level]}`);
|
|
264
|
+
buf.pushTitle((0, formatter_1.frameLine)(level, titleContent, width));
|
|
265
|
+
// ヘッドライン
|
|
266
|
+
if (info.headline) {
|
|
267
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
268
|
+
const headlineWrapped = (0, formatter_1.wrapFrameLines)(level, chalk_1.default.bold.white(info.headline), width);
|
|
269
|
+
for (let i = 0; i < headlineWrapped.length; i++) {
|
|
270
|
+
if (i === 0) {
|
|
271
|
+
buf.pushHeadline(headlineWrapped[i]);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
buf.push(headlineWrapped[i]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// カード1行目: 最重要項目
|
|
279
|
+
const cardParts = [];
|
|
280
|
+
if (info.intensity) {
|
|
281
|
+
const ic = (0, formatter_1.intensityColor)(info.intensity.maxInt);
|
|
282
|
+
cardParts.push(chalk_1.default.white("最大震度 ") + ic.bold(info.intensity.maxInt));
|
|
283
|
+
}
|
|
284
|
+
if (info.intensity?.maxLgInt && (0, formatter_1.lgIntToNumeric)(info.intensity.maxLgInt) >= 1) {
|
|
285
|
+
const lc = (0, formatter_1.lgIntensityColor)(info.intensity.maxLgInt);
|
|
286
|
+
cardParts.push(chalk_1.default.white("長周期階級 ") + lc.bold(info.intensity.maxLgInt));
|
|
287
|
+
}
|
|
288
|
+
if (info.earthquake?.magnitude) {
|
|
289
|
+
cardParts.push((0, formatter_1.colorMagnitude)(info.earthquake.magnitude));
|
|
290
|
+
}
|
|
291
|
+
if (info.earthquake?.depth) {
|
|
292
|
+
cardParts.push(chalk_1.default.white("深さ ") + chalk_1.default.white(info.earthquake.depth));
|
|
293
|
+
}
|
|
294
|
+
const tsunamiText = tsunamiShort(info);
|
|
295
|
+
if (tsunamiText) {
|
|
296
|
+
cardParts.push(tsunamiText);
|
|
297
|
+
}
|
|
298
|
+
if (cardParts.length > 0) {
|
|
299
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
300
|
+
buf.pushCard((0, formatter_1.frameLine)(level, cardParts.join(chalk_1.default.gray(" │ ")), width));
|
|
301
|
+
}
|
|
302
|
+
// 震源詳細
|
|
303
|
+
if (info.earthquake) {
|
|
304
|
+
const eq = info.earthquake;
|
|
305
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
306
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("震源地: ") + theme.getRoleChalk("hypocenter")(eq.hypocenterName), width));
|
|
307
|
+
if (eq.originTime) {
|
|
308
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("発生: ") + chalk_1.default.white((0, formatter_1.formatTimestamp)(eq.originTime)), width));
|
|
309
|
+
}
|
|
310
|
+
if (eq.latitude && eq.longitude) {
|
|
311
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("位置: ") + chalk_1.default.white(`${eq.latitude} ${eq.longitude}`), width));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else if (info.type === "VXSE51") {
|
|
315
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
316
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.yellow("※ 震源についてはただいま調査中です"), width));
|
|
317
|
+
}
|
|
318
|
+
// 震度一覧
|
|
319
|
+
if (info.intensity && info.intensity.areas.length > 0) {
|
|
320
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
321
|
+
// 震度×地域名 → エリアデータの Map を事前構築 (O(n) ルックアップ用)
|
|
322
|
+
const areaDataMap = new Map();
|
|
323
|
+
const byIntensity = new Map();
|
|
324
|
+
for (const area of info.intensity.areas) {
|
|
325
|
+
const key = area.intensity;
|
|
326
|
+
if (!byIntensity.has(key))
|
|
327
|
+
byIntensity.set(key, []);
|
|
328
|
+
byIntensity.get(key).push(area.name);
|
|
329
|
+
areaDataMap.set(`${key}:${area.name}`, area);
|
|
330
|
+
}
|
|
331
|
+
const order = ["7", "6+", "6強", "6-", "6弱", "5+", "5強", "5-", "5弱", "4", "3", "2", "1"];
|
|
332
|
+
const sorted = [...byIntensity.entries()].sort((a, b) => {
|
|
333
|
+
const ai = order.indexOf(a[0]);
|
|
334
|
+
const bi = order.indexOf(b[0]);
|
|
335
|
+
return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
|
|
336
|
+
});
|
|
337
|
+
// 折りたたみ: 表示地点数を制限
|
|
338
|
+
let totalAreas = 0;
|
|
339
|
+
let hiddenAreas = 0;
|
|
340
|
+
const maxObs = (0, formatter_1.getMaxObservations)();
|
|
341
|
+
for (const [int, names] of sorted) {
|
|
342
|
+
// 折りたたみ判定
|
|
343
|
+
let displayNames = names;
|
|
344
|
+
if (maxObs != null) {
|
|
345
|
+
const remaining = maxObs - totalAreas;
|
|
346
|
+
if (remaining <= 0) {
|
|
347
|
+
hiddenAreas += names.length;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (names.length > remaining) {
|
|
351
|
+
hiddenAreas += names.length - remaining;
|
|
352
|
+
displayNames = names.slice(0, remaining);
|
|
353
|
+
}
|
|
354
|
+
totalAreas += displayNames.length;
|
|
355
|
+
}
|
|
356
|
+
const color = (0, formatter_1.intensityColor)(int);
|
|
357
|
+
// 長周期地震動階級付きの地域名を生成
|
|
358
|
+
const areaTexts = displayNames.map((name) => {
|
|
359
|
+
const areaData = areaDataMap.get(`${int}:${name}`);
|
|
360
|
+
if (areaData?.lgIntensity && (0, formatter_1.lgIntToNumeric)(areaData.lgIntensity) >= 1) {
|
|
361
|
+
const lc = (0, formatter_1.lgIntensityColor)(areaData.lgIntensity);
|
|
362
|
+
return chalk_1.default.white(name) + lc(` [長周期${areaData.lgIntensity}]`);
|
|
363
|
+
}
|
|
364
|
+
return chalk_1.default.white(name);
|
|
365
|
+
});
|
|
366
|
+
const prefix = color(`震度${int}: `);
|
|
367
|
+
const indentWidth = (0, formatter_1.visualWidth)((0, formatter_1.stripAnsi)(prefix));
|
|
368
|
+
const content = prefix + areaTexts.join(chalk_1.default.white(", "));
|
|
369
|
+
for (const line of (0, formatter_1.wrapFrameLines)(level, content, width, indentWidth)) {
|
|
370
|
+
buf.push(line);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (hiddenAreas > 0) {
|
|
374
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... 他 ${hiddenAreas} 地点`), width));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// 津波 (詳細)
|
|
378
|
+
if (info.tsunami) {
|
|
379
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
380
|
+
for (const wl of (0, formatter_1.wrapFrameLines)(level, chalk_1.default.white(`${info.tsunami.text}`), width)) {
|
|
381
|
+
buf.push(wl);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// フッター
|
|
385
|
+
(0, formatter_1.renderFooter)(level, info.type, info.reportDateTime, info.publishingOffice, width, buf);
|
|
386
|
+
buf.push((0, formatter_1.frameBottom)(level, width));
|
|
387
|
+
buf.pushEmpty();
|
|
388
|
+
(0, formatter_1.flushWithRecap)(buf, level, width);
|
|
389
|
+
}
|
|
390
|
+
/** 津波情報を整形して表示 */
|
|
391
|
+
function displayTsunamiInfo(info) {
|
|
392
|
+
const level = tsunamiFrameLevel(info);
|
|
393
|
+
const label = typeLabel(info.type);
|
|
394
|
+
const bannerLabel = tsunamiBannerLabel(info);
|
|
395
|
+
const width = (0, formatter_1.getFrameWidth)();
|
|
396
|
+
// コンパクトモード
|
|
397
|
+
if ((0, formatter_1.getDisplayMode)() === "compact") {
|
|
398
|
+
const parts = [];
|
|
399
|
+
parts.push(formatter_1.SEVERITY_LABELS[level]);
|
|
400
|
+
parts.push(bannerLabel);
|
|
401
|
+
if (info.forecast && info.forecast.length > 0) {
|
|
402
|
+
const areas = info.forecast.slice(0, (0, formatter_1.getTruncation)().tsunamiCompactForecastAreas).map((f) => f.areaName);
|
|
403
|
+
parts.push(areas.join(", "));
|
|
404
|
+
}
|
|
405
|
+
const color = (0, formatter_1.frameColor)(level);
|
|
406
|
+
console.log(color(parts.join(" ")));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const buf = (0, formatter_1.createRenderBuffer)();
|
|
410
|
+
buf.pushEmpty();
|
|
411
|
+
// バナー表示 (津波注意報/津波警報/大津波警報)
|
|
412
|
+
if (level === "critical") {
|
|
413
|
+
const bannerText = ` ${bannerLabel}`;
|
|
414
|
+
const decorStyle = theme.getRoleChalk("tsunamiMajorBannerDecor");
|
|
415
|
+
const majorStyle = theme.getRoleChalk("tsunamiMajorBanner");
|
|
416
|
+
buf.push(decorStyle(" ".repeat(width)));
|
|
417
|
+
buf.push(majorStyle((0, formatter_1.visualPadEnd)(bannerText, width)));
|
|
418
|
+
buf.push(decorStyle(" ".repeat(width)));
|
|
419
|
+
}
|
|
420
|
+
else if (level === "warning") {
|
|
421
|
+
const bannerText = ` ${bannerLabel}`;
|
|
422
|
+
const warnStyle = theme.getRoleChalk("tsunamiWarningBanner");
|
|
423
|
+
buf.push(warnStyle(" ".repeat(width)));
|
|
424
|
+
buf.push(warnStyle((0, formatter_1.visualPadEnd)(bannerText, width)));
|
|
425
|
+
buf.push(warnStyle(" ".repeat(width)));
|
|
426
|
+
}
|
|
427
|
+
else if (level === "normal") {
|
|
428
|
+
const bannerText = ` ${bannerLabel}`;
|
|
429
|
+
const advStyle = theme.getRoleChalk("tsunamiAdvisoryBanner");
|
|
430
|
+
buf.push(advStyle(" ".repeat(width)));
|
|
431
|
+
buf.push(advStyle((0, formatter_1.visualPadEnd)(bannerText, width)));
|
|
432
|
+
buf.push(advStyle(" ".repeat(width)));
|
|
433
|
+
}
|
|
434
|
+
buf.push((0, formatter_1.frameTop)(level, width));
|
|
435
|
+
if (info.isTest) {
|
|
436
|
+
buf.push((0, formatter_1.frameLine)(level, theme.getRoleChalk("testBadge")(" テスト電文 "), width));
|
|
437
|
+
}
|
|
438
|
+
const titleContent = chalk_1.default.bold(`${label}`) + chalk_1.default.gray(` ${info.infoType}`) + chalk_1.default.gray(` ${formatter_1.SEVERITY_LABELS[level]}`);
|
|
439
|
+
buf.pushTitle((0, formatter_1.frameLine)(level, titleContent, width));
|
|
440
|
+
if (info.headline) {
|
|
441
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
442
|
+
const headlineLines = info.headline
|
|
443
|
+
.split(/\r?\n/)
|
|
444
|
+
.map((l) => l.trimEnd())
|
|
445
|
+
.filter((l) => l.trim().length > 0);
|
|
446
|
+
let firstHeadline = true;
|
|
447
|
+
for (const hl of headlineLines) {
|
|
448
|
+
for (const wrapped of (0, formatter_1.wrapFrameLines)(level, chalk_1.default.bold.white(hl), width)) {
|
|
449
|
+
if (firstHeadline) {
|
|
450
|
+
buf.pushHeadline(wrapped);
|
|
451
|
+
firstHeadline = false;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
buf.push(wrapped);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (info.earthquake) {
|
|
460
|
+
const eq = info.earthquake;
|
|
461
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
462
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("震源地: ") + theme.getRoleChalk("hypocenter")(eq.hypocenterName), width));
|
|
463
|
+
if (eq.originTime) {
|
|
464
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("発生: ") + chalk_1.default.white((0, formatter_1.formatTimestamp)(eq.originTime)), width));
|
|
465
|
+
}
|
|
466
|
+
if (eq.latitude && eq.longitude) {
|
|
467
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("位置: ") + chalk_1.default.white(`${eq.latitude} ${eq.longitude}`), width));
|
|
468
|
+
}
|
|
469
|
+
if (eq.magnitude && !isNaN(parseFloat(eq.magnitude))) {
|
|
470
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("規模: ") + (0, formatter_1.colorMagnitude)(eq.magnitude), width));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (info.forecast && info.forecast.length > 0) {
|
|
474
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
475
|
+
const sorted = [...info.forecast].sort((a, b) => tsunamiKindRank(a.kind) - tsunamiKindRank(b.kind));
|
|
476
|
+
// 折りたたみ
|
|
477
|
+
const maxObs = (0, formatter_1.getMaxObservations)();
|
|
478
|
+
const displaySorted = maxObs != null ? sorted.slice(0, maxObs) : sorted;
|
|
479
|
+
const hiddenForecast = sorted.length - displaySorted.length;
|
|
480
|
+
if (width >= WIDE_TABLE_THRESHOLD) {
|
|
481
|
+
const headers = ["区分", "地域名", "波高", "到達予想"];
|
|
482
|
+
const rows = displaySorted.map((item) => {
|
|
483
|
+
let kindText = chalk_1.default.white(item.kind);
|
|
484
|
+
if (item.kind.includes("大津波警報")) {
|
|
485
|
+
kindText = theme.getRoleChalk("tsunamiMajor")(item.kind);
|
|
486
|
+
}
|
|
487
|
+
else if (item.kind.includes("津波警報")) {
|
|
488
|
+
kindText = theme.getRoleChalk("tsunamiWarning")(item.kind);
|
|
489
|
+
}
|
|
490
|
+
else if (item.kind.includes("津波注意報")) {
|
|
491
|
+
kindText = theme.getRoleChalk("tsunamiAdvisory")(item.kind);
|
|
492
|
+
}
|
|
493
|
+
return [
|
|
494
|
+
kindText,
|
|
495
|
+
chalk_1.default.white(item.areaName),
|
|
496
|
+
item.maxHeightDescription ? chalk_1.default.white(item.maxHeightDescription) : chalk_1.default.gray("―"),
|
|
497
|
+
item.firstHeight ? chalk_1.default.white(prettyTimeOrText(item.firstHeight)) : chalk_1.default.gray("―"),
|
|
498
|
+
];
|
|
499
|
+
});
|
|
500
|
+
(0, formatter_1.renderFrameTable)(level, headers, rows, width, buf);
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
for (const item of displaySorted) {
|
|
504
|
+
let kindText = chalk_1.default.white(item.kind);
|
|
505
|
+
if (item.kind.includes("大津波警報")) {
|
|
506
|
+
kindText = theme.getRoleChalk("tsunamiMajor")(item.kind);
|
|
507
|
+
}
|
|
508
|
+
else if (item.kind.includes("津波警報")) {
|
|
509
|
+
kindText = theme.getRoleChalk("tsunamiWarning")(item.kind);
|
|
510
|
+
}
|
|
511
|
+
else if (item.kind.includes("津波注意報")) {
|
|
512
|
+
kindText = theme.getRoleChalk("tsunamiAdvisory")(item.kind);
|
|
513
|
+
}
|
|
514
|
+
const extra = [];
|
|
515
|
+
if (item.maxHeightDescription)
|
|
516
|
+
extra.push(item.maxHeightDescription);
|
|
517
|
+
if (item.firstHeight)
|
|
518
|
+
extra.push(prettyTimeOrText(item.firstHeight));
|
|
519
|
+
const extraText = extra.length > 0 ? chalk_1.default.gray(` (${extra.join(" / ")})`) : "";
|
|
520
|
+
for (const wl of (0, formatter_1.wrapFrameLines)(level, kindText + chalk_1.default.white(` ${item.areaName}`) + extraText, width)) {
|
|
521
|
+
buf.push(wl);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (hiddenForecast > 0) {
|
|
526
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... 他 ${hiddenForecast} 地点`), width));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (info.observations && info.observations.length > 0) {
|
|
530
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
531
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.bold.white("沖合観測"), width));
|
|
532
|
+
const maxObs = (0, formatter_1.getMaxObservations)();
|
|
533
|
+
const displayObs = maxObs != null ? info.observations.slice(0, maxObs) : info.observations;
|
|
534
|
+
const hiddenObs = info.observations.length - displayObs.length;
|
|
535
|
+
if (width >= WIDE_TABLE_THRESHOLD) {
|
|
536
|
+
const headers = ["観測点", "センサー", "初動", "最大波高", "到達時刻"];
|
|
537
|
+
const rows = displayObs.map((station) => [
|
|
538
|
+
chalk_1.default.white(station.name),
|
|
539
|
+
station.sensor ? chalk_1.default.white(station.sensor) : chalk_1.default.gray("―"),
|
|
540
|
+
station.initial ? chalk_1.default.white(station.initial) : chalk_1.default.gray("―"),
|
|
541
|
+
station.maxHeightCondition ? chalk_1.default.white(station.maxHeightCondition) : chalk_1.default.gray("―"),
|
|
542
|
+
station.arrivalTime ? chalk_1.default.white(prettyTimeOrText(station.arrivalTime)) : chalk_1.default.gray("―"),
|
|
543
|
+
]);
|
|
544
|
+
(0, formatter_1.renderFrameTable)(level, headers, rows, width, buf);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
for (const station of displayObs) {
|
|
548
|
+
const parts = [
|
|
549
|
+
station.name,
|
|
550
|
+
station.sensor,
|
|
551
|
+
station.initial,
|
|
552
|
+
station.maxHeightCondition,
|
|
553
|
+
].filter((v) => Boolean(v));
|
|
554
|
+
const arrival = station.arrivalTime ? ` ${prettyTimeOrText(station.arrivalTime)}` : "";
|
|
555
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white(parts.join(" / ") + arrival), width));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (hiddenObs > 0) {
|
|
559
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... 他 ${hiddenObs} 地点`), width));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (info.estimations && info.estimations.length > 0) {
|
|
563
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
564
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.bold.white("沿岸推定"), width));
|
|
565
|
+
const maxObs = (0, formatter_1.getMaxObservations)();
|
|
566
|
+
const displayEst = maxObs != null ? info.estimations.slice(0, maxObs) : info.estimations;
|
|
567
|
+
const hiddenEst = info.estimations.length - displayEst.length;
|
|
568
|
+
if (width >= WIDE_TABLE_THRESHOLD) {
|
|
569
|
+
const headers = ["地域名", "波高", "到達予想"];
|
|
570
|
+
const rows = displayEst.map((estimation) => [
|
|
571
|
+
chalk_1.default.white(estimation.areaName),
|
|
572
|
+
estimation.maxHeightDescription ? chalk_1.default.white(estimation.maxHeightDescription) : chalk_1.default.gray("―"),
|
|
573
|
+
estimation.firstHeight ? chalk_1.default.white(prettyTimeOrText(estimation.firstHeight)) : chalk_1.default.gray("―"),
|
|
574
|
+
]);
|
|
575
|
+
(0, formatter_1.renderFrameTable)(level, headers, rows, width, buf);
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
for (const estimation of displayEst) {
|
|
579
|
+
const extra = [];
|
|
580
|
+
if (estimation.maxHeightDescription)
|
|
581
|
+
extra.push(estimation.maxHeightDescription);
|
|
582
|
+
if (estimation.firstHeight)
|
|
583
|
+
extra.push(prettyTimeOrText(estimation.firstHeight));
|
|
584
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white(`${estimation.areaName}${extra.length ? ` (${extra.join(" / ")})` : ""}`), width));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (hiddenEst > 0) {
|
|
588
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... 他 ${hiddenEst} 地点`), width));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (info.warningComment) {
|
|
592
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
593
|
+
const warnStyle = theme.getRoleChalk("warningComment");
|
|
594
|
+
const commentLines = info.warningComment
|
|
595
|
+
.split(/\r?\n/)
|
|
596
|
+
.map((l) => l.trimEnd())
|
|
597
|
+
.filter((l) => l.trim().length > 0);
|
|
598
|
+
for (const line of commentLines) {
|
|
599
|
+
for (const wrapped of (0, formatter_1.wrapFrameLines)(level, warnStyle(line), width)) {
|
|
600
|
+
buf.push(wrapped);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// フッター
|
|
605
|
+
(0, formatter_1.renderFooter)(level, info.type, info.reportDateTime, info.publishingOffice, width, buf);
|
|
606
|
+
buf.push((0, formatter_1.frameBottom)(level, width));
|
|
607
|
+
buf.pushEmpty();
|
|
608
|
+
(0, formatter_1.flushWithRecap)(buf, level, width);
|
|
609
|
+
}
|
|
610
|
+
/** 地震活動テキスト情報を整形して表示 */
|
|
611
|
+
function displaySeismicTextInfo(info) {
|
|
612
|
+
const level = info.infoType === "取消" ? "cancel" : "info";
|
|
613
|
+
const label = typeLabel(info.type);
|
|
614
|
+
const width = (0, formatter_1.getFrameWidth)();
|
|
615
|
+
// コンパクトモード
|
|
616
|
+
if ((0, formatter_1.getDisplayMode)() === "compact") {
|
|
617
|
+
const parts = [];
|
|
618
|
+
parts.push(formatter_1.SEVERITY_LABELS[level]);
|
|
619
|
+
parts.push(label);
|
|
620
|
+
if (info.headline)
|
|
621
|
+
parts.push(info.headline.slice(0, 40));
|
|
622
|
+
const color = (0, formatter_1.frameColor)(level);
|
|
623
|
+
console.log(color(parts.join(" ")));
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const buf = (0, formatter_1.createRenderBuffer)();
|
|
627
|
+
buf.pushEmpty();
|
|
628
|
+
buf.push((0, formatter_1.frameTop)(level, width));
|
|
629
|
+
if (info.isTest) {
|
|
630
|
+
buf.push((0, formatter_1.frameLine)(level, theme.getRoleChalk("testBadge")(" テスト電文 "), width));
|
|
631
|
+
}
|
|
632
|
+
const titleContent = chalk_1.default.bold(`${label}`) + chalk_1.default.gray(` ${info.infoType}`) + chalk_1.default.gray(` ${formatter_1.SEVERITY_LABELS[level]}`);
|
|
633
|
+
buf.pushTitle((0, formatter_1.frameLine)(level, titleContent, width));
|
|
634
|
+
if (info.headline) {
|
|
635
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
636
|
+
const headlineWrapped = (0, formatter_1.wrapFrameLines)(level, chalk_1.default.bold.white(info.headline), width);
|
|
637
|
+
for (let i = 0; i < headlineWrapped.length; i++) {
|
|
638
|
+
if (i === 0) {
|
|
639
|
+
buf.pushHeadline(headlineWrapped[i]);
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
buf.push(headlineWrapped[i]);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const bodyLines = info.bodyText
|
|
647
|
+
.split(/\r?\n/)
|
|
648
|
+
.map((line) => line.trimEnd())
|
|
649
|
+
.filter((line) => line.trim().length > 0);
|
|
650
|
+
if (bodyLines.length > 0) {
|
|
651
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
652
|
+
const showFull = (0, formatter_1.getInfoFullText)();
|
|
653
|
+
const maxLines = (0, formatter_1.getTruncation)().seismicTextLines;
|
|
654
|
+
const innerWidth = width - 4;
|
|
655
|
+
const rules = getSeismicTextRules(info.type);
|
|
656
|
+
const displayLines = showFull ? bodyLines : bodyLines.slice(0, maxLines);
|
|
657
|
+
for (const line of displayLines) {
|
|
658
|
+
for (const highlighted of (0, formatter_1.highlightAndWrap)(line, rules, innerWidth)) {
|
|
659
|
+
buf.push((0, formatter_1.frameLine)(level, highlighted, width));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (!showFull && bodyLines.length > maxLines) {
|
|
663
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... (全${bodyLines.length}行)`), width));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
// フッター
|
|
667
|
+
(0, formatter_1.renderFooter)(level, info.type, info.reportDateTime, info.publishingOffice, width, buf);
|
|
668
|
+
buf.push((0, formatter_1.frameBottom)(level, width));
|
|
669
|
+
buf.pushEmpty();
|
|
670
|
+
(0, formatter_1.flushWithRecap)(buf, level, width);
|
|
671
|
+
}
|
|
672
|
+
/** 南海トラフ関連情報を整形して表示 */
|
|
673
|
+
function displayNankaiTroughInfo(info) {
|
|
674
|
+
const level = nankaiTroughFrameLevel(info);
|
|
675
|
+
const label = typeLabel(info.type);
|
|
676
|
+
const width = (0, formatter_1.getFrameWidth)();
|
|
677
|
+
// コンパクトモード
|
|
678
|
+
if ((0, formatter_1.getDisplayMode)() === "compact") {
|
|
679
|
+
const parts = [];
|
|
680
|
+
parts.push(formatter_1.SEVERITY_LABELS[level]);
|
|
681
|
+
parts.push(label);
|
|
682
|
+
if (info.infoSerial)
|
|
683
|
+
parts.push(info.infoSerial.name);
|
|
684
|
+
const color = (0, formatter_1.frameColor)(level);
|
|
685
|
+
console.log(color(parts.join(" ")));
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const buf = (0, formatter_1.createRenderBuffer)();
|
|
689
|
+
buf.pushEmpty();
|
|
690
|
+
// critical/warning 時はバナー表示
|
|
691
|
+
if (level === "critical") {
|
|
692
|
+
const bannerText = ` ${info.title}`;
|
|
693
|
+
const critBanner = theme.getRoleChalk("nankaiCriticalBanner");
|
|
694
|
+
buf.push(critBanner(" ".repeat(width)));
|
|
695
|
+
buf.push(critBanner((0, formatter_1.visualPadEnd)(bannerText, width)));
|
|
696
|
+
buf.push(critBanner(" ".repeat(width)));
|
|
697
|
+
}
|
|
698
|
+
else if (level === "warning") {
|
|
699
|
+
const bannerText = ` ${info.title}`;
|
|
700
|
+
const warnBanner = theme.getRoleChalk("nankaiWarningBanner");
|
|
701
|
+
buf.push(warnBanner(" ".repeat(width)));
|
|
702
|
+
buf.push(warnBanner((0, formatter_1.visualPadEnd)(bannerText, width)));
|
|
703
|
+
buf.push(warnBanner(" ".repeat(width)));
|
|
704
|
+
}
|
|
705
|
+
buf.push((0, formatter_1.frameTop)(level, width));
|
|
706
|
+
// テスト電文
|
|
707
|
+
if (info.isTest) {
|
|
708
|
+
buf.push((0, formatter_1.frameLine)(level, theme.getRoleChalk("testBadge")(" テスト電文 "), width));
|
|
709
|
+
}
|
|
710
|
+
// タイトル行
|
|
711
|
+
const titleContent = chalk_1.default.bold(`${label}`) + chalk_1.default.gray(` ${info.infoType}`) + chalk_1.default.gray(` ${formatter_1.SEVERITY_LABELS[level]}`);
|
|
712
|
+
buf.pushTitle((0, formatter_1.frameLine)(level, titleContent, width));
|
|
713
|
+
// InfoSerial (状態名)
|
|
714
|
+
if (info.infoSerial) {
|
|
715
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
716
|
+
const serialColor = level === "critical" ? theme.getRoleChalk("nankaiSerialCritical") : theme.getRoleChalk("nankaiSerialWarning");
|
|
717
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("状態: ") + serialColor(info.infoSerial.name), width));
|
|
718
|
+
}
|
|
719
|
+
// 本文
|
|
720
|
+
const bodyLines = info.bodyText
|
|
721
|
+
.split(/\r?\n/)
|
|
722
|
+
.map((line) => line.trimEnd())
|
|
723
|
+
.filter((line) => line.trim().length > 0);
|
|
724
|
+
if (bodyLines.length > 0) {
|
|
725
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
726
|
+
const showFull = (0, formatter_1.getInfoFullText)();
|
|
727
|
+
const maxLines = (0, formatter_1.getTruncation)().nankaiTroughLines;
|
|
728
|
+
const innerWidth = width - 4;
|
|
729
|
+
const rules = getNankaiRules(info.type);
|
|
730
|
+
const displayLines = showFull ? bodyLines : bodyLines.slice(0, maxLines);
|
|
731
|
+
for (const line of displayLines) {
|
|
732
|
+
for (const highlighted of (0, formatter_1.highlightAndWrap)(line, rules, innerWidth)) {
|
|
733
|
+
buf.push((0, formatter_1.frameLine)(level, highlighted, width));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (!showFull && bodyLines.length > maxLines) {
|
|
737
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... (全${bodyLines.length}行)`), width));
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// 次回情報予告
|
|
741
|
+
if (info.nextAdvisory) {
|
|
742
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
743
|
+
for (const line of (0, formatter_1.wrapFrameLines)(level, theme.getRoleChalk("nextAdvisory")(info.nextAdvisory), width)) {
|
|
744
|
+
buf.push(line);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
// フッター
|
|
748
|
+
(0, formatter_1.renderFooter)(level, info.type, info.reportDateTime, info.publishingOffice, width, buf);
|
|
749
|
+
buf.push((0, formatter_1.frameBottom)(level, width));
|
|
750
|
+
buf.pushEmpty();
|
|
751
|
+
(0, formatter_1.flushWithRecap)(buf, level, width);
|
|
752
|
+
}
|
|
753
|
+
/** 長周期地震動観測情報を整形して表示 */
|
|
754
|
+
function displayLgObservationInfo(info) {
|
|
755
|
+
const level = lgObservationFrameLevel(info);
|
|
756
|
+
const label = typeLabel(info.type);
|
|
757
|
+
const width = (0, formatter_1.getFrameWidth)();
|
|
758
|
+
// コンパクトモード
|
|
759
|
+
if ((0, formatter_1.getDisplayMode)() === "compact") {
|
|
760
|
+
const parts = [];
|
|
761
|
+
parts.push(formatter_1.SEVERITY_LABELS[level]);
|
|
762
|
+
parts.push(label);
|
|
763
|
+
if (info.earthquake)
|
|
764
|
+
parts.push(info.earthquake.hypocenterName);
|
|
765
|
+
if (info.maxLgInt)
|
|
766
|
+
parts.push(`長周期${info.maxLgInt}`);
|
|
767
|
+
if (info.maxInt)
|
|
768
|
+
parts.push(`震度${info.maxInt}`);
|
|
769
|
+
const color = (0, formatter_1.frameColor)(level);
|
|
770
|
+
console.log(color(parts.join(" ")));
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
const buf = (0, formatter_1.createRenderBuffer)();
|
|
774
|
+
buf.pushEmpty();
|
|
775
|
+
buf.push((0, formatter_1.frameTop)(level, width));
|
|
776
|
+
// テスト電文
|
|
777
|
+
if (info.isTest) {
|
|
778
|
+
buf.push((0, formatter_1.frameLine)(level, theme.getRoleChalk("testBadge")(" テスト電文 "), width));
|
|
779
|
+
}
|
|
780
|
+
// タイトル行
|
|
781
|
+
const titleContent = chalk_1.default.bold(`${label}`) + chalk_1.default.gray(` ${info.infoType}`) + chalk_1.default.gray(` ${formatter_1.SEVERITY_LABELS[level]}`);
|
|
782
|
+
buf.pushTitle((0, formatter_1.frameLine)(level, titleContent, width));
|
|
783
|
+
// ヘッドライン
|
|
784
|
+
if (info.headline) {
|
|
785
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
786
|
+
const headlineWrapped = (0, formatter_1.wrapFrameLines)(level, chalk_1.default.bold.white(info.headline), width);
|
|
787
|
+
for (let i = 0; i < headlineWrapped.length; i++) {
|
|
788
|
+
if (i === 0) {
|
|
789
|
+
buf.pushHeadline(headlineWrapped[i]);
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
buf.push(headlineWrapped[i]);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
// カード: 長周期階級 / 震度 / M / 深さ
|
|
797
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
798
|
+
const cardParts = [];
|
|
799
|
+
if (info.maxLgInt) {
|
|
800
|
+
const lc = (0, formatter_1.lgIntensityColor)(info.maxLgInt);
|
|
801
|
+
cardParts.push(chalk_1.default.white("長周期階級 ") + lc.bold(info.maxLgInt));
|
|
802
|
+
}
|
|
803
|
+
if (info.maxInt) {
|
|
804
|
+
const ic = (0, formatter_1.intensityColor)(info.maxInt);
|
|
805
|
+
cardParts.push(chalk_1.default.white("最大震度 ") + ic.bold(info.maxInt));
|
|
806
|
+
}
|
|
807
|
+
if (info.earthquake?.magnitude) {
|
|
808
|
+
cardParts.push((0, formatter_1.colorMagnitude)(info.earthquake.magnitude));
|
|
809
|
+
}
|
|
810
|
+
if (info.earthquake?.depth) {
|
|
811
|
+
cardParts.push(chalk_1.default.white("深さ ") + chalk_1.default.white(info.earthquake.depth));
|
|
812
|
+
}
|
|
813
|
+
if (cardParts.length > 0) {
|
|
814
|
+
buf.pushCard((0, formatter_1.frameLine)(level, cardParts.join(chalk_1.default.gray(" │ ")), width));
|
|
815
|
+
}
|
|
816
|
+
// 震源詳細
|
|
817
|
+
if (info.earthquake) {
|
|
818
|
+
const eq = info.earthquake;
|
|
819
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
820
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("震源地: ") + theme.getRoleChalk("hypocenter")(eq.hypocenterName), width));
|
|
821
|
+
if (eq.originTime) {
|
|
822
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("発生: ") + chalk_1.default.white((0, formatter_1.formatTimestamp)(eq.originTime)), width));
|
|
823
|
+
}
|
|
824
|
+
if (eq.latitude && eq.longitude) {
|
|
825
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.white("位置: ") + chalk_1.default.white(`${eq.latitude} ${eq.longitude}`), width));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
// 地域リスト (LgInt 降順)
|
|
829
|
+
if (info.areas.length > 0) {
|
|
830
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
831
|
+
const sorted = [...info.areas].sort((a, b) => (0, formatter_1.lgIntToNumeric)(b.maxLgInt) - (0, formatter_1.lgIntToNumeric)(a.maxLgInt));
|
|
832
|
+
// 折りたたみ
|
|
833
|
+
const maxObs = (0, formatter_1.getMaxObservations)();
|
|
834
|
+
const displayAreas = maxObs != null ? sorted.slice(0, maxObs) : sorted;
|
|
835
|
+
const hiddenCount = sorted.length - displayAreas.length;
|
|
836
|
+
for (const area of displayAreas) {
|
|
837
|
+
const lc = (0, formatter_1.lgIntensityColor)(area.maxLgInt);
|
|
838
|
+
const ic = (0, formatter_1.intensityColor)(area.maxInt);
|
|
839
|
+
buf.push((0, formatter_1.frameLine)(level, lc(`長周期${area.maxLgInt}: `) +
|
|
840
|
+
chalk_1.default.white(area.name) +
|
|
841
|
+
ic(` (震度${area.maxInt})`), width));
|
|
842
|
+
}
|
|
843
|
+
if (hiddenCount > 0) {
|
|
844
|
+
buf.push((0, formatter_1.frameLine)(level, chalk_1.default.gray(`... 他 ${hiddenCount} 地点`), width));
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
// コメント
|
|
848
|
+
if (info.comment) {
|
|
849
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
850
|
+
const commentLines = info.comment.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
851
|
+
for (const line of commentLines) {
|
|
852
|
+
const wrapped = (0, formatter_1.wrapFrameLines)(level, chalk_1.default.gray(line.trimEnd()), width);
|
|
853
|
+
for (const wl of wrapped) {
|
|
854
|
+
buf.push(wl);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
// 詳細URI
|
|
859
|
+
if (info.detailUri) {
|
|
860
|
+
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
861
|
+
const uriWrapped = (0, formatter_1.wrapFrameLines)(level, theme.getRoleChalk("detailUri")(info.detailUri), width);
|
|
862
|
+
for (const wl of uriWrapped) {
|
|
863
|
+
buf.push(wl);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
// フッター
|
|
867
|
+
(0, formatter_1.renderFooter)(level, info.type, info.reportDateTime, info.publishingOffice, width, buf);
|
|
868
|
+
buf.push((0, formatter_1.frameBottom)(level, width));
|
|
869
|
+
buf.pushEmpty();
|
|
870
|
+
(0, formatter_1.flushWithRecap)(buf, level, width);
|
|
871
|
+
}
|