giggles 0.3.9 → 0.3.11

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.
@@ -9,9 +9,9 @@ var defaultTheme = {
9
9
  hintDimColor: "#5C5C5C",
10
10
  hintHighlightColor: "#D4D4D4",
11
11
  hintHighlightDimColor: "#A0A0A0",
12
- indicator: "\u25B8",
13
- checkedIndicator: "\u2713",
14
- uncheckedIndicator: "\u25CB"
12
+ indicator: "\u25B6",
13
+ checkedIndicator: "\u25A3",
14
+ uncheckedIndicator: "\u25FB"
15
15
  };
16
16
  var ThemeContext = createContext(defaultTheme);
17
17
  function ThemeProvider({ theme, children }) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useTheme
3
- } from "./chunk-PVXJL6SX.js";
3
+ } from "./chunk-EVD6YPS3.js";
4
4
 
5
5
  // src/ui/CodeBlock.tsx
6
6
  import Prism from "prismjs";
@@ -16,6 +16,9 @@ var defaultTokenColors = {
16
16
  punctuation: "#ABB2BF",
17
17
  builtin: "#E5C07B",
18
18
  className: "#E5C07B",
19
+ variable: "#E06C75",
20
+ property: "#E06C75",
21
+ regex: "#98C379",
19
22
  inserted: "#98C379",
20
23
  deleted: "#E06C75"
21
24
  };
@@ -72,9 +75,31 @@ function getTokenColor(type, colors) {
72
75
  return colors.builtin;
73
76
  case "class-name":
74
77
  case "maybe-class-name":
78
+ case "namespace":
75
79
  return colors.className;
80
+ case "variable":
81
+ return colors.variable;
82
+ case "property":
83
+ case "string-property":
84
+ return colors.property;
85
+ case "regex":
86
+ case "regex-source":
87
+ case "regex-delimiter":
88
+ case "regex-flags":
89
+ return colors.regex;
90
+ case "url":
91
+ return colors.string;
76
92
  case "attr-name":
77
93
  return colors.function;
94
+ case "entity":
95
+ return colors.builtin;
96
+ case "atrule":
97
+ return colors.keyword;
98
+ case "selector":
99
+ return colors.className;
100
+ case "bold":
101
+ case "italic":
102
+ return colors.keyword;
78
103
  case "inserted-sign":
79
104
  case "inserted":
80
105
  return colors.inserted;
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  import {
19
19
  ThemeProvider,
20
20
  useTheme
21
- } from "./chunk-PVXJL6SX.js";
21
+ } from "./chunk-EVD6YPS3.js";
22
22
 
23
23
  // src/core/GigglesProvider.tsx
24
24
  import { jsx } from "react/jsx-runtime";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  CodeBlock
3
- } from "../chunk-TFYBLNEZ.js";
3
+ } from "../chunk-WJRBHS5J.js";
4
4
  import {
5
5
  useTheme
6
- } from "../chunk-PVXJL6SX.js";
6
+ } from "../chunk-EVD6YPS3.js";
7
7
 
8
8
  // src/ui/Markdown.tsx
9
9
  import Table from "cli-table3";
@@ -144,20 +144,23 @@ type VirtualListProps<T> = VirtualListBase<T> & ({
144
144
  });
145
145
  declare function VirtualList<T>({ items, highlightIndex, scrollOffset: controlledOffset, gap, maxVisible, paginatorStyle, render }: VirtualListProps<T>): react_jsx_runtime.JSX.Element;
146
146
 
147
- type ViewportRenderProps<T> = {
148
- item: T;
149
- index: number;
150
- focused: boolean;
147
+ type ViewportRef = {
148
+ scrollTo: (offset: number) => void;
149
+ scrollBy: (delta: number) => void;
150
+ scrollToTop: () => void;
151
+ scrollToBottom: () => void;
152
+ scrollToItem: (index: number) => void;
153
+ getScrollOffset: () => number;
154
+ getContentHeight: () => number;
155
+ getViewportHeight: () => number;
151
156
  };
152
- type ViewportProps<T> = {
153
- items: T[];
154
- maxVisible: number;
155
- showLineNumbers?: boolean;
156
- gap?: number;
157
- paginatorStyle?: PaginatorStyle;
158
- render?: (props: ViewportRenderProps<T>) => React__default.ReactNode;
157
+ type ViewportProps = {
158
+ children?: React__default.ReactNode;
159
+ height: number;
160
+ keybindings?: boolean;
161
+ footer?: React__default.ReactNode;
159
162
  };
160
- declare function Viewport<T>({ items, maxVisible, showLineNumbers, gap, paginatorStyle, render }: ViewportProps<T>): react_jsx_runtime.JSX.Element;
163
+ declare const Viewport: React__default.ForwardRefExoticComponent<ViewportProps & React__default.RefAttributes<ViewportRef>>;
161
164
 
162
165
  type BorderStyle = 'single' | 'double' | 'round' | 'bold' | 'singleDouble' | 'doubleSingle' | 'classic' | 'arrow';
163
166
  type ModalProps = {
@@ -196,6 +199,9 @@ type TokenColors = {
196
199
  punctuation: string;
197
200
  builtin: string;
198
201
  className: string;
202
+ variable: string;
203
+ property: string;
204
+ regex: string;
199
205
  inserted: string;
200
206
  deleted: string;
201
207
  };
@@ -207,4 +213,64 @@ type CodeBlockProps = {
207
213
  };
208
214
  declare function CodeBlock({ children, language, showBorder, tokenColors }: CodeBlockProps): react_jsx_runtime.JSX.Element;
209
215
 
210
- export { Autocomplete, type AutocompleteRenderProps, Badge, type BadgeProps, type BadgeVariant, CodeBlock, CommandPalette, type CommandPaletteRenderProps, Confirm, Modal, MultiSelect, type MultiSelectRenderProps, Paginator, type PaginatorStyle, Panel, type PanelProps, Select, type SelectOption, type SelectRenderProps, TextInput, type TextInputRenderProps, type TokenColors, Viewport, type ViewportRenderProps, VirtualList, type VirtualListRenderProps };
216
+ type SpinnerDef = {
217
+ frames: string[];
218
+ interval: number;
219
+ };
220
+ declare const spinners: {
221
+ line: {
222
+ frames: string[];
223
+ interval: number;
224
+ };
225
+ dot: {
226
+ frames: string[];
227
+ interval: number;
228
+ };
229
+ miniDot: {
230
+ frames: string[];
231
+ interval: number;
232
+ };
233
+ jump: {
234
+ frames: string[];
235
+ interval: number;
236
+ };
237
+ pulse: {
238
+ frames: string[];
239
+ interval: number;
240
+ };
241
+ points: {
242
+ frames: string[];
243
+ interval: number;
244
+ };
245
+ clock: {
246
+ frames: string[];
247
+ interval: number;
248
+ };
249
+ hearts: {
250
+ frames: string[];
251
+ interval: number;
252
+ };
253
+ moon: {
254
+ frames: string[];
255
+ interval: number;
256
+ };
257
+ meter: {
258
+ frames: string[];
259
+ interval: number;
260
+ };
261
+ hamburger: {
262
+ frames: string[];
263
+ interval: number;
264
+ };
265
+ ellipsis: {
266
+ frames: string[];
267
+ interval: number;
268
+ };
269
+ };
270
+ type SpinnerProps = {
271
+ spinner?: SpinnerDef;
272
+ color?: string;
273
+ };
274
+ declare function Spinner({ spinner, color }: SpinnerProps): react_jsx_runtime.JSX.Element;
275
+
276
+ export { Autocomplete, type AutocompleteRenderProps, Badge, type BadgeProps, type BadgeVariant, CodeBlock, CommandPalette, type CommandPaletteRenderProps, Confirm, Modal, MultiSelect, type MultiSelectRenderProps, Paginator, type PaginatorStyle, Panel, type PanelProps, Select, type SelectOption, type SelectRenderProps, Spinner, type SpinnerDef, TextInput, type TextInputRenderProps, type TokenColors, Viewport, type ViewportRef, VirtualList, type VirtualListRenderProps, spinners };
package/dist/ui/index.js CHANGED
@@ -7,10 +7,10 @@ import {
7
7
  } from "../chunk-CKA5JJ4B.js";
8
8
  import {
9
9
  CodeBlock
10
- } from "../chunk-TFYBLNEZ.js";
10
+ } from "../chunk-WJRBHS5J.js";
11
11
  import {
12
12
  useTheme
13
- } from "../chunk-PVXJL6SX.js";
13
+ } from "../chunk-EVD6YPS3.js";
14
14
 
15
15
  // src/ui/CommandPalette.tsx
16
16
  import { useState } from "react";
@@ -689,53 +689,137 @@ function Autocomplete({
689
689
  }
690
690
 
691
691
  // src/ui/Viewport.tsx
692
- import { useState as useState6 } from "react";
693
- import { Text as Text8 } from "ink";
692
+ import {
693
+ Children,
694
+ forwardRef,
695
+ isValidElement,
696
+ useCallback,
697
+ useImperativeHandle,
698
+ useLayoutEffect,
699
+ useRef as useRef4,
700
+ useState as useState6
701
+ } from "react";
702
+ import { Box as Box7, measureElement } from "ink";
694
703
  import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
695
- function Viewport({ items, maxVisible, showLineNumbers, gap, paginatorStyle, render }) {
704
+ function MeasurableItem({
705
+ children,
706
+ index,
707
+ onMeasure
708
+ }) {
709
+ const ref = useRef4(null);
710
+ useLayoutEffect(() => {
711
+ if (ref.current) {
712
+ const { height } = measureElement(ref.current);
713
+ onMeasure(index, height);
714
+ }
715
+ }, [index, onMeasure, children]);
716
+ return /* @__PURE__ */ jsx8(Box7, { ref, flexShrink: 0, width: "100%", flexDirection: "column", children });
717
+ }
718
+ var Viewport = forwardRef(function Viewport2({ children, height, keybindings: enableKeybindings = true, footer }, ref) {
696
719
  const focus = useFocus();
697
720
  const [scrollOffset, setScrollOffset] = useState6(0);
698
- const maxOffset = Math.max(0, items.length - maxVisible);
699
- const scroll = (delta) => {
700
- setScrollOffset((prev) => Math.max(0, Math.min(maxOffset, prev + delta)));
701
- };
702
- useKeybindings(focus, {
703
- j: () => scroll(1),
704
- k: () => scroll(-1),
705
- down: () => scroll(1),
706
- up: () => scroll(-1),
707
- pagedown: () => scroll(maxVisible),
708
- pageup: () => scroll(-maxVisible),
709
- g: () => setScrollOffset(0),
710
- G: () => setScrollOffset(maxOffset)
721
+ const contentHeightRef = useRef4(0);
722
+ const itemHeightsRef = useRef4({});
723
+ const itemKeysRef = useRef4([]);
724
+ const scrollOffsetRef = useRef4(0);
725
+ scrollOffsetRef.current = scrollOffset;
726
+ const childKeys = [];
727
+ Children.forEach(children, (child, index) => {
728
+ if (!child) return;
729
+ const key = isValidElement(child) ? child.key : null;
730
+ childKeys[index] = key !== null ? key : index;
711
731
  });
712
- const gutterWidth = showLineNumbers ? String(items.length).length + 1 : 0;
713
- return /* @__PURE__ */ jsx8(
714
- VirtualList,
715
- {
716
- items,
717
- scrollOffset,
718
- gap,
719
- maxVisible,
720
- paginatorStyle,
721
- render: ({ item, index }) => {
722
- if (render) {
723
- return render({ item, index, focused: focus.focused });
724
- }
725
- return /* @__PURE__ */ jsxs9(Text8, { dimColor: !focus.focused, children: [
726
- showLineNumbers && /* @__PURE__ */ jsxs9(Text8, { dimColor: true, children: [
727
- String(index + 1).padStart(gutterWidth - 1),
728
- " "
729
- ] }),
730
- String(item)
731
- ] });
732
+ itemKeysRef.current = childKeys;
733
+ const getMaxOffset = useCallback(() => Math.max(0, contentHeightRef.current - height), [height]);
734
+ const clampAndSetOffset = useCallback(
735
+ (offset) => {
736
+ const clamped = Math.max(0, Math.min(offset, getMaxOffset()));
737
+ setScrollOffset(clamped);
738
+ },
739
+ [getMaxOffset]
740
+ );
741
+ const handleItemMeasure = useCallback(
742
+ (index, measuredHeight) => {
743
+ const key = itemKeysRef.current[index] ?? index;
744
+ if (itemHeightsRef.current[key] === measuredHeight) return;
745
+ itemHeightsRef.current[key] = measuredHeight;
746
+ let total = 0;
747
+ for (const k of itemKeysRef.current) {
748
+ total += itemHeightsRef.current[k] ?? 0;
749
+ }
750
+ contentHeightRef.current = total;
751
+ const maxOffset = Math.max(0, total - height);
752
+ if (scrollOffsetRef.current > maxOffset) {
753
+ setScrollOffset(maxOffset);
732
754
  }
755
+ },
756
+ [height]
757
+ );
758
+ const getItemTop = useCallback((index) => {
759
+ let top = 0;
760
+ for (let i = 0; i < index && i < itemKeysRef.current.length; i++) {
761
+ const key = itemKeysRef.current[i] ?? i;
762
+ top += itemHeightsRef.current[key] ?? 0;
733
763
  }
764
+ return top;
765
+ }, []);
766
+ useImperativeHandle(
767
+ ref,
768
+ () => ({
769
+ scrollTo: (offset) => clampAndSetOffset(offset),
770
+ scrollBy: (delta) => clampAndSetOffset(scrollOffsetRef.current + delta),
771
+ scrollToTop: () => setScrollOffset(0),
772
+ scrollToBottom: () => setScrollOffset(getMaxOffset()),
773
+ scrollToItem: (index) => {
774
+ const itemTop = getItemTop(index);
775
+ const key = itemKeysRef.current[index] ?? index;
776
+ const itemHeight = itemHeightsRef.current[key] ?? 0;
777
+ const itemBottom = itemTop + itemHeight;
778
+ const current = scrollOffsetRef.current;
779
+ if (itemTop < current) {
780
+ clampAndSetOffset(itemTop);
781
+ } else if (itemBottom > current + height) {
782
+ clampAndSetOffset(itemBottom - height);
783
+ }
784
+ },
785
+ getScrollOffset: () => scrollOffsetRef.current,
786
+ getContentHeight: () => contentHeightRef.current,
787
+ getViewportHeight: () => height
788
+ }),
789
+ [clampAndSetOffset, getMaxOffset, getItemTop, height]
734
790
  );
735
- }
791
+ useKeybindings(
792
+ focus,
793
+ enableKeybindings ? {
794
+ j: () => clampAndSetOffset(scrollOffsetRef.current + 1),
795
+ k: () => clampAndSetOffset(scrollOffsetRef.current - 1),
796
+ down: () => clampAndSetOffset(scrollOffsetRef.current + 1),
797
+ up: () => clampAndSetOffset(scrollOffsetRef.current - 1),
798
+ pagedown: () => clampAndSetOffset(scrollOffsetRef.current + height),
799
+ pageup: () => clampAndSetOffset(scrollOffsetRef.current - height),
800
+ g: () => setScrollOffset(0),
801
+ G: () => setScrollOffset(getMaxOffset())
802
+ } : {}
803
+ );
804
+ return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
805
+ /* @__PURE__ */ jsx8(Box7, { height, overflow: "hidden", children: /* @__PURE__ */ jsx8(Box7, { marginTop: -scrollOffset, flexDirection: "column", width: "100%", children: Children.map(children, (child, index) => {
806
+ if (!child) return null;
807
+ return /* @__PURE__ */ jsx8(
808
+ MeasurableItem,
809
+ {
810
+ index,
811
+ onMeasure: handleItemMeasure,
812
+ children: child
813
+ },
814
+ isValidElement(child) ? child.key ?? index : index
815
+ );
816
+ }) }) }),
817
+ footer
818
+ ] });
819
+ });
736
820
 
737
821
  // src/ui/Modal.tsx
738
- import { Box as Box7, Text as Text9 } from "ink";
822
+ import { Box as Box8, Text as Text8 } from "ink";
739
823
  import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
740
824
  function ModalInner({ children, onClose, title, borderStyle = "round" }) {
741
825
  const focus = useFocus();
@@ -744,7 +828,7 @@ function ModalInner({ children, onClose, title, borderStyle = "round" }) {
744
828
  escape: onClose
745
829
  });
746
830
  return /* @__PURE__ */ jsxs10(
747
- Box7,
831
+ Box8,
748
832
  {
749
833
  flexDirection: "column",
750
834
  alignSelf: "flex-start",
@@ -752,7 +836,7 @@ function ModalInner({ children, onClose, title, borderStyle = "round" }) {
752
836
  borderColor: theme.borderColor,
753
837
  paddingX: 1,
754
838
  children: [
755
- title != null && /* @__PURE__ */ jsx9(Text9, { bold: true, children: title }),
839
+ title != null && /* @__PURE__ */ jsx9(Text8, { bold: true, children: title }),
756
840
  children
757
841
  ]
758
842
  }
@@ -763,7 +847,7 @@ function Modal({ children, onClose, title, borderStyle }) {
763
847
  }
764
848
 
765
849
  // src/ui/Badge.tsx
766
- import { Text as Text10 } from "ink";
850
+ import { Text as Text9 } from "ink";
767
851
  import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
768
852
  var glyphs = {
769
853
  round: ["\uE0B6", "\uE0B4"],
@@ -776,16 +860,16 @@ function Badge({ children, color, background, variant = "round" }) {
776
860
  const fg = color ?? "#000000";
777
861
  const [left, right] = glyphs[variant];
778
862
  const label = variant === "plain" ? ` ${children} ` : children;
779
- return /* @__PURE__ */ jsxs11(Text10, { children: [
780
- left && /* @__PURE__ */ jsx10(Text10, { color: bg, children: left }),
781
- /* @__PURE__ */ jsx10(Text10, { color: fg, backgroundColor: bg, bold: true, children: label }),
782
- right && /* @__PURE__ */ jsx10(Text10, { color: bg, children: right })
863
+ return /* @__PURE__ */ jsxs11(Text9, { children: [
864
+ left && /* @__PURE__ */ jsx10(Text9, { color: bg, children: left }),
865
+ /* @__PURE__ */ jsx10(Text9, { color: fg, backgroundColor: bg, bold: true, children: label }),
866
+ right && /* @__PURE__ */ jsx10(Text9, { color: bg, children: right })
783
867
  ] });
784
868
  }
785
869
 
786
870
  // src/ui/Panel.tsx
787
871
  import { useState as useState7 } from "react";
788
- import { Box as Box8, Text as Text11, measureElement } from "ink";
872
+ import { Box as Box9, Text as Text10, measureElement as measureElement2 } from "ink";
789
873
  import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
790
874
  function Panel({ children, title, width, borderColor, footer }) {
791
875
  const theme = useTheme();
@@ -795,7 +879,7 @@ function Panel({ children, title, width, borderColor, footer }) {
795
879
  const renderTopBorder = () => {
796
880
  if (effectiveWidth === 0) return null;
797
881
  if (title == null) {
798
- return /* @__PURE__ */ jsxs12(Text11, { color, children: [
882
+ return /* @__PURE__ */ jsxs12(Text10, { color, children: [
799
883
  "\u256D",
800
884
  "\u2500".repeat(Math.max(0, effectiveWidth - 2)),
801
885
  "\u256E"
@@ -805,10 +889,10 @@ function Panel({ children, title, width, borderColor, footer }) {
805
889
  const displayTitle = title.length > maxTitleWidth ? title.slice(0, maxTitleWidth - 1) + "\u2026" : title;
806
890
  const titlePart = `\u256D ${displayTitle} `;
807
891
  const totalDashes = Math.max(1, effectiveWidth - titlePart.length - 1);
808
- return /* @__PURE__ */ jsxs12(Text11, { children: [
809
- /* @__PURE__ */ jsx11(Text11, { color, children: "\u256D " }),
810
- /* @__PURE__ */ jsx11(Text11, { color, bold: true, children: displayTitle }),
811
- /* @__PURE__ */ jsxs12(Text11, { color, children: [
892
+ return /* @__PURE__ */ jsxs12(Text10, { children: [
893
+ /* @__PURE__ */ jsx11(Text10, { color, children: "\u256D " }),
894
+ /* @__PURE__ */ jsx11(Text10, { color, bold: true, children: displayTitle }),
895
+ /* @__PURE__ */ jsxs12(Text10, { color, children: [
812
896
  " ",
813
897
  "\u2500".repeat(totalDashes),
814
898
  "\u256E"
@@ -816,27 +900,58 @@ function Panel({ children, title, width, borderColor, footer }) {
816
900
  ] });
817
901
  };
818
902
  return /* @__PURE__ */ jsxs12(
819
- Box8,
903
+ Box9,
820
904
  {
821
905
  flexDirection: "column",
822
906
  width,
823
907
  flexGrow: width == null ? 1 : void 0,
824
908
  ref: (node) => {
825
909
  if (node) {
826
- const { width: w } = measureElement(node);
910
+ const { width: w } = measureElement2(node);
827
911
  if (w !== measuredWidth) setMeasuredWidth(w);
828
912
  }
829
913
  },
830
914
  children: [
831
915
  renderTopBorder(),
832
- /* @__PURE__ */ jsx11(Box8, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderTop: false, borderColor: color, paddingX: 1, children: footer ? /* @__PURE__ */ jsxs12(Fragment3, { children: [
833
- /* @__PURE__ */ jsx11(Box8, { flexDirection: "column", flexGrow: 1, children }),
916
+ /* @__PURE__ */ jsx11(Box9, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderTop: false, borderColor: color, paddingX: 1, children: footer ? /* @__PURE__ */ jsxs12(Fragment3, { children: [
917
+ /* @__PURE__ */ jsx11(Box9, { flexDirection: "column", flexGrow: 1, children }),
834
918
  footer
835
919
  ] }) : children })
836
920
  ]
837
921
  }
838
922
  );
839
923
  }
924
+
925
+ // src/ui/Spinner.tsx
926
+ import { useEffect as useEffect5, useState as useState8 } from "react";
927
+ import { Text as Text11 } from "ink";
928
+ import { jsx as jsx12 } from "react/jsx-runtime";
929
+ var spinners = {
930
+ line: { frames: ["-", "\\", "|", "/"], interval: 130 },
931
+ dot: { frames: ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"], interval: 130 },
932
+ miniDot: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 80 },
933
+ jump: { frames: ["\u2884", "\u2882", "\u2881", "\u2841", "\u2848", "\u2850", "\u2860"], interval: 100 },
934
+ pulse: { frames: ["\u2588", "\u2593", "\u2592", "\u2591"], interval: 120 },
935
+ points: { frames: ["\u2219\u2219\u2219", "\u25CF\u2219\u2219", "\u2219\u25CF\u2219", "\u2219\u2219\u25CF"], interval: 200 },
936
+ clock: { frames: ["\u{1F55B}", "\u{1F550}", "\u{1F551}", "\u{1F552}", "\u{1F553}", "\u{1F554}", "\u{1F555}", "\u{1F556}", "\u{1F557}", "\u{1F558}", "\u{1F559}", "\u{1F55A}"], interval: 100 },
937
+ hearts: { frames: ["\u2764\uFE0F", "\u{1F9E1}", "\u{1F49B}", "\u{1F49A}", "\u{1F499}", "\u{1F49C}"], interval: 120 },
938
+ moon: { frames: ["\u{1F311}", "\u{1F312}", "\u{1F313}", "\u{1F314}", "\u{1F315}", "\u{1F316}", "\u{1F317}", "\u{1F318}"], interval: 180 },
939
+ meter: { frames: ["\u25B1\u25B1\u25B1", "\u25B0\u25B1\u25B1", "\u25B0\u25B0\u25B1", "\u25B0\u25B0\u25B0", "\u25B0\u25B0\u25B1", "\u25B0\u25B1\u25B1", "\u25B1\u25B1\u25B1"], interval: 100 },
940
+ hamburger: { frames: ["\u2631", "\u2632", "\u2634"], interval: 100 },
941
+ ellipsis: { frames: [". ", ".. ", "...", " "], interval: 300 }
942
+ };
943
+ function Spinner({ spinner = spinners.line, color }) {
944
+ const theme = useTheme();
945
+ const [frame, setFrame] = useState8(0);
946
+ useEffect5(() => {
947
+ setFrame(0);
948
+ const id = setInterval(() => {
949
+ setFrame((f) => (f + 1) % spinner.frames.length);
950
+ }, spinner.interval);
951
+ return () => clearInterval(id);
952
+ }, [spinner]);
953
+ return /* @__PURE__ */ jsx12(Text11, { color: color ?? theme.accentColor, children: spinner.frames[frame] });
954
+ }
840
955
  export {
841
956
  Autocomplete,
842
957
  Badge,
@@ -848,7 +963,9 @@ export {
848
963
  Paginator,
849
964
  Panel,
850
965
  Select,
966
+ Spinner,
851
967
  TextInput,
852
968
  Viewport,
853
- VirtualList
969
+ VirtualList,
970
+ spinners
854
971
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "giggles",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",