create-asciitorium 0.1.19 → 0.1.20
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/templates/base/public/art/computer.txt +17 -0
- package/dist/templates/base/src/examples/AsciiArtExample.tsx +9 -0
- package/dist/templates/base/src/examples/ButtonExample.tsx +21 -0
- package/dist/templates/base/src/examples/LayoutExample.tsx +45 -0
- package/dist/templates/base/src/examples/MultiSelectExample.tsx +51 -0
- package/dist/templates/base/src/examples/ProgressBarExample.tsx +42 -0
- package/dist/templates/base/src/examples/SelectExample.tsx +40 -0
- package/dist/templates/base/src/examples/TabsExample.tsx +54 -0
- package/dist/templates/base/src/examples/TextExample.tsx +24 -0
- package/dist/templates/base/src/examples/TextInputExample.tsx +25 -0
- package/dist/templates/base/src/main.tsx +119 -29
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
_____________________
|
|
2
|
+
/ ` \
|
|
3
|
+
| .-----------. | |-----.
|
|
4
|
+
| | | | |-=---|
|
|
5
|
+
| | | | |-----|
|
|
6
|
+
| | | | |-----|
|
|
7
|
+
| | | | |-----|
|
|
8
|
+
| `-----------' | |-----'/\
|
|
9
|
+
\________________/___' / \
|
|
10
|
+
/ / / /
|
|
11
|
+
/ // // / / /
|
|
12
|
+
/ / / /
|
|
13
|
+
/ _/_/_/_/_/_/_/_/_/_/ / /
|
|
14
|
+
/ _/_/_/_/_/_/_/_/_/_/ / /
|
|
15
|
+
/ _/_/_/_______/_/_/_/ / __/
|
|
16
|
+
/______________________/ /
|
|
17
|
+
\______________________\/
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AsciiArt, Box, loadArt } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
const computer = await loadArt('./art/computer.txt');
|
|
4
|
+
|
|
5
|
+
export const AsciiArtExample = () => (
|
|
6
|
+
<Box width={42} height={28} layout="relaxed" label="ASCII Art Example:" border>
|
|
7
|
+
<AsciiArt content={computer} align="center" />
|
|
8
|
+
</Box>
|
|
9
|
+
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Text, State, Button, HR, Box } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const ButtonExample = () => {
|
|
4
|
+
// State for button click count - local to each component instance
|
|
5
|
+
const buttonClickCount = new State(0);
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<Box width={42} height={28} label="Button Example:" border>
|
|
9
|
+
<Button
|
|
10
|
+
label="I'm a Button!"
|
|
11
|
+
align="center"
|
|
12
|
+
onClick={() => (buttonClickCount.value = buttonClickCount.value + 1)}
|
|
13
|
+
gap={{top: 4, bottom: 3}}
|
|
14
|
+
/>
|
|
15
|
+
<Box layout="horizontal" gap={{left: 6, right: 2}} align="center">
|
|
16
|
+
<Text>Click Count: </Text>
|
|
17
|
+
<Text width={4}>{buttonClickCount}</Text>
|
|
18
|
+
</Box>
|
|
19
|
+
</Box>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Text, HR, VR, Box, CelticBorder } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const LayoutExample = () => (
|
|
4
|
+
<Box width={42} height={27} border layout="vertical">
|
|
5
|
+
<Text
|
|
6
|
+
width={40}
|
|
7
|
+
height={2}
|
|
8
|
+
>
|
|
9
|
+
Layout Components - HR, VR, Box, Borders
|
|
10
|
+
</Text>
|
|
11
|
+
<HR gap={1} />
|
|
12
|
+
<Box
|
|
13
|
+
width={20}
|
|
14
|
+
height={5}
|
|
15
|
+
border
|
|
16
|
+
gap={1}
|
|
17
|
+
children={[
|
|
18
|
+
new Text({
|
|
19
|
+
width: 16,
|
|
20
|
+
height: 3,
|
|
21
|
+
children: ['Box with border\nand content'],
|
|
22
|
+
}),
|
|
23
|
+
]}
|
|
24
|
+
/>
|
|
25
|
+
<CelticBorder
|
|
26
|
+
width={20}
|
|
27
|
+
height={5}
|
|
28
|
+
gap={1}
|
|
29
|
+
children={[
|
|
30
|
+
new Text({
|
|
31
|
+
width: 16,
|
|
32
|
+
height: 3,
|
|
33
|
+
children: ['Celtic border\ndecoration'],
|
|
34
|
+
}),
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
<HR gap={1} />
|
|
38
|
+
<Text
|
|
39
|
+
width={35}
|
|
40
|
+
height={2}
|
|
41
|
+
>
|
|
42
|
+
Layout components help organize\nand structure your UI.
|
|
43
|
+
</Text>
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Text, State, MultiSelect, Box, HR } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const MultiSelectExample = () => {
|
|
4
|
+
const multiValue = new State(['Suspension', 'Brakes']);
|
|
5
|
+
const displayText = new State(multiValue.value.join(', '));
|
|
6
|
+
|
|
7
|
+
multiValue.subscribe((newValue) => {
|
|
8
|
+
displayText.value = newValue.join(', ');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const carParts = [
|
|
12
|
+
'Engine',
|
|
13
|
+
'Transmission',
|
|
14
|
+
'Brakes',
|
|
15
|
+
'Suspension',
|
|
16
|
+
'Exhaust',
|
|
17
|
+
'Radiator',
|
|
18
|
+
'Battery',
|
|
19
|
+
'Alternator',
|
|
20
|
+
'Starter',
|
|
21
|
+
'Fuel Pump',
|
|
22
|
+
'Air Filter',
|
|
23
|
+
'Oil Filter',
|
|
24
|
+
'Spark Plugs',
|
|
25
|
+
'Tires',
|
|
26
|
+
'Wheels',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Box label="MultiSelect Example:" width={42} height={28} border>
|
|
31
|
+
<MultiSelect
|
|
32
|
+
label="Car Parts:"
|
|
33
|
+
align="center"
|
|
34
|
+
width={34}
|
|
35
|
+
height={10}
|
|
36
|
+
items={carParts}
|
|
37
|
+
selectedItems={multiValue}
|
|
38
|
+
gap={{ top: 2, bottom: 2 }}
|
|
39
|
+
/>
|
|
40
|
+
<Box align="center" layout="horizontal">
|
|
41
|
+
<Text align="top">Selected: </Text>
|
|
42
|
+
<Text
|
|
43
|
+
width={24}
|
|
44
|
+
height={7}
|
|
45
|
+
align="top"
|
|
46
|
+
content={displayText}
|
|
47
|
+
/>
|
|
48
|
+
</Box>
|
|
49
|
+
</Box>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Text, State, ProgressBar, Button, Box, HR } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const ProgressBarExample = () => {
|
|
4
|
+
// State for progress bar - local to each component instance
|
|
5
|
+
const progressValue = new State(25);
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<Box width={42} height={28} border label="ProgressBar Example:" >
|
|
9
|
+
<Text align="center" gap={{top: 2}}>With Percentage</Text>
|
|
10
|
+
<ProgressBar
|
|
11
|
+
width={35}
|
|
12
|
+
percent={progressValue}
|
|
13
|
+
align="center"
|
|
14
|
+
showPercentage
|
|
15
|
+
/>
|
|
16
|
+
<Text align="center" gap={{top: 2}}>Without Percentage</Text>
|
|
17
|
+
<ProgressBar
|
|
18
|
+
width={35}
|
|
19
|
+
percent={progressValue}
|
|
20
|
+
showPercentage={false}
|
|
21
|
+
align="center"
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
<Box layout="horizontal" align="center" gap={{ top: 3 }}>
|
|
25
|
+
<Button
|
|
26
|
+
label="Increase"
|
|
27
|
+
onClick={() =>
|
|
28
|
+
(progressValue.value = Math.min(100, progressValue.value + 10))
|
|
29
|
+
}
|
|
30
|
+
gap={1}
|
|
31
|
+
/>
|
|
32
|
+
<Button
|
|
33
|
+
label="Decrease"
|
|
34
|
+
onClick={() =>
|
|
35
|
+
(progressValue.value = Math.max(0, progressValue.value - 10))
|
|
36
|
+
}
|
|
37
|
+
gap={1}
|
|
38
|
+
/>
|
|
39
|
+
</Box>
|
|
40
|
+
</Box>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Text, State, Select, Box } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const SelectExample = () => {
|
|
4
|
+
const selectValue = new State('Tesla Model S');
|
|
5
|
+
|
|
6
|
+
const carModels = [
|
|
7
|
+
'Tesla Model S',
|
|
8
|
+
'BMW M3',
|
|
9
|
+
'Audi A4',
|
|
10
|
+
'Mercedes C-Class',
|
|
11
|
+
'Honda Civic',
|
|
12
|
+
'Toyota Camry',
|
|
13
|
+
'Ford Mustang',
|
|
14
|
+
'Chevrolet Corvette',
|
|
15
|
+
'Porsche 911',
|
|
16
|
+
'Ferrari 488',
|
|
17
|
+
'Lamborghini Huracan',
|
|
18
|
+
'McLaren 720S',
|
|
19
|
+
'Nissan GTR',
|
|
20
|
+
'Subaru WRX',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Box width={42} height={28} label="Select Example:" border>
|
|
25
|
+
<Box align="center" gap={{ top: 2, bottom: 2 }}>
|
|
26
|
+
<Select
|
|
27
|
+
label="Car's to Select:"
|
|
28
|
+
width={34}
|
|
29
|
+
height={10}
|
|
30
|
+
items={carModels}
|
|
31
|
+
selectedItem={selectValue}
|
|
32
|
+
/>
|
|
33
|
+
</Box>
|
|
34
|
+
<Box align="center" gap={{ left: 5 }} layout="horizontal">
|
|
35
|
+
<Text>Car Selected: </Text>
|
|
36
|
+
<Text width={20}>{selectValue}</Text>
|
|
37
|
+
</Box>
|
|
38
|
+
</Box>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Text, State, Tabs, Box } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const TabsExample = () => {
|
|
4
|
+
// State for tabs - local to each component instance
|
|
5
|
+
const tabsValue = new State('Tab 1');
|
|
6
|
+
|
|
7
|
+
// Dynamic content state based on selected tab
|
|
8
|
+
const tabContentState = new State('');
|
|
9
|
+
tabsValue.subscribe((tab) => {
|
|
10
|
+
switch (tab) {
|
|
11
|
+
case 'Tab 1':
|
|
12
|
+
tabContentState.value =
|
|
13
|
+
'Content for Tab 1\nThis shows dynamic content\nbased on selected tab.';
|
|
14
|
+
break;
|
|
15
|
+
case 'Tab 2':
|
|
16
|
+
tabContentState.value =
|
|
17
|
+
'Content for Tab 2\nDifferent content here!\nTabs make navigation easy.';
|
|
18
|
+
break;
|
|
19
|
+
case 'Tab 3':
|
|
20
|
+
tabContentState.value =
|
|
21
|
+
'Content for Tab 3\nYet another section.\nUse arrow keys to switch.';
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
tabContentState.value = 'Select a tab above';
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Initialize content
|
|
29
|
+
tabContentState.value =
|
|
30
|
+
tabsValue.value === 'Tab 1'
|
|
31
|
+
? 'Content for Tab 1\nThis shows dynamic content\nbased on selected tab.'
|
|
32
|
+
: tabsValue.value === 'Tab 2'
|
|
33
|
+
? 'Content for Tab 2\nDifferent content here!\nTabs make navigation easy.'
|
|
34
|
+
: 'Content for Tab 3\nYet another section.\nUse arrow keys to switch.';
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Box width={42} height={28} border label="Tabs Example:">
|
|
38
|
+
<Tabs
|
|
39
|
+
tabs={['Tab 1', 'Tab 2', 'Tab 3']}
|
|
40
|
+
align="center"
|
|
41
|
+
selectedTab={tabsValue}
|
|
42
|
+
gap={1}
|
|
43
|
+
/>
|
|
44
|
+
<Box
|
|
45
|
+
width={38}
|
|
46
|
+
height={8}
|
|
47
|
+
gap={1}
|
|
48
|
+
children={[
|
|
49
|
+
new Text({ width: 30, height: 6, children: [tabContentState] }),
|
|
50
|
+
]}
|
|
51
|
+
/>
|
|
52
|
+
</Box>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Text, AsciiArt, Box } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const TextExample = () => (
|
|
4
|
+
<Box width={42} height={28} border label="Text Example:">
|
|
5
|
+
<Text
|
|
6
|
+
width={36}
|
|
7
|
+
height={8}
|
|
8
|
+
border
|
|
9
|
+
gap={1}
|
|
10
|
+
>
|
|
11
|
+
{'This is bordered text with:\n\n - multiple lines,\n - food for thought, and\n - some formatting.'}
|
|
12
|
+
</Text>
|
|
13
|
+
<Text
|
|
14
|
+
width={36}
|
|
15
|
+
height={7}
|
|
16
|
+
border
|
|
17
|
+
gap={1}
|
|
18
|
+
>
|
|
19
|
+
This is a long text that should wrap automatically when width and height are specified and the content exceeds the available space.
|
|
20
|
+
</Text>
|
|
21
|
+
|
|
22
|
+
<Text>Simple Text.</Text>
|
|
23
|
+
</Box>
|
|
24
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Text, State, TextInput, HR, Box } from 'asciitorium';
|
|
2
|
+
|
|
3
|
+
export const TextInputExample = () => {
|
|
4
|
+
// States for text input components - local to each component instance
|
|
5
|
+
const textInputValue = new State('Hello World');
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<Box width={42} height={28} label="TextInput Example:" border>
|
|
9
|
+
<TextInput
|
|
10
|
+
gap={{ top: 2 }}
|
|
11
|
+
width={20}
|
|
12
|
+
align="center"
|
|
13
|
+
value={textInputValue}
|
|
14
|
+
placeholder="Enter text here..."
|
|
15
|
+
/>
|
|
16
|
+
<Text width={20} align="center" height={3} gap={{ left: 5, bottom: 4 }}>
|
|
17
|
+
{textInputValue}
|
|
18
|
+
</Text>
|
|
19
|
+
|
|
20
|
+
<Text align="center" width={30} height={3}>
|
|
21
|
+
Use arrow keys to move cursor. Numeric input restricts to numbers only.
|
|
22
|
+
</Text>
|
|
23
|
+
</Box>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -1,44 +1,134 @@
|
|
|
1
|
-
/** Single entry that runs on Web (DOM) and CLI (Terminal) */
|
|
2
1
|
import {
|
|
3
2
|
App,
|
|
4
|
-
Box,
|
|
5
3
|
Text,
|
|
6
|
-
State,
|
|
7
4
|
AsciiArt,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
Select,
|
|
6
|
+
Box,
|
|
7
|
+
Component,
|
|
8
|
+
PersistentState,
|
|
9
|
+
loadArt,
|
|
13
10
|
} from 'asciitorium';
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
// Import example components
|
|
13
|
+
import { ButtonExample } from './examples/ButtonExample';
|
|
14
|
+
import { SelectExample } from './examples/SelectExample';
|
|
15
|
+
import { MultiSelectExample } from './examples/MultiSelectExample';
|
|
16
|
+
import { TextInputExample } from './examples/TextInputExample';
|
|
17
|
+
import { ProgressBarExample } from './examples/ProgressBarExample';
|
|
18
|
+
import { TabsExample } from './examples/TabsExample';
|
|
19
|
+
import { TextExample } from './examples/TextExample';
|
|
20
|
+
import { AsciiArtExample } from './examples/AsciiArtExample';
|
|
19
21
|
|
|
20
22
|
// Load the title ASCII art
|
|
21
|
-
const titleArt = await
|
|
23
|
+
const titleArt = await loadArt('./art/asciitorium.txt');
|
|
22
24
|
|
|
23
|
-
//
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
// Main state for component selection with persistence
|
|
26
|
+
const selectedComponent = new PersistentState(
|
|
27
|
+
'Button',
|
|
28
|
+
'demo-selected-component'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Component list for navigation
|
|
32
|
+
const componentList = [
|
|
33
|
+
'AsciiArt',
|
|
34
|
+
'Button',
|
|
35
|
+
'MultiSelect',
|
|
36
|
+
'ProgressBar',
|
|
37
|
+
'Select',
|
|
38
|
+
'Tabs',
|
|
39
|
+
'Text',
|
|
40
|
+
'TextInput'
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Component mapping
|
|
44
|
+
const examples: Record<string, any> = {
|
|
45
|
+
Button: ButtonExample,
|
|
46
|
+
Select: SelectExample,
|
|
47
|
+
MultiSelect: MultiSelectExample,
|
|
48
|
+
TextInput: TextInputExample,
|
|
49
|
+
ProgressBar: ProgressBarExample,
|
|
50
|
+
Tabs: TabsExample,
|
|
51
|
+
Text: TextExample,
|
|
52
|
+
AsciiArt: AsciiArtExample
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Create a wrapper component that dynamically switches based on state
|
|
34
56
|
|
|
35
|
-
|
|
57
|
+
class DynamicExampleWrapper extends Box {
|
|
58
|
+
constructor() {
|
|
59
|
+
super({ width: 53, height: 28 });
|
|
36
60
|
|
|
37
|
-
|
|
61
|
+
// Initially set the current example
|
|
62
|
+
this.updateExample();
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
// Subscribe to changes in selectedComponent
|
|
65
|
+
selectedComponent.subscribe(() => {
|
|
66
|
+
this.updateExample();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private updateExample() {
|
|
71
|
+
// Properly remove existing children using removeChild to notify parent
|
|
72
|
+
const childrenToRemove = [...this.children]; // Create copy to avoid mutation during iteration
|
|
73
|
+
for (const child of childrenToRemove) {
|
|
74
|
+
child.destroy();
|
|
75
|
+
this.removeChild(child);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get the current example factory function
|
|
79
|
+
const exampleFactory = examples[selectedComponent.value];
|
|
80
|
+
if (exampleFactory) {
|
|
81
|
+
// Call the factory function to create a new component instance
|
|
82
|
+
const exampleComponent = exampleFactory();
|
|
83
|
+
this.addChild(exampleComponent);
|
|
84
|
+
} else {
|
|
85
|
+
// Fallback for unknown component
|
|
86
|
+
this.addChild(
|
|
87
|
+
new Text({
|
|
88
|
+
width: 40,
|
|
89
|
+
height: 5,
|
|
90
|
+
border: true,
|
|
91
|
+
children: ['Unknown component'],
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Find the root App component and reset focus manager
|
|
97
|
+
this.notifyAppOfFocusChange();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private notifyAppOfFocusChange() {
|
|
101
|
+
// Walk up the parent chain to find the App
|
|
102
|
+
let current: Component | undefined = this;
|
|
103
|
+
while (current && current.constructor.name !== 'App') {
|
|
104
|
+
current = current.parent;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// If we found the App, reset its focus manager
|
|
108
|
+
if (current && (current as any).focus) {
|
|
109
|
+
(current as any).focus.reset(current);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Construct the app with horizontal layout
|
|
115
|
+
const app = (
|
|
116
|
+
<App width={67} height={35}>
|
|
117
|
+
<AsciiArt content={titleArt} align="center" gap={{ bottom: 2 }} />
|
|
118
|
+
<Box layout="horizontal">
|
|
119
|
+
{/* Left Panel - Navigation */}
|
|
120
|
+
<Select
|
|
121
|
+
label="Components:"
|
|
122
|
+
width={25}
|
|
123
|
+
height={28}
|
|
124
|
+
items={componentList}
|
|
125
|
+
selectedItem={selectedComponent}
|
|
126
|
+
border
|
|
127
|
+
/>
|
|
128
|
+
{/* Right Panel - Dynamic content */}
|
|
129
|
+
{new DynamicExampleWrapper()}
|
|
130
|
+
</Box>
|
|
41
131
|
</App>
|
|
42
132
|
);
|
|
43
133
|
|
|
44
|
-
await app.start();
|
|
134
|
+
await app.start();
|