Make the Scheduler panel an instance, not a second chat
It kept its own bar, send loop and poll, which is why a thread there died on reload while the other two were saved and why its controls had drifted from the same controls everywhere else.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
// PURPOSE
|
||||
// The conversation surface itself — profile bar, transcript, composer, source viewer and the
|
||||
// stored-chat list — rendered wherever a full chat belongs. Currently the AI tab and the
|
||||
// Monitor tab's AI row.
|
||||
// The conversation surface itself — transcript, composer, profile and history pickers, source
|
||||
// viewer and the stored-chat list — rendered wherever a chat belongs. All three surfaces use
|
||||
// it: the AI tab, the Monitor tab's AI row, and the Scheduler's right-hand panel.
|
||||
//
|
||||
// WHY THIS IS AN INCLUDE
|
||||
// There were two places that needed a real transcript and one implementation, which meant the
|
||||
@@ -12,10 +12,18 @@
|
||||
// teardown of a stale instance across an Unraid tab swap — is subtle because each of them
|
||||
// already cost a diagnosis session once. A copy inherits none of the fixes that come after it.
|
||||
//
|
||||
// The Scheduler tab's dock is deliberately NOT built on this. It is a one-line bar that follows
|
||||
// the view you have open, with its own scope chip, fix flow and incident capture; it is a
|
||||
// different component that happens to talk to the same endpoint. Folding it in here would mean
|
||||
// one widget with two personalities and a mode flag deciding which.
|
||||
// The Scheduler panel used to be exactly that copy: its own bar, send loop, poll and store
|
||||
// handling. It proved the point — a thread there died on reload while the other two were saved,
|
||||
// and its bar drifted into a different shape from the same control everywhere else. It is an
|
||||
// instance now. What was genuinely particular to it turned into options rather than a second
|
||||
// implementation: `scope` as a function for a subject that follows the open view, `beforeSend`
|
||||
// for the one reply that is recorded rather than asked, `think` for reasoning on diagnosis only,
|
||||
// `setHeights()` for a placement whose size is a share of a panel and not knowable until layout.
|
||||
//
|
||||
// WHAT A PLACEMENT MAY CHOOSE
|
||||
// How much room it gets, and which extra controls belong to that surface. Not the shape: the
|
||||
// control row, its order, and what the buttons do are fixed. A chat that rearranges itself per
|
||||
// tab is three components wearing one name, which is the state this replaced.
|
||||
//
|
||||
// INSTANCES
|
||||
// Every id is composed from a prefix, so two chats can coexist on one page. Each instance
|
||||
@@ -69,10 +77,10 @@ require_once __DIR__ . '/ai_profiles.php';
|
||||
|
||||
// The conversation store, on its own, with no styling and no chat widget attached.
|
||||
//
|
||||
// Emitted separately because the surface that most needs it is the one that does not want the
|
||||
// rest: the Scheduler dock draws its own one-line bar, with its own scope chip and fix flow, and
|
||||
// pulling in the full transcript stylesheet to reach a save function would restyle a component
|
||||
// that was deliberately built to look different.
|
||||
// Emitted separately because a page may want the store without the component — a list of saved
|
||||
// conversations, or a save, with no transcript rendered. Every surface currently takes both, but
|
||||
// the two are not the same dependency and pulling in a stylesheet to reach a save function is how
|
||||
// they would become one.
|
||||
//
|
||||
// Where a conversation lives is not a presentation decision, so it gets one answer for every
|
||||
// surface. Anything added to the store — a new field, a new cap, a changed prune rule — arrives
|
||||
@@ -132,8 +140,8 @@ function vv_ai_chat_assets(): void {
|
||||
.vv-ai-input:focus { outline:none; border-color:#2d4a6a; }
|
||||
.vv-ai-toggle { font-size:11px; color:#6a6a6a; display:flex; align-items:center; gap:5px; cursor:pointer; }
|
||||
|
||||
/* Profile picker styling lives in css/varaverk.css — the Scheduler dock uses the same control
|
||||
and loads none of this stylesheet. */
|
||||
/* Profile picker styling lives in css/varaverk.css, alongside the menus it shares its shape
|
||||
with, rather than here. */
|
||||
.vv-ai-switch { text-align:center; font-size:10px; color:#3a3a3a; margin:10px 0;
|
||||
border-top:1px dashed #1e1e1e; padding-top:8px; }
|
||||
|
||||
@@ -222,9 +230,9 @@ function vv_ai_chat_assets(): void {
|
||||
background:#161616; border-color:#333; }
|
||||
.vv-ai-c .vv-ai-input:focus { border-color:#6495ed; }
|
||||
|
||||
/* Styled after the Scheduler dock: 12px, tighter leading, questions marked with a chevron rather
|
||||
than a role label. The role labels cost a line each and say the same two things forever — on a
|
||||
card this size that is a third of the visible transcript spent on "You" and "Varaverk". */
|
||||
/* 12px, tighter leading, questions marked with a chevron rather than a role label. The role
|
||||
labels cost a line each and say the same two things forever — in a space this size that is a
|
||||
third of the visible transcript spent on "You" and "Varaverk". */
|
||||
.vv-ai-c .vv-ai-role { display:none; }
|
||||
.vv-ai-c .vv-ai-body { font-size:12px; line-height:1.55; }
|
||||
.vv-ai-c .vv-ai-msg.user .vv-ai-body { color:#8fb0c4; }
|
||||
@@ -300,7 +308,7 @@ function vv_ai_chat_assets(): void {
|
||||
<?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
|
||||
// knew four, which is why troubleshoot could not be offered here and the Scheduler had to
|
||||
// hand-roll its own labels.
|
||||
vv_ai_profiles_script();
|
||||
?>
|
||||
@@ -493,6 +501,12 @@ vv_ai_profiles_script();
|
||||
const q = $('input').value.trim();
|
||||
if (!q) return;
|
||||
|
||||
// A page may claim what was typed instead of asking it. The Scheduler does this once, after
|
||||
// an offer is accepted, when the next thing typed is the fix being recorded rather than a
|
||||
// question. Checked here rather than in a wrapper the page calls, because Ask and Ctrl+Enter
|
||||
// both land here directly and a wrapper would only catch the ones routed through it.
|
||||
if (o.beforeSend && o.beforeSend(q)) { $('input').value = ''; return; }
|
||||
|
||||
busy = true;
|
||||
$('send').disabled = true;
|
||||
addUser(q);
|
||||
@@ -516,7 +530,12 @@ vv_ai_profiles_script();
|
||||
question: q,
|
||||
history: JSON.stringify(sendable()),
|
||||
kind: (PROFILES[profile].kind && kindEl) ? kindEl.value : '',
|
||||
think: (thinkEl ? thinkEl.checked : true) ? '1' : '0',
|
||||
// A checkbox where the page offers one, otherwise whatever the page decides from the
|
||||
// profile, otherwise on. The Scheduler reasons only when diagnosing: working out what a
|
||||
// log means is worth waiting ~15s for, and a lookup like "what does this setting do" is
|
||||
// not — an inline answer that stalls reads as broken.
|
||||
think: (typeof o.think === 'function' ? o.think(profile)
|
||||
: thinkEl ? thinkEl.checked : true) ? '1' : '0',
|
||||
});
|
||||
res = fetch(API, { method: 'POST', headers: POST_HEAD, body });
|
||||
} catch (e) {
|
||||
@@ -966,7 +985,7 @@ vv_ai_profiles_script();
|
||||
box.innerHTML = '<div class="vv-ai-none">no saved conversations yet</div>';
|
||||
return;
|
||||
}
|
||||
// The scope is shown, not just stored. A Scheduler dock thread is about one script or log,
|
||||
// The scope is shown, not just stored. A Scheduler thread is about one script or log,
|
||||
// and a list of titles alone makes "why does this one talk about a log I never opened"
|
||||
// an unanswerable question.
|
||||
// Tagged only when the tag says something. A scope always does; a profile does unless it
|
||||
|
||||
Reference in New Issue
Block a user