fix: YAML folded scalar parser, delete redirect, #protocols routing, forked_from null
- Fix YAML fallback parser to properly handle folded scalars (>-) at
both top-level (description, outcome, practice) and step-level
(step descriptions). Previously stored '>-' as the literal value
instead of the folded text.
- Fix delete protocol/collection: reset currentRoute guard so
navigate('library') actually works after deletion.
- Change #library hash to #protocols throughout (routing, nav links,
breadcrumb shows 'protocols/' on the main page).
- Fix breadcrumb home click not working from detail pages (allow
re-navigation to library even if currentRoute matches).
- Fix forked_from: null showing as a clickable link — now suppressed
when value is null or the string 'null'.
- README: replace curl seed instructions with About page button,
add About page customization instructions for whitelabeling.
This commit is contained in:
+63
-27
@@ -121,60 +121,77 @@ function parse_protocol_yaml(string $yaml_text): ?array {
|
||||
|
||||
/**
|
||||
* Minimal YAML parser for simple protocol files.
|
||||
* Handles: top-level key: value, nested lists with - headline:, folded scalars
|
||||
* Handles: top-level key: value, nested lists with - headline:, folded scalars (>-)
|
||||
* This is NOT a full YAML parser — it handles the protocol format only.
|
||||
*/
|
||||
function simple_yaml_parse(string $text): ?array {
|
||||
$result = [];
|
||||
$lines = explode("\n", $text);
|
||||
$current_key = null;
|
||||
$in_steps = false;
|
||||
$current_step_idx = -1;
|
||||
|
||||
// State for folded scalar tracking
|
||||
$in_folded = false;
|
||||
$folded_target = null; // 'top' for top-level key, 'step' for step description
|
||||
$folded_top_key = null;
|
||||
$folded_buffer = '';
|
||||
$folded_key = null;
|
||||
$folded_indent = 0;
|
||||
|
||||
$flush_folded = function() use (&$result, &$folded_buffer, &$folded_key, &$in_folded) {
|
||||
if ($in_folded && $folded_key !== null) {
|
||||
$result[$folded_key] = trim($folded_buffer);
|
||||
$folded_buffer = '';
|
||||
$flush_folded = function() use (&$result, &$in_folded, &$folded_target, &$folded_top_key, &$folded_buffer, &$current_step_idx) {
|
||||
if ($in_folded) {
|
||||
$val = trim($folded_buffer);
|
||||
if ($folded_target === 'top' && $folded_top_key !== null) {
|
||||
$result[$folded_top_key] = $val;
|
||||
} elseif ($folded_target === 'step' && $current_step_idx >= 0) {
|
||||
$result['steps'][$current_step_idx]['description'] = $val;
|
||||
}
|
||||
$in_folded = false;
|
||||
$folded_key = null;
|
||||
$folded_target = null;
|
||||
$folded_top_key = null;
|
||||
$folded_buffer = '';
|
||||
}
|
||||
};
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Skip comments and empty lines
|
||||
if (preg_match('/^\s*#/', $line) || trim($line) === '') continue;
|
||||
// Skip comments
|
||||
if (preg_match('/^\s*#/', $line)) continue;
|
||||
|
||||
// Folded scalar continuation
|
||||
// Folded scalar continuation — indented lines that are more indented than the key
|
||||
if ($in_folded) {
|
||||
if (preg_match('/^(\s{' . ($folded_indent + 2) . ',})/', $line, $m)) {
|
||||
$folded_buffer .= ' ' . trim($line);
|
||||
$trimmed = trim($line);
|
||||
if ($trimmed === '') { continue; } // blank lines in folded scalars become spaces
|
||||
// Check if this line is indented more than the key (continuation of folded scalar)
|
||||
$line_indent = strlen($line) - strlen(ltrim($line));
|
||||
if ($line_indent > $folded_indent) {
|
||||
$folded_buffer .= ' ' . $trimmed;
|
||||
continue;
|
||||
} else {
|
||||
$flush_folded();
|
||||
// fall through to process this line normally
|
||||
}
|
||||
}
|
||||
|
||||
// Steps list items
|
||||
if (preg_match('/^(\s+) - headline:\s*(.*)$/', $line, $m)) {
|
||||
$in_steps = true;
|
||||
// Skip empty lines (not inside folded)
|
||||
if (trim($line) === '') continue;
|
||||
|
||||
// Step headline: " - headline: ..."
|
||||
if (preg_match('/^(\s+)-\s+headline:\s*(.*)$/', $line, $m)) {
|
||||
$flush_folded();
|
||||
$current_step_idx++;
|
||||
$result['steps'][] = ['headline' => trim($m[2], '"'), 'description' => ''];
|
||||
$current_key = 'description';
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/^(\s+) description:\s*(.*)$/', $line, $m)) {
|
||||
|
||||
// Step description: " description: ..."
|
||||
if (preg_match('/^(\s+)description:\s*(.*)$/', $line, $m) && $current_step_idx >= 0) {
|
||||
$val = trim($m[2], '"');
|
||||
if (str_starts_with($val, '>-') || str_starts_with($val, '|-')) {
|
||||
if ($val === '>-' || $val === '>+' || $val === '|' || $val === '|-' || $val === '|+') {
|
||||
// Start folded scalar for step description
|
||||
$in_folded = true;
|
||||
$folded_key = null; // Will set on last step
|
||||
$folded_target = 'step';
|
||||
$folded_buffer = '';
|
||||
$folded_indent = strlen($m[1]);
|
||||
} else {
|
||||
if (!empty($result['steps'])) {
|
||||
$result['steps'][count($result['steps']) - 1]['description'] = $val;
|
||||
}
|
||||
$result['steps'][$current_step_idx]['description'] = $val;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -184,17 +201,36 @@ function simple_yaml_parse(string $text): ?array {
|
||||
$flush_folded();
|
||||
$key = $m[1];
|
||||
$val = trim($m[2]);
|
||||
if ($val === '' ) {
|
||||
$current_key = $key;
|
||||
if ($key === 'steps') { $result['steps'] = []; $in_steps = true; }
|
||||
|
||||
if ($val === '') {
|
||||
// Could be "steps:" (empty value, starts a list) or just empty
|
||||
if ($key === 'steps') { $result['steps'] = []; $current_step_idx = -1; }
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for folded scalar indicators
|
||||
if ($val === '>-' || $val === '>+' || $val === '|' || $val === '|-' || $val === '|+') {
|
||||
$in_folded = true;
|
||||
$folded_target = 'top';
|
||||
$folded_top_key = $key;
|
||||
$folded_buffer = '';
|
||||
$folded_indent = 0; // top-level keys have indent 0
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse inline array [a, b, c]
|
||||
if (str_starts_with($val, '[') && str_ends_with($val, ']')) {
|
||||
$inner = substr($val, 1, -1);
|
||||
$result[$key] = array_map(fn($s) => trim($s, ' "'), array_filter(explode(',', $inner), fn($s) => trim($s) !== ''));
|
||||
continue;
|
||||
}
|
||||
|
||||
// null literal
|
||||
if ($val === 'null' || $val === '~') {
|
||||
$result[$key] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$key] = trim($val, '"');
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user