summaryrefslogtreecommitdiff
path: root/src/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'src/desktop')
-rw-r--r--src/desktop/apps/buttons.tsx0
-rw-r--r--src/desktop/apps/edit.tsx13
-rw-r--r--src/desktop/apps/index.tsx5
-rw-r--r--src/desktop/apps/music.tsx43
-rw-r--r--src/desktop/index.tsx43
-rw-r--r--src/desktop/window.tsx81
6 files changed, 185 insertions, 0 deletions
diff --git a/src/desktop/apps/buttons.tsx b/src/desktop/apps/buttons.tsx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/desktop/apps/buttons.tsx
diff --git a/src/desktop/apps/edit.tsx b/src/desktop/apps/edit.tsx
new file mode 100644
index 0000000..00aba45
--- /dev/null
+++ b/src/desktop/apps/edit.tsx
@@ -0,0 +1,13 @@
+import { ComponentChildren } from "preact"
+
+export function Edit(props: Edit.Props) {
+ return <textarea autocomplete="off" spellcheck={false}>
+ {props.children}
+ </textarea>
+}
+
+namespace Edit {
+ export type Props = {
+ children?: ComponentChildren
+ }
+}
diff --git a/src/desktop/apps/index.tsx b/src/desktop/apps/index.tsx
new file mode 100644
index 0000000..a2954b6
--- /dev/null
+++ b/src/desktop/apps/index.tsx
@@ -0,0 +1,5 @@
+import { FunctionalComponent } from "preact";
+import { Edit } from "./edit";
+import { Music } from "./music";
+
+export const APP_REGISTRY: Map<string, FunctionalComponent> = new Map();
diff --git a/src/desktop/apps/music.tsx b/src/desktop/apps/music.tsx
new file mode 100644
index 0000000..b5646bd
--- /dev/null
+++ b/src/desktop/apps/music.tsx
@@ -0,0 +1,43 @@
+// type Song = {
+// name: string,
+// url: string,
+// length: number,
+//
+// bc_exclusive: boolean
+// }
+//
+// const SONGS: = [
+// [],
+// [],
+// [],
+// [],
+// [],
+// [],
+// [],
+// [],
+// [],
+// [],
+// []
+// ]
+//
+// function Song() {
+//
+// }
+
+import { useState } from "preact/hooks"
+
+export function Music() {
+ const [userApproves, setUserApproves] = useState(false);
+
+ if (userApproves) {
+ return <iframe style="border: 0; width: 350px; height: 500px;" src="https://bandcamp.com/EmbeddedPlayer/album=3374731395/size=large/bgcol=000000/linkcol=ff0047/transparent=true/" seamless><a href="https://sunnexo.bandcamp.com/album/netpunk">Netpunk by Sunnexo</a></iframe>
+ } else {
+ return <div class="external_warning">
+ <div>Click here to load player from <a href="https://bandcamp.com">https://bandcamp.com</a></div>
+ <button onClick={() => setUserApproves(true)}>
+ Load player
+ </button>
+ </div>
+ }
+}
+
diff --git a/src/desktop/index.tsx b/src/desktop/index.tsx
new file mode 100644
index 0000000..021d3a0
--- /dev/null
+++ b/src/desktop/index.tsx
@@ -0,0 +1,43 @@
+import { useRef } from "preact/hooks"
+import { Edit } from "./apps/edit"
+import { Window } from "./window"
+import { Music } from "./apps/music";
+
+export function Desktop() {
+ const ref = useRef(null);
+ return <div id="desktop" ref={ref}>
+ <div class="icon_grid">
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ </div>
+ <Window title="README.txt" width="30vw" height={300} x={40} y={40} desktop={ref}>
+ <Edit>
+ Fine release by Sunnexo 2026&#10;
+ &#10;
+ Tracklist:&#10;
+ 1. Internet Exploder&#10;
+ 2. Netpunk (feat. Kasane Teto)&#10;
+ 3. Free Download&#10;
+ 4. I am not a Robot&#10;
+ 5. Content&#10;
+ 6. Keygen&#10;
+ 7. Base64&#10;
+ 8. Readme (feat. Kasane Teto)&#10;
+ 9. Symlink&#10;
+ 10. Memory&#10;
+
+ An album about borrowing software forever.&#10;
+
+ A clumsy robot forgets about their girlfriend's anniversary, scrambling to find something to do for the date, eventually concluding they should watch a movie. Of course the movie is in high demand, so the tickets have sold out. Ultimately they resort to piracy, and things spiral out of control... &#10;
+ </Edit>
+ </Window>
+ <Window title="Stream Netpunk" desktop={ref}>
+ <Music />
+ </Window>
+
+ <img id="netpunk" src="/assets/netpunk.png" />
+ </div>
+}
diff --git a/src/desktop/window.tsx b/src/desktop/window.tsx
new file mode 100644
index 0000000..b2deaec
--- /dev/null
+++ b/src/desktop/window.tsx
@@ -0,0 +1,81 @@
+import { ComponentChildren, Ref, RefObject } from "preact";
+import { useCallback, useRef, useState } from "preact/hooks";
+
+export function Window(props: Window.Props) {
+ const windowRef: Ref<HTMLDivElement> = useRef(null);
+ const dragTimeout: Ref<ReturnType<typeof setTimeout>> = useRef(null);
+
+ const [x, setX] = useState(props.x ?? 0);
+ const [y, setY] = useState(props.y ?? 0);
+
+ const initialOffsetX = useRef(0);
+ const initialOffsetY = useRef(0);
+
+ const updatePosition = useCallback((e: MouseEvent) => {
+ setX(e.clientX - initialOffsetX.current)
+ setY(e.clientY - initialOffsetY.current)
+ }, [])
+
+ const startDrag = useCallback((e: MouseEvent) => {
+ if (e.button === 0) {
+
+ const BORDER_WIDTH = 1;
+ console.log(e);
+ // Naturally, I have to add the border width here, because offset* uses padding-edge for the calculations
+ initialOffsetX.current = e.offsetX + BORDER_WIDTH
+ initialOffsetY.current = e.offsetY + BORDER_WIDTH
+ windowRef.current?.classList.add("dragging")
+
+ if (dragTimeout.current !== null) {
+ clearTimeout(dragTimeout.current);
+ dragTimeout.current = null;
+ } else {
+ props.desktop.current?.addEventListener("mousemove", updatePosition)
+ props.desktop.current?.addEventListener("mouseleave", stopDrag)
+ }
+ }
+ }, []);
+
+ const stopDrag = useCallback((e: MouseEvent) => {
+ if (e.button === 0) {
+ dragTimeout.current = setTimeout(() => {
+ dragTimeout.current = null;
+ windowRef.current?.classList.remove("dragging")
+ props.desktop.current?.removeEventListener("mousemove", updatePosition)
+ props.desktop.current?.removeEventListener("mouseleave", stopDrag)
+ }, 50) // intrdouce a small delay where the user can still drag the window without actually pressing down lmb to prevent accidental drops
+ }
+ }, []);
+
+ return <div class="window" ref={windowRef} style={{ width: props.width, height: props.height, left: x, top: y}}>
+ <div class="titlebar" onPointerDown={startDrag} onPointerUp={stopDrag}>
+ <div class="title">{props.title}</div>
+ <div class="buttons" onPointerDown={e => e.stopPropagation()}>
+ <button>
+ <img src="/assets/minimize.png" />
+ </button>
+ <button>
+ <img src="/assets/maximize.png" />
+ </button>
+ <button>
+ <img src="/assets/close.png" />
+ </button>
+ </div>
+ </div>
+ <div class="content">
+ {props.children}
+ </div>
+ </div>
+}
+
+namespace Window {
+ export type Props = {
+ title: string;
+ width?: string | number;
+ height?: string | number;
+ x?: string | number;
+ y?: string | number;
+ desktop: RefObject<HTMLDivElement>;
+ children: ComponentChildren
+ }
+}