Skip to content

fix(desktop): preserve artifact dark theme in iframe#77

Merged
hqhq1025 merged 5 commits intomainfrom
wt/loop-fix-iframe-darkbg
Apr 19, 2026
Merged

fix(desktop): preserve artifact dark theme in iframe#77
hqhq1025 merged 5 commits intomainfrom
wt/loop-fix-iframe-darkbg

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

Summary

  • 用户做 dark dashboard 时 iframe 外层 cream/white bg 透出,导致 dark artifact 看起来变浅。
  • 改 iframe 外层 bg-transparent + buildSrcdoc 注入 low-specificity baseline white html, body { background },artifact 自己的 body bg 通过 cascade order 胜出。
  • 同时新增 vitest 覆盖:dark body bg 仍然在 baseline 之后;fragment / 缺 head 的 html 也注入 baseline。

Root cause

PreviewPane 给 iframe 元素设了 bg-[var(--color-artifact-bg)](白)。当 artifact body 没有显式 background 时,iframe body 透明 → iframe 元素 bg 显示。Dark artifacts 通常只把 bg 设在内层容器(如 bg-gray-900),body 仍透明,于是看起来浅。

Fix

  • packages/runtime/src/index.ts — 在 srcdoc head 最前面注入 baseline <style>html,body{background:#ffffff;...}</style>,artifact 自己的 body bg 依然是 last-wins。
  • apps/desktop/src/renderer/src/components/PreviewPane.tsx — iframe className 由 bg-[var(--color-artifact-bg)] 改为 bg-transparent,让内部 body bg 决定可见颜色。

Compat / upgradeability / no bloat / elegance

  • Compatibility: 不动 schema、不动 IPC、不动 token;仅 srcdoc HTML 内容。
  • Upgradeability: baseline style 与 overlay 同居 buildSrcdoc,未来可抽 token。
  • No bloat: 0 新依赖。
  • Elegance: 单一改动点,buildSrcdoc 仍是唯一 srcdoc 装配处。

Test plan

  • pnpm --filter @open-codesign/runtime exec vitest run (6 passed)
  • pnpm typecheck (clean)
  • 手动:用 dark dashboard prompt 生成,确认 iframe 内显示深色而非 cream/white

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Baseline style injection misses valid <body> documents without <html>/<head> — after switching iframe background to transparent, these artifacts will not receive the fallback background, causing inconsistent preview surfaces. Evidence: packages/runtime/src/index.ts:31
    Suggested fix:

    const withBaseline = /<head[^>]*>/i.test(stripped)
      ? stripped.replace(/<head[^>]*>/i, (match) => `${match}${BASELINE_STYLE}`)
      : /<html[^>]*>/i.test(stripped)
        ? stripped.replace(/<html[^>]*>/i, (match) => `${match}<head>${BASELINE_STYLE}</head>`)
        : stripped.replace(/<body[^>]*>/i, (match) => `<head>${BASELINE_STYLE}</head>${match}`);
  • [Minor] Hardcoded color violates the UI token constraint — #ffffff is newly introduced in runtime preview styling, which conflicts with the “UI values via packages/ui tokens” rule. Evidence: packages/runtime/src/index.ts:15
    Suggested fix:

    const BASELINE_STYLE =
      '<style>html,body{margin:0;padding:0;background:var(--color-artifact-bg);min-height:100%;}</style>';

Summary

  • Review mode: initial
  • 2 issues found in changed lines: one preview correctness regression path and one hard-constraint violation.
  • docs/VISION.md and docs/PRINCIPLES.md: Not found in repo/docs.

Testing

  • Not run (automation): pnpm is unavailable in this runner, so Vitest/Playwright could not be executed here.

open-codesign Bot

Comment thread packages/runtime/src/index.ts Outdated
return stripped.replace(
const withBaseline = /<head[^>]*>/i.test(stripped)
? stripped.replace(/<head[^>]*>/i, (match) => `${match}${BASELINE_STYLE}`)
: stripped.replace(/<html[^>]*>/i, (match) => `${match}<head>${BASELINE_STYLE}</head>`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</body>-based input can still be missing both <head> and <html>. In that case this branch currently injects nothing, so the new baseline is skipped. Consider adding a <body> fallback branch so transparent iframe mode stays deterministic.

Comment thread packages/runtime/src/index.ts Outdated
* artifact wins via cascade order.
*/
const BASELINE_STYLE =
'<style>html,body{margin:0;padding:0;background:#ffffff;min-height:100%;}</style>';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a hardcoded color (#ffffff). Project constraints require UI values to come from tokens; please switch to a tokenized CSS variable here.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Hardcoded color token bypass in runtime baseline style — this introduces a new raw #ffffff, violating the project constraint that UI values must come from shared tokens and risking token drift across themes. Evidence: packages/runtime/src/index.ts:15
    Suggested fix:

    const BASELINE_STYLE =
      '<style>html,body{margin:0;padding:0;background:var(--color-artifact-bg, #ffffff);min-height:100%;}</style>';
  • [Blocker] Missing DCO sign-off in new commits — repository policy requires Signed-off-by on commits, and the current PR commits do not include it (checked commit messages at head).
    Suggested fix:

    git commit --amend -s
    # repeat for each unsigned commit (or rebase -i with reword)
    git push --force-with-lease

Summary

  • Review mode: follow-up after new commits
  • 2 issues remain: 1 code-level hard-constraint violation in changed lines and 1 PR-level policy blocker (DCO).
  • docs/VISION.md and docs/PRINCIPLES.md: Not found in repo/docs.

Testing

  • Not run (automation)

open-codesign Bot

Comment thread packages/runtime/src/index.ts Outdated
* artifact wins via cascade order.
*/
const BASELINE_STYLE =
'<style>html,body{margin:0;padding:0;background:#ffffff;min-height:100%;}</style>';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded #ffffff violates the project token constraint and can drift from themed surfaces. Please use a tokenized value (for example var(--color-artifact-bg, #ffffff)) in BASELINE_STYLE.

hqhq1025 added a commit that referenced this pull request Apr 19, 2026
The BASELINE_STYLE injected into the preview iframe used a hardcoded #ffffff,
which violates the project rule that UI values must come from shared design
tokens. Switch to var(--color-artifact-bg, #ffffff) so the canonical token
in packages/ui is the source of truth, with #ffffff retained as a safe
fallback for sandboxed iframes that don't inherit the parent's CSS vars.

Codex review feedback on PR #77.
@hqhq1025 hqhq1025 force-pushed the wt/loop-fix-iframe-darkbg branch from 91c7f78 to 81177fa Compare April 19, 2026 07:46
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Addressed Codex [Major] finding on packages/runtime/src/index.ts:15: replaced the raw #ffffff in BASELINE_STYLE with var(--color-artifact-bg, #ffffff), sourcing the color from the canonical token in packages/ui/src/tokens.css. The literal is retained only as a sandbox fallback (parent CSS vars don't propagate into the iframe's allow-scripts-only sandbox). Tests updated and green; lint clean for touched files. Commit: 81177fa.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Malformed body reconstruction when input has </body> but no <body> opener — this branch wraps the original HTML inside a new <body>...</body> without removing the existing closing tag, yielding invalid structure and potentially placing the overlay script outside the intended body parsing flow. Evidence: packages/runtime/src/index.ts:47, packages/runtime/src/index.ts:49
    Suggested fix:

    } else {
      const bodyContent = stripped.replace(/<\/body\s*>/gi, '');
      withBaseline = `<!doctype html><html><head>${BASELINE_STYLE}</head><body>${bodyContent}</body></html>`;
    }
  • [Blocker] Missing DCO sign-off in new commits — hard constraint requires Signed-off-by on each commit, and the new commits at 1bd8c6197d87ebe40a0cefabb039d0854899e195, 433521938d2a61dd7971ae8b82d101bb4c4a5b6f, and 81177faa8ce82e7f9afb65eee98f3c16fdde97d6 do not include it.
    Suggested fix:

    git rebase -i --signoff 91c7f78cb5adf13c1f5abab982d5820f4115b1a8
    git push --force-with-lease

Summary

  • Review mode: follow-up after new commits
  • 2 issues found: 1 code-level correctness risk in new buildSrcdoc branch and 1 PR policy blocker (DCO).
  • docs/VISION.md and docs/PRINCIPLES.md: Not found in repo/docs.

Testing

  • Not run (automation)

open-codesign Bot

Comment thread packages/runtime/src/index.ts Outdated
(match) => `<html><head>${BASELINE_STYLE}</head>${match}`,
)}</html>`;
} else {
withBaseline = `<!doctype html><html><head>${BASELINE_STYLE}</head><body>${stripped}</body></html>`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping the entire stripped payload here can produce </body></body> when input has an orphan closing body tag. That creates malformed structure and can move the injected overlay script outside the intended body context. Consider stripping orphan </body> tags before wrapping.

hqhq1025 added a commit that referenced this pull request Apr 19, 2026
The BASELINE_STYLE injected into the preview iframe used a hardcoded #ffffff,
which violates the project rule that UI values must come from shared design
tokens. Switch to var(--color-artifact-bg, #ffffff) so the canonical token
in packages/ui is the source of truth, with #ffffff retained as a safe
fallback for sandboxed iframes that don't inherit the parent's CSS vars.

Codex review feedback on PR #77.
@hqhq1025 hqhq1025 force-pushed the wt/loop-fix-iframe-darkbg branch from 81177fa to f7035f6 Compare April 19, 2026 08:06
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Addressed Codex [Major] on malformed body reconstruction.

Refactored buildSrcdoc to run a dedicated normalizeBodyTags pass that explicitly handles all 4 input shapes before baseline/overlay injection:

  1. <body> + </body> -> leave structure alone
  2. <body> only -> append </body>
  3. </body> only -> insert <body> opener (after </head> / <html> if present, else at start)
  4. neither -> wrap content in <body>...</body> (after </head> / <html> if present)

Added 5 new unit tests under describe('body tag shape normalization') covering each shape plus the </body>-only-with-head sub-case. Full suite: 16/16 green via pnpm --filter @open-codesign/runtime test.

Commit: f7035f6

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Blocker] Missing DCO Signed-off-by in new commits — repository hard constraints require DCO sign-off, but the commits in this PR head (b23c9a756501f6dd07fb5041120fb6e34bd1a431, 7f28ffb088d80b4a69f9af87f4f42c06abbd1632, 52f346a0ae8435679a564fdc37928b1e0aeb2cbb, f7035f651d2ef5656b130e36df77fd54413e42ea) do not include Signed-off-by trailers.
    Suggested fix:

    git rebase -i --signoff <base-before-these-commits>
    git push --force-with-lease
  • [Major] normalizeBodyTags appends </body> after </html> for several malformed shapes, producing crossed tags and risking overlay/script injection outside the intended body context, evidence packages/runtime/src/index.ts:37, packages/runtime/src/index.ts:52, packages/runtime/src/index.ts:55.
    Suggested fix:

    function closeBody(html: string): string {
      return /<\/html\s*>/i.test(html)
        ? html.replace(/<\/html\s*>/i, '</body></html>')
        : `${html}</body>`;
    }
    
    if (hasOpen && !hasClose) return closeBody(html);
    
    if (!hasOpen && !hasClose) {
      if (HEAD_CLOSE_RE.test(html)) return closeBody(html.replace(HEAD_CLOSE_RE, (m) => `${m}<body>`));
      if (HTML_RE.test(html)) return closeBody(html.replace(HTML_RE, (m) => `${m}<body>`));
      return `<body>${html}</body>`;
    }

Summary

  • Review mode: follow-up after new commits
  • 2 issues found (1 blocker policy issue, 1 major correctness issue).
  • docs/VISION.md and docs/PRINCIPLES.md: Not found in repo/docs.

Testing

  • Not run (automation): pnpm is unavailable in this runner environment.

open-codesign Bot

Comment thread packages/runtime/src/index.ts Outdated
if (hasOpen && hasClose) return html;

if (hasOpen && !hasClose) {
return `${html}</body>`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When <body> is opened but missing its closer, this branch appends </body> at EOF. If input already ends with </html>, output becomes ...</html></body> (crossed tags). Please insert the closer before </html> when present, otherwise append at EOF.

When a model produced a dark-theme artifact, the iframe's outer white
artifact-bg token bled through any artifact whose body background was
unset, making dark dashboards look washed out. Make the iframe element
transparent and inject a low-specificity baseline white html/body
background inside the srcdoc so artifacts that explicitly set a dark
body { background } now win via cascade order.
…docs

buildSrcdoc previously only synthesised a <head> when the artifact had an
<html> wrapper. A valid document containing only <body>...</body> (or a
fragment with </body> but no <html>) silently skipped baseline injection,
because the regex replace on <html> matched nothing.

Handle four document shapes explicitly:
- has <head>: inject inside head (unchanged)
- has <html> but no <head>: synth <head> after <html> (unchanged)
- has <body> but no <html>: wrap with <html><head>...</head> + close </html>
- has </body> but no <body> opener: rebuild as full doctype document

Add vitest cases for body-only docs, plain fragments, and full docs with
<head> to lock the four branches in.
The BASELINE_STYLE injected into the preview iframe used a hardcoded #ffffff,
which violates the project rule that UI values must come from shared design
tokens. Switch to var(--color-artifact-bg, #ffffff) so the canonical token
in packages/ui is the source of truth, with #ffffff retained as a safe
fallback for sandboxed iframes that don't inherit the parent's CSS vars.

Codex review feedback on PR #77.
Codex flagged a malformed reconstruction when input had </body> but no <body>: the prior cascade fell through to the bare-fragment branch, wrapping content that already ended with </body> inside another <body>...</body>, producing nested/crossed tags.

Refactor into a single normalizeBodyTags step that explicitly handles all four shapes (both, opener-only, closer-only, neither), then runs baseline + overlay injection on the normalized document. Adds unit tests for each shape.
@hqhq1025 hqhq1025 force-pushed the wt/loop-fix-iframe-darkbg branch from f7035f6 to 3f48024 Compare April 19, 2026 08:20
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Addressed Codex [Major] on packages/runtime/src/index.ts re: </body> appended after </html> in malformed shapes. Extracted a closeBody helper that inserts </body> before </html> when present, falling back to append otherwise. Applied to all branches that previously did naive ${...}</body> concatenation.

New tests cover:

  • <body> only + </html> -> </body> lands before </html>
  • <html>...</html> shell, no body tags -> <body> after <html>, </body> before </html>
  • </head> + </html>, no body tags -> <body> after </head>, </body> before </html>

Commit: 3f48024. All 17 runtime tests pass. Also rebased onto latest origin/main.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Blocker] Missing DCO Signed-off-by trailer on the new follow-up commit — CI/policy requires DCO for every commit, and the latest head commit 3f48024ef0f9d57a7b1fbacad16de18c78e84f11 does not include a Signed-off-by: trailer in its commit message metadata.
    Suggested fix:
    git commit --amend --signoff
    git push --force-with-lease

Summary

  • Review mode: follow-up after new commits
  • 1 issue found (policy blocker).
  • docs/VISION.md and docs/PRINCIPLES.md: Not found in repo/docs.

Testing

  • Not run (automation)

open-codesign Bot

@hqhq1025 hqhq1025 merged commit b49dfca into main Apr 19, 2026
4 of 6 checks passed
@hqhq1025 hqhq1025 deleted the wt/loop-fix-iframe-darkbg branch April 19, 2026 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant