mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
Two preview-time bugs surfaced ahead of 0.8.0: 1. Pitch-deck example (#2215): the official html-ppt-pitch-deck prompt asked the agent to confirm three facts first, but the manifest had no structured `od.inputs`, so the platform's required-input gate had no fields to enforce and the run could publish HTML that still contained unresolved fundraising placeholders (`Name to confirm`, `$X.XM`, `Replace this panel with`, ...). Add structured required inputs to the manifest and a daemon-side publication guard that rejects HTML/deck artifact writes whose body still contains those placeholders. Scope is the file-write boundary only (no assistant-text scanning), so the guard cannot trip on the agent's chat prose mid-clarification. 2. Framework deck preview off-screen: `injectDeckBridge` injected `place-content: center !important` on `.deck-shell` for every deck-mode srcdoc, which forced the framework's `display: grid` shell to re-center its implicit track. The framework's `fit()` already centers a `transform-origin: top left` stage with an explicit `translate(tx, ty)` that assumes the stage's natural layout position is (0, 0); the two centerings stacked and the scaled stage landed ~1000px off-screen, so the preview showed a sliver of slide content in the top-left with the rest black. Skip the override when the framework's `id="deck-stage"` marker is in the doc, and drop the dead `display: grid; place-items: center` from the deck framework template so future drift can't re-introduce the same stack.
93 lines
4.3 KiB
TypeScript
93 lines
4.3 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import { buildSrcdoc } from '../../src/runtime/srcdoc';
|
|
|
|
// Regression coverage for the "deck-stage shows a sliver of content in the
|
|
// top-left with the rest of the preview black" symptom. Root cause: the
|
|
// srcdoc deck bridge injected `place-content: center !important` on
|
|
// `.stage, .deck-stage, .deck-shell` for ALL deck-mode artifacts, even
|
|
// framework decks (DECK_SKELETON_HTML in apps/daemon/src/prompts/
|
|
// deck-framework.ts) whose `fit()` already centers a `transform-origin:
|
|
// top left` stage with an explicit `translate(tx, ty)` that assumes the
|
|
// stage's natural layout position is (0, 0). Forcing place-content on
|
|
// the shell re-centered the implicit grid track, doubled the offset, and
|
|
// pushed the scaled stage off-screen.
|
|
//
|
|
// The fix: detect the framework deck via its `id="deck-stage"` marker and
|
|
// skip the `data-od-deck-fix` styleFix for it. Legacy / non-framework
|
|
// decks that authored their own `.stage` grid still get the override.
|
|
|
|
function frameworkDeckHtml(): string {
|
|
return [
|
|
'<!doctype html><html><head><style>',
|
|
'.deck-shell { position: fixed; inset: 0; overflow: hidden; }',
|
|
'.deck-stage { width: 1920px; height: 1080px; position: relative; transform-origin: top left; }',
|
|
'.slide { position: absolute; inset: 0; }',
|
|
'.slide:not(.active) { display: none !important; }',
|
|
'</style></head><body>',
|
|
'<div class="deck-shell">',
|
|
' <div class="deck-stage" id="deck-stage">',
|
|
' <section class="slide active">slide 1</section>',
|
|
' <section class="slide">slide 2</section>',
|
|
' </div>',
|
|
'</div>',
|
|
'<script>(function(){ var stage = document.getElementById(\'deck-stage\'); /* fit() ... */ })();</script>',
|
|
'</body></html>',
|
|
].join('\n');
|
|
}
|
|
|
|
function legacyDeckHtml(): string {
|
|
return [
|
|
'<!doctype html><html><head><style>',
|
|
// A common authoring shape: `.stage` is the grid container with no
|
|
// explicit fit() function. This is exactly what the deck-fix style
|
|
// was designed for.
|
|
'.stage { display: grid; place-items: center; width: 100vw; height: 100vh; overflow: hidden; }',
|
|
'.canvas { width: 1920px; height: 1080px; transform-origin: center center; }',
|
|
'.slide { display: none; }',
|
|
'.slide.is-active { display: block; }',
|
|
'</style></head><body>',
|
|
'<div class="stage">',
|
|
' <div class="canvas">',
|
|
' <section class="slide is-active">slide 1</section>',
|
|
' <section class="slide">slide 2</section>',
|
|
' </div>',
|
|
'</div>',
|
|
'</body></html>',
|
|
].join('\n');
|
|
}
|
|
|
|
describe('injectDeckBridge — framework-deck detection (#deck-stage)', () => {
|
|
it('skips the place-content fix when the deck carries the framework #deck-stage marker', () => {
|
|
const out = buildSrcdoc(frameworkDeckHtml(), { deck: true });
|
|
expect(out).not.toMatch(/<style[^>]*data-od-deck-fix/);
|
|
expect(out).not.toContain('place-content: center !important');
|
|
// The bridge script itself must still ship — the framework's own
|
|
// fit() handles centering, but the host-side counter / keyboard
|
|
// bridge still needs the slide-state postMessage channel.
|
|
expect(out).toMatch(/<script[^>]*data-od-deck-bridge/);
|
|
});
|
|
|
|
it('keeps injecting the place-content fix for legacy / non-framework decks', () => {
|
|
const out = buildSrcdoc(legacyDeckHtml(), { deck: true });
|
|
expect(out).toMatch(/<style[^>]*data-od-deck-fix/);
|
|
expect(out).toContain('.stage, .deck-stage, .deck-shell { place-content: center !important; }');
|
|
expect(out).toMatch(/<script[^>]*data-od-deck-bridge/);
|
|
});
|
|
|
|
it('skips the fix when #deck-stage uses single quotes, extra whitespace, or uppercase ID syntax', () => {
|
|
// The detector should match the framework's emit shape but also
|
|
// tolerate the minor formatting variations that DOMParser /
|
|
// serializeHtmlDocument introduce in the middle of the pipeline.
|
|
const variants = [
|
|
`<div class="deck-stage" id='deck-stage'></div>`,
|
|
`<div class="deck-stage" ID = "deck-stage"></div>`,
|
|
`<div class="deck-stage" id = 'deck-stage'></div>`,
|
|
];
|
|
for (const variant of variants) {
|
|
const out = buildSrcdoc(`<!doctype html><html><body>${variant}</body></html>`, { deck: true });
|
|
expect(out, `variant ${JSON.stringify(variant)}`).not.toContain('data-od-deck-fix');
|
|
}
|
|
});
|
|
});
|