mcp-data-platform-v1.39.3
Shared Content Renderer with Single CSS Build
This release unifies how the public content viewer renders all content types. Instead of server-side rendering with separate Go libraries, the public viewer now uses the same React components and Tailwind CSS as the authenticated portal — eliminating an entire class of styling bugs caused by divergent Tailwind builds.
Highlights
Unified client-side rendering — The public viewer now renders markdown, SVG, JSX, HTML, and plain text using the same ContentRenderer React component as the internal portal. Server-side rendering via goldmark and bluemonday has been removed entirely. Content is passed as JSON to an embedded IIFE bundle that hydrates client-side.
Single CSS build — Previously, the content viewer had its own @import "tailwindcss" entry point, producing a second CSS build with different prose styles. Now the Makefile builds the SPA first, then copies its CSS output as content-viewer.css. The IIFE Vite config (vite.content-viewer.config.ts) produces JS only.
Dark mode fix — Added @custom-variant dark (&:where(.dark, .dark *)) to index.css so Tailwind's dark: variants key off the .dark class instead of @media(prefers-color-scheme:dark). This fixes white-on-white markdown text when the OS is in dark mode but the page is toggled to light.
What Changed
Content rendering (pkg/portal/public.go):
- Replaced
renderContent(),renderMarkdown(),sanitizeSVG(),jsxIframe(),sandboxedIframe(),blobIframe()with a single JSON payload passed to the client-side content viewer bundle - Unified
publicCSP()to a single policy for all content types (client-side rendering makes per-type CSP unnecessary) - Removed
goldmarkandbluemondayGo dependencies (-4 direct deps, -8 transitive deps from go.sum)
Public viewer template (pkg/portal/templates/public_viewer.html):
- Added shadcn HSL CSS variables (
--background,--foreground,--card,--muted,--border,--primary, etc.) for light and dark modes so the SPA CSS resolves colors correctly - Renamed page shell
--borderto--page-borderto avoid collision with the shadcn HSL--bordertriplet - Added
.darkclass toggle alongsidedata-themeattribute for Tailwind compatibility
New: content viewer embed package (internal/contentviewer/):
embed.go— Go embed for JS + CSS bundles withloadBundles()for testability- Gracefully handles missing bundles (empty strings when dist only has
.gitkeep)
New: content viewer entry point (ui/src/content-viewer-entry.tsx):
- Reads content from embedded
<script type="application/json">data element - Renders via the shared
ContentRenderercomponent - Includes a
MutationObserveras a defensive fallback fordata-theme→.darkclass bridging
New: Vite IIFE build config (ui/vite.content-viewer.config.ts):
- Builds
content-viewer-entry.tsxas an IIFE bundle (JS only, no CSS) - Output goes to
ui/dist-content-viewer/
New: dev preview server (cmd/preview-content-viewer/main.go):
- Standalone HTTP server at localhost:9090 for testing the content viewer
- Sample content for all 5 content types (markdown, SVG, JSX, HTML, plain text)
- Light/dark theme toggle with theme persistence
Build system (Makefile):
- New
frontend-build-content-viewertarget for standalone JS bundle builds frontend-buildnow builds SPA first, then content viewer JS, then copies SPA CSS- Resilient CSS file discovery using
findinstead of fragilels index-*.cssglob cleanandembed-cleantargets updated for the newinternal/contentviewer/dist/directory
Dev environment (dev/):
- Added SeaweedFS S3-compatible object store to docker-compose for local portal testing
- Pinned SeaweedFS image to
3.88@sha256:98e034... - Added portal config to
dev/platform.yamlwith S3 connection - Added
dev/seaweedfs-s3.jsonfor SeaweedFS S3 credentials
Security Notes
- SVG content is now sanitized client-side via
DOMPurify(SVG profile) instead of server-sidebluemonday - HTML content continues to render in sandboxed
<iframe>via blob: URL (sandbox="allow-scripts") - Markdown uses
react-markdown(nodangerouslySetInnerHTML), safe by default - Content JSON injection uses
json.Marshalwhich escapes<,>,&as\uXXXX, preventing</script>breakout
Files Changed
18 files changed, 682 additions, 476 deletions
| File | Change |
|---|---|
pkg/portal/public.go |
-216 lines: removed 6 server-side rendering functions |
pkg/portal/public_test.go |
Replaced render-specific tests with unified template tests |
internal/contentviewer/embed.go |
New: Go embed package for JS/CSS bundles |
internal/contentviewer/embed_test.go |
New: tests with fstest.MapFS for full coverage |
cmd/preview-content-viewer/main.go |
New: dev preview server |
cmd/preview-content-viewer/main_test.go |
New: table-driven handler tests |
ui/src/content-viewer-entry.tsx |
New: React IIFE entry point |
ui/vite.content-viewer.config.ts |
New: Vite IIFE build config |
ui/src/index.css |
@custom-variant dark for class-based dark mode |
pkg/portal/templates/public_viewer.html |
HSL variables, .dark class bridge |
Makefile |
Content viewer build targets, resilient CSS glob |
go.mod / go.sum |
Removed goldmark, bluemonday, and transitive deps |
dev/docker-compose.yml |
Added pinned SeaweedFS service |
dev/platform.yaml |
Portal + S3 config for local dev |
dev/seaweedfs-s3.json |
New: SeaweedFS S3 credentials |
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.39.3Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.3_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.3_linux_amd64.tar.gz