@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.
Files changed (3) hide show
  1. package/README.md +58 -0
  2. package/dist/index.js +43 -46
  3. 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
- {/* Quit shortcut */}
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, border: "round", borderColor: "blackBright", paddingX: 1 }}
204
- focusedStyle={{ border: "round", borderColor: "cyan", color: "white", paddingX: 1 }}
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={{ border: "round", borderColor: "blackBright", paddingX: 2 }}
210
- focusedStyle={{ border: "round", borderColor: "cyan", bg: "cyan", color: "black", paddingX: 2 }}
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
- <Box key={todo.id} style={{ flexDirection: "row", gap: 1, alignItems: "center" }}>
221
- <Checkbox
222
- checked={todo.done}
223
- onChange={() => handleToggle(todo.id)}
224
- label={todo.text}
225
- style={todo.done ? { dim: true, color: "blackBright" } : { color: "white" }}
226
- focusedStyle={{ color: "black", bg: "cyan", bold: true }}
227
- />
228
- <Spacer />
229
- <Button
230
- label=" \xD7 "
231
- onPress={() => handleDelete(todo.id)}
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
- const fullPath = path.join(targetDir, filePath);
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: "inherit"
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(` ${green("\u2713")} ${bold(dirName)} is ready! Run this to get started:`);
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semos-labs/create-glyph",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "description": "Scaffold a new Glyph terminal UI app",
5
5
  "type": "module",
6
6
  "bin": {