create-asciitorium 0.1.45 → 0.1.47

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.
@@ -1,6 +1,8 @@
1
1
  # ASCIITORIUM Quick Reference
2
2
 
3
- > **For LLMs**: This document provides correct usage patterns for ASCIITORIUM components. Always refer to the examples here rather than assuming React-like conventions.
3
+ > **For LLMs**: This document provides correct usage patterns for ASCIITORIUM
4
+ > components. Always refer to the examples here rather than assuming React-like
5
+ > conventions.
4
6
 
5
7
  ## Quick Index
6
8
 
@@ -35,7 +37,7 @@
35
37
  - [Keybind](#keybind)
36
38
  - [PerfMonitor](#perfmonitor)
37
39
 
38
- ---
40
+
39
41
 
40
42
  ## State Management
41
43
 
@@ -118,7 +120,7 @@ count++; // NO! count is a State object
118
120
  count.value++;
119
121
  ```
120
122
 
121
- ---
123
+
122
124
 
123
125
  ## JSX & Props
124
126
 
@@ -172,7 +174,7 @@ align="center" // Alignment (layout-specific)
172
174
  </Text>
173
175
  ```
174
176
 
175
- ---
177
+
176
178
 
177
179
  ## Row
178
180
 
@@ -215,7 +217,7 @@ align="center" // Alignment (layout-specific)
215
217
  </Row>
216
218
  ```
217
219
 
218
- ---
220
+
219
221
 
220
222
  ## Column
221
223
 
@@ -244,7 +246,7 @@ align="center" // Alignment (layout-specific)
244
246
 
245
247
  ```tsx
246
248
  <Column align="center" width="100%" gap={2}>
247
- <Art src="logo" />
249
+ <Art sprite="logo" />
248
250
  <Text>Welcome</Text>
249
251
  <Button>Start</Button>
250
252
  </Column>
@@ -262,7 +264,7 @@ align="center" // Alignment (layout-specific)
262
264
  </Column>
263
265
  ```
264
266
 
265
- ---
267
+
266
268
 
267
269
  ## Text
268
270
 
@@ -361,7 +363,7 @@ const playerName = new State('Hero');
361
363
  <Text>{count.value}</Text> // Explicit .value also works
362
364
  ```
363
365
 
364
- ---
366
+
365
367
 
366
368
  ## Button
367
369
 
@@ -439,7 +441,7 @@ const count = new State(0);
439
441
  <Button>Very Long Text</Button> // Auto-sizes
440
442
  ```
441
443
 
442
- ---
444
+
443
445
 
444
446
  ## Select
445
447
 
@@ -553,142 +555,170 @@ const selected = new State("option1");
553
555
  </Select>
554
556
  ```
555
557
 
556
- ---
558
+
557
559
 
558
560
  ## Switch
559
561
 
560
562
  ### Basic Usage
561
563
 
562
564
  ```tsx
563
- import { State, Switch } from 'asciitorium';
565
+ import { State, Switch, Case, Default } from 'asciitorium';
564
566
 
565
- // State holding the component to display
566
- const currentView = new State<any>(DashboardComponent);
567
+ const userRole = new State<string>('guest');
567
568
 
568
- <Switch component={currentView} />;
569
+ <Switch condition={userRole}>
570
+ <Case when="admin" create={AdminPanel} />
571
+ <Case when="user" create={UserPanel} />
572
+ <Case when="guest" create={GuestPanel} />
573
+ <Default create={GuestPanel} />
574
+ </Switch>;
569
575
  ```
570
576
 
571
- ### Props
577
+ ### Props (Switch)
578
+
579
+ | Prop | Type | Default | Description |
580
+ | ----------- | -------------------------------- | ------- | ---------------------------------- |
581
+ | `condition` | `State<string> \| State<number>` | - | State to match against Case values |
582
+ | `width` | `number \| 'fill'` | - | Component width |
583
+ | `height` | `number \| 'fill'` | - | Component height |
584
+
585
+ ### Props (Case)
586
+
587
+ | Prop | Type | Description |
588
+ | -------- | ------------------ | ----------------------------------- |
589
+ | `when` | `string \| number` | Value to match against condition |
590
+ | `create` | `any` | Component class/function to create |
591
+ | `with` | `any` | Optional props to pass to component |
572
592
 
573
- | Prop | Type | Default | Description |
574
- | ----------- | ---------------------------------------------------------------- | ------- | --------------------------------------------------------- |
575
- | `component` | `State<Component \| (() => Component) \| (new () => Component)>` | - | State holding component instance, constructor, or factory |
576
- | `width` | `number \| 'fill'` | - | Component width |
577
- | `height` | `number \| 'fill'` | - | Component height |
593
+ ### Props (Default)
594
+
595
+ | Prop | Type | Description |
596
+ | -------- | ----- | ----------------------------------- |
597
+ | `create` | `any` | Component class/function to create |
598
+ | `with` | `any` | Optional props to pass to component |
578
599
 
579
600
  ### Common Patterns
580
601
 
581
- **Pattern 1: View Switching**
602
+ **Pattern 1: Simple Conditional Rendering**
582
603
 
583
604
  ```tsx
584
- import { State, Switch, Button, Column } from 'asciitorium';
605
+ import { State, Switch, Case, Default } from 'asciitorium';
585
606
 
586
- // Component classes/constructors
587
- class DashboardView extends Component {
588
- /* ... */
589
- }
590
- class SettingsView extends Component {
591
- /* ... */
592
- }
593
- class ProfileView extends Component {
594
- /* ... */
595
- }
607
+ // Function components
608
+ const AdminPanel = () => <Column>Admin View</Column>;
609
+ const UserPanel = () => <Column>User View</Column>;
610
+ const GuestPanel = () => <Column>Guest View</Column>;
596
611
 
597
- const currentView = new State<any>(DashboardView);
612
+ const userRole = new State<string>('guest');
598
613
 
599
614
  <Column>
600
- <Button onClick={() => (currentView.value = DashboardView)}>Dashboard</Button>
601
- <Button onClick={() => (currentView.value = SettingsView)}>Settings</Button>
602
- <Button onClick={() => (currentView.value = ProfileView)}>Profile</Button>
603
-
604
- <Switch component={currentView} width="fill" height="fill" />
615
+ <Button onClick={() => (userRole.value = 'admin')}>Admin</Button>
616
+ <Button onClick={() => (userRole.value = 'user')}>User</Button>
617
+ <Button onClick={() => (userRole.value = 'guest')}>Guest</Button>
618
+
619
+ <Switch condition={userRole} width="fill" height="fill">
620
+ <Case when="admin" create={AdminPanel} />
621
+ <Case when="user" create={UserPanel} />
622
+ <Case when="guest" create={GuestPanel} />
623
+ <Default create={GuestPanel} />
624
+ </Switch>
605
625
  </Column>;
606
626
  ```
607
627
 
608
- **Pattern 2: Dynamic Content with Select**
628
+ **Pattern 2: Class Components with Props**
609
629
 
610
630
  ```tsx
611
- import { State, Select, Option, Switch } from 'asciitorium';
631
+ import { State, Switch, Case, Component } from 'asciitorium';
612
632
 
613
- // Component map
614
- const pageMap: Record<string, any> = {
615
- home: HomeComponent,
616
- about: AboutComponent,
617
- contact: ContactComponent,
618
- };
633
+ class SettingsPanel extends Component {
634
+ constructor(props: { theme: string }) {
635
+ super(props);
636
+ // Use props.theme
637
+ }
638
+ }
619
639
 
620
- const selectedKey = new State<string>('home');
621
- const selectedComponent = new State<any>(pageMap['home']);
640
+ const currentView = new State<string>('settings');
622
641
 
623
- // Sync selected component with key
624
- selectedKey.subscribe((key) => {
625
- selectedComponent.value = pageMap[key];
626
- });
642
+ <Switch condition={currentView}>
643
+ <Case when="settings" create={SettingsPanel} with={{ theme: 'dark' }} />
644
+ <Case when="profile" create={ProfilePanel} />
645
+ </Switch>;
646
+ ```
647
+
648
+ **Pattern 3: Navigation with Select**
649
+
650
+ ```tsx
651
+ import { State, Select, Option, Switch, Case } from 'asciitorium';
652
+
653
+ const selectedPage = new State<string>('home');
627
654
 
628
655
  <Column>
629
- <Select selected={selectedKey}>
656
+ <Select selected={selectedPage}>
630
657
  <Option value="home" label="Home" />
631
658
  <Option value="about" label="About" />
632
659
  <Option value="contact" label="Contact" />
633
660
  </Select>
634
661
 
635
- <Switch component={selectedComponent} width="fill" height="fill" />
662
+ <Switch condition={selectedPage} width="fill" height="fill">
663
+ <Case when="home" create={HomePage} />
664
+ <Case when="about" create={AboutPage} />
665
+ <Case when="contact" create={ContactPage} />
666
+ </Switch>
636
667
  </Column>;
637
668
  ```
638
669
 
639
- **Pattern 3: Factory Functions**
640
-
641
- ```tsx
642
- // Using factory functions instead of constructors
643
- const viewFactory = () => new CustomView({ customProp: 'value' });
644
- const currentView = new State<any>(viewFactory);
645
-
646
- <Switch component={currentView} />;
647
- ```
648
-
649
- **Pattern 4: Component Instances**
670
+ **Pattern 4: Numeric Conditions**
650
671
 
651
672
  ```tsx
652
- // Using pre-instantiated components
653
- const dashboard = new DashboardView({ width: 80 });
654
- const settings = new SettingsView({ width: 80 });
655
-
656
- const currentView = new State<Component>(dashboard);
673
+ const level = new State<number>(1);
657
674
 
658
- <Button onClick={() => currentView.value = settings}>Switch to Settings</Button>
659
- <Switch component={currentView} />
675
+ <Switch condition={level}>
676
+ <Case when={1} create={Level1} />
677
+ <Case when={2} create={Level2} />
678
+ <Case when={3} create={Level3} />
679
+ <Default create={CompletionScreen} />
680
+ </Switch>;
660
681
  ```
661
682
 
662
683
  ### Common Mistakes
663
684
 
664
- ❌ **WRONG** - Not using State
685
+ ❌ **WRONG** - Using JSX children
665
686
 
666
687
  ```tsx
667
- <Switch component={MyComponent} /> // NO! component must be a State
688
+ <Switch condition={role}>
689
+ <Case when="admin">
690
+ <AdminPanel /> {/* Components persist in memory! */}
691
+ </Case>
692
+ </Switch>
668
693
  ```
669
694
 
670
- ✅ **CORRECT** - Using State
695
+ ✅ **CORRECT** - Using create prop
671
696
 
672
697
  ```tsx
673
- const view = new State(MyComponent);
674
- <Switch component={view} />;
698
+ <Switch condition={role}>
699
+ <Case when="admin" create={AdminPanel} />
700
+ </Switch>
675
701
  ```
676
702
 
677
- ❌ **WRONG** - Trying to pass component as string
703
+ ❌ **WRONG** - Missing Default fallback
678
704
 
679
705
  ```tsx
680
- const view = new State<string>('DashboardComponent'); // NO! Must be actual component
681
- <Switch component={view} />;
706
+ <Switch condition={status}>
707
+ <Case when="loading" create={Spinner} />
708
+ {/* No fallback - nothing shows if status is unknown */}
709
+ </Switch>
682
710
  ```
683
711
 
684
- ✅ **CORRECT** - Using actual component reference
712
+ ✅ **CORRECT** - Including Default
685
713
 
686
714
  ```tsx
687
- const view = new State<any>(DashboardComponent); // Component constructor
688
- <Switch component={view} />;
715
+ <Switch condition={status}>
716
+ <Case when="loading" create={Spinner} />
717
+ <Default create={ErrorMessage} />
718
+ </Switch>
689
719
  ```
690
720
 
691
- ---
721
+
692
722
 
693
723
  ## TextInput
694
724
 
@@ -771,73 +801,88 @@ const text = new State('initial');
771
801
  <Button onClick={() => console.log(inputValue.value)}>Submit</Button>
772
802
  ```
773
803
 
774
- ---
804
+
775
805
 
776
806
  ## Art
777
807
 
778
808
  ### Basic Usage
779
809
 
780
810
  ```tsx
781
- // Load from file
782
- <Art src="logo" />
783
-
784
- // ASCII art from figlet font
785
- <Art font="standard" text="Hello" />
811
+ // Load sprite from file
812
+ <Art sprite="logo" />
786
813
 
787
814
  // Align center
788
- <Art src="banner" align="center" />
815
+ <Art sprite="banner" align="center" />
789
816
  ```
790
817
 
791
818
  ### Props
792
819
 
793
- | Prop | Type | Default | Description |
794
- | -------- | ------------------------------- | -------- | -------------------------------- |
795
- | `src` | `string` | - | Art file name (from art/ folder) |
796
- | `font` | `string` | - | Figlet font name |
797
- | `text` | `string` | - | Text to render with font |
798
- | `width` | `number \| 'auto'` | `'auto'` | Component width |
799
- | `height` | `number \| 'auto'` | `'auto'` | Component height |
800
- | `align` | `'left' \| 'center' \| 'right'` | `'left'` | Horizontal alignment |
801
- | `border` | `boolean` | `false` | Show border |
820
+ | Prop | Type | Default | Description |
821
+ | -------- | ------------------------------- | -------- | ------------------------------------ |
822
+ | `sprite` | `string` | - | Sprite file name (from art/sprites/) |
823
+ | `width` | `number \| 'auto'` | `'auto'` | Component width |
824
+ | `height` | `number \| 'auto'` | `'auto'` | Component height |
825
+ | `align` | `'left' \| 'center' \| 'right'` | `'left'` | Horizontal alignment |
826
+ | `border` | `boolean` | `false` | Show border |
802
827
 
803
828
  ### Common Patterns
804
829
 
805
- **Pattern 1: Logo from File**
830
+ **Pattern 1: Static Sprite**
806
831
 
807
832
  ```tsx
808
- // Loads from art/logo.art
809
- <Art src="logo" align="center" />
833
+ // Loads from art/sprites/logo.art
834
+ <Art sprite="logo" align="center" />
810
835
  ```
811
836
 
812
- **Pattern 2: Generated ASCII Text**
837
+ **Pattern 2: Animated Sprite**
813
838
 
814
839
  ```tsx
815
- <Art font="standard" text="Game Over" align="center" />
840
+ // Loads animated art (auto-plays)
841
+ <Art sprite="beating-heart" width={20} />
816
842
  ```
817
843
 
818
- **Pattern 3: Animated Sprite**
844
+
845
+
846
+ ## Banner
847
+
848
+ ### Basic Usage
819
849
 
820
850
  ```tsx
821
- // Loads animated art (auto-plays)
822
- <Art src="beating-heart" width={20} />
851
+ // Render text with font
852
+ <Banner font="standard" text="Hello" />
853
+
854
+ // Align center
855
+ <Banner font="shadows" text="Game Over" align="center" />
823
856
  ```
824
857
 
825
- ### Common Mistakes
858
+ ### Props
826
859
 
827
- **WRONG** - Using both src and font
860
+ | Prop | Type | Default | Description |
861
+ | --------------- | ------------------------------- | -------- | --------------------------------- |
862
+ | `font` | `string` | - | Font name (from art/fonts/) |
863
+ | `text` | `string \| State<string>` | - | Text to render with font |
864
+ | `letterSpacing` | `number` | `0` | Additional spacing between chars |
865
+ | `width` | `number \| 'auto'` | `'auto'` | Component width |
866
+ | `height` | `number \| 'auto'` | `'auto'` | Component height |
867
+ | `align` | `'left' \| 'center' \| 'right'` | `'left'` | Horizontal alignment |
868
+ | `border` | `boolean` | `false` | Show border |
869
+
870
+ ### Common Patterns
871
+
872
+ **Pattern 1: Title Screen**
828
873
 
829
874
  ```tsx
830
- <Art src="logo" font="standard" /> // Conflicting props
875
+ <Banner font="shadows" text="asciitorium" align="center" />
831
876
  ```
832
877
 
833
- **CORRECT** - Use one or the other
878
+ **Pattern 2: Reactive Text**
834
879
 
835
880
  ```tsx
836
- <Art src="logo" /> // From file
837
- <Art font="standard" text="Title" /> // Generated
881
+ const score = new State(0);
882
+ <Banner font="pixel" text={score} letterSpacing={1} />
838
883
  ```
839
884
 
840
- ---
885
+
841
886
 
842
887
  ## Keybind
843
888
 
@@ -926,7 +971,7 @@ const gameStarted = new State(false);
926
971
  <Keybind keyBinding="ArrowUp" action={() => moveUp()} />
927
972
  ```
928
973
 
929
- ---
974
+
930
975
 
931
976
  ## GameWorld
932
977
 
@@ -1055,7 +1100,7 @@ gameWorld.player.value.x += 1; // Bypasses collision detection
1055
1100
  gameWorld.moveForward(); // Handles collision
1056
1101
  ```
1057
1102
 
1058
- ---
1103
+
1059
1104
 
1060
1105
  ## MapView
1061
1106
 
@@ -1138,7 +1183,7 @@ gameWorld.player.subscribe((p) => {
1138
1183
  <MapView mapAsset={gameWorld.mapAsset} player={gameWorld.player} />
1139
1184
  ```
1140
1185
 
1141
- ---
1186
+
1142
1187
 
1143
1188
  ## FirstPersonView
1144
1189
 
@@ -1216,7 +1261,7 @@ gameWorld.player.subscribe((p) => {
1216
1261
  <FirstPersonView mapAsset={gameWorld.mapAsset} player={gameWorld.player} />
1217
1262
  ```
1218
1263
 
1219
- ---
1264
+
1220
1265
 
1221
1266
  ## PerfMonitor
1222
1267
 
@@ -1267,7 +1312,7 @@ const showPerf = new State(false);
1267
1312
  <PerfMonitor visible={showPerf} />
1268
1313
  ```
1269
1314
 
1270
- ---
1315
+
1271
1316
 
1272
1317
  ## Component Lifecycle
1273
1318
 
@@ -1307,7 +1352,7 @@ count.subscribe((newValue) => {
1307
1352
  // No manual cleanup needed in most cases
1308
1353
  ```
1309
1354
 
1310
- ---
1355
+
1311
1356
 
1312
1357
  ## Legend System (Game Maps)
1313
1358
 
@@ -1368,7 +1413,7 @@ if (tile?.tag === 'door') {
1368
1413
  }
1369
1414
  ```
1370
1415
 
1371
- ---
1416
+
1372
1417
 
1373
1418
  ## Directory Structure
1374
1419
 
@@ -1409,7 +1454,7 @@ Example:
1409
1454
  ╚══════════════════╝
1410
1455
  ```
1411
1456
 
1412
- ---
1457
+
1413
1458
 
1414
1459
  ## Type Annotations
1415
1460
 
@@ -1451,7 +1496,7 @@ const entry: LegendEntry = {
1451
1496
  };
1452
1497
  ```
1453
1498
 
1454
- ---
1499
+
1455
1500
 
1456
1501
  ## Complete Example: Simple Game
1457
1502
 
@@ -1522,28 +1567,32 @@ const app = (
1522
1567
  await app.start();
1523
1568
  ```
1524
1569
 
1525
- ---
1570
+
1526
1571
 
1527
1572
  ## Understanding "auto" vs Omitting Props
1528
1573
 
1529
- **Key Concept:** In ASCIITORIUM, omitting `width` or `height` props is the same as setting them to `undefined` or `"auto"`.
1574
+ **Key Concept:** In ASCIITORIUM, omitting `width` or `height` props is the same
1575
+ as setting them to `undefined` or `"auto"`.
1530
1576
 
1531
1577
  - `width={40}` → Fixed width of 40 characters
1532
1578
  - `width="fill"` → Fill available parent space
1533
1579
  - `width="auto"` → Auto-size to content (same as omitting the prop)
1534
1580
  - _Omitting width prop_ → Auto-size to content (recommended approach)
1535
1581
 
1536
- **Recommendation:** Omit props instead of using `"auto"` for clarity. The type system allows `"auto"` but it's unnecessary.
1582
+ **Recommendation:** Omit props instead of using `"auto"` for clarity. The type
1583
+ system allows `"auto"` but it's unnecessary.
1537
1584
 
1538
1585
  **Component-specific auto-sizing:**
1539
1586
 
1540
- - **Text**: Auto-sizes to content length (width) and wrapped lines (height) when props omitted
1541
- - **Button**: Always calculates size based on content (`buttonText.length + 7` for width, `4` for height)
1587
+ - **Text**: Auto-sizes to content length (width) and wrapped lines (height) when
1588
+ props omitted
1589
+ - **Button**: Always calculates size based on content (`buttonText.length + 7`
1590
+ for width, `4` for height)
1542
1591
  - **Art**: Auto-sizes to loaded art dimensions when props omitted
1543
1592
  - **Column**: Auto-sizes to fit children when width omitted
1544
1593
  - **Row**: Defaults to `width="fill"`
1545
1594
 
1546
- ---
1595
+
1547
1596
 
1548
1597
  ## Tips for LLMs
1549
1598
 
@@ -1552,12 +1601,16 @@ await app.start();
1552
1601
  3. **Text newlines use `¶` (pilcrow)** - Not `\n`
1553
1602
  4. **Components accept children via JSX or explicit props** - Both patterns work
1554
1603
  5. **GameWorld must be awaited** - `await gameWorld.ready` before rendering
1555
- 6. **Keybinds are invisible components** - They never render, just register handlers
1556
- 7. **MapView and FirstPersonView are display-only** - Movement logic goes in GameWorld or Keybinds
1604
+ 6. **Keybinds are invisible components** - They never render, just register
1605
+ handlers
1606
+ 7. **MapView and FirstPersonView are display-only** - Movement logic goes in
1607
+ GameWorld or Keybinds
1557
1608
  8. **Legend system drives collision** - Use `isSolid()` for movement validation
1558
1609
  9. **No CSS or className** - ASCIITORIUM uses ASCII rendering, not DOM styling
1559
- 10. **Omit width/height props for auto-sizing** - Components have smart defaults (Text/Art/Column size to content, Button calculates from label, Row fills width)
1610
+ 10. **Omit width/height props for auto-sizing** - Components have smart defaults
1611
+ (Text/Art/Column size to content, Button calculates from label, Row fills
1612
+ width)
1613
+
1560
1614
 
1561
- ---
1562
1615
 
1563
1616
  _End of Reference_