pediatric-ai-scribe-v3/public/components/my-resources.html
Daniel 59226f2109
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 46s
Forgejo Docker Build / Root app tests (push) Successful in 46s
Forgejo Android APK / Build signed APK (push) Successful in 2m13s
Forgejo Docker Build / Build Docker image (push) Successful in 9s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
feat: deck themes — a palette you pick, previewed by the renderer itself
A deck had exactly one look. The slide vocabulary is structural — bullets,
compare, table, callout, figure — and none of it carries a colour, so "make it
yellow" had nowhere to land but the image prompts, and produced yellow figures
on a blue deck.

A theme is a palette and a typeface in assets/deck-themes.json. render_pptx.py
rebinds INK, MUTED, ACCENT, RULE and PAPER from it in one place, so every slide
builder follows without a line changing in any of them — five themes restyle ten
slide types for free. An unusable theme leaves the default standing, because a
deck in the wrong colours beats a deck that will not render.

The theme rides on the deck, which is already the renderer's spec, so nothing
has to thread it through. It is validated against the same catalogue the
renderer reads: an id the renderer would ignore is never stored, so a deck
cannot claim a look it does not have.

PUT /my-resources/:id/theme re-skins a stored deck — a column write, no model
call, nothing that can reword a slide — and the next download is in the new
colours. Offered in the library only on rows that have a deck; flat markdown has
no palette.

Previews are rendered by the renderer, one representative compare slide per
theme, cached because each costs a pptx render, a Gotenberg round trip and a
rasterise. Drawn rather than mocked up: a hand-made swatch drifts the moment a
palette or a layout changes, and a preview that is not true is worse than none.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 19:40:19 +02:00

196 lines
11 KiB
HTML

<div class="card">
<div class="card-header">
<h3><i class="fas fa-folder-open"></i> My Resources</h3>
<span style="font-size:12px;color:var(--g500);">Only you can see these</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<p style="margin:0;font-size:13px;color:var(--g600);line-height:1.5;">
Build a teaching deck or handout on any topic &mdash; grounded in the clinical library,
the literature, or both &mdash; then revise it below and download it as PowerPoint,
Word or PDF.
</p>
<div class="admin-row">
<label for="mr-topic" class="admin-row-label">Topic</label>
<input id="mr-topic" type="text" class="admin-control" placeholder="e.g. febrile seizures in under-fives" maxlength="300">
</div>
<div class="admin-row">
<label for="mr-kind" class="admin-row-label">Format</label>
<div style="flex:1;display:flex;gap:10px;align-items:center;flex-wrap:wrap;min-width:0;">
<select id="mr-kind" class="admin-control" style="max-width:220px;">
<option value="presentation">Presentation (slides)</option>
<option value="article">Article (prose)</option>
</select>
<label style="font-size:12px;color:var(--g600);display:flex;align-items:center;gap:6px;">
Slides
<input id="mr-slide-count" type="number" min="3" max="30" value="8" style="width:70px;font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;">
</label>
<label style="font-size:12px;color:var(--g600);display:flex;align-items:center;gap:6px;" id="mr-word-wrap" hidden>
Words
<input id="mr-word-count" type="number" min="200" max="3000" step="100" value="800" style="width:90px;font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;">
</label>
</div>
</div>
<div class="admin-row" id="mr-model-row" hidden>
<label for="mr-model" class="admin-row-label">Model</label>
<div style="flex:1;display:flex;flex-direction:column;gap:4px;min-width:0;">
<select id="mr-model" class="admin-control" style="max-width:320px;"></select>
</div>
</div>
<!-- One group rather than four rows. Each option hides itself when an
administrator has not enabled it, so nothing appears that a person
could tick and then be refused. -->
<!-- The look, chosen by the person who knows the room it will be shown in.
Hidden until the catalogue loads, so it never flashes as an empty row. -->
<div class="admin-row" id="mr-theme-row" hidden>
<label for="mr-theme" class="admin-row-label">Theme</label>
<div style="flex:1;display:flex;flex-direction:column;gap:4px;min-width:0;">
<select id="mr-theme" class="admin-control" style="max-width:320px;"></select>
<p id="mr-theme-hint" style="margin:0;font-size:12px;color:var(--g500);"></p>
<!-- A slide drawn by the renderer itself, so the preview cannot promise
a look the download does not deliver. Hidden if it cannot render. -->
<div id="mr-theme-preview" hidden style="margin-top:4px;"></div>
</div>
</div>
<div class="admin-row" style="align-items:flex-start;">
<strong class="admin-row-label">Draw on</strong>
<div style="flex:1;display:flex;flex-direction:column;gap:8px;min-width:0;">
<label style="display:flex;align-items:center;gap:8px;font-size:13px;">
<input type="checkbox" id="mr-use-corpus" checked>
The clinical library
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;" id="mr-pubmed-row" hidden>
<input type="checkbox" id="mr-pubmed">
PubMed &mdash; cite published literature
<span style="font-size:12px;color:var(--g500);">The query goes to NCBI.</span>
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;flex-wrap:wrap;" id="mr-web-row" hidden>
<input type="checkbox" id="mr-web-search">
The web &mdash; for anything newer than the library
<span style="font-size:12px;color:var(--g500);">The query leaves this network; keep the topic non-identifying.</span>
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;" id="mr-images-row" hidden>
<input type="checkbox" id="mr-with-images">
Add illustrations &mdash; say how many in Instructions, or leave it to the model
</label>
<!-- Filled in when the instructions ask for a figure the options do not
allow. Silence there is the failure worth avoiding. -->
<p id="mr-image-hint" style="margin:0;font-size:12px;color:#92400e;" hidden></p>
</div>
</div>
<div class="admin-row" style="align-items:flex-start;">
<label for="mr-refinement" class="admin-row-label">Instructions <span style="color:var(--g400);font-weight:400;">(optional)</span></label>
<textarea id="mr-refinement" class="admin-control" style="min-height:70px;resize:vertical;font-family:inherit;" placeholder="e.g. for FY1s, case-based, emphasise red flags, use 3 diagrams"></textarea>
</div>
<div style="border-top:1px solid var(--g100);padding-top:12px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
<button id="btn-mr-generate" class="btn-sm btn-primary" type="button"><i class="fas fa-wand-magic-sparkles"></i> Generate</button>
<span id="mr-status" role="status" style="font-size:12px;color:var(--g600);"></span>
</div>
<!-- An illustration the model asked for. It renders here rather than in an
image history, because this feature does not have one and telling
someone to look somewhere that does not exist is worse than silence. -->
<div id="mr-images" style="display:flex;flex-direction:column;gap:10px;"></div>
</div>
</div>
<!-- Modify — change something already generated rather than starting again.
The markdown is what is stored, so "redo slide 4" is a text edit; the
resource keeps its id, its downloads and its place in the library. -->
<div class="card" style="margin-top:14px;">
<div class="card-header">
<h3><i class="fas fa-pen-to-square"></i> Modify</h3>
<span style="font-size:12px;color:var(--g500);">Revise one you already have</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div class="admin-row">
<label for="mr-modify-target" class="admin-row-label">Resource</label>
<select id="mr-modify-target" class="admin-control" style="max-width:420px;"></select>
</div>
<!-- The same choices as generating. A modification can need material the
original never had — "add what the 2024 trial showed" is a request for
sources, not a rewording — so the options are offered again rather
than inherited from whatever was ticked at the time. -->
<div class="admin-row" style="align-items:flex-start;">
<strong class="admin-row-label">Draw on</strong>
<div style="flex:1;display:flex;flex-direction:column;gap:8px;min-width:0;">
<label style="display:flex;align-items:center;gap:8px;font-size:13px;">
<input type="checkbox" id="mr-modify-corpus" checked>
The clinical library
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;" id="mr-modify-pubmed-row" hidden>
<input type="checkbox" id="mr-modify-pubmed">
PubMed &mdash; cite published literature
<span style="font-size:12px;color:var(--g500);">The query goes to NCBI.</span>
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;flex-wrap:wrap;" id="mr-modify-web-row" hidden>
<input type="checkbox" id="mr-modify-web-search">
The web &mdash; for anything newer than the library
<span style="font-size:12px;color:var(--g500);">The query leaves this network.</span>
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;" id="mr-modify-images-row" hidden>
<input type="checkbox" id="mr-modify-images">
Add illustrations
</label>
<p id="mr-modify-image-hint" style="margin:0;font-size:12px;color:#92400e;" hidden></p>
</div>
</div>
<div class="admin-row" style="align-items:flex-start;">
<label for="mr-modify-instructions" class="admin-row-label">What to change</label>
<div style="flex:1;display:flex;flex-direction:column;gap:4px;min-width:0;">
<textarea id="mr-modify-instructions" class="admin-control" style="min-height:70px;resize:vertical;font-family:inherit;" placeholder="e.g. split slide 4 into two, drop the pharmacology, add a summary slide at the end"></textarea>
<p style="margin:0;font-size:12px;color:var(--g500);">
The previous version is replaced.
</p>
</div>
</div>
<div style="border-top:1px solid var(--g100);padding-top:12px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
<button id="btn-mr-modify" class="btn-sm btn-primary" type="button"><i class="fas fa-pen-to-square"></i> Apply changes</button>
<span id="mr-modify-status" role="status" style="font-size:12px;color:var(--g600);"></span>
</div>
</div>
</div>
<div class="card" style="margin-top:14px;">
<div class="card-header">
<h3><i class="fas fa-book"></i> Library</h3>
<div style="display:flex;align-items:center;gap:6px;">
<!-- Two views of the same library. Documents is what this card has always
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. -->
<div role="tablist" aria-label="Library view" class="lib-tabs">
<button id="tab-mr-docs" role="tab" aria-selected="true" aria-controls="mr-list" class="lib-tab" type="button">Documents</button>
<button id="tab-mr-images" role="tab" aria-selected="false" aria-controls="mr-images-panel" class="lib-tab" type="button">Images</button>
</div>
<button id="btn-mr-refresh" class="btn-sm btn-ghost" type="button"><i class="fas fa-rotate"></i> Refresh</button>
</div>
</div>
<div id="mr-docs-search" style="padding:12px 16px 0;">
<input id="mr-search" type="search" class="admin-control" placeholder="Search your resources by title or topic" autocomplete="off">
</div>
<!-- Bounded rather than unlimited: a long library otherwise pushes everything
else off the page. Tall enough to show several at a glance, and it
collapses to the content when there are only a few. -->
<div id="mr-list" role="tabpanel" aria-labelledby="tab-mr-docs" style="padding:12px 16px;display:flex;flex-direction:column;gap:6px;max-height:360px;overflow-y:auto;"></div>
<!-- A grid rather than a list: a picture is recognised by looking at it, and a
row of filenames would defeat the point of keeping them. Auto-fill so it
reflows to one or two columns on a phone without a breakpoint. -->
<div id="mr-images-panel" role="tabpanel" aria-labelledby="tab-mr-images" hidden
style="padding:12px 16px;max-height:420px;overflow-y:auto;">
<p id="mr-images-empty" style="margin:0;font-size:12px;color:var(--g500);">Loading…</p>
<div id="mr-images-grid" class="img-grid"></div>
<div style="display:flex;justify-content:center;padding-top:10px;">
<button id="btn-mr-images-more" class="btn-sm btn-ghost" type="button" hidden>Load more</button>
</div>
</div>
</div>