varaverk: suggestions panel color + parser regex fix

- Change suggestion title color from bright green (#6fcf97) to soft grey-blue (#7a9eb5)
- Fix vv_parse_user_script_template() block-detection regex — $ end-anchor
  failed on lines ending with multibyte U+2500 ─ chars; removed anchor so
  parser now returns all 22 blocks from user_script_plug-in.sh
This commit is contained in:
Gmer4Lfe
2026-05-23 20:47:20 -04:00
parent a1745a575b
commit f6cb861f36
2 changed files with 110 additions and 8 deletions
@@ -103,6 +103,33 @@
.vv-dry-btn:hover { background: #e65100 !important; color: #fff !important;
border-color: #ff9800 !important; }
/* Suggestions panel — accordion */
#vv-suggestions { overflow-y: auto; }
.vv-sug-block { border-bottom: 1px solid #1e1e1e; }
.vv-sug-header { display: flex; align-items: center; gap: 8px; padding: 7px 4px;
cursor: pointer; user-select: none; flex-wrap: wrap; }
.vv-sug-header:hover { background: rgba(255,255,255,0.03); }
.vv-sug-chevron { color: #555; font-size: 11px; flex-shrink: 0; width: 10px; }
.vv-sug-title { color: #7a9eb5; font-weight: bold; font-size: 13px; flex: 1; min-width: 120px; }
.vv-sug-cron { color: #ddd; background: #111; border: 1px solid #333; padding: 1px 6px;
border-radius: 3px; font-size: 11px; font-family: monospace; white-space: nowrap; flex-shrink: 0; }
.vv-sug-label { color: #666; font-size: 12px; flex: 1; min-width: 0; white-space: nowrap;
overflow: hidden; text-overflow: ellipsis; }
.vv-sug-status { font-size: 11px; flex-shrink: 0; margin-left: auto; }
.vv-sug-on { color: #4caf50; }
.vv-sug-off { color: #777; }
.vv-sug-none { color: #444; }
.vv-sug-body { padding: 0 14px 10px; }
.vv-sug-desc { font-size: 12px; color: #888; white-space: pre-wrap; word-break: break-word;
background: none; border: none; margin: 4px 0 8px; padding: 0;
font-family: inherit; line-height: 1.6; }
.vv-sug-scripts { display: flex; flex-direction: column; gap: 4px; }
.vv-sug-script-row { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.vv-sug-inline-cron { color: #aaa; background: #111; padding: 1px 5px; border-radius: 3px;
font-size: 11px; white-space: nowrap; }
.vv-sug-path { color: #888; font-family: monospace; }
.vv-sug-configured { color: #4caf50; font-size: 11px; }
/* Log panel */
.vv-log-panel { margin-top: 10px; border-top: 1px solid #333; padding-top: 8px; }
.vv-log-toolbar { display: flex; justify-content: space-between; align-items: center;
@@ -66,6 +66,78 @@ function vv_cron_rebuild(array $schedule): bool {
return file_put_contents(CRON_FILE, implode("\n", $lines)) !== false;
}
// Extract the suggested cron and label from a bash script header.
// Looks for: # Schedule: */7 * * * * (every 7 minutes via User Scripts plugin)
function vv_script_suggested_cron(string $path): array {
if (!file_exists($path)) return ['cron' => '', 'label' => ''];
$lines = array_slice(file($path) ?: [], 0, 30);
foreach ($lines as $raw) {
$raw = rtrim($raw);
if (!preg_match('/^#\s*Schedule:\s*(.+)$/i', $raw, $m)) continue;
$tail = trim($m[1]);
$parts = preg_split('/\s+/', $tail, 6);
$cron = implode(' ', array_slice($parts, 0, 5));
$label = isset($parts[5]) ? trim($parts[5], '() ') : '';
return ['cron' => $cron, 'label' => $label];
}
return ['cron' => '', 'label' => ''];
}
// Parse user_script_plug-in.sh into an array of script blocks.
// Each block: title, schedule, desc (array of lines), scripts (array of {rel, cron})
function vv_parse_user_script_template(): array {
$file = SCRIPTS_DIR . '/user_script_plug-in.sh';
if (!file_exists($file)) return [];
$lines = file($file, FILE_IGNORE_NEW_LINES);
$prefix = rtrim(SCRIPTS_DIR, '/') . '/';
$blocks = [];
$cur = null;
foreach ($lines as $line) {
// Block header: # ── TITLE ──...
if (preg_match('/^# ── (.+?) ─/', $line, $m)) {
if ($cur) $blocks[] = $cur;
$cur = ['title' => trim($m[1]), 'schedule' => '', 'desc' => [], 'scripts' => []];
continue;
}
if (!$cur) continue;
// Schedule / Background — captured but not added to desc
if (preg_match('/^# (Schedule|Background):\s+(.+)$/i', $line, $m)) {
if (strtolower($m[1]) === 'schedule') $cur['schedule'] = trim($m[2]);
continue;
}
// Sunday block inline cron: # 0 6 * * 0 bash /path/script.sh [args]
if (preg_match('/^#\s+(\S+ +\S+ +\S+ +\S+ +\S+)\s+bash\s+(\S+\.sh)/', $line, $m)) {
$rel = str_replace($prefix, '', trim($m[2]));
$cur['scripts'][] = ['rel' => $rel, 'cron' => preg_replace('/\s+/', ' ', trim($m[1]))];
continue;
}
// Standard bash line: # bash /prefix/path/script.sh [args]
if (preg_match('/^# bash\s+(\S+\.sh)/', $line, $m)) {
$rel = str_replace($prefix, '', $m[1]);
// Only add primary command (dedupe by rel)
$rels = array_column($cur['scripts'], 'rel');
if (!in_array($rel, $rels)) {
$cur['scripts'][] = ['rel' => $rel, 'cron' => ''];
}
continue;
}
// Description line
if (preg_match('/^# ?(.*)$/', $line, $m)) {
$inner = $m[1];
if (!preg_match('/^[─━=\-]{3,}\s*$/', $inner) && !preg_match('/^█/', $inner)) {
$cur['desc'][] = $inner;
}
}
}
if ($cur) $blocks[] = $cur;
return $blocks;
}
// Extract a one-line description from a bash script header.
// Supports two patterns:
// 1. # PURPOSE (or # DESCRIPTION) block — returns first non-separator line after it
@@ -109,15 +181,18 @@ function vv_job_tree(): array {
foreach (glob($orchPattern) ?: [] as $path) {
$id = 'Orchestrators/' . basename($path);
$entry = $schedule[$id] ?? ['enabled' => false, 'cron' => ''];
$suggested = vv_script_suggested_cron($path);
$orchs[] = [
'id' => $id,
'label' => basename($path, '.sh'),
'desc' => vv_script_description($path),
'type' => 'orchestrator',
'enabled' => (bool)($entry['enabled'] ?? false),
'cron' => $entry['cron'] ?? '',
'log_enabled' => (bool)($entry['log_enabled'] ?? false),
'children' => vv_script_children($path, $schedule),
'id' => $id,
'label' => basename($path, '.sh'),
'desc' => vv_script_description($path),
'type' => 'orchestrator',
'enabled' => (bool)($entry['enabled'] ?? false),
'cron' => $entry['cron'] ?? '',
'log_enabled' => (bool)($entry['log_enabled'] ?? false),
'suggested_cron' => $suggested['cron'],
'suggested_label' => $suggested['label'],
'children' => vv_script_children($path, $schedule),
];
}
return $orchs;