Commit graph

149 commits

Author SHA1 Message Date
f7862bc54a
feat(profile): let guests manage their localStorage username
/profile previously required auth and returned a raw JSON 401 for
guests. Now it renders a guest variant: edit guestUsername in
localStorage, clear it to re-prompt on next visit, or sign in to
claim a permanent username. Unifies the two username flows
(authed DB display_name vs guest localStorage) under one page.
2026-06-10 12:50:51 -04:00
b2fb60d1b7
fix(tts): skip inlined sender header when wrapping sentences
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.
2026-06-03 18:28:00 -04:00
cdf3c7ca24
debug(tts): log attachSentenceGlow path to diagnose missing manual highlights
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.
2026-06-03 17:50:04 -04:00
8589c91ca9
feat(chat): persist guest username in localStorage across page loads
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
2026-06-03 17:03:16 -04:00
a829c97136
fix(tts): rewrap sentence glow when innerHTML overwrites destroyed spans
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.
2026-06-03 16:07:21 -04:00
ad080a7e2a
fix(tts): manual mode pause-toggles + sentence glow on F5
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).
2026-06-03 15:43:13 -04:00
264f0e7269
fix(tts): pause button actually pauses queued audio
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.
2026-06-03 15:12:49 -04:00
adc4fd79d2
tts: queued/loading indicator on play button
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.
2026-06-03 11:19:19 -04:00
d405ebc6f9
ui: drop misleading homepage stats (active users, active rooms)
Active Users counted all-time distinct UserSession usernames, not anyone
currently online. Active Rooms counted any room that ever received a
single message, with no decay. Both read as live metrics but aren't.

Keep Public Rooms and (when signed in) Private Rooms — those are real
counts users can verify by browsing.
2026-06-02 09:19:11 -04:00
f2ebfc16a0
tts: stream F5 over SSE — gapless MSE audio + live timing glow
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.
2026-05-29 15:23:36 -04:00
4b0a443928
ui: add dark/light theme toggle above Auto-Play TTS
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.
2026-05-29 12:51:07 -04:00
511c429927
tts: purge cached audio and dequeue on message delete
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.
2026-05-29 12:44:43 -04:00
34f4fc325b
tts: drive sentence glow from exact server timestamps
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.
2026-05-29 12:29:37 -04:00
0b66a50938
feat(thinking): default thinking mode ON
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.
2026-05-29 12:00:46 -04:00
0fdb673a70
feat(tts): per-sentence glow synced to audio via Web Audio pause detection
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.
2026-05-29 11:52:08 -04:00
57a5b6f243
feat(thinking): toggle to enable/disable model thinking mode
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.
2026-05-29 11:43:10 -04:00
e50fe6e830
streaming: forward reasoning_content to client as separate channel
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.
2026-05-25 14:49:43 -04:00
d7fcfc8935 Clean up debug code, keep Send button
Root cause found: uncloseai extension was clobbering window.sendMessage.
Fixed in uncloseai.com repo by namespacing under window.uncloseai.
2026-02-23 19:55:55 -05:00
c06b3706f2 Debug send failure - self-contained handler with visible error output 2026-02-23 19:39:04 -05:00
844ed05cf0 Add Send button and inline keydown handler for chat
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.
2026-02-23 19:30:00 -05:00
c0ec5ded04 Fix socket.io connection for reverse proxy deployments
Replace deprecated document.domain + port construction with io()
which auto-connects to the serving host regardless of proxy setup.
2026-02-23 19:19:42 -05:00
47bf62ef66 Add streaming TTS using MediaSource API for instant playback
- 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
2026-01-28 07:53:11 -05:00
c6e6e81c3c Fix artifact API - use correct parameter and field names
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.
2026-01-20 09:07:25 -05:00
acee097d3b Add debug logging for artifact structure
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.
2026-01-20 08:50:29 -05:00
7a54952191 Fix artifact handling - use base64 from response payload
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
  }]
}
2026-01-20 08:38:37 -05:00
5576179ffa Add comprehensive artifact support for code execution
Implement full artifact support for Unsandbox code execution, enabling
download and inline viewing of generated files (binaries, images, videos).

Backend changes (app.py):
- Update /api/code/execute to accept artifacts parameter
- Add /api/code/artifacts/<path> proxy endpoint for authenticated downloads
- Use SDK's _make_request for full parameter support
- Support artifacts in execution request body

Frontend changes (templates/chat.html):
- Pass artifacts=true in all code execution requests
- Display artifacts section with file info (name, type, size)
- Add download button for all artifact types
- Add view button with inline display for images/videos (disabled for binaries)
- Implement formatFileSize, downloadArtifact, viewArtifact helper functions
- Images and videos render directly in chat
- Text files display in formatted <pre> blocks

Documentation (CLAUDE.md):
- Document artifact support section
- List supported artifact types
- Describe frontend and backend features
- Update frontend integration notes

Artifact types supported:
- Compiled binaries (executables from C, C++, Rust, Go, etc.)
- Images (PNG, JPG, GIF, SVG)
- Videos (MP4, WebM)
- Text/data files (JSON, CSV, TXT)
2026-01-20 08:34:35 -05:00
331c11e8f7 Only truncate large code blocks on page refresh, not during streaming 2025-12-15 11:31:43 -05:00
5329bccdec Add Open Graph and Twitter Card meta tags for social sharing
- Add helper functions to extract first image and generate description from chat messages
- Update base.html and index.html with og:image, og:description, twitter:card meta tags
- Chat rooms now use first image from messages for unfurling instead of site logo
- Description shows first 500 chars of room content for link previews
- Add default OG image (black with OC) as fallback when no image found
2025-12-15 08:17:01 -05:00
6e279c0ed5 Use backend proxy for Unsandbox API calls to keep API key secure 2025-12-07 14:54:35 -05:00
ff9d8011a6 Fix model/voice localStorage persistence on page refresh
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.
2025-12-07 11:29:06 -05:00
0d19e0b9ce Fix model selection persistence: populate both dropdowns, validate stored value 2025-12-07 10:17:43 -05:00
589406d11d Fix CORS proxy URL to cors-proxy.uncloseai.com 2025-12-07 08:49:28 -05:00
19b8ce4037 Add external image URL support via CORS proxy
- 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
2025-12-07 08:47:36 -05:00
d9976ffe31 Fix image hover: use mouseover instead of mouseenter for event delegation 2025-12-07 08:39:58 -05:00
bd2dbf7ed5 Add vision model support with auto alt-text on image hover
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
2025-12-07 08:36:47 -05:00
030b1cc447 Use only stderr for code auto-fix error detection
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.
2025-12-01 11:49:33 -05:00
22ed72da7e Store auto-exec attempt on code block to avoid empty DOM elements
- 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
2025-11-30 15:17:27 -05:00
5c25229429 Fix classList check for non-element nodes
- Add null check for classList before calling contains()
- Handles case where nextSibling might be a text node
- Prevents 'classList is undefined' error
2025-11-30 14:56:04 -05:00
36e713147a Fix DOM insertion error in auto-exec results container
- 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
2025-11-30 14:47:17 -05:00
53f3e66943 Post auto-fixed code as new message and auto-execute
- 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
2025-11-30 14:45:43 -05:00
09c6f62083 Fix auto-retry: execute fixed code directly without posting to chat
- 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
2025-11-30 14:42:06 -05:00
52cb0e4fdf Fix auto-retry: check stdout for errors (Python sends tracebacks to stdout) 2025-11-30 14:38:59 -05:00
474b12f175 Add full job response logging to debug stderr field 2025-11-30 14:36:18 -05:00
d79a9c8117 Add debug logging for code execution auto-fix retry logic 2025-11-30 14:31:50 -05:00
6400a5eacb Add automatic code error fixing feature
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
2025-11-30 11:59:46 -05:00
a3d1e18a37 Fix light mode flash on chat page
Add inline script at top of base.html <head> to set theme immediately
before any rendering occurs. Prevents white flash when loading chat
and other pages that extend base.html in dark mode.
2025-11-30 10:23:53 -05:00
2a85bbe53e Make logged-in username clickable to profile page
- 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.
2025-11-30 10:02:58 -05:00
4d93819571 Fix theme flash and add logout to profile page
- Add inline script to set theme before page render (browse, profile)
- Prevents white flash when loading pages in dark mode
- Add logout button with confirmation to profile page
- Username already clickable in browse page header (links to profile)
2025-11-30 10:00:48 -05:00
41e9e8ae7c Switch code execution to Unsandbox API
- 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
2025-11-30 06:08:17 -05:00
dc263f8613 modified: templates/browse.html 2025-11-30 05:22:11 -05:00