init layout, notifications, tabs & more

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-08-17 22:09:32 +02:00
parent f3a1877832
commit 5f99fa7521
68 changed files with 6375 additions and 330 deletions
+103
View File
@@ -0,0 +1,103 @@
# notification
Toast-style notifications, triggered from anywhere in a bubbletea program via an exported
`tea.Msg` (`ShowMsg`/`Show`) rather than a direct reference to the `Model` that ends up rendering
them - standard Elm architecture, no IPC between processes.
It composites over an already-rendered string, so it has no dependency on
`github.com/anotherhadi/ilovetui/layout`: the same `Model` works whether the host uses `layout`
for its main content or not.
## Quick start
```go
import (
"github.com/anotherhadi/ilovetui/notification"
)
type model struct {
notif notification.Model
width, height int
}
func newModel() model {
return model{notif: notification.New()}
}
func (m model) Init() tea.Cmd { return m.notif.Init() }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
if msg.String() == "s" {
return m, notification.Show("Saved", "Config written to disk", notification.Success)
}
}
var cmd tea.Cmd
m.notif, cmd = m.notif.Update(msg)
return m, cmd
}
func (m model) View() tea.View {
background := renderYourUI(m.width, m.height)
view := tea.NewView(m.notif.Render(background))
view.AltScreen = true
return view
}
```
Any component in the same bubbletea program can trigger a toast via `notification.Show`, without
holding a reference to the `notification.Model` that will actually render it - that `Model` just
needs to see every `tea.Msg` the program produces (i.e. get its `Update` called from the top-level
`Update`), same as any other child model.
## Showing and dismissing
```go
return m, notification.Show("Saved", "Config written to disk", notification.Success)
return m, notification.Show("Sticky", "Stays until dismissed",
notification.Info, notification.WithID("sticky-demo"), notification.WithDuration(0))
return m, notification.Dismiss("sticky-demo")
```
Four kinds: `Info`, `Success`, `Warning`, `Error`, each with its own color preset (see Styling
below). By default a toast auto-dismisses after `notification.DefaultDuration` (3s);
`WithDuration(0)` makes it sticky - it stays until `Dismiss(id)` removes it, so a sticky toast
needs `WithID` to be dismissable later (an auto-generated id is never returned to the caller).
Showing again with the same id replaces the toast in place, resetting its position and timer,
instead of stacking a duplicate.
## Position and stacking
```go
n := notification.New(notification.WithPosition(notification.TopRight))
```
Six anchors: `Top`, `TopLeft`, `TopRight`, `Bottom`, `BottomLeft`, `BottomRight` - toasts always
hug an edge or corner, never the middle of the screen. Multiple toasts stack along the anchored
edge, newest closest to it; a stack that overflows the background's height clips the oldest
toasts first, so the newest ones stay visible.
## Styling
```go
n := notification.New(notification.WithMaxWidth(40), notification.WithStyles(myStyles))
return m, notification.Show("Title", "Message", notification.Success,
notification.WithToastStyle(oneOffStyle))
```
`WithMaxWidth` caps how wide a toast box can grow before its message wraps; a toast narrower than
the cap shrinks to fit its content instead of padding out to it. A toast can also never overflow
past the edge of whatever background it's rendered on, regardless of this cap. `WithStyles` sets
the default per-`Kind` look for every toast shown by this `Model`; `WithToastStyle` (a `Show`
option) overrides it for one toast alone. `DefaultStyles()` builds from `style.S`: `Info` uses
`Primary` (no dedicated "info" color in the theme), `Success`/`Warning`/`Error` use their matching
`style.S` alias.
## Examples
- `examples/notification` - all four kinds, a sticky toast with manual dismiss, cycling through
all six positions.