From c09038bfeca7408dad2198f00279ec5701087e8a Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 7 Sep 2026 16:11:49 +0200 Subject: [PATCH] fix: compact prompt administration with robust admin loading and concise styling --- public/components/admin.html | 11 +- public/js/admin.js | 171 ++++++++++++------ public/js/admin/clinicalAssistant.js | 6 + test/frontend-prompt-env.test.js | 261 ++++++++++++++++++--------- 4 files changed, 301 insertions(+), 148 deletions(-) diff --git a/public/components/admin.html b/public/components/admin.html index ba747517..b4f9ffe5 100644 --- a/public/components/admin.html +++ b/public/components/admin.html @@ -167,7 +167,6 @@ Saved overrides replace shipped defaults immediately
-

Global Scribe instructions for the operations listed under each prompt. These are not Clinical Assistant prompts or Markdown formatting controls. Personal Memories and templates are separate and are not managed here.

Loading Scribe prompts...
@@ -336,30 +335,26 @@
- +
- +
-

The result limit also bounds selected sources; visual queries separately request up to 8 multimodal candidates before selection. Excerpt size is not the conversation budget.

Conversation input budget — read-only server configuration

Loading server budget metadata...

Set by CLINICAL_ASSISTANT_CONVERSATION_CHARS, or the server default when unset; legacy saved settings are ignored. Counts all prior user/assistant text plus the new question using JavaScript string length (UTF-16 code units), not provider tokens. Over-budget requests stop before inference; no automatic clipping or summarization.

-

Text answer output limits: 2,600 tokens initially and 5,000 for completion retries. Image input is separate: existing image routes trim and clip the user prompt at 5,000 UTF-16 code units before adding poster/layout instructions. This is not a new image budget or output-token limit.

+

Output limits: 2,600 tokens per text answer (5,000 on completion retry); image prompts clip at 5,000 UTF-16 code units.

Clinical Assistant TEXT — system behavior

-

Overrides the default behavior for retrieved text answers (including streaming). Fixed greeting/no-source replies, search rewrites and explicit handoff instructions are separate, not controlled by this editor.

-

Fixed citation safeguards (read-only): Use only retrieved evidence for factual claims and keep the exact provided source numbers. Do not invent, renumber, merge or move citations. Runtime evidence/citation safeguards are appended separately; this editor does not replace them.

Loading clinical text prompt...

Clinical Assistant IMAGE — poster instructions

-

Overrides the default poster instruction appended to the image user prompt by both direct and background-job image generation. Existing automatic portrait/landscape layout suffixes still follow it. This does not change text answers or image-job storage.

Loading clinical image prompt...
diff --git a/public/js/admin.js b/public/js/admin.js index 2123d27a..cae0bd1a 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -31,6 +31,13 @@ function adminFlashButtonBackground(btn, color) { setTimeout(function() { if (btn) btn.style.background = ''; }, 2000); } +// True when the admin tab is already active AND its component markup is loaded, +// i.e. tabChanged for admin has already fired (or will never fire again). +function adminTabActive() { + var tab = document.getElementById('admin-tab'); + return !!tab && tab.classList.contains('active') && tab.dataset.loaded === '1'; +} + { let loaded = false; @@ -292,14 +299,22 @@ function adminFlashButtonBackground(btn, color) { { let cmsLoaded = false; + let promptsLoaded = false; + let promptsLoading = false; + + function startCms() { + if (!cmsLoaded) { loadCms(); loadOidcConfig(); cmsLoaded = true; } + } // Load CMS when admin tab is opened (via tabChanged event or click) document.addEventListener('tabChanged', function(e) { - if (e.detail && e.detail.tab === 'admin') { - if (!cmsLoaded) { loadCms(); loadOidcConfig(); cmsLoaded = true; } - } + if (e.detail && e.detail.tab === 'admin') startCms(); }); + // Catch-up: when the admin tab is already active and loaded at module init + // (e.g. restored tab before this module evaluated), tabChanged may never fire again. + if (adminTabActive()) startCms(); + document.addEventListener('click', function(e) { // Save buttons @@ -511,9 +526,6 @@ function adminFlashButtonBackground(btn, color) { } }); - let promptsLoaded = false; - let promptsLoading = false; - async function promptRequest(url, method, body) { var response = await fetch(url, { headers: getAuthHeaders(), method: method || 'GET', @@ -539,9 +551,12 @@ function adminFlashButtonBackground(btn, color) { groups.forEach(function(group) { if (!group[1]) return; group[1].replaceChildren(); - (data.prompts || []).filter(function(p) { return p.family === group[0] && p.editable === true; }) - .forEach(function(p) { group[1].appendChild(createPromptEditor(p)); }); - if (!group[1].children.length) group[1].textContent = 'No editable prompts available in this family.'; + var prompts = (data.prompts || []).filter(function(p) { return p.family === group[0] && p.editable === true; }); + if (!prompts.length) { + group[1].textContent = 'No editable prompts available in this family.'; + return; + } + group[1].appendChild(createPromptFamilyEditor(group[0], prompts)); }); promptsLoaded = true; } catch (error) { @@ -556,50 +571,69 @@ function adminFlashButtonBackground(btn, color) { } finally { promptsLoading = false; } } - function createPromptEditor(prompt) { - var revision = prompt.revision; - var currentRevision = revision; - var viewedRevision = null; - var busy = false; - var base = '/api/admin/config/prompts/' + encodeURIComponent(prompt.dbKey); - var editor = document.createElement('details'); - editor.dataset.promptKey = prompt.dbKey; - editor.open = prompt.family !== 'scribe'; - editor.style.cssText = 'border-top:1px solid var(--g200);padding:12px 0;'; + // Compact per-family editor: one prompt select, one draft textarea, one status + // line, save/restore/history actions and a compact revision list for the + // SELECTED prompt. Drafts and revision baselines are kept per key in memory, + // so switching prompts (or families) never loses another prompt's unsaved draft. + function createPromptFamilyEditor(family, prompts) { + var editor = document.createElement('div'); + editor.className = 'cms-prompt-family'; + editor.dataset.family = family; + editor.style.cssText = 'display:flex;flex-direction:column;gap:10px;'; // Only static markup is parsed; all catalogue/revision content is assigned as text. - editor.innerHTML = '

' + - '' + - '

' + + editor.innerHTML = + '' + + '' + + '

' + + '
' + '' + - '' + + '' + '
' + - '