giggles 0.3.7 → 0.3.9

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.
@@ -119,7 +119,13 @@ var FocusProvider = ({ children }) => {
119
119
  const nodes = nodesRef.current;
120
120
  const parent = nodes.get(parentId);
121
121
  if (parent && parent.childrenIds.length > 0) {
122
- focusNode(parent.childrenIds[0]);
122
+ let target = parent.childrenIds[0];
123
+ let targetNode = nodes.get(target);
124
+ while (targetNode && targetNode.childrenIds.length > 0) {
125
+ target = targetNode.childrenIds[0];
126
+ targetNode = nodes.get(target);
127
+ }
128
+ focusNode(target);
123
129
  } else {
124
130
  pendingFocusFirstChildRef.current.add(parentId);
125
131
  }
@@ -196,16 +202,34 @@ var FocusProvider = ({ children }) => {
196
202
  return pathArray;
197
203
  }, [focusedId]);
198
204
  const navigateSibling = useCallback2(
199
- (direction, wrap = true) => {
200
- const currentId = focusedId;
201
- if (!currentId) return;
205
+ (direction, wrap = true, groupId) => {
206
+ if (!focusedId) return;
202
207
  const nodes = nodesRef.current;
203
- const currentNode = nodes.get(currentId);
204
- if (!(currentNode == null ? void 0 : currentNode.parentId)) return;
205
- const parent = nodes.get(currentNode.parentId);
206
- if (!parent || parent.childrenIds.length === 0) return;
207
- const siblings = parent.childrenIds;
208
- const currentIndex = siblings.indexOf(currentId);
208
+ let currentChildId;
209
+ let siblings;
210
+ if (groupId) {
211
+ const group = nodes.get(groupId);
212
+ if (!group || group.childrenIds.length === 0) return;
213
+ siblings = group.childrenIds;
214
+ let cursor = focusedId;
215
+ while (cursor) {
216
+ const node = nodes.get(cursor);
217
+ if ((node == null ? void 0 : node.parentId) === groupId) {
218
+ currentChildId = cursor;
219
+ break;
220
+ }
221
+ cursor = (node == null ? void 0 : node.parentId) ?? null;
222
+ }
223
+ if (!currentChildId) return;
224
+ } else {
225
+ const currentNode = nodes.get(focusedId);
226
+ if (!(currentNode == null ? void 0 : currentNode.parentId)) return;
227
+ const parent = nodes.get(currentNode.parentId);
228
+ if (!parent || parent.childrenIds.length === 0) return;
229
+ siblings = parent.childrenIds;
230
+ currentChildId = focusedId;
231
+ }
232
+ const currentIndex = siblings.indexOf(currentChildId);
209
233
  if (currentIndex === -1) return;
210
234
  let nextIndex;
211
235
  if (wrap) {
@@ -213,9 +237,15 @@ var FocusProvider = ({ children }) => {
213
237
  } else {
214
238
  nextIndex = direction === "next" ? Math.min(currentIndex + 1, siblings.length - 1) : Math.max(currentIndex - 1, 0);
215
239
  }
216
- focusNode(siblings[nextIndex]);
240
+ const targetId = siblings[nextIndex];
241
+ const target = nodes.get(targetId);
242
+ if (target && target.childrenIds.length > 0) {
243
+ focusFirstChild(targetId);
244
+ } else {
245
+ focusNode(targetId);
246
+ }
217
247
  },
218
- [focusedId, focusNode]
248
+ [focusedId, focusNode, focusFirstChild]
219
249
  );
220
250
  return /* @__PURE__ */ jsx2(
221
251
  FocusContext.Provider,
@@ -311,18 +341,18 @@ function InputRouter({ children }) {
311
341
  if (!keyName) return;
312
342
  for (const nodeId of path) {
313
343
  const nodeBindings = getNodeBindings(nodeId);
314
- if (!nodeBindings) continue;
315
- const entry = nodeBindings.bindings.get(keyName);
316
- if (entry && entry.when !== "mounted") {
317
- entry.handler(input, key);
318
- return;
319
- }
320
- if (nodeBindings.capture && nodeBindings.onKeypress) {
321
- if ((_a = nodeBindings.passthrough) == null ? void 0 : _a.has(keyName)) {
322
- continue;
344
+ if (nodeBindings) {
345
+ const entry = nodeBindings.bindings.get(keyName);
346
+ if (entry && entry.when !== "mounted") {
347
+ entry.handler(input, key);
348
+ return;
349
+ }
350
+ if (nodeBindings.capture && nodeBindings.onKeypress) {
351
+ if (!((_a = nodeBindings.passthrough) == null ? void 0 : _a.has(keyName))) {
352
+ nodeBindings.onKeypress(input, key);
353
+ return;
354
+ }
323
355
  }
324
- nodeBindings.onKeypress(input, key);
325
- return;
326
356
  }
327
357
  if (nodeId === trapNodeId) {
328
358
  return;
@@ -401,8 +431,8 @@ function FocusGroup({
401
431
  const bindContextValue = useMemo(() => value ? { register, unregister } : null, [value, register, unregister]);
402
432
  const navigationKeys = useMemo(() => {
403
433
  if (!navigable) return {};
404
- const next = () => navigateSibling("next", wrap);
405
- const prev = () => navigateSibling("prev", wrap);
434
+ const next = () => navigateSibling("next", wrap, focus.id);
435
+ const prev = () => navigateSibling("prev", wrap, focus.id);
406
436
  const base = direction === "vertical" ? {
407
437
  j: next,
408
438
  k: prev,
@@ -415,7 +445,7 @@ function FocusGroup({
415
445
  left: prev
416
446
  };
417
447
  return { ...base, tab: next, "shift+tab": prev };
418
- }, [navigable, direction, wrap, navigateSibling]);
448
+ }, [navigable, direction, wrap, navigateSibling, focus.id]);
419
449
  const mergedBindings = useMemo(
420
450
  () => ({ ...navigationKeys, ...customBindings }),
421
451
  [navigationKeys, customBindings]
@@ -452,31 +482,6 @@ function FocusTrap({ children }) {
452
482
  return /* @__PURE__ */ jsx5(FocusNodeContext.Provider, { value: id, children });
453
483
  }
454
484
 
455
- // src/core/theme.tsx
456
- import { createContext as createContext4, useContext as useContext4 } from "react";
457
- import { jsx as jsx6 } from "react/jsx-runtime";
458
- var defaultTheme = {
459
- accentColor: "#6B9FD4",
460
- borderColor: "#5C5C5C",
461
- selectedColor: "#8FBF7F",
462
- hintColor: "#8A8A8A",
463
- hintDimColor: "#5C5C5C",
464
- hintHighlightColor: "#D4D4D4",
465
- hintHighlightDimColor: "#A0A0A0",
466
- indicator: "\u25B8",
467
- checkedIndicator: "\u2713",
468
- uncheckedIndicator: "\u25CB"
469
- };
470
- var ThemeContext = createContext4(defaultTheme);
471
- function ThemeProvider({ theme, children }) {
472
- const parent = useContext4(ThemeContext);
473
- const merged = { ...parent, ...theme };
474
- return /* @__PURE__ */ jsx6(ThemeContext.Provider, { value: merged, children });
475
- }
476
- function useTheme() {
477
- return useContext4(ThemeContext);
478
- }
479
-
480
485
  export {
481
486
  GigglesError,
482
487
  FocusProvider,
@@ -489,7 +494,5 @@ export {
489
494
  FocusTrap,
490
495
  useFocus,
491
496
  FocusGroup,
492
- useFocusState,
493
- ThemeProvider,
494
- useTheme
497
+ useFocusState
495
498
  };
@@ -0,0 +1,29 @@
1
+ // src/core/theme.tsx
2
+ import { createContext, useContext } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var defaultTheme = {
5
+ accentColor: "#6B9FD4",
6
+ borderColor: "#5C5C5C",
7
+ selectedColor: "#8FBF7F",
8
+ hintColor: "#8A8A8A",
9
+ hintDimColor: "#5C5C5C",
10
+ hintHighlightColor: "#D4D4D4",
11
+ hintHighlightDimColor: "#A0A0A0",
12
+ indicator: "\u25B8",
13
+ checkedIndicator: "\u2713",
14
+ uncheckedIndicator: "\u25CB"
15
+ };
16
+ var ThemeContext = createContext(defaultTheme);
17
+ function ThemeProvider({ theme, children }) {
18
+ const parent = useContext(ThemeContext);
19
+ const merged = { ...parent, ...theme };
20
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: merged, children });
21
+ }
22
+ function useTheme() {
23
+ return useContext(ThemeContext);
24
+ }
25
+
26
+ export {
27
+ ThemeProvider,
28
+ useTheme
29
+ };
@@ -0,0 +1,93 @@
1
+ import {
2
+ useTheme
3
+ } from "./chunk-PVXJL6SX.js";
4
+
5
+ // src/ui/CodeBlock.tsx
6
+ import Prism from "prismjs";
7
+ import { Box, Text } from "ink";
8
+ import { jsx } from "react/jsx-runtime";
9
+ var defaultTokenColors = {
10
+ keyword: "#C678DD",
11
+ string: "#98C379",
12
+ number: "#D19A66",
13
+ comment: "#5C6370",
14
+ function: "#61AFEF",
15
+ operator: "#56B6C2",
16
+ punctuation: "#ABB2BF",
17
+ builtin: "#E5C07B",
18
+ className: "#E5C07B",
19
+ inserted: "#98C379",
20
+ deleted: "#E06C75"
21
+ };
22
+ function CodeBlock({ children, language, showBorder = true, tokenColors }) {
23
+ const theme = useTheme();
24
+ const colors = { ...defaultTokenColors, ...tokenColors };
25
+ const grammar = language ? Prism.languages[language] : void 0;
26
+ const content = grammar ? renderTokens(Prism.tokenize(children, grammar), colors) : /* @__PURE__ */ jsx(Text, { children });
27
+ if (!showBorder) {
28
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { children: content }) });
29
+ }
30
+ return /* @__PURE__ */ jsx(Box, { paddingX: 1, borderStyle: "single", borderColor: theme.borderColor, children: /* @__PURE__ */ jsx(Text, { children: content }) });
31
+ }
32
+ function renderTokens(tokens, colors) {
33
+ return tokens.map((token, idx) => {
34
+ if (typeof token === "string") {
35
+ return /* @__PURE__ */ jsx(Text, { children: token }, idx);
36
+ }
37
+ const color = getTokenColor(token.type, colors);
38
+ const content = Array.isArray(token.content) ? renderTokens(token.content, colors) : typeof token.content === "string" ? token.content : renderTokens([token.content], colors);
39
+ return color ? /* @__PURE__ */ jsx(Text, { color, children: content }, idx) : /* @__PURE__ */ jsx(Text, { children: content }, idx);
40
+ });
41
+ }
42
+ function getTokenColor(type, colors) {
43
+ switch (type) {
44
+ case "keyword":
45
+ case "tag":
46
+ case "boolean":
47
+ case "important":
48
+ return colors.keyword;
49
+ case "string":
50
+ case "char":
51
+ case "template-string":
52
+ case "attr-value":
53
+ return colors.string;
54
+ case "number":
55
+ return colors.number;
56
+ case "comment":
57
+ case "prolog":
58
+ case "doctype":
59
+ case "cdata":
60
+ return colors.comment;
61
+ case "function":
62
+ case "function-variable":
63
+ return colors.function;
64
+ case "operator":
65
+ case "arrow":
66
+ return colors.operator;
67
+ case "punctuation":
68
+ return colors.punctuation;
69
+ case "builtin":
70
+ case "constant":
71
+ case "symbol":
72
+ return colors.builtin;
73
+ case "class-name":
74
+ case "maybe-class-name":
75
+ return colors.className;
76
+ case "attr-name":
77
+ return colors.function;
78
+ case "inserted-sign":
79
+ case "inserted":
80
+ return colors.inserted;
81
+ case "deleted-sign":
82
+ case "deleted":
83
+ return colors.deleted;
84
+ case "unchanged":
85
+ return colors.comment;
86
+ default:
87
+ return void 0;
88
+ }
89
+ }
90
+
91
+ export {
92
+ CodeBlock
93
+ };
package/dist/index.js CHANGED
@@ -9,14 +9,16 @@ import {
9
9
  GigglesError,
10
10
  InputProvider,
11
11
  InputRouter,
12
- ThemeProvider,
13
12
  useFocus,
14
13
  useFocusContext,
15
14
  useFocusState,
16
15
  useKeybindingRegistry,
17
- useKeybindings,
16
+ useKeybindings
17
+ } from "./chunk-CKA5JJ4B.js";
18
+ import {
19
+ ThemeProvider,
18
20
  useTheme
19
- } from "./chunk-X5PYVHXM.js";
21
+ } from "./chunk-PVXJL6SX.js";
20
22
 
21
23
  // src/core/GigglesProvider.tsx
22
24
  import { jsx } from "react/jsx-runtime";
@@ -0,0 +1,8 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type MarkdownProps = {
4
+ children: string;
5
+ };
6
+ declare function Markdown({ children }: MarkdownProps): react_jsx_runtime.JSX.Element;
7
+
8
+ export { Markdown };
@@ -0,0 +1,122 @@
1
+ import {
2
+ CodeBlock
3
+ } from "../chunk-TFYBLNEZ.js";
4
+ import {
5
+ useTheme
6
+ } from "../chunk-PVXJL6SX.js";
7
+
8
+ // src/ui/Markdown.tsx
9
+ import Table from "cli-table3";
10
+ import { marked } from "marked";
11
+ import { useMemo } from "react";
12
+ import { Box, Text } from "ink";
13
+ import Link from "ink-link";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ function Markdown({ children }) {
16
+ const tokens = useMemo(() => marked.lexer(children), [children]);
17
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: tokens.map((token, idx) => /* @__PURE__ */ jsx(TokenRenderer, { token }, idx)) });
18
+ }
19
+ function TokenRenderer({ token }) {
20
+ var _a, _b;
21
+ const theme = useTheme();
22
+ switch (token.type) {
23
+ case "heading":
24
+ return /* @__PURE__ */ jsx(Box, { marginTop: token.depth === 1 ? 0 : 1, children: /* @__PURE__ */ jsx(Text, { bold: true, underline: token.depth === 1, children: renderInline(token.tokens, theme) }) });
25
+ case "paragraph": {
26
+ const hasLinks = (_a = token.tokens) == null ? void 0 : _a.some(
27
+ (t) => {
28
+ var _a2;
29
+ return t.type === "link" || t.type === "strong" && "tokens" in t && ((_a2 = t.tokens) == null ? void 0 : _a2.some((st) => st.type === "link"));
30
+ }
31
+ );
32
+ if (hasLinks) {
33
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: renderInline(token.tokens, theme) });
34
+ }
35
+ return /* @__PURE__ */ jsx(Text, { children: renderInline(token.tokens, theme) });
36
+ }
37
+ case "code":
38
+ return /* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsx(CodeBlock, { language: token.lang || void 0, children: token.text }) });
39
+ case "blockquote":
40
+ return /* @__PURE__ */ jsxs(Box, { marginY: 1, children: [
41
+ /* @__PURE__ */ jsx(Text, { color: theme.borderColor, children: "\u2502 " }),
42
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: (_b = token.tokens) == null ? void 0 : _b.map((t, idx) => /* @__PURE__ */ jsx(TokenRenderer, { token: t }, idx)) })
43
+ ] });
44
+ case "list":
45
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: token.items.map((item, idx) => /* @__PURE__ */ jsxs(Box, { children: [
46
+ /* @__PURE__ */ jsx(Text, { children: token.ordered ? `${idx + 1}. ` : "\u2022 " }),
47
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: item.tokens.map((t, i) => /* @__PURE__ */ jsx(TokenRenderer, { token: t }, i)) })
48
+ ] }, idx)) });
49
+ case "table":
50
+ return /* @__PURE__ */ jsx(TableRenderer, { token });
51
+ case "hr":
52
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(40) });
53
+ case "space":
54
+ return null;
55
+ default:
56
+ if ("text" in token && typeof token.text === "string") {
57
+ return /* @__PURE__ */ jsx(Text, { children: token.text });
58
+ }
59
+ return null;
60
+ }
61
+ }
62
+ function TableRenderer({ token }) {
63
+ const table = new Table({
64
+ head: token.header.map((cell) => renderInlineToString(cell.tokens)),
65
+ style: { head: [], border: [] }
66
+ });
67
+ for (const row of token.rows) {
68
+ table.push(row.map((cell) => renderInlineToString(cell.tokens)));
69
+ }
70
+ return /* @__PURE__ */ jsx(Text, { children: table.toString() });
71
+ }
72
+ function renderInline(tokens, theme) {
73
+ if (!tokens) return null;
74
+ return tokens.map((token, idx) => {
75
+ switch (token.type) {
76
+ case "text":
77
+ return /* @__PURE__ */ jsx(Text, { children: token.text }, idx);
78
+ case "strong":
79
+ return /* @__PURE__ */ jsx(Text, { bold: true, children: renderInline(token.tokens, theme) }, idx);
80
+ case "em":
81
+ return /* @__PURE__ */ jsx(Text, { italic: true, children: renderInline(token.tokens, theme) }, idx);
82
+ case "codespan":
83
+ return /* @__PURE__ */ jsxs(Text, { color: theme.accentColor, children: [
84
+ "`",
85
+ token.text,
86
+ "`"
87
+ ] }, idx);
88
+ case "link":
89
+ return /* @__PURE__ */ jsx(Link, { url: token.href, children: /* @__PURE__ */ jsx(Text, { color: theme.accentColor, children: renderInlineToString(token.tokens) }) }, idx);
90
+ case "image":
91
+ return /* @__PURE__ */ jsxs(Text, { color: theme.accentColor, children: [
92
+ "[Image: ",
93
+ token.text || token.href,
94
+ "]"
95
+ ] }, idx);
96
+ case "br":
97
+ return /* @__PURE__ */ jsx(Text, { children: "\n" }, idx);
98
+ case "del":
99
+ return /* @__PURE__ */ jsx(Text, { strikethrough: true, children: renderInline(token.tokens, theme) }, idx);
100
+ default:
101
+ if ("text" in token && typeof token.text === "string") {
102
+ return /* @__PURE__ */ jsx(Text, { children: token.text }, idx);
103
+ }
104
+ return null;
105
+ }
106
+ });
107
+ }
108
+ function renderInlineToString(tokens) {
109
+ if (!tokens) return "";
110
+ return tokens.map((token) => {
111
+ if ("text" in token && typeof token.text === "string") {
112
+ return token.text;
113
+ }
114
+ if ("tokens" in token && Array.isArray(token.tokens)) {
115
+ return renderInlineToString(token.tokens);
116
+ }
117
+ return "";
118
+ }).join("");
119
+ }
120
+ export {
121
+ Markdown
122
+ };
@@ -168,4 +168,43 @@ type ModalProps = {
168
168
  };
169
169
  declare function Modal({ children, onClose, title, borderStyle }: ModalProps): react_jsx_runtime.JSX.Element;
170
170
 
171
- export { Autocomplete, type AutocompleteRenderProps, CommandPalette, type CommandPaletteRenderProps, Confirm, Modal, MultiSelect, type MultiSelectRenderProps, Paginator, type PaginatorStyle, Select, type SelectOption, type SelectRenderProps, TextInput, type TextInputRenderProps, Viewport, type ViewportRenderProps, VirtualList, type VirtualListRenderProps };
171
+ type BadgeVariant = 'round' | 'arrow' | 'plain';
172
+ type BadgeProps = {
173
+ children: string;
174
+ color?: string;
175
+ background?: string;
176
+ variant?: BadgeVariant;
177
+ };
178
+ declare function Badge({ children, color, background, variant }: BadgeProps): react_jsx_runtime.JSX.Element;
179
+
180
+ type PanelProps = {
181
+ children: React__default.ReactNode;
182
+ title?: string;
183
+ width?: number;
184
+ borderColor?: string;
185
+ footer?: React__default.ReactNode;
186
+ };
187
+ declare function Panel({ children, title, width, borderColor, footer }: PanelProps): react_jsx_runtime.JSX.Element;
188
+
189
+ type TokenColors = {
190
+ keyword: string;
191
+ string: string;
192
+ number: string;
193
+ comment: string;
194
+ function: string;
195
+ operator: string;
196
+ punctuation: string;
197
+ builtin: string;
198
+ className: string;
199
+ inserted: string;
200
+ deleted: string;
201
+ };
202
+ type CodeBlockProps = {
203
+ children: string;
204
+ language?: string;
205
+ showBorder?: boolean;
206
+ tokenColors?: Partial<TokenColors>;
207
+ };
208
+ declare function CodeBlock({ children, language, showBorder, tokenColors }: CodeBlockProps): react_jsx_runtime.JSX.Element;
209
+
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 };
package/dist/ui/index.js CHANGED
@@ -3,9 +3,14 @@ import {
3
3
  GigglesError,
4
4
  useFocus,
5
5
  useKeybindingRegistry,
6
- useKeybindings,
6
+ useKeybindings
7
+ } from "../chunk-CKA5JJ4B.js";
8
+ import {
9
+ CodeBlock
10
+ } from "../chunk-TFYBLNEZ.js";
11
+ import {
7
12
  useTheme
8
- } from "../chunk-X5PYVHXM.js";
13
+ } from "../chunk-PVXJL6SX.js";
9
14
 
10
15
  // src/ui/CommandPalette.tsx
11
16
  import { useState } from "react";
@@ -756,13 +761,92 @@ function ModalInner({ children, onClose, title, borderStyle = "round" }) {
756
761
  function Modal({ children, onClose, title, borderStyle }) {
757
762
  return /* @__PURE__ */ jsx9(FocusTrap, { children: /* @__PURE__ */ jsx9(ModalInner, { onClose, title, borderStyle, children }) });
758
763
  }
764
+
765
+ // src/ui/Badge.tsx
766
+ import { Text as Text10 } from "ink";
767
+ import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
768
+ var glyphs = {
769
+ round: ["\uE0B6", "\uE0B4"],
770
+ arrow: ["\uE0B2", "\uE0B0"],
771
+ plain: ["", ""]
772
+ };
773
+ function Badge({ children, color, background, variant = "round" }) {
774
+ const theme = useTheme();
775
+ const bg = background ?? theme.accentColor;
776
+ const fg = color ?? "#000000";
777
+ const [left, right] = glyphs[variant];
778
+ 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 })
783
+ ] });
784
+ }
785
+
786
+ // src/ui/Panel.tsx
787
+ import { useState as useState7 } from "react";
788
+ import { Box as Box8, Text as Text11, measureElement } from "ink";
789
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
790
+ function Panel({ children, title, width, borderColor, footer }) {
791
+ const theme = useTheme();
792
+ const color = borderColor ?? theme.borderColor;
793
+ const [measuredWidth, setMeasuredWidth] = useState7(width ?? 0);
794
+ const effectiveWidth = width ?? measuredWidth;
795
+ const renderTopBorder = () => {
796
+ if (effectiveWidth === 0) return null;
797
+ if (title == null) {
798
+ return /* @__PURE__ */ jsxs12(Text11, { color, children: [
799
+ "\u256D",
800
+ "\u2500".repeat(Math.max(0, effectiveWidth - 2)),
801
+ "\u256E"
802
+ ] });
803
+ }
804
+ const maxTitleWidth = Math.max(0, effectiveWidth - 5);
805
+ const displayTitle = title.length > maxTitleWidth ? title.slice(0, maxTitleWidth - 1) + "\u2026" : title;
806
+ const titlePart = `\u256D ${displayTitle} `;
807
+ 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: [
812
+ " ",
813
+ "\u2500".repeat(totalDashes),
814
+ "\u256E"
815
+ ] })
816
+ ] });
817
+ };
818
+ return /* @__PURE__ */ jsxs12(
819
+ Box8,
820
+ {
821
+ flexDirection: "column",
822
+ width,
823
+ flexGrow: width == null ? 1 : void 0,
824
+ ref: (node) => {
825
+ if (node) {
826
+ const { width: w } = measureElement(node);
827
+ if (w !== measuredWidth) setMeasuredWidth(w);
828
+ }
829
+ },
830
+ children: [
831
+ 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 }),
834
+ footer
835
+ ] }) : children })
836
+ ]
837
+ }
838
+ );
839
+ }
759
840
  export {
760
841
  Autocomplete,
842
+ Badge,
843
+ CodeBlock,
761
844
  CommandPalette,
762
845
  Confirm,
763
846
  Modal,
764
847
  MultiSelect,
765
848
  Paginator,
849
+ Panel,
766
850
  Select,
767
851
  TextInput,
768
852
  Viewport,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "giggles",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,6 +21,10 @@
21
21
  "./ui": {
22
22
  "import": "./dist/ui/index.js",
23
23
  "types": "./dist/ui/index.d.ts"
24
+ },
25
+ "./markdown": {
26
+ "import": "./dist/markdown/index.js",
27
+ "types": "./dist/markdown/index.d.ts"
24
28
  }
25
29
  },
26
30
  "keywords": [
@@ -37,7 +41,7 @@
37
41
  "build": "tsup",
38
42
  "build:watch": "nodemon --watch src --ext ts,tsx --exec tsup",
39
43
  "dev": "tsx --watch playground/index.tsx",
40
- "dev:docs": "pnpm build && cd documentation && pnpm link ../ && pnpm dev",
44
+ "dev:docs": "pnpm build && pnpm --filter documentation dev",
41
45
  "lint": "prettier --write . && eslint . --fix"
42
46
  },
43
47
  "files": [
@@ -50,6 +54,7 @@
50
54
  "devDependencies": {
51
55
  "@trivago/prettier-plugin-sort-imports": "^4.3.0",
52
56
  "@types/node": "^22.0.0",
57
+ "@types/prismjs": "^1.26.6",
53
58
  "@types/react": "^19.2.13",
54
59
  "eslint": "^9.0.0",
55
60
  "eslint-plugin-react": "^7.32.2",
@@ -64,6 +69,10 @@
64
69
  "typescript-eslint": "^8.0.0"
65
70
  },
66
71
  "dependencies": {
67
- "execa": "^9.6.1"
72
+ "cli-table3": "^0.6.5",
73
+ "execa": "^9.6.1",
74
+ "ink-link": "^5.0.0",
75
+ "marked": "^17.0.3",
76
+ "prismjs": "^1.30.0"
68
77
  }
69
78
  }