Configuration management for the Brazen user scripts framework
이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/418665/1875065/Brazen%20Framework%20-%20Configuration%20Manager.js을(를) 사용하여 포함하는 라이브러리입니다.
Typed settings schema, pluggable storage drivers (localStorage + Tampermonkey GM), cross-tab sync, backup/restore, and DOM factories bound to View Layer widgets.
Greasy Fork: Configuration Manager · Requires: Utilities, View Layer · Used by: Framework core
GM_setValue blobs for settings in apps. Prefer defining fields here so the framework can apply/save/reset consistently and include values in backups.Define fields in your app’s constructor, then mount them into the settings panel during UI composition.
// constructor (define schema)
this._configurationManager
.addFlagField('Enable Feature')
.setDefault(true)
.setHelpText('Turns the feature on/off.')
this._configurationManager
.addRulesetField('Tag Blacklist')
.setRows(15)
.setHelpText('One rule per line.')
.setSortRules(true)
// UI composition (mount controls)
this._userInterface = [
this._uiGen.createTabsSection(['Filters'], [
this._uiGen.createTabPanel('Filters', true).append([
this._configurationManager.createElement('Enable Feature'),
this._configurationManager.createElement('Tag Blacklist'),
]),
]),
]
new BrazenConfigurationManager(scriptPrefix, uiGenerator, tagSelectorGenerator)
| Parameter | Role |
|---|---|
scriptPrefix |
Prefix for storage keys (per driver) |
uiGenerator |
BrazenViewLayer instance |
tagSelectorGenerator |
(tag: string) => string — used by addTagRulesetField / _optimizeTagRuleset |
Framework exposes this as this._configurationManager. Register fields in the subclass constructor; call initialize() during init() before UI build.
Display name strings (e.g. 'Tag Blacklist') are normalized to kebab-case keys via Utilities.toKebabCase (tag-blacklist). Use the same display name in createElement, getValue, and getField.
What it does: You define a stable settings schema by adding typed fields. Fields become the single source of truth for UI binding, persistence, validation gating, and backup/restore.
How it works (developer-relevant):
add*Field(...) returns the field object so you can chain configuration (setDefault, setHelpText, setRows, etc.).field.attachStorage('gm') when a value should live in GM storage.persist: false) and use driver primitives directly (bookmarks, ledger).| Constant | add* method |
Stored value type |
UI |
|---|---|---|---|
CONFIG_TYPE_FLAG |
addFlagField(name) |
boolean |
Checkbox |
CONFIG_TYPE_TEXT |
addTextField(name) |
string |
Text input; empty → default on read (setDefault) |
CONFIG_TYPE_NUMBER |
addNumberField(name, min, max) |
number |
Number input |
CONFIG_TYPE_COLOR |
addColorField(name) |
color string | Color input |
CONFIG_TYPE_RANGE |
addRangeField(name, min, max) |
{ minimum, maximum } |
Two number inputs |
CONFIG_TYPE_SELECT |
addSelectField(name, pairs) |
option value | See known limitation in spec |
CONFIG_TYPE_CHECKBOXES_GROUP |
addCheckboxesGroup(name, pairs) |
string[] of checked data-values |
Checkbox group |
CONFIG_TYPE_RADIOS_GROUP |
addRadiosGroup(name, pairs) |
selected data-value |
Radio group |
CONFIG_TYPE_RULESET |
addRulesetField(name) |
string[] lines |
Textarea |
CONFIG_TYPE_ACTION |
addActionField(name) |
n/a (persist: false) |
Form button or dock-only action |
CONFIG_TYPE_BOOKMARKS |
addBookmarksField(name) |
array (self-managed on gm) |
Bookmarks panel |
CONFIG_TYPE_LEDGER |
addLedgerField(name) |
self-managed id set (not in aggregate blob) | No UI — GM-backed duplicate ledger; reload() migrates legacy index-only blobs to per-id entry keys + normalized { [primaryField]: ids } |
All add*Field methods return the field object. Chain setHelpText, setDefault, setRows, bookmark/ledger/dock setters, then field.attachStorage('local'|'gm') as needed (default driver: local). Manager methods (onExternalConfigurationChange, setDockActive, …) return the manager.
this._configurationManager.addFlagField('Enable Feature').setHelpText('…').setDefault(true)
this._configurationManager.addRulesetField('Tag Blacklist').setRows(15).setHelpText('…').setSortRules(true)
What it does: Ruleset fields turn user-edited textarea lines into a cleaned list of rules, optionally sorted/deduplicated, and optionally optimized into a runtime-friendly structure.
Apply/Save pipeline (setFromUserInterface, used by Apply/Save):
On Apply/Save (setFromUserInterface):
REGEX_LINE_BREAK; trim empty lines.sortRules: true — locale-aware sort.onTranslateFromUI(values) — normalize (optional).onOptimize(values) — store result in field.optimized (filters read this at runtime).On interface refresh (updateUserInterface):
onFormatForUI(value) → join with \n for textarea display.addTagRulesetField(key, useSelectors, rows, helpText?, formatter?, sortRules?) wraps addRulesetField with default onOptimize → _optimizeTagRuleset.
Per-rule API (programmatic edits):
Every ruleset field exposes methods for programmatic add/remove without going through the textarea. Callers get the field via getField(name) and invoke these directly; side effects (save(), compliance refresh, UI refresh elsewhere) remain the caller's responsibility.
| Method | Role |
|---|---|
onMatchRule(storedRule, target) |
Optional matcher callback; default is strict equality (storedRule === target) |
setRules(lines) |
Canonical write: onTranslateFromUI → optional sortRules → value → onOptimize → updateUserInterface |
findRuleIndex(target) |
Index of first matching rule, or -1 |
hasRule(target) |
Membership test via onMatchRule |
addRule(rule, target?) |
Upsert: remove matches for target (defaults to rule), append rule, then setRules |
removeRule(target) |
Drop all matches, then setRules |
toggleRule(rule, target?) |
Add or remove via hasRule |
clearRules() |
setRules([]) |
setRules does not deduplicate (preserves programmatic edits); setFromUserInterface (Apply/Save) still deduplicates via _deduplicateRulesetRules.
Tag rule syntax (one rule per line):
& — AND (all tags in segment must match)| — OR within a segment// — comment suffix (ignored after split)Optimized output: array of arrays (each inner array = one conjunctive rule). Sorted shortest-first.
addBookmarksFieldWhat it does: A self-managed bookmarks field backed by GM storage, rendered via the View Layer bookmarks panel.
Typical use cases:
addBookmarksField('Bookmarks', 'Add pages you visit often.', {
storageKey: 'myscript-bookmarks', // default: {scriptPrefix}bookmarks
storage: 'gm', // default
normalizeBookmarks: (stored) => /* sanitize array */,
showAddButton: true,
pageMatch: { getCurrentUrl, normalizeUrl, onMatchChange, watchSelectors, isActive },
onAdd, onSort, onRemove(id), onNavigate(url),
getRowActions(bookmark), // optional → HTMLElement[] | JQuery[]
})
| Behaviour | Detail |
|---|---|
persist |
false — not in aggregate blob; dedicated GM key via GmStorageDriver |
storage |
'gm' by default |
| Field API | reload(), save(), serializeForBackup(), applyFromBackup() (replace on restore) |
widget |
BrazenBookmarksPanel from createBookmarksPanel |
setFromUserInterface |
No-op — mutations call field.save() from site handlers |
updateUserInterface |
reload() then widget.render(value) |
createElement (mounting fields)What it does: Builds the View Layer DOM for a field, stores a reference on the field (field.element), and returns the mountable node.
this._configurationManager.createElement('My Setting') // display name
Invokes the field's createElement() which:
field.element.Fields without createElement called remain memory-only until mounted.
Each field implements:
| Callback | When |
|---|---|
createElement() |
First mount |
setFromUserInterface() |
Apply/Save — read DOM → field.value (+ ruleset optimize) |
updateUserInterface() |
Load/reset/tab switch — write field.value → DOM |
this._configurationManager.getValue('My Setting')
this._configurationManager.getField('My Setting') // ConfigurationField | null
this._configurationManager.getFieldOrFail('My Setting') // throws if missing
this._configurationManager.hasField('My Setting')
ConfigurationField properties: title, type, element, value, optimized?, helpText, minimum, maximum, options, persist, widget, ruleset callbacks.
Each field has a storage driver key (local by default). Simple fields persist in a per-driver aggregate blob; self-managed types (persist: false, e.g. ledger, bookmarks) use driver primitives directly and participate in backup v2 when they implement serializeForBackup.
| Store | Driver | Key | Content |
|---|---|---|---|
| Settings | local |
{scriptPrefix}settings |
JSON object: kebab keys → values (skips persist: false) |
| Sync id | local |
{scriptPrefix}settings-id |
{ id: number } — bumps on save |
| Settings | gm |
{scriptPrefix}settings |
Same shape in GM namespace (lazy; only when a field uses gm) |
| Sync id | gm |
{scriptPrefix}settings-id |
Same in GM namespace |
initialize()local driver stores (LocalStore)._syncDriverStores('local') — merge disk into fields; rulesets run onOptimize.onChange on settings store → updateInterface().visibilitychange — if tab visible and local id changed, re-sync + onExternalConfigurationChange.| Method | Behaviour |
|---|---|
update() |
All mounted fields: setFromUserInterface() (rulesets: trim → optional sort → onTranslateFromUI → dedupe rules case-sensitively → onOptimize) |
save() |
update() → write store → bump sync id |
revertChanges() |
Re-read store into fields + updateInterface() |
updateInterface() |
All mounted fields: updateUserInterface() |
Framework button wiring:
update() + re-run compliance (no disk).save().revertChanges() + compliance.backup() downloads {scriptPrefix}backup.json:
{
"version": 2,
"id": 123456789,
"drivers": {
"local": {
"tag-blacklist": ["rule1", "rule2"],
"pagination-threshold": 20
},
"gm": {
"download-ledger": { "postIds": ["123", "456"] },
"bookmarks": [{ "id": "…", "label": "…", "tags": "…", "url": "…" }]
}
}
}
Legacy flat backups (no version / drivers) still import as local values.
restore(response) — fetch/Response with JSON body; each field's applyFromBackup handles semantics (ledger merges ids; bookmarks replace list); restores values + sync id; alerts success/failure. Legacy v1 backups without a bookmarks key leave local bookmarks untouched.
onExternalConfigurationChange(handler) — (manager) => void when another tab saved. Framework registers compliance re-run.
generateValidationCallbackgenerateValidationCallback(configKey) → default enable checks:
| Type | Active when |
|---|---|
| flag / radios / select | truthy value |
| checkboxes | length > 0 |
| number | value > 0 |
| range | `minimum > 0 \ |
| ruleset / text | length > 0 |
Used by framework _addItemComplianceFilter when no custom validate callback is supplied.
init() builds UI.createElement inside tab panels.addFlagField + ruleset).addLedgerField, addBookmarksField) use dedicated driver keys — no ad-hoc GM_setValue in apps.#bv-ui userScript reference if tabs need updateInterface from custom widgets.Next in stack: Item Attributes Resolver (before Framework core).
addFlagField, addTextField, addNumberField, addColorField, addRangeField, addSelectField, addCheckboxesGroup, addRadiosGroup, addRulesetField, addActionField, addBookmarksField, addLedgerField, addTagRulesetFieldcreateElement(name)getValue, getField, getFieldOrFail, hasFieldinitialize, update, save, revertChanges, updateInterfacebackup, restore, onExternalConfigurationChangegenerateValidationCallback