From c74a9af8be2ca2f81c14b8f58ea5b091d3ab0020 Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Wed, 17 Jun 2026 02:18:10 +0000 Subject: [PATCH] feat: mobile UI, chat history search, news source filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Mobile (#11): responsive 3-button bottom nav (STATS/CHAT/INFO), panel switching, compact topbar, touch-friendly inputs; panels show one-at-a-time on screens <900px - Search (#12): πŸ” button next to TRANSMIT opens search modal; history.php endpoint queries conversations table; results show role, timestamp, and snippet - News filter (#13): βš™ gear on NEWS tab reveals category checkboxes; hidden categories stored in localStorage; empty-state message when all hidden Co-Authored-By: Claude Sonnet 4.6 --- api/endpoints/history.php | 21 +++++ public_html/api.php | 1 + public_html/index.html | 175 +++++++++++++++++++++++++++++++++++++- 3 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 api/endpoints/history.php diff --git a/api/endpoints/history.php b/api/endpoints/history.php new file mode 100644 index 0000000..a710072 --- /dev/null +++ b/api/endpoints/history.php @@ -0,0 +1,21 @@ + [], 'error' => 'Query too short']); + exit; +} + +$rows = JarvisDB::query( + "SELECT role, content, created_at FROM conversations + WHERE content LIKE ? ORDER BY created_at DESC LIMIT 25", + ['%' . $q . '%'] +) ?? []; + +echo json_encode(['results' => $rows, 'total' => count($rows)]); diff --git a/public_html/api.php b/public_html/api.php index 577c36b..49afe8b 100644 --- a/public_html/api.php +++ b/public_html/api.php @@ -72,6 +72,7 @@ $endpoints = [ 'agent' => 'agent.php', 'planner' => 'planner.php', 'jellyfin' => 'jellyfin.php', + 'history' => 'history.php', 'arc' => 'arc.php', 'directives' => 'directives.php', 'memory' => 'memory.php', diff --git a/public_html/index.html b/public_html/index.html index 57d59ec..9e627b8 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -301,11 +301,39 @@ body::after{ #mainLayout.swapped.focus-mode #rightPanel{transform:translateX(-20px)} #btn-swap-panels{background:none;border:1px solid var(--panel-border);color:var(--text-dim);padding:3px 8px;cursor:pointer;font-family:var(--font-mono);font-size:0.6rem;letter-spacing:1px;transition:all 0.2s} #btn-swap-panels:hover,#btn-swap-panels.active{color:var(--cyan);border-color:var(--cyan)} -/* Mobile fallback */ +/* ── MOBILE RESPONSIVE ──────────────────────────────────────────────── */ @media(max-width:900px){ - #mainLayout{grid-template-columns:1fr;grid-template-rows:auto} + #mainLayout{grid-template-columns:1fr!important;grid-template-rows:1fr!important;padding:6px;gap:6px} #leftPanel,#rightPanel{display:none} + #leftPanel.mob-active,#rightPanel.mob-active{display:flex!important;flex-direction:column} + #centerPanel{display:none} + #centerPanel.mob-active{display:flex!important;flex-direction:column} + #topBar{padding:0 10px;height:42px} + .tb-center{display:none} + .tb-logo{font-size:0.85rem;letter-spacing:2px} + #clock{font-size:0.85rem} + #date-display{display:none} + #btn-swap-panels,.btn-panels{display:none} + #inputArea{padding:8px 6px} + #textInput{font-size:0.85rem;padding:8px 10px;min-height:40px} + #sendBtn{min-height:40px;padding:0 14px;font-size:0.65rem} + #micBtn{min-height:40px;min-width:40px;font-size:1.1rem} + #app{padding-bottom:48px} + #mobileNav{display:flex!important} } +@media(min-width:901px){#mobileNav{display:none!important}} +#mobileNav{ + display:none;position:fixed;bottom:0;left:0;right:0;z-index:100; + background:rgba(0,8,22,0.96);border-top:1px solid var(--panel-border);height:48px; +} +.mob-nav-btn{ + flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center; + font-family:var(--font-display);font-size:0.5rem;letter-spacing:1.5px; + color:var(--text-dim);cursor:pointer;gap:2px;border:none;background:none;transition:color 0.2s; +} +.mob-nav-btn .mob-icon{font-size:1rem;line-height:1} +.mob-nav-btn.active{color:var(--cyan)} +.mob-nav-btn.active .mob-icon{filter:drop-shadow(0 0 4px var(--cyan))} /* Panel toggle button */ .btn-panels{ background:rgba(0,212,255,0.06); @@ -1210,6 +1238,7 @@ body::after{ + @@ -1283,6 +1312,13 @@ body::after{
+
+ +
+
@@ -2547,6 +2583,7 @@ function showApp(name, greeting, silent = false) { loadAlerts(); loadWeather(); loadNews(); + initMobile(); // Guardian Mode β€” badge refresh + proactive chat setTimeout(() => { _refreshGuardianBadge(); @@ -3231,6 +3268,17 @@ async function loadWeather() { } // ── NEWS ────────────────────────────────────────────────────────────── +function getNewsHidden() { + try { return JSON.parse(localStorage.getItem('news_hidden_cats') || '[]'); } catch(e) { return []; } +} +function setNewsHidden(arr) { localStorage.setItem('news_hidden_cats', JSON.stringify(arr)); } + +function toggleNewsFilter() { + const panel = document.getElementById('news-filter-panel'); + panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; +} + +let _newsCats = []; async function loadNews() { const d = await api('news'); const el = document.getElementById('news-list'); @@ -3238,10 +3286,24 @@ async function loadNews() { el.innerHTML = '
News loading...
'; return; } - const catLabels = { headlines: 'πŸ“° TOP HEADLINES', technology: 'πŸ’» TECHNOLOGY' }; + const catLabels = { headlines: 'πŸ“° TOP HEADLINES', technology: 'πŸ’» TECHNOLOGY', pinned: 'πŸ“Œ JARVIS PINNED' }; + const hidden = getNewsHidden(); + _newsCats = Object.keys(d.categories); + + // Build filter checkboxes + const cbContainer = document.getElementById('news-filter-checkboxes'); + if (cbContainer) { + cbContainer.innerHTML = _newsCats.map(cat => ` + `).join(''); + } + let html = ''; for (const [cat, articles] of Object.entries(d.categories)) { - if (!articles.length) continue; + if (!articles.length || hidden.includes(cat)) continue; html += `
${catLabels[cat] || cat.toUpperCase()}
`; for (const a of articles.slice(0, 5)) { const ctxKey = 'news_' + (cat + '_' + a.title).replace(/[^a-z0-9]/gi,'').slice(0,30); @@ -3254,11 +3316,24 @@ async function loadNews() {
`; } } + if (!html) html = '
All categories hidden β€” use βš™ to show sources
'; const ageMin = d.cache_age_s > 0 ? Math.round(d.cache_age_s/60) : 0; html += `
Updated ${ageMin}m ago
`; el.innerHTML = html; } +function toggleNewsCat(cat, show) { + const hidden = getNewsHidden(); + if (show) { + const idx = hidden.indexOf(cat); + if (idx > -1) hidden.splice(idx, 1); + } else { + if (!hidden.includes(cat)) hidden.push(cat); + } + setNewsHidden(hidden); + loadNews(); +} + // ── TABS ────────────────────────────────────────────────────────────── function switchTab(name) { if (name === 'sites') { openSitesModal(); return; } @@ -4996,6 +5071,68 @@ document.addEventListener('keydown', e => { if (e.key === 'Escape') closeVisionLightbox(); }); +// ── CHAT HISTORY SEARCH ─────────────────────────────────────────────────────── +function openSearchModal() { + document.getElementById('searchModal').style.display = 'flex'; + document.getElementById('searchInput').focus(); +} +function closeSearchModal() { + document.getElementById('searchModal').style.display = 'none'; + document.getElementById('searchResults').innerHTML = '
Type to search your JARVIS conversations
'; + document.getElementById('searchInput').value = ''; +} +async function runSearch() { + const q = document.getElementById('searchInput').value.trim(); + if (!q) return; + const el = document.getElementById('searchResults'); + el.innerHTML = '
Searching...
'; + try { + const d = await api('history?q=' + encodeURIComponent(q)); + if (!d.results || !d.results.length) { + el.innerHTML = '
No results for "' + q + '"
'; + return; + } + el.innerHTML = d.results.map(r => { + const role = r.role === 'user' ? 'πŸ‘€' : 'πŸ€–'; + const ts = new Date(r.created_at).toLocaleString('en-US', {month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}); + const snippet = r.content.length > 200 ? r.content.slice(0,197) + '…' : r.content; + return `
+
+ ${role} ${r.role.toUpperCase()} + ${ts} +
+
${snippet.replace(/ +
`; + }).join(''); + } catch(e) { + el.innerHTML = '
Search failed
'; + } +} +document.getElementById('searchModal')?.addEventListener('click', e => { + if (e.target === document.getElementById('searchModal')) closeSearchModal(); +}); + +// ── MOBILE PANEL SWITCHER ───────────────────────────────────────────────────── +function mobSwitch(which) { + if (window.innerWidth > 900) return; + const panels = {left:'leftPanel', center:'centerPanel', right:'rightPanel'}; + Object.entries(panels).forEach(([k, id]) => { + document.getElementById(id)?.classList.toggle('mob-active', k === which); + }); + document.querySelectorAll('.mob-nav-btn').forEach(b => b.classList.remove('active')); + document.getElementById('mob-btn-' + which)?.classList.add('active'); + if (which === 'right') loadNews(); +} +function initMobile() { + if (window.innerWidth > 900) return; + ['leftPanel','centerPanel','rightPanel'].forEach(id => + document.getElementById(id)?.classList.remove('mob-active')); + document.getElementById('leftPanel')?.classList.add('mob-active'); + document.querySelectorAll('.mob-nav-btn').forEach(b => b.classList.remove('active')); + document.getElementById('mob-btn-left')?.classList.add('active'); +} +window.addEventListener('resize', initMobile); + @@ -5009,5 +5146,35 @@ document.addEventListener('keydown', e => {

 
+ + + +