Give the AI subsystem one profile table and one collection, read everywhere

This commit is contained in:
Gmer4Lfe
2026-08-08 22:35:37 -04:00
parent 0f4d381ac0
commit 0f92609425
10 changed files with 355 additions and 103 deletions
+64 -24
View File
@@ -57,10 +57,16 @@
// vv_ai_chat_list_markup($prefix) the stored-conversations list container
//
// DEPENDS ON
// api/ai.php ask / poll / clear / chats / chat_get / chat_save / chat_delete
// api/readscript.php source viewer contents
// include/ai_profiles.php the profile registry, served to the browser rather than restated
// api/ai.php ask / poll / clear / chats / chat_get / chat_save / chat_delete
// api/readscript.php source viewer contents
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// Required directly, not assumed. The Monitor tab pulls this file in without include/ai.php,
// so relying on something else having loaded the registry first works on the AI tab and fatals
// on the dashboard.
require_once __DIR__ . '/ai_profiles.php';
// Emitted once even if two instances are rendered. A second copy of the script would re-register
// the factories harmlessly but would also install a second Escape handler and a second copy of
// every keyframe, so the guard is cheaper than reasoning about whether it matters.
@@ -145,18 +151,41 @@ function vv_ai_chat_assets(): void {
.vv-ai-hint { font-size:10px; color:#3a3a3a; margin-left:auto; }
/* ── Compact form, for a chat living inside a Monitor card ──────────────── */
/* Not a different component — the same markup with the chrome pulled in. The card supplies its
own heading and border, so the transcript drops its own and the hint text goes away rather
than wrapping to three lines at this width. */
.vv-ai-c .vv-ai-chat { border:none; border-radius:0; padding:10px 2px; min-height:0; }
/* Not a different component — the same markup with the chrome pulled in. The card supplies the
heading and the border, so the transcript drops its own and the hint text goes away rather
than wrapping to three lines at this width.
Surfaces are re-based on the card, not merely un-bordered. The standalone palette paints a
#0b0b0b transcript because on the AI tab it sits on the page ground with nothing beside it
to compare against; dropped into a .vv-card (#1e1e1e) that same fill reads as a hole cut in
the card rather than as part of it. Transparent here, and the remaining controls move to the
values the plugin already uses inside a card — .vv-btn-sm is #2a2a2a on #555 — so the whole
thing reads as one object.
This is the second hardcoded dark palette in the plugin, which is exactly one too many. When
theming happens it wants tokens (--vv-surface, --vv-line) defined once in varaverk.css, and
this block becomes a token swap instead of a second set of literals. */
.vv-ai-c .vv-ai-chat { border:none; border-radius:0; padding:10px 2px; min-height:0;
background:transparent; }
.vv-ai-c .vv-ai-empty { padding:26px 14px; }
.vv-ai-c .vv-ai-composer { border:none; padding:8px 0 0; background:none; }
.vv-ai-c .vv-ai-input { min-height:44px; font-size:12px; padding:7px; }
.vv-ai-c .vv-ai-prof { padding:3px 9px; font-size:10px; }
.vv-ai-c .vv-ai-input { min-height:44px; font-size:12px; padding:7px;
background:#161616; border-color:#333; }
.vv-ai-c .vv-ai-input:focus { border-color:#6495ed; }
.vv-ai-c .vv-ai-prof { padding:3px 9px; font-size:10px; background:#2a2a2a;
border-color:#555; color:#ccc; }
.vv-ai-c .vv-ai-prof:hover { border-color:#888; color:#fff; }
.vv-ai-c .vv-ai-prof.active { background:#152238; border-color:#4a7ab0; color:#9bd; }
.vv-ai-c .vv-ai-prof-hint { display:none; }
.vv-ai-c .vv-ai-hint { display:none; }
.vv-ai-c .vv-ai-btn { padding:4px 11px; font-size:11px; }
.vv-ai-c .vv-ai-btn.ghost { border-color:#555; color:#ccc; }
.vv-ai-c .vv-ai-msg { margin-bottom:12px; }
.vv-ai-c .vv-ai-think { background:#161616; border-left-color:#3a3a3a; }
.vv-ai-c .vv-ai-body pre { background:#161616; border-color:#3a3a3a; }
.vv-ai-c .vv-ai-body code { background:#2a2a2a; }
.vv-ai-c .vv-ai-src { border-top-color:#333; }
.vv-ai-c .vv-ai-switch { border-top-color:#333; }
/* ── Stored conversations ───────────────────────────────────────────────── */
.vv-ai-clist { display:flex; flex-direction:column; gap:1px; }
@@ -171,6 +200,14 @@ function vv_ai_chat_assets(): void {
.vv-ai-crow-x { font-size:11px; color:#333; flex-shrink:0; padding:0 2px; visibility:hidden; }
.vv-ai-crow:hover .vv-ai-crow-x { visibility:visible; }
.vv-ai-crow-x:hover { color:#e57; }
/* Hover lifts off the card rather than sinking into it. #141414 is a highlight against the AI
tab's #0e0e0e panel and a shadow against a #1e1e1e card — the same value reads as the
opposite gesture depending on what it sits on. */
.vv-ai-c .vv-ai-crow:hover { background:#2a2a2a; }
.vv-ai-c .vv-ai-crow-t { color:#ccc; }
.vv-ai-c .vv-ai-crow-m { color:#666; }
.vv-ai-c .vv-ai-crow-x { color:#666; }
.vv-ai-c .vv-ai-none { color:#666; }
.vv-ai-chead { display:flex; align-items:center; gap:8px; margin-bottom:5px; }
/* ── Source overlay ─────────────────────────────────────────────────────── */
@@ -195,21 +232,21 @@ function vv_ai_chat_assets(): void {
</div>
</div>
<?php
// The profile registry, served rather than restated. This used to be a literal table in the
// script below that had already drifted from the PHP — it knew three profiles where the server
// knew four, which is why troubleshoot could not be offered here and the Scheduler dock had to
// hand-roll its own labels.
vv_ai_profiles_script();
?>
<script>
(function () {
const API = '/plugins/varaverk/api/ai.php';
// Server-side is the authority on retrieval depth and history; these are for the UI only, and
// they must not drift from VV_AI_PROFILES in api/ai.php.
const PROFILES = {
varaverk: { label: 'Varaverk Assistant', turns: 3, kind: true,
hint: 'Answers only from Varaverk\'s own docs, with sources. Says so when they don\'t cover it.' },
chat: { label: 'General Chat', turns: 8, kind: false,
hint: 'Ordinary conversation. Hands anything about this install to the assistant on its own.' },
code: { label: 'Code Sketcher', turns: 4, kind: false,
hint: 'Drafts short scripts for Custom Scripts. First drafts — test before trusting.' },
};
window.VvAiProfiles = PROFILES;
// The server is the authority. It re-derives depth and capability on every request regardless
// of what is here — these values draw buttons and decide whether to show the kind filter, they
// do not make policy.
const PROFILES = window.VvAiProfiles;
const esc = s => String(s == null ? '' : s)
.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
@@ -678,9 +715,9 @@ function vv_ai_chat_markup(string $prefix, array $o = []): void {
<div class="vv-ai-chatwrap<?= $compact ? ' vv-ai-c' : '' ?>" style="display:flex;flex-direction:column;gap:<?= $compact ? '4px' : '12px' ?>;min-width:0;">
<div class="vv-ai-profiles" id="<?= $p ?>-profiles">
<?php foreach (['varaverk' => 'Varaverk Assistant', 'chat' => 'General Chat',
'code' => 'Code Sketcher'] as $key => $label): ?>
<button class="vv-ai-prof" data-prof="<?= $key ?>" type="button"><?= $label ?></button>
<?php foreach (vv_ai_profiles_ui() as $key => $def): ?>
<button class="vv-ai-prof" data-prof="<?= htmlspecialchars($key, ENT_QUOTES) ?>" type="button"
title="<?= htmlspecialchars($def['hint'], ENT_QUOTES) ?>"><?= htmlspecialchars($def['label']) ?></button>
<?php endforeach; ?>
<span class="vv-ai-prof-hint" id="<?= $p ?>-prof-hint"></span>
</div>
@@ -705,9 +742,12 @@ function vv_ai_chat_markup(string $prefix, array $o = []): void {
// The stored-conversations container. Rendered separately from the chat because the two live in
// different cards on Monitor and in different parts of the column on the AI tab.
function vv_ai_chat_list_markup(string $prefix): void {
//
// Takes compact for the same reason the chat does: these rows are painted against whatever they
// sit on, and a card and a panel are not the same ground.
function vv_ai_chat_list_markup(string $prefix, bool $compact = false): void {
$p = htmlspecialchars($prefix, ENT_QUOTES);
?>
<div id="<?= $p ?>-chats"><div class="vv-ai-none">loading…</div></div>
<div id="<?= $p ?>-chats"<?= $compact ? ' class="vv-ai-c"' : '' ?>><div class="vv-ai-none">loading…</div></div>
<?php
}