Give the wizard an assistant scoped to the step you are actually stuck on, so a first-run question can say "this" and mean something

This commit is contained in:
Gmer4Lfe
2026-08-26 17:31:52 -04:00
parent d4c19baa32
commit 7f4921de49
+63
View File
@@ -37,6 +37,12 @@
// Step 2: auto-populate + guide + checklist. // Step 2: auto-populate + guide + checklist.
// master.conf pull (for partner servers) lives in the checklist, not here. // master.conf pull (for partner servers) lives in the checklist, not here.
// The assistant is a bonus on this page, never a dependency: vv_ai_ui_on() is false on a node
// with AI off or no model reachable, which is the normal state of the fresh install this wizard
// exists to serve. The card simply is not rendered and setup proceeds exactly as before.
require_once dirname(__DIR__) . '/include/ai_chat.php';
if (vv_ai_ui_on()) vv_ai_chat_assets();
$detectedHostname = vv_get_hostname(); $detectedHostname = vv_get_hostname();
// ── Identity already on disk? ──────────────────────────────────────────────────────────────── // ── Identity already on disk? ────────────────────────────────────────────────────────────────
@@ -313,6 +319,21 @@ hr.vv-hr { border: none; border-top: 1px solid #1e1e1e; margin: 22px 0; }
<div id="vv-onboard-panel"></div> <div id="vv-onboard-panel"></div>
<div id="vv-done-banner"></div> <div id="vv-done-banner"></div>
<?php if (vv_ai_ui_on()): ?>
<hr class="vv-hr">
<div id="vv-su-ai-card">
<?php vv_ai_chat_markup('vv-su-ai', [
'profile' => 'varaverk',
'compact' => true,
'title' => 'Setup assistant',
'height' => '260px',
'tall' => '460px',
'empty' => 'Stuck on a step? Ask what it wants and why — "what is the API key for", '
. '"why does SSH need the other host", "what does populate actually do".',
]); ?>
</div>
<?php endif; ?>
<div style="margin-top:18px;text-align:right;"> <div style="margin-top:18px;text-align:right;">
<a href="#" id="vv-exit-link" onclick="vvGoNext(event)" <a href="#" id="vv-exit-link" onclick="vvGoNext(event)"
style="font-size:11px;color:#3a3a3a;text-decoration:none;">Skip →</a> style="font-size:11px;color:#3a3a3a;text-decoration:none;">Skip →</a>
@@ -443,6 +464,7 @@ function vvShowStep2(redirect, apiKey) {
_vvRedirect = redirect || '?tab=scheduler'; _vvRedirect = redirect || '?tab=scheduler';
document.getElementById('vv-step1').style.display = 'none'; document.getElementById('vv-step1').style.display = 'none';
document.getElementById('vv-step2').style.display = 'block'; document.getElementById('vv-step2').style.display = 'block';
vvSetupAiInit(); // card is visible now, so it can measure itself correctly
if (apiKey && apiKey.ok) { if (apiKey && apiKey.ok) {
const btn = document.getElementById('vv-key-btn'); const btn = document.getElementById('vv-key-btn');
const status = document.getElementById('vv-key-status'); const status = document.getElementById('vv-key-status');
@@ -676,6 +698,47 @@ function vvStartPhase2Watch(sinceMs) {
_vvPartnerPoll = setInterval(check, 6000); _vvPartnerPoll = setInterval(check, 6000);
} }
// ── Setup assistant ───────────────────────────────────────────────────────────
// Scoped to the step the operator is actually stuck on, so "what does this want" resolves without
// them naming it. The worker turns a non-empty scope into a line saying what is open in the
// WebGUI; the varaverk profile holds no scoped_log capability, so a checklist id never triggers a
// log lookup and cannot produce a "log missing" note for something that was never a script.
//
// Re-read at send time, not captured: the checklist re-polls while the page is open and the step
// they are on can change between opening the composer and pressing Ask.
//
// First not-ok, not-deferred item — deferred means "I have decided to skip this", which is not
// where they are stuck. Falls back to the bare page when the list is clean or has not loaded.
function vvSetupScope() {
const d = _vvLastChecklist;
if (!d || !Array.isArray(d.items)) return 'Setup';
const stuck = d.items.find(i => !i.ok && !i.deferred);
return stuck && stuck.id ? 'Setup/' + stuck.id : 'Setup';
}
// Mounted when Step 2 is revealed, not at parse time. This is the only page where the card starts
// inside a display:none block, and a chat that measured itself while hidden would come up wrong
// with nothing to correct it — the component carries no resize observer. Idempotent because
// vvShowStep2 is reachable more than once.
let _vvSetupAiUp = false;
function vvSetupAiInit() {
if (_vvSetupAiUp) return;
if (typeof VvAiChat !== 'function') return;
if (!document.getElementById('vv-su-ai-chat')) return; // AI off — card was never rendered
_vvSetupAiUp = true;
VvAiChat({
prefix: 'vv-su-ai',
profile: 'varaverk',
// Pinned, for the same reason the Partnership card pins it: resuming whatever thread was last
// touched anywhere in the UI could land a first-run operator mid-way through someone else's
// Scheduler conversation, on a card with no picker to get back from.
resumeProfile: 'varaverk',
scope: vvSetupScope,
scopeLabel: 'Setup',
empty: 'Stuck on a step? Ask what it wants and why.',
});
}
function vvLoadChecklist() { function vvLoadChecklist() {
fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now()) fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now())
.then(r => r.json()).then(d => { .then(r => r.json()).then(d => {