Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions src/static/css/pad.css
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,22 @@ input {
.history-banner-date { opacity: 0.85; }
.history-banner #history-banner-return { margin-left: auto; }

/* The history iframe takes the place of the live ACE editor. We use the */
/* same positioning model (absolute, fill the editor area) so the page */
/* layout is identical between modes. */
/* The history iframe takes the place of the live ACE editor by occupying the
* same in-flow flex slot that #editorcontainer fills in live mode (flex: 1 1
* auto inside the row-flex #editorcontainerbox). This keeps the layout
* identical between modes — crucially including any in-flow side panels in
* #editorcontainerbox, such as ep_webrtc's video column, which now sit beside
* the history iframe exactly as they sit beside the live editor.
*
* This used to be an absolute, inset:0 overlay: it filled the editor area but
* took the iframe out of flow, so an in-flow side panel ended up hidden
* beneath it (see ether/ep_webrtc rendering nothing in the timeslider). */
.history-frame-mount {
display: none;
position: absolute;
inset: 0;
flex: 1 1 auto;
min-width: 0; /* allow the iframe to shrink to make room for a side panel */
border: 0;
background: var(--bg-color, #f2f3f4);
z-index: 4;
}
.history-frame-mount[hidden] { display: none; }
.history-frame-mount > iframe {
Expand All @@ -164,8 +170,14 @@ input {
/* editor, so we swap it for the history controls (slider + play/step */
/* buttons) that drive the iframe. The right-side menu (Settings / Share / */
/* Users / Chat / Home) stays fully interactive across modes. */
body.history-mode #editorcontainer { display: none; }
body.history-mode #editorcontainerbox { position: relative; }
/* The two-id `#editorcontainerbox #editorcontainer { display: flex }` layout
* rule (specificity 0-2-0) outranks a plain `body.history-mode #editorcontainer`
* (0-1-1), so match the same container path here to actually win the cascade
* and remove the live editor from flow. This was latent before: the old
* absolute, inset:0 iframe overlay painted over the still-in-flow editor, so
* nobody noticed it wasn't hidden. Now that the iframe sits in the normal
* flex flow, the editor must genuinely be gone or the two would split the row. */
body.history-mode #editorcontainerbox #editorcontainer { display: none; }
body.history-mode .history-frame-mount { display: block; }
body.history-mode #editbar .menu_left { display: none; }
body.history-mode #editbar .show-more-icon-btn { display: none; }
Expand Down
51 changes: 51 additions & 0 deletions src/tests/frontend-new/specs/padmode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,4 +345,55 @@ test.describe('in-pad history mode', () => {
await expect(page.locator('body.history-mode')).toHaveCount(0);
await expect(tbl.locator('.history-authors-row')).toHaveCount(0);
});

test('history iframe occupies the editor flex slot so side panels sit beside it', async ({page}) => {
await goToNewPad(page);
await clearPadContent(page);
await writeToPad(page, 'one');
await page.waitForTimeout(300);
await writeToPad(page, ' two');
await page.waitForTimeout(500);

// Simulate a plugin side panel mounted in #editorcontainerbox (e.g.
// ep_webrtc's #rtcbox video column): an in-flow flex item pinned left.
await page.evaluate(() => {
const box = document.getElementById('editorcontainerbox')!;
const panel = document.createElement('div');
panel.id = 'test-side-panel';
panel.style.cssText = 'display:flex;order:-1;flex:0 1 auto;width:120px';
panel.innerHTML = '<div style="width:120px;height:120px"></div>';
box.appendChild(panel);
});
const editorRight = await page.locator('#editorcontainer').evaluate(
(el) => Math.round(el.getBoundingClientRect().right));

await page.locator('.buttonicon-history').click();
await expect(page.locator('body.history-mode')).toBeVisible();
await page.locator('#history-frame-mount iframe').waitFor({state: 'attached'});

// 1) The live editor is genuinely removed from flow — not merely painted
// over. The two-id `#editorcontainerbox #editorcontainer { display:flex }`
// layout rule outranks a plain `body.history-mode #editorcontainer`, so
// the hide rule must match the same container path to win the cascade.
expect(await page.locator('#editorcontainer').evaluate(
(el) => getComputedStyle(el).display)).toBe('none');

// 2) The history iframe is in normal flow (not an absolute overlay)...
expect(await page.locator('#history-frame-mount').evaluate(
(el) => getComputedStyle(el).position)).not.toBe('absolute');

// 3) ...so it lays out beside the side panel instead of on top of it, and
// still fills the rest of the editor's old width.
const r = (sel: string) => page.locator(sel).evaluate((el) => {
const b = el.getBoundingClientRect();
return {left: b.left, right: b.right, top: b.top, bottom: b.bottom};
});
const panel = await r('#test-side-panel');
const frame = await r('#history-frame-mount');
const overlaps = !(frame.right <= panel.left || panel.right <= frame.left ||
frame.bottom <= panel.top || panel.bottom <= frame.top);
expect(overlaps).toBe(false);
expect(Math.round(frame.left)).toBeGreaterThanOrEqual(Math.round(panel.right) - 2);
expect(Math.round(frame.right)).toBeGreaterThanOrEqual(editorRight - 3);
});
});
Loading