@nick-skriabin/glyph 0.1.49 → 0.1.51

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
@@ -40,7 +40,7 @@ Build real terminal applications with React. Glyph provides a full component mod
40
40
  | | |
41
41
  |---|---|
42
42
  | **Flexbox Layout** | Full CSS-like flexbox via Yoga — rows, columns, wrapping, alignment, gaps, padding |
43
- | **Rich Components** | Box, Text, Input, Button, Checkbox, Radio, Select, ScrollView, List, Menu, Progress, Spinner, Toasts, Dialogs, Portal, JumpNav |
43
+ | **Rich Components** | Box, Text, Input, Button, Checkbox, Radio, Select, ScrollView, List, Menu, Progress, Spinner, Image, Toasts, Dialogs, Portal, JumpNav |
44
44
  | **Focus System** | Tab navigation, focus scopes, focus trapping for modals, JumpNav quick-jump hints |
45
45
  | **Keyboard Input** | `useInput` hook, declarative `<Keybind>` component, vim-style bindings |
46
46
  | **Smart Rendering** | Double-buffered framebuffer with character-level diffing &mdash; only changed cells are written |
@@ -455,6 +455,65 @@ Animated spinner with configurable frames. Cleans up timers on unmount.
455
455
  <Spinner frames={["|", "/", "-", "\\"]} intervalMs={100} />
456
456
  ```
457
457
 
458
+ ### `<Image>`
459
+
460
+ Display images in the terminal with inline rendering or OS preview. Supports local files and remote URLs.
461
+
462
+ ```tsx
463
+ <Image
464
+ src="./photo.jpg"
465
+ style={{ width: 40, height: 15 }}
466
+ autoLoad
467
+ />
468
+
469
+ <Image
470
+ src="https://images.unsplash.com/photo-123"
471
+ style={{ flexGrow: 1 }}
472
+ />
473
+ ```
474
+
475
+ **How it works:**
476
+ 1. By default, shows a placeholder with the image name
477
+ 2. Focus the component and press `Space` to load
478
+ 3. Image renders inline (Kitty/iTerm2 protocol) or opens OS preview (Quick Look on macOS)
479
+ 4. Press `Escape` to return to placeholder, `R` to reload
480
+
481
+ **Props:**
482
+
483
+ | Prop | Type | Default | Description |
484
+ |------|------|---------|-------------|
485
+ | `src` | `string` | required | Local path or remote URL |
486
+ | `width` | `number` | auto | Fixed width in cells |
487
+ | `height` | `number` | auto | Fixed height in cells |
488
+ | `style` | `Style` | `{}` | Container style (flexbox) |
489
+ | `focusedStyle` | `Style` | `{}` | Style when focused |
490
+ | `inline` | `boolean` | `true` | Allow inline terminal rendering |
491
+ | `autoLoad` | `boolean` | `false` | Load automatically on mount |
492
+ | `focusable` | `boolean` | `true` | Whether the component is focusable |
493
+ | `placeholder` | `string` | filename | Custom placeholder text |
494
+ | `onStateChange` | `(state) => void` | - | Called when state changes |
495
+ | `onError` | `(error) => void` | - | Called on error |
496
+
497
+ **Terminal support:**
498
+ - **Inline rendering:** Kitty, Ghostty, WezTerm, iTerm2 (via Kitty Graphics or iTerm2 protocol)
499
+ - **OS preview fallback:** Quick Look (macOS), xdg-open (Linux), start (Windows)
500
+
501
+ **Remote images** are automatically downloaded and cached. Supported formats: PNG, JPEG, GIF, WebP.
502
+
503
+ ```tsx
504
+ // Inline disabled - always use OS preview
505
+ <Image src="./large-photo.jpg" inline={false} />
506
+
507
+ // With state callback
508
+ <Image
509
+ src={imageUrl}
510
+ onStateChange={(state) => {
511
+ // "placeholder" | "loading" | "loaded" | "error" | "preview"
512
+ console.log("Image state:", state);
513
+ }}
514
+ />
515
+ ```
516
+
458
517
  ### `<ToastHost>` + `useToast()`
459
518
 
460
519
  Lightweight toast notifications rendered via Portal. Wrap your app in `<ToastHost>`, then push toasts from anywhere with `useToast()`.
@@ -763,6 +822,7 @@ Interactive examples are included in the repo. Each demonstrates different compo
763
822
  | **dialog-demo** | Alert and Confirm dialogs | [View →](https://github.com/nick-skriabin/glyph/tree/main/examples/dialog-demo) |
764
823
  | **jump-nav** | Quick navigation with keyboard hints | [View →](https://github.com/nick-skriabin/glyph/tree/main/examples/jump-nav) |
765
824
  | **ansi-text** | ANSI escape codes and colored output | [View →](https://github.com/nick-skriabin/glyph/tree/main/examples/ansi-text) |
825
+ | **image** | Inline images and OS preview | [View →](https://github.com/nick-skriabin/glyph/tree/main/examples/image) |
766
826
  | **showcase** | Progress bars, Spinners, Toasts | [View →](https://github.com/nick-skriabin/glyph/tree/main/examples/showcase) |
767
827
  | **dashboard** | Full task manager (all components) | [View →](https://github.com/nick-skriabin/glyph/tree/main/examples/dashboard) |
768
828
 
package/dist/index.d.ts CHANGED
@@ -509,6 +509,42 @@ interface JumpNavProps {
509
509
  */
510
510
  declare function JumpNav({ children, activationKey, hintStyle, hintBg, hintFg, hintChars, enabled, debug, }: JumpNavProps): React.JSX.Element;
511
511
 
512
+ /**
513
+ * Image component for terminal UIs
514
+ *
515
+ * Supports inline rendering via terminal protocols (Kitty, iTerm2)
516
+ * and OS-level preview (Quick Look on macOS, xdg-open on Linux)
517
+ */
518
+
519
+ type ImageState = "placeholder" | "loading" | "loaded" | "error" | "preview";
520
+ interface ImageProps {
521
+ /** Image source - local path or remote URL */
522
+ src: string;
523
+ /** Fixed width in cells (optional, uses flexbox if not set) */
524
+ width?: number;
525
+ /** Fixed height in cells (optional, uses flexbox if not set) */
526
+ height?: number;
527
+ /** Container style (flexbox) */
528
+ style?: Style;
529
+ /** Style when focused */
530
+ focusedStyle?: Style;
531
+ /** Style for the placeholder */
532
+ placeholderStyle?: Style;
533
+ /** Whether the component is focusable (default: true) */
534
+ focusable?: boolean;
535
+ /** Allow inline rendering in terminal (default: true) */
536
+ inline?: boolean;
537
+ /** Custom placeholder text (default: image name) */
538
+ placeholder?: string;
539
+ /** Called when image state changes */
540
+ onStateChange?: (state: ImageState) => void;
541
+ /** Called on error */
542
+ onError?: (error: Error) => void;
543
+ /** Auto-load on mount (default: false - user presses space) */
544
+ autoLoad?: boolean;
545
+ }
546
+ declare function Image({ src, width, height, style, focusedStyle, placeholderStyle, focusable, inline, placeholder, onStateChange, onError, autoLoad, }: ImageProps): React.JSX.Element;
547
+
512
548
  declare function useInput(handler: (key: Key) => void, deps?: any[]): void;
513
549
 
514
550
  interface UseFocusResult {
@@ -725,4 +761,22 @@ declare function parseAnsi(input: string): StyledSegment[];
725
761
  */
726
762
  declare function stripAnsi(input: string): string;
727
763
 
728
- export { type AlertOptions, type AnsiStyle, type AppHandle, type BorderStyle, Box, type BoxProps, Button, type ButtonProps, Checkbox, type CheckboxProps, type Color, type ConfirmOptions, type DialogContextValue, DialogHost, type DialogHostProps, type DimensionValue, type FocusRegistryValue, FocusScope, type FocusScopeProps, type FocusableElement, type HexColor, Input, type InputProps, type InputType, JumpNav, type JumpNavProps, type Key, Keybind, type KeybindProps, type LayoutRect, List, type ListItemInfo, type ListProps, type MaskOptions, Menu, type MenuItem, type MenuProps, type NamedColor, Portal, type PortalProps, Progress, type ProgressProps, type RGBColor, Radio, type RadioItem, type RadioProps, type RenderOptions, ScrollView, type ScrollViewProps, Select, type SelectItem, type SelectProps, Spacer, type SpacerProps, Spinner, type SpinnerProps, type Style, type StyledSegment, Text, type TextAlign, type TextProps, type Toast, ToastHost, type ToastHostProps, type ToastPosition, type ToastVariant, type UseFocusableOptions, type UseFocusableResult, type WrapMode, createMask, masks, parseAnsi, render, stripAnsi, useApp, useDialog, useFocus, useFocusRegistry, useFocusable, useInput, useLayout, useToast };
764
+ /**
765
+ * Terminal capability detection for image protocols
766
+ */
767
+ interface TerminalCapabilities {
768
+ name: string;
769
+ supportsKittyGraphics: boolean;
770
+ supportsIterm2Images: boolean;
771
+ supportsSixel: boolean;
772
+ }
773
+ /**
774
+ * Detect which terminal we're running in and what image protocols it supports
775
+ */
776
+ declare function detectTerminalCapabilities(debug?: boolean): TerminalCapabilities;
777
+ /**
778
+ * Check if the terminal supports any inline image protocol
779
+ */
780
+ declare function supportsInlineImages(): boolean;
781
+
782
+ export { type AlertOptions, type AnsiStyle, type AppHandle, type BorderStyle, Box, type BoxProps, Button, type ButtonProps, Checkbox, type CheckboxProps, type Color, type ConfirmOptions, type DialogContextValue, DialogHost, type DialogHostProps, type DimensionValue, type FocusRegistryValue, FocusScope, type FocusScopeProps, type FocusableElement, type HexColor, Image, type ImageProps, type ImageState, Input, type InputProps, type InputType, JumpNav, type JumpNavProps, type Key, Keybind, type KeybindProps, type LayoutRect, List, type ListItemInfo, type ListProps, type MaskOptions, Menu, type MenuItem, type MenuProps, type NamedColor, Portal, type PortalProps, Progress, type ProgressProps, type RGBColor, Radio, type RadioItem, type RadioProps, type RenderOptions, ScrollView, type ScrollViewProps, Select, type SelectItem, type SelectProps, Spacer, type SpacerProps, Spinner, type SpinnerProps, type Style, type StyledSegment, type TerminalCapabilities, Text, type TextAlign, type TextProps, type Toast, ToastHost, type ToastHostProps, type ToastPosition, type ToastVariant, type UseFocusableOptions, type UseFocusableResult, type WrapMode, createMask, detectTerminalCapabilities, masks, parseAnsi, render, stripAnsi, supportsInlineImages, useApp, useDialog, useFocus, useFocusRegistry, useFocusable, useInput, useLayout, useToast };