sessionStorage was per-tab which forced re-entering the password on
every new tab to the same site. localStorage is scoped per-origin
which is the right granularity. The lock button clears the session
from localStorage when the user wants to end their session.
Each tab now requires its own password unlock. Navigating within a tab
preserves the session (sessionStorage survives navigation). Closing the
tab or opening a new tab requires re-entering the password. Previously
the session was in localStorage, shared across all tabs indefinitely.
Lock button was not properly securing the UI:
- decrypted chat messages stayed in DOM behind the vault gate
- vault gate had no background in base CSS (transparent when no theme set)
- settings content and input area remained visible after locking
- container lacked position:relative for proper gate overlay scoping
- add v1.0.0 release link and inline SHA-256 checksums
- update storage/privacy section to reflect mandatory vault encryption
- replace optional vault setup steps with first launch and returning flows
- add per-site vault and releases page link
window.sendMessage, window.handleUserInput, window.speakText etc
were overwriting host page functions with the same names. This broke
OpenCompletion chat (sendMessage clobbered). Now all exports live
under window.uncloseai namespace. Only uniquely-named globals
(UncloseVault, toggleUncloseaiEmbeddedModal, openTTSModal, etc)
remain on window directly for internal cross-module compat.
Vault password prompt is now the first thing shown when opening
uncloseai. No chat, no greeting, no data written until the user
creates or unlocks their vault. Each site has its own vault with
AES-256 encryption using password + device-specific salt.
- Add vault gate overlay blocking modal until password entered
- Remove plaintext conversation fallback from storage.js
- Route TTS mode, selected voice, language through vault
- Route custom API config reads through vault in models.js
- Add vault gate i18n strings (26 languages) explaining per-site
encryption of chat history, API keys, and settings
- Add missing keys to migration (voice, TTS mode)
- Re-show vault gate on lock from settings
- Permacomputer headers across all source files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
browser-toys.html: new "Encrypting your chats" section documenting UncloseVault
AES-256 encryption of conversations, settings, and API keys. How to enable,
what gets encrypted, how it works, automatic plaintext migration.
page-intelligence.js: deterministic page analysis (zero API calls) extracting
structured data, heading outline, reading metrics, readability score, code
detection, link topology, media inventory, forms, tables, entity patterns.
Integrated into greeting flow alongside LLM classification.
for known dynamic pages (GitLab CI jobs), fetch the raw/plain text
endpoint instead of scraping a half-loaded DOM. falls back to DOM
extraction with a MutationObserver settle wait when raw isn't accessible.
extractWebpageContent is now async, all callers updated.
new browser-toys.html with install instructions, storage/privacy
docs, and per-browser guides. sidebar link added to all 26 pages.
browser toys section added to index.html and uncloseai-js.html.
Explain how to download the 42+ cloned voice samples from LibriSpeech
using the new Makefile target. Updated on both the main TTS page and
the Qwen3-TTS subpage.
Replace generic engine names (Voice Cloning, Fast Synthesis, etc.)
with actual model names (Qwen3-TTS, Piper TTS, XTTS v2, Silero TTS,
Kokoro TTS) across all 6 TTS pages including titles, headers, and
sidebar nav. Add upstream HuggingFace/GitHub links in technical
details section of each engine subpage.
Rewrite text-to-speech.html to focus on the live service (Qwen3-TTS
voice cloning with 42+ voices). Each of the 5 engines now has its own
page under /tts/ with detailed docs, examples, and self-hosting
instructions. Engine cards on the main page link out with Live/Self-host
badges. Sidebar nav updated with TTS subsection on all 6 pages.
New pages:
- /tts/voice-cloning.html (Live - Qwen3-TTS)
- /tts/fast-synthesis.html (Self-host - Piper)
- /tts/hd-cloning.html (Self-host - XTTS v2)
- /tts/multilingual-cpu.html (Self-host - Silero)
- /tts/lightweight.html (Self-host - Kokoro)
- Update all examples from "alloy" to real voice names (aria, atlas)
- Add clickable links to /v1/voices and /v1/models endpoints
- Update voice count from 20+ to 42+ built-in cloned voices
- Bump TTS modal cache version to 4
- Update deployment instructions (remove vars.sh, add deploy-cpu)
- Add in-memory TTS cache for replay without regeneration
Firefox supports audio/webm;codecs=opus in MediaSource API,
enabling true streaming playback. Remove broken progressive
playback (partial blobs don't stream). Simplified fallback
to full buffer for any browser without MSE support.
Progressive playback was silently failing if response.body was null
or if the reader threw. Now falls back to full buffer if body isn't
streamable, logs chunk progress, and properly unblocks callers on
error instead of hanging forever.
Firefox can't use MediaSource for streaming. Instead of buffering
the entire response before playing, progressively read chunks and
start playback after ~8KB arrives. Replaces audio src with complete
blob when download finishes for seeking support.
Update text-to-speech.html to document Qwen3-TTS as the default
5th engine with 20 cloned voices and 10-language support.
Firefox on Linux reports canPlayType('audio/mpeg') as supported
but then fails to decode mp3. Use user-agent detection instead:
Firefox always gets opus (ogg), Chromium browsers get mp3.
Firefox on Linux lacks mp3 decoders. Detect browser capabilities
at load time: use mp3 for Chrome, opus (ogg) for Firefox.
MediaSource streaming only works with mp3 (Chrome); Firefox
falls back to buffered playback with opus format.
Sends response_format parameter to TTS server.
- speakChatText now uses handleTTS instead of speakTextDirect
so embed modal TTS (intro, chat messages) respects streaming mode
- Streaming result updates blobUrl on completion so existing
download click handlers work without changes
- speakTextStreaming now returns immediately with the audio element
already playing, plus a done promise for when the stream completes
- Modal wires up controls immediately using the live audio element
- Pause/play works during streaming (stream continues in background)
- Button shows "Streaming..." while receiving, switches to "Pause" when done
- Download button enables only after stream completes
- All TTS functions now return consistent done promise interface