fix: make formatter happy
Some checks failed
📊 Benchmark the Runtime / release (push) Successful in 1m3s
🚀 Lint & Test & Deploy / release (push) Failing after 1m32s

This commit is contained in:
2026-04-20 01:32:30 +02:00
parent 4de15b19c8
commit 5d4e2e9280
21 changed files with 339 additions and 245 deletions

View File

@@ -1,13 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View File

@@ -1,13 +1,13 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en" class="theme-dark">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="text-scale" content="scale" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="text-scale" content="scale" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@@ -80,7 +80,7 @@
{#if rect}
<div
class="highlight fixed z-99999 rounded-md pointer-events-none"
class="highlight pointer-events-none fixed z-99999 rounded-md"
style:top="{rect.top}px"
style:left="{rect.left}px"
style:width="{rect.width}px"
@@ -91,7 +91,8 @@
<style>
@keyframes pulse {
0%, 100% {
0%,
100% {
box-shadow:
0 0 0 9999px rgba(0, 0, 0, 0.45),
0 0 0 2px rgba(255, 255, 255, 0.9),

View File

@@ -13,12 +13,7 @@
onComplete?: () => void;
}
let {
config,
hooks = {},
onStepChange,
onComplete
}: Props = $props();
let { config, hooks = {}, onStepChange, onComplete }: Props = $props();
const AVATAR_SIZE = 80;
const SCREEN_PADDING = 20;
@@ -182,11 +177,7 @@
{#if isActive}
<div class="pointer-events-none fixed inset-0 z-99999">
{#if highlight}
<Highlight
selector={highlight.selector}
hookName={highlight.hookName}
{hooks}
/>
<Highlight selector={highlight.selector} hookName={highlight.hookName} {hooks} />
{/if}
<PlantyAvatar bind:x={avatarX} bind:y={avatarY} {mood} />

View File

@@ -82,14 +82,10 @@
}
const left = $derived(
displayMood === 'talking'
? { px: 0, py: 0 }
: pupilOffset(cursorX, cursorY, 9.5, 30.5)
displayMood === 'talking' ? { px: 0, py: 0 } : pupilOffset(cursorX, cursorY, 9.5, 30.5)
);
const right = $derived(
displayMood === 'talking'
? { px: 0, py: 0 }
: pupilOffset(cursorX, cursorY, 31.5, 35.5)
displayMood === 'talking' ? { px: 0, py: 0 } : pupilOffset(cursorX, cursorY, 31.5, 35.5)
);
</script>
@@ -176,10 +172,10 @@
cursor: grab;
user-select: none;
pointer-events: auto;
filter: drop-shadow(0px 0px 10px black);
filter: drop-shadow(0px 0px 10px black);
transition:
left 0.85s cubic-bezier(0.33, 1, 0.68, 1),
top 0.85s cubic-bezier(0.33, 1, 0.68, 1),
top 0.85s cubic-bezier(0.33, 1, 0.68, 1);
}
.dragging {
@@ -189,49 +185,83 @@
/* idle: steady vertical bob */
@keyframes bob {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
.mood-idle {
animation: bob 2.6s ease-in-out infinite;
}
.mood-happy {
animation: bob 1.8s ease-in-out infinite;
}
.mood-idle { animation: bob 2.6s ease-in-out infinite; }
.mood-happy { animation: bob 1.8s ease-in-out infinite; }
/* thinking: head tilted to the side — clearly different from idle */
@keyframes think {
0%, 100% { transform: rotate(-12deg) translateY(0); }
50% { transform: rotate(-12deg) translateY(-3px); }
0%,
100% {
transform: rotate(-12deg) translateY(0);
}
50% {
transform: rotate(-12deg) translateY(-3px);
}
}
.mood-thinking {
animation: think 2.8s ease-in-out infinite;
}
.mood-thinking { animation: think 2.8s ease-in-out infinite; }
/* talking: subtle head waggle */
@keyframes waggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-2deg) translateY(-1px); }
75% { transform: rotate(2deg) translateY(1px); }
0%,
100% {
transform: rotate(0deg);
}
25% {
transform: rotate(-2deg) translateY(-1px);
}
75% {
transform: rotate(2deg) translateY(1px);
}
}
.mood-talking {
animation: waggle 0.3s ease-in-out infinite;
}
.mood-talking { animation: waggle 0.3s ease-in-out infinite; }
/* moving: forward-lean glide */
@keyframes glide {
0%, 100% { transform: translateY(0) rotate(-6deg); }
50% { transform: translateY(-8px) rotate(-4deg); }
0%,
100% {
transform: translateY(0) rotate(-6deg);
}
50% {
transform: translateY(-8px) rotate(-4deg);
}
}
.mood-moving {
animation: glide 0.4s ease-in-out infinite;
}
.mood-moving { animation: glide 0.4s ease-in-out infinite; }
/* ── Drop shadows ────────────────────────────────────────────────── */
.body {
filter: drop-shadow(1px 1px 0 rgba(0, 0, 0, 0.5));
transition: d 0.12s ease-in-out;
}
.eye-left, .eye-right {
.eye-left,
.eye-right {
filter: drop-shadow(1px 1px 0 rgba(0, 0, 0, 0.5));
}
.mood-talking {
.eye-left, .eye-right {
> g {
transition: transform 0.5s ease-in-out;
}
}
.eye-left,
.eye-right {
> g {
transition: transform 0.5s ease-in-out;
}
}
}
/* ── Leaves ──────────────────────────────────────────────────────── */
@@ -246,74 +276,154 @@
/* idle: slow gentle breathing wave */
@keyframes idle-right {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(-9deg); }
0%,
100% {
transform: rotate(0deg);
}
50% {
transform: rotate(-9deg);
}
}
@keyframes idle-left {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(7deg); }
0%,
100% {
transform: rotate(0deg);
}
50% {
transform: rotate(7deg);
}
}
.mood-idle .leave-right {
animation: idle-right 3s ease-in-out infinite;
}
.mood-idle .leave-left {
animation: idle-left 3s ease-in-out infinite 0.15s;
}
.mood-idle .leave-right { animation: idle-right 3s ease-in-out infinite; }
.mood-idle .leave-left { animation: idle-left 3s ease-in-out infinite 0.15s; }
/* thinking: wings held raised, minimal drift */
@keyframes think-right {
0%, 100% { transform: rotate(-14deg); }
50% { transform: rotate(-10deg); }
0%,
100% {
transform: rotate(-14deg);
}
50% {
transform: rotate(-10deg);
}
}
@keyframes think-left {
0%, 100% { transform: rotate(10deg); }
50% { transform: rotate(7deg); }
0%,
100% {
transform: rotate(10deg);
}
50% {
transform: rotate(7deg);
}
}
.mood-thinking .leave-right {
animation: think-right 4s ease-in-out infinite;
}
.mood-thinking .leave-left {
animation: think-left 4s ease-in-out infinite 0.3s;
}
.mood-thinking .leave-right { animation: think-right 4s ease-in-out infinite; }
.mood-thinking .leave-left { animation: think-left 4s ease-in-out infinite 0.3s; }
/* talking: nearly still — tiny passive counter-sway */
@keyframes talk-right {
0%, 100% { transform: rotate(-2deg); }
50% { transform: rotate(2deg); }
0%,
100% {
transform: rotate(-2deg);
}
50% {
transform: rotate(2deg);
}
}
@keyframes talk-left {
0%, 100% { transform: rotate(2deg); }
50% { transform: rotate(-2deg); }
0%,
100% {
transform: rotate(2deg);
}
50% {
transform: rotate(-2deg);
}
}
.mood-talking .leave-right {
animation: talk-right 0.6s ease-in-out infinite;
}
.mood-talking .leave-left {
animation: talk-left 0.6s ease-in-out infinite 0.1s;
}
.mood-talking .leave-right { animation: talk-right 0.6s ease-in-out infinite; }
.mood-talking .leave-left { animation: talk-left 0.6s ease-in-out infinite 0.1s; }
/* happy: light casual flap */
@keyframes happy-right {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(-18deg); }
0%,
100% {
transform: rotate(0deg);
}
50% {
transform: rotate(-18deg);
}
}
@keyframes happy-left {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(13deg); }
0%,
100% {
transform: rotate(0deg);
}
50% {
transform: rotate(13deg);
}
}
.mood-happy .leave-right {
animation: happy-right 1.4s ease-in-out infinite;
}
.mood-happy .leave-left {
animation: happy-left 1.4s ease-in-out infinite 0.1s;
}
.mood-happy .leave-right { animation: happy-right 1.4s ease-in-out infinite; }
.mood-happy .leave-left { animation: happy-left 1.4s ease-in-out infinite 0.1s; }
/* moving: vigorous wing flap — full range, fast */
@keyframes flap-right {
0%, 100% { transform: rotate(0deg); }
40% { transform: rotate(-40deg); }
0%,
100% {
transform: rotate(0deg);
}
40% {
transform: rotate(-40deg);
}
}
@keyframes flap-left {
0%, 100% { transform: rotate(0deg); }
40% { transform: rotate(26deg); }
0%,
100% {
transform: rotate(0deg);
}
40% {
transform: rotate(26deg);
}
}
.mood-moving .leave-right {
animation: flap-right 0.34s ease-in-out infinite;
}
.mood-moving .leave-left {
animation: flap-left 0.34s ease-in-out infinite 0.04s;
}
.mood-moving .leave-right { animation: flap-right 0.34s ease-in-out infinite; }
.mood-moving .leave-left { animation: flap-left 0.34s ease-in-out infinite 0.04s; }
/* ── Eye blink (on pupil so it doesn't fight cursor translate) ───── */
@keyframes blink {
0%, 93%, 100% { transform: scaleY(1); }
96% { transform: scaleY(0.05); }
0%,
93%,
100% {
transform: scaleY(1);
}
96% {
transform: scaleY(0.05);
}
}
.pupil {
transform-box: fill-box;
transform-origin: center;
animation: blink 4s ease-in-out infinite;
}
.eye-left .pupil { animation-delay: 0s; }
.eye-right .pupil { animation-delay: 0.07s; }
.eye-left .pupil {
animation-delay: 0s;
}
.eye-right .pupil {
animation-delay: 0.07s;
}
</style>

View File

@@ -55,7 +55,7 @@
'<code class="text-[11px] rounded px-1 font-mono" style="background: var(--color-layer-3); color: var(--color-text);">$1</code>'
)
.replaceAll(/\*/, '')
.replaceAll(/\_/, '')
.replaceAll(/_/, '')
.replaceAll(/\n+/g, '<br>');
}
@@ -73,7 +73,6 @@
if (i < target.length) {
displayed = target.slice(0, ++i);
typeTimer = setTimeout(tick, 26);
} else {
}
}
// Defer first tick so no reads happen during the synchronous effect body
@@ -86,11 +85,11 @@
</script>
<div
class="fixed p-2 z-99999 pointer-events-auto rounded-md border"
class="pointer-events-auto fixed z-99999 rounded-md border p-2"
style:width="{BUBBLE_WIDTH}px"
style:left="{left}px"
style:bottom={bottom}
style:top={top}
style:bottom
style:top
style:background="var(--color-layer-0)"
style:border-color="var(--color-outline)"
>
@@ -98,7 +97,10 @@
<!-- Tail pointing up toward avatar -->
<div
class="absolute -top-2 h-3.5 w-3.5 rotate-45 border-t border-l"
style:left="{Math.min(Math.max(avatarX - left + AVATAR_SIZE / 2 - 25, 12), BUBBLE_WIDTH - 28)}px"
style:left="{Math.min(
Math.max(avatarX - left + AVATAR_SIZE / 2 - 25, 12),
BUBBLE_WIDTH - 28
)}px"
style:background="var(--color-layer-0)"
style:border-color="var(--color-outline)"
>
@@ -106,26 +108,29 @@
{:else}
<!-- Tail pointing down toward avatar -->
<div
class="absolute -bottom-2 h-3.5 w-3.5 rotate-45 border-b border-r"
style:left="{Math.min(Math.max(avatarX - left + AVATAR_SIZE / 2 - 25, 12), BUBBLE_WIDTH - 28)}px"
class="absolute -bottom-2 h-3.5 w-3.5 rotate-45 border-r border-b"
style:left="{Math.min(
Math.max(avatarX - left + AVATAR_SIZE / 2 - 25, 12),
BUBBLE_WIDTH - 28
)}px"
style:background="var(--color-layer-0)"
style:border-color="var(--color-outline)"
>
</div>
{/if}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="mb-2 min-h-[1.4em] text-sm leading-relaxed" style="color: var(--color-text)">
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html renderMarkdown(displayed)}
</div>
{#if choices.length > 0}
<div class="flex flex-col gap-1.5">
{#each choices as choice, i}
{#each choices as choice, i (choice.label)}
{#if finished}
<button
in:fade={{ duration: 200, delay: i * 250 }}
class="rounded-lg px-3 py-1.5 text-left text-sm font-medium transition-colors cursor-pointer"
class="cursor-pointer rounded-lg px-3 py-1.5 text-left text-sm font-medium transition-colors"
style:background="var(--color-layer-1)"
style:border-color="var(--color-outline)"
style:color="var(--color-text)"
@@ -138,9 +143,9 @@
</div>
{/if}
<div class="flex items-center justify-between gap-2 mt-2">
<div class="mt-2 flex items-center justify-between gap-2">
<button
class="text-xs transition-colors cursor-pointer"
class="cursor-pointer text-xs transition-colors"
style="color: var(--color-outline)"
onclick={onClose}
>
@@ -154,7 +159,7 @@
{/if}
{#if showNext && finished}
<button
class="rounded-lg px-3 py-1 text-xs font-semibold cursor-pointer transition-colors"
class="cursor-pointer rounded-lg px-3 py-1 text-xs font-semibold transition-colors"
style:background="var(--color-outline)"
style:color="var(--color-layer-0)"
onclick={onNext}

View File

@@ -18,49 +18,49 @@ import type { DialogNode, StepCallback } from './types.js';
* <Planty {config} {steps} />
*/
export class PlantySteps {
private _before = new Map<string, StepCallback[]>();
private _after = new Map<string, StepCallback[]>();
private _before = new Map<string, StepCallback[]>();
private _after = new Map<string, StepCallback[]>();
/** Register a handler to run before `nodeId` becomes active. Chainable. */
before(nodeId: string, fn: StepCallback): this {
const list = this._before.get(nodeId) ?? [];
this._before.set(nodeId, [...list, fn]);
return this;
}
/** Register a handler to run before `nodeId` becomes active. Chainable. */
before(nodeId: string, fn: StepCallback): this {
const list = this._before.get(nodeId) ?? [];
this._before.set(nodeId, [...list, fn]);
return this;
}
/** Register a handler to run after the user leaves `nodeId`. Chainable. */
after(nodeId: string, fn: StepCallback): this {
const list = this._after.get(nodeId) ?? [];
this._after.set(nodeId, [...list, fn]);
return this;
}
/** Register a handler to run after the user leaves `nodeId`. Chainable. */
after(nodeId: string, fn: StepCallback): this {
const list = this._after.get(nodeId) ?? [];
this._after.set(nodeId, [...list, fn]);
return this;
}
/** Remove all handlers for a node (or all nodes if omitted). */
clear(nodeId?: string) {
if (nodeId) {
this._before.delete(nodeId);
this._after.delete(nodeId);
} else {
this._before.clear();
this._after.clear();
}
}
/** Remove all handlers for a node (or all nodes if omitted). */
clear(nodeId?: string) {
if (nodeId) {
this._before.delete(nodeId);
this._after.delete(nodeId);
} else {
this._before.clear();
this._after.clear();
}
}
/** @internal — called by Planty */
async runBefore(nodeId: string, node: DialogNode): Promise<void> {
for (const fn of this._before.get(nodeId) ?? []) {
await fn(nodeId, node);
}
}
/** @internal — called by Planty */
async runBefore(nodeId: string, node: DialogNode): Promise<void> {
for (const fn of this._before.get(nodeId) ?? []) {
await fn(nodeId, node);
}
}
/** @internal — called by Planty */
async runAfter(nodeId: string, node: DialogNode): Promise<void> {
for (const fn of this._after.get(nodeId) ?? []) {
await fn(nodeId, node);
}
}
/** @internal — called by Planty */
async runAfter(nodeId: string, node: DialogNode): Promise<void> {
for (const fn of this._after.get(nodeId) ?? []) {
await fn(nodeId, node);
}
}
}
export function createPlantySteps(): PlantySteps {
return new PlantySteps();
return new PlantySteps();
}

View File

@@ -34,7 +34,7 @@
>
<!-- Header -->
<header
class="flex items-center gap-4 px-8 py-5 h-12"
class="flex h-12 items-center gap-4 px-8 py-5"
style="border-color: var(--color-outline);"
>
<h1 class="text-xl font-semibold">🌿 Planty</h1>
@@ -75,11 +75,17 @@
<PlantyAvatar x={0} y={0} mood={previewMood} />
</div>
<div class="flex gap-2">
{#each moods as m}
{#each moods as m (m)}
<button
class="rounded-lg border px-3 py-1 text-xs transition"
onclick={() => (previewMood = m)}
style="border-color: {previewMood === m ? 'var(--color-selected)' : 'var(--color-outline)'}; color: {previewMood === m ? 'var(--color-selected)' : 'var(--color-text)'}; background: {previewMood === m ? 'var(--color-layer-2)' : 'transparent'};"
style="border-color: {previewMood === m
? 'var(--color-selected)'
: 'var(--color-outline)'}; color: {previewMood === m
? 'var(--color-selected)'
: 'var(--color-text)'}; background: {previewMood === m
? 'var(--color-layer-2)'
: 'transparent'};"
>
{m}
</button>
@@ -95,7 +101,7 @@
style="border-color: var(--color-outline); background-color: var(--color-layer-0);"
>
<span
class="text-xs font-semibold uppercase tracking-widest"
class="text-xs font-semibold tracking-widest uppercase"
style="color: var(--color-outline);"
>Parameters</span>
<div
@@ -117,7 +123,7 @@
Leaf density: 0.6
</div>
<span
class="mt-2 text-xs font-semibold uppercase tracking-widest"
class="mt-2 text-xs font-semibold tracking-widest uppercase"
style="color: var(--color-outline);"
>Export</span>
<div
@@ -139,4 +145,3 @@
}}
/>
{/if}

View File

@@ -12,11 +12,8 @@
'custom'
];
let { theme = $bindable() } = $props();
let themeIndex = $state(0);
$effect(() => {
theme = themes[themeIndex];
const classList = document.documentElement.classList;
for (const c of classList) {
if (c.startsWith('theme-')) document.documentElement.classList.remove(c);

View File

@@ -1,7 +1,7 @@
@import "tailwindcss";
@import 'tailwindcss';
body {
color: var(--color-text);
background-color: var(--color-layer-0);
margin: 0;
color: var(--color-text);
background-color: var(--color-layer-0);
margin: 0;
}