diff --git a/Plugin/unraid/include/ai_chat.php b/Plugin/unraid/include/ai_chat.php
index 7b58647..8a39801 100644
--- a/Plugin/unraid/include/ai_chat.php
+++ b/Plugin/unraid/include/ai_chat.php
@@ -2163,9 +2163,60 @@ vv_ai_profiles_script();
if (!box) return null;
let rows = [], activeId = '';
+ // Optional first section: this card's own profile, collapsed, holding its most recent
+ // conversations. Underneath it the full list carries on unchanged.
+ //
+ // The two answer different questions. "Where was I in General Chat" is the one asked on a
+ // card pinned to General Chat, and a single flat list buries it under whatever the tabs have
+ // been doing — the profile with the most threads wins the top of the list regardless of which
+ // card you are looking at. Collapsed by default because the card resumes the newest of these
+ // anyway; opening it is for reaching the other nine.
+ const GROUP = o.groupProfile || '';
+ const GROUP_MAX = o.groupMax || 10;
+ const OPEN_KEY = 'vvAiListOpen:' + (o.into || '');
+ let groupOpen = false;
+ try { groupOpen = localStorage.getItem(OPEN_KEY) === '1'; } catch (_) {}
+
+ function rowHtml(c, P) {
+ const prof = (c.profile && c.profile !== 'chat' && P[c.profile]) ? P[c.profile].short : '';
+ const tag = [prof, c.scope || ''].filter(Boolean).join(' · ');
+ return `
`
+ + ``
+ + (tag ? `${esc(tag)} ` : '')
+ + `${esc(c.title)}`
+ + `${esc(ago(c.ts))}`
+ + `×`
+ + `
`;
+ }
+
+ function groupHtml() {
+ if (!GROUP) return '';
+ const P = window.VvAiProfiles || {};
+ const name = (P[GROUP] && P[GROUP].short) || (GROUP === 'chat' ? 'General Chat' : GROUP);
+ const mine = rows.filter(c => (c.profile || 'chat') === GROUP)
+ .sort((a, b) => (b.updated || b.ts || 0) - (a.updated || a.ts || 0))
+ .slice(0, GROUP_MAX);
+ return ``
+ + `
`
+ + `${groupOpen ? '▾' : '▸'}`
+ + `${esc(name)}`
+ + `${mine.length}`
+ + `
`
+ + (groupOpen
+ ? `
`
+ + (mine.length ? mine.map(c => rowHtml(c, P)).join('')
+ : '
none yet
')
+ + `
`
+ : '')
+ + `
`
+ + `
`;
+ }
+
function render() {
if (!rows.length) {
- box.innerHTML = 'no saved conversations yet
';
+ box.innerHTML = groupHtml() + 'no saved conversations yet
';
return;
}
// The scope is shown, not just stored. A Scheduler thread is about one script or log,
@@ -2176,18 +2227,8 @@ vv_ai_profiles_script();
// makes the rows that are genuinely different harder to pick out, which is the opposite
// of the point.
const P = window.VvAiProfiles || {};
- box.innerHTML = '' + rows.map(c => {
- const prof = (c.profile && c.profile !== 'chat' && P[c.profile]) ? P[c.profile].short : '';
- const tag = [prof, c.scope || ''].filter(Boolean).join(' · ');
- return `
`
- + ``
- + (tag ? `${esc(tag)} ` : '')
- + `${esc(c.title)}`
- + `${esc(ago(c.ts))}`
- + `×`
- + `
`;
- }).join('') + '
';
+ box.innerHTML = groupHtml()
+ + '' + rows.map(c => rowHtml(c, P)).join('') + '
';
}
function load() {
@@ -2197,6 +2238,14 @@ vv_ai_profiles_script();
}
box.addEventListener('click', e => {
+ // Before the row handler: the header sits above the rows and must not be read as one.
+ const grp = e.target.closest('[data-grp]');
+ if (grp) {
+ groupOpen = !groupOpen;
+ try { localStorage.setItem(OPEN_KEY, groupOpen ? '1' : '0'); } catch (_) {}
+ render();
+ return;
+ }
const del = e.target.closest('[data-del]');
if (del) {
e.stopPropagation();
diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php
index 60e6e74..8364c15 100644
--- a/Plugin/unraid/pages/monitor.php
+++ b/Plugin/unraid/pages/monitor.php
@@ -2867,7 +2867,14 @@ if (document.getElementById('vv-mon-ai-chat')) {
// for the poll to reflect it reads as the card being broken.
onTurn: () => vvLoadTokens(),
});
- vvMonChatList = VvAiChatList({ into: 'vv-mon-ai-chats', chat: vvMonChat });
+ vvMonChatList = VvAiChatList({
+ into: 'vv-mon-ai-chats',
+ chat: vvMonChat,
+ // This card is pinned to General Chat, so its own threads get the top section rather than
+ // competing for position with everything the tabs have been doing.
+ groupProfile: 'chat',
+ groupMax: 10,
+ });
vvMonChatList.reload();
// Re-measured rather than computed once. The Assistant changes height when the transcript is