feat: add highlighted protocol views and inline operation forms
Reference site / build (push) Canceled after 0s

Expose Rust Text View tokens and safely highlight signatures and JSON results. Add animated Interface and inline Run disclosures with a shared 10ms line cadence, typed argument inputs, and explicit execution.

Update documentation and browser regressions for the new controls and explicit counter-value responses.
This commit is contained in:
2026-10-26 10:12:22 +09:00
parent 41e88e3c40
commit db4c8ba97f
21 changed files with 1453 additions and 129 deletions
+57 -17
View File
@@ -14,7 +14,7 @@ async function descriptor(page: Page) {
await expect(page.getByLabel('Object Text View')).toContainText('object {');
await page.locator('[data-interface-toggle]').first().click();
await expect(page.getByLabel('Interface Text View')).toContainText('interface');
await page.getByLabel('Operation', { exact: true }).selectOption('read');
await page.getByRole('button', { name: 'Run read', exact: true }).click();
}
async function traceObserves(page: Page) {
@@ -60,6 +60,39 @@ for (const [route, path, name] of [
});
}
for (const colorScheme of ['light', 'dark'] as const) {
test(`Interface buttons use a text-width TUI background in ${colorScheme} mode`, async ({ page }, info) => {
await page.emulateMedia({ colorScheme });
await page.goto(protocolURL(info.project.use.baseURL!, 'playground/')); await ready(page);
const button = page.getByRole('button', { name: '? /::collection', exact: true });
const toggle = page.locator('[data-interface-toggle]').filter({ hasText: '/::collection' });
await expect(button).toBeVisible();
const background = await button.evaluate(element => {
const probe = document.createElement('span'); probe.style.backgroundColor = 'var(--code-bg)';
element.append(probe); const color = getComputedStyle(probe).backgroundColor; probe.remove(); return color;
});
expect(background).not.toBe('rgba(0, 0, 0, 0)');
for (const state of ['?', 'v', '>']) {
await page.mouse.move(0, 0);
await page.getByRole('button', { name: 'Read', exact: true }).focus();
await expect(toggle).toHaveText(`${state} /::collection`);
await expect(toggle).toHaveCSS('background-color', background);
await expect(toggle).toHaveCSS('border-radius', '0px');
await expect(toggle).toHaveCSS('border-top-width', '0px');
await expect(toggle).toHaveCSS('padding-top', '0px');
expect(await toggle.evaluate(element => element.getBoundingClientRect().width < element.parentElement!.getBoundingClientRect().width)).toBe(true);
await toggle.hover();
await expect(toggle).not.toHaveCSS('background-color', background);
if (state !== '>') await toggle.click();
}
await page.mouse.move(0, 0); await toggle.focus(); await page.keyboard.press('Enter');
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
await expect(toggle).toHaveCSS('outline-width', '2px');
await expect(page.getByLabel('Interface Text View').filter({ visible: true })).toContainText('/::collection');
await page.screenshot({ path: info.outputPath(`interface-background-${colorScheme}.png`), fullPage: true });
});
}
test('Object selection shows an unboxed Text View without Observe forms or step panels', async ({ page }, info) => {
await page.goto(protocolURL(info.project.use.baseURL!, 'sources/')); await ready(page);
const object = page.getByLabel('Object Text View');
@@ -70,16 +103,20 @@ test('Object selection shows an unboxed Text View without Observe forms or step
await expect(object).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
await expect(panel(page).locator('fieldset, legend, h2, h3, [data-protocol-path], [data-protocol-depth], [data-protocol-observe], [data-protocol-stats], [data-protocol-children]')).toHaveCount(0);
await expect(page.locator('[data-protocol-status]')).toBeHidden();
await expect(page.locator('[data-protocol-operation-controls]')).toBeHidden();
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-operation-form], [data-protocol-call]')).toHaveCount(0);
await expect(page.getByLabel('Operation result')).toBeHidden();
await expect(page.getByRole('button', { name: '? /::document', exact: true })).toBeVisible();
await page.screenshot({ path: info.outputPath('protocol-object.png'), fullPage: true });
await page.getByRole('button', { name: '? /::document', exact: true }).click();
await expect(page.getByLabel('Interface Text View')).toBeVisible();
await expect(page.locator('[data-protocol-operation-controls]')).toBeVisible();
await expect(page.getByRole('button', { name: 'Run read', exact: true })).toBeEnabled();
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-protocol-call]')).toHaveCount(0);
await page.getByRole('button', { name: 'Run read', exact: true }).click();
await expect(page.getByRole('form', { name: 'read arguments', exact: true })).toHaveAttribute('data-operation-form', 'read');
await expect(page.getByRole('button', { name: 'Execute read', exact: true })).toBeEnabled();
await expect(page.getByLabel('Operation result')).toBeHidden();
await page.getByRole('button', { name: 'v /::document', exact: true }).click();
await expect(page.locator('[data-protocol-operation-controls]')).toBeHidden();
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-operation-form], [data-protocol-call]')).toHaveCount(0);
await expect(object).toBeVisible();
});
@@ -96,12 +133,12 @@ test('tree selection automatically observes Depth 1 only, including root, nested
await expect(page.getByLabel('Interface Text View')).toBeHidden();
await expect(panel(page)).toHaveAttribute('data-interfaces', '0');
await expect(panel(page)).toHaveAttribute('data-calls', '0');
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
expected.push({ path: canonicalPath, depth: 1 });
expect(await observedCalls(page)).toEqual(expected);
}
await page.locator('[data-interface-toggle]').first().click();
await page.getByLabel('Operation', { exact: true }).selectOption('read');
await page.getByRole('button', { name: 'Run read', exact: true }).click();
await page.locator('[data-protocol-call]').click();
await expect(panel(page)).toHaveAttribute('data-calls', '1');
// Selecting the current Object resets the previous Interface and result.
@@ -167,12 +204,12 @@ test('Interface clicks fetch unknown signatures and fold/reopen acquired ones wi
const signature = await view.textContent();
await expect(panel(page)).toHaveAttribute('data-interfaces', '1');
await expect(panel(page)).toHaveAttribute('data-calls', '0');
await expect(page.getByLabel('Operation', { exact: true })).toBeEnabled();
await expect(page.getByRole('button', { name: 'Run read', exact: true })).toBeEnabled();
await toggle.focus(); await page.keyboard.press('Space');
await expect(toggle).toHaveText('> /::document');
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(view).toBeHidden();
await expect(page.getByLabel('Operation', { exact: true })).toBeDisabled();
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
await toggle.click();
await expect(view).toHaveText(signature!);
await expect(panel(page)).toHaveAttribute('data-interfaces', '1');
@@ -201,21 +238,21 @@ test('failed Interface acquisition remains unknown and cannot retain a previous
await page.goto(protocolURL(info.project.use.baseURL!)); await ready(page);
await page.locator('.demo-node[data-path="/"]').click();
await page.getByRole('button', { name: '? /::document', exact: true }).click();
await page.getByLabel('Operation', { exact: true }).selectOption('read');
await page.getByRole('button', { name: 'Run read', exact: true }).click();
const missing = page.getByRole('button', { name: `? /::${missingName}`, exact: true });
await missing.click();
await expect(missing).toHaveAttribute('aria-expanded', 'false');
await expect(page.getByRole('button', { name: '> /::document', exact: true })).toBeVisible();
await expect(page.locator('[data-protocol-status]')).toContainText('protocol');
await expect(page.locator('[data-interface-view]:visible')).toHaveCount(0);
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
await expect(page.getByLabel('Operation result')).toBeHidden();
await expect(panel(page)).toHaveAttribute('data-calls', '0');
await expect(page.locator('[data-protocol-interfaces] img')).toHaveCount(0);
expect(await page.evaluate(() => '__interfaceExecuted' in window)).toBe(false);
await page.getByRole('button', { name: '> /::document', exact: true }).click();
await expect(page.locator('[data-interface-view]:visible')).toContainText('interface "/::document"');
await expect(page.getByLabel('Operation', { exact: true })).toBeEnabled();
await expect(page.getByRole('button', { name: 'Run read', exact: true })).toBeEnabled();
});
test('Protocol observes automatically but acquires Interfaces and calls Operations only explicitly', async ({ page }, info) => {
@@ -283,7 +320,7 @@ test('selection, history, reload and mode switches never replay an operation', a
await ready(page);
await expect(panel(page)).toHaveAttribute('data-calls', '1');
await expect(page.getByLabel('Operation result')).toBeHidden();
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
}
await page.goBack(); await ready(page);
await expect(panel(page)).toHaveAttribute('data-path', '/sources');
@@ -400,22 +437,25 @@ test('input and protocol errors remain visible, and tree selection clears stale
await page.locator('.demo-node[data-path="/sources"]').click(); await ready(page);
await expect(page.locator('[data-protocol-status]')).toBeVisible();
await expect(page.locator('[data-protocol-status]')).toContainText('protocol');
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
await expect(page.getByLabel('Object Text View')).toBeHidden();
await page.locator('.demo-node[data-path="/"]').click();
await expect(panel(page)).toHaveAttribute('data-path', '/');
await ready(page);
await descriptor(page);
await page.locator('[data-protocol-arguments]').fill('{broken');
await page.locator('.demo-node[data-path="/playground"]').click(); await ready(page);
await page.locator('.demo-node[data-path="/playground/apple"]').click(); await ready(page);
await expect(panel(page)).toHaveAttribute('data-path', '/playground/apple');
await page.getByRole('button', { name: '? /::counter', exact: true }).click();
await page.getByRole('button', { name: 'Run increment', exact: true }).click();
await page.getByLabel('increment.amount', { exact: true }).fill('broken');
await page.locator('[data-protocol-call]').click();
await expect(page.locator('[data-protocol-status]')).toBeVisible();
await expect(page.locator('[data-protocol-status]')).toContainText('input');
await expect(panel(page)).toHaveAttribute('data-calls', '0');
await page.locator('.demo-node[data-path="/book"]').click(); await ready(page);
await expect(page.getByLabel('Object Text View')).toContainText('name: "book"');
await expect(page.locator('[data-protocol-operation-controls]')).toBeHidden();
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-operation-form], [data-protocol-call]')).toHaveCount(0);
await expect(page.locator('[data-protocol-status]')).toBeHidden();
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
});
test('Text View and returned markup are displayed as text, including in dark mode', async ({ page }, info) => {