magi-ai 0.1.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/LICENSE +21 -0
- package/README.ja.md +377 -0
- package/README.md +377 -0
- package/dist/bin/magi-benchmark.d.ts +14 -0
- package/dist/bin/magi-benchmark.js +93 -0
- package/dist/bin/magi-mcp.d.ts +8 -0
- package/dist/bin/magi-mcp.js +28 -0
- package/dist/bin/magi.d.ts +2 -0
- package/dist/bin/magi.js +634 -0
- package/dist/src/adapters/base.d.ts +34 -0
- package/dist/src/adapters/base.js +149 -0
- package/dist/src/adapters/claude.d.ts +29 -0
- package/dist/src/adapters/claude.js +65 -0
- package/dist/src/adapters/codex.d.ts +21 -0
- package/dist/src/adapters/codex.js +41 -0
- package/dist/src/adapters/gemini.d.ts +18 -0
- package/dist/src/adapters/gemini.js +31 -0
- package/dist/src/adapters/registry.d.ts +19 -0
- package/dist/src/adapters/registry.js +59 -0
- package/dist/src/audit/hash-chain.d.ts +21 -0
- package/dist/src/audit/hash-chain.js +70 -0
- package/dist/src/audit/types.d.ts +25 -0
- package/dist/src/audit/types.js +1 -0
- package/dist/src/audit/writer.d.ts +18 -0
- package/dist/src/audit/writer.js +100 -0
- package/dist/src/benchmark/golden-tasks.d.ts +9 -0
- package/dist/src/benchmark/golden-tasks.js +476 -0
- package/dist/src/benchmark/reporter.d.ts +5 -0
- package/dist/src/benchmark/reporter.js +107 -0
- package/dist/src/benchmark/runner.d.ts +30 -0
- package/dist/src/benchmark/runner.js +224 -0
- package/dist/src/benchmark/scorer.d.ts +12 -0
- package/dist/src/benchmark/scorer.js +124 -0
- package/dist/src/benchmark/types.d.ts +54 -0
- package/dist/src/benchmark/types.js +1 -0
- package/dist/src/cache/deliberation-cache.d.ts +49 -0
- package/dist/src/cache/deliberation-cache.js +127 -0
- package/dist/src/cli/commands/config-cmd.d.ts +11 -0
- package/dist/src/cli/commands/config-cmd.js +190 -0
- package/dist/src/cli/commands/demo.d.ts +12 -0
- package/dist/src/cli/commands/demo.js +66 -0
- package/dist/src/cli/commands/setup.d.ts +7 -0
- package/dist/src/cli/commands/setup.js +182 -0
- package/dist/src/cli/i18n.d.ts +89 -0
- package/dist/src/cli/i18n.js +176 -0
- package/dist/src/cli/interactive-select.d.ts +27 -0
- package/dist/src/cli/interactive-select.js +130 -0
- package/dist/src/cli/tui-setup.d.ts +24 -0
- package/dist/src/cli/tui-setup.js +42 -0
- package/dist/src/config/cli-detector.d.ts +37 -0
- package/dist/src/config/cli-detector.js +99 -0
- package/dist/src/config/user-config.d.ts +81 -0
- package/dist/src/config/user-config.js +134 -0
- package/dist/src/context/auto-collector.d.ts +43 -0
- package/dist/src/context/auto-collector.js +337 -0
- package/dist/src/context/manager.d.ts +35 -0
- package/dist/src/context/manager.js +162 -0
- package/dist/src/context/serializer.d.ts +20 -0
- package/dist/src/context/serializer.js +52 -0
- package/dist/src/demo/recorded-deliberation.d.ts +13 -0
- package/dist/src/demo/recorded-deliberation.js +277 -0
- package/dist/src/engine/angel-detector.d.ts +83 -0
- package/dist/src/engine/angel-detector.js +334 -0
- package/dist/src/engine/at-field.d.ts +40 -0
- package/dist/src/engine/at-field.js +195 -0
- package/dist/src/engine/berserk-orchestrator.d.ts +66 -0
- package/dist/src/engine/berserk-orchestrator.js +378 -0
- package/dist/src/engine/change-metrics.d.ts +56 -0
- package/dist/src/engine/change-metrics.js +214 -0
- package/dist/src/engine/consensus.d.ts +20 -0
- package/dist/src/engine/consensus.js +146 -0
- package/dist/src/engine/dead-sea-scrolls.d.ts +132 -0
- package/dist/src/engine/dead-sea-scrolls.js +610 -0
- package/dist/src/engine/drift-detector.d.ts +39 -0
- package/dist/src/engine/drift-detector.js +225 -0
- package/dist/src/engine/dummy-plug.d.ts +44 -0
- package/dist/src/engine/dummy-plug.js +190 -0
- package/dist/src/engine/engram-manager.d.ts +55 -0
- package/dist/src/engine/engram-manager.js +306 -0
- package/dist/src/engine/events.d.ts +130 -0
- package/dist/src/engine/events.js +44 -0
- package/dist/src/engine/gospel.d.ts +30 -0
- package/dist/src/engine/gospel.js +129 -0
- package/dist/src/engine/hallucination-detector.d.ts +33 -0
- package/dist/src/engine/hallucination-detector.js +215 -0
- package/dist/src/engine/human-resolver.d.ts +19 -0
- package/dist/src/engine/human-resolver.js +89 -0
- package/dist/src/engine/instrumentality.d.ts +64 -0
- package/dist/src/engine/instrumentality.js +297 -0
- package/dist/src/engine/iruel-battle.d.ts +79 -0
- package/dist/src/engine/iruel-battle.js +319 -0
- package/dist/src/engine/kernel/deliberation-kernel.d.ts +12 -0
- package/dist/src/engine/kernel/deliberation-kernel.js +303 -0
- package/dist/src/engine/kernel/index.d.ts +8 -0
- package/dist/src/engine/kernel/index.js +7 -0
- package/dist/src/engine/kernel/phase-runner.d.ts +10 -0
- package/dist/src/engine/kernel/phase-runner.js +155 -0
- package/dist/src/engine/kernel/post-processor.d.ts +17 -0
- package/dist/src/engine/kernel/post-processor.js +131 -0
- package/dist/src/engine/kernel/types.d.ts +107 -0
- package/dist/src/engine/kernel/types.js +1 -0
- package/dist/src/engine/kernel/unit-executor.d.ts +6 -0
- package/dist/src/engine/kernel/unit-executor.js +132 -0
- package/dist/src/engine/lcl-manager.d.ts +44 -0
- package/dist/src/engine/lcl-manager.js +143 -0
- package/dist/src/engine/middleware/cache.d.ts +7 -0
- package/dist/src/engine/middleware/cache.js +29 -0
- package/dist/src/engine/middleware/chain.d.ts +18 -0
- package/dist/src/engine/middleware/chain.js +45 -0
- package/dist/src/engine/middleware/firewall.d.ts +8 -0
- package/dist/src/engine/middleware/firewall.js +24 -0
- package/dist/src/engine/middleware/index.d.ts +4 -0
- package/dist/src/engine/middleware/index.js +3 -0
- package/dist/src/engine/middleware/types.d.ts +43 -0
- package/dist/src/engine/middleware/types.js +1 -0
- package/dist/src/engine/nebuchadnezzar-key.d.ts +61 -0
- package/dist/src/engine/nebuchadnezzar-key.js +203 -0
- package/dist/src/engine/neon-genesis.d.ts +52 -0
- package/dist/src/engine/neon-genesis.js +203 -0
- package/dist/src/engine/objective-judge.d.ts +53 -0
- package/dist/src/engine/objective-judge.js +214 -0
- package/dist/src/engine/offline-mode.d.ts +18 -0
- package/dist/src/engine/offline-mode.js +46 -0
- package/dist/src/engine/orchestrator.d.ts +79 -0
- package/dist/src/engine/orchestrator.js +58 -0
- package/dist/src/engine/secret-cipher.d.ts +26 -0
- package/dist/src/engine/secret-cipher.js +114 -0
- package/dist/src/engine/seele-council.d.ts +90 -0
- package/dist/src/engine/seele-council.js +482 -0
- package/dist/src/engine/self-destruct.d.ts +61 -0
- package/dist/src/engine/self-destruct.js +231 -0
- package/dist/src/engine/self-evolution.d.ts +64 -0
- package/dist/src/engine/self-evolution.js +368 -0
- package/dist/src/engine/sync-rate.d.ts +45 -0
- package/dist/src/engine/sync-rate.js +151 -0
- package/dist/src/engine/type666-firewall.d.ts +76 -0
- package/dist/src/engine/type666-firewall.js +343 -0
- package/dist/src/engine/umbilical-cable.d.ts +41 -0
- package/dist/src/engine/umbilical-cable.js +192 -0
- package/dist/src/index.d.ts +106 -0
- package/dist/src/index.js +426 -0
- package/dist/src/mcp/server.d.ts +38 -0
- package/dist/src/mcp/server.js +196 -0
- package/dist/src/metrics/token-tracker.d.ts +38 -0
- package/dist/src/metrics/token-tracker.js +112 -0
- package/dist/src/parsers/json-extractor.d.ts +9 -0
- package/dist/src/parsers/json-extractor.js +239 -0
- package/dist/src/parsers/opinion-schema.d.ts +81 -0
- package/dist/src/parsers/opinion-schema.js +147 -0
- package/dist/src/parsers/unstructured-parser.d.ts +20 -0
- package/dist/src/parsers/unstructured-parser.js +122 -0
- package/dist/src/pipelines/architecture.d.ts +10 -0
- package/dist/src/pipelines/architecture.js +9 -0
- package/dist/src/pipelines/bug-analysis.d.ts +9 -0
- package/dist/src/pipelines/bug-analysis.js +8 -0
- package/dist/src/pipelines/code-review.d.ts +10 -0
- package/dist/src/pipelines/code-review.js +30 -0
- package/dist/src/pipelines/custom.d.ts +14 -0
- package/dist/src/pipelines/custom.js +29 -0
- package/dist/src/pipelines/registry.d.ts +9 -0
- package/dist/src/pipelines/registry.js +20 -0
- package/dist/src/prompts/personas.d.ts +6 -0
- package/dist/src/prompts/personas.js +44 -0
- package/dist/src/prompts/schemas.d.ts +4 -0
- package/dist/src/prompts/schemas.js +24 -0
- package/dist/src/prompts/templates.d.ts +6 -0
- package/dist/src/prompts/templates.js +91 -0
- package/dist/src/repl/accessibility.d.ts +23 -0
- package/dist/src/repl/accessibility.js +46 -0
- package/dist/src/repl/banner.d.ts +4 -0
- package/dist/src/repl/banner.js +28 -0
- package/dist/src/repl/boot-animation.d.ts +13 -0
- package/dist/src/repl/boot-animation.js +143 -0
- package/dist/src/repl/completer.d.ts +21 -0
- package/dist/src/repl/completer.js +168 -0
- package/dist/src/repl/context.d.ts +24 -0
- package/dist/src/repl/context.js +42 -0
- package/dist/src/repl/display-utils.d.ts +13 -0
- package/dist/src/repl/display-utils.js +65 -0
- package/dist/src/repl/event-listener.d.ts +18 -0
- package/dist/src/repl/event-listener.js +112 -0
- package/dist/src/repl/export-formatter.d.ts +8 -0
- package/dist/src/repl/export-formatter.js +73 -0
- package/dist/src/repl/ghost-text.d.ts +31 -0
- package/dist/src/repl/ghost-text.js +119 -0
- package/dist/src/repl/handoff-animation.d.ts +15 -0
- package/dist/src/repl/handoff-animation.js +65 -0
- package/dist/src/repl/history.d.ts +16 -0
- package/dist/src/repl/history.js +130 -0
- package/dist/src/repl/job-registry.d.ts +26 -0
- package/dist/src/repl/job-registry.js +80 -0
- package/dist/src/repl/magi-repl.d.ts +72 -0
- package/dist/src/repl/magi-repl.js +1008 -0
- package/dist/src/repl/multiline-input.d.ts +45 -0
- package/dist/src/repl/multiline-input.js +78 -0
- package/dist/src/repl/prompt-builder.d.ts +19 -0
- package/dist/src/repl/prompt-builder.js +36 -0
- package/dist/src/repl/repl-state.d.ts +5 -0
- package/dist/src/repl/repl-state.js +19 -0
- package/dist/src/repl/result-display.d.ts +8 -0
- package/dist/src/repl/result-display.js +195 -0
- package/dist/src/repl/session-stats.d.ts +26 -0
- package/dist/src/repl/session-stats.js +119 -0
- package/dist/src/repl/slash-commands.d.ts +60 -0
- package/dist/src/repl/slash-commands.js +725 -0
- package/dist/src/repl/terminal-sanitize.d.ts +14 -0
- package/dist/src/repl/terminal-sanitize.js +19 -0
- package/dist/src/reporters/console.d.ts +7 -0
- package/dist/src/reporters/console.js +78 -0
- package/dist/src/reporters/json.d.ts +2 -0
- package/dist/src/reporters/json.js +3 -0
- package/dist/src/reporters/markdown.d.ts +2 -0
- package/dist/src/reporters/markdown.js +65 -0
- package/dist/src/reporters/streaming.d.ts +20 -0
- package/dist/src/reporters/streaming.js +178 -0
- package/dist/src/tui/activity-log.d.ts +23 -0
- package/dist/src/tui/activity-log.js +67 -0
- package/dist/src/tui/animations.d.ts +39 -0
- package/dist/src/tui/animations.js +167 -0
- package/dist/src/tui/ansi.d.ts +28 -0
- package/dist/src/tui/ansi.js +51 -0
- package/dist/src/tui/boot-sequence.d.ts +11 -0
- package/dist/src/tui/boot-sequence.js +98 -0
- package/dist/src/tui/colors.d.ts +101 -0
- package/dist/src/tui/colors.js +71 -0
- package/dist/src/tui/header.d.ts +24 -0
- package/dist/src/tui/header.js +122 -0
- package/dist/src/tui/index.d.ts +3 -0
- package/dist/src/tui/index.js +3 -0
- package/dist/src/tui/keypress.d.ts +25 -0
- package/dist/src/tui/keypress.js +95 -0
- package/dist/src/tui/layout.d.ts +74 -0
- package/dist/src/tui/layout.js +171 -0
- package/dist/src/tui/magi-tui.d.ts +101 -0
- package/dist/src/tui/magi-tui.js +754 -0
- package/dist/src/tui/panel.d.ts +45 -0
- package/dist/src/tui/panel.js +292 -0
- package/dist/src/tui/screen-buffer.d.ts +54 -0
- package/dist/src/tui/screen-buffer.js +262 -0
- package/dist/src/tui/status-bar.d.ts +25 -0
- package/dist/src/tui/status-bar.js +124 -0
- package/dist/src/tui/terminal-detect.d.ts +26 -0
- package/dist/src/tui/terminal-detect.js +44 -0
- package/dist/src/tui/tui-helpers.d.ts +12 -0
- package/dist/src/tui/tui-helpers.js +37 -0
- package/dist/src/types/adapter.d.ts +75 -0
- package/dist/src/types/adapter.js +36 -0
- package/dist/src/types/config.d.ts +108 -0
- package/dist/src/types/config.js +85 -0
- package/dist/src/types/consensus.d.ts +55 -0
- package/dist/src/types/consensus.js +17 -0
- package/dist/src/types/core.d.ts +178 -0
- package/dist/src/types/core.js +85 -0
- package/dist/src/types/magi-api.d.ts +62 -0
- package/dist/src/types/magi-api.js +7 -0
- package/dist/src/types/phase-h.d.ts +142 -0
- package/dist/src/types/phase-h.js +7 -0
- package/dist/src/types/phase-i.d.ts +186 -0
- package/dist/src/types/phase-i.js +6 -0
- package/dist/src/types/phase-k.d.ts +259 -0
- package/dist/src/types/phase-k.js +6 -0
- package/dist/src/types/phase-l.d.ts +199 -0
- package/dist/src/types/phase-l.js +6 -0
- package/dist/src/types/pipeline.d.ts +37 -0
- package/dist/src/types/pipeline.js +2 -0
- package/dist/src/utils/abstain-factory.d.ts +2 -0
- package/dist/src/utils/abstain-factory.js +18 -0
- package/dist/src/utils/errors.d.ts +34 -0
- package/dist/src/utils/errors.js +59 -0
- package/dist/src/utils/file-validator.d.ts +50 -0
- package/dist/src/utils/file-validator.js +124 -0
- package/dist/src/utils/fire-and-forget.d.ts +5 -0
- package/dist/src/utils/fire-and-forget.js +10 -0
- package/dist/src/utils/flag-validator.d.ts +21 -0
- package/dist/src/utils/flag-validator.js +79 -0
- package/dist/src/utils/freeze.d.ts +8 -0
- package/dist/src/utils/freeze.js +16 -0
- package/dist/src/utils/language-detector.d.ts +16 -0
- package/dist/src/utils/language-detector.js +159 -0
- package/dist/src/utils/latency-tracker.d.ts +45 -0
- package/dist/src/utils/latency-tracker.js +100 -0
- package/dist/src/utils/logger.d.ts +33 -0
- package/dist/src/utils/logger.js +112 -0
- package/dist/src/utils/process.d.ts +40 -0
- package/dist/src/utils/process.js +253 -0
- package/dist/src/utils/retry.d.ts +12 -0
- package/dist/src/utils/retry.js +30 -0
- package/dist/src/utils/safe-fs.d.ts +38 -0
- package/dist/src/utils/safe-fs.js +56 -0
- package/dist/src/utils/safe-json-parse.d.ts +15 -0
- package/dist/src/utils/safe-json-parse.js +49 -0
- package/dist/src/utils/sanitize.d.ts +14 -0
- package/dist/src/utils/sanitize.js +186 -0
- package/dist/src/utils/semaphore.d.ts +22 -0
- package/dist/src/utils/semaphore.js +57 -0
- package/dist/src/utils/shutdown.d.ts +6 -0
- package/dist/src/utils/shutdown.js +51 -0
- package/dist/src/utils/tty.d.ts +5 -0
- package/dist/src/utils/tty.js +7 -0
- package/package.json +82 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation controller for TUI effects.
|
|
3
|
+
* Manages breathing, cascade, flash, and overlay animations.
|
|
4
|
+
*/
|
|
5
|
+
import { isApproval, isRejection } from '../types/consensus.js';
|
|
6
|
+
// ── Constants ───────────────────────────────────────────────
|
|
7
|
+
const BREATHING_INTERVAL_MS = 62; // ~16fps
|
|
8
|
+
const BREATHING_CYCLE_MS = 2600;
|
|
9
|
+
const FLASH_INTERVAL_MS = 150;
|
|
10
|
+
const FLASH_COUNT = 3;
|
|
11
|
+
// ── Animation Controller ────────────────────────────────────
|
|
12
|
+
export class AnimationController {
|
|
13
|
+
timers = new Map();
|
|
14
|
+
disposed = false;
|
|
15
|
+
/**
|
|
16
|
+
* Start breathing animation for a unit.
|
|
17
|
+
* Calls onFrame with a phase value (0.0 ~ 1.0) cycling at ~16fps.
|
|
18
|
+
*/
|
|
19
|
+
startBreathing(unitId, onFrame) {
|
|
20
|
+
this.startBreathingWithOffset(unitId, 0, onFrame);
|
|
21
|
+
}
|
|
22
|
+
startBreathingWithOffset(unitId, phaseOffset, onFrame) {
|
|
23
|
+
this.stopBreathing(unitId);
|
|
24
|
+
if (this.disposed)
|
|
25
|
+
return;
|
|
26
|
+
let phase = phaseOffset % 1.0;
|
|
27
|
+
const step = BREATHING_INTERVAL_MS / BREATHING_CYCLE_MS;
|
|
28
|
+
const timer = setInterval(() => {
|
|
29
|
+
phase = (phase + step) % 1.0;
|
|
30
|
+
onFrame(phase);
|
|
31
|
+
}, BREATHING_INTERVAL_MS);
|
|
32
|
+
this.timers.set(`breath:${unitId}`, timer);
|
|
33
|
+
}
|
|
34
|
+
/** Stop breathing animation for a unit. */
|
|
35
|
+
stopBreathing(unitId) {
|
|
36
|
+
const key = `breath:${unitId}`;
|
|
37
|
+
const timer = this.timers.get(key);
|
|
38
|
+
if (timer) {
|
|
39
|
+
clearInterval(timer);
|
|
40
|
+
this.timers.delete(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Cascade reveal: call onReveal for each unit with a staggered delay.
|
|
45
|
+
* Returns a promise that resolves when all reveals are done.
|
|
46
|
+
*/
|
|
47
|
+
async cascade(units, onReveal) {
|
|
48
|
+
if (this.disposed)
|
|
49
|
+
return;
|
|
50
|
+
for (let i = 0; i < units.length; i++) {
|
|
51
|
+
if (this.disposed)
|
|
52
|
+
return;
|
|
53
|
+
onReveal(units[i]);
|
|
54
|
+
if (i < units.length - 1) {
|
|
55
|
+
await sleep(cascadeDelayMs(i, units.length));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* BERSERK flash: toggle red flash on/off multiple times.
|
|
61
|
+
*/
|
|
62
|
+
async berserkFlash(onFlash) {
|
|
63
|
+
if (this.disposed)
|
|
64
|
+
return;
|
|
65
|
+
for (let i = 0; i < FLASH_COUNT; i++) {
|
|
66
|
+
if (this.disposed)
|
|
67
|
+
return;
|
|
68
|
+
onFlash(true);
|
|
69
|
+
await sleep(FLASH_INTERVAL_MS);
|
|
70
|
+
if (this.disposed)
|
|
71
|
+
return;
|
|
72
|
+
onFlash(false);
|
|
73
|
+
await sleep(FLASH_INTERVAL_MS);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* UNANIMOUS reveal: show overlay text after a brief pause.
|
|
78
|
+
*/
|
|
79
|
+
async unanimousReveal(decision, onOverlay) {
|
|
80
|
+
if (this.disposed)
|
|
81
|
+
return;
|
|
82
|
+
await sleep(400);
|
|
83
|
+
if (this.disposed)
|
|
84
|
+
return;
|
|
85
|
+
const approve = isApproval(decision);
|
|
86
|
+
const text = approve ? '可 決' : '否 決';
|
|
87
|
+
onOverlay(text, approve);
|
|
88
|
+
}
|
|
89
|
+
async verdictLock(decision, onOverlay) {
|
|
90
|
+
if (this.disposed)
|
|
91
|
+
return;
|
|
92
|
+
const tone = isApproval(decision) ? 'approve' : isRejection(decision) ? 'reject' : 'deadlock';
|
|
93
|
+
const text = tone === 'approve' ? '可 決' : tone === 'reject' ? '否 決' : '膠 着';
|
|
94
|
+
const timings = verdictTiming(tone);
|
|
95
|
+
for (const timing of timings) {
|
|
96
|
+
onOverlay(text, tone, false);
|
|
97
|
+
await sleep(timing.onMs);
|
|
98
|
+
if (timing.offMs > 0) {
|
|
99
|
+
onOverlay(text, tone, true);
|
|
100
|
+
await sleep(timing.offMs);
|
|
101
|
+
}
|
|
102
|
+
if (this.disposed)
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
onOverlay(text, tone, false);
|
|
106
|
+
}
|
|
107
|
+
async linePulse(length, onPulse) {
|
|
108
|
+
if (this.disposed)
|
|
109
|
+
return;
|
|
110
|
+
for (let i = 0; i < length; i++) {
|
|
111
|
+
if (this.disposed)
|
|
112
|
+
return;
|
|
113
|
+
onPulse(i);
|
|
114
|
+
await sleep(linePulseDelay(i, length));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Stop all animations and prevent new ones. */
|
|
118
|
+
dispose() {
|
|
119
|
+
this.disposed = true;
|
|
120
|
+
for (const timer of this.timers.values()) {
|
|
121
|
+
clearInterval(timer);
|
|
122
|
+
}
|
|
123
|
+
this.timers.clear();
|
|
124
|
+
}
|
|
125
|
+
/** Check if a breathing animation is active. */
|
|
126
|
+
isBreathing(unitId) {
|
|
127
|
+
return this.timers.has(`breath:${unitId}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function sleep(ms) {
|
|
131
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
132
|
+
}
|
|
133
|
+
function cascadeDelayMs(index, total) {
|
|
134
|
+
if (total <= 1)
|
|
135
|
+
return 0;
|
|
136
|
+
const progress = index / Math.max(1, total - 1);
|
|
137
|
+
return Math.round(90 + progress * 110);
|
|
138
|
+
}
|
|
139
|
+
function linePulseDelay(index, total) {
|
|
140
|
+
if (total <= 1)
|
|
141
|
+
return 28;
|
|
142
|
+
const progress = index / Math.max(1, total - 1);
|
|
143
|
+
return Math.round(18 + Math.sin(progress * Math.PI) * 18);
|
|
144
|
+
}
|
|
145
|
+
function verdictTiming(tone) {
|
|
146
|
+
switch (tone) {
|
|
147
|
+
case 'approve':
|
|
148
|
+
return [
|
|
149
|
+
{ onMs: 180, offMs: 70 },
|
|
150
|
+
{ onMs: 150, offMs: 80 },
|
|
151
|
+
{ onMs: 260, offMs: 0 },
|
|
152
|
+
];
|
|
153
|
+
case 'reject':
|
|
154
|
+
return [
|
|
155
|
+
{ onMs: 110, offMs: 70 },
|
|
156
|
+
{ onMs: 120, offMs: 70 },
|
|
157
|
+
{ onMs: 200, offMs: 0 },
|
|
158
|
+
];
|
|
159
|
+
default:
|
|
160
|
+
return [
|
|
161
|
+
{ onMs: 130, offMs: 60 },
|
|
162
|
+
{ onMs: 120, offMs: 60 },
|
|
163
|
+
{ onMs: 120, offMs: 60 },
|
|
164
|
+
{ onMs: 220, offMs: 0 },
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANSI escape code constants and helpers.
|
|
3
|
+
* Low-level terminal control — chalk doesn't cover cursor movement or screen control.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ALT_SCREEN_ENTER = "\u001B[?1049h";
|
|
6
|
+
export declare const ALT_SCREEN_LEAVE = "\u001B[?1049l";
|
|
7
|
+
export declare const CURSOR_HIDE = "\u001B[?25l";
|
|
8
|
+
export declare const CURSOR_SHOW = "\u001B[?25h";
|
|
9
|
+
export declare const CLEAR_SCREEN = "\u001B[2J";
|
|
10
|
+
export declare const CURSOR_HOME = "\u001B[H";
|
|
11
|
+
export declare function moveTo(row: number, col: number): string;
|
|
12
|
+
export declare function moveUp(n: number): string;
|
|
13
|
+
export declare function moveDown(n: number): string;
|
|
14
|
+
export declare function moveRight(n: number): string;
|
|
15
|
+
export declare function moveLeft(n: number): string;
|
|
16
|
+
export declare const ERASE_LINE = "\u001B[2K";
|
|
17
|
+
export declare const ERASE_TO_END = "\u001B[0J";
|
|
18
|
+
export declare const RESET = "\u001B[0m";
|
|
19
|
+
export declare const BOLD = "\u001B[1m";
|
|
20
|
+
export declare const DIM = "\u001B[2m";
|
|
21
|
+
export declare const ITALIC = "\u001B[3m";
|
|
22
|
+
export declare const UNDERLINE = "\u001B[4m";
|
|
23
|
+
export declare const BLINK = "\u001B[5m";
|
|
24
|
+
export declare const REVERSE = "\u001B[7m";
|
|
25
|
+
export declare function fg(r: number, g: number, b: number): string;
|
|
26
|
+
export declare function bg(r: number, g: number, b: number): string;
|
|
27
|
+
export declare const SYNC_BEGIN = "\u001B[?2026h";
|
|
28
|
+
export declare const SYNC_END = "\u001B[?2026l";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANSI escape code constants and helpers.
|
|
3
|
+
* Low-level terminal control — chalk doesn't cover cursor movement or screen control.
|
|
4
|
+
*/
|
|
5
|
+
const ESC = '\x1b';
|
|
6
|
+
const CSI = `${ESC}[`;
|
|
7
|
+
// ── Screen control ──────────────────────────────────────────
|
|
8
|
+
export const ALT_SCREEN_ENTER = `${CSI}?1049h`;
|
|
9
|
+
export const ALT_SCREEN_LEAVE = `${CSI}?1049l`;
|
|
10
|
+
export const CURSOR_HIDE = `${CSI}?25l`;
|
|
11
|
+
export const CURSOR_SHOW = `${CSI}?25h`;
|
|
12
|
+
export const CLEAR_SCREEN = `${CSI}2J`;
|
|
13
|
+
export const CURSOR_HOME = `${CSI}H`;
|
|
14
|
+
// ── Cursor movement (1-based row/col) ───────────────────────
|
|
15
|
+
export function moveTo(row, col) {
|
|
16
|
+
return `${CSI}${row};${col}H`;
|
|
17
|
+
}
|
|
18
|
+
export function moveUp(n) {
|
|
19
|
+
return n > 0 ? `${CSI}${n}A` : '';
|
|
20
|
+
}
|
|
21
|
+
export function moveDown(n) {
|
|
22
|
+
return n > 0 ? `${CSI}${n}B` : '';
|
|
23
|
+
}
|
|
24
|
+
export function moveRight(n) {
|
|
25
|
+
return n > 0 ? `${CSI}${n}C` : '';
|
|
26
|
+
}
|
|
27
|
+
export function moveLeft(n) {
|
|
28
|
+
return n > 0 ? `${CSI}${n}D` : '';
|
|
29
|
+
}
|
|
30
|
+
// ── Erase ───────────────────────────────────────────────────
|
|
31
|
+
export const ERASE_LINE = `${CSI}2K`;
|
|
32
|
+
export const ERASE_TO_END = `${CSI}0J`;
|
|
33
|
+
// ── Text attributes ─────────────────────────────────────────
|
|
34
|
+
export const RESET = `${CSI}0m`;
|
|
35
|
+
export const BOLD = `${CSI}1m`;
|
|
36
|
+
export const DIM = `${CSI}2m`;
|
|
37
|
+
export const ITALIC = `${CSI}3m`;
|
|
38
|
+
export const UNDERLINE = `${CSI}4m`;
|
|
39
|
+
export const BLINK = `${CSI}5m`;
|
|
40
|
+
export const REVERSE = `${CSI}7m`;
|
|
41
|
+
// ── True Color (24-bit RGB) ─────────────────────────────────
|
|
42
|
+
export function fg(r, g, b) {
|
|
43
|
+
return `${CSI}38;2;${r};${g};${b}m`;
|
|
44
|
+
}
|
|
45
|
+
export function bg(r, g, b) {
|
|
46
|
+
return `${CSI}48;2;${r};${g};${b}m`;
|
|
47
|
+
}
|
|
48
|
+
// ── Synchronized output (DEC Mode 2026) ─────────────────────
|
|
49
|
+
// Prevents tearing on supported terminals; ignored on unsupported ones.
|
|
50
|
+
export const SYNC_BEGIN = `${CSI}?2026h`;
|
|
51
|
+
export const SYNC_END = `${CSI}?2026l`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NERV-style boot sequence animation.
|
|
3
|
+
* Displays a UNIX bootstrap dump before the main TUI layout appears.
|
|
4
|
+
*/
|
|
5
|
+
export interface BootSequenceOptions {
|
|
6
|
+
write: (s: string) => void;
|
|
7
|
+
cols: number;
|
|
8
|
+
rows: number;
|
|
9
|
+
skipAnimation?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function runBootSequence(options: BootSequenceOptions): Promise<void>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NERV-style boot sequence animation.
|
|
3
|
+
* Displays a UNIX bootstrap dump before the main TUI layout appears.
|
|
4
|
+
*/
|
|
5
|
+
import { ALT_SCREEN_ENTER, CURSOR_HIDE, CLEAR_SCREEN, CURSOR_HOME, fg, bg, RESET, BOLD, DIM, REVERSE } from './ansi.js';
|
|
6
|
+
import { EVA_PALETTE } from './colors.js';
|
|
7
|
+
const READY_BOX_WIDTH = 38;
|
|
8
|
+
// ── Boot lines ──────────────────────────────────────────────
|
|
9
|
+
const BOOT_LINES = [
|
|
10
|
+
{ text: '', delay: 100 },
|
|
11
|
+
{ text: 'MAGI SYSTEM v0.1.0 (Build 2015-EVA)', delay: 60, color: 'orange' },
|
|
12
|
+
{ text: 'Copyright (c) GEHIRN / NERV — Not for distribution', delay: 40, color: 'dim' },
|
|
13
|
+
{ text: '', delay: 80 },
|
|
14
|
+
{ text: 'POST: Memory test .............. 262144K OK', delay: 50 },
|
|
15
|
+
{ text: 'POST: Persona matrix check ..... 3 units found', delay: 50 },
|
|
16
|
+
{ text: '', delay: 60 },
|
|
17
|
+
{ text: 'Initializing MELCHIOR-1 ........ ', delay: 70 },
|
|
18
|
+
{ text: ' [ONLINE] Scientist persona loaded', delay: 40, color: 'green' },
|
|
19
|
+
{ text: 'Initializing BALTHASAR-2 ....... ', delay: 70 },
|
|
20
|
+
{ text: ' [ONLINE] Pragmatist persona loaded', delay: 40, color: 'green' },
|
|
21
|
+
{ text: 'Initializing CASPER-3 .......... ', delay: 70 },
|
|
22
|
+
{ text: ' [ONLINE] Reviewer persona loaded', delay: 40, color: 'green' },
|
|
23
|
+
{ text: '', delay: 80 },
|
|
24
|
+
{ text: 'Loading consensus protocol ...... ACTIVE', delay: 50 },
|
|
25
|
+
{ text: 'Synchronization rate ........... NOMINAL (50.0%)', delay: 50 },
|
|
26
|
+
{ text: 'A.T. Field barrier ............. ACTIVE (Level 1)', delay: 50, color: 'green' },
|
|
27
|
+
{ text: 'Umbilical cable ................ ALL CONNECTED', delay: 50, color: 'green' },
|
|
28
|
+
{ text: 'Gospel chronicle ............... INITIALIZED', delay: 40 },
|
|
29
|
+
{ text: 'Dummy plug system .............. STANDBY', delay: 40, color: 'dim' },
|
|
30
|
+
{ text: '', delay: 60 },
|
|
31
|
+
{ text: 'WARNING: Berserk threshold set to 4.0', delay: 30, color: 'red' },
|
|
32
|
+
{ text: '', delay: 80 },
|
|
33
|
+
{ text: renderReadyBoxBorder('╔', '╗'), delay: 50, color: 'orange' },
|
|
34
|
+
{ text: renderReadyBoxLine('M A G I S Y S T E M'), delay: 50, color: 'orange' },
|
|
35
|
+
{ text: renderReadyBoxLine('MELCHIOR-1 / BALTHASAR-2'), delay: 50, color: 'orange' },
|
|
36
|
+
{ text: renderReadyBoxLine('/ CASPER-3 ONLINE /'), delay: 50, color: 'orange' },
|
|
37
|
+
{ text: renderReadyBoxBorder('╚', '╝'), delay: 50, color: 'orange' },
|
|
38
|
+
{ text: '', delay: 100 },
|
|
39
|
+
{ text: ' 提 訴 / 決 議', delay: 80, color: 'orange' },
|
|
40
|
+
{ text: '', delay: 80 },
|
|
41
|
+
{ text: 'MAGI SYSTEM READY', delay: 80, color: 'green' },
|
|
42
|
+
{ text: '', delay: 300 },
|
|
43
|
+
];
|
|
44
|
+
// ── Boot sequence runner ────────────────────────────────────
|
|
45
|
+
export async function runBootSequence(options) {
|
|
46
|
+
const { write, skipAnimation } = options;
|
|
47
|
+
// Enter alternate screen
|
|
48
|
+
write(ALT_SCREEN_ENTER + CURSOR_HIDE + CLEAR_SCREEN + CURSOR_HOME);
|
|
49
|
+
if (skipAnimation)
|
|
50
|
+
return;
|
|
51
|
+
const gc = EVA_PALETTE.scanline;
|
|
52
|
+
const oc = EVA_PALETTE.frame;
|
|
53
|
+
const rc = EVA_PALETTE.warning;
|
|
54
|
+
for (const line of BOOT_LINES) {
|
|
55
|
+
let colored;
|
|
56
|
+
switch (line.color) {
|
|
57
|
+
case 'green':
|
|
58
|
+
colored = `${fg(gc.r, gc.g, gc.b)}${line.text}${RESET}`;
|
|
59
|
+
break;
|
|
60
|
+
case 'orange':
|
|
61
|
+
colored = `${fg(oc.r, oc.g, oc.b)}${BOLD}${line.text}${RESET}`;
|
|
62
|
+
break;
|
|
63
|
+
case 'dim':
|
|
64
|
+
colored = `${DIM}${line.text}${RESET}`;
|
|
65
|
+
break;
|
|
66
|
+
case 'red':
|
|
67
|
+
colored = `${fg(rc.r, rc.g, rc.b)}${line.text}${RESET}`;
|
|
68
|
+
break;
|
|
69
|
+
default:
|
|
70
|
+
colored = `${fg(gc.r, gc.g, gc.b)}${DIM}${line.text}${RESET}`;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
write(colored + '\r\n');
|
|
74
|
+
// Random jitter on delay for realism
|
|
75
|
+
const jitter = Math.floor(Math.random() * 30) - 15;
|
|
76
|
+
await sleep(Math.max(10, line.delay + jitter));
|
|
77
|
+
}
|
|
78
|
+
await flashTransition(write);
|
|
79
|
+
// Brief pause, then clear for main layout
|
|
80
|
+
await sleep(200);
|
|
81
|
+
write(CLEAR_SCREEN + CURSOR_HOME);
|
|
82
|
+
}
|
|
83
|
+
function sleep(ms) {
|
|
84
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
85
|
+
}
|
|
86
|
+
function renderReadyBoxBorder(left, right) {
|
|
87
|
+
return ` ${left}${'═'.repeat(READY_BOX_WIDTH)}${right}`;
|
|
88
|
+
}
|
|
89
|
+
function renderReadyBoxLine(text) {
|
|
90
|
+
return ` ║${text.padStart(Math.floor((READY_BOX_WIDTH + text.length) / 2)).padEnd(READY_BOX_WIDTH)}║`;
|
|
91
|
+
}
|
|
92
|
+
async function flashTransition(write) {
|
|
93
|
+
const c = EVA_PALETTE.frame;
|
|
94
|
+
const bgc = EVA_PALETTE.background;
|
|
95
|
+
write(`${REVERSE}${fg(c.r, c.g, c.b)}${bg(bgc.r, bgc.g, bgc.b)}MAGI SYSTEM READY${RESET}\r\n`);
|
|
96
|
+
await sleep(80);
|
|
97
|
+
write(`${fg(c.r, c.g, c.b)}${BOLD}提 訴 / 決 議${RESET}\r\n`);
|
|
98
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evangelion-inspired color palette and utilities.
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
export interface RGBColor {
|
|
6
|
+
readonly r: number;
|
|
7
|
+
readonly g: number;
|
|
8
|
+
readonly b: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const EVA_PALETTE: {
|
|
11
|
+
readonly melchior: {
|
|
12
|
+
readonly r: 232;
|
|
13
|
+
readonly g: 66;
|
|
14
|
+
readonly b: 28;
|
|
15
|
+
};
|
|
16
|
+
readonly balthasar: {
|
|
17
|
+
readonly r: 86;
|
|
18
|
+
readonly g: 216;
|
|
19
|
+
readonly b: 126;
|
|
20
|
+
};
|
|
21
|
+
readonly casper: {
|
|
22
|
+
readonly r: 86;
|
|
23
|
+
readonly g: 171;
|
|
24
|
+
readonly b: 248;
|
|
25
|
+
};
|
|
26
|
+
readonly frame: {
|
|
27
|
+
readonly r: 255;
|
|
28
|
+
readonly g: 153;
|
|
29
|
+
readonly b: 52;
|
|
30
|
+
};
|
|
31
|
+
readonly background: {
|
|
32
|
+
readonly r: 6;
|
|
33
|
+
readonly g: 8;
|
|
34
|
+
readonly b: 11;
|
|
35
|
+
};
|
|
36
|
+
readonly scanline: {
|
|
37
|
+
readonly r: 118;
|
|
38
|
+
readonly g: 255;
|
|
39
|
+
readonly b: 168;
|
|
40
|
+
};
|
|
41
|
+
readonly textPrimary: {
|
|
42
|
+
readonly r: 255;
|
|
43
|
+
readonly g: 183;
|
|
44
|
+
readonly b: 74;
|
|
45
|
+
};
|
|
46
|
+
readonly textSecondary: {
|
|
47
|
+
readonly r: 255;
|
|
48
|
+
readonly g: 239;
|
|
49
|
+
readonly b: 214;
|
|
50
|
+
};
|
|
51
|
+
readonly warning: {
|
|
52
|
+
readonly r: 255;
|
|
53
|
+
readonly g: 88;
|
|
54
|
+
readonly b: 48;
|
|
55
|
+
};
|
|
56
|
+
readonly approve: {
|
|
57
|
+
readonly r: 132;
|
|
58
|
+
readonly g: 255;
|
|
59
|
+
readonly b: 138;
|
|
60
|
+
};
|
|
61
|
+
readonly reject: {
|
|
62
|
+
readonly r: 255;
|
|
63
|
+
readonly g: 112;
|
|
64
|
+
readonly b: 92;
|
|
65
|
+
};
|
|
66
|
+
readonly abstain: {
|
|
67
|
+
readonly r: 151;
|
|
68
|
+
readonly g: 159;
|
|
69
|
+
readonly b: 173;
|
|
70
|
+
};
|
|
71
|
+
readonly magi: {
|
|
72
|
+
readonly r: 255;
|
|
73
|
+
readonly g: 191;
|
|
74
|
+
readonly b: 72;
|
|
75
|
+
};
|
|
76
|
+
readonly grid: {
|
|
77
|
+
readonly r: 53;
|
|
78
|
+
readonly g: 33;
|
|
79
|
+
readonly b: 18;
|
|
80
|
+
};
|
|
81
|
+
readonly panelShade: {
|
|
82
|
+
readonly r: 18;
|
|
83
|
+
readonly g: 12;
|
|
84
|
+
readonly b: 10;
|
|
85
|
+
};
|
|
86
|
+
readonly stampGhost: {
|
|
87
|
+
readonly r: 92;
|
|
88
|
+
readonly g: 62;
|
|
89
|
+
readonly b: 36;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
export declare function unitColor(unit: string): RGBColor;
|
|
93
|
+
export declare function unitChalk(unit: string): ReturnType<typeof chalk.rgb>;
|
|
94
|
+
export declare function voteColor(vote: string): RGBColor;
|
|
95
|
+
export declare function colorFg(text: string, color: RGBColor): string;
|
|
96
|
+
export declare function colorBg(text: string, fgColor: RGBColor, bgColor: RGBColor): string;
|
|
97
|
+
/**
|
|
98
|
+
* Adjust brightness of a color (0.0 = black, 1.0 = original, >1.0 = brighter).
|
|
99
|
+
*/
|
|
100
|
+
export declare function adjustBrightness(color: RGBColor, factor: number): RGBColor;
|
|
101
|
+
export declare function mixColors(base: RGBColor, accent: RGBColor, ratio: number): RGBColor;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evangelion-inspired color palette and utilities.
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { fg, bg, RESET } from './ansi.js';
|
|
6
|
+
export const EVA_PALETTE = {
|
|
7
|
+
melchior: { r: 232, g: 66, b: 28 }, // #E8421C — 赤
|
|
8
|
+
balthasar: { r: 86, g: 216, b: 126 }, // #56D87E — 緑
|
|
9
|
+
casper: { r: 86, g: 171, b: 248 }, // #56ABF8 — 青
|
|
10
|
+
frame: { r: 255, g: 153, b: 52 }, // #FF9934 — 橙
|
|
11
|
+
background: { r: 6, g: 8, b: 11 }, // #06080B — 深い黒
|
|
12
|
+
scanline: { r: 118, g: 255, b: 168 }, // #76FFA8 — 緑
|
|
13
|
+
textPrimary: { r: 255, g: 183, b: 74 }, // #FFB74A — 明橙
|
|
14
|
+
textSecondary: { r: 255, g: 239, b: 214 }, // #FFEFD6 — 温白
|
|
15
|
+
warning: { r: 255, g: 88, b: 48 }, // #FF5830 — 警告赤
|
|
16
|
+
approve: { r: 132, g: 255, b: 138 }, // #84FF8A — 承認緑
|
|
17
|
+
reject: { r: 255, g: 112, b: 92 }, // #FF705C — 否決赤
|
|
18
|
+
abstain: { r: 151, g: 159, b: 173 }, // #979FAD — 保留灰
|
|
19
|
+
magi: { r: 255, g: 191, b: 72 }, // #FFBF48 — MAGI node
|
|
20
|
+
grid: { r: 53, g: 33, b: 18 }, // #352112 — 暗い橙
|
|
21
|
+
panelShade: { r: 18, g: 12, b: 10 }, // #120C0A — 面の陰影
|
|
22
|
+
stampGhost: { r: 92, g: 62, b: 36 }, // #5C3E24 — 点滅時の残像
|
|
23
|
+
};
|
|
24
|
+
// ── Unit → Color mapping ────────────────────────────────────
|
|
25
|
+
const UNIT_COLOR_MAP = {
|
|
26
|
+
MELCHIOR: EVA_PALETTE.melchior,
|
|
27
|
+
BALTHASAR: EVA_PALETTE.balthasar,
|
|
28
|
+
CASPER: EVA_PALETTE.casper,
|
|
29
|
+
};
|
|
30
|
+
export function unitColor(unit) {
|
|
31
|
+
return UNIT_COLOR_MAP[unit] ?? EVA_PALETTE.frame;
|
|
32
|
+
}
|
|
33
|
+
export function unitChalk(unit) {
|
|
34
|
+
const c = unitColor(unit);
|
|
35
|
+
return chalk.rgb(c.r, c.g, c.b);
|
|
36
|
+
}
|
|
37
|
+
// ── Vote → Color mapping ────────────────────────────────────
|
|
38
|
+
export function voteColor(vote) {
|
|
39
|
+
switch (vote) {
|
|
40
|
+
case 'APPROVE': return EVA_PALETTE.approve;
|
|
41
|
+
case 'REJECT': return EVA_PALETTE.reject;
|
|
42
|
+
case 'ABSTAIN': return EVA_PALETTE.abstain;
|
|
43
|
+
default: return EVA_PALETTE.textSecondary;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ── Colorize helpers (raw ANSI for ScreenBuffer) ────────────
|
|
47
|
+
export function colorFg(text, color) {
|
|
48
|
+
return `${fg(color.r, color.g, color.b)}${text}${RESET}`;
|
|
49
|
+
}
|
|
50
|
+
export function colorBg(text, fgColor, bgColor) {
|
|
51
|
+
return `${fg(fgColor.r, fgColor.g, fgColor.b)}${bg(bgColor.r, bgColor.g, bgColor.b)}${text}${RESET}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Adjust brightness of a color (0.0 = black, 1.0 = original, >1.0 = brighter).
|
|
55
|
+
*/
|
|
56
|
+
export function adjustBrightness(color, factor) {
|
|
57
|
+
return {
|
|
58
|
+
r: Math.max(0, Math.min(255, Math.round(color.r * factor))),
|
|
59
|
+
g: Math.max(0, Math.min(255, Math.round(color.g * factor))),
|
|
60
|
+
b: Math.max(0, Math.min(255, Math.round(color.b * factor))),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function mixColors(base, accent, ratio) {
|
|
64
|
+
const clamped = Math.max(0, Math.min(1, ratio));
|
|
65
|
+
const inv = 1 - clamped;
|
|
66
|
+
return {
|
|
67
|
+
r: Math.round(base.r * inv + accent.r * clamped),
|
|
68
|
+
g: Math.round(base.g * inv + accent.g * clamped),
|
|
69
|
+
b: Math.round(base.b * inv + accent.b * clamped),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NERV-style header panel renderer.
|
|
3
|
+
* Displays deliberation metadata in Evangelion aesthetic.
|
|
4
|
+
*/
|
|
5
|
+
import type { ScreenBuffer } from './screen-buffer.js';
|
|
6
|
+
import type { Rect } from './layout.js';
|
|
7
|
+
export interface HeaderData {
|
|
8
|
+
taskTitle: string;
|
|
9
|
+
taskType: string;
|
|
10
|
+
deliberationId: string;
|
|
11
|
+
sourceLabel?: string;
|
|
12
|
+
priorityLabel?: string;
|
|
13
|
+
modeLabel?: string;
|
|
14
|
+
codeLabel?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface VerdictStampData {
|
|
17
|
+
text: string;
|
|
18
|
+
tone: 'pending' | 'approve' | 'reject' | 'deadlock' | 'warning';
|
|
19
|
+
blink?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class HeaderRenderer {
|
|
22
|
+
drawHeader(buf: ScreenBuffer, rect: Rect, data: HeaderData): void;
|
|
23
|
+
drawVerdictStamp(buf: ScreenBuffer, rect: Rect, data: VerdictStampData): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NERV-style header panel renderer.
|
|
3
|
+
* Displays deliberation metadata in Evangelion aesthetic.
|
|
4
|
+
*/
|
|
5
|
+
import { BOX_DOUBLE } from './layout.js';
|
|
6
|
+
import { EVA_PALETTE, mixColors } from './colors.js';
|
|
7
|
+
import { ATTR_BOLD, stringDisplayWidth, truncateToDisplayWidth } from './screen-buffer.js';
|
|
8
|
+
import { deriveSourceLabel as sharedDeriveSourceLabel, derivePriorityLabel as sharedDerivePriorityLabel } from './tui-helpers.js';
|
|
9
|
+
// ── Header Renderer ─────────────────────────────────────────
|
|
10
|
+
export class HeaderRenderer {
|
|
11
|
+
drawHeader(buf, rect, data) {
|
|
12
|
+
const fc = EVA_PALETTE.frame;
|
|
13
|
+
const tc = EVA_PALETTE.textPrimary;
|
|
14
|
+
const tw = EVA_PALETTE.textSecondary;
|
|
15
|
+
const { row, col, width } = rect;
|
|
16
|
+
const reservedStamp = width >= 72 ? 18 : 0;
|
|
17
|
+
const usableWidth = Math.max(16, width - reservedStamp - 4);
|
|
18
|
+
// Line 1: 「提 訴」 (left) and 「決 議」 (right)
|
|
19
|
+
const leftLabel = '提 訴';
|
|
20
|
+
const rightLabel = '決 議';
|
|
21
|
+
buf.writeAt(row, col + 2, leftLabel, fc.r, fc.g, fc.b, 0, 0, 0, ATTR_BOLD);
|
|
22
|
+
buf.writeAt(row, col + width - stringDisplayWidth(rightLabel) - 2, rightLabel, fc.r, fc.g, fc.b, 0, 0, 0, ATTR_BOLD);
|
|
23
|
+
// Line 2: Double-line separator
|
|
24
|
+
for (let x = col + 2; x < col + width - 2; x++) {
|
|
25
|
+
buf.writeAt(row + 1, x, BOX_DOUBLE.h, fc.r, fc.g, fc.b);
|
|
26
|
+
}
|
|
27
|
+
// Line 3: CODE + FILE + TYPE
|
|
28
|
+
const code = data.codeLabel ?? generateCode(data.deliberationId);
|
|
29
|
+
const idShort = data.deliberationId.slice(0, 8);
|
|
30
|
+
const source = data.sourceLabel ?? truncateToDisplayWidth(sharedDeriveSourceLabel(data.taskType, data.taskTitle), 18);
|
|
31
|
+
const meta = truncateToDisplayWidth(`CODE ${code} • FILE ${source} • TYPE ${data.taskType.toUpperCase()}`, usableWidth);
|
|
32
|
+
buf.writeAt(row + 2, col + 2, meta, tc.r, tc.g, tc.b);
|
|
33
|
+
// Line 4: PRIORITY + MODE + ID
|
|
34
|
+
const priority = data.priorityLabel ?? sharedDerivePriorityLabel(data.taskType);
|
|
35
|
+
const mode = data.modeLabel ?? 'NORMAL';
|
|
36
|
+
const instrumentation = truncateToDisplayWidth(`PRIORITY ${priority} • MODE ${mode} • ID ${idShort}`, usableWidth);
|
|
37
|
+
buf.writeAt(row + 3, col + 2, instrumentation, tc.r, tc.g, tc.b);
|
|
38
|
+
// Line 5: Task title (normalize whitespace from shell line-wrapping, then truncate)
|
|
39
|
+
const title = truncateToDisplayWidth(normalizeTitle(data.taskTitle), width - 6);
|
|
40
|
+
buf.writeAt(row + 4, col + 2, title, tw.r, tw.g, tw.b);
|
|
41
|
+
}
|
|
42
|
+
drawVerdictStamp(buf, rect, data) {
|
|
43
|
+
const c = verdictToneColor(data.tone);
|
|
44
|
+
const label = truncateToDisplayWidth(data.text, rect.width - 2);
|
|
45
|
+
const innerWidth = Math.max(1, rect.width - 2);
|
|
46
|
+
const leftPad = Math.max(0, Math.floor((innerWidth - stringDisplayWidth(label)) / 2));
|
|
47
|
+
const border = data.blink ? mixColors(EVA_PALETTE.background, EVA_PALETTE.stampGhost, 0.8) : c;
|
|
48
|
+
const fill = stampFillColor(data.tone);
|
|
49
|
+
for (let y = rect.row; y < rect.row + rect.height; y++) {
|
|
50
|
+
for (let x = rect.col; x < rect.col + rect.width; x++) {
|
|
51
|
+
buf.writeAt(y, x, ' ', EVA_PALETTE.textSecondary.r, EVA_PALETTE.textSecondary.g, EVA_PALETTE.textSecondary.b, fill.r, fill.g, fill.b);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (rect.height >= 3 && rect.width >= 4) {
|
|
55
|
+
for (let x = rect.col; x < rect.col + rect.width; x++) {
|
|
56
|
+
buf.writeAt(rect.row, x, BOX_DOUBLE.h, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
57
|
+
buf.writeAt(rect.row + rect.height - 1, x, BOX_DOUBLE.h, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
58
|
+
}
|
|
59
|
+
buf.writeAt(rect.row, rect.col, BOX_DOUBLE.tl, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
60
|
+
buf.writeAt(rect.row, rect.col + rect.width - 1, BOX_DOUBLE.tr, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
61
|
+
buf.writeAt(rect.row + rect.height - 1, rect.col, BOX_DOUBLE.bl, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
62
|
+
buf.writeAt(rect.row + rect.height - 1, rect.col + rect.width - 1, BOX_DOUBLE.br, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
63
|
+
for (let y = rect.row + 1; y < rect.row + rect.height - 1; y++) {
|
|
64
|
+
buf.writeAt(y, rect.col, BOX_DOUBLE.v, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
65
|
+
buf.writeAt(y, rect.col + rect.width - 1, BOX_DOUBLE.v, border.r, border.g, border.b, fill.r, fill.g, fill.b);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!data.blink) {
|
|
69
|
+
buf.writeAt(rect.row + Math.floor(rect.height / 2), rect.col + 1 + leftPad, label, c.r, c.g, c.b, fill.r, fill.g, fill.b, ATTR_BOLD);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// CJK character range regex for detecting CJK-to-CJK spurious spaces
|
|
74
|
+
const CJK = '\\u3000-\\u9FFF\\uF900-\\uFAFF\\u{20000}-\\u{2FA1F}';
|
|
75
|
+
const CJK_SPACE_RE = new RegExp(`([${CJK}])\\s+([${CJK}])`, 'gu');
|
|
76
|
+
/** Normalize title text for display: strip zero-width chars, collapse whitespace, remove CJK-internal spaces. */
|
|
77
|
+
function normalizeTitle(raw) {
|
|
78
|
+
return raw
|
|
79
|
+
.replace(/[\u200B-\u200D\u2060\uFEFF]/g, '') // zero-width chars
|
|
80
|
+
.replace(/[\u00A0\u3000]/g, ' ') // NBSP + ideographic space → ASCII space
|
|
81
|
+
.replace(/\s+/g, ' ') // collapse whitespace
|
|
82
|
+
.replace(CJK_SPACE_RE, '$1$2') // remove space between CJK chars
|
|
83
|
+
.trim();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generate a 3-digit code from deliberation ID hash (Eva aesthetic).
|
|
87
|
+
* Deterministic: same ID always produces same code.
|
|
88
|
+
*/
|
|
89
|
+
function generateCode(id) {
|
|
90
|
+
let hash = 0;
|
|
91
|
+
for (let i = 0; i < id.length; i++) {
|
|
92
|
+
hash = ((hash << 5) - hash + id.charCodeAt(i)) | 0;
|
|
93
|
+
}
|
|
94
|
+
const num = Math.abs(hash) % 1000;
|
|
95
|
+
return String(num).padStart(3, '0');
|
|
96
|
+
}
|
|
97
|
+
function verdictToneColor(tone) {
|
|
98
|
+
switch (tone) {
|
|
99
|
+
case 'approve':
|
|
100
|
+
return EVA_PALETTE.approve;
|
|
101
|
+
case 'reject':
|
|
102
|
+
return EVA_PALETTE.reject;
|
|
103
|
+
case 'deadlock':
|
|
104
|
+
case 'warning':
|
|
105
|
+
return EVA_PALETTE.warning;
|
|
106
|
+
default:
|
|
107
|
+
return EVA_PALETTE.frame;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function stampFillColor(tone) {
|
|
111
|
+
switch (tone) {
|
|
112
|
+
case 'approve':
|
|
113
|
+
return mixColors(EVA_PALETTE.background, EVA_PALETTE.approve, 0.16);
|
|
114
|
+
case 'reject':
|
|
115
|
+
return mixColors(EVA_PALETTE.background, EVA_PALETTE.reject, 0.18);
|
|
116
|
+
case 'deadlock':
|
|
117
|
+
case 'warning':
|
|
118
|
+
return mixColors(EVA_PALETTE.background, EVA_PALETTE.frame, 0.18);
|
|
119
|
+
default:
|
|
120
|
+
return mixColors(EVA_PALETTE.background, EVA_PALETTE.grid, 0.42);
|
|
121
|
+
}
|
|
122
|
+
}
|