Skip to content

Commit bad1fb5

Browse files
author
FolderView Plus Test
committed
Implement theme workspace and folder defaults
1 parent 88fb5e3 commit bad1fb5

13 files changed

Lines changed: 1804 additions & 4 deletions

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/Folder.page

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ $folderEditorPageModeSource = 'modern-only';
2121
$folderEditorRequestedId = '';
2222
$folderEditorResolvedId = '';
2323
$folderEditorPageBuildVersion = readInstalledVersion();
24+
$folderEditorTypePrefs = [];
2425
$folderEditorCookieType = '';
2526
$folderEditorCookieId = '';
2627
$folderEditorBootstrapContext = [
@@ -44,6 +45,7 @@ try {
4445
}
4546
try {
4647
$folderEditorPageType = ensureType((string)($_GET['type'] ?? $_REQUEST['type'] ?? $folderEditorCookieType ?? ''));
48+
$folderEditorTypePrefs = readTypePrefs($folderEditorPageType);
4749
$folderEditorModePreference = resolveTypeFolderEditorModePreference($folderEditorPageType);
4850
$folderEditorPageMode = normalizeFolderEditorMode((string)($folderEditorModePreference['mode'] ?? 'modern'));
4951
$folderEditorPageModeSource = trim((string)($folderEditorModePreference['source'] ?? 'modern-only')) ?: 'modern-only';
@@ -69,6 +71,7 @@ try {
6971
$folderEditorPageModeSource = 'modern-only';
7072
$folderEditorRequestedId = '';
7173
$folderEditorResolvedId = '';
74+
$folderEditorTypePrefs = [];
7275
$folderEditorBootstrapContext = [
7376
'requestedId' => '',
7477
'resolvedId' => '',
@@ -85,6 +88,7 @@ try {
8588
window.FolderViewPlusFolderEditorRequestedId = <?php echo json_encode($folderEditorRequestedId, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
8689
window.FolderViewPlusFolderEditorResolvedId = <?php echo json_encode($folderEditorResolvedId, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
8790
window.FolderViewPlusFolderEditorBootstrapContext = <?php echo json_encode($folderEditorBootstrapContext, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
91+
window.FolderViewPlusFolderEditorTypePrefs = <?php echo json_encode($folderEditorTypePrefs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
8892
window.FolderViewPlusFolderEditorPageBuildVersion = <?php echo json_encode($folderEditorPageBuildVersion, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
8993
window.FolderViewPlusFolderEditorRuntimeLoaded = false;
9094
window.FolderViewPlusFolderEditorRuntimeBootStage = 'page-bootstrap';

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/FolderViewPlus.page

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,43 @@ if (!empty($fvplusRuntimeConflicts)) {
7979
<span id="update-check-status" class="status-line"></span>
8080
<span id="rollback-status" class="status-line"></span>
8181

82+
<div id="fv-theme-workspace-panel" class="fv-theme-workspace-panel">
83+
<div class="rules-panel fv-theme-workspace-shell">
84+
<div class="rules-header">
85+
<h3>Theme Workspace</h3>
86+
<span class="rules-help">Import GitHub CSS themes, activate one managed theme at a time, and layer FolderView Plus token overrides and custom CSS on top.</span>
87+
</div>
88+
<div class="fv-theme-workspace-body">
89+
<div class="fv-theme-import-row">
90+
<input id="fv-theme-github-source" type="text" placeholder="owner/repo, owner/repo/tree/branch, or GitHub raw CSS URL">
91+
<button type="button" onclick="importThemeWorkspaceGithub()"><i class="fa fa-github"></i> Import from GitHub</button>
92+
<button type="button" onclick="checkThemeWorkspaceUpdates()"><i class="fa fa-refresh"></i> Check updates</button>
93+
</div>
94+
<div id="fv-theme-workspace-status" class="fv-theme-workspace-status">Theme workspace idle.</div>
95+
<div id="fv-theme-workspace-list" class="fv-theme-workspace-list">
96+
<div class="fv-theme-empty-state">
97+
<strong>No managed themes imported yet.</strong>
98+
<span>Import a GitHub CSS theme and it will be written into the existing custom asset pipeline.</span>
99+
</div>
100+
</div>
101+
<div class="fv-theme-customize-grid">
102+
<div class="fv-theme-customize-card">
103+
<div class="settings-mini-title">Token overrides</div>
104+
<div id="fv-theme-variable-grid" class="fv-theme-variable-grid"></div>
105+
</div>
106+
<div class="fv-theme-customize-card">
107+
<div class="settings-mini-title">Custom CSS</div>
108+
<textarea id="fv-theme-custom-css" class="fv-theme-custom-css" spellcheck="false" placeholder="/* Appended after the active managed theme for Docker, VMs, and Dashboard. */"></textarea>
109+
</div>
110+
</div>
111+
<div class="fv-theme-customize-actions">
112+
<button type="button" onclick="saveThemeWorkspaceCustomize()"><i class="fa fa-save"></i> Save customization layer</button>
113+
<button type="button" onclick="deactivateThemeWorkspaceTheme()"><i class="fa fa-ban"></i> Disable managed theme</button>
114+
</div>
115+
</div>
116+
</div>
117+
</div>
118+
82119

83120
<h2 data-i18n="docker" data-fv-section="docker">Docker</h2>
84121
<div class="folder-table">
@@ -195,6 +232,20 @@ if (!empty($fvplusRuntimeConflicts)) {
195232
</label>
196233
<div class="setting-help">Use performance mode + lazy previews for larger libraries to reduce render overhead.</div>
197234
</div>
235+
<div class="settings-mini-card fv-folder-defaults-card">
236+
<div class="settings-mini-title">Folder defaults</div>
237+
<label class="setting-select" for="docker-folder-defaults-source">
238+
<span>Source folder</span>
239+
<select id="docker-folder-defaults-source"></select>
240+
</label>
241+
<div id="docker-folder-defaults-summary" class="setting-help">No Docker folder defaults saved yet.</div>
242+
<div class="fv-folder-defaults-actions">
243+
<button type="button" onclick="saveFolderDefaultsFromSelection('docker')"><i class="fa fa-bookmark"></i> Save as defaults</button>
244+
<button type="button" onclick="applySavedFolderDefaultsToAll('docker')"><i class="fa fa-repeat"></i> Apply to all</button>
245+
<button type="button" onclick="clearFolderDefaults('docker')"><i class="fa fa-eraser"></i> Clear</button>
246+
</div>
247+
<div class="setting-note">New folders inherit this saved profile when the folder editor opens in create mode.</div>
248+
</div>
198249
<div class="settings-mini-card">
199250
<div class="settings-mini-title">Dashboard</div>
200251
<label class="setting-select" for="docker-dashboard-layout">
@@ -432,6 +483,20 @@ if (!empty($fvplusRuntimeConflicts)) {
432483
</label>
433484
<div class="setting-help">Use performance mode + lazy previews for larger libraries to reduce render overhead.</div>
434485
</div>
486+
<div class="settings-mini-card fv-folder-defaults-card">
487+
<div class="settings-mini-title">Folder defaults</div>
488+
<label class="setting-select" for="vm-folder-defaults-source">
489+
<span>Source folder</span>
490+
<select id="vm-folder-defaults-source"></select>
491+
</label>
492+
<div id="vm-folder-defaults-summary" class="setting-help">No VM folder defaults saved yet.</div>
493+
<div class="fv-folder-defaults-actions">
494+
<button type="button" onclick="saveFolderDefaultsFromSelection('vm')"><i class="fa fa-bookmark"></i> Save as defaults</button>
495+
<button type="button" onclick="applySavedFolderDefaultsToAll('vm')"><i class="fa fa-repeat"></i> Apply to all</button>
496+
<button type="button" onclick="clearFolderDefaults('vm')"><i class="fa fa-eraser"></i> Clear</button>
497+
</div>
498+
<div class="setting-note">New folders inherit this saved profile when the folder editor opens in create mode.</div>
499+
</div>
435500
<div class="settings-mini-card">
436501
<div class="settings-mini-title">Dashboard</div>
437502
<label class="setting-select" for="vm-dashboard-layout">
@@ -1397,6 +1462,8 @@ if (!empty($fvplusRuntimeConflicts)) {
13971462
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.utils.js')?>"></script>
13981463
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.request.js')?>"></script>
13991464
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.theme-resolver.js')?>"></script>
1465+
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folder.settings-transfer.js')?>"></script>
1466+
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.theme-workspace.js')?>"></script>
14001467
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.chrome.js')?>"></script>
14011468
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.dirty.js')?>"></script>
14021469
<script src="<?=fvplus_asset('/plugins/folderview.plus/scripts/folderviewplus.runtime-parity.js')?>"></script>

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ const folderThemeSurfaceBinding = bindFolderThemeAwareSurface
217217
})
218218
: null;
219219
const utils = window.FolderViewPlusUtils || null;
220+
const folderEditorTypePrefs = window.FolderViewPlusFolderEditorTypePrefs
221+
&& typeof window.FolderViewPlusFolderEditorTypePrefs === 'object'
222+
? window.FolderViewPlusFolderEditorTypePrefs
223+
: {};
220224
const folderEditorRulesModule = window.FolderViewPlusFolderEditorRules || null;
221225
const folderSettingsTransferModule = window.FolderViewPlusFolderSettingsTransfer || null;
222226
const bulkAssignmentSharedModule = window.FolderViewPlusBulkAssignmentShared || null;
@@ -3316,8 +3320,10 @@ const startFolderEditorRuntime = async () => {
33163320
});
33173321
clearEditorNavigationPrefill();
33183322
folderHierarchyState.currentFolderDescendantIds = new Set();
3319-
refreshParentFolderChooser(folders, '', new Set());
3320-
setParentDefaultsNote('Select a parent to inherit preview/icon defaults automatically.', 'info');
3323+
if (!applySavedFolderDefaultsToNewFolder(folders)) {
3324+
refreshParentFolderChooser(folders, '', new Set());
3325+
setParentDefaultsNote('Select a parent to inherit preview/icon defaults automatically.', 'info');
3326+
}
33213327
}
33223328
renderMemberBulkMoveTargets();
33233329
updateMemberBulkMoveUi();
@@ -4037,6 +4043,55 @@ const suggestSiblingName = (...args) => getFolderHierarchyApi().suggestSiblingNa
40374043
const setParentDefaultsNote = (...args) => getFolderHierarchyApi().setParentDefaultsNote(...args);
40384044
const applySmartDefaultsFromParent = (...args) => getFolderHierarchyApi().applySmartDefaultsFromParent(...args);
40394045
const markSmartDefaultFieldTouched = (...args) => getFolderHierarchyApi().markSmartDefaultFieldTouched(...args);
4046+
const getSavedFolderDefaultsProfile = () => {
4047+
const normalizedPrefs = typeof utils?.normalizePrefs === 'function'
4048+
? utils.normalizePrefs(folderEditorTypePrefs || {})
4049+
: (folderEditorTypePrefs && typeof folderEditorTypePrefs === 'object' ? folderEditorTypePrefs : {});
4050+
const folderDefaults = normalizedPrefs?.folderDefaults && typeof normalizedPrefs.folderDefaults === 'object'
4051+
? normalizedPrefs.folderDefaults
4052+
: {};
4053+
const profile = folderDefaults.profile && typeof folderDefaults.profile === 'object'
4054+
? folderDefaults.profile
4055+
: {};
4056+
const icon = String(profile.icon || '').trim();
4057+
const settings = profile.settings && typeof profile.settings === 'object'
4058+
? JSON.parse(JSON.stringify(profile.settings))
4059+
: {};
4060+
const actions = Array.isArray(profile.actions)
4061+
? JSON.parse(JSON.stringify(profile.actions))
4062+
: [];
4063+
if (!icon && Object.keys(settings).length <= 0 && actions.length <= 0) {
4064+
return null;
4065+
}
4066+
return {
4067+
sourceId: String(folderDefaults.sourceId || '').trim(),
4068+
sourceName: String(folderDefaults.sourceName || '').trim(),
4069+
folder: {
4070+
name: '',
4071+
parentId: '',
4072+
icon,
4073+
regex: '',
4074+
containers: [],
4075+
settings,
4076+
actions
4077+
}
4078+
};
4079+
};
4080+
const applySavedFolderDefaultsToNewFolder = (foldersMap = {}) => {
4081+
if (String(activeFolderEditorFolderId || folderId || '').trim()) {
4082+
return false;
4083+
}
4084+
const savedDefaults = getSavedFolderDefaultsProfile();
4085+
if (!savedDefaults) {
4086+
return false;
4087+
}
4088+
selected = [];
4089+
selectedRegex = [];
4090+
hydrateCurrentEditFolder(savedDefaults.folder, '', foldersMap, { clearPrefill: false });
4091+
const sourceLabel = savedDefaults.sourceName || savedDefaults.sourceId || 'saved profile';
4092+
setParentDefaultsNote(`Loaded saved defaults from "${sourceLabel}".`, 'info');
4093+
return true;
4094+
};
40404095
const getFolderEditorParentPickerApi = (() => {
40414096
let cachedApi = null;
40424097
return () => {

0 commit comments

Comments
 (0)