giggles 0.3.12 → 0.3.14

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 CHANGED
@@ -4,10 +4,42 @@
4
4
 
5
5
  # giggles
6
6
 
7
- the easiest way to build terminal apps with react.
7
+ giggles is a batteries-included react framework for building terminal apps. built on ink, it handles focus, input routing, screen navigation, and theming out of the box so you can skip the plumbing and build.
8
8
 
9
- ```
9
+ inspired by the [charmbracelet](https://github.com/charmbracelet) ecosystem, it comes with a rich set of UI components, hooks for focus and navigation management, and terminal utilities for things like running shell commands.
10
+
11
+ to get started, run
12
+
13
+ ```bash
10
14
  npx create-giggles-app
11
15
  ```
12
16
 
13
- see [giggles.zzzzion.com](https://giggles.zzzzion.com) for docs.
17
+ see [giggles.zzzzion.com](https://giggles.zzzzion.com) for API documentation and live demos.
18
+
19
+ ## sample ui components
20
+
21
+ for a full list of UI components, see [giggles/ui](https://giggles.zzzzion.com/ui). here are a few samples:
22
+
23
+ ### viewport
24
+
25
+ ![viewport](https://github.com/user-attachments/assets/df6ec2fd-cb15-4359-9e3d-1f41431e7853)
26
+
27
+ ### markdown
28
+
29
+ <img width="594" height="603" alt="image" src="https://github.com/user-attachments/assets/fc5648e4-3791-450c-adea-44b7057db071" />
30
+
31
+ ### code block
32
+
33
+ supports syntax highlighting with [prism](https://prismjs.com/index.html#supported-languages). just import 'prism/language' and you're good to go!
34
+
35
+ <img width="548" height="527" alt="code-block" src="https://github.com/user-attachments/assets/fd9952fa-55ea-4c70-aadb-7a772d791018" />
36
+
37
+ ### multi-select
38
+
39
+ ![multi-select](https://github.com/user-attachments/assets/7acc3b61-7f11-4168-bd1c-ef847a5f266a)
40
+
41
+ ### spinner
42
+
43
+ ported from [charmbracelet/bubbles](https://github.com/charmbracelet/bubbles/blob/master/spinner/spinner.go).
44
+
45
+ ![spinner](https://github.com/user-attachments/assets/4d6af189-8bb5-4539-9f7e-23aeb7487737)
@@ -26,9 +26,12 @@ import { Box } from "ink";
26
26
  import { Fragment, jsx } from "react/jsx-runtime";
27
27
  var _a;
28
28
  var isTTY = typeof process !== "undefined" && ((_a = process.stdout) == null ? void 0 : _a.write);
29
+ function FullScreenBox({ children }) {
30
+ const { rows, columns } = useTerminalSize();
31
+ return /* @__PURE__ */ jsx(Box, { height: rows, width: columns, children });
32
+ }
29
33
  function AlternateScreen({ children, fullScreen = true }) {
30
34
  const [ready, setReady] = useState2(!isTTY);
31
- const { rows, columns } = useTerminalSize();
32
35
  useEffect2(() => {
33
36
  if (!isTTY) return;
34
37
  process.stdout.write("\x1B[?1049h");
@@ -40,7 +43,7 @@ function AlternateScreen({ children, fullScreen = true }) {
40
43
  };
41
44
  }, []);
42
45
  if (!ready) return null;
43
- return fullScreen ? /* @__PURE__ */ jsx(Box, { height: rows, width: columns, children }) : /* @__PURE__ */ jsx(Fragment, { children });
46
+ return fullScreen ? /* @__PURE__ */ jsx(FullScreenBox, { children }) : /* @__PURE__ */ jsx(Fragment, { children });
44
47
  }
45
48
 
46
49
  export {
package/dist/index.js CHANGED
@@ -12,13 +12,13 @@ import {
12
12
  useKeybindingRegistry,
13
13
  useKeybindings
14
14
  } from "./chunk-CKA5JJ4B.js";
15
+ import {
16
+ AlternateScreen
17
+ } from "./chunk-N2MMNJV3.js";
15
18
  import {
16
19
  ThemeProvider,
17
20
  useTheme
18
21
  } from "./chunk-EVD6YPS3.js";
19
- import {
20
- AlternateScreen
21
- } from "./chunk-5BONVNP7.js";
22
22
 
23
23
  // src/core/GigglesProvider.tsx
24
24
  import { jsx } from "react/jsx-runtime";
@@ -1,11 +1,59 @@
1
- import {
2
- useShellOut,
3
- useTerminalFocus
4
- } from "../chunk-4LEJFY5C.js";
5
1
  import {
6
2
  AlternateScreen,
7
3
  useTerminalSize
8
- } from "../chunk-5BONVNP7.js";
4
+ } from "../chunk-N2MMNJV3.js";
5
+
6
+ // src/terminal/hooks/useTerminalFocus.ts
7
+ import { useEffect, useRef } from "react";
8
+ function useTerminalFocus(callback) {
9
+ const callbackRef = useRef(callback);
10
+ callbackRef.current = callback;
11
+ useEffect(() => {
12
+ const handler = (data) => {
13
+ const str = data.toString();
14
+ if (str.includes("\x1B[I")) callbackRef.current(true);
15
+ if (str.includes("\x1B[O")) callbackRef.current(false);
16
+ };
17
+ process.stdin.on("data", handler);
18
+ const timer = setTimeout(() => {
19
+ process.stdout.write("\x1B[?1004h");
20
+ }, 0);
21
+ return () => {
22
+ clearTimeout(timer);
23
+ process.stdout.write("\x1B[?1004l");
24
+ process.stdin.off("data", handler);
25
+ };
26
+ }, []);
27
+ }
28
+
29
+ // src/terminal/hooks/useShellout.ts
30
+ import { execa } from "execa";
31
+ import { useCallback, useState } from "react";
32
+ import { useStdin } from "ink";
33
+ function useShellOut() {
34
+ const [, setRedrawCount] = useState(0);
35
+ const { setRawMode } = useStdin();
36
+ const run = useCallback(
37
+ async (command) => {
38
+ process.stdout.write("\x1B[?1049l");
39
+ setRawMode(false);
40
+ try {
41
+ const result = await execa(command, { stdio: "inherit", shell: true, reject: false });
42
+ return { exitCode: result.exitCode ?? 0 };
43
+ } finally {
44
+ setRawMode(true);
45
+ process.stdout.write("\x1B[?1049h");
46
+ process.stdout.write("\x1B[2J");
47
+ process.stdout.write("\x1B[H");
48
+ setRedrawCount((c) => c + 1);
49
+ }
50
+ },
51
+ [setRawMode]
52
+ );
53
+ return {
54
+ run
55
+ };
56
+ }
9
57
  export {
10
58
  AlternateScreen,
11
59
  useShellOut,
package/dist/ui/index.js CHANGED
@@ -11,10 +11,6 @@ import {
11
11
  import {
12
12
  useTheme
13
13
  } from "../chunk-EVD6YPS3.js";
14
- import "../chunk-4LEJFY5C.js";
15
- import {
16
- useTerminalSize
17
- } from "../chunk-5BONVNP7.js";
18
14
 
19
15
  // src/ui/CommandPalette.tsx
20
16
  import { useState } from "react";
@@ -876,16 +872,10 @@ function Badge({ children, color, background, variant = "round" }) {
876
872
  import { useState as useState7 } from "react";
877
873
  import { Box as Box9, Text as Text10, measureElement as measureElement2 } from "ink";
878
874
  import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
879
- function parsePercentage(value, total) {
880
- const pct = parseFloat(value);
881
- return isNaN(pct) ? 0 : Math.floor(pct / 100 * total);
882
- }
883
875
  function Panel({ children, title, width, borderColor, footer, ...boxProps }) {
884
876
  const theme = useTheme();
885
- const { columns } = useTerminalSize();
886
877
  const color = borderColor ?? theme.borderColor;
887
- const initialWidth = typeof width === "number" ? width : typeof width === "string" && width.endsWith("%") ? parsePercentage(width, columns) : 0;
888
- const [measuredWidth, setMeasuredWidth] = useState7(initialWidth);
878
+ const [measuredWidth, setMeasuredWidth] = useState7(typeof width === "number" ? width : 0);
889
879
  const effectiveWidth = typeof width === "number" ? width : measuredWidth;
890
880
  const renderTopBorder = () => {
891
881
  if (effectiveWidth === 0) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "giggles",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,56 +0,0 @@
1
- // src/terminal/hooks/useTerminalFocus.ts
2
- import { useEffect, useRef } from "react";
3
- function useTerminalFocus(callback) {
4
- const callbackRef = useRef(callback);
5
- callbackRef.current = callback;
6
- useEffect(() => {
7
- const handler = (data) => {
8
- const str = data.toString();
9
- if (str.includes("\x1B[I")) callbackRef.current(true);
10
- if (str.includes("\x1B[O")) callbackRef.current(false);
11
- };
12
- process.stdin.on("data", handler);
13
- const timer = setTimeout(() => {
14
- process.stdout.write("\x1B[?1004h");
15
- }, 0);
16
- return () => {
17
- clearTimeout(timer);
18
- process.stdout.write("\x1B[?1004l");
19
- process.stdin.off("data", handler);
20
- };
21
- }, []);
22
- }
23
-
24
- // src/terminal/hooks/useShellout.ts
25
- import { execa } from "execa";
26
- import { useCallback, useState } from "react";
27
- import { useStdin } from "ink";
28
- function useShellOut() {
29
- const [, setRedrawCount] = useState(0);
30
- const { setRawMode } = useStdin();
31
- const run = useCallback(
32
- async (command) => {
33
- process.stdout.write("\x1B[?1049l");
34
- setRawMode(false);
35
- try {
36
- const result = await execa(command, { stdio: "inherit", shell: true, reject: false });
37
- return { exitCode: result.exitCode ?? 0 };
38
- } finally {
39
- setRawMode(true);
40
- process.stdout.write("\x1B[?1049h");
41
- process.stdout.write("\x1B[2J");
42
- process.stdout.write("\x1B[H");
43
- setRedrawCount((c) => c + 1);
44
- }
45
- },
46
- [setRawMode]
47
- );
48
- return {
49
- run
50
- };
51
- }
52
-
53
- export {
54
- useTerminalFocus,
55
- useShellOut
56
- };