From 30969f8f0480aafe99047a61da8859b979b56c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Wed, 9 Sep 2026 23:46:04 +0300 Subject: [PATCH] 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. --- frontend/index.html | 34 ++++++++++++++++++++++++++++++--- frontend/sw.js | 2 +- frontend/tests/profiler.test.js | 10 +++++++++- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 57288d2..48fd941 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -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 += '
' + rec.num + '' + - '
'; + ''; + if (c.records.length > 1) { + html += ''; + } + html += ''; } html += ''; + html += ''; } else { html += ''; } @@ -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(); diff --git a/frontend/sw.js b/frontend/sw.js index 1cbafc7..668c43b 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v46'; +const CACHE = 'otaman-v47'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/profiler.test.js b/frontend/tests/profiler.test.js index 1a33af4..5a633b8 100644 --- a/frontend/tests/profiler.test.js +++ b/frontend/tests/profiler.test.js @@ -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');