@semos-labs/create-glyph 0.1.79 → 0.1.81
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/README.md +58 -0
- package/dist/index.js +43 -46
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/semos-labs/glyph/main/images/Glyph.png" alt="Glyph" width="200">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">create-glyph</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Scaffold a new <a href="https://github.com/semos-labs/glyph">Glyph</a> terminal UI app in seconds</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@semos-labs/create-glyph"><img src="https://img.shields.io/npm/v/@semos-labs/create-glyph?color=crimson&logo=npm" alt="npm version"></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/License-MIT-blue" alt="MIT License">
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# bun
|
|
22
|
+
bun create @semos-labs/glyph my-app
|
|
23
|
+
|
|
24
|
+
# npm
|
|
25
|
+
npm create @semos-labs/glyph my-app
|
|
26
|
+
|
|
27
|
+
# pnpm
|
|
28
|
+
pnpm create @semos-labs/glyph my-app
|
|
29
|
+
|
|
30
|
+
# yarn
|
|
31
|
+
yarn create @semos-labs/glyph my-app
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This will create a `my-app` directory with a ready-to-run Glyph project — a small todo app that demonstrates components, focus management, keybinds, and more.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd my-app && bun dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What you get
|
|
41
|
+
|
|
42
|
+
- **TypeScript** project with `tsconfig.json` pre-configured
|
|
43
|
+
- **Example app** showcasing `Box`, `Text`, `Input`, `Button`, `Checkbox`, `Progress`, `Keybind`, `JumpNav`, and more
|
|
44
|
+
- **Dependencies** installed automatically
|
|
45
|
+
|
|
46
|
+
## What is Glyph?
|
|
47
|
+
|
|
48
|
+
[Glyph](https://github.com/semos-labs/glyph) is a React renderer for the terminal. Build real TUI applications with the same component model you use on the web — flexbox layout (powered by Yoga), focus management, keyboard input, and efficient diff-based rendering.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
npm install @semos-labs/glyph
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
→ **[Full documentation & examples](https://github.com/semos-labs/glyph)**
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -106,11 +106,12 @@ import {
|
|
|
106
106
|
Button,
|
|
107
107
|
Checkbox,
|
|
108
108
|
Keybind,
|
|
109
|
+
JumpNav,
|
|
109
110
|
Progress,
|
|
110
111
|
Spacer,
|
|
111
112
|
useApp,
|
|
112
113
|
} from "@semos-labs/glyph";
|
|
113
|
-
import type { Key, InputHandle } from "@semos-labs/glyph";
|
|
114
|
+
import type { Key, InputHandle, CheckboxHandle } from "@semos-labs/glyph";
|
|
114
115
|
|
|
115
116
|
// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
116
117
|
|
|
@@ -127,6 +128,7 @@ let nextId = 1;
|
|
|
127
128
|
function App() {
|
|
128
129
|
const { exit } = useApp();
|
|
129
130
|
const inputRef = useRef<InputHandle>(null);
|
|
131
|
+
const checkboxRefs = useRef(new Map<number, CheckboxHandle>());
|
|
130
132
|
|
|
131
133
|
const [todos, setTodos] = useState<Todo[]>([
|
|
132
134
|
{ id: nextId++, text: "Learn Glyph basics", done: true },
|
|
@@ -167,7 +169,17 @@ function App() {
|
|
|
167
169
|
setTodos((prev) => prev.filter((t) => t.id !== id));
|
|
168
170
|
}, []);
|
|
169
171
|
|
|
172
|
+
const handleDeleteFocused = useCallback(() => {
|
|
173
|
+
for (const [id, handle] of checkboxRefs.current) {
|
|
174
|
+
if (handle.isFocused) {
|
|
175
|
+
handleDelete(id);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}, [handleDelete]);
|
|
180
|
+
|
|
170
181
|
return (
|
|
182
|
+
<JumpNav>
|
|
171
183
|
<Box
|
|
172
184
|
style={{
|
|
173
185
|
flexDirection: "column",
|
|
@@ -177,8 +189,9 @@ function App() {
|
|
|
177
189
|
gap: 1,
|
|
178
190
|
}}
|
|
179
191
|
>
|
|
180
|
-
{/*
|
|
192
|
+
{/* Shortcuts */}
|
|
181
193
|
<Keybind keypress="q" onPress={() => exit()} />
|
|
194
|
+
<Keybind keypress="ctrl+d" onPress={handleDeleteFocused} />
|
|
182
195
|
|
|
183
196
|
{/* Header */}
|
|
184
197
|
<Box style={{ flexDirection: "row", alignItems: "center" }}>
|
|
@@ -200,14 +213,14 @@ function App() {
|
|
|
200
213
|
onChange={setNewTodo}
|
|
201
214
|
onKeyPress={handleInputKey}
|
|
202
215
|
placeholder="What needs to be done?"
|
|
203
|
-
style={{ flexGrow: 1,
|
|
204
|
-
focusedStyle={{
|
|
216
|
+
style={{ flexGrow: 1, paddingX: 1, color: "white" }}
|
|
217
|
+
focusedStyle={{ paddingX: 1, color: "white", bg: "blackBright" }}
|
|
205
218
|
/>
|
|
206
219
|
<Button
|
|
207
220
|
label=" add "
|
|
208
221
|
onPress={handleAdd}
|
|
209
|
-
style={{
|
|
210
|
-
focusedStyle={{
|
|
222
|
+
style={{ color: "blackBright" }}
|
|
223
|
+
focusedStyle={{ bg: "cyan", color: "black" }}
|
|
211
224
|
/>
|
|
212
225
|
</Box>
|
|
213
226
|
|
|
@@ -217,31 +230,28 @@ function App() {
|
|
|
217
230
|
<Text style={{ dim: true, italic: true }}>No todos yet. Add one above!</Text>
|
|
218
231
|
)}
|
|
219
232
|
{todos.map((todo) => (
|
|
220
|
-
<
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
style={{ color: "blackBright" }}
|
|
233
|
-
focusedStyle={{ color: "black", bg: "red", bold: true }}
|
|
234
|
-
/>
|
|
235
|
-
</Box>
|
|
233
|
+
<Checkbox
|
|
234
|
+
key={todo.id}
|
|
235
|
+
ref={(h) => {
|
|
236
|
+
if (h) checkboxRefs.current.set(todo.id, h);
|
|
237
|
+
else checkboxRefs.current.delete(todo.id);
|
|
238
|
+
}}
|
|
239
|
+
checked={todo.done}
|
|
240
|
+
onChange={() => handleToggle(todo.id)}
|
|
241
|
+
label={todo.text}
|
|
242
|
+
style={todo.done ? { dim: true, color: "blackBright" } : { color: "white" }}
|
|
243
|
+
focusedStyle={{ color: "black", bg: "cyan", bold: true }}
|
|
244
|
+
/>
|
|
236
245
|
))}
|
|
237
246
|
</Box>
|
|
238
247
|
|
|
239
248
|
{/* Footer */}
|
|
240
249
|
<Spacer />
|
|
241
250
|
<Text style={{ dim: true }}>
|
|
242
|
-
tab navigate \xB7 space toggle \xB7 enter add \xB7 q quit
|
|
251
|
+
tab navigate \xB7 ctrl+o jump \xB7 space toggle \xB7 enter add \xB7 ctrl+d delete \xB7 q quit
|
|
243
252
|
</Text>
|
|
244
253
|
</Box>
|
|
254
|
+
</JumpNav>
|
|
245
255
|
);
|
|
246
256
|
}
|
|
247
257
|
|
|
@@ -301,15 +311,11 @@ function main() {
|
|
|
301
311
|
const dirName = path.basename(targetDir);
|
|
302
312
|
const pm = detectPackageManager();
|
|
303
313
|
console.log();
|
|
304
|
-
console.log(` ${cyan("\u25C6")} ${bold("Creating")} ${green(dirName)}${dim("...")}`);
|
|
305
314
|
if (fs.existsSync(targetDir)) {
|
|
306
315
|
const entries = fs.readdirSync(targetDir);
|
|
307
316
|
if (entries.length > 0) {
|
|
308
|
-
console.log(
|
|
309
|
-
|
|
310
|
-
${red("\u2717")} Directory ${bold(dirName)} already exists and is not empty.
|
|
311
|
-
`
|
|
312
|
-
);
|
|
317
|
+
console.log(` ${red("\u2717")} Directory ${bold(dirName)} already exists and is not empty.
|
|
318
|
+
`);
|
|
313
319
|
process.exit(1);
|
|
314
320
|
}
|
|
315
321
|
}
|
|
@@ -321,31 +327,22 @@ function main() {
|
|
|
321
327
|
[".gitignore", templateGitignore()]
|
|
322
328
|
];
|
|
323
329
|
for (const [filePath, content] of files) {
|
|
324
|
-
|
|
325
|
-
fs.writeFileSync(fullPath, content, "utf-8");
|
|
326
|
-
console.log(` ${dim("\u251C")} ${green("+")} ${filePath}`);
|
|
330
|
+
fs.writeFileSync(path.join(targetDir, filePath), content, "utf-8");
|
|
327
331
|
}
|
|
328
|
-
console.log();
|
|
329
|
-
console.log(` ${cyan("\u25C6")} ${bold("Installing dependencies")}${dim("...")}`);
|
|
330
|
-
console.log();
|
|
332
|
+
console.log(` ${green("\u2713")} Created project structure`);
|
|
331
333
|
try {
|
|
332
334
|
execSync(getInstallCommand(pm), {
|
|
333
335
|
cwd: targetDir,
|
|
334
|
-
stdio: "
|
|
336
|
+
stdio: "pipe"
|
|
335
337
|
});
|
|
336
|
-
console.log();
|
|
337
|
-
console.log(` ${green("\u2713")} Dependencies installed!`);
|
|
338
|
+
console.log(` ${green("\u2713")} Installed dependencies`);
|
|
338
339
|
} catch {
|
|
339
|
-
console.log();
|
|
340
|
-
console.log(` ${yellow("\u26A0")} Install failed. You can run it manually:`);
|
|
341
|
-
console.log(` ${cyan("cd")} ${dirName} && ${cyan(getInstallCommand(pm))}`);
|
|
340
|
+
console.log(` ${yellow("\u26A0")} Could not install dependencies \u2014 run ${cyan(getInstallCommand(pm))} manually`);
|
|
342
341
|
}
|
|
342
|
+
console.log(` ${green("\u2713")} Ready to build something great`);
|
|
343
343
|
console.log();
|
|
344
|
-
console.log(` ${
|
|
345
|
-
console.log();
|
|
346
|
-
console.log(` ${bold(cyan(`cd ${dirName} && ${getRunCommand(pm)}`))}`);
|
|
347
|
-
console.log();
|
|
348
|
-
console.log(` ${dim("Happy hacking! \u2726")}`);
|
|
344
|
+
console.log(` ${dim("Next:")}`);
|
|
345
|
+
console.log(` ${bold(cyan(`cd ${dirName} && ${getRunCommand(pm)}`))}`);
|
|
349
346
|
console.log();
|
|
350
347
|
}
|
|
351
348
|
main();
|