diff --git a/control/frontend/package-lock.json b/control/frontend/package-lock.json index 996cebd..7bad98d 100644 --- a/control/frontend/package-lock.json +++ b/control/frontend/package-lock.json @@ -1155,9 +1155,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.46.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.46.4.tgz", - "integrity": "sha512-J1fd80WokLzIm6EAV7z7C2+/C02qVAX645LZomARARTRJkbbJSY1Jln3wtBZYibUB8c9/5Z6xqLAV39VdbtWCQ==", + "version": "2.47.3", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.47.3.tgz", + "integrity": "sha512-zN2yzBc2dIES2BSzLhNP2weYhwB77kgM/oAktICZVmmljyEmPZrlUwr14jjdK9/eDu7WdAuf6gTdYIJLTcN3Fw==", "dev": true, "license": "MIT", "peer": true, @@ -4018,9 +4018,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", - "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "peer": true, diff --git a/control/frontend/src/app.css b/control/frontend/src/app.css index e4cc2b8..e14c6c0 100644 --- a/control/frontend/src/app.css +++ b/control/frontend/src/app.css @@ -6,7 +6,7 @@ @theme { --color-stone-150: oklch(0.9465 0.002 77.571); --color-stone-250: oklch(0.896 0.004 52.542); - --color-stone-350: oklch(0.711 0.009 57.218); + --color-stone-350: oklch(0.789 0.0075 56.313); --color-stone-450: oklch(0.631 0.0115 57.165); --color-stone-550: oklch(0.4985 0.012 65.855); --color-stone-650: oklch(0.409 0.0105 70.599); diff --git a/control/frontend/src/components/Button.svelte b/control/frontend/src/components/Button.svelte index 6b706d7..9787035 100644 --- a/control/frontend/src/components/Button.svelte +++ b/control/frontend/src/components/Button.svelte @@ -13,6 +13,8 @@ title = '', click_function = (e: MouseEvent) => {}, menu_options = null, + menu_class = 'right-0', + div_class = '', children } = $props<{ className?: string; @@ -23,6 +25,8 @@ title?: string; click_function?: (e: MouseEvent) => void; menu_options?: MenuOption[] | null; + menu_class?: string; + div_class?: string, children?: any; }>(); @@ -126,7 +130,7 @@ } -
+
{/if} -
+
{option.name}
diff --git a/control/frontend/src/components/DisplayGroupObject.svelte b/control/frontend/src/components/DisplayGroupObject.svelte index 95a91b3..16455e1 100644 --- a/control/frontend/src/components/DisplayGroupObject.svelte +++ b/control/frontend/src/components/DisplayGroupObject.svelte @@ -7,16 +7,14 @@ import { add_empty_display_group, all_displays_of_group_selected, - is_selected, remove_empty_display_groups, - select, select_all_of_group, - selected_display_ids, set_new_display_group_data } from '../ts/stores/displays'; import DNDGrip from './DNDGrip.svelte'; import { fade } from 'svelte/transition'; import type { DisplayGroup } from '../ts/types'; + import { selected_display_ids } from '../ts/stores/select'; let { display_group } = $props<{ display_group: DisplayGroup; diff --git a/control/frontend/src/components/DisplayObject.svelte b/control/frontend/src/components/DisplayObject.svelte index 3e8cdd9..67071ab 100644 --- a/control/frontend/src/components/DisplayObject.svelte +++ b/control/frontend/src/components/DisplayObject.svelte @@ -1,16 +1,15 @@ - -
-
-
- -
-
- Tolle - Dateifjisodfhsuidfhdufighdfiughdfuighdfuihguifdghiudfhguidfgdfijghdfjkghdfkjghdfkjghdfkughdfjghdfkjghdfkjghfdkghdfkjghdfkghdfkghdfkjghdfkghdfkjghdfgkhdfkghfdkjghdfkghfdkghdfkjghdfjkghdk.zip -
-
-
- -
25.09.25
-
-
MPEG
-
-
523 MB
-
-
diff --git a/control/frontend/src/components/FolderElementObject.svelte b/control/frontend/src/components/FolderElementObject.svelte new file mode 100644 index 0000000..07eb828 --- /dev/null +++ b/control/frontend/src/components/FolderElementObject.svelte @@ -0,0 +1,223 @@ + + +
+
+ +
+
{ + if (e.key === 'Enter' || e.key === ' ') onclick(e); + }} + {onclick} + class="{get_selectable_color_classes(is_selected(file.id, $selected_file_ids), { + bg: true, + hover: true, + active: true, + text: true + })} rounded-r-lg transition-colors duration-200 gap-4 flex flex-row justify-between cursor-pointer group w-full h-full min-w-0" + > +
+
+ {#if is_folder} + + {:else if file.thumbnail} +
+ {:else if get_file_type(file)?.icon} + {@const Icon = get_file_type(file)?.icon} + + {/if} +
+
+ {file.name.includes('.') ? file.name.slice(0, file.name.lastIndexOf('.')) : file.name} +
+
+
+ {#if get_display_ids_where_file_is_missing($current_file_path, file, $selected_display_ids, $all_files)[1].length !== 0} + + {:else if get_display_ids_where_file_is_missing($current_file_path, file, $selected_display_ids, $all_files)[0].length !== 0} + + {/if} +
+ {get_created_string(file.date_created)} +
+
+
+ {is_folder ? 'Ordner' : (get_file_type(file)?.display_name ?? '?')} +
+
+
+ {file.size} +
+
+
+
diff --git a/control/frontend/src/components/PathBar.svelte b/control/frontend/src/components/PathBar.svelte new file mode 100644 index 0000000..02c3be5 --- /dev/null +++ b/control/frontend/src/components/PathBar.svelte @@ -0,0 +1,160 @@ + + +
+
+
+
+ +
+ {#if cut_folders !== 0} + + {/if} +
+
+ {#each get_sliced_folders($current_file_path, cut_folders) as folder, i (i)} +
+ +
+ {/each} +
+
+
diff --git a/control/frontend/src/components/RefreshPlay.svelte b/control/frontend/src/components/RefreshPlay.svelte new file mode 100644 index 0000000..c4a36a9 --- /dev/null +++ b/control/frontend/src/components/RefreshPlay.svelte @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/control/frontend/src/routes/+page.svelte b/control/frontend/src/routes/+page.svelte index ce55fb6..e1e4d47 100644 --- a/control/frontend/src/routes/+page.svelte +++ b/control/frontend/src/routes/+page.svelte @@ -7,10 +7,12 @@ ClipboardPaste, Download, FolderOutput, + FolderPlus, Info, Keyboard, Menu, Minus, + Pen, Pencil, PinOff, Plus, @@ -33,13 +35,13 @@ import Button from '../components/Button.svelte'; import SplashScreen from './../../../../shared/splash_screen.html?raw'; import { - change_display_screen_height, - display_screen_height, + change_height, + current_height, dnd_flip_duration_ms, get_selectable_color_classes, is_display_drag, is_group_drag, - next_step_possible, + next_height_step_size, pinned_display_id } from '../ts/stores/ui_behavior'; import { dragHandleZone } from 'svelte-dnd-action'; @@ -47,9 +49,7 @@ all_displays_of_group_selected, displays, get_display_by_id, - is_selected, - select_all_of_group, - selected_display_ids + select_all_of_group } from '../ts/stores/displays'; import { cubicOut } from 'svelte/easing'; import { flip } from 'svelte/animate'; @@ -58,7 +58,11 @@ import OnlineState from '../components/OnlineState.svelte'; import type { DisplayGroup } from '../ts/types'; import PopUp from '../components/PopUp.svelte'; - import FileObject from '../components/FileObject.svelte'; + import FileObject from '../components/FolderElementObject.svelte'; + import FolderElementObject from '../components/FolderElementObject.svelte'; + import PathBar from '../components/PathBar.svelte'; + import { all_files, current_file_path, get_current_folder_elements } from '../ts/stores/files'; + import { selected_display_ids, selected_file_ids } from '../ts/stores/select'; let displays_scroll_box: HTMLElement; @@ -93,17 +97,17 @@ -
+
- PLG MuDiCS -
-
+
{#if $pinned_display_id}
@@ -201,9 +205,9 @@ title="Bildschirme größer darstellen" className="aspect-square !p-1 rounded-r-none" bg="bg-stone-600" - disabled={next_step_possible($display_screen_height, 1)} + disabled={!Boolean(next_height_step_size('display', $current_height, 1))} click_function={() => { - change_display_screen_height(1); + change_height('display', 1); }} > @@ -212,9 +216,9 @@ title="Bildschirme kleiner darstellen" className="aspect-square !p-1 rounded-l-none" bg="bg-stone-600" - disabled={next_step_possible($display_screen_height, -1)} + disabled={!Boolean(next_height_step_size('display', $current_height, -1))} click_function={() => { - change_display_screen_height(-1); + change_height('display', -1); }} > @@ -256,7 +260,7 @@
-
+
Dateien anzeigen und verwalten @@ -317,9 +321,9 @@ title="Dateien größer darstellen" className="aspect-square !p-1 rounded-r-none" bg="bg-stone-600" - disabled={next_step_possible($display_screen_height, 1)} + disabled={!Boolean(next_height_step_size('file', $current_height, 1))} click_function={() => { - change_display_screen_height(1); + change_height('file', 1); }} > @@ -328,9 +332,9 @@ title="Dateien kleiner darstellen" className="aspect-square !p-1 rounded-l-none" bg="bg-stone-600" - disabled={next_step_possible($display_screen_height, -1)} + disabled={!Boolean(next_height_step_size('file', $current_height, -1))} click_function={() => { - change_display_screen_height(-1); + change_height('file', -1); }} > @@ -338,35 +342,62 @@
-
-
- - - -
-
- - -
- - -
- +
+ +
+
+ +
+ + +
+ +
+
+ + +
+ + +
-
- +
+
+ {#each get_current_folder_elements($all_files, $current_file_path, $selected_display_ids) as folder_element (folder_element.id)} +
+ +
+ {/each} +
-
diff --git a/control/frontend/src/routes/test/+page.svelte b/control/frontend/src/routes/test/+page.svelte index 7091730..ed67673 100644 --- a/control/frontend/src/routes/test/+page.svelte +++ b/control/frontend/src/routes/test/+page.svelte @@ -1,35 +1,47 @@ - -
-

Ich bin das Haupt-Div

- -
(isHoveringChild = true)} - on:mouseleave={() => (isHoveringChild = false)} - on:click={handleClickChild} - > - Ich bin das Kind-Div (no-display-selectable) -
- -

Noch mehr Inhalt...

-
+
+
+ PLG MuDiCS + +
+
+
+
+
+
+ + Dateien anzeigen und verwalten + +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
diff --git a/control/frontend/src/tailwind.config.js b/control/frontend/src/tailwind.config.js index 87465a6..be77b68 100644 --- a/control/frontend/src/tailwind.config.js +++ b/control/frontend/src/tailwind.config.js @@ -61,13 +61,134 @@ module.exports = { 'active:bg-stone-900', 'active:bg-stone-950', - 'h-5', - 'h-10', + 'text-stone-50', + 'text-stone-150', + 'text-stone-200', + 'text-stone-250', + 'text-stone-300', + 'text-stone-350', + 'text-stone-100', + 'text-stone-400', + 'text-stone-450', + 'text-stone-500', + 'text-stone-550', + 'text-stone-600', + 'text-stone-650', + 'text-stone-700', + 'text-stone-750', + 'text-stone-800', + 'text-stone-850', + 'text-stone-900', + 'text-stone-950', + + 'group-hover:text-stone-100', + 'group-hover:text-stone-150', + 'group-hover:text-stone-200', + 'group-hover:text-stone-50', + 'group-hover:text-stone-250', + 'group-hover:text-stone-300', + 'group-hover:text-stone-350', + 'group-hover:text-stone-400', + 'group-hover:text-stone-450', + 'group-hover:text-stone-500', + 'group-hover:text-stone-550', + 'group-hover:text-stone-600', + 'group-hover:text-stone-650', + 'group-hover:text-stone-700', + 'group-hover:text-stone-750', + 'group-hover:text-stone-800', + 'group-hover:text-stone-850', + 'group-hover:text-stone-900', + 'group-hover:text-stone-950', + + 'group-active:text-stone-50', + 'group-active:text-stone-100', + 'group-active:text-stone-150', + 'group-active:text-stone-200', + 'group-active:text-stone-250', + 'group-active:text-stone-300', + 'group-active:text-stone-350', + 'group-active:text-stone-400', + 'group-active:text-stone-450', + 'group-active:text-stone-500', + 'group-active:text-stone-550', + 'group-active:text-stone-600', + 'group-active:text-stone-650', + 'group-active:text-stone-700', + 'group-active:text-stone-750', + 'group-active:text-stone-800', + 'group-active:text-stone-850', + 'group-active:text-stone-950', + 'group-active:text-stone-900', + + 'border-stone-50', + 'border-stone-100', + 'border-stone-150', + 'border-stone-200', + 'border-stone-250', + 'border-stone-300', + 'border-stone-350', + 'border-stone-400', + 'border-stone-450', + 'border-stone-500', + 'border-stone-550', + 'border-stone-600', + 'border-stone-650', + 'border-stone-700', + 'border-stone-750', + 'border-stone-800', + 'border-stone-850', + 'border-stone-900', + 'border-stone-950', + + 'group-hover:border-stone-50', + 'group-hover:border-stone-100', + 'group-hover:border-stone-150', + 'group-hover:border-stone-200', + 'group-hover:border-stone-250', + 'group-hover:border-stone-300', + 'group-hover:border-stone-350', + 'group-hover:border-stone-400', + 'group-hover:border-stone-450', + 'group-hover:border-stone-500', + 'group-hover:border-stone-550', + 'group-hover:border-stone-600', + 'group-hover:border-stone-650', + 'group-hover:border-stone-700', + 'group-hover:border-stone-750', + 'group-hover:border-stone-800', + 'group-hover:border-stone-850', + 'group-hover:border-stone-950', + 'group-hover:border-stone-900', + + 'group-active:border-stone-50', + 'group-active:border-stone-100', + 'group-active:border-stone-150', + 'group-active:border-stone-200', + 'group-active:border-stone-250', + 'group-active:border-stone-300', + 'group-active:border-stone-350', + 'group-active:border-stone-400', + 'group-active:border-stone-450', + 'group-active:border-stone-500', + 'group-active:border-stone-550', + 'group-active:border-stone-600', + 'group-active:border-stone-650', + 'group-active:border-stone-700', + 'group-active:border-stone-750', + 'group-active:border-stone-800', + 'group-active:border-stone-850', + 'group-active:border-stone-900', + 'group-active:border-stone-950', + 'h-15', 'h-20', 'h-25', 'h-30', 'h-35', 'h-40', + + 'right-0', + 'left-0', ], } diff --git a/control/frontend/src/ts/api_handler.ts b/control/frontend/src/ts/api_handler.ts new file mode 100644 index 0000000..b49be87 --- /dev/null +++ b/control/frontend/src/ts/api_handler.ts @@ -0,0 +1,25 @@ + + + +export async function get_file_data(ip: string, path: string = './') { + const options = { + method: 'PATCH', + headers: { 'content-type': 'application/json' }, + body: `{"command":"cd ${path} && find . -maxdepth 1 -mindepth 1 -print0 | while IFS= read -r -d \'\' f; do\n typ=$(file -b --mime-type -- \"$f\")\n size=$(stat -c \'%s\' -- \"$f\")\n created=$(stat -c \'%w\' -- \"$f\")\n [ \"$created\" = \"-\" ] && created=$(stat -c \'%y\' -- \"$f\")\n jq -n --arg name \"$f\" --arg type \"$typ\" --arg size \"$size\" --arg created \"$created\" \'{name:$name, type:$type, size:($size|tonumber), created:$created}\' | tr -d \'\\n\'\n echo\n done\n"}` + }; + console.log(request(ip, '/shellCommand', options)); +} + + + + +async function request(ip: string, api_route: string, options: { method: string, headers?: Record, body?: string }) { + try { + const url = `http://${ip}:1323/api${api_route}`; + const response = await fetch(url, options); + const data = await response.json(); + return data; + } catch (error) { + console.error(error); + } +} \ No newline at end of file diff --git a/control/frontend/src/ts/stores/displays.ts b/control/frontend/src/ts/stores/displays.ts index 99b32db..a43c440 100644 --- a/control/frontend/src/ts/stores/displays.ts +++ b/control/frontend/src/ts/stores/displays.ts @@ -1,14 +1,12 @@ import { get, writable, type Writable } from "svelte/store"; +import type { Display, DisplayGroup } from "../types"; +import { is_selected, select, selected_display_ids } from "./select"; export const displays: Writable = writable([{ id: crypto.randomUUID(), data: [] }]); -export const selected_display_ids: Writable = writable([]); - - -add_testing_displays(); function add_display(ip: string, mac: string, name: string, status: string) { displays.update((displays: DisplayGroup[]) => { @@ -17,24 +15,6 @@ function add_display(ip: string, mac: string, name: string, status: string) { }); } -export function select(display_id: string, new_value: boolean | null = null) { - selected_display_ids.update((all_ids: string[]) => { - if (all_ids.includes(display_id)) { - const index = all_ids.indexOf(display_id); - if (index > -1 && new_value !== true) { - all_ids.splice(index, 1); - } - } else if (new_value !== false) { - all_ids.push(display_id); - } - return all_ids; - }); -} - -export function is_selected(display_id: string, current_selected_display_ids: string[]): boolean { - return current_selected_display_ids.includes(display_id); -} - export function all_displays_of_group_selected(display_group: DisplayGroup, current_selected_displays: string[]) { if (display_group.data.length === 0) return false; for (const display of display_group.data) { @@ -47,7 +27,7 @@ export function all_displays_of_group_selected(display_group: DisplayGroup, curr export function select_all_of_group(display_group: DisplayGroup, new_value: boolean | null = null) { for (const display of display_group.data) { - select(display.id, new_value); + select(selected_display_ids, display.id, new_value); } } @@ -99,10 +79,12 @@ export function remove_empty_display_groups() { - +add_testing_displays(); function add_testing_displays() { - const names = ["Vorne Rechts", "Vorne Links", "Vorne Mitte", "Fernseher Rechts", "Fernseher Bühne", "UIUIUIUIUIUIUISEHRLANGERTEXT DER IST WIRKLICH LANG, DER TEXT, so lang, dass er wirklich nirgendswo hinpasst, nichtmal da oben /\\"]; - for (const name of names) { - add_display("192.168.1.42", "00:1A:2B:3C:4D:5E", name, "Offline"); - } + // const names = ["Vorne Rechts", "Vorne Links", "Vorne Mitte", "Fernseher Rechts", "Fernseher Bühne", "UIUIUIUIUIUIUISEHRLANGERTEXT DER IST WIRKLICH LANG, DER TEXT, so lang, dass er wirklich nirgendswo hinpasst, nichtmal da oben /\\"]; + // for (const name of names) { + // add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", name, "Offline"); + // } + + add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", "Test", "Offline") } \ No newline at end of file diff --git a/control/frontend/src/ts/stores/files.ts b/control/frontend/src/ts/stores/files.ts new file mode 100644 index 0000000..8df0c89 --- /dev/null +++ b/control/frontend/src/ts/stores/files.ts @@ -0,0 +1,239 @@ +import { get, writable, type Writable } from "svelte/store"; +import type { FolderElement } from "../types"; +import { displays } from "./displays"; +import { selected_display_ids, selected_file_ids } from "./select"; +import { get_file_data } from "../api_handler"; + +export const all_files: Writable>> = writable>>({}); +// { +// path: { +// display_id: FolderElement[] +// ... +// }, +// path2: { +// display_id: FolderElement[] +// ... +// }, +// ... +// } + +export const current_file_path: Writable = writable('/'); + + +export function change_file_path(new_path: string) { + current_file_path.update(() => { + return new_path; + }); + selected_file_ids.update(() => { + return []; + }) +} + +export function get_display_ids_where_file_is_missing(path: string, file: FolderElement, selected_display_ids: string[], all_files: Record>): string[][] { + if (!all_files.hasOwnProperty(path)) return []; + const missing: string[] = []; + const colliding: string[] = []; + Display: + for (const selected_display_id of selected_display_ids) { + if (!all_files[path].hasOwnProperty(selected_display_id)) { + missing.push(selected_display_id); + continue; + } + for (const folder_element of all_files[path][selected_display_id]) { + if (folder_element.name === file.name) { + if (folder_element.hash !== file.hash) { + colliding.push(selected_display_id); + } + continue Display; + } + } + missing.push(selected_display_id); + } + return [missing, colliding]; +} + + +export function updates_files_on_display(display_id: string, new_folder_elements: FolderElement[], file_path: string) { + all_files.update((files) => { + if (!files.hasOwnProperty(file_path)) { + files[file_path] = {}; + } + for (const new_folder_element of new_folder_elements) { + new_folder_element.id = crypto.randomUUID(); + } + files[file_path][display_id] = new_folder_elements; + return files; + }); +} + +function get_files_on_all_displays() { + for (const display_group of get(displays)) { + for (const display of display_group.data) { + get_file_data(display.ip) + } + } +} + + +export function get_current_folder_elements(all_files: Record>, current_file_path: string, selected_display_ids: string[]) { + if (!all_files.hasOwnProperty(current_file_path)) { + get_files_on_all_displays(); + return []; + } + const files_on_display_array = all_files[current_file_path]; + const files: FolderElement[] = []; + for (const key of Object.keys(files_on_display_array)) { + if (selected_display_ids.includes(key)) { + FileOnDisplay: + for (const file_on_display of files_on_display_array[key]) { + for (const existing_file of files) { + if (file_on_display.name === existing_file.name) { + if (file_on_display.hash === existing_file.hash) { + continue FileOnDisplay; + } + } + } + files.push(file_on_display); + } + } + } + + return sort_files(files); +} + +function sort_files(files: FolderElement[]) { + files.sort((a, b) => { + const isDirA = a.type === 'inode/directory'; + const isDirB = b.type === 'inode/directory'; + + // Ordner zuerst + if (isDirA && !isDirB) return -1; + if (!isDirA && isDirB) return 1; + + // Danach alphabetisch nach name (case-insensitive) + const nameCompare = a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }); + if (nameCompare !== 0) return nameCompare; + + // Wenn name gleich, absteigend nach date_created + return b.date_created.getTime() - a.date_created.getTime(); + }); + return files; +} + + + + + + +add_test_files(); +export function add_test_files() { + // updates_files_on_display(get(displays)[0].data[0].id, [ + // { + // hash: "ok", + // thumbnail: null, + // name: "ok.png", + // type: "image/png", + // date_created: new Date("2022-09-10"), + // size: "32 KB" + // }, + // { + // hash: "schön", + // thumbnail: null, + // name: "schön.png", + // type: "image/png", + // date_created: new Date("2023-09-10"), + // size: "2 GB" + // }, + // { + // hash: "lang", + // thumbnail: null, + // name: "langer Bildname der wirklich gar nicht mehr aufhört, sodass man mal gut testen kann, wie die UI darauf reagiert.odp", + // type: "application/vnd.oasis.opendocument.presentation", + // date_created: new Date("2028-09-10"), + // size: "324 KB" + // }, + // { + // hash: "Schön hier aber waren Sie mal in Baden Würtemberg?", + // thumbnail: null, + // name: "Schön hier aber waren Sie mal in Baden Würtemberg?.mp4", + // type: "video/mp4", + // date_created: new Date("2024-09-10"), + // size: "32 KB" + // }, + // { + // hash: "lan4234g", + // thumbnail: null, + // name: "Ein schöner Ordner", + // type: 'inode/directory', + // date_created: new Date("2025-01-02"), + // size: "324 TB" + // }, + // { + // hash: "Schön hi23424er aber waren Sie mal in Baden Würtemberg?", + // thumbnail: null, + // name: "Ein hässlicher Ordner", + // type: 'inode/directory', + // date_created: new Date("2025-10-22"), + // size: "1 B" + // }, + // ], '/'); + + + // updates_files_on_display(get(displays)[0].data[0].id, [ + // { + // hash: "ok", + // thumbnail: null, + // name: "ok.png", + // type: "image/png", + // date_created: new Date("2022-09-10"), + // size: "32 KB" + // }, + // { + // hash: "schön", + // thumbnail: null, + // name: "schön.png", + // type: "image/png", + // date_created: new Date("2023-09-10"), + // size: "2 GB" + // }, + // ], '/Ein hässlicher Ordner/'); + + // updates_files_on_display(get(displays)[0].data[0].id, [ + // { + // hash: "nö", + // thumbnail: null, + // name: "nö.png", + // type: "image/png", + // date_created: new Date("2022-09-10"), + // size: "32 KB" + // }, + // { + // hash: "na gut", + // thumbnail: null, + // name: "na gut.png", + // type: "image/png", + // date_created: new Date("2023-09-10"), + // size: "2 GB" + // }, + // ], '/Ein schöner Ordner/'); + + + + // updates_files_on_display(get(displays)[0].data[1].id, [ + // { + // hash: "okk", + // thumbnail: null, + // name: "ok.png", + // type: "image/png", + // date_created: new Date("2022-09-10"), + // size: "32 KB" + // }, + // { + // hash: "schön", + // thumbnail: null, + // name: "schön.png", + // type: "image/png", + // date_created: new Date("2023-09-10"), + // size: "2 GB" + // },], '/'); +} diff --git a/control/frontend/src/ts/stores/select.ts b/control/frontend/src/ts/stores/select.ts new file mode 100644 index 0000000..31d36fe --- /dev/null +++ b/control/frontend/src/ts/stores/select.ts @@ -0,0 +1,23 @@ +import { writable, type Writable } from "svelte/store"; + +export const selected_file_ids: Writable = writable([]); +export const selected_display_ids: Writable = writable([]); + + +export function select(selected_ids: Writable, id: string, new_value: boolean | null = null) { + selected_ids.update((all_ids: string[]) => { + if (all_ids.includes(id)) { + const index = all_ids.indexOf(id); + if (index > -1 && new_value !== true) { + all_ids.splice(index, 1); + } + } else if (new_value !== false) { + all_ids.push(id); + } + return all_ids; + }); +} + +export function is_selected(id: string, selected_ids: string[]): boolean { + return selected_ids.includes(id); +} diff --git a/control/frontend/src/ts/stores/ui_behavior.ts b/control/frontend/src/ts/stores/ui_behavior.ts index 8ff8f1a..6057ec3 100644 --- a/control/frontend/src/ts/stores/ui_behavior.ts +++ b/control/frontend/src/ts/stores/ui_behavior.ts @@ -1,36 +1,51 @@ -import type { NumericRange } from "@sveltejs/kit"; -import { get, writable, type Writable } from "svelte/store"; +import { writable, type Writable } from "svelte/store"; -const screen_height_step_size = 5; export const dnd_flip_duration_ms = 300; -const min_display_screen_height = 15; -const max_display_screen_height = 40; -export const display_screen_height: Writable = writable(25); +const heights_options: Record = { + display: { + min: 15, + max: 40, + step: 5, + }, + file: { + min: 10, + max: 20, + step: 5, + } +} + +export const current_height: Writable> = writable>({ + display: 25, + file: 10, +}); + export const is_group_drag: Writable = writable(false); export const is_display_drag: Writable = writable(false); export const pinned_display_id: Writable = writable(null); -export function change_display_screen_height(factor: number) { - display_screen_height.update((current_height) => { - const new_size = current_height + (factor * screen_height_step_size); - if (new_size > max_display_screen_height || new_size < min_display_screen_height) { - return current_height; - } else { - return new_size; - } + +export function change_height(key: 'display' | 'file', factor: number) { + current_height.update((height) => { + height[key] = next_height_step_size(key, height, factor) || height[key]; + return height; }); } - -export function next_step_possible(current_height: number, factor: number) { - const new_size = current_height + (factor * screen_height_step_size); - return new_size > max_display_screen_height || new_size < min_display_screen_height; +export function next_height_step_size(key: 'display' | 'file', current_height_array: Record, factor: number): number { + const new_size = current_height_array[key] + (factor * heights_options[key].step); + if (new_size > heights_options[key].max || new_size < heights_options[key].min) { + return 0; + } else { + return new_size; + } } + + export function get_selectable_color_classes( selected: boolean, returning_classes: { bg?: boolean; hover?: boolean; active?: boolean; text?: boolean } = {}, diff --git a/control/frontend/src/ts/types.ts b/control/frontend/src/ts/types.ts index fa738c9..6e9ac42 100644 --- a/control/frontend/src/ts/types.ts +++ b/control/frontend/src/ts/types.ts @@ -1,4 +1,59 @@ -import type { X } from "lucide-svelte"; +import { FileBox, FileImage, FileVideoCamera, ImagePlay, type X } from "lucide-svelte"; + +export type SupportedFileType = { + display_name: + string; mime_type: + string; icon: typeof X; +}; + +export const supported_file_types: Record = { + '.mp4': { + display_name: 'MP4', + mime_type: 'video/mp4', + icon: FileVideoCamera, + }, + '.jpg': { + display_name: 'JPG', + mime_type: 'image/jpg', + icon: FileImage, + }, + '.jpeg': { + display_name: 'JPG', + mime_type: 'image/jpeg', + icon: FileImage, + }, + '.png': { + display_name: 'PNG', + mime_type: 'image/png', + icon: FileImage, + }, + '.gif': { + display_name: 'GIF', + mime_type: 'image/gif', + icon: ImagePlay, + }, + '.pptx': { + display_name: 'PPTX', + mime_type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + icon: FileBox, + }, + '.odp': { + display_name: 'ODP', + mime_type: 'application/vnd.oasis.opendocument.presentation', + icon: FileBox, + } +} + +export type FolderElement = { + id?: string; + hash: string; + thumbnail: Blob | null; + name: string; + type: string; + date_created: Date; + size: string; +} + export type Display = { id: string; @@ -20,5 +75,4 @@ export type MenuOption = { class?: string; on_select?: () => void; disabled?: boolean; -} - +} \ No newline at end of file diff --git a/display/requests/shellComand.bru b/display/requests/shellComand.bru index 48bb412..1dfcde0 100644 --- a/display/requests/shellComand.bru +++ b/display/requests/shellComand.bru @@ -12,10 +12,12 @@ patch { body:json { { - "command": "ls" + "command": "find . -maxdepth 1 -mindepth 1 -print0 | while IFS= read -r -d '' f; do\n typ=$(file -b --mime-type -- \"$f\")\n size=$(stat -c '%s' -- \"$f\")\n created=$(stat -c '%w' -- \"$f\")\n [ \"$created\" = \"-\" ] && created=$(stat -c '%y' -- \"$f\")\n jq -n --arg name \"$f\" --arg type \"$typ\" --arg size \"$size\" --arg created \"$created\" '{name:$name, type:$type, size:($size|tonumber), created:$created}' | tr -d '\\n'\n echo\n done\n" } + } settings { encodeUrl: true + timeout: 0 } diff --git a/display/requests/uploadFile.bru b/display/requests/uploadFile.bru index 7b5c92b..57dee83 100644 --- a/display/requests/uploadFile.bru +++ b/display/requests/uploadFile.bru @@ -5,11 +5,16 @@ meta { } post { - url: 127.0.0.1:1323/api/file/titel.jpg + url: 127.0.0.1:1323/api/file/test.mp4 body: file auth: inherit } +body:file { + file: @file(/home/fedorra-44/Downloads/10 Revenge Party und Playoff.mov) @contentType(video/quicktime) +} + settings { encodeUrl: true + timeout: 0 }