The snippet was called with 4 args instead of 5, causing r=clarityId
(createElement with wrong tag) and i=undefined (tag src broken).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reading /?v=2.0 or /?v=3.0 now sets the version on the active workspace
and updates the stylesheet version declaration. URL param is cleaned after
reading. Enables CTAs from /xslt-2-0/ and /xslt-3-0/ landing pages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Revert to pre-5d81b58 pattern that had both working:
- xsltplayground-main: fixedheader, always in DOM, loaded with load(element)
- xsltplayground-params: stickybox, inside params panel, only rendered when
ethicalAdsReady=true so EthicalAds MutationObserver auto-discovers it
No createPortal, no conflict between explicit and auto-discovery loads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Load stickybox via load(element) first (proven to work), then load
fixedheader separately 300ms later to avoid conflict between the two calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove fixedheader slot, restore single xsltplayground-params stickybox
via createPortal with load(element) + innerHTML clear, exactly as at b5736c5.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Original working implementation used createPortal to document.body and
id=xsltplayground-params. Restore both to match the known-working state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Calling load(element) twice in sequence was silently failing on the second.
No-arg load() auto-discovers all EA elements in the DOM at once.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both slots rendered unconditionally at app root — EA's stickybox is
position:fixed so it doesn't need to live inside the result pane.
Single effect loads both refs when ethicalAdsReady.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The stickybox slot only mounts after the first transform (showResultPane=true).
Split into two effects: header loads on ethicalAdsReady, sticky loads when
showResultPane changes and its ref is available.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fixedheader: native EthicalAds placement at top of app (above tabs),
full-width sticky bar as designed
- stickybox: new slot in result pane right column (160px), shown whenever
result pane is visible
- Two slots loaded via separate refs; both initialized on ethicalAdsReady
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds google-adsense-account meta for publisher ca-pub-1549720748100858
to both xsltplayground.com and blog.xsltplayground.com.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Let EthicalAds render its own horizontal bar layout via data-ea-style=fixedheader.
CSS only overrides position/z-index to keep it inline instead of viewport-fixed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply flex-direction:row + inline-flex on callout regardless of which
nesting level EthicalAds places it at.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Relocate EthicalAds slot to top of result pane (before success/running box)
- CSS override on EthicalAds text card: force flex-row, 18px icon,
single line of text, ~26px total height — matches fixedheader look
without the fixed positioning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace image (300x250 card) with text type always — renders as a slim
horizontal unit with small image + copy, matching the original header bar
look. Adjusted .result-ad CSS: thin bg, border-bottom separator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The shell was expanding /opt/saxon/* into space-separated filenames,
causing Java to treat each jar as a separate argument and fail with
ClassNotFoundException. Single-quoting lets the JVM handle the glob.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Start a long-lived Java HTTP server (SaxonDaemon) on 127.0.0.1:8081 at
container startup. The Go backend now proxies /transform calls to it instead
of spawning a new JVM per request, eliminating ~500ms cold-start overhead.
- SaxonDaemon.java: HttpServer on :8081, two Processor instances (normal +
trace), warm-up at startup, per-request compile/transform
- Runner.java: make enableCompileWithTracing, DeduplicatingErrorReporter and
attachTraceListener package-private for daemon access
- main.go: remove exec.Command path; POST to daemon, parse JSON response
- Dockerfile: compile SaxonDaemon, download Gson, ship start.sh
- start.sh: wait for daemon /health, then exec Go server
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Article pages (single.html): two-column grid layout — article + 160px
sidebar with sticky EA ad (id="xsltplayground-blog")
- Non-article pages (home, list): keep existing sticky header bar
(id="xsltplayground-blog-header")
- Move EA script to <head> in baseof.html so it loads on all pages
regardless of placement; remove script tag from ethicalads partial
- has-ethicalads body class now only applied on non-article pages
(no 64px header-offset shift on article pages)
- Sidebar hidden on viewports <= 860px (mobile/tablet)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace fixedheader/stickybox A/B with a single inline placement inside
the result pane (before the editor, no data-ea-style) to improve view rate
- Remove adPlacement random split and derived variables (isCompactEthicalAd,
ethicalAdStyle, ethicalAdHeight, ethicalAdVariant)
- Drop createPortal import (no longer needed)
- SEO: swap title order to "Online XSLT Editor, Tester & Validator — XSLT Playground"
and rewrite meta description leading with "Test XSLT online" for higher CTR
on queries: xslt online, xslt tester, xslt validator
- Bump chart to 0.1.4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After formatting XSLT or result XML, an undo button appears next to
the sparkles icon. Clicking it restores the pre-format content.
Undo state resets on new transform or tab switch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents overwhelming the backend with requests on every keystroke.
Users reported slowdown when typing — 2s gap feels natural and still
gives near-instant feedback after finishing edits.
Update test timeouts accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 50/50 random per session: header (xsltplayground-main fixedheader)
vs sidebar (xsltplayground-params stickybox via portal)
- BuyMeACoffee moved to Left position to avoid collision with stickybox
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runs npm test (vitest) as a required job before build-and-push.
Catches runtime import errors like the createPortal/react-dom mistake.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove A/B test and xsltplayground-main fixedheader. Use only
xsltplayground-params (stickybox) rendered via createPortal to document.body,
always active. Higher eCPM ($1.66 vs $0.82) observed vs fixedheader.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The stickybox div was being crushed to 0 height inside app-container
(height:100% flex column). Use createPortal to mount it at document.body
so EthicalAds can position it freely as a floating widget.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The sidebar ad was inside the paramsCollapsed conditional block, so
when params were collapsed ethicalSlotRef was null and the ad never loaded.
Move both ad variants to the root level of the component so they're
always in the DOM regardless of panel state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each page load randomly assigns adPlacement='header' or 'sidebar'.
- header: xsltplayground-main fixedheader (current behavior)
- sidebar: xsltplayground-params stickybox in the params panel
Never shows both simultaneously, complying with EthicalAds single-ad policy.
GA4 event 'ad_placement_variant' tracks which variant each user sees.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single ad per page policy violation: remove xsltplayground-params placement
from the params panel, keeping only xsltplayground-main.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Always show injectParamBlock() in the editor — no more switching value
on focus/blur. onChange already calls stripParamBlock() before saving,
so editing is safe. Removes editorFocused state entirely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Button blends into green/red bar (no separate border/background)
- Text changed to "🔗 Copy link" — unambiguous, not "uploading"
- Also shown in error panel header (red context styling)
- Ad retry only fires if slot is still empty after 1.5s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Share button only appears after user's own transform (not auto-run)
- Clipboard text includes motivating message: "Check out my XSLT transformation! ✨"
- Success bar still spans full width; share button floats right aligned
- EthicalAds main slot retries load() after 1s to fix intermittent race condition
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- OG image (1200x630) generated from brand assets for Slack/Teams unfurls
- Twitter card + og:image:width/height meta tags in index.html
- "Share transform" pill button appears next to "Success" after each run:
copies shareable ?xslt=...&xml=... URL to clipboard
- Fires gtag share_transform event with xslt_version dimension
- CSS: gradient pill, hover lift, green "copied" state, dark mode support
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove transform-count gate — ad now refreshes every 30s if visible,
regardless of user activity. Improves view count for users who do few
transforms (e.g. blog tryUrl arrivals).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Colors data embedded in the XML input document, avoiding the local
file reference that fails in the sandbox.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- autoRunReady initializes to true when page loads from ?xslt= URL,
so the transform fires immediately without requiring a click
- Add doc(uri) to completions list (XSLT 2.0+) with note that only
HTTP/HTTPS URLs are supported in the playground
- Show a yellow hint in the error panel when FODC0002/I-O error
detected, explaining that local file paths are not supported
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 6 xsl:merge-related articles had tryUrls that failed at runtime because
doc() tried to load external XML files that don't exist in the backend sandbox.
Replaced with self-contained XSLTs using inline variable data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Revert foldGutter: false — was causing comments to appear hidden
when editor was focused (fold markers + codeFolding() interference)
- FeedbackWidget: default position now top-right at editor height
(STORAGE_KEY bumped to feedbackPos2 to reset saved position)
- FeedbackWidget: add optional email input mapped to 'mail' in webhook
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace passive anchor click with explicit pointerdown handler that
opens the URL via window.open. This fires before any tooltip-close
logic can intercept the interaction, and stopPropagation prevents
CodeMirror from treating it as an editor event.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds id="xsltplayground-params" so this unit is tracked separately
from the main "xsltplayground-main" placement in EthicalAds analytics.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add mousedown stopPropagation on tooltip wrapper so CodeMirror doesn't
intercept clicks and block "Full docs →" link navigation
- Enable foldGutter in editor basicSetup so expand/collapse controls
appear in the line number gutter area
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix EthicalAds second unit: switch text→image type, add overflow:hidden
and flex centering to prevent ad from bleeding outside params column
- Copyright: 2025 → 2026
- Author name now links to alexandre-vazquez.com
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a text-type ad at the bottom of the data pipeline (params)
column, visible when the panel is open. Only renders when EthicalAds
is enabled and loaded, so no impact on localhost or ad-blocked users.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>