Show the reference tables collapsed and the task guide on expand, from one file

This commit is contained in:
Gmer4Lfe
2026-08-04 19:45:23 -04:00
parent 64a28acd64
commit 87fa2693f0
3 changed files with 49 additions and 15 deletions
+3
View File
@@ -532,6 +532,9 @@ body.vv-fullscreen #displaybox { padding-left: 1rem !important; padding-top: .5r
padding: 4px 6px 4px 0; vertical-align: top; } padding: 4px 6px 4px 0; vertical-align: top; }
.vv-doc-code { background: #0d0d0d; border: 1px solid #222; border-radius: 3px; padding: 7px 9px; .vv-doc-code { background: #0d0d0d; border: 1px solid #222; border-radius: 3px; padding: 7px 9px;
font-size: 11px; color: #9ab; overflow-x: auto; break-inside: avoid; } font-size: 11px; color: #9ab; overflow-x: auto; break-inside: avoid; }
/* The always-visible reference tier. Sits below the collapsible guide, so the rule reads as a
separator when the guide is open and as a header underline when it is shut. */
.vv-doc-quick { border-top: 1px solid #1c1c1c; padding-top: 9px; }
.vv-info-divider { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: #444; .vv-info-divider { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: #444;
padding: 10px 12px 4px; border-top: 1px solid #2a2a2a; margin-top: 2px; } padding: 10px 12px 4px; border-top: 1px solid #2a2a2a; margin-top: 2px; }
+32 -13
View File
@@ -72,7 +72,11 @@ function vv_docs_tree(): array {
return $tree; return $tree;
} }
function vv_docs_render(string $rel, array $vars): string { // $keep, when given, selects which `##` sections are rendered. It receives the section heading —
// or null for the title and any preamble before the first one — and returns whether to include
// it. That is what lets one file drive both tiers of a disclosure without a second copy, and
// without inventing markdown syntax to mark the split: the section titles already carry it.
function vv_docs_render(string $rel, array $vars, ?callable $keep = null): string {
// Containment check — $rel is expected to come from a request parameter once this is // Containment check — $rel is expected to come from a request parameter once this is
// wired to a page. Resolve it and require the result to stay inside SCRIPTS_DIR and to // wired to a page. Resolve it and require the result to stay inside SCRIPTS_DIR and to
// still be a .md file, so a traversal sequence cannot reach arbitrary files. // still be a .md file, so a traversal sequence cannot reach arbitrary files.
@@ -83,7 +87,7 @@ function vv_docs_render(string $rel, array $vars): string {
if (strtolower(pathinfo($path, PATHINFO_EXTENSION)) !== 'md') return '<p>File not found.</p>'; if (strtolower(pathinfo($path, PATHINFO_EXTENSION)) !== 'md') return '<p>File not found.</p>';
if (!is_file($path)) return '<p>File not found.</p>'; if (!is_file($path)) return '<p>File not found.</p>';
return vv_docs_markdown(file_get_contents($path), $vars); return vv_docs_markdown(file_get_contents($path), $vars, $keep);
} }
// Markdown → HTML for the subset these docs actually use: headings, paragraphs, bullet and // Markdown → HTML for the subset these docs actually use: headings, paragraphs, bullet and
@@ -102,7 +106,7 @@ function vv_docs_render(string $rel, array $vars): string {
// here, after escaping. Injecting <code> into the markdown before rendering, as this file used to // here, after escaping. Injecting <code> into the markdown before rendering, as this file used to
// do, meant both Parsedown's safe mode and the <pre> fallback escaped the tags and printed them // do, meant both Parsedown's safe mode and the <pre> fallback escaped the tags and printed them
// as literal text. The feature never worked; nothing called it, so nothing reported it. // as literal text. The feature never worked; nothing called it, so nothing reported it.
function vv_docs_markdown(string $md, array $vars): string { function vv_docs_markdown(string $md, array $vars, ?callable $keep = null): string {
if (file_exists(PARSEDOWN_PATH)) { if (file_exists(PARSEDOWN_PATH)) {
require_once PARSEDOWN_PATH; require_once PARSEDOWN_PATH;
$pd = new Parsedown(); $pd = new Parsedown();
@@ -161,17 +165,39 @@ function vv_docs_markdown(string $md, array $vars): string {
if ($inTable) { $out .= "</tbody></table>\n"; $inTable = false; } if ($inTable) { $out .= "</tbody></table>\n"; $inTable = false; }
}; };
// Content before the first heading belongs to the same null-headed group as the title.
$skip = $keep !== null && !$keep(null);
foreach (explode("\n", str_replace("\r\n", "\n", $md)) as $line) { foreach (explode("\n", str_replace("\r\n", "\n", $md)) as $line) {
// Fence state is tracked even inside a skipped section, or a ``` that is being dropped
// would leave the parser convinced every following line is code.
if (preg_match('/^```/', $line)) { if (preg_match('/^```/', $line)) {
$flushPara(); $closeList(); $closeTable(); if (!$skip) {
$out .= $inCode ? "</code></pre>\n" : '<pre class="vv-doc-code"><code>'; $flushPara(); $closeList(); $closeTable();
$out .= $inCode ? "</code></pre>\n" : '<pre class="vv-doc-code"><code>';
}
$inCode = !$inCode; $inCode = !$inCode;
continue; continue;
} }
if ($inCode) { $out .= htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . "\n"; continue; } if ($inCode) {
if (!$skip) $out .= htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . "\n";
continue;
}
$t = trim($line); $t = trim($line);
// Headings are resolved before the skip test, because a heading is what changes it.
if (preg_match('/^(#{1,6})\s+(.*)$/', $t, $m)) {
$flushQuote(); $flushPara(); $closeList(); $closeTable();
$n = strlen($m[1]);
// A level-1 heading is the document title, not a section — it and the preamble that
// follows are offered to $keep as null so a caller can take or leave them as a unit.
$skip = $keep !== null && !$keep($n === 1 ? null : $m[2]);
if (!$skip) $out .= "<h$n>" . $inline($m[2]) . "</h$n>\n";
continue;
}
if ($skip) continue;
// One guard rather than a flush in every branch: the quote ends the moment a line is // One guard rather than a flush in every branch: the quote ends the moment a line is
// not a quote line, whatever that next line turns out to be. // not a quote line, whatever that next line turns out to be.
if ($quote && !str_starts_with($t, '>')) $flushQuote(); if ($quote && !str_starts_with($t, '>')) $flushQuote();
@@ -179,13 +205,6 @@ function vv_docs_markdown(string $md, array $vars): string {
if ($t === '') { $flushPara(); $closeList(); $closeTable(); continue; } if ($t === '') { $flushPara(); $closeList(); $closeTable(); continue; }
if (preg_match('/^(---+|\*\*\*+)$/', $t)) { $flushPara(); $closeList(); $closeTable(); $out .= "<hr>\n"; continue; } if (preg_match('/^(---+|\*\*\*+)$/', $t)) { $flushPara(); $closeList(); $closeTable(); $out .= "<hr>\n"; continue; }
if (preg_match('/^(#{1,6})\s+(.*)$/', $t, $m)) {
$flushPara(); $closeList(); $closeTable();
$n = strlen($m[1]);
$out .= "<h$n>" . $inline($m[2]) . "</h$n>\n";
continue;
}
// Consecutive > lines are one quote, not one per line — same wrapping convention as // Consecutive > lines are one quote, not one per line — same wrapping convention as
// paragraphs, which is how they are written. // paragraphs, which is how they are written.
if (preg_match('/^>\s?(.*)$/', $t, $m)) { if (preg_match('/^>\s?(.*)$/', $t, $m)) {
+14 -2
View File
@@ -507,12 +507,24 @@ $runningScripts = array_unique($runningScripts);
<!-- Rendered from pages/readme/scheduler-readme.md, not maintained here. That file is <!-- Rendered from pages/readme/scheduler-readme.md, not maintained here. That file is
also what the AI tab retrieves, so the panel you read and the answer the also what the AI tab retrieves, so the panel you read and the answer the
assistant gives are the same text and cannot drift — the same argument that assistant gives are the same text and cannot drift — the same argument that
makes this page parse script PURPOSE blocks instead of restating them. --> makes this page parse script PURPOSE blocks instead of restating them.
Two tiers, one file. Collapsed leaves the reference tables visible: that is the
terse per-control lookup this panel has always been, and it is what you want
open while actually working. Expanding adds the task guide above it — the
walkthroughs you need once and then stop reading. The split is taken from the
section titles, so neither tier is a second copy of anything.
vvToggleSug() toggles the header's immediate next sibling, so the guide must
stay directly after the header and the quick tier must sit outside it. -->
<div class="vv-sug-body vv-info-body"> <div class="vv-sug-body vv-info-body">
<div class="vv-doc"> <div class="vv-doc">
<?= vv_docs_render('Plugin/unraid/pages/readme/scheduler-readme.md', $_vv_doc_vars) ?> <?= vv_docs_render('Plugin/unraid/pages/readme/scheduler-readme.md', $_vv_doc_vars,
fn($h) => !is_string($h) || !str_starts_with($h, 'Reference —')) ?>
</div> </div>
</div> </div>
<div class="vv-info-body vv-doc vv-doc-quick">
<?= vv_docs_render('Plugin/unraid/pages/readme/scheduler-readme.md', $_vv_doc_vars,
fn($h) => is_string($h) && str_starts_with($h, 'Reference —')) ?>
</div>
</div> </div>
<!-- ── Next Runs ── --> <!-- ── Next Runs ── -->