https://github.com/nimblemarkets/ntemoji
Nimbe Terminal Emoji Golang module
https://github.com/nimblemarkets/ntemoji
Last synced: about 2 months ago
JSON representation
Nimbe Terminal Emoji Golang module
- Host: GitHub
- URL: https://github.com/nimblemarkets/ntemoji
- Owner: NimbleMarkets
- License: mit
- Created: 2026-05-24T21:23:48.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-24T23:50:51.000Z (2 months ago)
- Last Synced: 2026-05-25T01:34:26.061Z (2 months ago)
- Language: Go
- Size: 26.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# ntemoji — Emoji picker and swatch keyboard overlay for Bubble Tea
`ntemoji` is a [Bubble Tea](https://github.com/charmbracelet/bubbletea) widget that provides a keyboard- and mouse-enabled emoji picker overlay keypad for terminal user interfaces (TUIs).
It features curated categories of TUI-safe, highly compatible emojis, support for custom palettes, search/filter capabilities, preset shortcuts, and positioning integration via [`bubble-overlay`](https://github.com/madicen/bubble-overlay) and [`bubblezone`](https://github.com/lrstanley/bubblezone).
[Try out the live WASM demo.](https://nimblemarkets.github.io/ntemoji)
This module was inspired by the [@madicen's `bubble-color-picker` library](https://github.com/madicen/bubble-color-picker).
## Quickstart
```go
package main
import (
"fmt"
"os"
"github.com/NimbleMarkets/ntemoji"
tea "github.com/charmbracelet/bubbletea"
zone "github.com/lrstanley/bubblezone"
)
type model struct {
picker ntemoji.Model
zm *zone.Manager
}
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.String() == "q" || msg.String() == "ctrl+c" {
return m, tea.Quit
}
case ntemoji.EmojiChangedMsg:
fmt.Printf("Selected emoji: %s\n", msg.Emoji)
return m, tea.Quit
case ntemoji.EmojiCanceledMsg:
return m, tea.Quit
}
var cmd tea.Cmd
updated, cmd := m.picker.Update(msg)
m.picker = updated.(ntemoji.Model)
return m, cmd
}
func (m model) View() string {
return m.zm.Scan(m.picker.View())
}
func main() {
zm := zone.New()
picker := ntemoji.New(
ntemoji.WithInitialEmoji("🚀"),
ntemoji.WithPresets([]string{"😀", "👍", "🔥", "✅", "❌"}),
ntemoji.WithShowSearch(true),
)
picker.SetZoneManager(zm)
p := tea.NewProgram(model{picker: picker, zm: zm}, tea.WithAltScreen(), tea.WithMouseAllMotion())
if _, err := p.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
```
## SwatchPicker (Popup Modal overlay)
The `SwatchPicker` is a small UI component representing the selected emoji with a dropdown arrow (e.g. `😀 ▼`). Clicking it opens the full emoji keyboard popup centered on the button.
1. **Create the swatch**: `swatch := ntemoji.NewSwatchPicker("🚀", "Trigger")`
2. **Setup bubblezone**: `swatch.SetZoneManager(zm)`
3. **Set bounds & render**: call `swatch.SetBounds(row, col, width, height)` and wrap with overlay rendering:
```go
mainView = swatch.ViewWithOverlay(mainView, width, height)
return zm.Scan(mainView)
```
4. **Update**: forward key and mouse events to the open swatch.
## Keyboard & Mouse Interactions
- **Tab / Shift+Tab**: Cycle focus among the **Presets row**, **Search bar**, **Category selectors**, and the **Emoji grid**.
- **Arrows / hjkl**: Navigate selections within the active presets or the emoji grid. Changing focus to category selection and using ←/→ instantly switches category tabs.
- **Enter**: Confirms the selected emoji and fires an `EmojiChangedMsg`.
- **Escape**: Cancels selection and fires an `EmojiCanceledMsg`.
- **Mouse clicks**: Clicking any category tab switches categories. Hovering over presets/grid items highlights them, and releasing the left-click selects the emoji.
## Configuration Options
Construct the emoji picker with **`New(...Option)`**:
| Option | Purpose |
|--------|---------|
| `WithInitialEmoji(emoji)` | Sets the starting emoji (auto-focuses it in grid/presets). |
| `WithPresets([]string)` | Shows a row of quick-select preset emojis above search/categories. |
| `WithPalettes([]Palette)` | Supplies custom emoji lists and category tabs. |
| `WithStyle(lipgloss.Style)` | Configures custom outer frame border and padding. |
| `WithAutoDismiss(bool)` | When true, selection includes `Dismiss: true` to auto-hide the modal. |
| `WithShowSearch(bool)` | Toggles the search input box for matching keywords. |
## TUI Emoji Compatibility & Box-Sizing
Emojis are generally 2 columns wide in terminal user interfaces. However, some emojis (such as those containing the variation selector `\ufe0f`, e.g., `⚠️` or `🌧️`) report a width of **2** columns under standard Go string-width libraries (like Lip Gloss / `uniseg`), but render as only **1** column wide on standard terminal emulators.
When designing custom palettes or presets for terminal widgets like `ntemoji`, this discrepancy causes right-border layout alignment shifts (as the terminal renders the row narrower than Lip Gloss's box-sizing layout calculations).
To guarantee pixel-perfect rectangular borders in all terminal environments:
- **Only use standard 2-column emojis** where `lipgloss.Width` and terminal rendering widths agree.
- **Avoid variation selectors** (`\ufe0f`) in custom emoji palettes.
- All default palettes in `ntemoji` are pre-curated to strictly include safe, matching 2-column emojis.
## Demos
Built-in demo examples compile out-of-the-box:
```sh
# Run full-screen demo
task build-ex-simple
./bin/simple
# Run multi-swatch grid overlay demo
task build-ex-swatch
./bin/swatch
```
## Vibe coded
This comment was inserted by a human. Except it wasn't, but then it was reviewed and I wrote this.
## License
[MIT License](./LICENSE.txt) — Copyright (c) 2026 [Neomantra Corp](https://www.neomantra.com).
----
Made with :heart: and :fire: by the team behind [Nimble.Markets](https://nimble.markets).