chat_message (non-streaming) renders the sender header as the first
paragraph inside .message-content — '<p><strong>[name](link):</strong></p>'.
message_chunk uses a separate .message-header div, but the inline pattern
made the sender name sentence 0, so manual Play highlighted the model
name instead of the actual first sentence (and auto-play got every
highlight shifted by one because domIndexFor ratioed around it).
Detect the pattern (top-level <p> with one <strong> child, text ending
in ':') and exclude it from the sentence walk via a TreeWalker filter.
Also drops the diagnostic logging from cdf3c7c.
Temporary instrumentation to pinpoint why sentence highlighting fails on
manual Play. Logs whether audio/button are present, whether sentences is
the expected array, the post-wrap span count, and the wrap-flag state.
Will revert once root cause is found.
Guests had to retype their username on every page load. Now the entered
name is cached in localStorage and reused on subsequent visits. The
cache is invalidated by any login/logout transition:
- on a page load with a server-provided username (logged in), we clear
the cache, so a later logout starts fresh
- on a guest page load with no cache, we prompt and save what they type
wrapSentencesForGlow gated only on dataset.glowWrapped='1', but that flag
lives on the container element and survives innerHTML replacement. The
streaming chunk handler does targetMessageElement.innerHTML = sanitized
on every delta, and message_updated / edit-save both replace innerHTML
too. Spans got blown away while the flag persisted, so subsequent glow
attaches found the flag, skipped re-wrapping, and the highlight never
appeared.
Now also require an actual .tts-sentence span to exist before short-
circuiting. If the wrap was destroyed, re-wrap.
Two regressions surfaced after adding the Pause label in manual mode:
1. Clicking Pause restarted the cached blob from the beginning (looked
like the track played twice). speakText's onclick was still wired to
speakText itself, so each click created a fresh Audio and started
from 0. Manual play now uses the same takeOver pattern as the queued
path: while live audio owns playback, the click routes through
toggleAudioPlayback (which pauses any current audio and toggles this
one); when audio ends or errors, the original speakText handler is
restored so users can replay from cache.
2. Manual mode had no sentence highlights. The glow needs per-sentence
timing from the speech service, which only the SSE branch returns —
manual mode was using the older non-SSE streaming path. speakText now
branches on tts-1-f5 like speakTextQueued does, caches the sentences
array, and calls attachSentenceGlow. Pauses naturally pause the glow
(its tick loop checks audio.paused/ended).
Also routes the queued-audio onclick rebind through toggleAudioPlayback
for cross-message safety (was calling audio.play()/pause() directly,
which would let two queued audios play simultaneously if the user
clicked Play on a paused one while another was active).
The onplay handler set the button text to 'Pause' mid-playback but the
button's onclick was still wired to speakText. Clicking Pause re-ran
speakText, which created a fresh Audio from the cached blob — the queued
audio paused while a new one started from the beginning, sounding like a
double-play.
Rebind the click handler to direct audio.pause()/play() while the queued
audio owns playback, restore the original (cache-replay) handler on end
or error.
Auto-play TTS used to show zero feedback between message arrival and
audio start — users waited blind. Now the play button shows:
Queued + pulsing dot — message is waiting in line
Loading + spinner — actively fetching audio from speech service
Pause — audio is actually playing
Manual Play also uses the same spinner instead of the old 'Streaming...'
text-only state. Indicator clears on play, pause, end, error, and when
auto-play is toggled off.
Replace the non-streaming whole-clip timestamps fetch with an SSE consumer.
Each event carries one sentence's mp3 + exact start/end ms; audio feeds an
MSE SourceBuffer in sequence mode so playback starts after sentence 0, and
the sentences array grows live to drive the glow (tick reads its length each
frame). Falls back to a buffered single blob when the browser lacks mp3 MSE.
Removes fetchTTSWithTimestamps. Best of both: instant start and exact sync.
Restore the theme toggle into the chat utility belt (desktop + mobile),
placed directly above the Auto-Play TTS button. Re-wires updateThemeButtonText
to label both buttons (shows the mode a click switches to). toggleTheme()
already persisted to localStorage and swapped the highlight.js theme.
When a message is deleted, drop its cached TTS audio (keys `${messageId}-${voice}`),
remove any pending entries from the playback queue, and if it is the message
currently playing, stop it and advance the queue. Track currentQueuedMessageId
so the active item can be identified. Keeps auto-play sequencing correct and
prevents a deleted message's stale audio from replaying.
Replace the fragile client-side RMS / Web Audio pause detection (which
stalled on sentence 0 whenever the audio context was suspended or the
MSE duration was unknown) with exact per-sentence timing from the speech
service. For tts-1-f5, fetch via the new timestamps mode and light each
sentence by comparing audio.currentTime to the returned start/end ms;
falls back to proportional mapping if the rendered and spoken sentence
counts differ. Cache now stores sentences for correct replay glow.
Non-F5 models keep streaming playback with no glow.
Flip the Thinking toggle to default ON: localStorage init now reads
!== 'false' (matching the standard default-on idiom), and both desktop
and mobile buttons render green "Thinking: ON" by default. Users opt out
to save tokens; the OFF path still drives server-side enable_thinking=false.
When Auto-Play TTS reads a message, wrap its sentences in spans and advance a
glow highlight on detected inter-sentence silences (RMS dips), with a
char-proportional fallback. Comma/clause pauses are detected too, reserved for
a future word-level highlight. Tunable via window.GLOW.
Add a Thinking on/off button next to Auto-Play TTS. When OFF (default),
the client sends enable_thinking=false with each chat_message and the
server passes chat_template_kwargs.enable_thinking=false to self-hosted
OpenAI-compatible endpoints (Qwen3-style), suppressing chain-of-thought
to save tokens rather than merely hiding it. Skips the param for
api.openai.com (which would 400) and o1/o3 (always think); templates that
ignore the kwarg simply drop it. Client-side reasoning suppression remains
as a fallback. State persists to localStorage across desktop and mobile.
Qwen3.x / Deepseek-R1 / o1 stream model thinking via
delta.reasoning_content during SSE; the final answer arrives later via
delta.content. Both server streaming paths (OpenAI client + llama-cpp
Python lib) now extract reasoning_content per chunk and emit it via the
same socketio "message_chunk" event with a `reasoning_content` field
(distinct from `content`). Reasoning is forwarded but NOT accumulated
into the buffer that persists to the DB — it's transient model-private
state, not part of the saved message.
Frontend (templates/chat.html): the message_chunk handler dispatches
on payload shape. On `reasoning_content`: lazy-create a
<details class="message-thinking"> block at the top of the message
wrapper, append text, return early (no buffer/markdown render). On
`content`: if a thinking block exists and is still open, auto-collapse
it to "thinking (click to expand)" before standard content rendering.
If a response never thinks, no thinking DOM element is created — no
visual artefact at all. Compatible with all existing message_chunk
listeners; reasoning_content is purely additive.
Belt-and-suspenders: inline onkeydown on textarea ensures Enter
works regardless of JS event listener registration order, plus
a visible Send button as clickable fallback.
- Audio starts playing as soon as ~1KB arrives instead of waiting for full download
- Browser-aware format detection (webm+opus for Firefox, mp3 for Chrome)
- Falls back to buffered download if MediaSource not supported
- Updated speakText() and speakTextQueued() to use streaming
- Cache blobs instead of Audio objects for cleaner replay
Correct the artifact implementation to match Unsandbox API:
- Request parameter: return_artifact (boolean, singular)
- Response field: artifacts (array, plural)
- Data field: content_base64 (not data/content)
Changes:
- Backend: Changed artifacts to return_artifact in request body
- Frontend: Use return_artifact in execute request
- Frontend: Look for artifacts array in response (not artifact singleton)
- Frontend: Decode content_base64 field (not data/content)
- Frontend: Use filename field (primary) over name
- Remove debug console.log statements
- Update CLAUDE.md with correct API contract
The API parameter is singular but response is plural - this is intentional
design by Unsandbox API. Request enables artifact collection, response
returns array of generated artifacts.
Add console.log statements to see actual artifact object structure:
- Log artifacts array when received
- Log each artifact object and its keys
- Log download attempt with artifact keys and data presence
This will help diagnose why artifacts show metadata but fail on download/view.
Artifacts are returned as base64-encoded data directly in the job response
payload, not as URLs. Previous implementation incorrectly tried to fetch
artifacts from URLs via a proxy endpoint.
Changes:
- Remove /api/code/artifacts proxy endpoint (not needed)
- Update downloadArtifact() to decode base64 and trigger download
- Update viewArtifact() to decode base64 and display inline:
* Images: rendered as data URLs (data:image/png;base64,...)
* Videos: decoded to blob URLs with controls
* Text: decoded and displayed in <pre> blocks
- Update CLAUDE.md with correct artifact format and implementation details
- Add artifact response format example showing base64 data structure
Artifact format in response:
{
"artifacts": [{
"name": "output.png",
"type": "image/png",
"data": "base64string...",
"size": 12345
}]
}
Remove race condition where syncDropdownsAndQueryString() was called before
dropdowns were populated, causing empty values to overwrite localStorage.
Model and voice restoration now happens only in populateModelDropdown() and
populateVoiceDropdown() after async fetch completes.
- Fetch external images through proxy.unturf.com (respects robots.txt)
- Convert fetched images to base64 for vision API
- Cache both fetched images and descriptions
- Shows "Fetching image..." for external URLs, "Generating description..." for base64
- Handles 403 responses when blocked by robots.txt
Backend:
- Track VISION_MODELS list at startup from available endpoints
- Add is_vision_model() to detect vision-capable models (*-vl*, *vision*, gpt-4o)
- Add extract_base64_from_img_tag() and build_message_content() helpers
- Modify chat_gpt() to include base64 images for vision models
- Add GET /vision endpoint for vision availability status
- Add POST /vision/describe endpoint for image alt-text generation
Frontend:
- Check vision availability on page load via /vision
- Add hover event delegation on chat images
- On hover: call vision model, cache result, set img.title and img.alt
- Shows cursor:wait while loading description
Update code execution error detection to use stderr exclusively
instead of checking both stdout and stderr. This aligns with the
Unsandbox API's proper stream separation where:
- stdout is for program output
- stderr is for errors/warnings
Changes:
- Remove stdout from error detection logic
- Only trigger auto-fix when exit_code != 0 AND stderr is non-empty
- Send only stderr to /api/fix-code endpoint
This prevents false positives where stdout contains normal output
that was previously being treated as error content.
- Use codeBlock.dataset.autoExecAttempt to pass attempt number
- executeCodeBlock transfers attempt to results container
- No longer pre-creates empty results container
- Clean up data attribute after use
- Add null check for classList before calling contains()
- Handles case where nextSibling might be a text node
- Prevents 'classList is undefined' error
- Correctly insert results container after button container
- Use buttonContainer.parentNode.insertBefore() instead of preElement
- Add proper styling when creating results container
- Fixes 'Child to insert before is not a child of this node' error
- Post fixed code to chat so user can see what was changed
- Store fix data in window.pendingAutoExec for message handler
- Auto-trigger execution on newly posted code block
- Preserve attempt counter across fix iterations
- User now sees: original error → fixed code posted → auto-execution → results
- Remove chat message posting that prevented automatic re-execution
- Fixed code now executes directly using same blockElement
- Simplify re-execution flow with direct recursive call
- Results display in same execution results container
Implements an AI-powered auto-fix system that automatically attempts to
repair code execution errors up to 3 times.
Backend changes (app.py):
- Add /api/fix-code endpoint that uses MODEL_1 (Hermes) to analyze
stderr output and generate corrected code
- System prompt instructs AI to output only raw fixed code without
explanations or markdown formatting
- Accepts code, language, stderr, exit_code, and attempt number
Frontend changes (templates/chat.html):
- Modify executeCodeBlock() to detect failed executions (exit_code !== 0)
- Track fix attempts per code block (max 3) using dataset attributes
- Call /api/fix-code when errors are detected
- Display fixed code as new chat message with markdown formatting
- Automatically re-execute the corrected code recursively
- Show progress messages during auto-fix attempts
- Display warning when max attempts (3) are exhausted
Features:
- Fixes common issues: missing imports, syntax errors, type errors
- Posts fixed code to chat for transparency
- Prevents infinite loops with 3-attempt limit
- Graceful error handling with user-friendly status messages
- Chat messages: logged-in user's name links to /profile
- User lists (sidebar): logged-in user's name is clickable link
- Previous messages: same profile link logic applied
- Other users still link to /profile/username (future feature)
Anywhere the logged-in user's name appears, it now links to their
profile settings page for quick access to logout and username change.
- Update CODE_EXEC_URL from code.ai.unturf.com to api.unsandbox.com
- Fix response field handling for Unsandbox API format (flat structure)
- Add exit code display with color coding (green=0, red=error)
- Update displayExecutionResults to handle stdout/stderr/exit_code at top level
- Simplify error handling for timeout/cancelled jobs
- Add comprehensive Unsandbox API documentation to CLAUDE.md
* Redesign UI: unified design system across all pages
- Replace scattered inline CSS with comprehensive single style.css
- Implement modern design system:
- System font stack (-apple-system, Segoe UI, etc.)
- Consistent spacing scale (4px base)
- Unified color tokens for light/dark themes
- Reusable component classes (buttons, cards, badges, forms)
- Update all templates to use new CSS classes:
- browse.html: Less chunky, better space usage with room-grid
- index.html: Cleaner centered card layout
- auth.html: Streamlined 4-step flow with gradient background
- profile.html: Modern settings interface
- search.html: Browse-style card layout
- Chat page improvements:
- Tighter layouts (240px/280px sidebars instead of 15%/25%)
- Better message spacing
- Improved code blocks with proper padding
- Cleaner utility belt
- Better responsive design and dark mode support
* modified: app.py
modified: templates/base.html
modified: templates/profile.html
* Revert standalone pages to original design
- Browse, index, auth, profile restored to original inline CSS
- Chat page improvements preserved in style.css
- Search page still uses improved card layout
* Add dark mode support to index and browse pages
- index.html now supports dark mode with CSS variables
- browse.html now supports dark mode with CSS variables
- Theme persists from localStorage across pages
* Remove theme toggle from chat page
- Theme toggle button removed from desktop chat sidebar
- Theme toggle button removed from mobile chat modal
- Theme management now done via profile page only
* Make usernames clickable links to profile pages in chat
* Update search page to match browse page layout and CSS
* Fix chat layout positioning
* Fix room list auto-update when title changes
* Fix new room creation appearing in sidebar
- Add socketio.emit in create_room_api() to broadcast new rooms
- Update socket handler to add new rooms to sidebar dynamically
- Rooms now appear without hard refresh
* Fix /title and /cancel commands being sent to LLM
- Add missing return statements after command handlers
- Commands now properly terminate message processing
- Prevents commands from being interpreted as chat messages
* Make usernames in user lists clickable links to profiles
- Update updateUserLists() to create links for all usernames
- Add hover effect CSS for user list links
- Works for both active and inactive users
- Works for both desktop and mobile views
* Revert user list profile links and update profile page layout
- Remove profile links from user lists (no backend route for other users)
- Update profile page to full-screen layout like browse page
- Add header with navigation buttons
- Remove centered container, use full-width layout
- Add box shadows to sections for visual separation
* Convert all flexbox layouts to CSS grid
- Replace all display: flex with CSS grid equivalents
- Update templates: profile, browse, search, index, auth
- Update static CSS for consistent grid usage
- Use grid-template-columns, grid-auto-flow, and place-items
- Improve layout consistency across all pages
* Fix chatroom horizontal scrolling
- Add overflow-x: hidden to #chat-container and #chat to prevent horizontal scroll
- Add word-break and overflow-wrap to message content for text wrapping
- Change pre tags from overflow: hidden to overflow-x: auto for individual scrolling
- Add min-width: 0 to grid containers to prevent overflow
- Code blocks can now scroll individually while chatroom wraps content
* Remove duplicate CSS variables and fix XSS vulnerability
- search.html: Remove inline styles, link to style.css
- index.html: Remove duplicate CSS variable blocks, link to style.css
- browse.html: Remove duplicate CSS variable blocks, link to style.css
- chat.html: Fix XSS vulnerability in room list updates
- Use textContent/createTextNode instead of innerHTML for user data
- Use DOM methods instead of string concatenation
- Encode URL components with encodeURIComponent
- Extract user count from textContent instead of innerHTML regex
* Merge duplicate CSS rules and replace inline styles with design system
style.css:
- Merge duplicate html, body rules (lines 137-143 and 159-167)
- Consolidate typography and layout properties in single rule
- Remove duplicate BASE LAYOUT section
profile.html:
- Replace style.display mutations with classList API
- Add .availability-indicator.show CSS rule for visibility
- Use classList.add('show') and classList.remove('show')
- Consistent with existing .message.show pattern
browse.html:
- Replace hard-coded gradient colors with CSS variables
- Use var(--gradient-start) and var(--gradient-end) for buttons
- Replace #667eea with var(--button-primary) for tabs and room names
- Remove inline .room-badge styles, use .badge .badge-public/.badge-private
- Apply existing badge classes from style.css for dark mode support
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Russell Ballestrini <russell@unturf.com>
* Add email OTP authentication system and private room management
## Authentication System
- Implement email OTP-based authentication with User and OTPToken models
- Add auth.py module with OTP generation, email sending, and session management
- Create authentication endpoints: /auth/send-otp, /auth/verify-otp, /auth/claim-name, /auth/status, /auth/logout
- Add authentication modal UI with email verification and display name claiming
- Support SMTP configuration via environment variables (optional)
## Room Privacy & Ownership
- Add is_private, is_archived, owner_id, and forked_from_id fields to Room model
- Implement private rooms (single-user, owner-only access)
- Add room forking: users can fork public rooms to public or private
- Add room archive/delete endpoints (owner-only operations)
- Implement access control for private room viewing
## UI Improvements
- Add public/private room tabs in sidebar
- Show authentication prompt in private rooms tab for non-authenticated users
- Add room action buttons (fork, archive, delete) with proper permissions
- Update homepage with statistics (public/private rooms, active users, active rooms)
- Remove model/voice from URL query strings, use localStorage exclusively
## Database Migration
- Create migration 2025011100 for User, OTPToken tables and Room model updates
- Add indexes for email, display_name, is_private, is_archived, owner_id
## Breaking Changes
- URL parameters now only include username (model/voice moved to localStorage)
- Private rooms require authentication to access
- Room creation can now require authentication (for private rooms)
* Update README with authentication and private room documentation
* Simplify README to be less verbose
* Add missing session import to fix linter errors
* Fix migration dependency to resolve multiple heads conflict
* asdf
* Fix SQLAlchemy auto-correlation error in homepage statistics query
* Change tagline from AI-Powered to Machine Learning Powered
* Add dedicated authentication page instead of modal
- Create new /auth route with full-page authentication flow
- Remove modal code from index.html
- Update Sign in link to point to /auth page
- Auth page has 4-step flow: email, OTP, display name, success
- Better UX with gradient background and cleaner design
* Fix migration: remove batch_alter_table to avoid circular dependency
- Use op.add_column() directly instead of batch_alter_table()
- Remove foreign key constraints (defined in models, not needed in migration)
- User and OTPToken tables created by db.create_all() in make init-db
- Fixes CircularDependencyError during migration
* Fix migration: check if columns exist before adding
- Use inspector to check existing columns and indexes
- Only add columns/indexes if they don't already exist
- Handles case where db.create_all() was run before migration
- Fixes 'duplicate column name' error
* Add profile page, room browsing, and updated_at timestamp
Features:
- Profile page with username change and dark/light mode settings
- Browse page for discovering public and private rooms
- Room updated_at timestamp (integer Unix epoch) that updates on new messages
- Dynamic room tabs based on current room type (public/private)
- Fork and delete room actions moved to right sidebar utility belt
- Remove archive feature and success alerts from room actions
Technical changes:
- Add updated_at column to Room model (integer timestamp)
- Add /profile route with authentication requirement
- Add /browse route for room discovery
- Add API endpoints for username availability check and update
- Update room.updated_at on message creation in app.py:1189
- Wider right sidebar (25% instead of 15%) for better button layout
- Profile link on homepage and browse page for authenticated users
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: russell@unturf. <russell@unturf.com>
* Add AI-powered artifact naming for compiled binaries
Implements intelligent filename generation for downloaded binaries using
Hermes AI to analyze code and generate meaningful 1-3 word filenames.
Changes:
- Add /api/generate-artifact-name endpoint that uses MODEL_1 (Hermes)
- Modify frontend to call naming API before download
- Add ENABLE_AI_ARTIFACT_NAMING environment variable (enabled by default)
- Filenames are descriptive (e.g., "fizzbuzz", "hello-world", "prime-checker")
- Graceful fallback to "compiled_binary" if naming fails or is disabled
The feature can be disabled by setting ENABLE_AI_ARTIFACT_NAMING="false"
in environment variables.
* Rename env var to ENABLE_CODE_GEN_FILENAMES
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Reposition download button from output area to button container
- Place next to Copy and Run buttons with consistent styling
- Button hidden by default, shown only when artifact available
- Remove colored background to match other action buttons
Co-authored-by: Claude <noreply@anthropic.com>
* Enable binary downloads on timeout/cancellation
When code execution times out or is cancelled, the compiled binary
may still be available. This change ensures displayExecutionResults()
is called for timeout/cancelled jobs, allowing users to download
the binary artifact even when execution doesn't complete normally.
* Fix partial output display for timeout/cancellation
Previous commit broke partial output display by passing job.result
directly to displayExecutionResults(), but for timeout/cancelled jobs
the output is in partial_output field, not stdout.
Now properly maps partial_output to stdout before displaying, so users
see both the error message and any output that was captured before
timeout/cancellation, plus binary downloads if available.
* Add debugging for missing artifact on timeout/cancel
Check multiple possible locations for artifact:
- job.artifact (top level)
- job.result.artifact (nested)
Add console logging to see full job structure when timeout/cancel
occurs so we can understand why the binary isn't appearing.
* Try fetching artifact from separate endpoint on timeout/cancel
When timeout/cancel occurs, the artifact isn't in the job response.
Try fetching from /jobs/{job_id}/artifact endpoint as a fallback.
This explores whether the executor service has a separate artifact
endpoint that we can use to retrieve compiled binaries even when
execution is cancelled or times out.
* Remove debug logging, document artifact limitation
Removed console.log debugging statements now that we've confirmed
the executor service doesn't include artifacts in timeout/cancelled
responses and doesn't have a /jobs/{job_id}/artifact endpoint.
Kept the artifact fetching code with comments for future compatibility
if the executor service adds this feature.
Current limitation: Binary downloads only work for completed executions,
not for timeout/cancelled ones. The binary exists but the executor
service doesn't return it.
* Try multiple artifact endpoint patterns for timeout/cancel
When artifact isn't in the job response, try fetching from:
- /artifacts/{job_id}
- /jobs/{job_id}/artifact
- /jobs/{job_id}/download
- /jobs/{job_id}/binary
- /download/{job_id}
- /binary/{job_id}
Handles both JSON responses and direct binary responses. Logs
each attempt to console so we can see which endpoint (if any) works.
* Revert endpoint searching - artifact should be in /jobs/{id}
According to OpenAPI spec, there are no separate artifact endpoints.
The artifact should be included in GET /jobs/{id} response for ALL
job statuses (completed, cancelled, timeout).
Current limitation: The executor service only includes result.artifact
for "completed" status, not for "cancelled" or "timeout" status.
The frontend code is correct - it checks job.artifact and
job.result.artifact. The issue is the executor service needs to
include the artifact in cancelled/timeout responses.
* Add debug logging for cancelled/timeout artifact checks
Since the executor service was supposedly patched to include artifacts
in GET /jobs/{id} responses even for cancelled/timeout jobs, add
detailed logging to verify:
1. What the full job response looks like
2. Whether artifact is at job.artifact or job.result.artifact
3. Artifact details if found
This will help determine if the patch is deployed and working.
* Add test-artifact Makefile target for testing executor API
Tests binary artifact retrieval from code executor service:
- Compiles C code with return_artifact=true
- Extracts base64 artifact from response
- Decodes and executes the binary
Can test against different URLs:
make test-artifact URL=https://code.ai.unturf.com
Tested against production and confirmed:
- Artifacts ARE included for completed jobs
- Artifacts are NOT included for cancelled/timeout jobs (even with
return_artifact=true). Exit code 137 indicates SIGKILL.
* Document confirmed limitation - no artifacts for cancelled jobs
Tested against production executor API (make test-artifact) and confirmed:
- Cancelled jobs return exit_code 137 (SIGKILL)
- NO artifact field in response (neither job.artifact nor job.result.artifact)
- Artifacts only returned for fully completed jobs
Code still checks for artifacts in case this limitation is fixed
in the future, but currently binary downloads will not work for
cancelled/timeout executions.
To fix: Executor service needs to include compiled binary in
response even when execution is killed (compilation succeeded).
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Request compiled binaries via return_artifact parameter
- Add "Download Binary" button when artifact is available
- Support base64 decoding and browser download
- Handle artifact errors gracefully
- Works with C, C++, Rust, Go, Java, and other compiled languages
Co-authored-by: Claude <noreply@anthropic.com>
Problem:
- Buttons were being inserted as children of <pre> element
- This caused buttons to appear inside code blocks with wrong styling
- Template caching made changes appear to require "two commits"
Solution:
- Change insertion point from block.parentNode to preElement.parentNode
- This places buttons as siblings of <pre>, not children
- Add TEMPLATES_AUTO_RELOAD=True to prevent Flask template caching
Technical Details:
- block is the <code> element
- block.parentNode is the <pre> element
- preElement.parentNode.insertBefore puts buttons after <pre>
- Previous code put buttons inside <pre> after <code>
DOM Structure Before:
<pre>
<code>...</code>
<buttons> <!-- Wrong: inside pre -->
</pre>
DOM Structure After:
<pre>
<code>...</code>
</pre>
<buttons> <!-- Correct: after pre -->
Co-authored-by: Claude <noreply@anthropic.com>