mirror of
https://codeberg.org/PLG-Development/PLG-MuDiCS
synced 2026-07-06 17:07:08 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa7d1e7ee6 | |||
| 93f7b17222 | |||
| 0bfe6371fd | |||
| 10a09bc45e | |||
| bf08ec946a | |||
| e0f7f36383 | |||
| cf8eeacb5e | |||
| 2168873d3a | |||
| 9fd81821f1 | |||
| 9487fdd13e | |||
| aeea501a05 |
@@ -5,6 +5,7 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
- "**/*.yml"
|
- "**/*.yml"
|
||||||
|
- "**/*.sh"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -10,19 +10,25 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
name: Create Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Zip NixOS Config
|
||||||
|
run: zip -rj nixos.zip nixos/
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
name: PLG-MuDiCS ${{ github.ref_name }}
|
name: PLG-MuDiCS ${{ github.ref_name }}
|
||||||
body_path: CHANGELOG.md
|
body_path: CHANGELOG.md
|
||||||
|
files: nixos.zip
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
name: Build and Upload Assets
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -63,5 +69,5 @@ jobs:
|
|||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file: plg-mudics-${{ matrix.block }}
|
file: ${{ matrix.block }}/plg-mudics-${{ matrix.block }}
|
||||||
tag: ${{ github.ref_name }}
|
tag: ${{ github.ref_name }}
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
# PLG-MuDiCS
|
# PLG-MuDiCS
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
@@ -12,50 +12,90 @@
|
|||||||
TrafficCone
|
TrafficCone
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
import Button from './Button.svelte';
|
import Button from './Button.svelte';
|
||||||
|
import PopUp from './PopUp.svelte';
|
||||||
|
import type { PopupContent } from '../ts/types';
|
||||||
|
import KeyInput from './KeyInput.svelte';
|
||||||
|
|
||||||
|
let popup_content: PopupContent = $state({
|
||||||
|
open: false,
|
||||||
|
snippet: null,
|
||||||
|
title: '',
|
||||||
|
closable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function popup_close_function() {
|
||||||
|
popup_content.open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const show_send_keys_popup = () => {
|
||||||
|
popup_content = {
|
||||||
|
open: true,
|
||||||
|
snippet: send_keys_popup,
|
||||||
|
title: 'Tastatur-Eingaben durchgeben',
|
||||||
|
title_icon: Keyboard,
|
||||||
|
closable: true
|
||||||
|
};
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#snippet send_keys_popup()}
|
||||||
|
<div>
|
||||||
|
<KeyInput />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row justify-end gap-2">
|
||||||
|
<Button className="px-4 font-bold" click_function={popup_close_function}>Fertig</Button>
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
<div class="grid grid-rows-[2.5rem_auto] bg-stone-800 rounded-2xl min-w-0">
|
<div class="grid grid-rows-[2.5rem_auto] bg-stone-800 rounded-2xl min-w-0">
|
||||||
<div class="text-xl font-bold pl-3 content-center bg-stone-700 rounded-t-2xl truncate min-w-0">
|
<div class="text-xl font-bold pl-3 content-center bg-stone-700 rounded-t-2xl truncate min-w-0">
|
||||||
Bildschirme steuern
|
Bildschirme steuern
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 p-2 overflow-auto">
|
<div class="relative flex flex-col gap-2 p-2 overflow-auto">
|
||||||
<div class="flex flex-row justify-between gap-2">
|
<div class="flex flex-row justify-between gap-2">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex flex-row gap-2 w-70 justify-normal">
|
<div class="flex flex-row gap-2 w-75 justify-normal">
|
||||||
<Button title="Vorherige Folie (Pfeil nach Links)" className="px-7"
|
<Button title="Vorherige Folie (Pfeil nach Links)" className="px-9"
|
||||||
><ArrowBigLeft /></Button
|
><ArrowBigLeft /></Button
|
||||||
>
|
>
|
||||||
<Button title="Nächste Folie (Pfeil nach Rechts)" className="px-7"
|
<Button title="Nächste Folie (Pfeil nach Rechts)" className="px-9"
|
||||||
><ArrowBigRight /></Button
|
><ArrowBigRight /></Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<Button className="px-3 flex gap-3 w-70 justify-normal"
|
<Button className="px-3 flex gap-3 w-75 justify-normal"
|
||||||
><TextAlignStart /> Text anzeigen</Button
|
><TextAlignStart /> Text anzeigen</Button
|
||||||
>
|
>
|
||||||
<Button className="px-3 flex gap-3 w-70 justify-normal"><Presentation />Blackout</Button>
|
<Button className="px-3 flex gap-3 w-75 justify-normal"><Presentation />Blackout</Button>
|
||||||
<div class="flex flex-row justify-normal">
|
<div class="flex flex-row justify-normal">
|
||||||
<Button className="rounded-r-none pl-3 flex gap-3 grow w-60 justify-normal"
|
<Button className="rounded-r-none pl-3 flex gap-3 grow w-65 justify-normal"
|
||||||
><TrafficCone /> Fallback-Bild anzeigen</Button
|
><TrafficCone /> Fallback-Bild anzeigen</Button
|
||||||
>
|
>
|
||||||
<Button className="rounded-l-none flex grow-0 w-10"><ChevronDown /></Button>
|
<Button className="rounded-l-none flex grow-0 w-10"><ChevronDown /></Button>
|
||||||
</div>
|
</div>
|
||||||
<Button className="px-3 flex gap-3 w-70 justify-normal"
|
<Button
|
||||||
><Keyboard /> Tastatur-Inputs durchgeben</Button
|
className="px-3 flex gap-3 w-75 justify-normal"
|
||||||
|
click_function={show_send_keys_popup}><Keyboard /> Tastatur-Eingaben durchgeben</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 justify-between">
|
<div class="flex flex-col gap-2 justify-between">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<Button className="px-3 flex gap-3 w-full xl:w-70 justify-normal"
|
<Button className="px-3 flex gap-3 w-full xl:w-75 justify-normal"
|
||||||
><Power /> PC hochfahren</Button
|
><Power /> PC hochfahren</Button
|
||||||
>
|
>
|
||||||
<Button className="px-3 flex gap-3 w-full xl:w-70 justify-normal"
|
<Button className="px-3 flex gap-3 w-full xl:w-75 justify-normal"
|
||||||
><PowerOff /> PC herunterfahren</Button
|
><PowerOff /> PC herunterfahren</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<Button className="px-3 flex gap-3 w-full xl:w-70 justify-normal"
|
<Button className="px-3 flex gap-3 w-full xl:w-75 justify-normal"
|
||||||
><SquareTerminal /> Shell-Befehl ausführen</Button
|
><SquareTerminal /> Shell-Befehl ausführen</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<PopUp
|
||||||
|
content={popup_content}
|
||||||
|
close_function={popup_close_function}
|
||||||
|
className="rounded-b-2xl"
|
||||||
|
snippet_container_class="overflow-hidden"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import OnlineState from './OnlineState.svelte';
|
import OnlineState from './OnlineState.svelte';
|
||||||
import type { Display, MenuOption } from '../ts/types';
|
import type { Display, MenuOption } from '../ts/types';
|
||||||
import { is_selected, select, selected_display_ids } from '../ts/stores/select';
|
import { is_selected, select, selected_display_ids } from '../ts/stores/select';
|
||||||
|
import { update_screenshot } from '../ts/stores/displays';
|
||||||
|
|
||||||
let { display, get_display_menu_options } = $props<{
|
let { display, get_display_menu_options } = $props<{
|
||||||
display: Display;
|
display: Display;
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
} else {
|
} else {
|
||||||
$pinned_display_id = display.id;
|
$pinned_display_id = display.id;
|
||||||
}
|
}
|
||||||
|
update_screenshot(display.id);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
<div class="flex flex-row gap-1">
|
<div class="flex flex-row gap-1">
|
||||||
<OnlineState
|
<OnlineState
|
||||||
selected={false}
|
selected={false}
|
||||||
status={pinned_display?.status ?? ''}
|
status={pinned_display?.status ?? null}
|
||||||
className="flex items-center px-2"
|
className="flex items-center px-2"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -30,7 +30,12 @@
|
|||||||
import RefreshPlay from './RefreshPlay.svelte';
|
import RefreshPlay from './RefreshPlay.svelte';
|
||||||
import { get_file_size_display_string } from '../ts/utils';
|
import { get_file_size_display_string } from '../ts/utils';
|
||||||
import { open_file } from '../ts/api_handler';
|
import { open_file } from '../ts/api_handler';
|
||||||
import { displays, get_display_by_id, update_screenshot } from '../ts/stores/displays';
|
import {
|
||||||
|
displays,
|
||||||
|
get_display_by_id,
|
||||||
|
run_on_all_selected_displays,
|
||||||
|
update_screenshot
|
||||||
|
} from '../ts/stores/displays';
|
||||||
|
|
||||||
let { file } = $props<{ file: FolderElement }>();
|
let { file } = $props<{ file: FolderElement }>();
|
||||||
|
|
||||||
@@ -97,13 +102,7 @@
|
|||||||
change_file_path($current_file_path + file.name + '/');
|
change_file_path($current_file_path + file.name + '/');
|
||||||
} else {
|
} else {
|
||||||
const path_to_file = $current_file_path + file.name;
|
const path_to_file = $current_file_path + file.name;
|
||||||
for (const display_id of $selected_display_ids) {
|
await run_on_all_selected_displays(open_file, true, path_to_file);
|
||||||
const ip = get_display_by_id(display_id, $displays)?.ip ?? null;
|
|
||||||
if (ip) {
|
|
||||||
await open_file(ip, path_to_file);
|
|
||||||
await update_screenshot(display_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { flip } from 'svelte/animate';
|
||||||
|
import { get_selectable_color_classes } from '../ts/stores/ui_behavior';
|
||||||
|
import key_map_json from './../../../../shared/keys.json';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import { selected_display_ids } from '../ts/stores/select';
|
||||||
|
import { displays, get_display_by_id, run_on_all_selected_displays } from '../ts/stores/displays';
|
||||||
|
import { send_keyboard_input } from '../ts/api_handler';
|
||||||
|
|
||||||
|
const bg = 'bg-stone-700';
|
||||||
|
const key_map: Record<string, string> = key_map_json as Record<string, string>;
|
||||||
|
|
||||||
|
let active = $state(false);
|
||||||
|
let last_keys: { id: number; key: string }[] = $state([]);
|
||||||
|
|
||||||
|
let el: HTMLDivElement;
|
||||||
|
|
||||||
|
function add_to_last_keys(name: string) {
|
||||||
|
const id = Date.now();
|
||||||
|
last_keys.push({ id, key: name });
|
||||||
|
setTimeout(() => {
|
||||||
|
last_keys = last_keys.filter((e) => e.id !== id);
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function on_key_down(e: KeyboardEvent) {
|
||||||
|
if (!active) return;
|
||||||
|
const id = key_map[e.code];
|
||||||
|
if (!id) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
add_to_last_keys(e.code);
|
||||||
|
if (e.repeat) return;
|
||||||
|
|
||||||
|
await run_on_all_selected_displays(send_keyboard_input, true, id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="textbox"
|
||||||
|
tabindex="0"
|
||||||
|
bind:this={el}
|
||||||
|
onclick={() => {
|
||||||
|
if (active) {
|
||||||
|
el.blur();
|
||||||
|
} else {
|
||||||
|
el.focus();
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onblur={() => (active = false)}
|
||||||
|
onkeydown={on_key_down}
|
||||||
|
class="relative flex justify-center items-center h-15 w-full cursor-pointer rounded-xl transition-colors duration-200 select-none {get_selectable_color_classes(
|
||||||
|
active,
|
||||||
|
{
|
||||||
|
bg: true,
|
||||||
|
hover: true,
|
||||||
|
active: true,
|
||||||
|
text: true
|
||||||
|
}
|
||||||
|
)}"
|
||||||
|
>
|
||||||
|
{active ? 'Erfassung aktiv' : 'Hier für Erfassung klicken'}
|
||||||
|
<div class="absolute top-full left-0 ml-1 mt-0.5 flex flex-col-reverse text-sm text-stone-400">
|
||||||
|
{#each last_keys as key (key.id)}
|
||||||
|
<span animate:flip={{ duration: 200 }} in:fade={{ duration: 200 }} out:fade={{ duration: 500 }} >{key.key}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
import Button from "./Button.svelte";
|
||||||
|
import { X } from "lucide-svelte";
|
||||||
|
import { notifications } from "../ts/stores/notification";
|
||||||
|
import { get_shifted_color } from "../ts/stores/ui_behavior";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="fixed flex flex-col gap-2 {true ? "top-[41%]": "top-2"} right-2 left-2 md:top-auto md:left-auto md:bottom-2 md:w-100 z-50">
|
||||||
|
{#each $notifications as n (n.id)}
|
||||||
|
<div
|
||||||
|
transition:fade={{ duration: 200 }}
|
||||||
|
class="p-2 pl-4 pb-3 rounded-lg shadow-xl/30 text-white flex flex-col gap-2 overflow-hidden relative border-1 border-black/20 {n.className}"
|
||||||
|
class:bg-red-900={n.type === "error"}
|
||||||
|
class:bg-green-900={n.type === "success"}
|
||||||
|
class:bg-sky-900={n.type === "info"}
|
||||||
|
style="--dur: {n.duration}ms"
|
||||||
|
>
|
||||||
|
<div class="flex flex-row justify-between">
|
||||||
|
<span class="text-xl font-bold flex items-center">{n.title}</span>
|
||||||
|
<Button click_function={() => notifications.remove(n.id)} className="p-2" bg="bg-stone-900/50" hover_bg="bg-stone-600/70" active_bg="bg-stone-500/80"><X/></Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="whitespace-break-spaces">{n.message}</span>
|
||||||
|
|
||||||
|
<div class="absolute inset-x-0 bottom-0 h-1 bg-white/25">
|
||||||
|
<div
|
||||||
|
class="block h-full w-full bg-white/80 origin-left animate-progress-bar"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes progress {
|
||||||
|
from {
|
||||||
|
transform: scaleX(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animate-progress-bar {
|
||||||
|
animation: progress var(--dur) linear forwards;
|
||||||
|
transform-origin: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { display_status_to_info, type DisplayStatus } from "../ts/types";
|
||||||
|
|
||||||
let { selected, status, className = "" } = $props<{
|
let { selected, status, className = "" } = $props<{
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
status: string;
|
status: DisplayStatus;
|
||||||
className?: string;
|
className?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function get_text_color(selected: boolean, status: string) {
|
function get_text_color(selected: boolean, status: DisplayStatus) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'Online':
|
case 'app_online':
|
||||||
return selected ? 'text-green-700' : 'text-green-400';
|
return selected ? 'text-green-700' : 'text-green-400';
|
||||||
case 'Lädt':
|
case 'app_offline':
|
||||||
return selected ? 'text-amber-700' : 'text-amber-400';
|
return selected ? 'text-amber-700' : 'text-amber-400';
|
||||||
case 'Offline':
|
case 'host_offline':
|
||||||
return selected ? 'text-red-700' : 'text-red-400';
|
return selected ? 'text-red-700' : 'text-red-400';
|
||||||
default:
|
default:
|
||||||
return selected ? 'text-stone-700' : 'text-stone-400';
|
return selected ? 'text-stone-700' : 'text-stone-400';
|
||||||
@@ -20,5 +22,5 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="{get_text_color(selected, status)} {className} transition-colors duration-100">
|
<div class="{get_text_color(selected, status)} {className} transition-colors duration-100">
|
||||||
{status}
|
{display_status_to_info(status)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
import type { PopupContent } from '../ts/types';
|
import type { PopupContent } from '../ts/types';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
let { content, close_function } = $props<{
|
let { content, close_function, className = '', snippet_container_class = '' } = $props<{
|
||||||
content: PopupContent;
|
content: PopupContent;
|
||||||
close_function: () => void;
|
close_function: () => void;
|
||||||
|
className?: string;
|
||||||
|
snippet_container_class?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function try_to_close() {
|
function try_to_close() {
|
||||||
@@ -31,7 +33,7 @@
|
|||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 backdrop-blur bg-white/10 flex justify-center items-center"
|
class="absolute inset-0 backdrop-blur flex justify-center items-center {className}"
|
||||||
onclick={try_to_close}
|
onclick={try_to_close}
|
||||||
transition:fade={{ duration: 100 }}
|
transition:fade={{ duration: 100 }}
|
||||||
>
|
>
|
||||||
@@ -42,12 +44,12 @@
|
|||||||
onclick={(e) => e.stopPropagation()}
|
onclick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="text-2xl font-bold bg-stone-700 p-1.5 flex flex-row justify-between gap-6 w-full"
|
class="font-bold bg-stone-700 p-1.5 flex flex-row justify-between gap-8 w-full"
|
||||||
>
|
>
|
||||||
<div class="flex flex-row flex-1 gap-4 pl-2 py-1 items-center grow whitespace-nowrap min-w-0 flex-shrink-0 {content.title_class ?? ''}">
|
<div class="flex flex-row flex-1 gap-3 pl-2 py-1 items-center grow whitespace-nowrap min-w-0 flex-shrink-0 text-lg {content.title_class ?? ''}">
|
||||||
{#if content.title_icon}
|
{#if content.title_icon}
|
||||||
{@const Icon = content.title_icon}
|
{@const Icon = content.title_icon}
|
||||||
<Icon strokeWidth="2.8" class="flex-shrink-0" />
|
<Icon strokeWidth="2" class="flex-shrink-0" />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
{content.title}
|
{content.title}
|
||||||
@@ -61,7 +63,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2 min-h-0 overflow-auto flex flex-col gap-2">
|
<div class="p-2 min-h-0 overflow-auto flex flex-col gap-2 {snippet_container_class}">
|
||||||
{@render content.snippet(content.snippet_arg)}
|
{@render content.snippet(content.snippet_arg)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
|
import Notification from '../components/Notification.svelte';
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
|
||||||
|
<Notification />
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
import DisplayView from '../components/DisplayView.svelte';
|
import DisplayView from '../components/DisplayView.svelte';
|
||||||
import SplashScreen from './../../../../shared/splash_screen.html?raw';
|
import SplashScreen from './../../../../shared/splash_screen.html?raw';
|
||||||
import PopUp from '../components/PopUp.svelte';
|
import PopUp from '../components/PopUp.svelte';
|
||||||
import type { PopupContent } from '../ts/types';
|
import { display_status_to_info, type PopupContent } from '../ts/types';
|
||||||
import TextInput from '../components/TextInput.svelte';
|
import TextInput from '../components/TextInput.svelte';
|
||||||
import {
|
import {
|
||||||
add_display,
|
add_display,
|
||||||
@@ -17,6 +17,10 @@
|
|||||||
remove_display
|
remove_display
|
||||||
} from '../ts/stores/displays';
|
} from '../ts/stores/displays';
|
||||||
import { text } from '@sveltejs/kit';
|
import { text } from '@sveltejs/kit';
|
||||||
|
import { notifications } from '../ts/stores/notification';
|
||||||
|
import { ping_ip } from '../ts/api_handler';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { on_start } from '../ts/main';
|
||||||
|
|
||||||
const ip_regex =
|
const ip_regex =
|
||||||
/^(?:(?:10|127)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)|192\.168\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)|172\.(?:1[6-9]|2\d|3[0-1])\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d))$/;
|
/^(?:(?:10|127)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)|192\.168\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d)|172\.(?:1[6-9]|2\d|3[0-1])\.(?:25[0-5]|2[0-4]\d|1?\d?\d)\.(?:25[0-5]|2[0-4]\d|1?\d?\d))$/;
|
||||||
@@ -26,6 +30,7 @@
|
|||||||
open: false,
|
open: false,
|
||||||
snippet: null,
|
snippet: null,
|
||||||
title: '',
|
title: '',
|
||||||
|
title_class: '!text-xl',
|
||||||
closable: true
|
closable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,14 +50,15 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function finalize_add_edit_display(existing_display_id: string|null) {
|
async function finalize_add_edit_display(existing_display_id: string | null) {
|
||||||
const ip = text_inputs_valid.ip.value;
|
const ip = text_inputs_valid.ip.value;
|
||||||
const mac = text_inputs_valid.mac.value === '' ? null : text_inputs_valid.mac.value;
|
const mac = text_inputs_valid.mac.value === '' ? null : text_inputs_valid.mac.value;
|
||||||
const name = text_inputs_valid.name.value;
|
const name = text_inputs_valid.name.value;
|
||||||
if (!!existing_display_id) {
|
if (!!existing_display_id) {
|
||||||
edit_display_data(existing_display_id, ip, mac, name);
|
edit_display_data(existing_display_id, ip, mac, name);
|
||||||
} else {
|
} else {
|
||||||
add_display(ip, mac, name, 'Online');
|
const status = await ping_ip(text_inputs_valid.ip.value);
|
||||||
|
add_display(ip, mac, name, status);
|
||||||
}
|
}
|
||||||
popup_close_function();
|
popup_close_function();
|
||||||
}
|
}
|
||||||
@@ -68,6 +74,7 @@
|
|||||||
snippet: add_new_display_popup,
|
snippet: add_new_display_popup,
|
||||||
title: 'Neuen Bildschirm hinzufügen',
|
title: 'Neuen Bildschirm hinzufügen',
|
||||||
title_icon: Monitor,
|
title_icon: Monitor,
|
||||||
|
title_class: '!text-xl',
|
||||||
closable: true
|
closable: true
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -78,7 +85,7 @@
|
|||||||
snippet: remove_display_popup,
|
snippet: remove_display_popup,
|
||||||
snippet_arg: display_id,
|
snippet_arg: display_id,
|
||||||
title: 'Bildschirm wirklich löschen?',
|
title: 'Bildschirm wirklich löschen?',
|
||||||
title_class: 'text-red-400',
|
title_class: 'text-red-400 !text-xl',
|
||||||
title_icon: Trash2,
|
title_icon: Trash2,
|
||||||
closable: true
|
closable: true
|
||||||
};
|
};
|
||||||
@@ -98,9 +105,14 @@
|
|||||||
snippet_arg: display_id,
|
snippet_arg: display_id,
|
||||||
title: 'Bildschirm bearbeiten',
|
title: 'Bildschirm bearbeiten',
|
||||||
title_icon: Monitor,
|
title_icon: Monitor,
|
||||||
|
title_class: '!text-xl',
|
||||||
closable: true
|
closable: true
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
on_start();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet remove_display_popup(display_id: string)}
|
{#snippet remove_display_popup(display_id: string)}
|
||||||
@@ -153,8 +165,18 @@
|
|||||||
className="grow"
|
className="grow"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-end shrink-0">
|
<div class="flex items-end shrink-0">
|
||||||
<Button disabled={!text_inputs_valid.ip.valid} className="px-4 gap-2" bg="bg-stone-750"
|
<Button
|
||||||
><Radio /> Ping</Button
|
disabled={!text_inputs_valid.ip.valid}
|
||||||
|
className="px-4 gap-2"
|
||||||
|
bg="bg-stone-750"
|
||||||
|
click_function={async () => {
|
||||||
|
const status = await ping_ip(text_inputs_valid.ip.value);
|
||||||
|
notifications.push(
|
||||||
|
'info',
|
||||||
|
`Ping '${text_inputs_valid.ip.value}'`,
|
||||||
|
`Aktueller Zustand: ${display_status_to_info(status)}`
|
||||||
|
);
|
||||||
|
}}><Radio /> Ping</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -228,5 +250,5 @@
|
|||||||
<FileView />
|
<FileView />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PopUp content={popup_content} close_function={popup_close_function} />
|
<PopUp content={popup_content} close_function={popup_close_function} className="bg-white/10" />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -213,5 +213,8 @@ module.exports = {
|
|||||||
|
|
||||||
'hover:bg-red-400',
|
'hover:bg-red-400',
|
||||||
'active:bg-red-500',
|
'active:bg-red-500',
|
||||||
|
|
||||||
|
'hover:bg-stone-600/70',
|
||||||
|
'active:bg-stone-500/80',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,27 @@
|
|||||||
import type { FolderElement } from "./types";
|
import { notifications } from "./stores/notification";
|
||||||
|
import { to_display_status, type DisplayStatus, type FolderElement } from "./types";
|
||||||
import { get_uuid } from "./utils";
|
import { get_uuid } from "./utils";
|
||||||
|
|
||||||
export async function get_screenshot(ip: string) {
|
export async function get_screenshot(ip: string) {
|
||||||
const options = { method: 'PATCH' };
|
const options = { method: 'PATCH' };
|
||||||
return await request(ip, '/takeScreenshot', options);
|
return await request_display(ip, '/takeScreenshot', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function open_file(ip: string, path_to_file: string) {
|
export async function open_file(ip: string, path_to_file: string): Promise<void> {
|
||||||
const options = { method: 'PATCH', headers: { 'content-type': 'application/octet-stream' } };
|
const options = { method: 'PATCH', headers: { 'content-type': 'application/octet-stream' } };
|
||||||
const raw_response = await request(ip, `/file${path_to_file}`, options);
|
await request_display(ip, `/file${path_to_file}`, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function send_keyboard_input(ip: string, key: string): Promise<void> {
|
||||||
|
const options = {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'content-type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
key: key,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
console.log(options)
|
||||||
|
await request_display(ip, '/keyboardInput', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get_file_data(ip: string, path: string): Promise<FolderElement[]> {
|
export async function get_file_data(ip: string, path: string): Promise<FolderElement[]> {
|
||||||
@@ -34,7 +47,8 @@ export async function get_file_data(ip: string, path: string): Promise<FolderEle
|
|||||||
done
|
done
|
||||||
` })
|
` })
|
||||||
};
|
};
|
||||||
const raw_response = await request(ip, '/shellCommand', options);
|
const raw_response = await request_display(ip, '/shellCommand', options);
|
||||||
|
if (!raw_response) return [];
|
||||||
const response: FileInfo[] = raw_response.stdout.trim()
|
const response: FileInfo[] = raw_response.stdout.trim()
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -62,22 +76,53 @@ done
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function ping_ip(ip: string): Promise<DisplayStatus> {
|
||||||
|
const raw_response = await request_control(`/ping?ip=${ip}`, { method: 'GET' });
|
||||||
|
if (!raw_response) return null;
|
||||||
|
return raw_response.status ? to_display_status(raw_response.status) : null;
|
||||||
|
}
|
||||||
|
|
||||||
async function request(ip: string, api_route: string, options: { method: string, headers?: Record<string, string>, body?: any }) {
|
|
||||||
|
|
||||||
|
|
||||||
|
async function request_display(ip: string, api_route: string, options: { method: string, headers?: Record<string, string>, body?: any }): Promise<null | any> {
|
||||||
|
const url = `http://${ip}:1323/api${api_route}`;
|
||||||
|
return await raw_request(url, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request_control(api_route: string, options: { method: string, headers?: Record<string, string>, body?: any }): Promise<null | any> {
|
||||||
|
const url = `${window.location.origin}/api${api_route}`;
|
||||||
|
return await raw_request(url, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function raw_request(url: string, options: { method: string, headers?: Record<string, string>, body?: any }): Promise<null | any> {
|
||||||
try {
|
try {
|
||||||
const url = `http://${ip}:1323/api${api_route}?t=${Date.now()}`;
|
const cache_buster = `${url.includes('?') ? '&' : '?'}=${Date.now()}`;
|
||||||
console.log(url)
|
console.log(url + cache_buster)
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url + cache_buster, options);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.error(`HTTP error! Status: ${response.status}`);
|
console.error(`HTTP error! Status: ${response.status}`);
|
||||||
|
notifications.push("error", `HTTP-Fehler bei API-Anfrage`, `${url}\nHTTP-Status: ${response.status}`);
|
||||||
}
|
}
|
||||||
const contentType = response.headers.get("content-type") || "";
|
const contentType = response.headers.get("content-type") || "";
|
||||||
if (!contentType.includes("application/json")) {
|
if (!contentType.includes("application/json")) {
|
||||||
return await response.blob();
|
return await response.blob();
|
||||||
} else {
|
} else {
|
||||||
return await response.json();
|
const json = await response.json();
|
||||||
|
if (json.error && json.error !== '') {
|
||||||
|
notifications.push("error", `Interner Fehler bei API-Anfrage`, `${url}\nJSON: ${JSON.stringify(json)}`);
|
||||||
|
}
|
||||||
|
return json;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
if (error instanceof TypeError && /fetch|NetworkError/i.test(error.message)) {
|
||||||
|
console.log("Request failed - Is the targeted device online?")
|
||||||
|
} else {
|
||||||
|
console.log(typeof error, error instanceof TypeError, error.message);
|
||||||
|
console.error(error);
|
||||||
|
notifications.push("error", `Fehler bei API-Anfrage`, `${url}\nFehler: ${error}`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,23 @@
|
|||||||
export function on_start() {
|
import { get } from "svelte/store";
|
||||||
|
import { displays, update_displays_with_map } from "./stores/displays"
|
||||||
|
import { ping_ip } from "./api_handler";
|
||||||
|
import type { Display } from "./types";
|
||||||
|
import { change_file_path, update_all_display_files } from "./stores/files";
|
||||||
|
|
||||||
|
const update_display_status_interval_seconds = 20;
|
||||||
|
|
||||||
|
export function on_start() {
|
||||||
|
update_all_display_status();
|
||||||
|
setInterval(update_all_display_status, update_display_status_interval_seconds * 1000);
|
||||||
|
update_all_display_files('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function update_all_display_status() {
|
||||||
|
update_displays_with_map(async (display: Display) => {
|
||||||
|
const new_status = await ping_ip(display.ip);
|
||||||
|
if (new_status === null && display.status !== null) return display;
|
||||||
|
return { ...display, status: new_status, };
|
||||||
|
});
|
||||||
|
console.log("Display Status updated")
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { get, writable, type Writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
import type { Display, DisplayGroup } from "../types";
|
import type { Display, DisplayGroup, DisplayStatus } from "../types";
|
||||||
import { is_selected, select, selected_display_ids } from "./select";
|
import { is_selected, select, selected_display_ids } from "./select";
|
||||||
import { get_uuid, image_content_hash } from "../utils";
|
import { get_uuid, image_content_hash } from "../utils";
|
||||||
import { get_screenshot } from "../api_handler";
|
import { get_screenshot } from "../api_handler";
|
||||||
|
import DisplayGroupObject from "../../components/DisplayGroupObject.svelte";
|
||||||
|
|
||||||
export const displays: Writable<DisplayGroup[]> = writable<DisplayGroup[]>([{
|
export const displays: Writable<DisplayGroup[]> = writable<DisplayGroup[]>([{
|
||||||
id: get_uuid(),
|
id: get_uuid(),
|
||||||
@@ -17,7 +18,7 @@ export function is_display_name_taken(name: string): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function add_display(ip: string, mac: string | null, name: string, status: string) {
|
export function add_display(ip: string, mac: string | null, name: string, status: DisplayStatus) {
|
||||||
displays.update((displays: DisplayGroup[]) => {
|
displays.update((displays: DisplayGroup[]) => {
|
||||||
displays[0].data.push({ id: get_uuid(), ip, preview_url: null, preview_timeout_id: null, mac, name, status });
|
displays[0].data.push({ id: get_uuid(), ip, preview_url: null, preview_timeout_id: null, mac, name, status });
|
||||||
return displays;
|
return displays;
|
||||||
@@ -25,15 +26,10 @@ export function add_display(ip: string, mac: string | null, name: string, status
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function edit_display_data(display_id: string, ip: string, mac: string | null, name: string) {
|
export function edit_display_data(display_id: string, ip: string, mac: string | null, name: string) {
|
||||||
displays.update((display_groups) =>
|
update_displays_with_map((display: Display) => {
|
||||||
display_groups.map((group) => ({
|
if (display.id !== display_id) return display;
|
||||||
...group,
|
return { ...display, ip: ip, mac: mac, name: name };
|
||||||
data: group.data.map((display) => {
|
})
|
||||||
if (display.id !== display_id) return display;
|
|
||||||
return { ...display, ip: ip, mac: mac, name: name };
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function remove_display(display_id: string) {
|
export function remove_display(display_id: string) {
|
||||||
@@ -137,23 +133,41 @@ export async function update_screenshot(display_id: string, check_type: "first_c
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (update_needed) {
|
if (update_needed) {
|
||||||
displays.update((display_groups) =>
|
update_displays_with_map((display: Display) => {
|
||||||
display_groups.map((group) => ({
|
if (display.id !== display_id) return display;
|
||||||
...group,
|
if (display.preview_url) {
|
||||||
data: group.data.map((display) => {
|
URL.revokeObjectURL(display.preview_url);
|
||||||
if (display.id !== display_id) return display;
|
}
|
||||||
if (display.preview_url) {
|
const new_url = URL.createObjectURL(new_blob);
|
||||||
URL.revokeObjectURL(display.preview_url);
|
return { ...display, preview_url: new_url, preview_timeout_id: new_preview_timeout_id };
|
||||||
}
|
})
|
||||||
const new_url = URL.createObjectURL(new_blob);
|
|
||||||
return { ...display, preview_url: new_url, preview_timeout_id: new_preview_timeout_id };
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function update_displays_with_map(update_function: (display: Display) => Display | Promise<Display>) {
|
||||||
|
const display_groups = get(displays);
|
||||||
|
const updated_groups = await Promise.all(
|
||||||
|
display_groups.map(async (group: DisplayGroup) => ({
|
||||||
|
...group,
|
||||||
|
data: await Promise.all(group.data.map(update_function)),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
displays.set(updated_groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function run_on_all_selected_displays(run_function: ((ip: string, ...args: any[]) => void | Promise<void>), update_screenshot_afterwards: boolean, ...args: any[]) {
|
||||||
|
for (const display_id of get(selected_display_ids)) {
|
||||||
|
const display_ip = get_display_by_id(display_id, get(displays))?.ip;
|
||||||
|
if (display_ip) {
|
||||||
|
await run_function(display_ip, ...args)
|
||||||
|
if (update_screenshot_afterwards) {
|
||||||
|
await update_screenshot(display_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -164,6 +178,6 @@ function add_testing_displays() {
|
|||||||
// 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", name, "Offline");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", "PC", "Offline");
|
add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", "PC", "host_offline");
|
||||||
// add_display("192.168.178.111", "D4:81:D7:C0:DF:3C", "Laptop", "Online");
|
// add_display("192.168.178.111", "D4:81:D7:C0:DF:3C", "Laptop", "host_offline");
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { get, writable, type Writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
import type { FolderElement } from "../types";
|
import type { Display, FolderElement } from "../types";
|
||||||
import { displays } from "./displays";
|
import { displays, update_displays_with_map } from "./displays";
|
||||||
import { selected_display_ids, selected_file_ids } from "./select";
|
import { selected_display_ids, selected_file_ids } from "./select";
|
||||||
import { get_file_data } from "../api_handler";
|
import { get_file_data } from "../api_handler";
|
||||||
import { get_uuid } from "../utils";
|
import { get_uuid } from "../utils";
|
||||||
|
import type { Folder } from "lucide-svelte";
|
||||||
|
import { notifications } from "./notification";
|
||||||
|
|
||||||
export const all_files: Writable<Record<string, Record<string, FolderElement[]>>> = writable<Record<string, Record<string, FolderElement[]>>>({});
|
export const all_files: Writable<Record<string, Record<string, FolderElement[]>>> = writable<Record<string, Record<string, FolderElement[]>>>({});
|
||||||
// {
|
// {
|
||||||
@@ -28,6 +30,7 @@ export function change_file_path(new_path: string) {
|
|||||||
selected_file_ids.update(() => {
|
selected_file_ids.update(() => {
|
||||||
return [];
|
return [];
|
||||||
})
|
})
|
||||||
|
setTimeout(async () => { await update_all_display_files(new_path) }, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_display_ids_where_file_is_missing(path: string, file: FolderElement, selected_display_ids: string[], all_files: Record<string, Record<string, FolderElement[]>>): string[][] {
|
export function get_display_ids_where_file_is_missing(path: string, file: FolderElement, selected_display_ids: string[], all_files: Record<string, Record<string, FolderElement[]>>): string[][] {
|
||||||
@@ -53,36 +56,111 @@ export function get_display_ids_where_file_is_missing(path: string, file: Folder
|
|||||||
return [missing, colliding];
|
return [missing, colliding];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function update_all_display_files(path: string) {
|
||||||
|
for (const display_group of get(displays)) {
|
||||||
|
for (const display of display_group.data) {
|
||||||
|
await update_folder_elements_recursively(display, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function updates_files_on_display(display_id: string, new_folder_elements: FolderElement[], file_path: string) {
|
async function update_folder_elements_recursively(display: Display, file_path: string = '/'): Promise<number> {
|
||||||
all_files.update((files) => {
|
const new_folder_elements = await get_file_data(display.ip, file_path);
|
||||||
|
all_files.update((files: Record<string, Record<string, FolderElement[]>>) => {
|
||||||
if (!files.hasOwnProperty(file_path)) {
|
if (!files.hasOwnProperty(file_path)) {
|
||||||
files[file_path] = {};
|
files[file_path] = {};
|
||||||
}
|
}
|
||||||
for (const new_folder_element of new_folder_elements) {
|
if (!files[file_path].hasOwnProperty(display.id)) {
|
||||||
new_folder_element.id = get_uuid();
|
files[file_path][display.id] = [];
|
||||||
}
|
}
|
||||||
files[file_path][display_id] = new_folder_elements;
|
|
||||||
return files;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function get_files_on_all_displays(current_file_path: string) {
|
const existing_folder_elements = files[file_path].hasOwnProperty(display.id) ? files[file_path][display.id] : [];
|
||||||
for (const display_group of get(displays)) {
|
|
||||||
for (const display of display_group.data) {
|
const diff = get_folder_elements_difference(existing_folder_elements, new_folder_elements);
|
||||||
const new_folder_elements = await get_file_data(display.ip, current_file_path);
|
files[file_path][display.id].push(...diff.new);
|
||||||
updates_files_on_display(display.id, new_folder_elements, current_file_path)
|
return remove_folder_elements_recursively(files, display, diff.deleted, file_path);
|
||||||
console.log(new_folder_elements)
|
})
|
||||||
|
|
||||||
|
let folder_size = 0;
|
||||||
|
for (const element of new_folder_elements) {
|
||||||
|
if (element.type === 'inode/directory') {
|
||||||
|
const folder_content_size = await update_folder_elements_recursively(display, file_path + element.name + '/');
|
||||||
|
folder_size += folder_content_size;
|
||||||
|
// Update foldersize
|
||||||
|
all_files.update((files: Record<string, Record<string, FolderElement[]>>) => {
|
||||||
|
for (const current_folder_element of files[file_path][display.id]) {
|
||||||
|
if (current_folder_element.id === element.id) {
|
||||||
|
current_folder_element.size = folder_content_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
folder_size += element.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return folder_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function remove_folder_elements_recursively(files: Record<string, Record<string, FolderElement[]>>, display: Display, folder_elements: FolderElement[], file_path: string): Record<string, Record<string, FolderElement[]>> {
|
||||||
|
if (!files.hasOwnProperty(file_path) || !files[file_path].hasOwnProperty(display.id)) {
|
||||||
|
console.error("File remove path does not exist:", files, display, folder_elements, file_path);
|
||||||
|
notifications.push("error", "Fehler beim Aktualisieren der Dateien", `File remove path does not exist: ${file_path} display_ip: ${display.ip}`);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
for (const folder_element of folder_elements) {
|
||||||
|
files[file_path][display.id] = files[file_path][display.id].filter((f) => f.id !== folder_element.id);
|
||||||
|
|
||||||
|
if (folder_element.type === 'inode/directory') {
|
||||||
|
const new_file_path = file_path + folder_element.name + '/';
|
||||||
|
if (!files.hasOwnProperty(new_file_path) || !files[new_file_path].hasOwnProperty(display.id)) {
|
||||||
|
console.error("File remove path does not exist (recursion):", files, display, folder_elements, file_path, new_file_path);
|
||||||
|
notifications.push("error", "Fehler beim Aktualisieren der Dateien", `File remove path does not exist (recursion): ${new_file_path} display_ip: ${display.ip}`);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const sub_folder = files[new_file_path][display.id];
|
||||||
|
remove_folder_elements_recursively(files, display, sub_folder, new_file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_folder_elements_difference(old_elements: FolderElement[], new_elements: FolderElement[]): { deleted: FolderElement[], new: FolderElement[] } {
|
||||||
|
const old_hashes = new Set(old_elements.map(e => e.hash));
|
||||||
|
const new_hashes = new Set(new_elements.map(e => e.hash));
|
||||||
|
|
||||||
|
const only_in_old = old_elements.filter(e => !new_hashes.has(e.hash));
|
||||||
|
const only_in_new = new_elements.filter(e => !old_hashes.has(e.hash));
|
||||||
|
return { deleted: only_in_old, new: only_in_new };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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] = {};
|
||||||
|
// }
|
||||||
|
// files[file_path][display_id] = new_folder_elements;
|
||||||
|
// return files;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async function get_files_on_all_displays(current_file_path: string) {
|
||||||
|
// for (const display_group of get(displays)) {
|
||||||
|
// for (const display of display_group.data) {
|
||||||
|
// const new_folder_elements = await get_file_data(display.ip, current_file_path);
|
||||||
|
// updates_files_on_display(display.id, new_folder_elements, current_file_path)
|
||||||
|
// console.log(new_folder_elements)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
export function get_current_folder_elements(all_files: Record<string, Record<string, FolderElement[]>>, current_file_path: string, selected_display_ids: string[]) {
|
export function get_current_folder_elements(all_files: Record<string, Record<string, FolderElement[]>>, current_file_path: string, selected_display_ids: string[]) {
|
||||||
if (!all_files.hasOwnProperty(current_file_path)) {
|
if (!all_files.hasOwnProperty(current_file_path)) return [];
|
||||||
setTimeout(async () => { await get_files_on_all_displays(current_file_path) }, 0);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const files_on_display_array = all_files[current_file_path];
|
const files_on_display_array = all_files[current_file_path];
|
||||||
const files: FolderElement[] = [];
|
const files: FolderElement[] = [];
|
||||||
for (const key of Object.keys(files_on_display_array)) {
|
for (const key of Object.keys(files_on_display_array)) {
|
||||||
@@ -123,120 +201,3 @@ function sort_files(files: FolderElement[]) {
|
|||||||
return files;
|
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"
|
|
||||||
// },], '/');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
|
||||||
|
export type Notification = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
duration: number;
|
||||||
|
className: string;
|
||||||
|
type?: "error" | "success" | "info";
|
||||||
|
};
|
||||||
|
|
||||||
|
function createNotifications() {
|
||||||
|
const { subscribe, update } = writable<Notification[]>([]);
|
||||||
|
|
||||||
|
function push(type: "error" | "success" | "info", title: string, message: string = "", className: string = "") {
|
||||||
|
const id = Date.now();
|
||||||
|
const duration = type === "error" ? 16000 : 4000;
|
||||||
|
update((n) => [...n, { id, title, message, duration, className, type }]);
|
||||||
|
setTimeout(() => {
|
||||||
|
update((n) => n.filter((x) => x.id !== id));
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
const remove = (id: number) => update(n => n.filter(x => x.id !== id));
|
||||||
|
|
||||||
|
return { subscribe, push, remove };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notifications = createNotifications();
|
||||||
@@ -63,7 +63,7 @@ export type Display = {
|
|||||||
preview_timeout_id: number | null;
|
preview_timeout_id: number | null;
|
||||||
mac: string | null;
|
mac: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
status: string;
|
status: DisplayStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DisplayGroup = {
|
export type DisplayGroup = {
|
||||||
@@ -89,3 +89,24 @@ export type PopupContent = {
|
|||||||
title_icon?: typeof X | null;
|
title_icon?: typeof X | null;
|
||||||
closable?: boolean;
|
closable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DisplayStatus = "host_offline" | "app_offline" | "app_online" | null;
|
||||||
|
|
||||||
|
export function to_display_status(value: string): DisplayStatus {
|
||||||
|
return ["host_offline", "app_offline", "app_online"].includes(value)
|
||||||
|
? (value as DisplayStatus)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function display_status_to_info(status: DisplayStatus): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'app_online':
|
||||||
|
return 'Online';
|
||||||
|
case 'app_offline':
|
||||||
|
return 'Lädt';
|
||||||
|
case 'host_offline':
|
||||||
|
return 'Offline';
|
||||||
|
case null:
|
||||||
|
return '???';
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-3
@@ -2,9 +2,7 @@ module plg-mudics/control
|
|||||||
|
|
||||||
go 1.24.5
|
go 1.24.5
|
||||||
|
|
||||||
require (
|
require github.com/labstack/echo/v4 v4.13.4
|
||||||
github.com/labstack/echo/v4 v4.13.4
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
||||||
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
@@ -6,6 +8,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
|||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
@@ -21,3 +27,5 @@ golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
|||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
+9
-8
@@ -3,13 +3,13 @@ module plg-mudics/display
|
|||||||
go 1.24.4
|
go 1.24.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/a-h/templ v0.3.960
|
||||||
github.com/labstack/echo/v4 v4.13.4
|
github.com/labstack/echo/v4 v4.13.4
|
||||||
github.com/micmonay/keybd_event v1.1.2
|
github.com/micmonay/keybd_event v1.1.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||||
github.com/a-h/templ v0.3.924 // indirect
|
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cli/browser v1.3.0 // indirect
|
github.com/cli/browser v1.3.0 // indirect
|
||||||
@@ -21,13 +21,14 @@ require (
|
|||||||
github.com/natefinch/atomic v1.0.1 // indirect
|
github.com/natefinch/atomic v1.0.1 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.38.0 // indirect
|
golang.org/x/crypto v0.40.0 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.26.0 // indirect
|
||||||
golang.org/x/net v0.40.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/sync v0.14.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.25.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
golang.org/x/tools v0.32.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
|
golang.org/x/tools v0.35.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
tool github.com/a-h/templ/cmd/templ
|
tool github.com/a-h/templ/cmd/templ
|
||||||
|
|||||||
+20
-16
@@ -1,7 +1,7 @@
|
|||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
|
||||||
github.com/a-h/templ v0.3.924 h1:t5gZqTneXqvehpNZsgtnlOscnBboNh9aASBH2MgV/0k=
|
github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
|
||||||
github.com/a-h/templ v0.3.924/go.mod h1:FFAu4dI//ESmEN7PQkJ7E7QfnSEMdcnu7QrAY8Dn334=
|
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
@@ -14,6 +14,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
||||||
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
@@ -34,20 +36,22 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
|||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
|
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||||
|
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
+18
-1
@@ -1,7 +1,24 @@
|
|||||||
github.com/a-h/htmlformat v0.0.0-20250209131833-673be874c677/go.mod h1:FMIm5afKmEfarNbIXOaPHFY8X7fo+fRQB6I9MPG2nB0=
|
github.com/a-h/htmlformat v0.0.0-20250209131833-673be874c677/go.mod h1:FMIm5afKmEfarNbIXOaPHFY8X7fo+fRQB6I9MPG2nB0=
|
||||||
|
github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
|
||||||
|
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
|
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
|
||||||
|
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
|
||||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||||
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
|
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
## System Config
|
||||||
|
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
|
||||||
|
nix.settings.experimental-features = ["nix-command" "flakes"];
|
||||||
|
|
||||||
|
time.timeZone = "Europe/Berlin";
|
||||||
|
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
i18n.extraLocaleSettings = {
|
||||||
|
LC_ADDRESS = "de_DE.UTF-8";
|
||||||
|
LC_IDENTIFICATION = "de_DE.UTF-8";
|
||||||
|
LC_MEASUREMENT = "de_DE.UTF-8";
|
||||||
|
LC_MONETARY = "de_DE.UTF-8";
|
||||||
|
LC_NAME = "de_DE.UTF-8";
|
||||||
|
LC_NUMERIC = "de_DE.UTF-8";
|
||||||
|
LC_PAPER = "de_DE.UTF-8";
|
||||||
|
LC_TELEPHONE = "de_DE.UTF-8";
|
||||||
|
LC_TIME = "de_DE.UTF-8";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.xserver.enable = true;
|
||||||
|
|
||||||
|
services.xserver.displayManager.lightdm.enable = true;
|
||||||
|
services.xserver.desktopManager.xfce.enable = true;
|
||||||
|
|
||||||
|
services.xserver.xkb = {
|
||||||
|
layout = "de";
|
||||||
|
variant = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
console.keyMap = "de";
|
||||||
|
|
||||||
|
services.pulseaudio.enable = false;
|
||||||
|
security.rtkit.enable = true;
|
||||||
|
services.pipewire = {
|
||||||
|
enable = true;
|
||||||
|
alsa.enable = true;
|
||||||
|
alsa.support32Bit = true;
|
||||||
|
pulse.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.mudics = {
|
||||||
|
isNormalUser = true;
|
||||||
|
description = "mudics";
|
||||||
|
extraGroups = ["networkmanager" "wheel"];
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
|
system.stateVersion = "25.05"; # Don't change
|
||||||
|
|
||||||
|
## User Config
|
||||||
|
|
||||||
|
programs.nix-ld = {
|
||||||
|
enable = true;
|
||||||
|
libraries = with pkgs; [
|
||||||
|
stdenv.cc.cc
|
||||||
|
zlib
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.displayManager.autoLogin = {
|
||||||
|
enable = true;
|
||||||
|
user = "mudics";
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.hostName = "plg-mudics";
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
# Programs
|
||||||
|
libreoffice
|
||||||
|
#rustdesk
|
||||||
|
ungoogled-chromium
|
||||||
|
xfce.thunar-archive-plugin
|
||||||
|
git
|
||||||
|
nushell
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
imagemagick
|
||||||
|
ffmpeg
|
||||||
|
ghostscript
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services.update-mudics = {
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
after = ["network-online.target"];
|
||||||
|
wants = ["network-online.target"];
|
||||||
|
path = with pkgs; [nushell unzip];
|
||||||
|
script = "nu ${./update.sh}";
|
||||||
|
serviceConfig = {
|
||||||
|
WorkingDirectory = "/home/mudics/plg-mudics";
|
||||||
|
User = "mudics";
|
||||||
|
Type = "oneshot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.run-mudics = {
|
||||||
|
wantedBy = ["default.target"];
|
||||||
|
after = ["update-mudics.service" "graphical.target"];
|
||||||
|
wants = ["graphical.target"];
|
||||||
|
path = with pkgs; [ungoogled-chromium];
|
||||||
|
script = "./plg-mudics-display";
|
||||||
|
serviceConfig = {
|
||||||
|
WorkingDirectory = "/home/mudics/plg-mudics";
|
||||||
|
User = "mudics";
|
||||||
|
Type = "simple";
|
||||||
|
};
|
||||||
|
environment = {
|
||||||
|
DISPLAY = ":0";
|
||||||
|
XDG_RUNTIME_DIR = "/run/user/1000";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.build-mudics-system = {
|
||||||
|
wantedBy = ["default.target"];
|
||||||
|
after = ["update-mudics.service"];
|
||||||
|
path = with pkgs; [nixos-rebuild];
|
||||||
|
script = "nixos-rebuild boot --flake .#plg-mudics";
|
||||||
|
serviceConfig = {
|
||||||
|
WorkingDirectory = "/home/mudics/plg-mudics/nixos";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /home/mudics/plg-mudics 0755 mudics - -"
|
||||||
|
];
|
||||||
|
}
|
||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1761999846,
|
||||||
|
"narHash": "sha256-IYlYnp4O4dzEpL77BD/lj5NnJy2J8qbHkNSFiPBCbqo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "3de8f8d73e35724bf9abef41f1bdbedda1e14a31",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
} @ inputs: {
|
||||||
|
nixosConfigurations.plg-mudics = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs = {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
print "Checking for new version of PLG-MuDiCS ..."
|
||||||
|
if (new_version_available) {
|
||||||
|
print "New version available. Trying to update ..."
|
||||||
|
|
||||||
|
get_new_display_file
|
||||||
|
get_new_nixos_config
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Done"
|
||||||
|
|
||||||
|
def get_new_nixos_config [] {
|
||||||
|
let temp_folder_path = (mktemp -d "nixos-temp-XXXXXX")
|
||||||
|
let temp_file_path = (mktemp "nixos-temp-XXXXXX")
|
||||||
|
let nixos_config_path = "nixos"
|
||||||
|
|
||||||
|
http get https://github.com/PLG-Development/PLG-MuDiCS/releases/latest/download/nixos.zip --max-time 5sec | save -p -f $temp_file_path
|
||||||
|
|
||||||
|
unzip $temp_file_path -d $temp_folder_path
|
||||||
|
rm -rf $nixos_config_path
|
||||||
|
mv $temp_folder_path $nixos_config_path
|
||||||
|
|
||||||
|
rm $temp_file_path
|
||||||
|
|
||||||
|
cp /etc/nixos/hardware-configuration.nix $"($nixos_config_path)/hardware-configuration.nix"
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_new_display_file [] {
|
||||||
|
let temp_file_path = (mktemp "display-temp-XXXXXX")
|
||||||
|
|
||||||
|
http get https://github.com/PLG-Development/PLG-MuDiCS/releases/latest/download/plg-mudics-display --max-time 5sec | save -p -f $temp_file_path
|
||||||
|
|
||||||
|
chmod +x $temp_file_path
|
||||||
|
mv $temp_file_path plg-mudics-display
|
||||||
|
}
|
||||||
|
|
||||||
|
def new_version_available [] {
|
||||||
|
let file_path = "version.json"
|
||||||
|
if not ($file_path | path exists) {
|
||||||
|
{ "version": ""} | to json | save version.json
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_version = open $file_path | get version
|
||||||
|
|
||||||
|
let new_version = http get https://api.github.com/repos/PLG-Development/PLG-MuDiCS/releases/latest --max-time 5sec | get tag_name
|
||||||
|
# TODO: only write when all operations were successful
|
||||||
|
{ "version": $new_version} | to json | save version.json -f
|
||||||
|
|
||||||
|
if $current_version == $new_version {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"Escape": "VK_ESC",
|
||||||
|
"Digit1": "VK_1",
|
||||||
|
"Digit2": "VK_2",
|
||||||
|
"Digit3": "VK_3",
|
||||||
|
"Digit4": "VK_4",
|
||||||
|
"Digit5": "VK_5",
|
||||||
|
"Digit6": "VK_6",
|
||||||
|
"Digit7": "VK_7",
|
||||||
|
"Digit8": "VK_8",
|
||||||
|
"Digit9": "VK_9",
|
||||||
|
"Digit0": "VK_0",
|
||||||
|
"KeyQ": "VK_Q",
|
||||||
|
"KeyW": "VK_W",
|
||||||
|
"KeyE": "VK_E",
|
||||||
|
"KeyR": "VK_R",
|
||||||
|
"KeyT": "VK_T",
|
||||||
|
"KeyY": "VK_Y",
|
||||||
|
"KeyU": "VK_U",
|
||||||
|
"KeyI": "VK_I",
|
||||||
|
"KeyO": "VK_O",
|
||||||
|
"KeyP": "VK_P",
|
||||||
|
"KeyA": "VK_A",
|
||||||
|
"KeyS": "VK_S",
|
||||||
|
"KeyD": "VK_D",
|
||||||
|
"KeyF": "VK_F",
|
||||||
|
"KeyG": "VK_G",
|
||||||
|
"KeyH": "VK_H",
|
||||||
|
"KeyJ": "VK_J",
|
||||||
|
"KeyK": "VK_K",
|
||||||
|
"KeyL": "VK_L",
|
||||||
|
"KeyZ": "VK_Z",
|
||||||
|
"KeyX": "VK_X",
|
||||||
|
"KeyC": "VK_C",
|
||||||
|
"KeyV": "VK_V",
|
||||||
|
"KeyB": "VK_B",
|
||||||
|
"KeyN": "VK_N",
|
||||||
|
"KeyM": "VK_M",
|
||||||
|
"F1": "VK_F1",
|
||||||
|
"F2": "VK_F2",
|
||||||
|
"F3": "VK_F3",
|
||||||
|
"F4": "VK_F4",
|
||||||
|
"F5": "VK_F5",
|
||||||
|
"F6": "VK_F6",
|
||||||
|
"F7": "VK_F7",
|
||||||
|
"F8": "VK_F8",
|
||||||
|
"F9": "VK_F9",
|
||||||
|
"F10": "VK_F10",
|
||||||
|
"F11": "VK_F11",
|
||||||
|
"F12": "VK_F12",
|
||||||
|
"F13": "VK_F13",
|
||||||
|
"F14": "VK_F14",
|
||||||
|
"F15": "VK_F15",
|
||||||
|
"F16": "VK_F16",
|
||||||
|
"F17": "VK_F17",
|
||||||
|
"F18": "VK_F18",
|
||||||
|
"F19": "VK_F19",
|
||||||
|
"F20": "VK_F20",
|
||||||
|
"F21": "VK_F21",
|
||||||
|
"F22": "VK_F22",
|
||||||
|
"F23": "VK_F23",
|
||||||
|
"F24": "VK_F24",
|
||||||
|
"NumLock": "VK_NUMLOCK",
|
||||||
|
"ScrollLock": "VK_SCROLLLOCK",
|
||||||
|
"CapsLock": "VK_CAPSLOCK",
|
||||||
|
"Minus": "VK_SP2",
|
||||||
|
"Equal": "VK_SP3",
|
||||||
|
"Backspace": "VK_BACKSPACE",
|
||||||
|
"Tab": "VK_TAB",
|
||||||
|
"BracketLeft": "VK_SP4",
|
||||||
|
"BracketRight": "VK_SP5",
|
||||||
|
"Enter": "VK_ENTER",
|
||||||
|
"Semicolon": "VK_SP6",
|
||||||
|
"Quote": "VK_SP7",
|
||||||
|
"Backquote": "VK_SP1",
|
||||||
|
"Backslash": "VK_SP8",
|
||||||
|
"Comma": "VK_SP9",
|
||||||
|
"Period": "VK_SP10",
|
||||||
|
"Slash": "VK_SP11",
|
||||||
|
"IntlBackslash": "VK_SP12",
|
||||||
|
"NumpadMultiply": "VK_KPASTERISK",
|
||||||
|
"NumpadDivide": "VK_KPSLASH",
|
||||||
|
"NumpadAdd": "VK_KPPLUS",
|
||||||
|
"NumpadSubtract": "VK_KPMINUS",
|
||||||
|
"NumpadDecimal": "VK_KPDOT",
|
||||||
|
"NumpadEnter": "VK_KPENTER",
|
||||||
|
"Space": "VK_SPACE",
|
||||||
|
"Numpad0": "VK_KP0",
|
||||||
|
"Numpad1": "VK_KP1",
|
||||||
|
"Numpad2": "VK_KP2",
|
||||||
|
"Numpad3": "VK_KP3",
|
||||||
|
"Numpad4": "VK_KP4",
|
||||||
|
"Numpad5": "VK_KP5",
|
||||||
|
"Numpad6": "VK_KP6",
|
||||||
|
"Numpad7": "VK_KP7",
|
||||||
|
"Numpad8": "VK_KP8",
|
||||||
|
"Numpad9": "VK_KP9",
|
||||||
|
"PageUp": "VK_PAGEUP",
|
||||||
|
"PageDown": "VK_PAGEDOWN",
|
||||||
|
"End": "VK_END",
|
||||||
|
"Home": "VK_HOME",
|
||||||
|
"ArrowLeft": "VK_LEFT",
|
||||||
|
"ArrowUp": "VK_UP",
|
||||||
|
"ArrowRight": "VK_RIGHT",
|
||||||
|
"ArrowDown": "VK_DOWN",
|
||||||
|
"PrintScreen": "VK_PRINT",
|
||||||
|
"Insert": "VK_INSERT",
|
||||||
|
"Delete": "VK_DELETE",
|
||||||
|
"Help": "VK_HELP",
|
||||||
|
"BrowserBack": "VK_BACK",
|
||||||
|
"Pause": "VK_PAUSE",
|
||||||
|
"Lang1": "VK_HANGUEL",
|
||||||
|
"Lang2": "VK_HANJA"
|
||||||
|
}
|
||||||
@@ -23,13 +23,15 @@ func OpenBrowserWindow(url string, fullscreen bool, temp bool) error {
|
|||||||
args = append(args, arg)
|
args = append(args, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errs := []string{}
|
||||||
for _, bin := range bins {
|
for _, bin := range bins {
|
||||||
cmd := exec.Command(bin, args...)
|
cmd := exec.Command(bin, args...)
|
||||||
commandOutput := RunShellCommand(cmd)
|
commandOutput := RunShellCommand(cmd)
|
||||||
if commandOutput.ExitCode == 0 {
|
if commandOutput.ExitCode == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
errs = append(errs, commandOutput.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("chromium not found in PATH")
|
return errors.New("failed to open browser window: " + fmt.Sprint(errs))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user