feat(tables): Edit YAML row-context menu item

Opens the row's backing .yaml in the browse tool's YAML editor
(preview-yaml.js — CodeMirror with syntax highlight, lint, Ctrl+S
save). Disabled on multi-row range and unsaved draft rows.

Three URL shapes resolve correctly:
  per-party row → <dir>/?file=<file>.yaml
  SSR virtual   → /<project>/archive/<party>/?file=ssr.yaml
  rollup virtual → /<project>/archive/<party>/<slot>/?file=<file>.yaml

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-19 08:31:17 -05:00
parent f3d334a221
commit 1604b62477
2 changed files with 121 additions and 25 deletions

View file

@ -150,13 +150,60 @@
return { status: 'ok', deleted: okCount, failed: failCount };
}
// Map a row's yamlUrl to the browse-tool deep-link that opens the
// file in the YAML editor (preview-yaml.js). Three URL shapes:
//
// /<project>/archive/<party>/<...>/<file>.yaml
// → /<project>/archive/<party>/<...>/?file=<file>.yaml
// (already canonical — just split into dir + file)
//
// /<project>/ssr/<party>.yaml (virtual SSR row)
// → /<project>/archive/<party>/?file=ssr.yaml
//
// /<project>/(mdl|rsk)/<party>__<file>.yaml (virtual rollup row)
// → /<project>/archive/<party>/(mdl|rsk)/?file=<file>.yaml
//
// Returns null when no editable URL is reachable (unsaved row, or
// a yamlUrl shape we don't recognize).
function browseEditUrl(row) {
if (!row || !row.yamlUrl) return null;
const url = row.yamlUrl;
let m = url.match(/^(\/[^/]+)\/ssr\/([^/]+)\.yaml$/);
if (m) return m[1] + '/archive/' + m[2] + '/?file=ssr.yaml';
m = url.match(/^(\/[^/]+)\/(mdl|rsk)\/([^/]+)__(.+)\.yaml$/);
if (m) return m[1] + '/archive/' + m[3] + '/' + m[2] + '/?file=' + m[4] + '.yaml';
const slash = url.lastIndexOf('/');
if (slash < 0) return null;
return url.slice(0, slash + 1) + '?file=' + url.slice(slash + 1);
}
function buildRowMenu(ctx) {
const rangeRows = ctx.rangeRowIds || [];
const inRange = rangeRows.length > 1 && rangeRows.indexOf(ctx.rowId) !== -1;
const targets = inRange ? rangeRows : [ctx.rowId];
const items = [];
// Edit YAML — opens the row's backing .yaml file in the browse
// tool's YAML editor. Disabled on multi-row range and unsaved
// draft rows (no file on disk yet).
const singleRow = targets.length === 1 ? ctx.row : null;
const editUrl = singleRow && !singleRow.isNew ? browseEditUrl(singleRow) : null;
items.push({
label: 'Edit YAML',
icon: '✎',
disabled: !editUrl,
action: function () {
if (editUrl) window.location.href = editUrl;
}
});
items.push({ separator: true });
const label = targets.length > 1 ? 'Delete ' + targets.length + ' rows' : 'Delete row';
return [
{
items.push({
label: label,
icon: '🗑',
danger: true,
@ -165,8 +212,9 @@
if (targets.length > 1) deleteRows(targets);
else deleteRow(targets[0]);
}
}
];
});
return items;
}
function onRowContext(ev) {

View file

@ -1511,7 +1511,7 @@ body.is-elevated::after {
</svg>
<div class="header-title-group">
<span class="app-header__title" id="table-title">ZDDC Table</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.17-alpha · 2026-05-19 13:13:20 · cef7188-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.17-alpha · 2026-05-19 13:30:29 · f3d334a-dirty</span></span>
</div>
</div>
<div class="header-right">
@ -5749,13 +5749,60 @@ body.is-elevated::after {
return { status: 'ok', deleted: okCount, failed: failCount };
}
// Map a row's yamlUrl to the browse-tool deep-link that opens the
// file in the YAML editor (preview-yaml.js). Three URL shapes:
//
// /<project>/archive/<party>/<...>/<file>.yaml
// → /<project>/archive/<party>/<...>/?file=<file>.yaml
// (already canonical — just split into dir + file)
//
// /<project>/ssr/<party>.yaml (virtual SSR row)
// → /<project>/archive/<party>/?file=ssr.yaml
//
// /<project>/(mdl|rsk)/<party>__<file>.yaml (virtual rollup row)
// → /<project>/archive/<party>/(mdl|rsk)/?file=<file>.yaml
//
// Returns null when no editable URL is reachable (unsaved row, or
// a yamlUrl shape we don't recognize).
function browseEditUrl(row) {
if (!row || !row.yamlUrl) return null;
const url = row.yamlUrl;
let m = url.match(/^(\/[^/]+)\/ssr\/([^/]+)\.yaml$/);
if (m) return m[1] + '/archive/' + m[2] + '/?file=ssr.yaml';
m = url.match(/^(\/[^/]+)\/(mdl|rsk)\/([^/]+)__(.+)\.yaml$/);
if (m) return m[1] + '/archive/' + m[3] + '/' + m[2] + '/?file=' + m[4] + '.yaml';
const slash = url.lastIndexOf('/');
if (slash < 0) return null;
return url.slice(0, slash + 1) + '?file=' + url.slice(slash + 1);
}
function buildRowMenu(ctx) {
const rangeRows = ctx.rangeRowIds || [];
const inRange = rangeRows.length > 1 && rangeRows.indexOf(ctx.rowId) !== -1;
const targets = inRange ? rangeRows : [ctx.rowId];
const items = [];
// Edit YAML — opens the row's backing .yaml file in the browse
// tool's YAML editor. Disabled on multi-row range and unsaved
// draft rows (no file on disk yet).
const singleRow = targets.length === 1 ? ctx.row : null;
const editUrl = singleRow && !singleRow.isNew ? browseEditUrl(singleRow) : null;
items.push({
label: 'Edit YAML',
icon: '✎',
disabled: !editUrl,
action: function () {
if (editUrl) window.location.href = editUrl;
}
});
items.push({ separator: true });
const label = targets.length > 1 ? 'Delete ' + targets.length + ' rows' : 'Delete row';
return [
{
items.push({
label: label,
icon: '🗑',
danger: true,
@ -5764,8 +5811,9 @@ body.is-elevated::after {
if (targets.length > 1) deleteRows(targets);
else deleteRow(targets[0]);
}
}
];
});
return items;
}
function onRowContext(ev) {