mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
67f47dbac1
* fix(annotate): pre-release QA fixes for the armed-mode interaction seams
Six confirmed QA findings on the HTML/live annotate surface plus missing
pi-extension resync coverage:
1. Armed pinpoint drifted click (>4px, no selection) was swallowed AND
leaked to the page: the always-on drag work armed the trailing-click
suppression on drift alone. The mouseup arming site now requires the
drag to have actually produced a text selection; drifted clicks pin
normally and never reach the page. Bridge tests for armed drift,
armed real drag, and Interact drift.
2. Esc ladder: hover-clear is no longer its own rung; clearing the
pinpoint outline and posting annotate-exit happen on the same press
when no draft is open. Draft-close keeps its own press.
3. Compact touch layouts no longer apply a restored toolsHidden:true
chrome cookie (both header toggles are desktop-only, so applying it
stranded the user); the cookie value is preserved for desktop.
4. The live-app probe now announces the static-conversion downgrade on
stderr when a loopback probe fails, naming --app to force live mode.
5. Live-app export: page group headers are now '## Page:' with '### N.'
entries nested below them; exports without pageUrl stay byte-identical.
6. Shift+1-4 mode shortcuts no longer fire while the annotation
toolbar's type-to-comment listener owns printable keys, so typing
! @ # $ into a starting comment cannot silently switch modes.
Also adds the missing tests for the two resyncPhaseFromSession
executing->idle fallbacks that arm idleNoticePending (verified by
mutation: flipping either arm fails its test).
* fix(annotate): compact arm/disarm affordance, guarded shutdown, restored chrome guards
Follow-up scope from the forensics sweep, same surface:
- Compact touch layouts get Options-menu actions for the HTML/live
surface: 'Annotate page'/'Interact with page' (the desktop pen and
Mod+Shift+A were unreachable on touch, so every tap annotated with no
way out) and 'Show tools'/'Hide tools' (the desktop eye). With the
menu as the way back, the toolsHidden cookie now applies on compact
again (desktop parity) instead of being ignored.
- The annotate servers' stop() now guards every disposal step
individually (Bun: runGuardedShutdown, mirrored inline in Pi): a
throwing agent-terminal teardown (#1314-class) no longer skips
liveProxy.stop() and the other disposals after it. Unit-tested with a
throwing disposer.
- Re-added the two regression guards dropped in the htmlHideTools ->
htmlChrome test rename: the restore commit never writes stale
pre-restore chrome values to the cookie, and the sidebar stays
reachable via Mod+B while tools are hidden.
* fix(annotate): scope the Agent TUI display reset to display settings only
The Display popover's 'Reset terminal display settings' button also called
onSideChange('left'), durably overwriting a user's chosen right/hidden
placement in config.json with no disclosure — the label scopes the reset
to font/appearance. Position is a layout preference with its own explicit
segmented control right below, so the reset no longer touches it: the
button now resets exactly the display settings through the panel's one
sanitized update path, and the popover no longer has any code path from
reset to the side.
AgentTerminalDisplayPopover is now exported with a defaultOpen test seam
(the surrounding panel needs a live WebTUI session to render it); tests
assert reset restores the display defaults without firing onSideChange,
and that the Position control remains the explicit way to change
placement.
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test';
|
|
import React, { act } from 'react';
|
|
import { createRoot, type Root } from 'react-dom/client';
|
|
import {
|
|
AgentTerminalDisplayPopover,
|
|
DEFAULT_DISPLAY_SETTINGS,
|
|
type AgentTerminalDisplaySettings,
|
|
} from './AnnotateAgentTerminalPanel';
|
|
import type { AnnotateAgentTerminalSide } from '@plannotator/ui/utils/annotateAgentTerminal';
|
|
|
|
/**
|
|
* The Display popover's reset button (DOM-gated).
|
|
*
|
|
* "Reset terminal display settings" must do exactly what its label says:
|
|
* reset the DISPLAY settings. Position (left/right/hidden) is a durable
|
|
* layout preference persisted to config.json through onSideChange — the
|
|
* regression this guards is the reset button also firing onSideChange("left"),
|
|
* silently overwriting a user's chosen right/hidden placement with no
|
|
* disclosure. The Position segmented control stays the one explicit way to
|
|
* change placement.
|
|
*/
|
|
|
|
const hasDom = typeof document !== 'undefined';
|
|
let root: Root | null = null;
|
|
let host: HTMLElement | null = null;
|
|
|
|
afterEach(async () => {
|
|
if (root) await act(async () => root?.unmount());
|
|
root = null;
|
|
host?.remove();
|
|
host = null;
|
|
if (hasDom) document.body.replaceChildren();
|
|
});
|
|
|
|
const NON_DEFAULT_SETTINGS: AgentTerminalDisplaySettings = {
|
|
fontFamily: 'geist',
|
|
fontSize: 18,
|
|
fontWeight: 'light',
|
|
lineHeight: 1.35,
|
|
};
|
|
|
|
async function mountPopover(options: {
|
|
side: AnnotateAgentTerminalSide;
|
|
onChange: (updates: Partial<AgentTerminalDisplaySettings>) => void;
|
|
onSideChange: (side: AnnotateAgentTerminalSide) => void;
|
|
}): Promise<void> {
|
|
host = document.createElement('div');
|
|
document.body.appendChild(host);
|
|
root = createRoot(host);
|
|
await act(async () => {
|
|
root?.render(
|
|
<AgentTerminalDisplayPopover
|
|
settings={NON_DEFAULT_SETTINGS}
|
|
side={options.side}
|
|
onChange={options.onChange}
|
|
onSideChange={options.onSideChange}
|
|
defaultOpen
|
|
/>,
|
|
);
|
|
});
|
|
}
|
|
|
|
function resetButton(): HTMLButtonElement {
|
|
const button = document.querySelector<HTMLButtonElement>(
|
|
'button[aria-label="Reset terminal display settings"]',
|
|
);
|
|
if (!button) throw new Error('reset button missing (popover did not open)');
|
|
return button;
|
|
}
|
|
|
|
describe.if(hasDom)('agent terminal Display popover reset', () => {
|
|
test('reset restores the display defaults and leaves the side/placement untouched', async () => {
|
|
const changes: Array<Partial<AgentTerminalDisplaySettings>> = [];
|
|
const sideChanges: AnnotateAgentTerminalSide[] = [];
|
|
await mountPopover({
|
|
side: 'right',
|
|
onChange: (updates) => changes.push(updates),
|
|
onSideChange: (side) => sideChanges.push(side),
|
|
});
|
|
|
|
await act(async () => resetButton().click());
|
|
|
|
// Display settings reset to the defaults through the panel's one
|
|
// sanitized update path...
|
|
expect(changes).toEqual([DEFAULT_DISPLAY_SETTINGS]);
|
|
// ...and the durable placement preference is NOT touched: a user who
|
|
// chose "right" (or "hidden") keeps it.
|
|
expect(sideChanges).toEqual([]);
|
|
});
|
|
|
|
test('the Position control stays the explicit way to change placement', async () => {
|
|
const sideChanges: AnnotateAgentTerminalSide[] = [];
|
|
await mountPopover({
|
|
side: 'left',
|
|
onChange: () => {},
|
|
onSideChange: (side) => sideChanges.push(side),
|
|
});
|
|
|
|
const rightOption = Array.from(document.querySelectorAll('button'))
|
|
.find((button) => button.textContent?.trim() === 'Right');
|
|
if (!rightOption) throw new Error('Position "Right" option missing');
|
|
await act(async () => rightOption.click());
|
|
|
|
expect(sideChanges).toEqual(['right']);
|
|
});
|
|
});
|