Files
librechat-custom/assets/custom2.js
T
Nathan Schneider d86483e5a1 restore green-wash theme (lost in the Oct 21 rebuild) + PWA band colors
The Oct 19 theming (light = #eaf3ea dashboard green wash, white cards/surfaces,
per approval) was never committed - the Oct 21 pin-migration rebuild from this
repo silently reverted the deployed theme to the older cream iteration.

- custom.css: light block restored to the approved green-wash palette
  (primary #eaf3ea, white secondary/chat cards, green hover/tertiary/border);
  loading screen + bare page background match.
- Dockerfile: manifest theme_color/background_color and the index.html
  theme-color media pair now follow the live palette (#eaf3ea light /
  #22271f deep pine dark).
- custom2.js: sync ONE meta theme-color to the app's resolved theme
  (html.dark class + prefers-color-scheme listener) so Android status/nav
  bands and iOS safe areas track the theme at runtime; the static media
  pair remains as the no-JS fallback.
2026-10-21 20:46:45 -06:00

96 lines
3.6 KiB
JavaScript

// Co-op UI polish.
// The auth form and footer are client-rendered (React), so a plain `load`
// listener runs too early — a MutationObserver re-checks on every DOM change.
(function () {
// One-time pin migration (2026-10-20): before the server sent
// interface.defaultPinnedTools, the client eager-seeded PIN_MCP_ and
// LAST_WEB_SEARCH_TOGGLE_pinned as 'false' — artifacts of the old default,
// not user choices (there is no unpin UI yet). Those stored values override
// the new server defaults forever, leaving Tools/Web Search unpinned.
// Clear them ONCE per browser; deliberate unpinning afterwards sticks.
try {
if (!localStorage.getItem('coopPinMigration20261020')) {
if (localStorage.getItem('PIN_MCP_') === 'false') localStorage.removeItem('PIN_MCP_');
if (localStorage.getItem('LAST_WEB_SEARCH_TOGGLE_pinned') === 'false') {
localStorage.removeItem('LAST_WEB_SEARCH_TOGGLE_pinned');
}
localStorage.setItem('coopPinMigration20261020', '1');
}
} catch (e) { /* storage unavailable — non-fatal */ }
function hideSignupPrompt() {
document.querySelectorAll('p').forEach(function (p) {
var link = p.querySelector('a[href*="register"]');
var text = (p.textContent || '');
if (link || text.indexOf("Don't have an account") !== -1) {
p.style.display = 'none';
}
});
}
// Footer: turn the plain co-op text (injected at build time) into the
// linked version — "Inference Cooperative" links to the homepage.
function fixFooter() {
var f = document.querySelector('footer');
if (!f || f.dataset.coopFooter) return;
f.dataset.coopFooter = '1';
f.innerHTML =
'<a href="https://inference.coop">Inference Cooperative</a>' +
' · Our AI, governed together';
}
// PWA chrome bands (Android status bar + navigation bar, iOS safe areas):
// LibreChat ships static meta theme-color tags and never updates them, so
// the OS bands keep the install-time colors. Sync ONE meta tag to the app's
// RESOLVED theme (html.dark class — LibreChat applies it from the user's
// theme setting, which defaults to system = the phone's theme). Falls back
// to the light/dark media-query pair in index.html when this script is off.
var COOP_BG = { light: '#eaf3ea', dark: '#22271f' };
function syncThemeColor() {
var dark = document.documentElement.classList.contains('dark');
var metas = document.querySelectorAll('meta[name="theme-color"]');
if (!metas.length) return;
// Replace the media-query pair with a single tag at the resolved color.
metas.forEach(function (m, i) {
if (i === 0) {
m.removeAttribute('media');
m.setAttribute('content', dark ? COOP_BG.dark : COOP_BG.light);
} else {
m.remove();
}
});
}
var themeObserverRunning = false;
function startThemeSync() {
if (themeObserverRunning) return;
themeObserverRunning = true;
syncThemeColor();
new MutationObserver(syncThemeColor).observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
});
var mq = window.matchMedia('(prefers-color-scheme: dark)');
if (mq && mq.addEventListener) mq.addEventListener('change', syncThemeColor);
}
function run() {
hideSignupPrompt();
fixFooter();
startThemeSync();
}
function start() {
run();
new MutationObserver(run).observe(document.body, {
childList: true,
subtree: true,
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', start);
} else {
start();
}
})();