mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-15 23:59:20 +00:00
* fix(sidebar): keep minimized icon rail in sync with per-tab visibility Per-tab visibility (Customize UI / Appearance checkboxes, stored in localStorage under `odysseus-ui-visibility`) was only applied to the full sidebar elements — `UI_VIS_MAP` never targeted the collapsed `#icon-rail` launchers. So a user who turned a tab off (e.g. Email) in the full view saw every tab reappear when minimizing the sidebar to the icon rail. Pair each tool/section selector with its `#rail-*` counterpart (mapping mirrors `_railToolMap`), so `applyUIVis()` hides the rail launcher too. Admin feature-flag handling is unaffected: the features-fetch reconcile at app.js already re-applies `applyUIVis()`, so rail launchers now track admin disables exactly like their sidebar buttons. Adds a static regression test asserting every customizable tab pairs its rail button. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(sidebar): extract UI visibility into testable module Move UI_VIS_MAP, UI_VIS_DEFAULT_OFF, and a pure resolveVisibility() into static/js/ui_visibility.js so the icon-rail visibility rules are unit testable without a DOM. app.js applies resolveVisibility() to the document, replacing the ad-hoc tools-section override with an inline parent rule (tools-section off hides every tool rail launcher). Add edge-case tests covering per-tool off, the tools-section parent rule, parent+child combos, email-section, and the tool-library <-> #rail-archive mapping. Refs #5985 Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Alexandre Teixeira <111787685+alteixeira20@users.noreply.github.com>
73 lines
No EOL
3.4 KiB
JavaScript
73 lines
No EOL
3.4 KiB
JavaScript
// static/js/ui_visibility.js
|
|
//
|
|
// Per-item visibility for the sidebar and collapsed icon rail. Drives the
|
|
// Settings → Appearance ("Customize UI") checkboxes, persisted in localStorage
|
|
// under `odysseus-ui-visibility` (loaded/saved by app.js).
|
|
//
|
|
// Each key maps to the CSS selector(s) it controls. Tool/section selectors pair
|
|
// the full-sidebar element with its #rail-* launcher so a tab hidden in the
|
|
// full view also hides when the sidebar is minimized to the icon rail (id
|
|
// mapping mirrors _railToolMap in app.js; #tool-library-btn ↔ #rail-archive).
|
|
|
|
// Selector map: UI customization key → CSS selector(s) for its target(s).
|
|
export const UI_VIS_MAP = {
|
|
'sidebar-brand': '.sidebar-brand-title',
|
|
'sidebar-new-chat': '#sidebar-new-chat-btn',
|
|
'sidebar-search': '#sidebar-search-btn',
|
|
'sessions-section': '#sessions-section',
|
|
'email-section': '#email-section, #rail-email',
|
|
'tools-section': '#tools-section',
|
|
// Per-tool entries pair the sidebar button with its rail launcher.
|
|
'tool-calendar': '#tool-calendar-btn, #rail-calendar',
|
|
'tool-compare': '#tool-compare-btn, #rail-compare',
|
|
'tool-cookbook': '#tool-cookbook-btn, #rail-cookbook',
|
|
'tool-research': '#tool-research-btn, #rail-research',
|
|
'tool-gallery': '#tool-gallery-btn, #rail-gallery',
|
|
'tool-library': '#tool-library-btn, #rail-archive',
|
|
'tool-memory': '#tool-memory-btn, #rail-memory',
|
|
'tool-notes': '#tool-notes-btn, #rail-notes',
|
|
'tool-tasks': '#tool-tasks-btn, #rail-tasks',
|
|
'tool-theme': '#tool-theme-btn, #rail-theme',
|
|
'user-bar': '#user-bar-profile',
|
|
'sidebar-settings-btn':'#user-bar-settings',
|
|
'chat-meta': '.chat-meta-overlay',
|
|
'welcome-text': '.welcome-name, .welcome-sub, #welcome-tip',
|
|
'incognito-btn': '.incognito-btn',
|
|
'web-toggle-btn': '#web-toggle-btn',
|
|
'doc-toggle-btn': '#overflow-doc-btn',
|
|
'rag-toggle-btn': '#overflow-rag-btn',
|
|
'bash-toggle-btn': '#bash-toggle-btn',
|
|
'overflow-plus-btn': '.overflow-wrapper',
|
|
'mode-toggle': '.mode-toggle',
|
|
'preset-mini-btn': '#overflow-preset-btn',
|
|
'attach-btn': '#overflow-attach-btn',
|
|
'research-btn': '#overflow-research-btn',
|
|
'rail-new-chat': '#rail-new-session',
|
|
};
|
|
|
|
// Keys hidden by default on first run (no localStorage yet).
|
|
export const UI_VIS_DEFAULT_OFF = new Set(['rag-toggle-btn', 'text-emojis', 'chat-fullwidth']);
|
|
|
|
/**
|
|
* Resolve every UI_VIS_MAP selector to visible (true) or hidden (false) for the
|
|
* given saved state. Pure: no DOM, no localStorage — app.js applies the result.
|
|
*
|
|
* A key is visible when its stored value is not `false`, defaulting to on
|
|
* unless it is in UI_VIS_DEFAULT_OFF. Per-tool entries also require the Tools
|
|
* section to be on: hiding Tools hides every tool, mirroring the full sidebar
|
|
* where the #tools-section container already hides them (the rail has no
|
|
* container, so this rule keeps it in sync).
|
|
*
|
|
* @param {Record<string, boolean>} state
|
|
* @returns {Record<string, boolean>} selector → visible
|
|
*/
|
|
export const resolveVisibility = (state = {}) => {
|
|
const toolsOn = state['tools-section'] !== false;
|
|
const out = {};
|
|
for (const [key, selector] of Object.entries(UI_VIS_MAP)) {
|
|
let visible = key in state ? state[key] !== false : !UI_VIS_DEFAULT_OFF.has(key);
|
|
if (!toolsOn && key.startsWith('tool-')) visible = false;
|
|
out[selector] = visible;
|
|
}
|
|
return out;
|
|
}; |