minor adjustments to the control panel UI

This commit is contained in:
2026-08-30 02:13:01 +08:00
parent 902093d75f
commit 446f31fe49
3 changed files with 123 additions and 248 deletions
@@ -1,112 +0,0 @@
<script lang="ts">
import { friendName, friends } from "$lib/listeners/friends";
import {
liveMetadata,
liveMetadataListenerError,
} from "$lib/listeners/live-metadata";
import { onMount } from "svelte";
import PanelMessage from "./panel-message.svelte";
import type { RegisterPanel } from "./types";
let { register }: { register: RegisterPanel } = $props();
const actions = { apply: async () => true, reset: () => undefined };
onMount(() => register("activity", actions, { dirty: false, busy: false }));
function userIds() {
return [
...new Set([
$liveMetadata.localId,
...Object.keys($liveMetadata.cursorPositions),
...$liveMetadata.foregroundApps.keys(),
]),
].filter(Boolean);
}
function nameFor(id: string) {
if (id === $liveMetadata.localId) return "You";
return friendName(
$friends.find((friend) => friend.id === id),
"Unknown friend",
);
}
function compactId(id: string) {
return id.length > 18 ? `${id.slice(0, 9)}…${id.slice(-6)}` : id;
}
</script>
<div class="flex h-full min-h-0 flex-col gap-2">
{#if $liveMetadataListenerError}
<PanelMessage kind="error">{$liveMetadataListenerError}</PanelMessage>
{:else if userIds().length === 0}
<PanelMessage
>Waiting for activity data. This usually appears after Wyd has started
monitoring.</PanelMessage
>
{/if}
<div
class="min-h-0 flex-1 overflow-y-auto border border-base-300 bg-base-100"
>
{#each userIds() as userId (userId)}
{@const cursor = $liveMetadata.cursorPositions[userId]}
{@const app = $liveMetadata.foregroundApps.get(userId)}
<article class="border-b border-base-300 p-2 last:border-b-0">
<div class="flex items-center gap-2">
{#if app?.ico}
<img
class="pixelated size-8 border border-base-300 bg-base-200 p-0.5"
src={`data:image/png;base64,${app.ico}`}
alt=""
width="32"
height="32"
/>
{:else}
<div
class="grid size-8 shrink-0 place-content-center border border-base-300 bg-base-200 text-sm"
aria-hidden="true"
>
▣
</div>
{/if}
<div class="min-w-0 flex-1">
<div class="flex items-center gap-1.5">
<strong class="truncate text-xs">{nameFor(userId)}</strong>
{#if userId === $liveMetadata.localId}<span class="badge badge-xs"
>Local</span
>{/if}
</div>
<p
class="truncate font-mono text-[9px] text-base-content/55"
title={userId}
>
{compactId(userId)}
</p>
</div>
</div>
<dl
class="mt-2 grid grid-cols-[5rem_1fr] gap-x-2 gap-y-0.5 text-[10px]"
>
<dt class="text-base-content/60">Foreground app</dt>
<dd class="truncate">
{app?.local ?? app?.unlocal ?? "Not available"}
</dd>
<dt class="text-base-content/60">Cursor</dt>
<dd>
{cursor
? `${Math.round(cursor.raw.x)}, ${Math.round(cursor.raw.y)} px`
: "Not available"}
</dd>
<dt class="text-base-content/60">Mapped</dt>
<dd>
{cursor
? `${cursor.mapped.x.toFixed(3)}, ${cursor.mapped.y.toFixed(3)}`
: "Not available"}
</dd>
</dl>
</article>
{/each}
</div>
</div>
@@ -109,28 +109,21 @@
} }
</script> </script>
<div class="space-y-3"> <div class="space-y-1">
{#if error || $sceneConfigurationListenerError || $profileListenerError} {#if error || $sceneConfigurationListenerError || $profileListenerError}
<PanelMessage kind="error"> <PanelMessage kind="error">
{error || $sceneConfigurationListenerError || $profileListenerError} {error || $sceneConfigurationListenerError || $profileListenerError}
</PanelMessage> </PanelMessage>
{/if} {/if}
<fieldset class="fieldset border border-base-300 bg-base-100 p-3"> <div class="grid grid-cols-[minmax(0,3fr)_minmax(0,4fr)] items-center gap-3">
<legend class="fieldset-legend px-1">Puppets</legend>
<div
class="grid grid-cols-[minmax(0,3fr)_minmax(0,4fr)] items-center gap-3"
>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="aspect-square min-w-0"> <div class="aspect-square min-w-0 mt-2">
{#if $profile} {#if $profile}
<div <div
class="border border-primary relative shadow-[inset_0_0_10px] bg-primary/5 shadow-primary card" class="border border-primary relative shadow-[inset_0_0_10px] bg-primary/5 shadow-primary card"
> >
<div <div class="size-full absolute bg-gridded opacity-25 inset-0"></div>
class="size-full absolute bg-gridded opacity-25 inset-0"
></div>
<div class="size-full absolute"> <div class="size-full absolute">
<div <div
class="flex flex-row size-full items-end justify-between text-[10px] text-primary p-1" class="flex flex-row size-full items-end justify-between text-[10px] text-primary p-1"
@@ -165,6 +158,8 @@
</div> </div>
<div class="min-w-0 space-y-2"> <div class="min-w-0 space-y-2">
<fieldset class="fieldset border border-base-300 bg-base-100 p-3 pt-1">
<legend class="fieldset-legend px-1">All puppets</legend>
<div> <div>
<label class="fieldset-label" for="puppet-scale">Scale</label> <label class="fieldset-label" for="puppet-scale">Scale</label>
<input <input
@@ -196,9 +191,7 @@
aria-describedby="puppet-opacity-help" aria-describedby="puppet-opacity-help"
/> />
</div> </div>
</fieldset>
<div>
<label class="fieldset-label" for="puppet-skin">Custom Skin</label>
<div> <div>
<button <button
class="btn w-full" class="btn w-full"
@@ -206,7 +199,7 @@
disabled={!$profile || busy} disabled={!$profile || busy}
onclick={() => skinInput?.click()} onclick={() => skinInput?.click()}
> >
Choose file Choose a custom skin
</button> </button>
<input <input
id="skin-file" id="skin-file"
@@ -220,10 +213,8 @@
</div> </div>
</div> </div>
</div> </div>
</div> <fieldset class="fieldset border border-base-300 bg-base-100 p-3 pt-1">
<legend class="fieldset-legend px-1">Puppets reposition</legend>
<fieldset class="mt-4">
<legend class="fieldset-label mb-2">Movement</legend>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<label <label
class:border-primary={puppetMovementMode === "free"} class:border-primary={puppetMovementMode === "free"}
@@ -288,7 +279,6 @@
</label> </label>
</div> </div>
</fieldset> </fieldset>
</fieldset>
</div> </div>
<style> <style>
+4 -7
View File
@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import ActivityPanel from "$lib/components/control-panel/activity-panel.svelte";
import FriendsPanel from "$lib/components/control-panel/friends-panel.svelte"; import FriendsPanel from "$lib/components/control-panel/friends-panel.svelte";
import GeneralPanel from "$lib/components/control-panel/general-panel.svelte"; import GeneralPanel from "$lib/components/control-panel/general-panel.svelte";
import NetworkPanel from "$lib/components/control-panel/network-panel.svelte"; import NetworkPanel from "$lib/components/control-panel/network-panel.svelte";
@@ -11,22 +10,20 @@
} from "$lib/components/control-panel/types"; } from "$lib/components/control-panel/types";
const tabs = [ const tabs = [
{ id: "general", label: "General" },
{ id: "scene", label: "Scene" }, { id: "scene", label: "Scene" },
{ id: "general", label: "Account" },
{ id: "friends", label: "Friends" }, { id: "friends", label: "Friends" },
{ id: "network", label: "Network" }, { id: "network", label: "Network" },
{ id: "activity", label: "Activity" },
] as const; ] as const;
type TabId = (typeof tabs)[number]["id"]; type TabId = (typeof tabs)[number]["id"];
let activeTab = $state<TabId>("general"); let activeTab = $state<TabId>("scene");
let actions: Partial<Record<TabId, PanelActions>> = {}; let actions: Partial<Record<TabId, PanelActions>> = {};
let panelStates = $state<Record<TabId, PanelState>>({ let panelStates = $state<Record<TabId, PanelState>>({
general: { dirty: false, busy: false }, general: { dirty: false, busy: false },
scene: { dirty: false, busy: false }, scene: { dirty: false, busy: false },
friends: { dirty: false, busy: false }, friends: { dirty: false, busy: false },
network: { dirty: false, busy: false }, network: { dirty: false, busy: false },
activity: { dirty: false, busy: false },
}); });
let anyDirty = $derived(tabs.some((tab) => panelStates[tab.id].dirty)); let anyDirty = $derived(tabs.some((tab) => panelStates[tab.id].dirty));
let anyBusy = $derived(tabs.some((tab) => panelStates[tab.id].busy)); let anyBusy = $derived(tabs.some((tab) => panelStates[tab.id].busy));
@@ -87,7 +84,7 @@
type="radio" type="radio"
name="control_panel_tabs" name="control_panel_tabs"
class="tab text-xs" class="tab text-xs"
aria-label={`${tab.label}${panelStates[tab.id].dirty ? " *" : ""}`} aria-label={tab.label}
value={tab.id} value={tab.id}
bind:group={activeTab} bind:group={activeTab}
/> />
@@ -106,7 +103,7 @@
{:else if tab.id === "network"} {:else if tab.id === "network"}
<NetworkPanel {register} /> <NetworkPanel {register} />
{:else} {:else}
<ActivityPanel {register} /> <p>You're not supposed to see this 👀</p>
{/if} {/if}
</div> </div>
</div> </div>