@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,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fitTokensToWidth = fitTokensToWidth;
|
|
4
|
+
const formatter_1 = require("../formatter");
|
|
5
|
+
const SEPARATOR = " ";
|
|
6
|
+
const SEPARATOR_WIDTH = 2;
|
|
7
|
+
function fitTokensToWidth(tokens, maxWidth) {
|
|
8
|
+
if (tokens.length === 0)
|
|
9
|
+
return "";
|
|
10
|
+
// Step 1: Check if all tokens fit at preferred width
|
|
11
|
+
const totalPreferred = tokens.reduce((sum, t) => sum + t.preferredWidth, 0);
|
|
12
|
+
const separatorTotal = SEPARATOR_WIDTH * Math.max(0, tokens.length - 1);
|
|
13
|
+
if (totalPreferred + separatorTotal <= maxWidth) {
|
|
14
|
+
return tokens.map((t) => t.text).join(SEPARATOR);
|
|
15
|
+
}
|
|
16
|
+
// Step 2: Drop tokens by priority (4 → 3 → 2)
|
|
17
|
+
let remaining = [...tokens];
|
|
18
|
+
for (const pri of [4, 3, 2]) {
|
|
19
|
+
const currentWidth = calcTotalWidth(remaining);
|
|
20
|
+
if (currentWidth <= maxWidth)
|
|
21
|
+
break;
|
|
22
|
+
remaining = remaining.filter((t) => !(t.priority === pri && t.dropMode === "drop"));
|
|
23
|
+
}
|
|
24
|
+
// Step 3: Shorten tokens if still too wide
|
|
25
|
+
if (calcTotalWidth(remaining) > maxWidth) {
|
|
26
|
+
remaining = remaining.map((t) => {
|
|
27
|
+
if (t.dropMode === "shorten" && t.shortText != null) {
|
|
28
|
+
return { ...t, text: t.shortText };
|
|
29
|
+
}
|
|
30
|
+
return t;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return remaining.map((t) => t.text).join(SEPARATOR);
|
|
34
|
+
}
|
|
35
|
+
function calcTotalWidth(tokens) {
|
|
36
|
+
if (tokens.length === 0)
|
|
37
|
+
return 0;
|
|
38
|
+
const textWidth = tokens.reduce((sum, t) => sum + (0, formatter_1.visualWidth)(t.text), 0);
|
|
39
|
+
const sepWidth = SEPARATOR_WIDTH * (tokens.length - 1);
|
|
40
|
+
return textWidth + sepWidth;
|
|
41
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildSparkline = buildSparkline;
|
|
7
|
+
exports.formatSummaryInterval = formatSummaryInterval;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const summary_tracker_1 = require("../engine/messages/summary-tracker");
|
|
10
|
+
/** sparkline で使う8段階の文字 */
|
|
11
|
+
const SPARK_CHARS = "▁▂▃▄▅▆▇█";
|
|
12
|
+
/** ドメインの表示ラベル */
|
|
13
|
+
const DOMAIN_LABELS = {
|
|
14
|
+
eew: "EEW",
|
|
15
|
+
earthquake: "地震",
|
|
16
|
+
tsunami: "津波",
|
|
17
|
+
seismicText: "テキスト",
|
|
18
|
+
lgObservation: "長周期",
|
|
19
|
+
volcano: "火山",
|
|
20
|
+
nankaiTrough: "南海トラフ",
|
|
21
|
+
raw: "その他",
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* sparklineData (数値配列) から sparkline 文字列を生成する。
|
|
25
|
+
* 最大値に対する比率で8段階の文字を選択する。全部0なら ▁ の繰り返し。
|
|
26
|
+
*/
|
|
27
|
+
function buildSparkline(data) {
|
|
28
|
+
const max = Math.max(...data);
|
|
29
|
+
if (max === 0) {
|
|
30
|
+
return SPARK_CHARS[0].repeat(data.length);
|
|
31
|
+
}
|
|
32
|
+
return data
|
|
33
|
+
.map((v) => {
|
|
34
|
+
const ratio = v / max;
|
|
35
|
+
const idx = Math.min(Math.round(ratio * (SPARK_CHARS.length - 1)), SPARK_CHARS.length - 1);
|
|
36
|
+
return SPARK_CHARS[idx];
|
|
37
|
+
})
|
|
38
|
+
.join("");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 要約行をフォーマットする。
|
|
42
|
+
* @param snapshot SummaryWindowTracker のスナップショット
|
|
43
|
+
* @param intervalMinutes 要約間隔(分)
|
|
44
|
+
* @param sparkline sparkline を含めるか
|
|
45
|
+
*/
|
|
46
|
+
function formatSummaryInterval(snapshot, intervalMinutes, sparkline) {
|
|
47
|
+
const parts = [];
|
|
48
|
+
// ドメイン別件数
|
|
49
|
+
const domainParts = [];
|
|
50
|
+
for (const [domain, count] of Object.entries(snapshot.byDomain)) {
|
|
51
|
+
if (count > 0) {
|
|
52
|
+
const label = DOMAIN_LABELS[domain] ?? domain;
|
|
53
|
+
domainParts.push(`${label} ${count}件`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ヘッダ
|
|
57
|
+
const header = chalk_1.default.gray(`── ${intervalMinutes}分要約 ──`);
|
|
58
|
+
const domainStr = domainParts.length > 0
|
|
59
|
+
? domainParts.join(chalk_1.default.gray(" | "))
|
|
60
|
+
: chalk_1.default.gray("受信なし");
|
|
61
|
+
// maxInt
|
|
62
|
+
const maxIntStr = snapshot.maxIntSeen != null
|
|
63
|
+
? chalk_1.default.gray(` (最大${snapshot.maxIntSeen})`)
|
|
64
|
+
: "";
|
|
65
|
+
parts.push(`${header} ${domainStr}${maxIntStr}`);
|
|
66
|
+
// sparkline 行
|
|
67
|
+
if (sparkline) {
|
|
68
|
+
const sparkStr = buildSparkline(snapshot.sparklineData);
|
|
69
|
+
parts.push(chalk_1.default.gray("受信 ") + sparkStr + chalk_1.default.gray(` (${summary_tracker_1.WINDOW_MINUTES}分)`));
|
|
70
|
+
}
|
|
71
|
+
return parts.join("\n");
|
|
72
|
+
}
|
package/dist/ui/theme.js
CHANGED
|
@@ -40,6 +40,8 @@ exports.DEFAULT_ROLES = exports.DEFAULT_PALETTE = void 0;
|
|
|
40
40
|
exports.hexToRgb = hexToRgb;
|
|
41
41
|
exports.rgbToHex = rgbToHex;
|
|
42
42
|
exports.resolveTheme = resolveTheme;
|
|
43
|
+
exports.setNightMode = setNightMode;
|
|
44
|
+
exports.isNightMode = isNightMode;
|
|
43
45
|
exports.getThemePath = getThemePath;
|
|
44
46
|
exports.loadTheme = loadTheme;
|
|
45
47
|
exports.loadThemeFromPath = loadThemeFromPath;
|
|
@@ -58,6 +60,7 @@ const fs = __importStar(require("fs"));
|
|
|
58
60
|
const path = __importStar(require("path"));
|
|
59
61
|
const chalk_1 = __importDefault(require("chalk"));
|
|
60
62
|
const config_1 = require("../config");
|
|
63
|
+
const night_overlay_1 = require("./night-overlay");
|
|
61
64
|
/** RoleStyleDef の型ガード */
|
|
62
65
|
function isRoleStyleDef(value) {
|
|
63
66
|
if (typeof value === "string")
|
|
@@ -352,7 +355,29 @@ function buildDefaultResolvedTheme() {
|
|
|
352
355
|
return deepFreezeTheme(theme);
|
|
353
356
|
}
|
|
354
357
|
// ── モジュール状態 ──
|
|
355
|
-
let
|
|
358
|
+
let baseTheme = buildDefaultResolvedTheme();
|
|
359
|
+
let nightModeEnabled = false;
|
|
360
|
+
let currentTheme = baseTheme;
|
|
361
|
+
// ── night mode ──
|
|
362
|
+
/** baseTheme に night overlay を適用して currentTheme を更新する */
|
|
363
|
+
function rebuildActiveTheme() {
|
|
364
|
+
if (nightModeEnabled) {
|
|
365
|
+
currentTheme = deepFreezeTheme((0, night_overlay_1.applyNightOverlay)(baseTheme));
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
currentTheme = baseTheme;
|
|
369
|
+
}
|
|
370
|
+
chalkCache.clear();
|
|
371
|
+
}
|
|
372
|
+
/** ナイトモードの ON/OFF を設定し、テーマを再構築する */
|
|
373
|
+
function setNightMode(enabled) {
|
|
374
|
+
nightModeEnabled = enabled;
|
|
375
|
+
rebuildActiveTheme();
|
|
376
|
+
}
|
|
377
|
+
/** ナイトモードが有効かどうかを返す */
|
|
378
|
+
function isNightMode() {
|
|
379
|
+
return nightModeEnabled;
|
|
380
|
+
}
|
|
356
381
|
// ── パス解決 ──
|
|
357
382
|
/** theme.json のパスを返す */
|
|
358
383
|
function getThemePath() {
|
|
@@ -367,14 +392,16 @@ function loadTheme() {
|
|
|
367
392
|
function loadThemeFromPath(themePath) {
|
|
368
393
|
chalkCache.clear();
|
|
369
394
|
if (!fs.existsSync(themePath)) {
|
|
370
|
-
|
|
395
|
+
baseTheme = buildDefaultResolvedTheme();
|
|
396
|
+
rebuildActiveTheme();
|
|
371
397
|
return [];
|
|
372
398
|
}
|
|
373
399
|
try {
|
|
374
400
|
const raw = fs.readFileSync(themePath, "utf-8");
|
|
375
401
|
const parsed = JSON.parse(raw);
|
|
376
402
|
if (typeof parsed !== "object" || parsed == null || Array.isArray(parsed)) {
|
|
377
|
-
|
|
403
|
+
baseTheme = buildDefaultResolvedTheme();
|
|
404
|
+
rebuildActiveTheme();
|
|
378
405
|
return ["theme.json の形式が不正です。デフォルトテーマを使用します。"];
|
|
379
406
|
}
|
|
380
407
|
const { themeFile, warnings: sanitizeWarnings } = sanitizeThemeInput(parsed);
|
|
@@ -382,11 +409,13 @@ function loadThemeFromPath(themePath) {
|
|
|
382
409
|
palette: exports.DEFAULT_PALETTE,
|
|
383
410
|
roles: exports.DEFAULT_ROLES,
|
|
384
411
|
});
|
|
385
|
-
|
|
412
|
+
baseTheme = deepFreezeTheme(theme);
|
|
413
|
+
rebuildActiveTheme();
|
|
386
414
|
return [...sanitizeWarnings, ...warnings];
|
|
387
415
|
}
|
|
388
416
|
catch (err) {
|
|
389
|
-
|
|
417
|
+
baseTheme = buildDefaultResolvedTheme();
|
|
418
|
+
rebuildActiveTheme();
|
|
390
419
|
if (err instanceof SyntaxError) {
|
|
391
420
|
return ["theme.json のJSONパースに失敗しました。デフォルトテーマを使用します。"];
|
|
392
421
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TipShuffler = void 0;
|
|
4
|
+
const waiting_tips_1 = require("./waiting-tips");
|
|
5
|
+
/**
|
|
6
|
+
* 待機中Tipのエポックデッキ生成シャッフラ。
|
|
7
|
+
*
|
|
8
|
+
* - カテゴリごとにシャッフルした後、同カテゴリ連続を避けつつ
|
|
9
|
+
* 全Tipを1エポック分のデッキにインターリーブする。
|
|
10
|
+
* - デッキを使い切ったら自動で再構築する。
|
|
11
|
+
* - タイミング制御は持たず、`next()` で次のTipを返すだけの純粋な順序供給器。
|
|
12
|
+
*/
|
|
13
|
+
class TipShuffler {
|
|
14
|
+
deck = [];
|
|
15
|
+
rng;
|
|
16
|
+
constructor(rng = Math.random) {
|
|
17
|
+
this.rng = rng;
|
|
18
|
+
this.rebuildDeck();
|
|
19
|
+
}
|
|
20
|
+
/** 次のTipを返す。デッキが空なら自動再構築。 */
|
|
21
|
+
next() {
|
|
22
|
+
if (this.deck.length === 0) {
|
|
23
|
+
this.rebuildDeck();
|
|
24
|
+
}
|
|
25
|
+
return this.deck.shift();
|
|
26
|
+
}
|
|
27
|
+
rebuildDeck() {
|
|
28
|
+
// 1. カテゴリごとにシャッフル
|
|
29
|
+
const buckets = [];
|
|
30
|
+
for (let ci = 0; ci < waiting_tips_1.TIP_CATEGORIES.length; ci++) {
|
|
31
|
+
const shuffled = this.shuffle([...waiting_tips_1.TIP_CATEGORIES[ci].tips]);
|
|
32
|
+
for (const tip of shuffled) {
|
|
33
|
+
buckets.push({ categoryIndex: ci, tip });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// 2. インターリーブ: 同カテゴリ連続を避けつつデッキ構築
|
|
37
|
+
this.deck = this.interleave(buckets);
|
|
38
|
+
}
|
|
39
|
+
/** 同カテゴリ連続を避けつつ全アイテムをインターリーブする */
|
|
40
|
+
interleave(items) {
|
|
41
|
+
// カテゴリごとのキューに分割
|
|
42
|
+
const queues = new Map();
|
|
43
|
+
for (const item of items) {
|
|
44
|
+
if (!queues.has(item.categoryIndex)) {
|
|
45
|
+
queues.set(item.categoryIndex, []);
|
|
46
|
+
}
|
|
47
|
+
queues.get(item.categoryIndex).push(item.tip);
|
|
48
|
+
}
|
|
49
|
+
const result = [];
|
|
50
|
+
let lastCategory = -1;
|
|
51
|
+
while (queues.size > 0) {
|
|
52
|
+
// 直前カテゴリ以外で残りがあるカテゴリから選択
|
|
53
|
+
const candidates = [...queues.keys()].filter((k) => k !== lastCategory);
|
|
54
|
+
if (candidates.length === 0) {
|
|
55
|
+
// 1カテゴリしか残っていない場合はそのまま流し込む
|
|
56
|
+
const remaining = [...queues.keys()][0];
|
|
57
|
+
result.push(...queues.get(remaining));
|
|
58
|
+
queues.delete(remaining);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
// ランダムに1カテゴリ選択
|
|
62
|
+
const chosen = candidates[Math.floor(this.rng() * candidates.length)];
|
|
63
|
+
const queue = queues.get(chosen);
|
|
64
|
+
result.push(queue.shift());
|
|
65
|
+
lastCategory = chosen;
|
|
66
|
+
if (queue.length === 0) {
|
|
67
|
+
queues.delete(chosen);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
/** Fisher-Yates シャッフル */
|
|
73
|
+
shuffle(arr) {
|
|
74
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
75
|
+
const j = Math.floor(this.rng() * (i + 1));
|
|
76
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
77
|
+
}
|
|
78
|
+
return arr;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.TipShuffler = TipShuffler;
|
|
@@ -86,13 +86,6 @@ function levelCodeToDisplay(code) {
|
|
|
86
86
|
};
|
|
87
87
|
return map[code] ?? code;
|
|
88
88
|
}
|
|
89
|
-
/** wrapFrameLines の結果を RenderBuffer に push */
|
|
90
|
-
function pushWrapped(buf, level, content, width) {
|
|
91
|
-
const lines = (0, formatter_1.wrapFrameLines)(level, content, width);
|
|
92
|
-
for (const line of lines) {
|
|
93
|
-
buf.push(line);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
89
|
// ── 火山本文ハイライト ──
|
|
97
90
|
/** 火山本文キーワード強調ルール */
|
|
98
91
|
const VOLCANO_HIGHLIGHT_RULES = [
|
|
@@ -211,20 +204,29 @@ function renderAlert(info, level, width, buf) {
|
|
|
211
204
|
cardParts.push(`(${actionLabel(info.action)})`);
|
|
212
205
|
buf.pushCard((0, formatter_1.frameLine)(level, ` ${cardParts.join(" ")}`, width));
|
|
213
206
|
}
|
|
214
|
-
// 対象市町村
|
|
207
|
+
// 対象市町村 (全件表示)
|
|
215
208
|
if (info.municipalities.length > 0) {
|
|
216
209
|
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
217
210
|
const muniNames = info.municipalities.map((m) => m.name);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
(0, formatter_1.renderSimpleNameList)({
|
|
212
|
+
level,
|
|
213
|
+
width,
|
|
214
|
+
items: muniNames,
|
|
215
|
+
label: "対象:",
|
|
216
|
+
buf,
|
|
217
|
+
});
|
|
222
218
|
}
|
|
223
219
|
// 対象海上予報区
|
|
224
220
|
if (info.marineAreas.length > 0) {
|
|
225
221
|
buf.push((0, formatter_1.frameDivider)(level, width));
|
|
226
222
|
const areaNames = info.marineAreas.map((a) => a.name);
|
|
227
|
-
|
|
223
|
+
(0, formatter_1.renderSimpleNameList)({
|
|
224
|
+
level,
|
|
225
|
+
width,
|
|
226
|
+
items: areaNames,
|
|
227
|
+
label: "対象海域:",
|
|
228
|
+
buf,
|
|
229
|
+
});
|
|
228
230
|
}
|
|
229
231
|
// 本文 (VolcanoActivity) / 防災事項 (VolcanoPrevention)
|
|
230
232
|
if (info.bodyText) {
|