Offer the assistant the same formatting the mesh composer has, and drop the mark lower

Colour is chosen by name and mapped to a fixed value here, so the model never writes a style property — the same closed-set rule the mesh store uses.
This commit is contained in:
Gmer4Lfe
2026-08-17 18:01:45 -04:00
parent 2e646f82b4
commit 4eb0a9c608
3 changed files with 48 additions and 2 deletions
+26
View File
@@ -915,6 +915,32 @@ if ($profile === 'chat' && vv_ai_chat_needs_varaverk($question)) {
if ($context !== '') $system .= "PASSAGES\n" . $context;
// What the transcript can render, offered rather than required.
//
// Appended to every profile at the single point they all pass through, so a new profile cannot
// be added without it. The set is the one the mesh composer already offers, and the renderer
// maps colour NAMES to fixed values — the model never writes a colour, only chooses one, so
// nothing it emits can become a style property.
//
// Deliberately permissive in tone. The interesting question is whether it reaches for these at
// all and where; an instruction to use them would answer that question by force and tell us
// nothing. Prose stays prose.
$system .= "\n\nFORMATTING AVAILABLE TO YOU\n"
. "The transcript renders a small set of marks. Use them where they genuinely help a "
. "reader — a warning that must not be skimmed, a value worth picking out of a "
. "sentence — and not otherwise. Plain prose is the default and needs no marking.\n"
. " **bold** emphasis\n"
. " *italic* aside or term\n"
. " __underline__ rarely; reserve for something that must not be missed\n"
. " {big}…{/big} one size up\n"
. " {small}…{/small} one size down, for asides\n"
. " {green}…{/green} also blue, amber, red, violet, grey\n"
. "Colour carries meaning here: green for confirmed-good, amber for caution, red for "
. "something broken or destructive. Do not colour text decoratively — a coloured word "
. "that means nothing costs the colours their meaning everywhere else. Code fences and "
. "`inline code` work as before and remain the right choice for anything typed at a "
. "shell.\n";
$messages = [['role' => 'system', 'content' => $system]];
$hist = json_decode($historyJson ?: '[]', true);
if (is_array($hist)) {
+21 -1
View File
@@ -162,7 +162,7 @@ function vv_ai_chat_assets(): void {
.vv-ai-chat-wrap { background:#0b0b0b; border-radius:6px; }
.vv-ai-chat-wrap::before {
content:''; position:absolute; inset:0; z-index:0; pointer-events:none;
background:url('/plugins/varaverk/icons/varaverk.png') center 58%/min(84%,520px) no-repeat;
background:url('/plugins/varaverk/icons/varaverk.png') center 74%/min(84%,520px) no-repeat;
opacity:.032; filter:grayscale(1);
}
.vv-ai-empty { color:#3a3a3a; font-size:12px; text-align:center; padding:60px 20px; line-height:1.7; }
@@ -789,12 +789,32 @@ vv_ai_profiles_script();
+ `<pre><code>${body}</code></pre></div>`;
}
// The same palette the mesh composer offers, by NAME only. The model never supplies a colour
// value — it picks from this list and the hex is looked up here, so nothing it writes can
// become a style property. Same closed-set principle as vv_nc_clean_style() on the server.
const FMT_COLORS = { green:'#4caf50', blue:'#4a9eff', amber:'#ff9800',
red:'#ef5350', violet:'#ab7df6', grey:'#bdbdbd' };
// ── Minimal markdown, applied strictly after escaping ────────────────
function fmt(text) {
let h = esc(text);
h = h.replace(/```(\w*)\n([\s\S]*?)```/g, (m, l, c) => codeCard(l, c));
h = h.replace(/`([^`\n]+)`/g, '<code>$1</code>');
h = h.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
// Emphasis and size. Single-asterisk italics run after bold so ** is already consumed.
h = h.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, '$1<em>$2</em>');
h = h.replace(/__([^_\n]+)__/g, '<u>$1</u>');
h = h.replace(/\{big\}([\s\S]*?)\{\/big\}/g, '<span style="font-size:15px">$1</span>');
h = h.replace(/\{small\}([\s\S]*?)\{\/small\}/g, '<span style="font-size:11px">$1</span>');
// {green}…{/green}. An unknown name is left as written rather than guessed at — a colour
// nobody chose is worse than a pair of braces the reader can see and ignore.
h = h.replace(/\{(\w+)\}([\s\S]*?)\{\/\1\}/g, (m, name, inner) => {
const c = FMT_COLORS[name.toLowerCase()];
return c ? `<span style="color:${c}">${inner}</span>` : m;
});
h = h.replace(/\[(\d+)\]/g, '<span class="vv-ai-cite" data-cite="$1">[$1]</span>');
return h;
}
+1 -1
View File
@@ -260,7 +260,7 @@ function vv_nc_pane_markup(string $prefix, bool $meshDefault = false,
it can carry the badge without competing with the text. -->
<div style="position:relative;">
<img src="/plugins/varaverk/icons/varaverk.png" alt="" aria-hidden="true"
style="position:absolute;left:50%;top:58%;transform:translate(-50%,-50%);
style="position:absolute;left:50%;top:74%;transform:translate(-50%,-50%);
width:min(94%,520px);opacity:.038;pointer-events:none;filter:grayscale(1);
z-index:0;">
<div id="vv-nc-log"