* feat(ui): add reusable json-file-manager widget for plugin file management
Introduces JsonFileManager — a zero-CDN, keyboard-accessible, configurable
widget for managing JSON data files from plugin configuration forms.
web_interface/static/v3/js/widgets/json-file-manager.js (new):
- Self-contained class with scoped CSS (no global leakage)
- File list with cards: enable/disable toggle, entry count, size, date
- Drag-and-drop + click-to-browse JSON upload
- Textarea-based JSON editor (no CDN); Format + Validate buttons
- Ctrl+S to save, Escape to close any open modal
- Create-new-file modal with configurable fields and validation
- Delete confirmation modal
- All actions (list/get/save/upload/delete/create/toggle) are configurable
via x-widget-config in config_schema.json — no plugin-ID hardcoding
web_interface/static/v3/plugins_manager.js:
- New handler for x-widget: "json-file-manager" — renders mount div,
instantiates JsonFileManager with x-widget-config and plugin ID
web_interface/templates/v3/base.html:
- Include json-file-manager.js (defer) before plugins_manager.js
Usage: set x-widget: "json-file-manager" + x-widget-config in any
plugin's config_schema.json (see ledmatrix-plugins of-the-day for a
complete example).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(json-file-manager): review fixes — type=button, finally, display_name, instance tracking
- Add type="button" to every button in the template (replace_all) so none
default to submit inside the plugin-config-form
- Wrap _doSave/_doDelete/_doCreate fetch blocks in try/finally so _idle()
always fires, not only on the error path
- _doCreate validation: skip the required-check for display_name (f.key
!== 'display_name') and only validate pattern when val is non-empty, so
the auto-derive logic at the end of the loop can run; simplify the
derive block to a single conditional instead of nested DOM lookups
- plugins_manager.js: track instances in window.__jfmInstances[safeFieldId]
and call _destroy() on any previous instance before mounting a new one,
preventing duplicate keydown handlers when the config form is re-rendered
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(json-file-manager): use validity.patternMismatch; destroy all instances on remount
- Replace `new RegExp(f.pattern).test(val)` with `el.validity.patternMismatch`
to avoid potential SyntaxError from untrusted pattern strings and rely on the
browser's already-validated pattern attribute instead
- plugins_manager.js: iterate all window.__jfmInstances and call _destroy() on
every entry before mounting, then reset the map, so no orphaned keydown
handlers survive when any plugin config form is re-rendered
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plugins_manager): scope jfm instance teardown to current mount key only
The global sweep (Object.values + window.__jfmInstances = {}) destroyed
sibling file-manager widgets when any one of them was remounted. Replace
with a targeted destroy of window.__jfmInstances[safeFieldId] only,
leaving all other entries untouched.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(json-file-manager): address Codacy security warnings
- Replace Math.random() with crypto.getRandomValues() for UID generation
- Remove unused variable `u` in _card()
- Guard this.actions property access with hasOwnProperty
- Replace btn.innerHTML in _busy/_idle with DOM manipulation + textContent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Chuck <chuck@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>