Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* session.js — Workshop Session Mode UI (Fund once, ask many)
|
|
|
|
|
|
*
|
|
|
|
|
|
* Flow:
|
|
|
|
|
|
* 1. User clicks "⚡ FUND SESSION" → left panel slides in
|
|
|
|
|
|
* 2. Picks amount (200–5000 sats) → POST /api/sessions → deposit invoice
|
|
|
|
|
|
* 3. Pays invoice (stub: "Simulate Payment") → 2 s polling until state=active
|
|
|
|
|
|
* 4. Macaroon + sessionId stored in localStorage; input bar activates (green border)
|
|
|
|
|
|
* 5. Every Enter/Send in the input bar → POST /api/sessions/:id/request
|
|
|
|
|
|
* 6. Timmy's response shown in speech bubble, balance ticks down in HUD
|
|
|
|
|
|
* 7. Balance < 50 sats → low-balance notice; Top Up button reopens panel topup step
|
|
|
|
|
|
* 8. On page reload: localStorage is read, session is validated, UI restored
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-03-19 18:09:44 +00:00
|
|
|
|
import { setSpeechBubble, setMood } from './agents.js';
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
import { appendSystemMessage, setSessionSendHandler, setInputBarSessionMode } from './ui.js';
|
2026-03-19 18:09:44 +00:00
|
|
|
|
import { getOrRefreshToken } from './nostr-identity.js';
|
2026-03-19 18:16:40 +00:00
|
|
|
|
import { sentiment } from './edge-worker-client.js';
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
|
|
|
|
|
|
const API = '/api';
|
|
|
|
|
|
const LS_KEY = 'timmy_session_v1';
|
|
|
|
|
|
const POLL_MS = 2000;
|
|
|
|
|
|
const POLL_TIMEOUT = 60_000;
|
|
|
|
|
|
const MIN_BALANCE = 50;
|
|
|
|
|
|
|
|
|
|
|
|
// ── Module state ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
let _panel = null;
|
|
|
|
|
|
let _sessionId = null;
|
|
|
|
|
|
let _macaroon = null;
|
|
|
|
|
|
let _balanceSats = 0;
|
|
|
|
|
|
let _sessionState = null; // null | 'awaiting_payment' | 'active' | 'paused' | 'expired'
|
|
|
|
|
|
let _pollTimer = null;
|
|
|
|
|
|
let _inFlight = false;
|
|
|
|
|
|
let _selectedSats = 500; // deposit amount selection
|
|
|
|
|
|
let _topupSats = 500; // topup amount selection
|
|
|
|
|
|
|
|
|
|
|
|
// ── Public API ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
export function initSessionPanel() {
|
|
|
|
|
|
_panel = document.getElementById('session-panel');
|
|
|
|
|
|
if (!_panel) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Buttons
|
|
|
|
|
|
_on('open-session-btn', 'click', _openPanel);
|
|
|
|
|
|
_on('session-close', 'click', _closePanel);
|
|
|
|
|
|
_on('session-create-btn', 'click', _createSession);
|
|
|
|
|
|
_on('session-pay-btn', 'click', _payDeposit);
|
|
|
|
|
|
_on('session-topup-btn', 'click', () => _setStep('topup'));
|
|
|
|
|
|
_on('session-topup-create-btn','click', _createTopup);
|
|
|
|
|
|
_on('session-topup-pay-btn', 'click', _payTopup);
|
|
|
|
|
|
_on('session-back-btn', 'click', () => _setStep('active'));
|
|
|
|
|
|
_on('topup-quick-btn', 'click', () => { _openPanel(); _setStep('topup'); });
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
_on('session-hud-topup', 'click', (e) => { e.preventDefault(); _openPanel(); _setStep('topup'); });
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Amount preset buttons — deposit (quick-fill the number input)
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
_panel.querySelectorAll('[data-session-step="fund"] .session-amount-btn').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
|
_panel.querySelectorAll('[data-session-step="fund"] .session-amount-btn')
|
|
|
|
|
|
.forEach(b => b.classList.remove('active'));
|
|
|
|
|
|
btn.classList.add('active');
|
|
|
|
|
|
_selectedSats = parseInt(btn.dataset.sats, 10);
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
const inp = document.getElementById('session-amount-input');
|
|
|
|
|
|
if (inp) inp.value = _selectedSats;
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Free-text number input — deposit
|
|
|
|
|
|
document.getElementById('session-amount-input')?.addEventListener('input', () => {
|
|
|
|
|
|
const v = parseInt(document.getElementById('session-amount-input').value, 10);
|
|
|
|
|
|
if (Number.isFinite(v)) {
|
|
|
|
|
|
_selectedSats = v;
|
|
|
|
|
|
_panel.querySelectorAll('[data-session-step="fund"] .session-amount-btn')
|
|
|
|
|
|
.forEach(b => b.classList.toggle('active', parseInt(b.dataset.sats, 10) === v));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Amount preset buttons — topup (quick-fill the topup number input)
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
_panel.querySelectorAll('[data-session-step="topup"] .session-amount-btn').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
|
_panel.querySelectorAll('[data-session-step="topup"] .session-amount-btn')
|
|
|
|
|
|
.forEach(b => b.classList.remove('active'));
|
|
|
|
|
|
btn.classList.add('active');
|
|
|
|
|
|
_topupSats = parseInt(btn.dataset.sats, 10);
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
const inp = document.getElementById('session-topup-input');
|
|
|
|
|
|
if (inp) inp.value = _topupSats;
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Free-text number input — topup
|
|
|
|
|
|
document.getElementById('session-topup-input')?.addEventListener('input', () => {
|
|
|
|
|
|
const v = parseInt(document.getElementById('session-topup-input').value, 10);
|
|
|
|
|
|
if (Number.isFinite(v)) {
|
|
|
|
|
|
_topupSats = v;
|
|
|
|
|
|
_panel.querySelectorAll('[data-session-step="topup"] .session-amount-btn')
|
|
|
|
|
|
.forEach(b => b.classList.toggle('active', parseInt(b.dataset.sats, 10) === v));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
// Try to restore from localStorage on init
|
|
|
|
|
|
_tryRestore();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function isSessionActive() {
|
|
|
|
|
|
return _sessionState === 'active' || _sessionState === 'paused';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Called by ui.js when user submits the input bar while session is active
|
|
|
|
|
|
export async function sessionSendHandler(text) {
|
|
|
|
|
|
if (!_sessionId || !_macaroon || _inFlight) return;
|
|
|
|
|
|
|
|
|
|
|
|
_inFlight = true;
|
|
|
|
|
|
_setSendBusy(true);
|
|
|
|
|
|
appendSystemMessage(`you: ${text}`);
|
|
|
|
|
|
|
2026-03-19 18:09:44 +00:00
|
|
|
|
// Attach Nostr token if available
|
|
|
|
|
|
const nostrToken = await getOrRefreshToken('/api');
|
|
|
|
|
|
const reqHeaders = {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
'Authorization': `Bearer ${_macaroon}`,
|
|
|
|
|
|
};
|
|
|
|
|
|
if (nostrToken) reqHeaders['X-Nostr-Token'] = nostrToken;
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`${API}/sessions/${_sessionId}/request`, {
|
|
|
|
|
|
method: 'POST',
|
2026-03-19 18:09:44 +00:00
|
|
|
|
headers: reqHeaders,
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
body: JSON.stringify({ request: text }),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (res.status === 402) {
|
|
|
|
|
|
_balanceSats = data.balance ?? 0;
|
|
|
|
|
|
_sessionState = 'paused';
|
|
|
|
|
|
_saveToStorage();
|
|
|
|
|
|
_applySessionUI();
|
|
|
|
|
|
setSpeechBubble('My energy is depleted... top me up to continue!');
|
|
|
|
|
|
appendSystemMessage('⚡ Low balance — tap Top Up');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (res.status === 401) {
|
|
|
|
|
|
// Macaroon rejected — stale session, clear and reset
|
|
|
|
|
|
_clearSession();
|
|
|
|
|
|
appendSystemMessage('Session expired — fund a new one.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
|
appendSystemMessage('Session error: ' + (data.error || res.status));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_balanceSats = data.balanceRemaining ?? 0;
|
|
|
|
|
|
_sessionState = _balanceSats < MIN_BALANCE ? 'paused' : 'active';
|
|
|
|
|
|
_saveToStorage();
|
|
|
|
|
|
_applySessionUI();
|
|
|
|
|
|
|
|
|
|
|
|
const reply = data.result || data.reason || '…';
|
|
|
|
|
|
setSpeechBubble(reply);
|
|
|
|
|
|
appendSystemMessage('Timmy: ' + reply.slice(0, 80));
|
|
|
|
|
|
|
2026-03-19 18:16:40 +00:00
|
|
|
|
// Sentiment-driven mood on inbound Timmy reply
|
|
|
|
|
|
sentiment(reply).then(s => {
|
|
|
|
|
|
setMood(s.label);
|
|
|
|
|
|
setTimeout(() => setMood(null), 10_000);
|
|
|
|
|
|
}).catch(() => {});
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
// Update active-step balance if panel is open
|
|
|
|
|
|
_updateActiveStep();
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
appendSystemMessage('Session error: ' + err.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
_inFlight = false;
|
|
|
|
|
|
_setSendBusy(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Panel open/close ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
function _openPanel() {
|
|
|
|
|
|
if (!_panel) return;
|
|
|
|
|
|
_panel.classList.add('open');
|
|
|
|
|
|
_clearError();
|
|
|
|
|
|
if (_sessionState === 'active') {
|
|
|
|
|
|
_setStep('active');
|
|
|
|
|
|
_updateActiveStep();
|
|
|
|
|
|
} else if (_sessionState === 'paused') {
|
|
|
|
|
|
_setStep('topup');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_setStep('fund');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _closePanel() {
|
|
|
|
|
|
_panel?.classList.remove('open');
|
|
|
|
|
|
_stopPolling();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Create session ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
async function _createSession() {
|
|
|
|
|
|
_clearError();
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Read from the number input (may differ from the last preset clicked)
|
|
|
|
|
|
const inp = document.getElementById('session-amount-input');
|
|
|
|
|
|
if (inp) {
|
|
|
|
|
|
const v = parseInt(inp.value, 10);
|
|
|
|
|
|
if (Number.isFinite(v)) _selectedSats = v;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_selectedSats < 200 || _selectedSats > 10_000) {
|
|
|
|
|
|
_setError('Amount must be 200–10,000 sats.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
_setStatus('fund', 'creating session…', '#ffaa00');
|
|
|
|
|
|
_btn('session-create-btn', true);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-19 18:09:44 +00:00
|
|
|
|
const nostrToken = await getOrRefreshToken('/api');
|
|
|
|
|
|
const createHeaders = { 'Content-Type': 'application/json' };
|
|
|
|
|
|
if (nostrToken) createHeaders['X-Nostr-Token'] = nostrToken;
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
const res = await fetch(`${API}/sessions`, {
|
|
|
|
|
|
method: 'POST',
|
2026-03-19 18:09:44 +00:00
|
|
|
|
headers: createHeaders,
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
body: JSON.stringify({ amount_sats: _selectedSats }),
|
|
|
|
|
|
});
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!res.ok) { _setError(data.error || 'Failed to create session.'); return; }
|
|
|
|
|
|
|
|
|
|
|
|
_sessionId = data.sessionId;
|
|
|
|
|
|
|
|
|
|
|
|
// Show deposit invoice
|
|
|
|
|
|
const inv = data.invoice;
|
|
|
|
|
|
_el('session-invoice-amount').textContent = inv.amountSats + ' sats';
|
|
|
|
|
|
_el('session-invoice-pr').textContent = inv.paymentRequest || '';
|
|
|
|
|
|
const hashEl = _el('session-invoice-hash');
|
|
|
|
|
|
if (hashEl) hashEl.dataset.hash = inv.paymentHash || '';
|
|
|
|
|
|
|
|
|
|
|
|
_setStep('invoice');
|
|
|
|
|
|
_setStatus('invoice', '⚡ Awaiting deposit…', '#ffaa00');
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
_setError('Network error: ' + err.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
_btn('session-create-btn', false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Pay deposit (stub) ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
async function _payDeposit() {
|
|
|
|
|
|
const hash = _el('session-invoice-hash')?.dataset.hash;
|
|
|
|
|
|
if (!hash) { _setError('No payment hash — use real Lightning?'); return; }
|
|
|
|
|
|
|
|
|
|
|
|
_btn('session-pay-btn', true);
|
|
|
|
|
|
_setStatus('invoice', 'Simulating payment…', '#ffaa00');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`${API}/dev/stub/pay/${hash}`, { method: 'POST' });
|
|
|
|
|
|
const ok = (await res.json()).ok;
|
|
|
|
|
|
if (!ok) { _setError('Payment simulation failed.'); return; }
|
|
|
|
|
|
_startDepositPolling();
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
_setError('Payment error: ' + err.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
_btn('session-pay-btn', false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Deposit polling ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
function _startDepositPolling() {
|
|
|
|
|
|
_stopPolling();
|
|
|
|
|
|
const deadline = Date.now() + POLL_TIMEOUT;
|
|
|
|
|
|
|
|
|
|
|
|
async function poll() {
|
|
|
|
|
|
if (!_sessionId) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`${API}/sessions/${_sessionId}`);
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (data.state === 'active') {
|
|
|
|
|
|
_macaroon = data.macaroon;
|
|
|
|
|
|
_balanceSats = data.balanceSats;
|
|
|
|
|
|
_sessionState = 'active';
|
|
|
|
|
|
_saveToStorage();
|
|
|
|
|
|
_applySessionUI();
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
_closePanel(); // panel auto-closes; user types in input bar
|
|
|
|
|
|
appendSystemMessage(`Session active — ${_balanceSats} sats`);
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch { /* network hiccup */ }
|
|
|
|
|
|
|
|
|
|
|
|
if (Date.now() > deadline) {
|
|
|
|
|
|
_setError('Timed out waiting for payment.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
_pollTimer = setTimeout(poll, POLL_MS);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_pollTimer = setTimeout(poll, POLL_MS);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Topup ─────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
async function _createTopup() {
|
|
|
|
|
|
if (!_sessionId || !_macaroon) return;
|
|
|
|
|
|
_clearError();
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Read from the topup number input
|
|
|
|
|
|
const inp = document.getElementById('session-topup-input');
|
|
|
|
|
|
if (inp) {
|
|
|
|
|
|
const v = parseInt(inp.value, 10);
|
|
|
|
|
|
if (Number.isFinite(v)) _topupSats = v;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_topupSats < 200 || _topupSats > 10_000) {
|
|
|
|
|
|
_setError('Topup amount must be 200–10,000 sats.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
_setStatus('topup', 'creating topup invoice…', '#ffaa00');
|
|
|
|
|
|
_btn('session-topup-create-btn', true);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`${API}/sessions/${_sessionId}/topup`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
'Authorization': `Bearer ${_macaroon}`,
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({ amount_sats: _topupSats }),
|
|
|
|
|
|
});
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
|
// Pending topup already exists — surface it
|
|
|
|
|
|
const pending = data.pendingTopup;
|
|
|
|
|
|
if (pending) {
|
|
|
|
|
|
_el('session-topup-pr').textContent = pending.paymentRequest || '';
|
|
|
|
|
|
const th = _el('session-topup-hash');
|
|
|
|
|
|
if (th) th.dataset.hash = pending.paymentHash || '';
|
|
|
|
|
|
_el('session-topup-pr-row').style.display = '';
|
|
|
|
|
|
_setStatus('topup', '⚡ Previous invoice still pending', '#ffaa00');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
_setError(data.error || 'Failed to create topup.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const inv = data.topup;
|
|
|
|
|
|
_el('session-topup-pr').textContent = inv.paymentRequest || '';
|
|
|
|
|
|
const th = _el('session-topup-hash');
|
|
|
|
|
|
if (th) th.dataset.hash = inv.paymentHash || '';
|
|
|
|
|
|
_el('session-topup-pr-row').style.display = '';
|
|
|
|
|
|
_setStatus('topup', '⚡ Awaiting topup payment…', '#ffaa00');
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
_setError('Topup error: ' + err.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
_btn('session-topup-create-btn', false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function _payTopup() {
|
|
|
|
|
|
const hash = _el('session-topup-hash')?.dataset.hash;
|
|
|
|
|
|
if (!hash) { _setError('No topup hash.'); return; }
|
|
|
|
|
|
|
|
|
|
|
|
_btn('session-topup-pay-btn', true);
|
|
|
|
|
|
_setStatus('topup', 'Simulating topup payment…', '#ffaa00');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`${API}/dev/stub/pay/${hash}`, { method: 'POST' });
|
|
|
|
|
|
const ok = (await res.json()).ok;
|
|
|
|
|
|
if (!ok) { _setError('Topup simulation failed.'); return; }
|
|
|
|
|
|
_startTopupPolling();
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
_setError('Topup payment error: ' + err.message);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
_btn('session-topup-pay-btn', false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _startTopupPolling() {
|
|
|
|
|
|
_stopPolling();
|
|
|
|
|
|
const deadline = Date.now() + POLL_TIMEOUT;
|
|
|
|
|
|
const prevBalance = _balanceSats;
|
|
|
|
|
|
|
|
|
|
|
|
async function poll() {
|
|
|
|
|
|
if (!_sessionId) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`${API}/sessions/${_sessionId}`);
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (data.balanceSats > prevBalance || data.state === 'active') {
|
|
|
|
|
|
_balanceSats = data.balanceSats;
|
|
|
|
|
|
_macaroon = data.macaroon || _macaroon;
|
|
|
|
|
|
_sessionState = data.state === 'active' ? 'active' : _sessionState;
|
|
|
|
|
|
_saveToStorage();
|
|
|
|
|
|
_applySessionUI();
|
|
|
|
|
|
_setStep('active');
|
|
|
|
|
|
_updateActiveStep();
|
|
|
|
|
|
_setStatus('active', `⚡ Topped up! ${_balanceSats} sats`, '#22aa66');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
|
|
|
|
|
|
|
if (Date.now() > deadline) { _setError('Topup timed out.'); return; }
|
|
|
|
|
|
_pollTimer = setTimeout(poll, POLL_MS);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_pollTimer = setTimeout(poll, POLL_MS);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Restore from localStorage ─────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
async function _tryRestore() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const raw = localStorage.getItem(LS_KEY);
|
|
|
|
|
|
if (!raw) return;
|
|
|
|
|
|
const { sessionId, macaroon, balanceSats } = JSON.parse(raw);
|
|
|
|
|
|
if (!sessionId || !macaroon) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Validate the session is still live
|
|
|
|
|
|
const res = await fetch(`${API}/sessions/${sessionId}`);
|
|
|
|
|
|
if (!res.ok) { localStorage.removeItem(LS_KEY); return; }
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (data.state !== 'active' && data.state !== 'paused') {
|
|
|
|
|
|
localStorage.removeItem(LS_KEY);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_sessionId = sessionId;
|
|
|
|
|
|
_macaroon = data.macaroon || macaroon;
|
|
|
|
|
|
_balanceSats = data.balanceSats ?? balanceSats;
|
|
|
|
|
|
_sessionState = data.state;
|
|
|
|
|
|
_saveToStorage();
|
|
|
|
|
|
_applySessionUI();
|
|
|
|
|
|
|
|
|
|
|
|
appendSystemMessage(`Session restored (${_balanceSats} sats)`);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// Ignore restore errors silently
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Session UI helpers ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
function _applySessionUI() {
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
const anyActive = isSessionActive(); // true for both 'active' and 'paused'
|
|
|
|
|
|
const canSend = _sessionState === 'active';
|
|
|
|
|
|
// Show low-balance overlay whenever session exists but balance is too low
|
|
|
|
|
|
const lowBal = anyActive && _balanceSats < MIN_BALANCE;
|
|
|
|
|
|
|
|
|
|
|
|
// HUD balance: "Balance: X sats ⚡ Top Up"
|
|
|
|
|
|
const $hud = document.getElementById('session-hud');
|
|
|
|
|
|
if ($hud) {
|
|
|
|
|
|
$hud.style.display = anyActive ? '' : 'none';
|
|
|
|
|
|
const $span = document.getElementById('session-hud-balance');
|
|
|
|
|
|
if ($span) $span.textContent = `Balance: ${_balanceSats} sats`;
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Top button label
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
const $btn = document.getElementById('open-session-btn');
|
|
|
|
|
|
if ($btn) {
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
$btn.textContent = anyActive
|
|
|
|
|
|
? `SESSION: ${_balanceSats} ⚡`
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
: '⚡ FUND SESSION';
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
$btn.style.background = anyActive ? '#1a4a30' : '';
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Input bar green border + placeholder when session can send
|
|
|
|
|
|
setInputBarSessionMode(canSend, 'Ask Timmy (session active)…');
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Route input bar to session handler only when active (not paused)
|
|
|
|
|
|
setSessionSendHandler(canSend ? sessionSendHandler : null);
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
// Low balance notice above input bar
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
const $notice = document.getElementById('low-balance-notice');
|
|
|
|
|
|
if ($notice) $notice.style.display = lowBal ? '' : 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _updateActiveStep() {
|
|
|
|
|
|
const el = document.getElementById('session-active-amount');
|
|
|
|
|
|
if (el) {
|
|
|
|
|
|
el.textContent = _balanceSats + ' sats';
|
|
|
|
|
|
el.style.color = _balanceSats < MIN_BALANCE ? '#ff8844' : '#44dd88';
|
|
|
|
|
|
}
|
|
|
|
|
|
// Reset topup pr row visibility
|
|
|
|
|
|
const prRow = document.getElementById('session-topup-pr-row');
|
|
|
|
|
|
if (prRow) prRow.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _clearSession() {
|
|
|
|
|
|
_sessionId = null;
|
|
|
|
|
|
_macaroon = null;
|
|
|
|
|
|
_balanceSats = 0;
|
|
|
|
|
|
_sessionState = null;
|
|
|
|
|
|
localStorage.removeItem(LS_KEY);
|
|
|
|
|
|
setInputBarSessionMode(false);
|
|
|
|
|
|
setSessionSendHandler(null);
|
Task #23: Workshop session mode UI — fund once, ask many (all review issues fixed)
## Changes
### the-matrix/js/session.js (new module)
- Full session lifecycle: create → invoice → deposit poll → active → request → topup → restore
- Presets + number input for deposit (200–10,000 sats) and topup amounts; reads from input on submit
- Input validation: 200–10,000 sats range enforced in JS before API call
- Auto-closes panel after deposit payment confirms (closePanel in _startDepositPolling success branch)
- Low-balance condition fixed: `isSessionActive()` (covers both 'active' and 'paused') not just `active`
- HUD: updates `#session-hud-balance` span with "Balance: X sats"; `#session-hud-topup` link clickable
- Topup reads from `#session-topup-input` number field, same validation
- localStorage restore: validates session via GET, restores macaroon + balance + UI state on reload
- Expired/401 sessions: clears storage, resets all UI
- Request in-flight guard prevents double-submit; send button disabled during request
### the-matrix/js/ui.js
- `setSessionSendHandler(fn)` — override input bar submit when session active
- `setInputBarSessionMode(active, placeholder)` — green border + placeholder swap
- `send()` routes to session handler when set, falls back to WS visitor_message
### the-matrix/index.html
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` with `#session-hud-balance` span + `#session-hud-topup` link (pointer-events: all)
- `#session-panel` (left slide-in): fund / invoice / active / topup steps
- Fund + topup steps each have preset buttons AND a number input (200–10k range)
- Added 10k preset button to both step grids
- `#visitor-input.session-active` green pulse border animation (3s keyframe)
- `#low-balance-notice` strip above input bar with inline Top Up button
- CSS: `.session-amount-input` green styled, spin buttons hidden; `.session-amount-row` flex layout
- CSS: `.primary-green` / `.muted` panel button variants for session panel theme
### the-matrix/js/main.js
- Import + call `initSessionPanel()` in firstInit block
## Verification
- npm run build: clean (0 errors, 15 modules)
- Testkit: 27/27 PASS (session tests 11–16, 22 all green)
2026-03-19 03:56:34 +00:00
|
|
|
|
const $hud = document.getElementById('session-hud');
|
|
|
|
|
|
if ($hud) $hud.style.display = 'none';
|
Task #23: Workshop session mode UI — fund once, ask many
## What was done
- **`the-matrix/js/session.js`** (new module): Full session mode UI lifecycle:
- Create session flow: amount presets → POST /api/sessions → deposit invoice step
- Deposit payment: stub simulate → 2s polling until state=active
- macaroon + sessionId stored in localStorage (`timmy_session_v1`)
- Request submission: intercepts input bar when session active → POST /api/sessions/:id/request
→ Timmy speech bubble shows result, balance updates in HUD
- Low-balance (< 50 sats): paused state, low-balance notice shown, topup quick-button
- Topup flow: preset amount → POST /api/sessions/:id/topup → topup invoice → stub pay → poll
- Restore from localStorage on page reload: validates session via GET, restores full UI state
- Session expiry / 401 macaroon rejection: clears storage, resets to unfunded state
- **`the-matrix/js/ui.js`**: Added `setSessionSendHandler(fn)` + `setInputBarSessionMode(active, placeholder)` exports; send() routes to session handler when active, falls back to WS visitor_message
- **`the-matrix/index.html`**:
- `#top-buttons` flex container: "⚡ SUBMIT JOB" (blue) + "⚡ FUND SESSION" (teal) side-by-side
- `#session-hud` balance line in HUD (green, hidden until session active)
- `#session-panel` left-side slide-in panel: fund / invoice / active / topup steps
- `.session-amount-btn` presets (200, 500, 1000, 2000, 5000 sats) with active state
- `#visitor-input.session-active` CSS: green border + 3s pulse keyframe animation
- `#low-balance-notice` strip above input bar with Top Up quick-button
- `.primary-green` / `.muted` panel button variants for session panel theme
- `#session-panel` inherits shared `.panel-btn`, `.invoice-box`, `.copy-btn` with green overrides
- **`the-matrix/js/main.js`**: Import + call `initSessionPanel()` in firstInit block
## Verification
- `npm run build` in the-matrix → clean build (0 errors)
- Full testkit: 27/27 PASS (all session tests 11–16, 22 still green)
2026-03-19 03:50:34 +00:00
|
|
|
|
const $btn = document.getElementById('open-session-btn');
|
|
|
|
|
|
if ($btn) { $btn.textContent = '⚡ FUND SESSION'; $btn.style.background = ''; }
|
|
|
|
|
|
const $notice = document.getElementById('low-balance-notice');
|
|
|
|
|
|
if ($notice) $notice.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _saveToStorage() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
localStorage.setItem(LS_KEY, JSON.stringify({
|
|
|
|
|
|
sessionId: _sessionId,
|
|
|
|
|
|
macaroon: _macaroon,
|
|
|
|
|
|
balanceSats: _balanceSats,
|
|
|
|
|
|
}));
|
|
|
|
|
|
} catch { /* storage unavailable */ }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── DOM helpers ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
function _el(id) { return document.getElementById(id); }
|
|
|
|
|
|
|
|
|
|
|
|
function _on(id, ev, fn) {
|
|
|
|
|
|
_el(id)?.addEventListener(ev, fn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _btn(id, disabled) {
|
|
|
|
|
|
const el = _el(id);
|
|
|
|
|
|
if (el) el.disabled = disabled;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _setSendBusy(busy) {
|
|
|
|
|
|
const btn = document.getElementById('send-btn');
|
|
|
|
|
|
if (btn) btn.disabled = busy;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _setStep(step) {
|
|
|
|
|
|
if (!_panel) return;
|
|
|
|
|
|
_panel.querySelectorAll('[data-session-step]').forEach(el => {
|
|
|
|
|
|
el.style.display = el.dataset.sessionStep === step ? '' : 'none';
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _setStatus(step, msg, color = '#22aa66') {
|
|
|
|
|
|
const el = document.getElementById(`session-status-${step}`);
|
|
|
|
|
|
if (el) { el.textContent = msg; el.style.color = color; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _setError(msg) {
|
|
|
|
|
|
const el = _el('session-error');
|
|
|
|
|
|
if (el) el.textContent = msg;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _clearError() {
|
|
|
|
|
|
const el = _el('session-error');
|
|
|
|
|
|
if (el) el.textContent = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _stopPolling() {
|
|
|
|
|
|
clearTimeout(_pollTimer);
|
|
|
|
|
|
_pollTimer = null;
|
|
|
|
|
|
}
|