Commit graph

539 commits

Author SHA1 Message Date
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
211dfa0eba
tts: explicit colors on queued/loading buttons for dark mode
Default browser disabled-button styling washes out to near-invisible
against dark backgrounds. Force --button-secondary background + white
text + opacity 1 on .tts-queued and .tts-loading so the indicator stays
legible in both themes.
2026-06-03 15:00:56 -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
b5958382df
fix(tts): clean sentence highlight, drop box-shadow square on line wraps
Use a background highlight with box-decoration-break: clone so a wrapped
sentence highlights smoothly instead of rendering a pale square box.
2026-05-29 13:33:54 -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
8110080a4f fix(code-exec): surface real Unsandbox errors instead of flat 500
/api/code/execute and /api/code/jobs (GET+DELETE) swallowed every
exception into a print() and returned a generic 500, making the
"Failed to execute code" failure undebuggable on deployed boxes
where buffered stdout is lost.

Add _unsandbox_error_response helper: passes the upstream HTTP
status + response body through to the JSON response and prints a
full traceback. Credentials live in request headers, never in
response bodies, so echoing the upstream body exposes no secrets.
2026-05-18 19:07:02 -04:00
8fe489a97c
modified: CLAUDE.md 2026-05-18 15:15:13 -04:00
073f766e6f style: avoid "the", use "our" — writing style rule + sweep 2026-03-31 13:20:21 -04:00
539bd21fce Ignore research/tmp*.yaml generated files 2026-03-16 09:52:30 -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
timehexon
f963cd9443 Replace "AI" with "machine learning" in CLAUDE.md
Machine learning is what we grow. "AI" is forbidden in all
permacomputer discourse, marketing, & documentation.
2026-02-02 19:56:03 +00: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
b53ee2168b modified: app.py 2026-01-20 08:58:19 -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
27a0df3d12 Migrate to official Unsandbox Python SDK
Replace manual HMAC authentication with official Unsandbox Python SDK (un.py).
Simplifies code execution proxy endpoints by using SDK methods: execute_async,
get_job, and cancel_job. Removes ~40 lines of manual HTTP/auth code.

Changes:
- Add official Unsandbox Python SDK (un.py)
- Refactor app.py proxy endpoints to use SDK methods
- Update CLAUDE.md documentation with SDK setup and usage
- Remove manual HMAC signing code
- Maintain backward compatibility with existing API endpoints
2026-01-20 08:21:58 -05:00
a825633be5 Add MODEL_ENDPOINT_0 to functional tests in GitHub Actions 2026-01-11 11:56:08 -05:00
ae875a7269 Exclude venv from flake8 and black in GitHub Actions 2026-01-11 11:55:41 -05:00
237888a6ab Exclude venv from flake8 and black in CI 2026-01-11 11:52:40 -05:00
532523e1b3 Remove GIT_CLEAN_FLAGS 2026-01-11 09:39:14 -05:00
8a77b395a9 Add GIT_CLEAN_FLAGS to clean venvs between CI runs 2026-01-11 09:38:26 -05:00
69703b1e82 Fix test_initialize_model_map_with_env_vars: clear CI env vars 2026-01-11 09:18:55 -05:00
cf63e5c6cd Fix tiktoken mock: use class not instance, remove TESTING check 2026-01-11 09:17:49 -05:00
9fb7047866 Fix tiktoken SSL/gevent conflict in tests: use fallback token counting 2026-01-11 08:42:51 -05:00
4cc7e61c8b Fix GitLab CI for shell executor: use python3 and venv 2026-01-11 08:32:35 -05:00
6a4d9d560b Add GitLab CI pipeline for unit, integration, and functional tests 2026-01-11 07:53:58 -05:00
f5afedc5f7 Fix unturf capitalization to lowercase 2026-01-11 07:14:43 -05:00
ddf071161e Document git remote configuration with dual push targets 2026-01-11 07:14:12 -05:00
0d5d2c27be Switch Unsandbox API auth to HMAC-SHA256 with public/secret keys
Replace simple Bearer token auth with HMAC-SHA256 signature scheme:
- UNSANDBOX_PUBLIC_KEY for account identification (Bearer token)
- UNSANDBOX_SECRET_KEY for request signing (never transmitted)
- X-Timestamp header for replay attack prevention
- X-Signature header with HMAC-SHA256(secret, ts:method:path:body)
2025-12-28 13:44:38 -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
50404f62d9 Fix F824 lint error and add lint instructions to CLAUDE.md 2025-12-07 15:34:35 -05:00
6e279c0ed5 Use backend proxy for Unsandbox API calls to keep API key secure 2025-12-07 14:54:35 -05:00
8c128878c4 Fix is_base64_image() to detect all image formats, add debug logging
- is_base64_image() now detects any base64 image (jpeg, png, gif, webp, etc.)
  not just the hardcoded jpeg/png patterns
- Added [Vision] debug logging to trace image fetch/save flow
2025-12-07 13:58:47 -05:00