tokengolf 0.4.1 → 0.4.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/dist/cli.js +1360 -643
- package/docs/index.html +844 -375
- package/package.json +1 -1
- package/scripts/demo-to-html.js +104 -0
- package/src/cli.js +45 -5
- package/src/lib/demo-active.js +56 -0
- package/src/lib/demo-fixtures.js +496 -0
- package/src/lib/demo-hud.js +3 -0
- package/src/lib/demo-render.js +16 -0
- package/src/lib/demo-scorecard.js +56 -0
- package/src/lib/demo-stats.js +56 -0
package/package.json
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Captures demo output and converts ANSI escape codes to HTML spans
|
|
3
|
+
// Usage: node scripts/demo-to-html.js [scorecard|active|stats|hud] [index]
|
|
4
|
+
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const component = process.argv[2] || 'scorecard';
|
|
8
|
+
const index = process.argv[3] != null ? `-i ${process.argv[3]}` : '';
|
|
9
|
+
|
|
10
|
+
const raw = execSync(`node dist/cli.js demo ${component} ${index}`, {
|
|
11
|
+
encoding: 'utf8',
|
|
12
|
+
env: { ...process.env, FORCE_COLOR: '1' },
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// ANSI color code → CSS class mapping
|
|
16
|
+
const ANSI_MAP = {
|
|
17
|
+
'1': 'b', // bold
|
|
18
|
+
'2': 'dim', // dim
|
|
19
|
+
'3': 'i', // italic
|
|
20
|
+
'31': 'red',
|
|
21
|
+
'32': 'green',
|
|
22
|
+
'33': 'yellow',
|
|
23
|
+
'34': 'blue',
|
|
24
|
+
'35': 'magenta',
|
|
25
|
+
'36': 'cyan',
|
|
26
|
+
'37': 'white',
|
|
27
|
+
'39': '', // default fg
|
|
28
|
+
'90': 'dim', // bright black = dim
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function ansiToHtml(str) {
|
|
32
|
+
// Remove the demo header lines (first 2 lines) and separator/title lines
|
|
33
|
+
const lines = str.split('\n');
|
|
34
|
+
|
|
35
|
+
// Find where actual content starts (skip "TokenGolf — X Demo" + "N variants" + separator + title)
|
|
36
|
+
let startIdx = 0;
|
|
37
|
+
for (let i = 0; i < lines.length; i++) {
|
|
38
|
+
if (lines[i].includes('TokenGolf —') || lines[i].includes('variant')) {
|
|
39
|
+
startIdx = i + 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Also skip the leading separator + title line
|
|
44
|
+
while (startIdx < lines.length && lines[startIdx].trim() === '') startIdx++;
|
|
45
|
+
// Skip "──────" separator
|
|
46
|
+
const stripped = lines[startIdx]?.replace(/\x1b\[[0-9;]*m/g, '').trim();
|
|
47
|
+
if (stripped && stripped.match(/^─+$/)) startIdx++;
|
|
48
|
+
// Skip title line
|
|
49
|
+
if (lines[startIdx]) startIdx++;
|
|
50
|
+
|
|
51
|
+
let content = lines.slice(startIdx).join('\n');
|
|
52
|
+
|
|
53
|
+
// Remove trailing empty lines
|
|
54
|
+
content = content.replace(/\n+$/, '');
|
|
55
|
+
|
|
56
|
+
// Also remove Ink's cleanup sequences ([2K[1A patterns)
|
|
57
|
+
content = content.replace(/(\x1b\[2K\x1b\[1A)+\x1b\[2K\x1b\[G/g, '');
|
|
58
|
+
content = content.replace(/\x1b\[2K/g, '');
|
|
59
|
+
content = content.replace(/\x1b\[1A/g, '');
|
|
60
|
+
content = content.replace(/\x1b\[\d*G/g, '');
|
|
61
|
+
|
|
62
|
+
// Remove trailing cleanup
|
|
63
|
+
content = content.replace(/\n+$/, '');
|
|
64
|
+
|
|
65
|
+
// Convert ANSI to HTML spans
|
|
66
|
+
let html = '';
|
|
67
|
+
let openTags = [];
|
|
68
|
+
|
|
69
|
+
const parts = content.split(/(\x1b\[[0-9;]*m)/);
|
|
70
|
+
|
|
71
|
+
for (const part of parts) {
|
|
72
|
+
const match = part.match(/^\x1b\[([0-9;]*)m$/);
|
|
73
|
+
if (match) {
|
|
74
|
+
const codes = match[1].split(';');
|
|
75
|
+
for (const code of codes) {
|
|
76
|
+
if (code === '0' || code === '') {
|
|
77
|
+
// Reset — close all open tags
|
|
78
|
+
html += openTags.map(() => '</span>').join('');
|
|
79
|
+
openTags = [];
|
|
80
|
+
} else {
|
|
81
|
+
const cls = ANSI_MAP[code];
|
|
82
|
+
if (cls) {
|
|
83
|
+
html += `<span class="t-${cls}">`;
|
|
84
|
+
openTags.push(cls);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// Escape HTML entities
|
|
90
|
+
html += part
|
|
91
|
+
.replace(/&/g, '&')
|
|
92
|
+
.replace(/</g, '<')
|
|
93
|
+
.replace(/>/g, '>');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Close remaining tags
|
|
98
|
+
html += openTags.map(() => '</span>').join('');
|
|
99
|
+
|
|
100
|
+
return html;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const html = ansiToHtml(raw);
|
|
104
|
+
console.log(html);
|
package/src/cli.js
CHANGED
|
@@ -114,11 +114,51 @@ program
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
program
|
|
117
|
-
.command('demo')
|
|
118
|
-
.description('Show
|
|
119
|
-
.
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
.command('demo [component]')
|
|
118
|
+
.description('Show UI demos — all, hud, scorecard, active, stats')
|
|
119
|
+
.option('-i, --index <n>', 'Show only the Nth variant (0-based)')
|
|
120
|
+
.action(async (component, opts) => {
|
|
121
|
+
const idx = opts.index != null ? parseInt(opts.index) : undefined;
|
|
122
|
+
const c = (component || 'all').toLowerCase();
|
|
123
|
+
|
|
124
|
+
if (c === 'all') {
|
|
125
|
+
const { runDemo } = await import('./lib/demo.js');
|
|
126
|
+
runDemo();
|
|
127
|
+
const { runScoreCardDemo } = await import('./lib/demo-scorecard.js');
|
|
128
|
+
await runScoreCardDemo(idx);
|
|
129
|
+
const { runActiveDemo } = await import('./lib/demo-active.js');
|
|
130
|
+
await runActiveDemo(idx);
|
|
131
|
+
const { runStatsDemo } = await import('./lib/demo-stats.js');
|
|
132
|
+
await runStatsDemo(idx);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (c === 'hud') {
|
|
137
|
+
const { runDemo } = await import('./lib/demo.js');
|
|
138
|
+
runDemo();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (c === 'scorecard') {
|
|
143
|
+
const { runScoreCardDemo } = await import('./lib/demo-scorecard.js');
|
|
144
|
+
await runScoreCardDemo(idx);
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (c === 'active') {
|
|
149
|
+
const { runActiveDemo } = await import('./lib/demo-active.js');
|
|
150
|
+
await runActiveDemo(idx);
|
|
151
|
+
process.exit(0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (c === 'stats') {
|
|
155
|
+
const { runStatsDemo } = await import('./lib/demo-stats.js');
|
|
156
|
+
await runStatsDemo(idx);
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log('Unknown demo component. Choose: all, hud, scorecard, active, stats');
|
|
161
|
+
process.exit(1);
|
|
122
162
|
});
|
|
123
163
|
|
|
124
164
|
program
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Demo: ActiveRun variants (Ink)
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { demoRender } from './demo-render.js';
|
|
5
|
+
import { ActiveRun } from '../components/ActiveRun.js';
|
|
6
|
+
import { ACTIVERUN_FIXTURES } from './demo-fixtures.js';
|
|
7
|
+
|
|
8
|
+
function DemoActiveRun({ fixture }) {
|
|
9
|
+
return (
|
|
10
|
+
<Box flexDirection="column">
|
|
11
|
+
<Box paddingX={1}>
|
|
12
|
+
<Text color="gray" dimColor>
|
|
13
|
+
{'─'.repeat(50)}
|
|
14
|
+
</Text>
|
|
15
|
+
</Box>
|
|
16
|
+
<Box paddingX={1}>
|
|
17
|
+
<Text color="gray" dimColor italic>
|
|
18
|
+
{fixture.title}
|
|
19
|
+
</Text>
|
|
20
|
+
</Box>
|
|
21
|
+
<ActiveRun run={fixture.run} />
|
|
22
|
+
</Box>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function runActiveDemo(index) {
|
|
27
|
+
const fixtures = index != null ? [ACTIVERUN_FIXTURES[index]] : ACTIVERUN_FIXTURES;
|
|
28
|
+
if (!fixtures[0]) {
|
|
29
|
+
console.log(
|
|
30
|
+
`Invalid index. ${ACTIVERUN_FIXTURES.length} variants available (0-${ACTIVERUN_FIXTURES.length - 1}).`
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log('\x1b[1m\x1b[36m⛳ TokenGolf — ActiveRun Demo\x1b[0m');
|
|
37
|
+
console.log(`\x1b[2m${fixtures.length} variant${fixtures.length > 1 ? 's' : ''}\x1b[0m`);
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
let i = 0;
|
|
41
|
+
function renderNext() {
|
|
42
|
+
if (i >= fixtures.length) {
|
|
43
|
+
resolve();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const fixture = fixtures[i];
|
|
47
|
+
const inst = demoRender(React.createElement(DemoActiveRun, { fixture }));
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
inst.unmount();
|
|
50
|
+
i++;
|
|
51
|
+
renderNext();
|
|
52
|
+
}, 100);
|
|
53
|
+
}
|
|
54
|
+
renderNext();
|
|
55
|
+
});
|
|
56
|
+
}
|