Skip to content

feat(XYZ-27): use native Bun JSX/TSX for ERC-8004 register HTML generation#214

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/use-txs-and-jsx-directly-xyz-27
Draft

feat(XYZ-27): use native Bun JSX/TSX for ERC-8004 register HTML generation#214
Copilot wants to merge 3 commits into
mainfrom
copilot/use-txs-and-jsx-directly-xyz-27

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 25, 2026

The aixyz erc-8004 register browser wallet page was generated via a ~600-line monolithic template string in buildHtml() — hard to read, maintain, and escape correctly. This replaces it with a .tsx file using Bun's native JSX support, with a clean separation between the JSX template and the server logic.

Changes

register/wallet/html.tsx (new)

  • Minimal self-contained JSX-to-HTML factory (h, Fragment) — zero external dependencies
  • Html wrapper class distinguishes already-rendered HTML from raw text, preventing double-escaping of nested elements
  • CSS extracted to a const CSS template literal; JS client code stays as a template literal (still needs safeJsonEmbed interpolations for data embedding)
  • buildHtml() reimplemented as JSX — structure is now readable and composable
  • HTML helpers escapeHtml and safeJsonEmbed co-located here as they are HTML-specific utilities
const doc = (
  <html lang="en">
    <head>
      <meta charset="UTF-8" />
      <title>aixyz.sh – ERC-8004 {actionLabel}</title>
      <style dangerouslySetInnerHTML={{ __html: CSS }} />
    </head>
    <body>
      <div class="details" id="details">
        <div class="detail-row">
          <span class="detail-label">Chain</span>
          <span class="detail-value">{chainName} ({chainId})</span>
        </div>
        {uri && (
          <div class="detail-row">
            <span class="detail-label">URI</span>
            <span class="detail-value">{displayUri}</span>
          </div>
        )}
      </div>
      <script dangerouslySetInnerHTML={{ __html: js }} />
    </body>
  </html>
);
return `<!DOCTYPE html>\n${doc}`;

register/wallet/browser.ts

  • Contains only server/runtime logic: signWithBrowser, openBrowser, BrowserSignParams
  • Lazily imports buildHtml from ./html.tsx via dynamic import inside signWithBrowser, keeping the module boundary clean

tsconfig.json

  • Added "jsx": "react", "jsxFactory": "h", "jsxFragmentFactory": "Fragment" to wire up the custom factory via Bun's native JSX processing

register/wallet/browser.test.ts

  • Updated to import buildHtml, escapeHtml, and safeJsonEmbed directly from ./html.tsx (their correct source)

Note

A follow-up improvement would be to replace the custom h/Fragment/Html factory with React's renderToStaticMarkup from react-dom/server (as shown in the Bun React guide), which would eliminate the hand-rolled JSX runtime entirely. This requires adding react and react-dom as dependencies and changing tsconfig.json to "jsx": "react-jsx".


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@linear
Copy link
Copy Markdown

linear Bot commented Feb 25, 2026

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-boilerplate Ready Ready Preview, Comment Feb 25, 2026 4:32pm
agent-chainlink Ready Ready Preview, Comment Feb 25, 2026 4:32pm
agent-job-hunter Ready Ready Preview, Comment Feb 25, 2026 4:32pm
agent-price-oracle Ready Ready Preview, Comment Feb 25, 2026 4:32pm
agent-travel Ready Ready Preview, Comment Feb 25, 2026 4:32pm
agent-with-custom-server Ready Ready Preview, Comment Feb 25, 2026 4:32pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ai-xyz.dev Ignored Ignored Feb 25, 2026 4:32pm

Request Review

… browser.tsx

Co-authored-by: fuxingloh <4266087+fuxingloh@users.noreply.github.com>
@fuxingloh
Copy link
Copy Markdown
Contributor

@copilot Is there a better way to do this?

…sx (JSX template)

Co-authored-by: fuxingloh <4266087+fuxingloh@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 25, 2026

@copilot Is there a better way to do this?

The better approach (commit a1ccb0f) is to split the responsibilities cleanly into two files instead of one fat file re-exporting everything:

  • html.tsx — contains only the JSX factory (h/Fragment), buildHtml, and the HTML helpers (escapeHtml, safeJsonEmbed). This is the .tsx file that Bun compiles with native JSX.
  • browser.ts — contains only the server/runtime logic (signWithBrowser, openBrowser, BrowserSignParams). It lazily await import("./html.tsx") inside signWithBrowser to avoid Bun module-cache collisions with the mock.module in sign.test.ts.

browser.test.ts now imports buildHtml/escapeHtml/safeJsonEmbed directly from ./html.tsx (the correct source), rather than through browser.ts. This is cleaner, has no shim, no re-export indirection, and all 21 tests pass.

@fuxingloh
Copy link
Copy Markdown
Contributor

@copilot how about looking at this https://bun.com/docs/guides/ecosystem/react

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 26, 2026

@copilot how about looking at this https://bun.com/docs/guides/ecosystem/react

The guide suggests using React's renderToStaticMarkup from react-dom/server instead of the hand-rolled h/Fragment/Html factory in html.tsx. That would let us delete the entire custom factory and just do:

import { renderToStaticMarkup } from "react-dom/server";
export function buildHtml(params: ...): string {
  return "<!DOCTYPE html>" + renderToStaticMarkup(<Page {...params} />);
}

with tsconfig.json changed to "jsx": "react-jsx".

I wasn't able to complete this change in the current session because bun add react react-dom fails in this environment (the registry auth token blocks all downloads). The custom factory is functionally equivalent and all 21 tests pass, but I agree using React proper is the cleaner approach and will complete it once the environment allows package installation.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • bun.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

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.

2 participants