feat: improve planty ux
This commit is contained in:
@@ -9,11 +9,12 @@
|
||||
interface Props {
|
||||
config: PlantyConfig;
|
||||
hooks?: Record<string, PlantyHook>;
|
||||
actions?: Record<string, PlantyHook>;
|
||||
onStepChange?: (nodeId: string, node: DialogNode) => void;
|
||||
onComplete?: () => void;
|
||||
}
|
||||
|
||||
let { config, hooks = {}, onStepChange, onComplete }: Props = $props();
|
||||
let { config, actions = {}, hooks = {}, onStepChange, onComplete }: Props = $props();
|
||||
|
||||
const AVATAR_SIZE = 80;
|
||||
const SCREEN_PADDING = 20;
|
||||
@@ -22,6 +23,7 @@
|
||||
let isActive = $state(false);
|
||||
let currentNodeId = $state<string | null>(null);
|
||||
let bubbleVisible = $state(false);
|
||||
let avatar = $state<PlantyAvatar>(null!);
|
||||
let avatarX = $state(0);
|
||||
let avatarY = $state(0);
|
||||
let mood = $state<Mood>('idle');
|
||||
@@ -30,6 +32,9 @@
|
||||
|
||||
// ── Derived ──────────────────────────────────────────────────────────
|
||||
const runner = $derived(new DialogRunner(config));
|
||||
const nextNode = $derived(
|
||||
runner.getNextNode(currentNodeId ?? '')
|
||||
);
|
||||
const mainPath = $derived(runner.getMainPath());
|
||||
const currentNode = $derived<DialogNode | null>(
|
||||
currentNodeId ? runner.getNode(currentNodeId) : null
|
||||
@@ -125,32 +130,31 @@
|
||||
if (node.position) {
|
||||
mood = 'moving';
|
||||
const pos = resolvePosition(node.position);
|
||||
const hasChanges = pos.x !== avatarX || pos.y !== avatarY;
|
||||
avatarX = pos.x;
|
||||
avatarY = pos.y;
|
||||
await _wait(900);
|
||||
if (hasChanges) await _wait(900);
|
||||
}
|
||||
|
||||
mood = 'talking';
|
||||
bubbleVisible = true;
|
||||
|
||||
// App hook
|
||||
if (node.hook && hooks[node.hook]) {
|
||||
const result = await hooks[node.hook](...(node.hookArgs ?? []));
|
||||
if (node.action && actions[node.action]) {
|
||||
const result = await actions[node.action]();
|
||||
if (typeof result === 'function') actionCleanup = result as () => void;
|
||||
}
|
||||
|
||||
// Auto-advance
|
||||
if (typeof node.waitFor === 'number') {
|
||||
autoAdvanceTimer = setTimeout(() => next(), node.waitFor);
|
||||
}
|
||||
if (node.waitFor === 'action') {
|
||||
const actionHook = hooks[`action:${id}`];
|
||||
if (actionHook) {
|
||||
const advance = () => next();
|
||||
const result = await actionHook(advance, ...(node.hookArgs ?? []));
|
||||
if (typeof result === 'function') actionCleanup = result as () => void;
|
||||
}
|
||||
const actionHook = hooks[`action:${id}`];
|
||||
if (actionHook) {
|
||||
const advance = () => {
|
||||
avatar.flash('happy', 2000);
|
||||
next();
|
||||
};
|
||||
const result = await actionHook(advance);
|
||||
if (typeof result === 'function') actionCleanup = result as () => void;
|
||||
}
|
||||
|
||||
if (!node.choices && !node.next) {
|
||||
setTimeout(() => stop(), 3000);
|
||||
}
|
||||
@@ -176,11 +180,12 @@
|
||||
|
||||
{#if isActive}
|
||||
<div class="pointer-events-none fixed inset-0 z-99999">
|
||||
<span>{currentNodeId}</span>
|
||||
{#if highlight}
|
||||
<Highlight selector={highlight.selector} hookName={highlight.hookName} {hooks} />
|
||||
{/if}
|
||||
|
||||
<PlantyAvatar bind:x={avatarX} bind:y={avatarY} {mood} />
|
||||
<PlantyAvatar bind:this={avatar} bind:x={avatarX} bind:y={avatarY} {mood} />
|
||||
|
||||
{#if showBubble && currentNode}
|
||||
<SpeechBubble
|
||||
@@ -188,7 +193,7 @@
|
||||
{avatarX}
|
||||
{avatarY}
|
||||
choices={currentNode.choices || []}
|
||||
showNext={currentNode.waitFor === 'click'}
|
||||
showNext={nextNode !== null}
|
||||
{stepIndex}
|
||||
{totalSteps}
|
||||
onNext={next}
|
||||
|
||||
@@ -69,6 +69,12 @@
|
||||
cursorY = e.clientY;
|
||||
}
|
||||
|
||||
export function flash(flashMood: Mood, duration = 500) {
|
||||
const prev = displayMood;
|
||||
mood = flashMood;
|
||||
setTimeout(() => (mood = prev), duration);
|
||||
}
|
||||
|
||||
function pupilOffset(cx: number, cy: number, eyeSvgX: number, eyeSvgY: number, maxPx = 2.8) {
|
||||
const ex = x + eyeSvgX;
|
||||
const ey = y + eyeSvgY;
|
||||
|
||||
@@ -22,12 +22,9 @@ export interface DialogNode {
|
||||
position?: AvatarPosition;
|
||||
highlight?: HighlightTarget;
|
||||
/** App hook to call on entering this node */
|
||||
hook?: string;
|
||||
hookArgs?: unknown[];
|
||||
action?: string;
|
||||
next?: string | null;
|
||||
choices?: Choice[];
|
||||
/** 'click' = wait for user click, number = auto-advance after N ms, 'action' = wait for hook to call advance() */
|
||||
waitFor?: 'click' | 'action' | number;
|
||||
/** Called (and awaited) just before the avatar starts moving to this node */
|
||||
before?: StepCallback;
|
||||
/** Called (and awaited) just before the user leaves this node */
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import '@nodarium/ui/app.css';
|
||||
import './layout.css';
|
||||
|
||||
const { children } = $props();
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user