1# 👻 oganesson.js - live and ephemeral23oganesson.js is a miniature of the live layer of a big chat app: who's here, who's typing, where their cursor is. Any element can send an ephemeral event (kinds 20000-29999) on a heartbeat or on input, and any element can show the latest one per person from a template, removing it when it goes stale or its author leaves. It publishes and subscribes through xenon.js, signs through the same `nhy:config` hook (so helium.js or any other signer applies), and argon.js keeps none of it: argon keeps what lasts, oganesson handles what vanishes.45```html6<main nog-room="lobby">7 <ul nog-send="presence" nog-show="presence">8 <template><li><b nrn-profile="{pubkey}" nrn-field="name">{npub}</b> {status}</li></template>9 </ul>10 <textarea nog-send="typing"></textarea>11 <p nog-show="typing"><template><span><b nrn-profile="{pubkey}" nrn-field="name">someone</b> is typing</span></template></p>12</main>13```1415The list announces you every minute and shows everyone else in the room, each once, until they go quiet or hide the page; the paragraph shows who is typing for five seconds after each keystroke.1617## Attributes1819| attribute | default | description |20|---|---|---|21| `nog-room` | none | Room id, sent as `["h", room]` and filtered with `#h`. Inherited from the closest ancestor. Without it events have no `h` tag and shows are not filtered by room |22| `nog-send` | | Topic this element publishes |23| `nog-on` | `input` on inputs, textareas and selects | Space-separated DOM events that publish. Pointer events send `{"x","y"}` as fractions of the element box. Without it (and not an input) the element sends a heartbeat every ttl/3 while the page is visible |24| `nog-status` | `online` | Content of heartbeats, such as `away` |25| `nog-show` | | Topic to show: one child per pubkey from the first element of the inner `<template>` (default `<span>{pubkey}</span>`), replaced on refresh, removed after the ttl |26| `nog-ttl` | per topic | Seconds an event stays shown, and the heartbeat period (ttl/3) |27| `nog-rate` | per topic | Minimum ms between sends. The latest throttled value is sent when the window ends |28| `nog-self` | | Also show your own events (hidden by default; own pubkeys are learned from events you sign) |29| `nog-ignore` | | oganesson skips this element and its descendants |3031Templates get xenon's fields (with neon.js `{npub}`, `{ago}`; with radon.js `nrn-profile` names), plus `{status}` and every key of JSON content, such as `{x}` and `{y}`, HTML-escaped. A cursor is `<i style="left:calc({x} * 100%); top:calc({y} * 100%)">`.3233## Topics3435| topic | kind | ttl | rate | content |36|---|---|---|---|---|37| `presence` | 20001 | 180 | 1000 | `online`, `away`, ... or `offline` |38| `typing` | 20002 | 5 | 3000 | `typing` or `stopped` |39| anything else | `nogCfg.kind` (20078) | 30 | 1000 | the status, or JSON such as `{"x":0.5,"y":0.25}`; tagged `["t", topic]` |4041Presence and typing follow the convention shared by Block's Buzz and tinyrelay. Other topics, such as cursors, are only a convention of ours, on one configurable kind (`window.nogCfg = {kind}`).4243## Events4445| event | cancel to | detail |46|---|---|---|47| `nog:send` | skip sending | `cfg` (unsigned `cfg.event`) |48| `nog:shown` | | `event`, `fields`, `elt` |49| `nog:expired` | keep the element | `pubkey`, `elt` |50| `nog:error` | | `cfg`, `error` |51| `nog:process` | | dispatch on an element to initialize new content |5253## Behavior5455- Readers drop events more than 5s in the future or older than their ttl, and events older than the one shown for that pubkey.56- `offline` and `stopped` remove the entry at once. Reading also accepts JSON `{"status"}` and `{"typing":bool}`, and `false`, `idle`, `offline` as stopped typing.57- Typing sends `stopped` when the field is emptied or blurred. Hiding the page sends `offline` for heartbeats and `stopped` for active typing; showing it sends a heartbeat.58- Seeing a new pubkey makes this page's heartbeats in the same room and topic send right away, so newcomers don't wait a minute.59- `oganesson.send(el, content)` publishes through `el`'s throttle, `oganesson.leave()` sends the goodbyes, `oganesson.mine` holds your pubkeys.6061## Caveats6263- Every heartbeat, keystroke burst and cursor move is a signature. A NIP-07 extension may prompt each time, so this works best with helium.js's local key or an extension set to auto-approve these kinds.64- Buzz and tinyrelay members-only rooms may require NIP-42 AUTH, which a future family library will handle.6566Requires xenon.js.