hotkey to activate neko interactions
This commit is contained in:
37
src/events/scene-interactive.ts
Normal file
37
src/events/scene-interactive.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export const sceneInteractive = writable<boolean>(false);
|
||||
|
||||
let unlisten: UnlistenFn | null = null;
|
||||
let isListening = false;
|
||||
|
||||
export async function initSceneInteractiveListener() {
|
||||
if (isListening) return;
|
||||
|
||||
try {
|
||||
// ensure initial default matches backend default
|
||||
sceneInteractive.set(false);
|
||||
unlisten = await listen<boolean>("scene-interactive", (event) => {
|
||||
sceneInteractive.set(Boolean(event.payload));
|
||||
});
|
||||
isListening = true;
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize scene interactive listener:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function stopSceneInteractiveListener() {
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
unlisten = null;
|
||||
isListening = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.dispose(() => {
|
||||
stopSceneInteractiveListener();
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { initCursorTracking, stopCursorTracking } from "../events/cursor";
|
||||
import { initAppDataListener } from "../events/app-data";
|
||||
import {
|
||||
initSceneInteractiveListener,
|
||||
stopSceneInteractiveListener,
|
||||
} from "../events/scene-interactive";
|
||||
|
||||
let { children } = $props();
|
||||
if (browser) {
|
||||
@@ -10,6 +14,7 @@
|
||||
try {
|
||||
await initCursorTracking();
|
||||
await initAppDataListener();
|
||||
await initSceneInteractiveListener();
|
||||
} catch (err) {
|
||||
console.error("Failed to initialize event listeners:", err);
|
||||
}
|
||||
@@ -17,6 +22,7 @@
|
||||
|
||||
onDestroy(() => {
|
||||
stopCursorTracking();
|
||||
stopSceneInteractiveListener();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
friendsActiveDolls,
|
||||
} from "../../events/cursor";
|
||||
import { appData } from "../../events/app-data";
|
||||
import { sceneInteractive } from "../../events/scene-interactive";
|
||||
|
||||
import DesktopPet from "./DesktopPet.svelte";
|
||||
|
||||
let innerWidth = 0;
|
||||
let innerHeight = 0;
|
||||
|
||||
$: isInteractive = $sceneInteractive;
|
||||
|
||||
function getFriendName(userId: string) {
|
||||
const friend = $appData?.friends?.find((f) => f.friend.id === userId);
|
||||
return friend ? friend.friend.name : userId.slice(0, 8) + "...";
|
||||
@@ -30,39 +33,34 @@
|
||||
|
||||
<div class="w-svw h-svh p-4 relative overflow-hidden">
|
||||
<div
|
||||
class="size-max mx-auto bg-base-100 border-base-200 border px-4 py-3 rounded-xl"
|
||||
class="size-max mx-auto bg-base-100 border-base-200 border p-1 rounded-lg shadow-md"
|
||||
>
|
||||
<div class="flex flex-col text-center">
|
||||
<p class="text-xl">Friendolls</p>
|
||||
<p class="text-sm opacity-50">Scene Screen</p>
|
||||
<div class="mt-4 flex flex-col gap-1">
|
||||
<span class="font-mono text-sm">
|
||||
Raw: ({$cursorPositionOnScreen.raw.x}, {$cursorPositionOnScreen.raw
|
||||
.y})
|
||||
</span>
|
||||
<span class="font-mono text-sm">
|
||||
Mapped: ({$cursorPositionOnScreen.mapped.x.toFixed(3)}, {$cursorPositionOnScreen.mapped.y.toFixed(
|
||||
3,
|
||||
)})
|
||||
<div class="flex flex-row gap-1 items-center text-center">
|
||||
<div>
|
||||
<span class="py-3 text-xs items-center gap-2 badge">
|
||||
<span
|
||||
class={`size-2 rounded-full ${isInteractive ? "bg-success" : "bg-base-300"}`}
|
||||
></span>
|
||||
Intercepting cursor events
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span class="font-mono text-xs badge py-3">
|
||||
({$cursorPositionOnScreen.mapped.x.toFixed(3)}, {$cursorPositionOnScreen.mapped.y.toFixed(
|
||||
3,
|
||||
)})
|
||||
</span>
|
||||
|
||||
{#if Object.keys($friendsCursorPositions).length > 0}
|
||||
<div class="mt-4 flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold opacity-75">Friends Online</p>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div>
|
||||
{#each Object.entries($friendsCursorPositions) as [userId, position]}
|
||||
{@const dollConfig = getFriendDollConfig(userId)}
|
||||
<div
|
||||
class="bg-base-200/50 p-2 rounded text-xs text-left flex gap-2 flex-col"
|
||||
>
|
||||
<div class="badge py-3 text-xs text-left flex flex-row gap-2">
|
||||
<span class="font-bold">{getFriendName(userId)}</span>
|
||||
<div class="flex flex-col font-mono">
|
||||
<span>
|
||||
Raw: ({position.raw.x}, {position.raw.y})
|
||||
</span>
|
||||
<span>
|
||||
Mapped: ({position.mapped.x.toFixed(3)}, {position.mapped.y.toFixed(
|
||||
({position.mapped.x.toFixed(3)}, {position.mapped.y.toFixed(
|
||||
3,
|
||||
)})
|
||||
</span>
|
||||
@@ -74,6 +72,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute inset-0 size-full">
|
||||
{#if Object.keys($friendsCursorPositions).length > 0}
|
||||
{#each Object.entries($friendsCursorPositions) as [userId, position]}
|
||||
@@ -85,6 +84,7 @@
|
||||
name={getFriendName(userId)}
|
||||
bodyColor={config?.colorScheme?.body}
|
||||
outlineColor={config?.colorScheme?.outline}
|
||||
{isInteractive}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
export let name = "";
|
||||
export let bodyColor: string | undefined = undefined;
|
||||
export let outlineColor: string | undefined = undefined;
|
||||
export let isInteractive = false;
|
||||
|
||||
let nekoPosX = 32;
|
||||
let nekoPosY = 32;
|
||||
@@ -251,15 +252,18 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="desktop-pet flex flex-col items-center"
|
||||
<button
|
||||
onclick={() => {
|
||||
console.log("clicked on neko");
|
||||
}}
|
||||
class="desktop-pet flex flex-col items-center relative"
|
||||
style="
|
||||
transform: translate({nekoPosX - 16}px, {nekoPosY - 16}px);
|
||||
z-index: 50;
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="pixelated"
|
||||
class="pixelated hover:scale-110 active:scale-95"
|
||||
style="
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
@@ -268,18 +272,18 @@
|
||||
"
|
||||
></div>
|
||||
<span
|
||||
class="text-[10px] bg-black/50 text-white px-1 rounded backdrop-blur-sm mt-1 whitespace-nowrap"
|
||||
class="absolute -bottom-5 width-full text-[10px] bg-black/50 text-white px-1 rounded backdrop-blur-sm mt-1 whitespace-nowrap opacity-0 transition-opacity"
|
||||
class:opacity-100={isInteractive}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.desktop-pet {
|
||||
position: fixed; /* Fixed relative to the viewport/container */
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none; /* Let clicks pass through */
|
||||
will-change: transform;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user