mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-18 21:00:22 -05:00
fix(ruview-live): open as a dialog that takes the keys, sized per view
Hotkeys reach a pane only while it holds the keyboard, and `focus` alone was not taking it. The engine's documented dialog form does: open with focus + closeOnEscape + holdToasts (Escape closes, as originally). Each open also requests `rows` for its view (overview 16, waterfall 30, radar 26) so the inline pane opens tall enough for the bordered design instead of a third of the screen; switching views re-requests the size, and picture heights leave room for the chrome (rows - 15). Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
1 parent
bf21c8c7a9
commit
c4f9d95e11
3 files changed
+32
-10
No files matched your search
@@ -20,6 +20,17 @@ export { ANIMATED, picturesOf, sizesOf, viewOf } from './views.mjs';
|
||||
export const PANE_ID = 'ruview-live';
|
||||
export const COMMAND = 'ruview';
|
||||
const TICK_MS = 5000;
|
||||
/** Body rows each view asks for when the pane sits inline above the prompt. */
|
||||
export const VIEW_ROWS = Object.freeze({ overview: 16, waterfall: 30, radar: 26 });
|
||||
|
||||
/**
|
||||
* The open request: a dialog (focus + closeOnEscape + holdToasts) takes the
|
||||
* keyboard, so the view keys work at once; `rows` sizes the inline pane to
|
||||
* the view instead of a third of the screen (the dock ignores it).
|
||||
*/
|
||||
export const openArgsOf = (mode) => ({
|
||||
id: PANE_ID, title: 'RuView', focus: true, closeOnEscape: true, holdToasts: true, rows: VIEW_ROWS[mode] ?? VIEW_ROWS.overview,
|
||||
});
|
||||
/** Animation frame period: ~12 fps, well inside blit's 60 shown a second. */
|
||||
export const FRAME_MS = 80;
|
||||
|
||||
@@ -138,15 +149,17 @@ export function register(on, options = {}) {
|
||||
function setMode(next) {
|
||||
if (!MODES.includes(next) || next === mode) return;
|
||||
mode = next;
|
||||
if (isOpen && host) { stop(); startPolling(); } else host?.invalidate();
|
||||
if (isOpen && host) {
|
||||
// Each open sets the size anew: re-request rows for this view, keeping the keys.
|
||||
void host.open(openArgsOf(mode)).catch(() => undefined);
|
||||
stop();
|
||||
startPolling();
|
||||
} else host?.invalidate();
|
||||
}
|
||||
|
||||
async function open(initialMode) {
|
||||
if (MODES.includes(initialMode)) mode = initialMode;
|
||||
// Ask for the keyboard so the view keys (1/2/3, r, c) work at once; the
|
||||
// surface grants it only over an empty prompt. No closeOnEscape: Escape
|
||||
// hands the keys back without closing the pane.
|
||||
await host.open({ id: PANE_ID, title: 'RuView', focus: true });
|
||||
await host.open(openArgsOf(mode));
|
||||
stop();
|
||||
startPolling();
|
||||
}
|
||||
@@ -180,8 +193,8 @@ export function register(on, options = {}) {
|
||||
if (arg === 'refresh') { await refresh(); return { text: statusOf(model) }; }
|
||||
if (MODES.includes(arg)) {
|
||||
if (isOpen) {
|
||||
setMode(arg);
|
||||
await host.open({ id: PANE_ID, title: 'RuView', focus: true }).catch(() => undefined);
|
||||
if (arg === mode) await host.open(openArgsOf(mode)).catch(() => undefined);
|
||||
else setMode(arg);
|
||||
} else await open(arg);
|
||||
return { text: `RuView pane: ${arg} view.` };
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export const ANIMATED = Object.freeze(['shimmer', 'waterfall', 'fan', 'pulse']);
|
||||
/** Picture sizes that fit the pane body. */
|
||||
export function sizesOf(columns = 80, rows = 30) {
|
||||
const width = Math.max(24, Math.min(128, columns - 6));
|
||||
const height = Math.max(6, Math.min(26, rows - 12));
|
||||
const height = Math.max(6, Math.min(26, rows - 15)); // the header, tabs, card chrome and footer take ~15 rows
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
ANIMATED, Braille, colormap, commandsOf, DEFAULT, drain, Grid, lineChart, modelOf, phaseOf, picturesOf, pingOf, pulse,
|
||||
ANIMATED, Braille, colormap, openArgsOf, VIEW_ROWS, commandsOf, DEFAULT, drain, Grid, lineChart, modelOf, phaseOf, picturesOf, pingOf, pulse,
|
||||
radarFan, rgb, settingsOf, shimmer, shownFrames, spectrumOf, spectrumWith, toBase64, viewOf, waterfall,
|
||||
} from '../mod/hooks/register.mjs';
|
||||
|
||||
@@ -150,7 +150,7 @@ test('radar view: fan, pulse and charts, with the honesty labels', () => {
|
||||
});
|
||||
|
||||
test('spectrum requests, merging across captures, and live commands', () => {
|
||||
assert.deepEqual(spectrumOf(120, 34), { bins: 114, frames: 44 });
|
||||
assert.deepEqual(spectrumOf(120, 34), { bins: 114, frames: 38 });
|
||||
assert.deepEqual(spectrumOf(10, 5), { bins: 24, frames: 12 });
|
||||
const c = commandsOf(settingsOf({ radarHost: 'kit.local' }), { live: true, spectrum: { bins: 64, frames: 48 } });
|
||||
assert.deepEqual(c.capture, ['esp32', '--seconds', '2', '--udp-port', '5005', '--json', '--spectrum', '--spectrum-bins', '64', '--spectrum-frames', '48']);
|
||||
@@ -167,3 +167,12 @@ test('an unfocused pane says how to give it the keys; a focused one does not', (
|
||||
assert.match(JSON.stringify(viewOf(UI, model, { ...base, focused: false })), /ctrl\+x tab to use the keys/);
|
||||
assert.doesNotMatch(JSON.stringify(viewOf(UI, model, { ...base, focused: true })), /ctrl\+x tab/);
|
||||
});
|
||||
|
||||
test('the pane opens as a dialog that takes the keys, sized to the view', () => {
|
||||
for (const mode of ['overview', 'waterfall', 'radar']) {
|
||||
const a = openArgsOf(mode);
|
||||
assert.deepEqual([a.id, a.focus, a.closeOnEscape, a.holdToasts], ['ruview-live', true, true, true], 'focus + closeOnEscape + holdToasts = a dialog that takes the keys');
|
||||
assert.equal(a.rows, VIEW_ROWS[mode]);
|
||||
}
|
||||
assert.ok(VIEW_ROWS.waterfall > VIEW_ROWS.overview, 'the live views ask for more room');
|
||||
});
|
||||
Reference in new issue
Block a user