- Added copyToClipboard() in utils.js with dual strategy:
- HTTPS: navigator.clipboard.writeText() (modern API)
- HTTP: textarea + execCommand('copy') (legacy fallback)
- viewer.js: uses copyToClipboard() instead of direct navigator.clipboard
- popout.html: same dual-strategy inline
- Fixed: 'Erreur lors de la copie' on HTTP contexts
- CSS: .code-block-wrapper with relative positioning
- CSS: .copy-code-btn with hover opacity transition (hidden until hover)
- CSS: .copied state (green accent) after successful copy
- viewer.js: wraps each <pre> in .code-block-wrapper + appends copy button
- popout.html: same logic with vanilla JS (ES5 compatible)
- Button: 'Copier' → '✓ Copié' on click, reverts after 2s
- Toast: showToast('Code copié dans le presse-papiers') via navigator.clipboard
- Script now re-executes itself with 'sudo' if available (or 'doas' as fallback)
- No need to prefix with sudo/doas — just run ./install-gitea-runner.sh ...
- Falls back to clear error if neither sudo nor doas is installed
- Exits early with clear error if not run as root
- Suggests 'doas' on Alpine, 'sudo' on Debian/Arch
- Prevents silent failure from apk add / apt-get requiring root
- act_runner register --no-interactive: prevents interactive prompt that ignores flags
- systemd: WorkingDirectory=/home/bruno: daemon needs .runner file in working dir
- systemd: User=bruno: runs as correct user
- Updated both install-gitea-runner.sh and Obsidian documentation
- All 8 server sections fixed with consistent config
- .gitea/workflows/desktop-build.yml: compiles on push to main
- Windows runner: Rust + Tauri + Python embed → MSI
- Linux runner: Rust + Tauri + webkit2gtk → AppImage + .deb
- Artifacts uploaded with 30-day retention
- Triggers when desktop/, frontend/, or backend/ changes
- Manual trigger via workflow_dispatch
- Updated ROADMAP #77 to mark CI/CD as done
- Iterates through ALL computed CSS variables (--*) from main app
- Uses getComputedStyle which works even if setProperty was used (themes.js)
- Sets properties via setProperty on popout root — EXACT theme match
- Fallback: localStorage obsigate-theme-mode for direct popout access
- Fixed typo in --text-primary (was 'teext-primary')
- Copies documentElement.style.cssText from main app (has all CSS variables from themes.js setProperty)
- This propagates ANY theme (Github, Linear, Spotify, Notion, etc.) dark+light
- Fallback: localStorage obsigate-theme + obsigate-theme-mode for direct popout access
- Removes the !important hack — replaced by proper cssText inheritance
- Added <style id='theme-force'> with !important dark theme defaults
- JS updates inline style for light mode
- Completely bypasses CSS variable loading, caching, or path issues
- Guarantees body bg/text color regardless of stylesheet state
- Added full dark theme variables to bare :root selector (no attribute required)
- This ensures dark theme always loads even if data-theme is not set or stripped
- :root[data-theme='light'] still overrides for light mode
- Popout windows now always get dark theme baseline
- Pass current theme as ?theme=dark|light in popout URL
- viewer.js, app.js, palette.js all pass theme to popout
- popout.html reads theme from URL param first (fallback: localStorage, then dark)
- Also syncs theme back to localStorage when set via query param
- Port 2020 already in use on CI runner
- Changed to port 2029 in CI workflow + playwright config + test
- Added docker rm -f obsigate-e2e before starting
- Root package.json has type:commonjs which breaks ES module syntax checks
- Added frontend/js/package.json with type:module for Node.js parsing
- All 27 JS modules now parse correctly in CI environment
- Both buttons call window.handleLogout (AuthManager.logout) or doLogoutFallback
- doLogoutFallback: XHR POST, hides #app, shows #login-screen, clears token
- No more localStorage.clear() or hard redirect — mimics showLoginScreen()
- Removed all conflicting JS event listeners
- Header #logout-btn: inline fetch+clear localStorage+redirect
- Settings #profile-logout: inline window.handleLogout or fetch fallback
- Removed all JS event listeners that were causing race conditions
- Zero dependency on ES module loading order
- profile-logout now handles logout directly via onclick attribute in HTML
- Calls window.handleLogout() (auth.js global) or direct fetch fallback
- Removed broken JS handler from config.js to avoid double execution
- Replaced window.handleLogout indirection with direct AuthManager.logout() call
- config.js already imports AuthManager from auth.js — no race condition
- Same direct call as header button (auth.js line 210)
- Removed localStorage restore of obsigate-right-sidebar-visible on init
- TOC only opens when user clicks the TOC button in a document
- Width preference still restored from localStorage
- Header logout (#logout-btn) already had no confirm (AuthManager.logout called directly)
- Settings panel (#logout-btn + #profile-logout) now call handleLogout() without confirm
- Both buttons redirect to login screen immediately — consistent behavior
- Both #logout-btn and #profile-logout now delegate to window.handleLogout()
- Previously they did bare fetch+redirect without clearing JWT token or session state
- Added credentials: 'include' fallback for robustness
#74 — Support PDF complet:
- backend/pdf_reader.py — pymupdf/pypdf text extraction
- .pdf added to SUPPORTED_EXTENSIONS in indexer.py
- PDF-aware file view API endpoint (metadata, text preview)
- Streaming endpoint /api/file/{vault}/pdf/stream
- Frontend PDF viewer with iframe, toolbar, download
- CSS for PDF viewer container
#75 — Split View (PaneManager):
- New module frontend/js/pane-manager.js — ES module
- 1-4 pane CSS grid with resize handles
- Split right/down via keyboard (Ctrl+Alt+\) or PaneManager API
- Persistence in localStorage (obsigate-panes)
- Collapse to single pane, per-pane active state
285 tests passent
Initialize i18n before auth so t() works when rendering
auth-dependent UI, and build the command palette lazily so
translations are loaded first. Bump service worker cache version.
The SW was the root cause of all persistent French text issues.
Even after rebuild, browsers with a registered SW would serve
cached old files (viewer.js without data-i18n, old locale JSONs).
SW-killer runs before any module script:
1. Detects if SW is controlling the page
2. Unregisters all SW registrations
3. Clears all caches
4. Reloads the page once (sessionStorage flag to prevent loop)
Other fixes in this commit:
- auth.js: import setLocale statically (was dynamic import)
- auth.js: import loadVaults/loadTags from sidebar.js
- sw.js: add JS modules + locales to STATIC_ASSETS for versioning
- sw.js: CACHE_VERSION v4→v5
Root cause: cacheFirstStatic() in sw.js was caching viewer.js,
i18n.js, auth.js, dashboard.js, and locale JSON files under the old
CACHE_VERSION. Even after rebuild, the old SW served stale cached
files without data-i18n attributes, so dashboard stayed in French.
Changes:
- sw.js: add js/viewer.js, js/i18n.js, js/auth.js, js/dashboard.js,
js/app.js, locales/en.json, locales/fr.json to STATIC_ASSETS
so they are versioned with CACHE_VERSION and properly invalidated
- CACHE_VERSION already bumped to v5 in previous commit
Manual fix for existing clients:
Chrome → F12 → Application → Service Workers → Unregister
Or: chrome://serviceworker-internals/ → Unregister
Then Ctrl+Shift+R to hard-refresh
- auth.js: window.handleLogout at module top level (was inside
renderUserMenu, not always called). Fixes settings dropdown
'Sign out' onclick='handleLogout()' doing nothing.
- auth.js: add missing imports (loadVaults, loadTags from sidebar.js)
and dynamic import for IndexUpdateManager (circular dep avoidance).
Fixes 'loadVaults is not defined' error on login.
- sw.js: CACHE_VERSION v4→v5 to bust stale Service Worker cache
that was serving old viewer.js without data-i18n attributes,
causing dashboard tabs to stay French despite English profile.
To apply: rebuild container + unregister SW in DevTools + reload.
The header dropdown 'Sign out' menu item used onclick='handleLogout()'
but handleLogout was never defined as a global function.
Added window.handleLogout in auth.js that delegates to
AuthManager.logout(), same as the existing logout-btn handler.