profiler editor: keep record/content fields visible
- Seed one empty record row when record content is created or reset (filetype change or Contents None->Exact/Mask) via profilerEmptyRecordContent - Add per-record delete and '+ Record' add controls so the record view is always editable; delete is hidden on the last remaining row SW cache v47.
This commit is contained in:
+31
-3
@@ -6744,6 +6744,11 @@ function profilerContentKindForFileType(ft) {
|
||||
return (ft === 'linear_fixed' || ft === 'cyclic') ? 'record' : 'transparent';
|
||||
}
|
||||
|
||||
// A fresh record content object seeded with one empty record row.
|
||||
function profilerEmptyRecordContent(mode) {
|
||||
return { mode: mode, kind: 'record', records: [{ num: 1, data: '' }] };
|
||||
}
|
||||
|
||||
function profilerValidateProfile(obj) {
|
||||
if (!obj || typeof obj !== 'object') return 'Not an object';
|
||||
if (!obj.name) return 'Missing name';
|
||||
@@ -7065,9 +7070,14 @@ function profilerRenderContentEditor(i, r) {
|
||||
for (let ri = 0; ri < c.records.length; ri++) {
|
||||
const rec = c.records[ri];
|
||||
html += '<div class="flex items-center gap-2"><span class="text-xs text-gray-500 w-8">' + rec.num + '</span>' +
|
||||
'<input value="' + escHtml(rec.data) + '" oninput="profilerUpdateRecord(' + i + ',' + ri + ',this.value)" class="flex-1 font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800"></div>';
|
||||
'<input value="' + escHtml(rec.data) + '" oninput="profilerUpdateRecord(' + i + ',' + ri + ',this.value)" class="flex-1 font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800">';
|
||||
if (c.records.length > 1) {
|
||||
html += '<button onclick="profilerDeleteRecord(' + i + ',' + ri + ')" class="text-red-400 hover:text-red-600 text-xs px-1">✕</button>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
html += '<button onclick="profilerAddRecord(' + i + ')" class="mt-1 text-xs px-2 py-0.5 rounded bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300">+ Record</button>';
|
||||
} else {
|
||||
html += '<textarea oninput="profilerUpdateRuleContent(' + i + ',this.value)" rows="2" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 dark:bg-slate-800">' + escHtml(c.expected || '') + '</textarea>';
|
||||
}
|
||||
@@ -7091,7 +7101,7 @@ function profilerUpdateRuleFileType(i, value) {
|
||||
const kind = profilerContentKindForFileType(value);
|
||||
if (r.content.kind !== kind) {
|
||||
r.content = kind === 'record'
|
||||
? { mode: r.content.mode, kind: 'record', records: [] }
|
||||
? profilerEmptyRecordContent(r.content.mode)
|
||||
: { mode: r.content.mode, kind: 'transparent', expected: '' };
|
||||
}
|
||||
}
|
||||
@@ -7108,7 +7118,7 @@ function profilerSetContent(i, mode) {
|
||||
} else {
|
||||
const kind = profilerContentKindForFileType(r.fileType);
|
||||
r.content = kind === 'record'
|
||||
? { mode: mode, kind: 'record', records: [] }
|
||||
? profilerEmptyRecordContent(mode)
|
||||
: { mode: mode, kind: 'transparent', expected: '' };
|
||||
}
|
||||
profilerRenderEditor();
|
||||
@@ -7126,6 +7136,24 @@ function profilerUpdateRecord(i, ri, value) {
|
||||
if (c && c.records) c.records[ri].data = value;
|
||||
}
|
||||
|
||||
function profilerAddRecord(i) {
|
||||
if (!profilerDraft) return;
|
||||
const c = profilerDraft.rules[i].content;
|
||||
if (!c || c.kind !== 'record') return;
|
||||
const lastNum = c.records.length ? c.records[c.records.length - 1].num : 0;
|
||||
c.records.push({ num: lastNum + 1, data: '' });
|
||||
profilerRenderEditor();
|
||||
}
|
||||
|
||||
function profilerDeleteRecord(i, ri) {
|
||||
if (!profilerDraft) return;
|
||||
const c = profilerDraft.rules[i].content;
|
||||
if (!c || c.kind !== 'record') return;
|
||||
if (c.records.length <= 1) return;
|
||||
c.records.splice(ri, 1);
|
||||
profilerRenderEditor();
|
||||
}
|
||||
|
||||
function profilerSaveProfile() {
|
||||
if (!profilerDraft) return;
|
||||
profilerDraft.name = document.getElementById('profiler-name').value.trim();
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'otaman-v46';
|
||||
const CACHE = 'otaman-v47';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -21,7 +21,7 @@ function extractFunc(src, name) {
|
||||
return src.slice(m.index, i + 1);
|
||||
}
|
||||
|
||||
const FNS = ['profilerNormHex', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerValidateProfile'];
|
||||
const FNS = ['profilerNormHex', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile'];
|
||||
let code = '';
|
||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||
eval(code);
|
||||
@@ -92,6 +92,14 @@ test('profilerContentKindForFileType', () => {
|
||||
assert.strictEqual(profilerContentKindForFileType('df'), 'transparent');
|
||||
});
|
||||
|
||||
test('profilerEmptyRecordContent seeds one empty record row', () => {
|
||||
const c = profilerEmptyRecordContent('exact');
|
||||
assert.strictEqual(c.kind, 'record');
|
||||
assert.strictEqual(c.mode, 'exact');
|
||||
assert.strictEqual(c.records.length, 1);
|
||||
assert.deepStrictEqual(c.records[0], { num: 1, data: '' });
|
||||
});
|
||||
|
||||
test('profile validation', () => {
|
||||
assert.strictEqual(profilerValidateProfile(null), 'Not an object');
|
||||
assert.strictEqual(profilerValidateProfile({ name: 'x' }), 'Missing rules array');
|
||||
|
||||
Reference in New Issue
Block a user