filebrowser ole :)

This commit is contained in:
lovebird 2026-02-20 19:37:39 +01:00
parent ed368d1f71
commit fa3304fe0b
4 changed files with 43 additions and 20 deletions

View File

@ -167,11 +167,6 @@ export const FileBrowserWidgetSchema = z.object({
viewMode: z.enum(['list', 'thumbs']).default('list'),
sortBy: z.enum(['name', 'ext', 'date', 'type']).default('name'),
showToolbar: z.boolean().default(true),
canChangeMount: z.boolean().default(true),
allowFileViewer: z.boolean().default(true),
allowLightbox: z.boolean().default(true),
allowDownload: z.boolean().default(true),
jail: z.boolean().default(false),
variables: WidgetVariablesSchema,
});

View File

@ -67,9 +67,11 @@ export const WidgetPropertiesForm: React.FC<WidgetPropertiesFormProps> = ({
}, [currentProps]);
const updateSetting = (key: string, value: any) => {
const newSettings = { ...settings, [key]: value };
setSettings(newSettings);
onSettingsChange(newSettings);
setSettings(prev => {
const newSettings = { ...prev, [key]: value };
onSettingsChange(newSettings);
return newSettings;
});
};
const renderField = (key: string, config: any) => {
@ -422,6 +424,8 @@ export const WidgetPropertiesForm: React.FC<WidgetPropertiesFormProps> = ({
setVfsPickerField(key);
setVfsPickerMountKey(mountKey);
setVfsPickerPathKey(pathKey);
setVfsBrowseMount(settings[mountKey] || config.defaultMount || 'home');
setVfsBrowsePath(settings[pathKey] || '/');
setVfsPickerOpen(true);
}}
>
@ -624,10 +628,6 @@ export const WidgetPropertiesForm: React.FC<WidgetPropertiesFormProps> = ({
{/* VFS Browse Dialog */}
<Dialog open={vfsPickerOpen} onOpenChange={(open) => {
if (!open) setVfsPickerOpen(false);
else {
setVfsBrowseMount(settings[vfsPickerMountKey] || 'home');
setVfsBrowsePath(settings[vfsPickerPathKey] || '/');
}
}}>
<DialogContent className="sm:max-w-2xl max-w-[95vw] p-0 gap-0">
<DialogHeader className="p-4 pb-2">
@ -638,12 +638,10 @@ export const WidgetPropertiesForm: React.FC<WidgetPropertiesFormProps> = ({
</DialogHeader>
<div style={{ height: 350, overflow: 'hidden' }}>
<FileBrowserWidget
key={vfsBrowseMount}
mount={vfsBrowseMount}
path={vfsBrowsePath}
onMountChange={(m: string) => {
setVfsBrowseMount(m);
setVfsBrowsePath('/');
}}
onMountChange={(m: string) => setVfsBrowseMount(m)}
onPathChange={(p: string) => setVfsBrowsePath(p)}
viewMode="list"
mode="simple"

View File

@ -504,6 +504,7 @@ export function registerAllWidgets() {
allowFileViewer: true,
allowLightbox: true,
allowDownload: true,
jail: false,
variables: {}
},
configSchema: {
@ -588,6 +589,12 @@ export function registerAllWidgets() {
label: 'Allow Download',
description: 'Show download button for files',
default: true
},
jail: {
type: 'boolean',
label: 'Jail Mode',
description: 'Prevent navigating above the configured mount and path',
default: false
}
},
minSize: { width: 300, height: 300 },

View File

@ -27,6 +27,7 @@ const FileBrowserWidget: React.FC<FileBrowserWidgetExtendedProps> = (props) => {
allowFileViewer = true,
allowLightbox = true,
allowDownload = true,
jail = false,
onPathChange,
onMountChange,
onSelect,
@ -43,10 +44,19 @@ const FileBrowserWidget: React.FC<FileBrowserWidgetExtendedProps> = (props) => {
const mount = onMountChange ? mountProp : internalMount;
const currentPath = isControlled ? pathProp : internalPath;
// Jail: normalize the root path for comparison
const jailRoot = pathProp.replace(/\/+$/, '') || '/';
const updatePath = useCallback((newPath: string) => {
// Jail guard: prevent navigating above the jail root
if (jail) {
const norm = newPath.replace(/\/+$/, '') || '/';
const root = pathProp.replace(/\/+$/, '') || '/';
if (root !== '/' && !norm.startsWith(root) && norm !== root) return;
}
if (isControlled) onPathChange!(newPath);
else setInternalPath(newPath);
}, [isControlled, onPathChange]);
}, [isControlled, onPathChange, jail, pathProp]);
const updateMount = useCallback((newMount: string) => {
if (onMountChange) onMountChange(newMount);
@ -140,7 +150,14 @@ const FileBrowserWidget: React.FC<FileBrowserWidgetExtendedProps> = (props) => {
// ── Sorted items ─────────────────────────────────────────────
const canGoUp = currentPath !== '/' && currentPath !== '';
const canGoUp = (() => {
if (currentPath === '/' || currentPath === '') return false;
if (jail) {
const normalized = currentPath.replace(/\/+$/, '') || '/';
return normalized !== jailRoot && normalized !== jailRoot.replace(/\/+$/, '');
}
return true;
})();
const sorted = useMemo(() => sortNodes(nodes, sortBy, sortAsc), [nodes, sortBy, sortAsc]);
const itemCount = sorted.length + (canGoUp ? 1 : 0);
const getNode = (idx: number): INode | null => {
@ -189,8 +206,14 @@ const FileBrowserWidget: React.FC<FileBrowserWidgetExtendedProps> = (props) => {
const crumbs = [{ label: '/', path: '/' }];
let acc = '';
for (const p of parts) { acc += (acc ? '/' : '') + p; crumbs.push({ label: p, path: acc }); }
// In jail mode, only show crumbs at or below the jail root
if (jail) {
const root = jailRoot === '/' ? '/' : jailRoot;
const rootParts = root === '/' ? 0 : root.split('/').filter(Boolean).length;
return crumbs.slice(rootParts);
}
return crumbs;
}, [currentPath, mount]);
}, [currentPath, mount, jail, jailRoot]);
// Return-to-sender focus
useEffect(() => {
@ -369,7 +392,7 @@ const FileBrowserWidget: React.FC<FileBrowserWidgetExtendedProps> = (props) => {
<FileBrowserToolbar
canGoUp={canGoUp}
goUp={goUp}
canChangeMount={canChangeMount}
canChangeMount={!jail && canChangeMount}
availableMounts={availableMounts}
mount={mount}
updateMount={updateMount}