From 61ab7bc30414aaee989c865dab0003f4d4b33e4d 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:24:18 +0300 Subject: [PATCH] profiler editor: filetype-driven fields and content view - Show Size only for transparent/ber_tlv; Record length/count only for linear_fixed/cyclic; hide all size/record fields for df (unknown shows all) - Hide the Contents section entirely for df (existence-only check) - Changing file type clears now-inapplicable fields and resets content (keeps mode) when its kind switches between record and transparent - profilerSetContent defaults content kind from the selected file type Add profilerFileFields + profilerContentKindForFileType helpers + tests. SW cache v45. --- frontend/index.html | 59 ++++++++++++++++++++++++++++----- frontend/sw.js | 2 +- frontend/tests/profiler.test.js | 20 ++++++++++- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 90d12d6..2946c32 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6731,6 +6731,19 @@ function profilerMaskPrefix4(hex) { return h.slice(0, 8) + '?'.repeat(h.length - 8); } +// Which size/record fields apply to a given file type. +function profilerFileFields(ft) { + if (ft === 'linear_fixed' || ft === 'cyclic') return { size: false, records: true }; + if (ft === 'df') return { size: false, records: false }; + if (ft === 'transparent' || ft === 'ber_tlv') return { size: true, records: false }; + return { size: true, records: true }; +} + +// Content representation ('record' vs 'transparent') for a given file type. +function profilerContentKindForFileType(ft) { + return (ft === 'linear_fixed' || ft === 'cyclic') ? 'record' : 'transparent'; +} + function profilerValidateProfile(obj) { if (!obj || typeof obj !== 'object') return 'Not an object'; if (!obj.name) return 'Missing name'; @@ -7014,17 +7027,24 @@ function profilerRenderRule(i, r) { html += '
' + '
'; html += ''; + const vis = profilerFileFields(r.fileType); html += '
'; html += '
' + - '
'; - html += '
' + - '
'; - html += '
' + - '
'; - html += '
' + - '
'; + '
'; + if (vis.size) { + html += '
' + + '
'; + } + if (vis.records) { + html += '
' + + '
'; + html += '
' + + '
'; + } html += ''; - html += profilerRenderContentEditor(i, r); + if (r.fileType !== 'df') { + html += profilerRenderContentEditor(i, r); + } html += ''; return html; } @@ -7059,6 +7079,24 @@ function profilerUpdateRule(i, field, value) { profilerDraft.rules[i][field] = value; } +function profilerUpdateRuleFileType(i, value) { + if (!profilerDraft) return; + const r = profilerDraft.rules[i]; + r.fileType = value; + const vis = profilerFileFields(value); + if (!vis.size) r.fileSize = null; + if (!vis.records) { r.recordLen = null; r.numRecords = null; } + if (r.content) { + const kind = profilerContentKindForFileType(value); + if (r.content.kind !== kind) { + r.content = kind === 'record' + ? { mode: r.content.mode, kind: 'record', records: [] } + : { mode: r.content.mode, kind: 'transparent', expected: '' }; + } + } + profilerRenderEditor(); +} + function profilerSetContent(i, mode) { if (!profilerDraft) return; const r = profilerDraft.rules[i]; @@ -7067,7 +7105,10 @@ function profilerSetContent(i, mode) { } else if (r.content) { r.content.mode = mode; } else { - r.content = { mode: mode, kind: 'transparent', expected: '' }; + const kind = profilerContentKindForFileType(r.fileType); + r.content = kind === 'record' + ? { mode: mode, kind: 'record', records: [] } + : { mode: mode, kind: 'transparent', expected: '' }; } profilerRenderEditor(); } diff --git a/frontend/sw.js b/frontend/sw.js index a87b9ca..89814fa 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v44'; +const CACHE = 'otaman-v45'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/profiler.test.js b/frontend/tests/profiler.test.js index 75f63f7..1a33af4 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', 'profilerValidateProfile']; +const FNS = ['profilerNormHex', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerValidateProfile']; let code = ''; for (const f of FNS) code += extractFunc(html, f) + '\n'; eval(code); @@ -74,6 +74,24 @@ test('profilerMaskPrefix4 keeps first 4 bytes and masks the rest', () => { assert.strictEqual(profilerMaskPrefix4('1234'), '1234'); }); +test('profilerFileFields maps file type to applicable fields', () => { + assert.deepStrictEqual(profilerFileFields('transparent'), { size: true, records: false }); + assert.deepStrictEqual(profilerFileFields('ber_tlv'), { size: true, records: false }); + assert.deepStrictEqual(profilerFileFields('linear_fixed'), { size: false, records: true }); + assert.deepStrictEqual(profilerFileFields('cyclic'), { size: false, records: true }); + assert.deepStrictEqual(profilerFileFields('df'), { size: false, records: false }); + assert.deepStrictEqual(profilerFileFields(null), { size: true, records: true }); + assert.deepStrictEqual(profilerFileFields(''), { size: true, records: true }); +}); + +test('profilerContentKindForFileType', () => { + assert.strictEqual(profilerContentKindForFileType('linear_fixed'), 'record'); + assert.strictEqual(profilerContentKindForFileType('cyclic'), 'record'); + assert.strictEqual(profilerContentKindForFileType('transparent'), 'transparent'); + assert.strictEqual(profilerContentKindForFileType('ber_tlv'), 'transparent'); + assert.strictEqual(profilerContentKindForFileType('df'), 'transparent'); +}); + test('profile validation', () => { assert.strictEqual(profilerValidateProfile(null), 'Not an object'); assert.strictEqual(profilerValidateProfile({ name: 'x' }), 'Missing rules array');