create-asciitorium 0.1.45 → 0.1.46

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
 
@@ -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,7 +801,7 @@ 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
 
@@ -837,7 +867,7 @@ const text = new State('initial');
837
867
  <Art font="standard" text="Title" /> // Generated
838
868
  ```
839
869
 
840
- ---
870
+
841
871
 
842
872
  ## Keybind
843
873
 
@@ -926,7 +956,7 @@ const gameStarted = new State(false);
926
956
  <Keybind keyBinding="ArrowUp" action={() => moveUp()} />
927
957
  ```
928
958
 
929
- ---
959
+
930
960
 
931
961
  ## GameWorld
932
962
 
@@ -1055,7 +1085,7 @@ gameWorld.player.value.x += 1; // Bypasses collision detection
1055
1085
  gameWorld.moveForward(); // Handles collision
1056
1086
  ```
1057
1087
 
1058
- ---
1088
+
1059
1089
 
1060
1090
  ## MapView
1061
1091
 
@@ -1138,7 +1168,7 @@ gameWorld.player.subscribe((p) => {
1138
1168
  <MapView mapAsset={gameWorld.mapAsset} player={gameWorld.player} />
1139
1169
  ```
1140
1170
 
1141
- ---
1171
+
1142
1172
 
1143
1173
  ## FirstPersonView
1144
1174
 
@@ -1216,7 +1246,7 @@ gameWorld.player.subscribe((p) => {
1216
1246
  <FirstPersonView mapAsset={gameWorld.mapAsset} player={gameWorld.player} />
1217
1247
  ```
1218
1248
 
1219
- ---
1249
+
1220
1250
 
1221
1251
  ## PerfMonitor
1222
1252
 
@@ -1267,7 +1297,7 @@ const showPerf = new State(false);
1267
1297
  <PerfMonitor visible={showPerf} />
1268
1298
  ```
1269
1299
 
1270
- ---
1300
+
1271
1301
 
1272
1302
  ## Component Lifecycle
1273
1303
 
@@ -1307,7 +1337,7 @@ count.subscribe((newValue) => {
1307
1337
  // No manual cleanup needed in most cases
1308
1338
  ```
1309
1339
 
1310
- ---
1340
+
1311
1341
 
1312
1342
  ## Legend System (Game Maps)
1313
1343
 
@@ -1368,7 +1398,7 @@ if (tile?.tag === 'door') {
1368
1398
  }
1369
1399
  ```
1370
1400
 
1371
- ---
1401
+
1372
1402
 
1373
1403
  ## Directory Structure
1374
1404
 
@@ -1409,7 +1439,7 @@ Example:
1409
1439
  ╚══════════════════╝
1410
1440
  ```
1411
1441
 
1412
- ---
1442
+
1413
1443
 
1414
1444
  ## Type Annotations
1415
1445
 
@@ -1451,7 +1481,7 @@ const entry: LegendEntry = {
1451
1481
  };
1452
1482
  ```
1453
1483
 
1454
- ---
1484
+
1455
1485
 
1456
1486
  ## Complete Example: Simple Game
1457
1487
 
@@ -1522,28 +1552,32 @@ const app = (
1522
1552
  await app.start();
1523
1553
  ```
1524
1554
 
1525
- ---
1555
+
1526
1556
 
1527
1557
  ## Understanding "auto" vs Omitting Props
1528
1558
 
1529
- **Key Concept:** In ASCIITORIUM, omitting `width` or `height` props is the same as setting them to `undefined` or `"auto"`.
1559
+ **Key Concept:** In ASCIITORIUM, omitting `width` or `height` props is the same
1560
+ as setting them to `undefined` or `"auto"`.
1530
1561
 
1531
1562
  - `width={40}` → Fixed width of 40 characters
1532
1563
  - `width="fill"` → Fill available parent space
1533
1564
  - `width="auto"` → Auto-size to content (same as omitting the prop)
1534
1565
  - _Omitting width prop_ → Auto-size to content (recommended approach)
1535
1566
 
1536
- **Recommendation:** Omit props instead of using `"auto"` for clarity. The type system allows `"auto"` but it's unnecessary.
1567
+ **Recommendation:** Omit props instead of using `"auto"` for clarity. The type
1568
+ system allows `"auto"` but it's unnecessary.
1537
1569
 
1538
1570
  **Component-specific auto-sizing:**
1539
1571
 
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)
1572
+ - **Text**: Auto-sizes to content length (width) and wrapped lines (height) when
1573
+ props omitted
1574
+ - **Button**: Always calculates size based on content (`buttonText.length + 7`
1575
+ for width, `4` for height)
1542
1576
  - **Art**: Auto-sizes to loaded art dimensions when props omitted
1543
1577
  - **Column**: Auto-sizes to fit children when width omitted
1544
1578
  - **Row**: Defaults to `width="fill"`
1545
1579
 
1546
- ---
1580
+
1547
1581
 
1548
1582
  ## Tips for LLMs
1549
1583
 
@@ -1552,12 +1586,16 @@ await app.start();
1552
1586
  3. **Text newlines use `¶` (pilcrow)** - Not `\n`
1553
1587
  4. **Components accept children via JSX or explicit props** - Both patterns work
1554
1588
  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
1589
+ 6. **Keybinds are invisible components** - They never render, just register
1590
+ handlers
1591
+ 7. **MapView and FirstPersonView are display-only** - Movement logic goes in
1592
+ GameWorld or Keybinds
1557
1593
  8. **Legend system drives collision** - Use `isSolid()` for movement validation
1558
1594
  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)
1595
+ 10. **Omit width/height props for auto-sizing** - Components have smart defaults
1596
+ (Text/Art/Column size to content, Button calculates from label, Row fills
1597
+ width)
1598
+
1560
1599
 
1561
- ---
1562
1600
 
1563
1601
  _End of Reference_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-asciitorium",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "private": false,
5
5
  "description": "Scaffold a Vite + TypeScript project prewired for asciitorium (web + cli).",
6
6
  "bin": {