From 1604b62477ab8fda11f1fffa984dddd311a2bbf4 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Tue, 19 May 2026 08:31:17 -0500 Subject: [PATCH] feat(tables): Edit YAML row-context menu item MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 → /?file=.yaml SSR virtual → //archive//?file=ssr.yaml rollup virtual → //archive///?file=.yaml Co-Authored-By: Claude Opus 4.7 (1M context) --- tables/js/row-ops.js | 72 +++++++++++++++++++++++++----- zddc/internal/handler/tables.html | 74 +++++++++++++++++++++++++------ 2 files changed, 121 insertions(+), 25 deletions(-) diff --git a/tables/js/row-ops.js b/tables/js/row-ops.js index 1e9e3f1..6d03748 100644 --- a/tables/js/row-ops.js +++ b/tables/js/row-ops.js @@ -150,23 +150,71 @@ 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: + // + // //archive//<...>/.yaml + // → //archive//<...>/?file=.yaml + // (already canonical — just split into dir + file) + // + // //ssr/.yaml (virtual SSR row) + // → //archive//?file=ssr.yaml + // + // //(mdl|rsk)/__.yaml (virtual rollup row) + // → //archive//(mdl|rsk)/?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 label = targets.length > 1 ? 'Delete ' + targets.length + ' rows' : 'Delete row'; - return [ - { - label: label, - icon: '🗑', - danger: true, - disabled: !ctx.row || ctx.row.editable === false, - action: function () { - if (targets.length > 1) deleteRows(targets); - else deleteRow(targets[0]); - } + 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'; + items.push({ + label: label, + icon: '🗑', + danger: true, + disabled: !ctx.row || ctx.row.editable === false, + action: function () { + if (targets.length > 1) deleteRows(targets); + else deleteRow(targets[0]); + } + }); + + return items; } function onRowContext(ev) { diff --git a/zddc/internal/handler/tables.html b/zddc/internal/handler/tables.html index c5cda0d..deac1d1 100644 --- a/zddc/internal/handler/tables.html +++ b/zddc/internal/handler/tables.html @@ -1511,7 +1511,7 @@ body.is-elevated::after {
ZDDC Table - v0.0.17-alpha · 2026-05-19 13:13:20 · cef7188-dirty + v0.0.17-alpha · 2026-05-19 13:30:29 · f3d334a-dirty
@@ -5749,23 +5749,71 @@ 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: + // + // //archive//<...>/.yaml + // → //archive//<...>/?file=.yaml + // (already canonical — just split into dir + file) + // + // //ssr/.yaml (virtual SSR row) + // → //archive//?file=ssr.yaml + // + // //(mdl|rsk)/__.yaml (virtual rollup row) + // → //archive//(mdl|rsk)/?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 label = targets.length > 1 ? 'Delete ' + targets.length + ' rows' : 'Delete row'; - return [ - { - label: label, - icon: '🗑', - danger: true, - disabled: !ctx.row || ctx.row.editable === false, - action: function () { - if (targets.length > 1) deleteRows(targets); - else deleteRow(targets[0]); - } + 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'; + items.push({ + label: label, + icon: '🗑', + danger: true, + disabled: !ctx.row || ctx.row.editable === false, + action: function () { + if (targets.length > 1) deleteRows(targets); + else deleteRow(targets[0]); + } + }); + + return items; } function onRowContext(ev) {