Files
fnx_web/svelte/src/filesystem/Filesystem.svelte

198 lines
4.7 KiB
Svelte
Raw Normal View History

<script lang="ts">
import { onMount } from "svelte";
import EditWindow from "./edit_window/EditWindow.svelte";
import Toolbar from "./Toolbar.svelte";
import Breadcrumbs from "./Breadcrumbs.svelte";
import DetailsWindow from "./DetailsWindow.svelte";
import FilePreview from "./viewers/FilePreview.svelte";
import FSUploadWidget from "./upload_widget/FSUploadWidget.svelte";
2025-10-09 15:48:23 +02:00
import { fs_download, type FSPath } from "lib/FilesystemAPI";
import { FSNavigator } from "./FSNavigator"
import { css_from_path } from "filesystem/edit_window/Branding";
import AffiliatePrompt from "user_home/AffiliatePrompt.svelte";
2025-10-09 15:48:23 +02:00
import { current_page_store } from "wrap/RouterStore";
let file_preview: FilePreview
let toolbar: Toolbar
let upload_widget: FSUploadWidget
let details_visible = false
let edit_window: EditWindow
let edit_visible = false
const nav = new FSNavigator(true)
onMount(() => {
2025-10-09 15:48:23 +02:00
if ((window as any).intial_node !== undefined) {
console.debug("Loading initial node")
nav.open_node((window as any).initial_node as FSPath, false)
} else {
console.debug("No initial node, fetching path", window.location.pathname)
nav.navigate(decodeURI(window.location.pathname).replace(/^\/d/, ""), false)
}
const page_sub = current_page_store.subscribe(() => {
console.debug("Caught page transition to", window.location.pathname)
nav.navigate(decodeURI(window.location.pathname).replace(/^\/d/, ""), false)
})
// Subscribe to navigation updates. This function returns a deconstructor
// which we can conveniently return from our mount function as well
2025-10-09 15:48:23 +02:00
const nav_sub = nav.subscribe(nav => {
if (!nav.initialized) {
return
}
// Custom CSS rules for the whole viewer
2024-09-10 18:51:13 +02:00
document.documentElement.style = css_from_path(nav.path)
})
2025-10-09 15:48:23 +02:00
return () => {
page_sub()
nav_sub()
document.documentElement.style = ""
}
})
const keydown = (e: KeyboardEvent) => {
if (e.ctrlKey || e.altKey || e.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts
} else if ((document.activeElement as any).type !== undefined && (document.activeElement as any).type === "text") {
return // Prevent shortcuts from interfering with input fields
}
2024-03-12 17:53:53 +01:00
let action_performed = true
switch (e.key) {
2023-11-16 12:17:36 +01:00
case "c":
toolbar.copy_link()
break;
2020-11-26 11:13:27 +01:00
case "i":
details_visible = !details_visible
break;
case "e":
if (edit_visible) {
edit_visible = false
} else {
edit_window.edit(nav.base, true, "file")
}
break;
2020-11-26 11:13:27 +01:00
case "s":
fs_download(nav.base)
break;
case "r":
nav.shuffle = !nav.shuffle
break;
2025-02-03 16:29:26 +01:00
case "f": // F fullscreen
if (toolbar) {
toolbar.toggle_fullscreen()
}
break
case "a":
case "ArrowLeft":
nav.open_sibling(-1)
break;
case "d":
case "ArrowRight":
nav.open_sibling(1)
break;
case " ": // Spacebar pauses / unpauses video and audio playback
if (file_preview) {
if (file_preview.toggle_playback()) {
2025-02-03 16:29:26 +01:00
e.preventDefault()
e.stopPropagation()
}
}
break
2025-02-03 15:38:07 +01:00
case "m": // M mutes / unmutes audio
if (file_preview) {
file_preview.toggle_mute()
}
break
2024-08-14 19:58:25 +02:00
case "h":
file_preview.seek(-20)
break
case "j":
file_preview.seek(-5)
break
case "k":
file_preview.seek(5)
break
case "l":
file_preview.seek(20)
break
case ",":
file_preview.seek(-0.04) // Roughly a single frame.. assuming 25fps
break
case ".":
file_preview.seek(0.04)
break
2024-03-12 17:53:53 +01:00
default:
action_performed = false
}
2023-05-25 17:06:17 +02:00
2024-03-12 17:53:53 +01:00
if (action_performed) {
e.preventDefault()
}
};
</script>
<svelte:window on:keydown={keydown} />
2025-10-09 15:48:23 +02:00
<div class="filesystem">
<Breadcrumbs nav={nav}/>
2025-10-09 15:48:23 +02:00
<div class="file_preview">
<FilePreview
bind:this={file_preview}
nav={nav}
2025-10-09 15:48:23 +02:00
upload_widget={upload_widget}
edit_window={edit_window}
2025-10-09 15:48:23 +02:00
on:open_sibling={e => nav.open_sibling(e.detail)}
on:download={() => fs_download(nav.base)}
2025-10-09 15:48:23 +02:00
on:details={() => details_visible = !details_visible}
/>
</div>
2025-10-09 15:48:23 +02:00
<Toolbar
bind:this={toolbar}
nav={nav}
bind:details_visible={details_visible}
edit_window={edit_window}
bind:edit_visible={edit_visible}
on:download={() => fs_download(nav.base)}
/>
</div>
2025-10-09 15:48:23 +02:00
<DetailsWindow nav={nav} bind:visible={details_visible} />
2025-10-09 15:48:23 +02:00
<EditWindow nav={nav} bind:this={edit_window} bind:visible={edit_visible} />
2025-10-09 15:48:23 +02:00
<!-- This one is included at the highest level so uploads can keep running
even when the user navigates to a different directory -->
<FSUploadWidget nav={nav} bind:this={upload_widget} />
2025-03-21 01:11:03 +01:00
2025-10-09 15:48:23 +02:00
<AffiliatePrompt/>
<style>
:global(*) {
2024-11-14 16:14:58 +01:00
transition: background-color 0.2s,
border 0.2s,
border-top 0.2s,
border-right 0.2s,
border-bottom 0.2s,
border-left 0.2s,
color 0.2s;
}
/* Viewer container */
2025-10-09 15:48:23 +02:00
.filesystem {
display: flex;
flex-direction: column;
2025-10-09 15:48:23 +02:00
height: 100vh;
width: 100%;
}
2023-05-25 17:06:17 +02:00
.file_preview {
2025-10-09 15:48:23 +02:00
flex: 1 1 auto;
overflow: auto;
2023-05-25 17:06:17 +02:00
}
</style>