Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 7x 7x 191x 191x 6594x 158x 33x 7x 7x 53x 53x 351x 50x 3x | // SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
/**
* @module Generators/PoliticalIntelligence/Icons
* @description Heuristic emoji icon picking for political-intelligence
* documents and daily analysis runs.
*
* The icons are chosen to visually differentiate the most common
* artifact types (SWOT, PESTLE, threat matrices, coalition dynamics,
* etc.) without depending on a heavy icon library. Rules are evaluated
* in declaration order, so more-specific matches must come before more-
* general ones (e.g. `intelligence-brief` before `intelligence`).
*
* Lifted out of `political-intelligence.ts` so the icon-picking rules
* can be unit-tested in isolation and so future renderers (e.g.
* `news-indexes`, sitemap HTML) can reuse them without pulling in the
* 1500-LOC PI module.
*/
/** Ordered keyword โ icon rules for analysis documents. */
const DOCUMENT_ICON_RULES: readonly [readonly string[], string][] = [
[['readme'], '๐'],
[['swot'], '๐งญ'],
[['pestle'], '๐'],
[['stride'], '๐ก๏ธ'],
[['threat'], 'โ ๏ธ'],
[['risk'], '๐'],
[['coalition'], '๐ค'],
[['stakeholder'], '๐ฅ'],
[['actor'], '๐ค'],
[['impact'], '๐ฅ'],
[['scenario', 'forecast', 'outlook', 'wildcard', 'blackswan'], '๐ฎ'],
[['economic', 'imf', 'worldbank', 'fiscal', 'monetary'], '๐ถ'],
[['trade', 'tariff'], '๐ณ๏ธ'],
[['timeline', 'historical', 'parallel'], '๐ฐ๏ธ'],
[['methodology', 'guide', 'style'], '๐งญ'],
[['classification'], '๐ท๏ธ'],
[['intelligence-brief', 'brief'], '๐๏ธ'],
[['intelligence'], '๐'],
[['network'], '๐ธ๏ธ'],
[['velocity'], 'โก'],
[['productivity', 'pipeline', 'workflow-audit', 'workflow'], '๐ง'],
[['legislative', 'legislation'], 'โ๏ธ'],
[['motion'], '๐ณ๏ธ'],
[['proposition', 'proposal'], '๐'],
[['committee'], '๐๏ธ'],
[['vote', 'voting'], '๐ณ๏ธ'],
[['plenary', 'session', 'meeting'], '๐๏ธ'],
[['procedure'], '๐'],
[['event', 'schedule', 'agenda'], '๐
'],
[['mep', 'parliamentarian'], '๐ง\u200d๐ผ'],
[['consequence'], '๐ฟ'],
[['disruption'], '๐'],
[['reflection'], '๐ช'],
[['reliability', 'audit', 'quality'], 'โ
'],
[['attack-surface', 'attack'], '๐ก๏ธ'],
[['diagnostic', 'outage'], '๐'],
[['forces', 'influence'], 'โ๏ธ'],
[['osint', 'tradecraft'], '๐ต๏ธ'],
[['catalog', 'index'], '๐'],
[['capital'], '๐ผ'],
[['synthesis', 'cross-daily', 'summary'], '๐งฉ'],
[['cross-session', 'cross-run'], '๐'],
[['sentiment'], '๐ฌ'],
[['baseline', 'precomputed'], '๐'],
[['significance'], '๐ฏ'],
[['devil', 'advocate'], '๐'],
[['media', 'framing'], '๐บ'],
[['reform', 'anti-corruption'], '๐งน'],
[['recess'], '๐ด'],
[['per-file', 'per-artifact'], '๐๏ธ'],
[['adopted'], '๐'],
[['document'], '๐'],
[['artifact'], '๐'],
];
/** Default icon used by {@link pickDocumentIcon} when no rule matches */
export const DEFAULT_DOCUMENT_ICON = '๐';
/**
* Heuristically pick an icon for an analysis document/slug. The icons
* are chosen to visually differentiate the most common artifact types
* without depending on a heavy icon library.
*
* Rules are evaluated in declaration order โ first match wins. To add
* a new rule, add a `[hints, icon]` tuple at the **most-specific
* position**: e.g. `intelligence-brief` is matched before
* `intelligence` so the brief-specific newspaper icon (๐๏ธ) takes
* precedence over the generic magnifying-glass (๐).
*
* @param stem - File/directory name stem (will be lowercased internally)
* @returns A single emoji character (or grapheme cluster for ZWJ-joined emoji)
*/
export function pickDocumentIcon(stem: string): string {
const s = stem.toLowerCase();
for (const [hints, icon] of DOCUMENT_ICON_RULES) {
if (hints.some((h) => s.includes(h))) {
return icon;
}
}
return DEFAULT_DOCUMENT_ICON;
}
/** Ordered slug-prefix โ icon rules for daily runs. */
const RUN_ICON_RULES: readonly [readonly string[], string][] = [
[['breaking'], '๐จ'],
[['week-ahead', 'month-ahead', 'year-ahead'], '๐ญ'],
[['week-in-review', 'weekly-review'], '๐
'],
[['month-in-review', 'monthly-review'], '๐๏ธ'],
[['year-in-review'], '๐'],
[['motions'], '๐ณ๏ธ'],
[['propositions'], 'โ๏ธ'],
[['committee-reports', 'committee'], '๐๏ธ'],
[['translate'], '๐'],
[['deep'], '๐ฌ'],
];
/** Default icon used by {@link pickRunIcon} when no rule matches */
export const DEFAULT_RUN_ICON = '๐';
/**
* Pick an icon for a daily run based on its slug prefix.
*
* Unlike {@link pickDocumentIcon}, this matches by `startsWith` (not
* `includes`) โ runs are named with a canonical prefix per article
* type, so prefix-matching is precise enough and avoids false positives
* from words appearing inside run-id suffixes.
*
* @param slug - Run slug such as `breaking-run190` or `motions-run46`
* @returns A single emoji character (or grapheme cluster for ZWJ-joined emoji)
*/
export function pickRunIcon(slug: string): string {
const s = slug.toLowerCase();
for (const [prefixes, icon] of RUN_ICON_RULES) {
if (prefixes.some((p) => s.startsWith(p))) {
return icon;
}
}
return DEFAULT_RUN_ICON;
}
|