diff --git a/src/App.tsx b/src/App.tsx
index 82cf6ee..467b632 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { StartScreen } from "./components/StartScreen.js";
-import { GameScreen } from "./components/GameScreen.js";
+import { GameScreen } from "./components/GameScreen/index.js";
import { GameContext } from "./components/GameContext.js";
import { ScoreScreen } from "./components/ScoreScreen.js";
import { HelpScreen } from "./components/HelpScreen.js";
diff --git a/src/components/GameScreen/Divider.tsx b/src/components/GameScreen/Divider.tsx
new file mode 100644
index 0000000..346df61
--- /dev/null
+++ b/src/components/GameScreen/Divider.tsx
@@ -0,0 +1,21 @@
+import React, { useState, useEffect } from "react";
+import { Text, Box, DOMElement, measureElement } from "ink";
+
+const dividerChar = "─";
+
+export function Divider({ containerRef }: { containerRef: DOMElement | null }) {
+ const [width, setWidth] = useState(0);
+
+ useEffect(() => {
+ if (containerRef) {
+ const output = measureElement(containerRef);
+ setWidth(output.width);
+ }
+ }, [containerRef]);
+
+ return (
+
+ {dividerChar.repeat(width)}
+
+ );
+}
diff --git a/src/components/TextInput/index.tsx b/src/components/GameScreen/TextInput/index.tsx
similarity index 100%
rename from src/components/TextInput/index.tsx
rename to src/components/GameScreen/TextInput/index.tsx
diff --git a/src/components/TextInput/use-text-input-state.ts b/src/components/GameScreen/TextInput/use-text-input-state.ts
similarity index 100%
rename from src/components/TextInput/use-text-input-state.ts
rename to src/components/GameScreen/TextInput/use-text-input-state.ts
diff --git a/src/components/TextInput/use-text-input.ts b/src/components/GameScreen/TextInput/use-text-input.ts
similarity index 100%
rename from src/components/TextInput/use-text-input.ts
rename to src/components/GameScreen/TextInput/use-text-input.ts
diff --git a/src/components/GameScreen/Typed.tsx b/src/components/GameScreen/Typed.tsx
new file mode 100644
index 0000000..116c984
--- /dev/null
+++ b/src/components/GameScreen/Typed.tsx
@@ -0,0 +1,46 @@
+import React, { useState, useEffect } from "react";
+import { Text, type TextProps, useInput } from "ink";
+import { GameContext } from "../GameContext.js";
+
+export function Typed({
+ text,
+ enabled = true,
+ children,
+ ...props
+}: React.PropsWithChildren<{ text: string; enabled?: boolean } & TextProps>) {
+ const settings = GameContext.useSelector((snapshot) => snapshot.context.settings);
+ const [current, setCurrent] = useState(0);
+ const [finished, setFinished] = useState(false);
+ const shouldAnimate = !settings.disableAnimations && enabled;
+
+ useEffect(() => {
+ const id = setTimeout(() => {
+ if (shouldAnimate && current < text.length) {
+ setCurrent(current + 1);
+ } else {
+ clearTimeout(id);
+ setFinished(true);
+ }
+ }, 50);
+
+ return () => clearTimeout(id);
+ }, [text, shouldAnimate, current]);
+
+ useInput(
+ (input) => {
+ if (input === " ") {
+ setCurrent(text.length);
+ }
+ },
+ {
+ isActive: shouldAnimate && current < text.length,
+ },
+ );
+
+ return (
+ <>
+ {shouldAnimate ? text.slice(0, current) : text}
+ {shouldAnimate ? (finished ? children : null) : children}
+ >
+ );
+}
diff --git a/src/components/GameScreen.tsx b/src/components/GameScreen/index.tsx
similarity index 89%
rename from src/components/GameScreen.tsx
rename to src/components/GameScreen/index.tsx
index 3b31f86..f1525c6 100644
--- a/src/components/GameScreen.tsx
+++ b/src/components/GameScreen/index.tsx
@@ -1,13 +1,13 @@
-import React, { useEffect, useState } from "react";
-import { Box, DOMElement, measureElement, Text, TextProps, useInput } from "ink";
+import React, { useState, useDeferredValue } from "react";
import chalk from "chalk";
-import { GameContext } from "./GameContext.js";
+import { Box, DOMElement, Text, useInput } from "ink";
import { Alert, Badge, ConfirmInput } from "@inkjs/ui";
+import { GameContext } from "../GameContext.js";
import { TextInput } from "./TextInput/index.js";
-import { calculateCostForRepair, getAvailableStorage, getShipStatus } from "../store/utils.js";
-import { goods, ports } from "../store/constants.js";
-
-const dividerChar = "─";
+import { Divider } from "./Divider.js";
+import { Typed } from "./Typed.js";
+import { calculateCostForRepair, getAvailableStorage, getShipStatus } from "../../store/utils.js";
+import { goods, ports } from "../../store/constants.js";
export function GameScreen() {
const [ref, setRef] = useState(null);
@@ -41,23 +41,6 @@ export function GameScreen() {
);
}
-function Divider({ containerRef }: { containerRef: DOMElement | null }) {
- const [width, setWidth] = useState(0);
-
- useEffect(() => {
- if (containerRef) {
- const output = measureElement(containerRef);
- setWidth(output.width);
- }
- }, [containerRef]);
-
- return (
-
- {dividerChar.repeat(width)}
-
- );
-}
-
function StatusBar() {
const context = GameContext.useSelector((snapshot) => snapshot.context);
const shipHealth = getShipStatus(context.ship.health);
@@ -470,46 +453,3 @@ function RetireAction() {
);
}
-
-function Typed({
- text,
- enabled = true,
- children,
- ...props
-}: React.PropsWithChildren<{ text: string; enabled?: boolean } & TextProps>) {
- const settings = GameContext.useSelector((snapshot) => snapshot.context.settings);
- const [current, setCurrent] = useState(0);
- const [finished, setFinished] = useState(false);
- const shouldAnimate = !settings.disableAnimations && enabled;
-
- useEffect(() => {
- const id = setTimeout(() => {
- if (shouldAnimate && current < text.length) {
- setCurrent(current + 1);
- } else {
- clearTimeout(id);
- setFinished(true);
- }
- }, 50);
-
- return () => clearTimeout(id);
- }, [text, shouldAnimate, current]);
-
- useInput(
- (input) => {
- if (input === " ") {
- setCurrent(text.length);
- }
- },
- {
- isActive: shouldAnimate && current < text.length,
- },
- );
-
- return (
- <>
- {shouldAnimate ? text.slice(0, current) : text}
- {shouldAnimate ? (finished ? children : null) : children}
- >
- );
-}