View layer for the Brazen user scripts framework
ของเมื่อวันที่
สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.sleazyfork.org/scripts/416104/1881858/Brazen%20Framework%20-%20View%20Layer.js
Shared settings panel UI: #bv-ui, tabs, form controls, modals, bookmarks panel, compliance statistics widgets, and tag-sidebar enhancements. Apps and other framework modules use this as a UI “toolbox” to build a consistent panel without per-site UI code.
Greasy Fork: View Layer · Requires: Utilities · Used by: Configuration Manager, Framework core
Styles ship inlined at runtime via GM_addStyle when this module loads. Requires @grant GM_addStyle on this script or the app.
createElement(...).In most apps you don’t “build the panel from scratch”; the framework embeds #bv-ui, and you mount your fields and extra widgets into tab panels.
// In your app (extending BrazenFramework), you compose nodes during the framework UI build
// (e.g. via _onBeforeUIBuild / _onAfterUIBuild):
this._userInterface = [
this._uiGen.createTabsSection(['Filters', 'Downloads'], [
this._uiGen.createTabPanel('Filters', true).append([
this._configurationManager.createElement('Tag Blacklist'),
]),
this._uiGen.createTabPanel('Downloads').append([
/* app-specific controls */
]),
]),
this._uiGen.createBottomSection([
this._uiGen.createStatisticsTotalsGroup(),
]),
]
const uiGen = new BrazenViewLayer(scriptPrefix)
scriptPrefix feeds SelectorGenerator for stat label element ids.
What it does: Creates the root panel (#bv-ui) and the resize handle used by the framework’s _embedUI step. The button dock (Framework configureDock) is the only way to open the panel.
How it behaves (developer-relevant):
mouseleave unless resizing.APIs:
| Factory / method | Role |
|---|---|
createSettingsSection() |
Root #bv-ui section (hidden by default); includes #bv-resizer; tagged bv-dock-slide-panel |
getSelectedSection() |
Returns the container from createContainer() / createSettingsSection() |
isSettingsPaneBeingResized() |
true while #bv-resizer drag is active |
createResizer() |
Width drag handle (min width 150px) |
Framework _embedUI:
configureDock(); appends panel + dock rail to body.mouseleave on panel schedules a 1 s hide via _hideMainPanel; re-entering cancels it (unless resizing).userScript on #bv-ui: Optional app reference for widgets that need the host instance (getSelectedSection()[0].userScript = this in _onAfterUIBuild). Tab buttons only switch visible panels — they do not call updateInterface() (that would discard unsaved edits).
What it does: Provides simple building blocks for consistent panel layout.
| Method | Returns | Notes |
|---|---|---|
appendToBody(nodes) |
— | Static. Appends jQuery nodes to body |
createContainer() |
JQuery |
<section class="bv-section bv-font-primary"> |
createBottomSection(children) |
JQuery |
.bv-bottom-section footer column |
createTitle(title) |
JQuery |
.bv-title label |
createSeparator() |
JQuery |
<hr/> |
createBreakSeparator() |
JQuery |
<br class="bv-break"/> |
createFormSection(title) |
JQuery |
Titled block with inner .bv-title |
What it does: Creates consistent “form group” markup. Configuration Manager fields use these factories when you mount a field via createElement.
| Method | Returns | Notes |
|---|---|---|
createFormGroup() |
JQuery |
.bv-group row |
createFormGroupLabel(label, inputType?) |
JQuery |
.bv-label; adds : for text/number |
createFormGroupInput(type) |
JQuery |
.bv-input; adds .bv-text or .bv-checkbox-radio |
createFormInputGroup(label, type, hoverHelp?) |
JQuery |
Label + single input |
createFormRangeInputGroup(label, type, min, max, help?) |
JQuery |
.bv-range-group with two inputs (min/max) |
createFormTextAreaGroup(label, rows, help?) |
JQuery |
.bv-textarea-group |
createFormSubstitutionComposer(options?) |
JQuery |
Subject → Alias + plus button (.bv-substitution-composer) |
createPlusIcon(iconClass?) |
SVGSVGElement |
Plus icon for add buttons |
createFormCheckBoxesGroupSection(label, pairs, help?) |
JQuery |
.bv-checkboxes-group; data-value on each input |
createFormRadiosGroupSection(label, pairs, help?) |
JQuery |
.bv-radios-group; mutual exclusive on change |
createFormGroupDropdown(id, pairs) |
JQuery |
<select class="bv-input"> — use directly if needed |
createFormButton(caption, hoverHelp, onClick) |
JQuery |
.bv-button |
createFormActions(children, wrapperClasses?) |
JQuery |
.bv-actions button row |
Configuration Manager uses these factories via each field's createElement binding.
What it does: Builds a tabbed section used by most scripts to separate Filters / Options / Downloads etc.
Critical rule: createTabPanel(tabName) sets the panel id to Utilities.toKebabCase(tabName). Your tab button text must match the panel id after kebab-casing (e.g. "My Filters" → id="my-filters").
| Method | Notes |
|---|---|
createTabsSection(tabNames[], tabPanels[]) |
.bv-tabs-section with nav + panels |
createTabButton(tabName, isFirst) |
.bv-tab-button; first tab gets .bv-active |
createTabPanel(tabName, isFirst?) |
.bv-tab-panel; id = Utilities.toKebabCase(tabName) |
Critical: Tab button label text must kebab-case to the panel id. Example: button "My Filters" → panel id="my-filters". Active panel gets .bv-active (display: flex).
On tab click: deactivates other buttons/panels, activates matching #kebab-tab-name. Does not reload field values from storage.
What it does: Provides UI nodes that StatisticsRecorder updates to show how many items each filter removed.
| Method | Notes |
|---|---|
createFormGroupStatLabel(statisticType) |
<label class="bv-stat-label"> with id from SelectorGenerator.getStatLabelSelector |
createStatisticsFormGroup(statisticType, label?) |
.bv-stat-group; when label is omitted, humanizes kebab-case statisticType and appends Filter |
createStatisticsTotalsGroup() |
Total row wired to Total stat key |
Pair statisticType with framework filter config keys passed to StatisticsRecorder.record.
What it does: Creates a consistent modal dialog with overlay, close controls, and Escape-to-close wiring.
let modal = this._uiGen.createModal('Title', bodyNodes) // bodyNodes: JQuery | JQuery[]
this._uiGen.showModal(modal)
this._uiGen.hideModal(modal)
| Behaviour | Detail |
|---|---|
| Overlay | .bv-modal-overlay; click outside closes |
| Close control | .bv-modal-close button |
| Escape | keydown.bvModal on document |
createModal builds .bv-modal-dialog > header (.bv-modal-title) + .bv-modal-body.
Dock-list panels (tag discovery, compliance rules, human interaction) share .bv-dock-list-* chrome (intro, groups, rows, name/meta, footer).
What it does: Dock-anchored slide panels that stack beside the rail. Framework passes them near-dock → outward (human-interaction → tag discovery → compliance → settings #bv-ui) so review panels sit on the rail and push #bv-ui outward. View Layer supplies the DOM factories and animation helpers.
When to use: Apps rarely call these directly — Download Manager and Framework wire tag discovery and human-interaction panels. Compliance apps with trackComplianceRules get the compliance rules panel from Framework. Use the static helpers when building a custom dock slide panel that should match Brazen chrome.
| Method | Role |
|---|---|
createDockSlidePanel(id, title) |
Static. Base #bv-* slide panel shell with close button |
isDockSlidePanelVisible(panel) |
Static. Whether panel is on-screen |
setDockSlidePanelOrientations(orientation) |
Static. Apply bv-dock-panel-left/right/bottom to all slide panels |
syncDockSlidePanelStack(dockElement, orientation, panels) |
Static. Position stacked panels relative to dock |
showDockSlidePanel(panel, orientation, beforeAnimate?) |
Static. Slide in (no restart if already visible) |
hideDockSlidePanel(panel, orientation, animate?, onHidden?) |
Static. Slide out |
setDockSlidePanelHidden(panel, hidden) |
Static. Toggle hidden class |
wireDockSlidePanel(panel, { onClose, onMouseLeave }) |
Static. Bind close / mouseleave |
updateDockSlidePanelToggleButtons({ mainPanel, compliancePanel }) |
Static. Sync active class on dock toggle buttons |
Shared list chrome: .bv-dock-list-intro, .bv-dock-list-groups, .bv-dock-list-group, .bv-dock-list, .bv-dock-list-row, .bv-dock-list-name, .bv-dock-list-name-link, .bv-dock-list-meta, .bv-dock-list-similar, .bv-dock-list-footer.
What it does: #bv-tag-discovery-panel — dock slide panel for reviewing unknown tags before downloads continue. Download Manager creates and drives it; apps supply tagDiscovery config (types, colors, actions). Includes a collapsed Similar tags section (filled by Download Manager after paint) and optional name clicks via onTagNameClick.
| Method | Role |
|---|---|
createTagDiscoveryPanel({ onConfirm, onSkip?, onOpenMedia? }) |
Static. Panel with Skip / Open media / Confirm footer and collapsed Similar tags shell |
renderTagDiscoveryPanelContent(panel, tags, renderActions, options?) |
Static. Unknown + known tag sections, grouped rows, incidence meta; resets Similar to loading unless resetSimilar: false; preserveSimilarOpen / clearSimilarCount control soft resets; optional intro |
resetTagDiscoverySimilarSection(panel, options?) |
Static. Set Similar status text; optional preserveOpen / count (omit count to keep the prior summary total) |
setTagDiscoverySimilarStatus(panel, status, options?) |
Static. Loading / empty status in Similar body; optional count updates the accordion summary |
renderTagDiscoverySimilarContent(panel, tags, renderActions, options?) |
Static. Fill Similar rows without re-rendering New/Known; keeps accordion open state |
prepareTagDiscoveryPanelOptions(config) |
Static. Merge tagTypes into group/row class resolvers |
applyTagDiscoveryTypeColors(tagTypes) |
Static. Inject per-type name colors |
renderTagActions(row, tag, actionsElement, config) |
Static. Reusable per-tag action row helper |
options.onTagNameClick (when set) makes .bv-dock-list-name clickable (pointer, no underline). Confirm / Skip / Open media map to Framework confirmTagDiscoveryMappings, skipTagDiscoveryInclusion, openTagDiscoveryMedia.
What it does: #bv-human-interaction-panel — shown on the processor-leader tab when a rate-limit handler sets humanInteractionBlocked. Does not auto-close on mouseleave.
| Method | Role |
|---|---|
createHumanInteractionPanel({ onConfirm?, onReopen?, title?, message?, confirmLabel?, reopenLabel?, showReopen? }) |
Static. Queue pane (Done — resume / Reopen) or standalone reload pane (showReopen: false) |
updateHumanInteractionPanelContent(panel, { title?, message?, confirmLabel?, reopenLabel? }) |
Static. Refresh copy (e.g. consumer reopenLabel) |
What it does: #bv-compliance-rules — dock slide panel listing active hide rules when trackComplianceRules is enabled (replaces the old modal).
| Method | Role |
|---|---|
createComplianceRulesSlidePanel() |
Instance factory — intro + .bv-dock-list-groups shell |
renderComplianceRulesPanelContent(panel, report, { canRemoveRule?, onRemoveRule? }) |
Grouped filter sections with name, count meta, Remove action |
What it does: Ready-to-append Patreon support tab for settings panels.
this._userInterface = [
this._uiGen.createTabsSection(['Filters', 'Donate'], [
this._uiGen.createTabPanel('Filters', true).append([/* … */]),
this.createDonateTabPanel({ tabName: 'Donate', patreonUrl: 'https://…' }),
]),
]
| Method | Role |
|---|---|
createDonateTabPanel({ tabName?, isFirst?, patreonUrl?, linkLabel?, message? }) |
Tab panel with Patreon link + icon |
createPatreonIcon(iconClass?) |
Static. SVG Patreon mark |
Framework exposes createDonateTabPanel(options?) as a thin wrapper.
Used by Download Manager for dock progress and search-tile pipeline overlays.
| Method | Role |
|---|---|
createDownloadManagerProgressSlot() |
Static. .bv-dock-progress-panel with download bar + current/total, unlabeled resolution bar (below on bottom dock; vertical beside counters on left/right), and a descriptive title |
updateDownloadManagerProgressSlot(slot, progress) |
Static. Fills download and resolution bars from progress.download / progress.resolution; label shows download current/total |
setDownloadManagerProgressSlotVisible(slot, visible) |
Static. Toggle .bv-dock-progress-hidden |
updateDownloadManagerItemProgress(element, progress) |
Static. Blur overlay + step label + segmented bar (.bv-dm-item-active dim/blur uses a 100ms transition) |
clearDownloadManagerItemProgress(element) |
Static. Remove .bv-dm-item-progress overlay |
updateDockFieldButton(field, presentation) |
Refresh dock button icon/tooltip/active/disabled from CM field |
progress for item overlays: { label, stepIndex, stepCount, failed?, complete? }.
| Method | Role |
|---|---|
ensureIdbBlockedBanner() |
Static. Prepends #bv-idb-blocked-banner when persistence is disabled |
| Method | Role |
|---|---|
showConfirmDialog({ title, message, confirmLabel?, cancelLabel?, showCancel? }) |
Static. Promise<boolean> modal |
What it does: A reusable bookmarks UI component (search, sort, add/remove, optional per-row actions, and optional “current page bookmarked?” state).
Typical use case: Use via Configuration Manager addBookmarksField(...), which handles persistence and wires render(...).
createBookmarksPanel(options?) → BrazenBookmarksPanel
BrazenBookmarksPanelOptions)| Option | Default | Role |
|---|---|---|
showAddButton |
true |
Show add button above list |
addCaption |
'Add Bookmark' |
Add button text |
addHelpText |
'' |
Add button tooltip |
searchPlaceholder |
'Search bookmarks…' |
Filter input placeholder |
searchHelpText |
Filter tooltip | |
sortHelpText |
Sort tooltip | |
emptyMessage |
'No bookmarks yet.' |
No bookmarks state |
noMatchMessage |
'No matching bookmarks.' |
Filter miss state |
onAdd / onSort / onRemove(id) / onNavigate(url) |
callbacks | onNavigate defaults to location.href = url |
getRowActions(bookmark) |
() => [] |
Optional extra buttons per row (see .bv-bookmark-main, .bv-bookmark-action) |
pageMatch |
optional | Current-page bookmark detection (below) |
pageMatch (BrazenBookmarksPageMatchOptions)| Option | Role |
|---|---|
getCurrentUrl() |
URL to compare (default location.href) |
normalizeUrl(url) |
Trim/normalize before compare |
onMatchChange(isBookmarked) |
Called when match state changes |
watchSelectors[] |
Re-check on input events |
isActive() |
If returns false, forces onMatchChange(false) |
BrazenBookmarksPanel)| Method | Role |
|---|---|
element |
Root .bv-bookmarks-group jQuery node |
render(bookmarks[]) |
BrazenBookmarkEntry[] → { id, label, tags, url } |
getFilterQuery() |
Current search string |
isCurrentPageBookmarked(url?) |
Compare normalized URL to list |
checkPageMatch() |
Refresh pageMatch state |
dispose() |
Disconnect scroll ResizeObserver and remove pageMatch input listeners |
Wired by Configuration Manager addBookmarksField — self-managed on the gm driver (reload / save, included in backup v2).
enhanceTagSidebarCountsWhat it does: Enhances a tag sidebar by wrapping the “count” span in a hover-reveal actions slot (e.g. bookmark/add-to-rule buttons).
enhanceTagSidebarCounts(options?) — wraps tag count spans with hover-reveal action slots.
| Option | Default |
|---|---|
sidebarSelector |
'#tag-sidebar' |
rowSelector |
'li[class*="tag-type-"]' |
countSelector |
'span.tag-count' |
hideDelayMs |
2000 (0 if prefers-reduced-motion) |
extractTag(row) |
() => ({ name: '' }) |
renderActions(row, tag, actionsEl, ctx) |
ctx.isBookmarked from isBookmarked(tagName) |
isBookmarked(name) |
() => false |
DOM: .bv-tag-actions-slot wraps count; .bv-tag-actions shown on pointer enter; count hidden while actions visible; .bv-tag-row-active on row.
What it does: Shared “token chip” UI to help users build filename/subfolder patterns by clicking tokens and separators into focused inputs.
When to call it: Typically from _onAfterUIBuild, after your Downloads tab exists in the DOM.
Shared chip UI for filename/subfolder pattern fields:
| Method | Role |
|---|---|
createPatternTokenChip(chip) |
One token/separator button |
createSharedPatternTokenBuilder(sections, separators) |
Full builder panel (.bv-pattern-builder-shared) |
insertIntoPatternInput(input, text) |
Insert at caret with flash feedback |
patchPatternTokenBuilder(filenameInput, subfolderInput, builder, eventNamespace?) |
Wire focus + click handlers |
patchStackedPatternFieldGroups(configurationManager, configKeys) |
Add .bv-pattern-group to folder/pattern fields |
patchRangeInputLayout() |
Wrap paired range inputs in .bv-range-inputs |
Call patchPatternTokenBuilder from _onAfterUIBuild after the Downloads tab is in the DOM.
What it is: The stable class/id set the view layer uses. This is mainly useful when you write custom CSS or build custom widgets that should visually match the built-ins.
| Class / id | Role |
|---|---|
#bv-ui |
Main settings panel (dock slide panel) |
#bv-resizer, #bv-resizer-icon |
Width handle |
.bv-section, .bv-bg-colour, .bv-font-primary |
Panel chrome |
.bv-tab-button, .bv-tab-panel, .bv-tabs-nav, .bv-tabs-section |
Tabs |
.bv-active |
Active tab button or panel |
.bv-group, .bv-label, .bv-input, .bv-button, .bv-actions |
Form |
.bv-checkboxes-group, .bv-radios-group, .bv-range-group, .bv-textarea-group |
Group layouts |
.bv-stat-group, .bv-stat-label |
Statistics |
.bv-pattern-builder, .bv-pattern-builder-shared, .bv-token-row, .bv-token-bar, .bv-token-btn, .bv-token-sep, .bv-pattern-group, .bv-pattern-active, .bv-range-inputs |
Pattern token builder |
.bv-bookmarks-group, .bv-bookmark-* |
Bookmarks UI |
.bv-modal-overlay, .bv-modal-dialog, .bv-modal-* |
Modals |
.bv-dock-list-*, .bv-dock-slide-panel-* |
Dock slide list panels (discovery, compliance, human interaction) |
.bv-tag-actions-slot, .bv-tag-actions, .bv-tag-row-active |
Sidebar tag actions |
this._userInterface = [
this._uiGen.createTabsSection(['Filters', 'Options'], [
this._uiGen.createTabPanel('Filters', true).append([
this._configurationManager.createElement('Tag Blacklist'),
]),
this._uiGen.createTabPanel('Options').append([/* ... */]),
]),
this._uiGen.createBottomSection([
this._uiGen.createStatisticsTotalsGroup(),
this._uiGen.createSettingsFormActions(), // from framework helper
]),
]
Next in stack: Configuration Manager
createSettingsSection, createResizer, isSettingsPaneBeingResized, getSelectedSectionappendToBody, createContainer, createBottomSection, createTitle, createSeparator, createBreakSeparator, createFormSectioncreateFormGroup*, createFormInputGroup, createFormRangeInputGroup, createFormTextAreaGroup, createFormSubstitutionComposer, createFormCheckBoxesGroupSection, createFormRadiosGroupSection, createFormGroupDropdown, createFormButton, createFormActions, createPlusIconcreateTabsSection, createTabButton, createTabPanelcreateFormGroupStatLabel, createStatisticsFormGroup, createStatisticsTotalsGroupcreateModal, showModal, hideModalcreateBookmarksPanel (and BrazenBookmarksPanel methods)enhanceTagSidebarCountscreatePatternTokenChip, createSharedPatternTokenBuilder, insertIntoPatternInput, patchPatternTokenBuilder, patchStackedPatternFieldGroups, patchRangeInputLayoutcreateDockSlidePanel, isDockSlidePanelVisible, setDockSlidePanelOrientations, syncDockSlidePanelStack, showDockSlidePanel, hideDockSlidePanel, setDockSlidePanelHidden, wireDockSlidePanel, updateDockSlidePanelToggleButtonscreateTagDiscoveryPanel, renderTagDiscoveryPanelContent, resetTagDiscoverySimilarSection, setTagDiscoverySimilarStatus, renderTagDiscoverySimilarContent, prepareTagDiscoveryPanelOptions, applyTagDiscoveryTypeColors, renderTagActionscreateHumanInteractionPanel, updateHumanInteractionPanelContentcreateComplianceRulesSlidePanel, renderComplianceRulesPanelContentcreateDonateTabPanel, createPatreonIconcreateDownloadManagerProgressSlot, updateDownloadManagerProgressSlot, setDownloadManagerProgressSlotVisible, updateDownloadManagerItemProgress, clearDownloadManagerItemProgress, updateDockFieldButtonensureIdbBlockedBanner, showConfirmDialog@grant GM_addStyle on this script or the app (styles are injected when this module loads).id match after Utilities.toKebabCase(...).