summaryrefslogtreecommitdiff
path: root/packages/create-astro/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/create-astro/src/components')
-rw-r--r--packages/create-astro/src/components/App.tsx93
-rw-r--r--packages/create-astro/src/components/Confirm.tsx49
-rw-r--r--packages/create-astro/src/components/Emoji.tsx5
-rw-r--r--packages/create-astro/src/components/Exit.tsx9
-rw-r--r--packages/create-astro/src/components/Finalize.tsx27
-rw-r--r--packages/create-astro/src/components/Header.tsx20
-rw-r--r--packages/create-astro/src/components/Help.tsx62
-rw-r--r--packages/create-astro/src/components/Install.tsx19
-rw-r--r--packages/create-astro/src/components/ProjectName.tsx24
-rw-r--r--packages/create-astro/src/components/Select.tsx32
-rw-r--r--packages/create-astro/src/components/Spacer.tsx5
-rw-r--r--packages/create-astro/src/components/Spinner.tsx200
-rw-r--r--packages/create-astro/src/components/Template.tsx23
-rw-r--r--packages/create-astro/src/components/Version.tsx6
14 files changed, 574 insertions, 0 deletions
diff --git a/packages/create-astro/src/components/App.tsx b/packages/create-astro/src/components/App.tsx
new file mode 100644
index 000000000..fd9192bb6
--- /dev/null
+++ b/packages/create-astro/src/components/App.tsx
@@ -0,0 +1,93 @@
+import React, {FC, useEffect} from 'react';
+import { prepareTemplate, isEmpty, emptyDir } from '../utils';
+import Header from './Header';
+import Install from './Install';
+import ProjectName from './ProjectName';
+import Template from './Template';
+import Confirm from './Confirm';
+import Finalize from './Finalize';
+
+interface Context {
+ use: 'npm'|'yarn';
+ run: boolean;
+ projectExists?: boolean;
+ force?: boolean;
+ projectName?: string;
+ template?: string;
+ templates: string[];
+ ready?: boolean;
+}
+
+const getStep = ({ projectName, projectExists: exists, template, force, ready }: Context) => {
+ switch (true) {
+ case !projectName: return {
+ key: 'projectName',
+ Component: ProjectName
+ };
+ case projectName && exists === true && typeof force === 'undefined': return {
+ key: 'force',
+ Component: Confirm
+ }
+ case (exists === false || force) && !template: return {
+ key: 'template',
+ Component: Template
+ };
+ case !ready: return {
+ key: 'install',
+ Component: Install
+ };
+ default: return {
+ key: 'final',
+ Component: Finalize
+ }
+ }
+}
+
+const App: FC<{ context: Context }> = ({ context }) => {
+ const [state, setState] = React.useState(context);
+ const step = React.useRef(getStep(context));
+ const onSubmit = (value: string|boolean) => {
+ const { key } = step.current;
+ const newState = { ...state, [key]: value };
+ step.current = getStep(newState)
+ setState(newState)
+ }
+
+ useEffect(() => {
+ let isSubscribed = true
+ if (state.projectName && typeof state.projectExists === 'undefined') {
+ const newState = { ...state, projectExists: !isEmpty(state.projectName) };
+ step.current = getStep(newState)
+ if (isSubscribed) {
+ setState(newState);
+ }
+ }
+
+ if (state.projectName && (state.projectExists === false || state.force) && state.template) {
+ if (state.force) emptyDir(state.projectName);
+ prepareTemplate(context.use, state.template, state.projectName).then(() => {
+ if (isSubscribed) {
+ setState(v => {
+ const newState = {...v, ready: true };
+ step.current = getStep(newState);
+ return newState;
+ });
+ }
+ });
+ }
+
+ return () => {
+ isSubscribed = false;
+ }
+ }, [state]);
+ const { Component } = step.current;
+
+ return (
+ <>
+ <Header context={state}/>
+ <Component context={state} onSubmit={onSubmit} />
+ </>
+ )
+};
+
+export default App;
diff --git a/packages/create-astro/src/components/Confirm.tsx b/packages/create-astro/src/components/Confirm.tsx
new file mode 100644
index 000000000..1fd1aa862
--- /dev/null
+++ b/packages/create-astro/src/components/Confirm.tsx
@@ -0,0 +1,49 @@
+import React, { FC } from 'react';
+import { Box, Text, useInput, useApp } from 'ink';
+import Spacer from './Spacer';
+import Select from './Select';
+
+const Confirm: FC<{ message?: any; context: any; onSubmit: (value: boolean) => void }> = ({ message, context: { projectName }, onSubmit }) => {
+ const { exit } = useApp();
+ const handleSubmit = (v: boolean) => {
+ if (!v) return exit();
+ onSubmit(v);
+ };
+
+ return (
+ <>
+ <Box display="flex">
+ {!message ? (
+ <>
+ <Text color="#FFBE2D">{'[uh-oh]'}</Text>
+ <Text>
+ {' '}
+ It appears <Text color="#17C083">./{projectName}</Text> is not empty. Overwrite?
+ </Text>
+ </>
+ ) : (
+ message
+ )}
+ </Box>
+ <Box display="flex">
+ <Spacer width={6} />
+ <Select
+ items={[
+ {
+ value: false,
+ label: 'no'
+ },
+ {
+ value: true,
+ label: 'yes',
+ description: <Text color="#FF1639">overwrite</Text>,
+ },
+ ]}
+ onSelect={handleSubmit}
+ />
+ </Box>
+ </>
+ );
+};
+
+export default Confirm;
diff --git a/packages/create-astro/src/components/Emoji.tsx b/packages/create-astro/src/components/Emoji.tsx
new file mode 100644
index 000000000..3af9ae508
--- /dev/null
+++ b/packages/create-astro/src/components/Emoji.tsx
@@ -0,0 +1,5 @@
+import React from 'react';
+import { Text } from 'ink';
+import { isWin } from '../utils';
+
+export default ({ children }) => isWin() ? null : <Text>{children}</Text>
diff --git a/packages/create-astro/src/components/Exit.tsx b/packages/create-astro/src/components/Exit.tsx
new file mode 100644
index 000000000..cc3096705
--- /dev/null
+++ b/packages/create-astro/src/components/Exit.tsx
@@ -0,0 +1,9 @@
+import React, { FC } from 'react';
+import { Box, Text } from 'ink';
+import { isDone } from '../utils';
+
+const Exit: FC<{ didError?: boolean }> = ({ didError }) => isDone ? null : <Box marginTop={1} display="flex">
+ <Text color={didError ? "#FF1639" : "#FFBE2D"}>[abort]</Text>
+ <Text> astro cancelled</Text>
+</Box>
+export default Exit;
diff --git a/packages/create-astro/src/components/Finalize.tsx b/packages/create-astro/src/components/Finalize.tsx
new file mode 100644
index 000000000..8d2a2103a
--- /dev/null
+++ b/packages/create-astro/src/components/Finalize.tsx
@@ -0,0 +1,27 @@
+import React, { FC, useEffect } from 'react';
+import { Box, Text } from 'ink';
+import { cancelProcessListeners } from '../utils';
+
+const Finalize: FC<{ context: any }> = ({ context: { use, projectName } }) => {
+ useEffect(() => {
+ cancelProcessListeners();
+ process.exit(0);
+ }, []);
+
+ return <>
+ <Box display="flex">
+ <Text color="#17C083">{'[ yes ]'}</Text>
+ <Text> Project initialized at <Text color="#3894FF">./{projectName}</Text></Text>
+ </Box>
+ <Box display="flex" marginY={1}>
+ <Text dimColor>{'[ tip ]'}</Text>
+ <Box display="flex" marginLeft={1} flexDirection="column">
+ <Text>Get started by running</Text>
+ <Text color="#3894FF">cd ./{projectName}</Text>
+ <Text color="#3894FF">{use} start</Text>
+ </Box>
+ </Box>
+ </>;
+};
+
+export default Finalize;
diff --git a/packages/create-astro/src/components/Header.tsx b/packages/create-astro/src/components/Header.tsx
new file mode 100644
index 000000000..1d894a60e
--- /dev/null
+++ b/packages/create-astro/src/components/Header.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { Box, Text } from 'ink';
+
+const getMessage = ({ projectName, template }) => {
+ switch (true) {
+ case !projectName: return <Text dimColor>Gathering mission details</Text>;
+ case !template: return <Text dimColor>Optimizing navigational system</Text>;
+ default: return <Text color="black" backgroundColor="white"> {projectName} </Text>
+ }
+}
+
+const Header: React.FC<{ context: any }> = ({ context }) => (
+ <Box width={48} display="flex" marginY={1}>
+ <Text backgroundColor="#882DE7" color="white">{' astro '}</Text>
+ <Box marginLeft={1}>
+ {getMessage(context)}
+ </Box>
+ </Box>
+)
+export default Header;
diff --git a/packages/create-astro/src/components/Help.tsx b/packages/create-astro/src/components/Help.tsx
new file mode 100644
index 000000000..88fcf5633
--- /dev/null
+++ b/packages/create-astro/src/components/Help.tsx
@@ -0,0 +1,62 @@
+import React, { FC } from 'react';
+import { Box, Text } from 'ink';
+import { ARGS, ARG } from '../config';
+
+const Type: FC<{ type: any, enum?: string[] }> = ({ type, enum: e }) => {
+ if (type === Boolean) {
+ return <>
+ <Text color="#3894FF">true</Text>
+ <Text dimColor>|</Text>
+ <Text color="#3894FF">false</Text>
+ </>
+ }
+ if (e?.length > 0) {
+ return <>
+ {e.map((item, i, { length: len}) => {
+ if (i !== len - 1) {
+ return <Box key={item}>
+ <Text color="#17C083">{item}</Text>
+ <Text dimColor>|</Text>
+ </Box>
+ }
+
+ return <Text color="#17C083" key={item}>{item}</Text>
+ })}
+ </>
+ }
+
+ return <Text color="#3894FF">string</Text>;
+}
+
+const Command: FC<{ name: string, info: ARG }> = ({ name, info: { alias, description, type, enum: e } }) => {
+ return (
+ <Box display="flex" alignItems="flex-start">
+ <Box width={24} display="flex" flexGrow={0}>
+ <Text color="whiteBright">--{name}</Text>{alias && <Text dimColor> -{alias}</Text>}
+ </Box>
+ <Box width={24}>
+ <Type type={type} enum={e} />
+ </Box>
+ <Box>
+ <Text>{description}</Text>
+ </Box>
+ </Box>
+ );
+}
+
+const Help: FC<{ context: any }> = ({ context: { templates }}) => {
+ return (
+ <>
+ <Box width={48} display="flex" marginY={1}>
+ <Text backgroundColor="#882DE7" color="white">{' astro '}</Text>
+ <Box marginLeft={1}>
+ <Text color="black" backgroundColor="white"> help </Text>
+ </Box>
+ </Box>
+ <Box marginBottom={1} marginLeft={2} display="flex" flexDirection="column">
+ {Object.entries(ARGS).map(([name, info]) => <Command key={name} name={name} info={name === 'template' ? { ...info, enum: templates.map(({ value }) => value) } : info} /> )}
+ </Box>
+ </>
+ )
+};
+export default Help;
diff --git a/packages/create-astro/src/components/Install.tsx b/packages/create-astro/src/components/Install.tsx
new file mode 100644
index 000000000..d9d2bc9b6
--- /dev/null
+++ b/packages/create-astro/src/components/Install.tsx
@@ -0,0 +1,19 @@
+import React, { FC } from 'react';
+import { Box, Text } from 'ink';
+import Spacer from './Spacer';
+import Spinner from './Spinner';
+
+const Install: FC<{ context: any }> = ({ context: { use } }) => {
+ return <>
+ <Box display="flex">
+ <Spinner/>
+ <Text> Initiating launch sequence...</Text>
+ </Box>
+ <Box>
+ <Spacer />
+ <Text color="white" dimColor>(aka running <Text color="#17C083">{use === 'npm' ? 'npm install' : 'yarn'}</Text>)</Text>
+ </Box>
+ </>;
+};
+
+export default Install;
diff --git a/packages/create-astro/src/components/ProjectName.tsx b/packages/create-astro/src/components/ProjectName.tsx
new file mode 100644
index 000000000..87b976494
--- /dev/null
+++ b/packages/create-astro/src/components/ProjectName.tsx
@@ -0,0 +1,24 @@
+import React, { FC } from 'react';
+import { Box, Text } from 'ink';
+import Spacer from './Spacer';
+import TextInput from 'ink-text-input';
+// @ts-expect-error
+const { default: Input } = TextInput;
+
+const ProjectName: FC<{ onSubmit: (value: string) => void }> = ({ onSubmit }) => {
+ const [value, setValue] = React.useState('');
+ const handleSubmit = (v: string) => onSubmit(v);
+
+ return <>
+ <Box display="flex">
+ <Text color="#17C083">{'[query]'}</Text>
+ <Text> What is your project name?</Text>
+ </Box>
+ <Box display="flex">
+ <Spacer />
+ <Input value={value} onChange={setValue} onSubmit={handleSubmit} placeholder="my-project" />
+ </Box>
+ </>;
+};
+
+export default ProjectName;
diff --git a/packages/create-astro/src/components/Select.tsx b/packages/create-astro/src/components/Select.tsx
new file mode 100644
index 000000000..acf8eb29f
--- /dev/null
+++ b/packages/create-astro/src/components/Select.tsx
@@ -0,0 +1,32 @@
+import SelectInput from 'ink-select-input';
+import React, { FC } from 'react';
+import { Text, Box } from 'ink';
+// @ts-expect-error
+const { default: Select } = SelectInput;
+
+interface Props {
+ isSelected?: boolean;
+ label: string;
+ description?: string;
+}
+const Indicator: FC<Props> = ({ isSelected }) => isSelected ? <Text color="#3894FF">[ </Text> : <Text> </Text>
+const Item: FC<Props> = ({isSelected = false, label, description }) => (
+ <Box display="flex">
+ <Text color={isSelected ? '#3894FF' : 'white'} dimColor={!isSelected}>{label}</Text>
+ {isSelected && description && typeof description === 'string' && <Text> {description}</Text>}
+ {isSelected && description && typeof description !== 'string' && <Box marginLeft={1}>{description}</Box>}
+ </Box>
+);
+
+interface SelectProps {
+ items: { value: string|number|boolean, label: string, description?: any }[]
+ onSelect(value: string|number|boolean): void;
+}
+const CustomSelect: FC<SelectProps> = ({ items, onSelect }) => {
+ const handleSelect = ({ value }) => onSelect(value);
+ return (
+ <Select indicatorComponent={Indicator} itemComponent={Item} items={items} onSelect={handleSelect} />
+ )
+}
+
+export default CustomSelect;
diff --git a/packages/create-astro/src/components/Spacer.tsx b/packages/create-astro/src/components/Spacer.tsx
new file mode 100644
index 000000000..1e4e14561
--- /dev/null
+++ b/packages/create-astro/src/components/Spacer.tsx
@@ -0,0 +1,5 @@
+import React, { FC } from 'react';
+import { Box } from 'ink';
+
+const Spacer: FC<{ width?: number }> = ({ width = 8 }) => <Box width={width} />
+export default Spacer;
diff --git a/packages/create-astro/src/components/Spinner.tsx b/packages/create-astro/src/components/Spinner.tsx
new file mode 100644
index 000000000..2d3335e1c
--- /dev/null
+++ b/packages/create-astro/src/components/Spinner.tsx
@@ -0,0 +1,200 @@
+import React, { FC, useEffect, useState } from 'react';
+import { Box, Text } from 'ink';
+
+const Spinner: FC<{ type?: keyof typeof spinners }> = ({ type = 'countdown' }) => {
+ const { interval, frames } = spinners[type];
+ const [i, setI] = useState(0);
+ useEffect(() => {
+ const _ = setInterval(() => {
+ setI(v => (v < frames.length - 1) ? v + 1 : 0)
+ }, interval)
+
+ return () => clearInterval(_);
+ }, [])
+
+ return frames[i]
+}
+
+const spinners = {
+ countdown: {
+ interval: 80,
+ frames: [
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ <Text backgroundColor="#17C083">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ <Text backgroundColor="#23B1AF">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ <Text backgroundColor="#2CA5D2">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ <Text backgroundColor="#3894FF">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ <Text backgroundColor="#5076F9">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ <Text backgroundColor="#6858F1">{' '}</Text>
+ </Box>,
+ <Box display="flex">
+ <Text backgroundColor="#882DE7">{' '}</Text>
+ </Box>,
+ ]
+ }
+}
+
+export default Spinner;
diff --git a/packages/create-astro/src/components/Template.tsx b/packages/create-astro/src/components/Template.tsx
new file mode 100644
index 000000000..7fbab035d
--- /dev/null
+++ b/packages/create-astro/src/components/Template.tsx
@@ -0,0 +1,23 @@
+import React, { FC } from 'react';
+import { Box, Text } from 'ink';
+import Spacer from './Spacer';
+import Select from './Select';
+
+const Template: FC<{ context: any, onSubmit: (value: string) => void }> = ({ context: { templates }, onSubmit }) => {
+ const items = templates.map(({ title: label, ...rest }) => ({ ...rest, label }));
+
+ return (
+ <>
+ <Box display="flex">
+ <Text color="#17C083">{'[query]'}</Text>
+ <Text> Which template should be used?</Text>
+ </Box>
+ <Box display="flex">
+ <Spacer width={6} />
+ <Select items={items} onSelect={onSubmit} />
+ </Box>
+ </>
+ );
+};
+
+export default Template;
diff --git a/packages/create-astro/src/components/Version.tsx b/packages/create-astro/src/components/Version.tsx
new file mode 100644
index 000000000..340952dc0
--- /dev/null
+++ b/packages/create-astro/src/components/Version.tsx
@@ -0,0 +1,6 @@
+import React, { FC } from 'react';
+import { Text } from 'ink';
+import pkg from '../../package.json';
+
+const Version: FC = () => <Text color="#17C083">v{pkg.version}</Text>;
+export default Version;