diff --git a/public/components/my-resources.html b/public/components/my-resources.html
index a2e2a9f8..a163086a 100644
--- a/public/components/my-resources.html
+++ b/public/components/my-resources.html
@@ -154,11 +154,9 @@
shown; Images is every picture this account has generated, wherever it
was made, because a figure is worth keeping after the deck it was
drawn for has been replaced. -->
-
-
-
+
+
+
diff --git a/public/css/styles.css b/public/css/styles.css
index 9bb44aae..77523032 100644
--- a/public/css/styles.css
+++ b/public/css/styles.css
@@ -1555,3 +1555,14 @@ button, a, .btn-sm, .btn-generate, .btn-send, .tab-btn, input, textarea, select,
border-radius:8px;box-shadow:0 16px 48px rgba(0,0,0,.45);background:var(--g100);}
.img-lightbox-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:center;max-width:640px;}
.img-lightbox-caption{color:#e5e7eb;font-size:12px;line-height:1.5;text-align:center;}
+
+/* Library view switch. The active view has to be unmistakable — two buttons of
+ the same weight read as two buttons, not as a current view and an alternative. */
+.lib-tabs{display:flex;gap:2px;background:var(--g100);border-radius:8px;padding:3px;}
+.lib-tab{border:0;border-radius:6px;background:transparent;color:var(--g500);
+ font:inherit;font-size:12px;font-weight:500;padding:5px 12px;cursor:pointer;
+ transition:background .15s ease,color .15s ease,box-shadow .15s ease;}
+.lib-tab:hover{color:var(--g700);}
+.lib-tab[aria-selected="true"]{background:var(--white,#fff);color:var(--g900);font-weight:600;
+ box-shadow:0 1px 2px rgba(17,24,39,.10),0 0 0 1px rgba(17,24,39,.04);}
+.lib-tab:focus-visible{outline:2px solid var(--blue);outline-offset:1px;}
diff --git a/public/js/myResources.js b/public/js/myResources.js
index 1fe39758..270b24d9 100644
--- a/public/js/myResources.js
+++ b/public/js/myResources.js
@@ -281,8 +281,9 @@
[['tab-mr-docs', docs], ['tab-mr-images', !docs]].forEach(function (pair) {
var tab = document.getElementById(pair[0]);
if (!tab) return;
+ // aria-selected is the whole state: the stylesheet keys off it, so the
+ // visible highlight and what a screen reader announces cannot disagree.
tab.setAttribute('aria-selected', pair[1] ? 'true' : 'false');
- tab.style.background = pair[1] ? 'var(--white)' : 'transparent';
});
var list = document.getElementById('mr-list');
var panel = document.getElementById('mr-images-panel');
diff --git a/src/utils/visionTool.js b/src/utils/visionTool.js
index bdbef305..79ab2544 100644
--- a/src/utils/visionTool.js
+++ b/src/utils/visionTool.js
@@ -58,7 +58,18 @@ function describePrompt(question) {
'cannot see it can reason about it. Report only what is observable — colours, ' +
'distribution, morphology, text, readings, scale. Do not diagnose, do not ' +
'speculate about causes, and say plainly when something is unclear or cut off ' +
- 'rather than guessing.\n\nThe question to answer: ' + question;
+ 'rather than guessing.\n\n' +
+ // The question is written by a model that has not seen the image, from what
+ // the user said. So it can presume something that is not there — "describe
+ // this rash" about a photograph of a drug chart. Answering the presumption
+ // rather than the picture is the failure that matters, because the answer
+ // then travels back as fact.
+ 'The question below was written by someone who cannot see the image, so it ' +
+ 'may assume something the image does not show. Describe what is actually ' +
+ 'there. If the question presumes something absent — a finding, a body part, ' +
+ 'a kind of document — say so first, in plain words, and then describe what ' +
+ 'the image does contain.\n\n' +
+ 'The question: ' + question;
}
/**
diff --git a/test/vision-tool.test.js b/test/vision-tool.test.js
index 44ee0832..21ae90d8 100644
--- a/test/vision-tool.test.js
+++ b/test/vision-tool.test.js
@@ -56,6 +56,16 @@ test('a call routes the image to the vision model, never to the chat model', asy
assert.equal(out.content, 'That pattern fits...');
});
+test('a question that presumes the wrong thing is corrected, not answered', () => {
+ // The question is written by a model that has not seen the image, from what
+ // the user said — so "describe this rash" can arrive about a photograph of a
+ // drug chart. Answering the presumption would send that back as fact.
+ const prompt = visionTool.describePrompt('what is the distribution of this rash');
+ assert.match(prompt, /may assume something the image does not show/);
+ assert.match(prompt, /If the question presumes something absent/);
+ assert.match(prompt, /describe what the image does contain/);
+});
+
test('the seeing model is told to report, not to diagnose', () => {
// It has a picture and no conversation, no sources and no system prompt. An
// opinion from it would carry weight it has not earned.