9.1 KiB
Embedded Web Content — sovereign:// pages from files
Problem
The sovereign://agents/chat page is hardcoded as a ~800-line C string literal
in src/nostr_bridge.c (handle_agents_chat_page()).
This causes:
- Bugs — C string escaping errors (
\",\\,%%) are easy to make and hard to spot. The current "messages don't render" and "New Chat doesn't work" bugs are likely caused by this. - Maintenance nightmare — No syntax highlighting, no IDE support, no formatting tools work on the embedded HTML/CSS/JS.
- Can't copy ai.html — The user wants to port
~/lt/client/www/ai.html's structure, but that's a standalone HTML file with separate CSS/JS. Embedding it as a C string would be thousands of lines of escaped strings.
Solution: Copy c-relay's approach
~/lt/c-relay solves this with a build-time embedding script:
- Author web files as normal files in a
www/directory (HTML, CSS, JS — no escaping needed, full IDE support). embed_web_files.sh— A shell script that runs at build time. It useshexdumpto convert each file into a C byte array (0x3c,0x21,0x44,...) in an auto-generatedsrc/embedded_web_content.c.embedded_web_content.h— Declaresembedded_file_tandget_embedded_file(path).embedded_web_content.c— Auto-generated byte arrays + a lookup table mapping paths to arrays.- The
sovereign://handler callsget_embedded_file()to serve the embedded content instead of building HTML withg_strdup_printf().
Architecture
┌─────────────────────────────────────────────────────────────┐
│ www/ (normal files, edit freely) │
│ agents/chat.html (standalone HTML) │
│ agents/chat.css (separate CSS) │
│ agents/chat.js (separate JS) │
│ agents/config.html (provider config page) │
│ agents/config.css │
│ agents/config.js │
│ settings.html │
│ bookmarks.html │
│ ... │
└─────────────────────────────────────────────────────────────┘
│
▼ embed_web_files.sh (build time)
┌─────────────────────────────────────────────────────────────┐
│ src/embedded_web_content.c (auto-generated, do not edit) │
│ static const unsigned char agents_chat_html_data[] = { │
│ 0x3c,0x21,0x44,0x4f,0x43,0x54,0x59,0x50,0x45,... │
│ }; │
│ static const size_t agents_chat_html_size = 12345; │
│ ... │
│ static embedded_file_t embedded_files[] = { │
│ {"agents/chat.html", agents_chat_html_data, ...}, │
│ {"agents/chat.css", agents_chat_css_data, ...}, │
│ {"agents/chat.js", agents_chat_js_data, ...}, │
│ {NULL, NULL, 0, NULL} │
│ }; │
└─────────────────────────────────────────────────────────────┘
│
▼ get_embedded_file(path)
┌─────────────────────────────────────────────────────────────┐
│ src/nostr_bridge.c (sovereign:// handler) │
│ on_sovereign_scheme(): │
│ if (strncmp(uri, "sovereign://agents/chat", ...) == 0) │
│ serve_embedded_file(request, "agents/chat.html"); │
│ ... │
└─────────────────────────────────────────────────────────────┘
Implementation plan
Phase 1 — Set up the embedding infrastructure
-
Create
www/directory at the project root for web files. -
Create
embed_web_files.sh(copy from c-relay, adapt for our file structure). It scanswww/**/*.html,www/**/*.css,www/**/*.jsand generatessrc/embedded_web_content.c+src/embedded_web_content.h. -
Create
src/embedded_web_content.h:typedef struct { const char *path; /* e.g. "agents/chat.html" */ const unsigned char *data; size_t size; const char *mime_type; /* "text/html", "text/css", "application/javascript" */ } embedded_file_t; const embedded_file_t *get_embedded_file(const char *path); -
Add a
serve_embedded_file()helper innostr_bridge.cthat callsget_embedded_file()and responds with the bytes viarespond_bytes(). -
Update
Makefileto runembed_web_files.shbefore compiling, and addsrc/embedded_web_content.ctoSRC. -
Update
.gitignoreto ignoresrc/embedded_web_content.candsrc/embedded_web_content.h(they're auto-generated).
Phase 2 — Migrate the chat page to files
-
Create
www/agents/chat.html— Extract the HTML structure fromhandle_agents_chat_page()into a standalone HTML file. No C string escaping needed. -
Create
www/agents/chat.css— Extract the CSS into a separate file. Link it from the HTML:<link rel="stylesheet" href="sovereign://agents/chat.css">. -
Create
www/agents/chat.js— Extract the JavaScript into a separate file. Link it:<script src="sovereign://agents/chat.js"></script>. -
Add routes in
on_sovereign_scheme():sovereign://agents/chat→ serveagents/chat.htmlsovereign://agents/chat.css→ serveagents/chat.csssovereign://agents/chat.js→ serveagents/chat.js
-
Fix the bugs while extracting:
- Messages not rendering: The
renderMessage()function must includedata-msg-index="N"on.msg-bubble-contentand.msg-bubble-menu-hostelements sorenderBubbleContent()andmountDotMenu()can find them. - New Chat not working: Verify
sovereign://agents/conversations/newcreates a new session and sets it as active. ThenewChat()JS must clear the message list and resetlastCount = -1.
- Messages not rendering: The
Phase 3 — Migrate other sovereign:// pages
Once the chat page works, migrate the other pages the same way:
sovereign://agents(config page) →www/agents/config.html+.css+.jssovereign://settings→www/settings.html+.css+.jssovereign://bookmarks→www/bookmarks.html+.css+.jssovereign://profile→www/profile.html+.css+.js
Each page keeps its C-side data endpoints (sovereign://agents/set,
sovereign://agents/messages, etc.) but the page HTML/CSS/JS moves to files.
Phase 4 — Port ai.html features
With the chat page as a standalone HTML file, we can directly copy:
- The ai.html message bubble CSS (from
messaging-ui.css) - The dot-menu CSS (from
dot-menu.css) - The markdown renderer (use
marked.jsfrom the client's vendor libs, or keep our minimal renderer) - The conversation list layout
- The skills list layout
- The provider config sidenav
Benefits
- No C string escaping — Edit HTML/CSS/JS normally with full IDE support
- Syntax highlighting — IDEs recognize
.html,.css,.jsfiles - Can copy ai.html directly — Just copy the relevant sections
- Smaller
nostr_bridge.c— The 800-line chat page string literal is gone - Easier debugging — Browser dev tools show the actual HTML, not escaped strings
- Build-time embedding — Files are bundled in the binary, no external files needed at runtime
Files to create
| File | Purpose |
|---|---|
embed_web_files.sh |
Build script: converts www/ files to C byte arrays |
src/embedded_web_content.h |
Header for the embedded file lookup |
src/embedded_web_content.c |
Auto-generated byte arrays (gitignored) |
www/agents/chat.html |
Chat page HTML |
www/agents/chat.css |
Chat page CSS |
www/agents/chat.js |
Chat page JS |
Files to modify
| File | Change |
|---|---|
Makefile |
Run embed script, add embedded_web_content.c to SRC |
.gitignore |
Ignore auto-generated embedded_web_content.* |
src/nostr_bridge.c |
Add serve_embedded_file(), route to embedded files, remove old handle_agents_chat_page() string literal |