arbiter-ai 1.0.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/README.md +41 -0
- package/assets/jerom_16x16.png +0 -0
- package/dist/arbiter.d.ts +43 -0
- package/dist/arbiter.js +486 -0
- package/dist/context-analyzer.d.ts +15 -0
- package/dist/context-analyzer.js +603 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +165 -0
- package/dist/orchestrator.d.ts +31 -0
- package/dist/orchestrator.js +227 -0
- package/dist/router.d.ts +187 -0
- package/dist/router.js +1135 -0
- package/dist/router.test.d.ts +15 -0
- package/dist/router.test.js +95 -0
- package/dist/session-persistence.d.ts +9 -0
- package/dist/session-persistence.js +63 -0
- package/dist/session-persistence.test.d.ts +1 -0
- package/dist/session-persistence.test.js +165 -0
- package/dist/sound.d.ts +31 -0
- package/dist/sound.js +50 -0
- package/dist/state.d.ts +72 -0
- package/dist/state.js +107 -0
- package/dist/state.test.d.ts +1 -0
- package/dist/state.test.js +194 -0
- package/dist/test-headless.d.ts +1 -0
- package/dist/test-headless.js +155 -0
- package/dist/tui/index.d.ts +14 -0
- package/dist/tui/index.js +17 -0
- package/dist/tui/layout.d.ts +30 -0
- package/dist/tui/layout.js +200 -0
- package/dist/tui/render.d.ts +57 -0
- package/dist/tui/render.js +266 -0
- package/dist/tui/scene.d.ts +64 -0
- package/dist/tui/scene.js +366 -0
- package/dist/tui/screens/CharacterSelect-termkit.d.ts +18 -0
- package/dist/tui/screens/CharacterSelect-termkit.js +216 -0
- package/dist/tui/screens/ForestIntro-termkit.d.ts +15 -0
- package/dist/tui/screens/ForestIntro-termkit.js +856 -0
- package/dist/tui/screens/GitignoreCheck-termkit.d.ts +14 -0
- package/dist/tui/screens/GitignoreCheck-termkit.js +185 -0
- package/dist/tui/screens/TitleScreen-termkit.d.ts +14 -0
- package/dist/tui/screens/TitleScreen-termkit.js +132 -0
- package/dist/tui/screens/index.d.ts +9 -0
- package/dist/tui/screens/index.js +10 -0
- package/dist/tui/tileset.d.ts +97 -0
- package/dist/tui/tileset.js +237 -0
- package/dist/tui/tui-termkit.d.ts +34 -0
- package/dist/tui/tui-termkit.js +2602 -0
- package/dist/tui/types.d.ts +41 -0
- package/dist/tui/types.js +4 -0
- package/package.json +71 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitignore Check Screen (terminal-kit version)
|
|
3
|
+
*
|
|
4
|
+
* Checks if Arbiter's generated files are gitignored in the user's project.
|
|
5
|
+
* If not, prompts user to add them. Skips silently if not in a git repo.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Shows the gitignore check prompt using terminal-kit.
|
|
9
|
+
* Skips silently if not in a git repo or files are already ignored.
|
|
10
|
+
*
|
|
11
|
+
* @returns Promise<void> - Resolves when check is complete
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkGitignore(): Promise<void>;
|
|
14
|
+
export default checkGitignore;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitignore Check Screen (terminal-kit version)
|
|
3
|
+
*
|
|
4
|
+
* Checks if Arbiter's generated files are gitignored in the user's project.
|
|
5
|
+
* If not, prompts user to add them. Skips silently if not in a git repo.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import termKit from 'terminal-kit';
|
|
11
|
+
const term = termKit.terminal;
|
|
12
|
+
// ANSI codes
|
|
13
|
+
const DIM = '\x1b[2m';
|
|
14
|
+
const RESET = '\x1b[0m';
|
|
15
|
+
const YELLOW = '\x1b[33m';
|
|
16
|
+
const GREEN = '\x1b[32m';
|
|
17
|
+
// Files that Arbiter creates and should be gitignored
|
|
18
|
+
const ARBITER_FILES = ['.claude/.arbiter-session.json', '.claude/arbiter.tmp.log'];
|
|
19
|
+
// What to append to .gitignore
|
|
20
|
+
const GITIGNORE_ENTRIES = `
|
|
21
|
+
# Arbiter
|
|
22
|
+
.claude/.arbiter-session.json
|
|
23
|
+
.claude/arbiter.tmp.log
|
|
24
|
+
`;
|
|
25
|
+
/**
|
|
26
|
+
* Check if we're in a git repository
|
|
27
|
+
* @returns The git root path, or null if not in a repo
|
|
28
|
+
*/
|
|
29
|
+
function getGitRoot() {
|
|
30
|
+
try {
|
|
31
|
+
const root = execSync('git rev-parse --show-toplevel', {
|
|
32
|
+
encoding: 'utf-8',
|
|
33
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
34
|
+
}).trim();
|
|
35
|
+
return root;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if a file is gitignored
|
|
43
|
+
* @param filePath - Path to check (relative to git root)
|
|
44
|
+
* @returns true if ignored, false if not
|
|
45
|
+
*/
|
|
46
|
+
function isGitignored(filePath) {
|
|
47
|
+
try {
|
|
48
|
+
execSync(`git check-ignore -q ${filePath}`, {
|
|
49
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
50
|
+
});
|
|
51
|
+
return true; // exit code 0 = ignored
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false; // exit code 1 = not ignored
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Append entries to .gitignore file
|
|
59
|
+
* @param gitRoot - Git repository root path
|
|
60
|
+
*/
|
|
61
|
+
function addToGitignore(gitRoot) {
|
|
62
|
+
const gitignorePath = path.join(gitRoot, '.gitignore');
|
|
63
|
+
// Append to existing or create new
|
|
64
|
+
fs.appendFileSync(gitignorePath, GITIGNORE_ENTRIES, 'utf-8');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Shows the gitignore check prompt using terminal-kit.
|
|
68
|
+
* Skips silently if not in a git repo or files are already ignored.
|
|
69
|
+
*
|
|
70
|
+
* @returns Promise<void> - Resolves when check is complete
|
|
71
|
+
*/
|
|
72
|
+
export async function checkGitignore() {
|
|
73
|
+
// Check if we're in a git repo
|
|
74
|
+
const gitRoot = getGitRoot();
|
|
75
|
+
if (!gitRoot) {
|
|
76
|
+
return; // Not a git repo, skip silently
|
|
77
|
+
}
|
|
78
|
+
// Check which files are not gitignored
|
|
79
|
+
const unignoredFiles = ARBITER_FILES.filter((file) => !isGitignored(file));
|
|
80
|
+
if (unignoredFiles.length === 0) {
|
|
81
|
+
return; // All files already ignored, skip silently
|
|
82
|
+
}
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
// Initialize terminal
|
|
85
|
+
term.fullscreen(true);
|
|
86
|
+
term.hideCursor();
|
|
87
|
+
term.grabInput({ mouse: 'button' });
|
|
88
|
+
// Get terminal dimensions
|
|
89
|
+
let width = 180;
|
|
90
|
+
let height = 50;
|
|
91
|
+
if (typeof term.width === 'number' && Number.isFinite(term.width) && term.width > 0) {
|
|
92
|
+
width = term.width;
|
|
93
|
+
}
|
|
94
|
+
if (typeof term.height === 'number' && Number.isFinite(term.height) && term.height > 0) {
|
|
95
|
+
height = term.height;
|
|
96
|
+
}
|
|
97
|
+
// Content lines
|
|
98
|
+
const lines = [
|
|
99
|
+
"The Arbiter creates files that shouldn't be committed:",
|
|
100
|
+
'',
|
|
101
|
+
...unignoredFiles.map((f) => ` ${f}`),
|
|
102
|
+
'',
|
|
103
|
+
'Add to .gitignore? [y/n]',
|
|
104
|
+
];
|
|
105
|
+
// Calculate centering
|
|
106
|
+
const maxLineWidth = Math.max(...lines.map((l) => l.length));
|
|
107
|
+
const contentHeight = lines.length;
|
|
108
|
+
const startX = Math.max(1, Math.floor((width - maxLineWidth) / 2));
|
|
109
|
+
const startY = Math.max(1, Math.floor((height - contentHeight) / 2));
|
|
110
|
+
// Clear screen
|
|
111
|
+
term.clear();
|
|
112
|
+
// Draw content
|
|
113
|
+
lines.forEach((line, idx) => {
|
|
114
|
+
term.moveTo(startX, startY + idx);
|
|
115
|
+
if (idx === 0) {
|
|
116
|
+
// Header in yellow
|
|
117
|
+
process.stdout.write(`${YELLOW}${line}${RESET}`);
|
|
118
|
+
}
|
|
119
|
+
else if (line.startsWith(' ')) {
|
|
120
|
+
// File paths dimmed
|
|
121
|
+
process.stdout.write(`${DIM}${line}${RESET}`);
|
|
122
|
+
}
|
|
123
|
+
else if (line.includes('[y/n]')) {
|
|
124
|
+
// Prompt
|
|
125
|
+
process.stdout.write(line);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
process.stdout.write(line);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
/**
|
|
132
|
+
* Cleanup and restore terminal
|
|
133
|
+
*/
|
|
134
|
+
function cleanup() {
|
|
135
|
+
term.removeAllListeners('key');
|
|
136
|
+
term.grabInput(false);
|
|
137
|
+
term.fullscreen(false);
|
|
138
|
+
term.hideCursor(false);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Show brief success message
|
|
142
|
+
*/
|
|
143
|
+
function showSuccess() {
|
|
144
|
+
const msg = 'Added to .gitignore';
|
|
145
|
+
const msgX = Math.max(1, Math.floor((width - msg.length) / 2));
|
|
146
|
+
const msgY = startY + lines.length + 1;
|
|
147
|
+
term.moveTo(msgX, msgY);
|
|
148
|
+
process.stdout.write(`${GREEN}${msg}${RESET}`);
|
|
149
|
+
// Brief pause so user sees the message
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
cleanup();
|
|
152
|
+
resolve();
|
|
153
|
+
}, 800);
|
|
154
|
+
}
|
|
155
|
+
// Handle key press
|
|
156
|
+
term.on('key', (key) => {
|
|
157
|
+
// Exit on quit keys
|
|
158
|
+
if (key === 'CTRL_C' || key === 'CTRL_Z') {
|
|
159
|
+
cleanup();
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
// Yes - add to gitignore
|
|
163
|
+
if (key === 'y' || key === 'Y') {
|
|
164
|
+
try {
|
|
165
|
+
addToGitignore(gitRoot);
|
|
166
|
+
showSuccess();
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Failed to write, just continue
|
|
170
|
+
cleanup();
|
|
171
|
+
resolve();
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// No - skip
|
|
176
|
+
if (key === 'n' || key === 'N') {
|
|
177
|
+
cleanup();
|
|
178
|
+
resolve();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Ignore other keys - wait for y/n
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export default checkGitignore;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Title Screen (terminal-kit version)
|
|
3
|
+
*
|
|
4
|
+
* Displays ASCII art title "The Arbiter" and continues on any key press.
|
|
5
|
+
* Uses terminal-kit for fullscreen rendering.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Shows the title screen using terminal-kit.
|
|
9
|
+
* Continues to character select on any key press.
|
|
10
|
+
*
|
|
11
|
+
* @returns Promise<void> - Resolves when user presses any key
|
|
12
|
+
*/
|
|
13
|
+
export declare function showTitleScreen(): Promise<void>;
|
|
14
|
+
export default showTitleScreen;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Title Screen (terminal-kit version)
|
|
3
|
+
*
|
|
4
|
+
* Displays ASCII art title "The Arbiter" and continues on any key press.
|
|
5
|
+
* Uses terminal-kit for fullscreen rendering.
|
|
6
|
+
*/
|
|
7
|
+
import termKit from 'terminal-kit';
|
|
8
|
+
import { playSfx } from '../../sound.js';
|
|
9
|
+
const term = termKit.terminal;
|
|
10
|
+
// ANSI codes
|
|
11
|
+
const _BOLD = '\x1b[1m';
|
|
12
|
+
const DIM = '\x1b[2m';
|
|
13
|
+
const RESET = '\x1b[0m';
|
|
14
|
+
// Fire gradient colors (top = bright, bottom = dark)
|
|
15
|
+
const FIRE_COLORS = [
|
|
16
|
+
'\x1b[97;1m', // bright white
|
|
17
|
+
'\x1b[93;1m', // bright yellow
|
|
18
|
+
'\x1b[93;1m', // bright yellow
|
|
19
|
+
'\x1b[33;1m', // yellow
|
|
20
|
+
'\x1b[33m', // dark yellow
|
|
21
|
+
'\x1b[38;5;208m', // orange
|
|
22
|
+
'\x1b[38;5;208m', // orange
|
|
23
|
+
'\x1b[38;5;202m', // dark orange
|
|
24
|
+
'\x1b[91m', // bright red
|
|
25
|
+
'\x1b[91m', // bright red
|
|
26
|
+
'\x1b[31m', // red
|
|
27
|
+
'\x1b[31m', // red
|
|
28
|
+
'\x1b[38;5;124m', // dark red
|
|
29
|
+
'\x1b[38;5;124m', // dark red
|
|
30
|
+
];
|
|
31
|
+
// ASCII art title (raw lines)
|
|
32
|
+
const TITLE_ART = [
|
|
33
|
+
' ',
|
|
34
|
+
' /### / / ## / ',
|
|
35
|
+
' / ############/ #/ /#### #/ # ',
|
|
36
|
+
'/ ######### ## / ### ## ### # ',
|
|
37
|
+
'# / # ## /## ## # ## ',
|
|
38
|
+
' ## / ## ## / ## ## ## ',
|
|
39
|
+
' / ### ## /## /## / ## ### /### ## /### ### ######## /## ### /### ',
|
|
40
|
+
' ## ## ## / ### / ### / ## ###/ #### / ##/ ### / ### ######## / ### ###/ #### / ',
|
|
41
|
+
' ## ## ##/ ### / ### / ## ## ###/ ## ###/ ## ## / ### ## ###/ ',
|
|
42
|
+
' ## ## ## ## ## ### / ## ## ## ## ## ## ## ### ## ',
|
|
43
|
+
' ## ## ## ## ######## /######## ## ## ## ## ## ######## ## ',
|
|
44
|
+
' ## ## ## ## ####### / ## ## ## ## ## ## ####### ## ',
|
|
45
|
+
' ## # / ## ## ## # ## ## ## ## ## ## ## ## ',
|
|
46
|
+
' ### / ## ## #### / /#### ## ## ## /# ## ## #### / ## ',
|
|
47
|
+
' ######/ ## ## ######/ / #### ## / ### ####/ ### / ## ######/ ### ',
|
|
48
|
+
' ### ## ## ##### / ## #/ ### ### ##/ ## ##### ### ',
|
|
49
|
+
' / # ',
|
|
50
|
+
' / ## ',
|
|
51
|
+
' / ',
|
|
52
|
+
' / ',
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Shows the title screen using terminal-kit.
|
|
56
|
+
* Continues to character select on any key press.
|
|
57
|
+
*
|
|
58
|
+
* @returns Promise<void> - Resolves when user presses any key
|
|
59
|
+
*/
|
|
60
|
+
export async function showTitleScreen() {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
// Initialize terminal
|
|
63
|
+
term.fullscreen(true);
|
|
64
|
+
term.hideCursor();
|
|
65
|
+
term.grabInput({ mouse: 'button' });
|
|
66
|
+
// Get terminal dimensions
|
|
67
|
+
let width = 180;
|
|
68
|
+
let height = 50;
|
|
69
|
+
if (typeof term.width === 'number' && Number.isFinite(term.width) && term.width > 0) {
|
|
70
|
+
width = term.width;
|
|
71
|
+
}
|
|
72
|
+
if (typeof term.height === 'number' && Number.isFinite(term.height) && term.height > 0) {
|
|
73
|
+
height = term.height;
|
|
74
|
+
}
|
|
75
|
+
// Calculate centering
|
|
76
|
+
const artWidth = TITLE_ART[0].length;
|
|
77
|
+
const artHeight = TITLE_ART.length;
|
|
78
|
+
const contentHeight = artHeight + 4; // art + spacing + prompt
|
|
79
|
+
const startX = Math.max(1, Math.floor((width - artWidth) / 2));
|
|
80
|
+
const startY = Math.max(1, Math.floor((height - contentHeight) / 2));
|
|
81
|
+
// Clear screen
|
|
82
|
+
term.clear();
|
|
83
|
+
// Draw title art (diagonal fire gradient - ~25 degrees, top-left to bottom-right)
|
|
84
|
+
// Weight row more than col for shallower angle (row * 3 ≈ 25 degrees)
|
|
85
|
+
const rowWeight = 6;
|
|
86
|
+
const maxDiagonal = TITLE_ART.length * rowWeight + TITLE_ART[0].length;
|
|
87
|
+
for (let row = 0; row < TITLE_ART.length; row++) {
|
|
88
|
+
term.moveTo(startX, startY + row);
|
|
89
|
+
let line = '';
|
|
90
|
+
let lastColorIdx = -1;
|
|
91
|
+
for (let col = 0; col < TITLE_ART[row].length; col++) {
|
|
92
|
+
const diagonal = row * rowWeight + col;
|
|
93
|
+
const colorIdx = Math.min(Math.floor((diagonal / maxDiagonal) * FIRE_COLORS.length), FIRE_COLORS.length - 1);
|
|
94
|
+
// Only add color code when it changes
|
|
95
|
+
if (colorIdx !== lastColorIdx) {
|
|
96
|
+
line += FIRE_COLORS[colorIdx];
|
|
97
|
+
lastColorIdx = colorIdx;
|
|
98
|
+
}
|
|
99
|
+
line += TITLE_ART[row][col];
|
|
100
|
+
}
|
|
101
|
+
process.stdout.write(line + RESET);
|
|
102
|
+
}
|
|
103
|
+
// Draw prompt (dim, centered below art)
|
|
104
|
+
const prompt = 'Press any key to continue...';
|
|
105
|
+
const promptX = Math.max(1, Math.floor((width - prompt.length) / 2));
|
|
106
|
+
const promptY = startY + artHeight + 3;
|
|
107
|
+
term.moveTo(promptX, promptY);
|
|
108
|
+
process.stdout.write(`${DIM}${prompt}${RESET}`);
|
|
109
|
+
/**
|
|
110
|
+
* Cleanup and restore terminal
|
|
111
|
+
*/
|
|
112
|
+
function cleanup() {
|
|
113
|
+
term.removeAllListeners('key');
|
|
114
|
+
term.grabInput(false);
|
|
115
|
+
term.fullscreen(false);
|
|
116
|
+
term.hideCursor(false);
|
|
117
|
+
}
|
|
118
|
+
// Handle any key press
|
|
119
|
+
term.on('key', (key) => {
|
|
120
|
+
// Exit on quit keys
|
|
121
|
+
if (key === 'CTRL_C' || key === 'CTRL_Z') {
|
|
122
|
+
cleanup();
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
// Any other key continues to character select
|
|
126
|
+
playSfx('menuSelect');
|
|
127
|
+
cleanup();
|
|
128
|
+
resolve();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export default showTitleScreen;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screen components re-export
|
|
3
|
+
*
|
|
4
|
+
* Terminal-kit based screen components for the TUI.
|
|
5
|
+
*/
|
|
6
|
+
export { showCharacterSelect } from './CharacterSelect-termkit.js';
|
|
7
|
+
export { showForestIntro } from './ForestIntro-termkit.js';
|
|
8
|
+
export { checkGitignore } from './GitignoreCheck-termkit.js';
|
|
9
|
+
export { showTitleScreen } from './TitleScreen-termkit.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screen components re-export
|
|
3
|
+
*
|
|
4
|
+
* Terminal-kit based screen components for the TUI.
|
|
5
|
+
*/
|
|
6
|
+
export { showCharacterSelect } from './CharacterSelect-termkit.js';
|
|
7
|
+
export { showForestIntro } from './ForestIntro-termkit.js';
|
|
8
|
+
export { checkGitignore } from './GitignoreCheck-termkit.js';
|
|
9
|
+
// Terminal-kit screen components
|
|
10
|
+
export { showTitleScreen } from './TitleScreen-termkit.js';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tileset loading and rendering utilities for the Arbiter TUI
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for loading PNG tilesets, extracting individual tiles,
|
|
5
|
+
* compositing tiles together, and rendering them to ANSI terminal output.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TILE_SIZE = 16;
|
|
8
|
+
export declare const TILES_PER_ROW = 10;
|
|
9
|
+
export declare const CHAR_HEIGHT = 8;
|
|
10
|
+
export declare const RESET = "\u001B[0m";
|
|
11
|
+
export declare const CLEAR_SCREEN = "\u001B[2J";
|
|
12
|
+
export declare const CURSOR_HOME = "\u001B[H";
|
|
13
|
+
export declare const HIDE_CURSOR = "\u001B[?25l";
|
|
14
|
+
export declare const SHOW_CURSOR = "\u001B[?25h";
|
|
15
|
+
export declare const TILE: {
|
|
16
|
+
readonly GRASS: 50;
|
|
17
|
+
readonly GRASS_SPARSE: 51;
|
|
18
|
+
readonly PINE_TREE: 57;
|
|
19
|
+
readonly BARE_TREE: 58;
|
|
20
|
+
readonly CAULDRON: 86;
|
|
21
|
+
readonly CAMPFIRE: 87;
|
|
22
|
+
readonly SMOKE: 90;
|
|
23
|
+
readonly SPELLBOOK: 102;
|
|
24
|
+
readonly HUMAN_1: 190;
|
|
25
|
+
readonly HUMAN_2: 191;
|
|
26
|
+
readonly HUMAN_3: 192;
|
|
27
|
+
readonly HUMAN_4: 193;
|
|
28
|
+
readonly HUMAN_5: 194;
|
|
29
|
+
readonly HUMAN_6: 195;
|
|
30
|
+
readonly HUMAN_7: 196;
|
|
31
|
+
readonly HUMAN_8: 197;
|
|
32
|
+
readonly ARBITER: 205;
|
|
33
|
+
readonly DEMON_1: 220;
|
|
34
|
+
readonly DEMON_2: 221;
|
|
35
|
+
readonly DEMON_3: 222;
|
|
36
|
+
readonly DEMON_4: 223;
|
|
37
|
+
readonly DEMON_5: 224;
|
|
38
|
+
readonly FOCUS: 270;
|
|
39
|
+
readonly CHAT_BUBBLE_QUARTERS: 267;
|
|
40
|
+
readonly ALERT_QUARTERS: 268;
|
|
41
|
+
readonly SCROLL: 124;
|
|
42
|
+
};
|
|
43
|
+
export type QuarterPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
44
|
+
export interface RGB {
|
|
45
|
+
r: number;
|
|
46
|
+
g: number;
|
|
47
|
+
b: number;
|
|
48
|
+
a: number;
|
|
49
|
+
}
|
|
50
|
+
export interface Tileset {
|
|
51
|
+
width: number;
|
|
52
|
+
height: number;
|
|
53
|
+
data: Buffer;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Load the tileset PNG and return cached data
|
|
57
|
+
*/
|
|
58
|
+
export declare function loadTileset(): Promise<Tileset>;
|
|
59
|
+
/**
|
|
60
|
+
* Extract a 16x16 tile by index from the tileset
|
|
61
|
+
*/
|
|
62
|
+
export declare function extractTile(tileset: Tileset, index: number): RGB[][];
|
|
63
|
+
/**
|
|
64
|
+
* Composite foreground tile on background tile using alpha threshold
|
|
65
|
+
* Pixels with alpha below threshold use the background pixel
|
|
66
|
+
*/
|
|
67
|
+
export declare function compositeTiles(fg: RGB[][], bg: RGB[][], alphaThreshold: number): RGB[][];
|
|
68
|
+
/**
|
|
69
|
+
* Composite focus overlay on character tile
|
|
70
|
+
* The focus overlay has transparent center, only the corner brackets show
|
|
71
|
+
*/
|
|
72
|
+
export declare function compositeWithFocus(charPixels: RGB[][], focusPixels: RGB[][], alphaThreshold?: number): RGB[][];
|
|
73
|
+
/**
|
|
74
|
+
* Mirror a tile horizontally (flip left-right)
|
|
75
|
+
*/
|
|
76
|
+
export declare function mirrorTile(pixels: RGB[][]): RGB[][];
|
|
77
|
+
/**
|
|
78
|
+
* Extract an 8x8 quarter from a 16x16 tile
|
|
79
|
+
* @param pixels The full 16x16 tile pixels
|
|
80
|
+
* @param quarter Which quarter to extract
|
|
81
|
+
* @returns 8x8 pixel array
|
|
82
|
+
*/
|
|
83
|
+
export declare function extractQuarterTile(pixels: RGB[][], quarter: QuarterPosition): RGB[][];
|
|
84
|
+
/**
|
|
85
|
+
* Composite an 8x8 quarter tile onto a specific corner of a 16x16 tile
|
|
86
|
+
* @param base The full 16x16 tile pixels (will be cloned, not mutated)
|
|
87
|
+
* @param quarter The 8x8 quarter tile to overlay
|
|
88
|
+
* @param position Where to place the quarter tile
|
|
89
|
+
* @param alphaThreshold Pixels with alpha below this use the base pixel
|
|
90
|
+
* @returns New 16x16 pixel array with quarter composited
|
|
91
|
+
*/
|
|
92
|
+
export declare function compositeQuarterTile(base: RGB[][], quarter: RGB[][], position: QuarterPosition, alphaThreshold?: number): RGB[][];
|
|
93
|
+
/**
|
|
94
|
+
* Render a tile to ANSI string array (16 chars wide x 8 rows)
|
|
95
|
+
* Uses half-block characters for 2:1 vertical compression
|
|
96
|
+
*/
|
|
97
|
+
export declare function renderTile(pixels: RGB[][]): string[];
|