initial: notify-display v0.1.0 — screen-space displays on lib-core.panel
This commit is contained in:
201
README.md
Normal file
201
README.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# lib-core.notify-display
|
||||
|
||||
Screen-space display layer for `lib-core.notify`. Routes channel messages into three
|
||||
UI modes: log (panel-widget list), toast (always-on HUD overlay), detail (modal
|
||||
panel that auto-opens on new message).
|
||||
|
||||
**Version:** 0.1.0
|
||||
**Lib-ID:** lib-core.notify-display
|
||||
**Requires:** lib-core.notify v0.1.0, lib-core.panel v0.1.0
|
||||
**Tags:** notify, display, ui, toast, log, detail, hud, overlay
|
||||
|
||||
## Topology
|
||||
|
||||
<!-- topology:start (auto-generated; do not edit) -->
|
||||
```mermaid
|
||||
graph LR
|
||||
this["lib-core.notify-display"]
|
||||
lib_notify["lib-core.notify"]
|
||||
lib_panel["lib-core.panel"]
|
||||
engine_render["engine.render.*"]
|
||||
this --> lib_notify
|
||||
this --> lib_panel
|
||||
this --> engine_render
|
||||
```
|
||||
<!-- topology:end -->
|
||||
|
||||
## Scope (v0.1.0)
|
||||
|
||||
v0.1 ships three display modes driven by `notify` channel `display_mode` declarations:
|
||||
|
||||
- **log** — panel widget that renders channel history as a sorted list (newest first).
|
||||
Plugs into `lib-core.panel` via `create_log_widget` + `panel.register`.
|
||||
- **toast** — always-on HUD overlay with fade-in/hold/fade-out animation and
|
||||
configurable position, max-visible cap, and per-message TTL.
|
||||
- **detail** — subscribes to `display_mode="detail"` channels; on each new message
|
||||
sets `current_detail` and auto-opens a registered panel widget.
|
||||
|
||||
**Intentional non-goals (deferred to v0.2+):**
|
||||
- Scroll/pagination in log widget
|
||||
- Multiple simultaneous toast columns
|
||||
- Toast queue persistence (survives mode detach/re-attach)
|
||||
- Per-severity toast theming overrides
|
||||
- Keyboard dismiss for detail panel
|
||||
|
||||
## API
|
||||
|
||||
### `display.attach_mode(mode, opts)`
|
||||
|
||||
Subscribes to existing `notify` channels that match `mode`. Must be called
|
||||
after `notify.create_channel` for those channels (channels created later are
|
||||
not automatically picked up in v0.1).
|
||||
|
||||
| mode | required opts | optional opts |
|
||||
|------|---------------|---------------|
|
||||
| `"log"` | `panel_widget_id: string` | — |
|
||||
| `"toast"` | — | `position`, `max_visible`, `default_ttl` |
|
||||
| `"detail"` | `panel_widget_id: string` | — |
|
||||
|
||||
**Toast opts schema:**
|
||||
```lua
|
||||
{
|
||||
position = "top-right", -- default; also: top-center, top-left,
|
||||
-- bottom-right, bottom-center, bottom-left
|
||||
max_visible = 5, -- oldest evicted when limit exceeded
|
||||
default_ttl = 3.0, -- seconds; overridden per-msg by msg.ttl
|
||||
}
|
||||
```
|
||||
|
||||
Loud-error on unknown `mode` or invalid `position`. Missing `panel_widget_id` for
|
||||
log/detail is a loud-error. Nil/missing `position` in toast silently defaults to
|
||||
`"top-right"`.
|
||||
|
||||
---
|
||||
|
||||
### `display.detach_mode(mode)`
|
||||
|
||||
Unsubscribes all subscriptions held for channels whose `display_mode == mode`,
|
||||
sets `mode_opts[mode] = nil`, and clears mode-specific state:
|
||||
- toast → empties `active_toasts`
|
||||
- detail → clears `current_detail`
|
||||
- log → clears `widget_channels`
|
||||
|
||||
---
|
||||
|
||||
### `display.create_log_widget(opts) → widget_def`
|
||||
|
||||
Returns a widget definition table compatible with `panel.register`.
|
||||
|
||||
```lua
|
||||
opts = {
|
||||
widget_id = "game-log", -- default
|
||||
title = "Game Log", -- default
|
||||
pause_on_open = false, -- default
|
||||
}
|
||||
```
|
||||
|
||||
The returned `widget_def.render(ctx)` reads from `notify.get_history` for all
|
||||
channels mapped to this `widget_id` (via `attach_mode("log", {panel_widget_id=...})`),
|
||||
sorts by timestamp descending, and renders up to `floor(h / row_height)` rows.
|
||||
Severity tinting: warn = `0xFFD000FF`, error = `0xFF4040FF`.
|
||||
|
||||
---
|
||||
|
||||
### `display.create_detail_widget(opts) → widget_def`
|
||||
|
||||
Returns a widget definition table compatible with `panel.register`.
|
||||
|
||||
```lua
|
||||
opts = {
|
||||
widget_id = "detail", -- default
|
||||
title = "Detail", -- default
|
||||
pause_on_open = false, -- default
|
||||
}
|
||||
```
|
||||
|
||||
Renders `current_detail` (the last message posted to any `display_mode="detail"`
|
||||
channel). Shows `msg.text` as title, `msg.data.description` as body, remaining
|
||||
`msg.data.*` keys as key-value rows. No-op if no detail message has arrived yet.
|
||||
|
||||
**Note:** Register the widget with `panel.register` **before** calling
|
||||
`attach_mode("detail", ...)` so that the auto-open `panel.open` call succeeds
|
||||
on the first message. If registered after attach, the first message silently
|
||||
fails to open the panel (pcall-guarded), but `current_detail` is still set.
|
||||
|
||||
---
|
||||
|
||||
### `display.update(dt)`
|
||||
|
||||
Must be called each game-update frame. Removes expired toasts from `active_toasts`
|
||||
(compares `engine.time.now()` against `spawn_time + ttl`). No-op if toast mode
|
||||
is not attached.
|
||||
|
||||
---
|
||||
|
||||
### `display.render()`
|
||||
|
||||
Must be called each render frame (typically after `panel.render()`). Draws the
|
||||
toast HUD overlay. No-op if toast mode is not attached or no toasts are active.
|
||||
|
||||
Toast geometry: 280 × 32 px, 8 px padding from screen edge. Positions stack in
|
||||
the growth direction of the chosen anchor (top-* grows down, bottom-* grows up).
|
||||
|
||||
Alpha animation per toast:
|
||||
- 0–10% of TTL: fade in
|
||||
- 10–85% of TTL: fully opaque
|
||||
- 85–100% of TTL: fade out
|
||||
|
||||
Screen dimensions: 1280 × 720 fallback (engine.render does not expose
|
||||
`get_screen_size` to Lua in v0.1 — same limitation as lib-core.panel).
|
||||
|
||||
---
|
||||
|
||||
## Theme keys consumed
|
||||
|
||||
`notify-display` renders toasts using hardcoded colors (no own theme keys in v0.1).
|
||||
Log and detail widgets render via `ctx.theme` supplied by `lib-core.panel`, consuming
|
||||
the standard panel theme keys: `text_color`, `text_color_dim`, `font_size_title`,
|
||||
`font_size_body`, `padding`, `row_height`.
|
||||
|
||||
---
|
||||
|
||||
## Glue pattern example
|
||||
|
||||
```lua
|
||||
local notify = require("lib-core.notify")
|
||||
local panel = require("lib-core.panel")
|
||||
local display = require("lib-core.notify-display")
|
||||
|
||||
-- 1. Declare channels
|
||||
notify.create_channel{ id = "game-events", filter = {"event"}, display_mode = "log" }
|
||||
notify.create_channel{ id = "alerts", filter = {"alert"}, display_mode = "toast" }
|
||||
notify.create_channel{ id = "item-detail", filter = {"item"}, display_mode = "detail" }
|
||||
|
||||
-- 2. Create and register widgets
|
||||
local log_def = display.create_log_widget { widget_id = "game-log" }
|
||||
local detail_def = display.create_detail_widget{ widget_id = "item-detail" }
|
||||
panel.register("game-log", log_def)
|
||||
panel.register("item-detail", detail_def)
|
||||
|
||||
-- 3. Attach modes (after register so detail auto-open works immediately)
|
||||
display.attach_mode("log", { panel_widget_id = "game-log" })
|
||||
display.attach_mode("toast", { position = "bottom-right", default_ttl = 4.0 })
|
||||
display.attach_mode("detail", { panel_widget_id = "item-detail" })
|
||||
|
||||
-- 4. Per-frame calls
|
||||
function game_update(dt)
|
||||
panel.update(dt)
|
||||
display.update(dt)
|
||||
end
|
||||
|
||||
function game_render()
|
||||
panel.render()
|
||||
display.render() -- toast overlay on top
|
||||
end
|
||||
|
||||
-- 5. Posting messages
|
||||
notify.post{ tags = {"event"}, text = "Player entered the forest" }
|
||||
notify.post{ tags = {"alert"}, text = "Low health!", ttl = 2.0, severity = "warn" }
|
||||
notify.post{ tags = {"item"}, text = "Iron Sword",
|
||||
data = { description = "A sturdy blade.", damage = 12 } }
|
||||
```
|
||||
Reference in New Issue
Block a user