kripke: add share buttons

This commit is contained in:
2025-02-19 22:28:34 +09:00
parent 2901d39d30
commit 095791a09f
5 changed files with 106 additions and 16 deletions

View File

@@ -17,6 +17,8 @@ export const buttonVariants = tv({
"border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm",
foreground:
"bg-foreground text-background hover:bg-foreground/80 shadow-sm",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},

View File

@@ -2,12 +2,14 @@
import { Button } from "$lib/components/ui/button";
import * as Dialog from "$lib/components/ui/dialog";
import { type Formula, isomorphic, validWorlds } from "@cannorin/kripke";
import LuRotateCw from "lucide-svelte/icons/rotate-cw";
import LuX from "lucide-svelte/icons/x";
import { onMount } from "svelte";
import FrameInput from "./frame-input.svelte";
import Game, { type GameStatus, type Move } from "./game.svelte";
import Rules from "./rules.svelte";
import Share from "./share.svelte";
import { daily } from "./store";
import { getDailyFrame, getTimeUntilNextGame } from "./system";
@@ -82,12 +84,20 @@ onMount(() => {
<FrameInput class="pb-6" disabled width={250} height={250} frame={frame} />
</div>
<Dialog.Footer class="gap-x-1 gap-y-2">
<Button
variant="outline"
onclick={() => (dialogOpen = false)}>
<LuX class="w-4 h-4 mt-[2px]" /> Close
</Button>
<Dialog.Footer>
<div class="flex flex-col md:flex-row gap-2 w-full justify-end">
<Share date={$daily.date} moves={$daily.moves} status={status} />
<Button
href="/kripke/random"
data-sveltekit-reload>
<LuRotateCw class="w-4 h-4 mt-[2px]" /> Play Random Challenge
</Button>
<Button
variant="foreground"
onclick={() => (dialogOpen = false)}>
<LuX class="w-4 h-4 mt-[2px]" /> Close
</Button>
</div>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

View File

@@ -200,10 +200,8 @@ const colors: Record<number, string> = {
</li>
{/if}
{#if status === "win"}
<li class="flex flex-col items-center gap-2 rounded bg-primary text-background p-5 animate-fade-in">
<li class="flex flex-col items-center gap-2 rounded bg-green-700 text-background p-5 animate-fade-in">
<p class="text-xl font-bold">YOU WIN!</p>
<p class="text-sm">Play <a class="underline font-medium" href="/kripke/random">random challenge</a>?</p>
</li>
{:else if status === "lose"}
{#await getAnswer() then answerId}
@@ -216,7 +214,6 @@ const colors: Record<number, string> = {
<span class="text-xs text-muted self-start px-2 py-1">id: {answerId}</span>
<FrameInput class="pb-6" disabled width={250} height={250} frame={getFrame(answerId)} />
</div>
<p class="text-sm self-end">Play <a class="underline font-medium" href="/kripke/random">random challenge</a>?</p>
</li>
{/await}
{/if}

View File

@@ -2,11 +2,13 @@
import { Button } from "$lib/components/ui/button";
import * as Dialog from "$lib/components/ui/dialog";
import { type Formula, isomorphic, validWorlds } from "@cannorin/kripke";
import LuRotateCw from "lucide-svelte/icons/rotate-cw";
import LuX from "lucide-svelte/icons/x";
import FrameInput from "../frame-input.svelte";
import Game, { type GameStatus, type Move } from "../game.svelte";
import Rules from "../rules.svelte";
import Share from "../share.svelte";
import { getRandomFrame } from "../system";
let { data } = $props();
@@ -78,12 +80,20 @@ $effect(() => {
<FrameInput class="pb-6" disabled width={250} height={250} frame={frame} />
</div>
<Dialog.Footer class="gap-x-1 gap-y-2">
<Button
variant="outline"
onclick={() => (dialogOpen = false)}>
<LuX class="w-4 h-4 mt-[2px]" /> Close
</Button>
<Dialog.Footer>
<div class="flex flex-col md:flex-row gap-2 w-full justify-end">
<Share seed={seed} moves={moves} status={status} />
<Button
href="/kripke/random"
data-sveltekit-reload>
<LuRotateCw class="w-4 h-4 mt-[2px]" /> Play New Game
</Button>
<Button
variant="foreground"
onclick={() => (dialogOpen = false)}>
<LuX class="w-4 h-4 mt-[2px]" /> Close
</Button>
</div>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

View File

@@ -0,0 +1,71 @@
<script lang="ts">
import SiBluesky from "@icons-pack/svelte-simple-icons/icons/SiBluesky";
import SiMastodon from "@icons-pack/svelte-simple-icons/icons/SiMastodon";
import SiMisskey from "@icons-pack/svelte-simple-icons/icons/SiMisskey";
import SiX from "@icons-pack/svelte-simple-icons/icons/SiX";
import type { GameStatus, Move } from "./game.svelte";
let {
date,
moves,
status,
seed,
}: { moves: Move[]; status: GameStatus } & (
| { date: string; seed?: undefined }
| { seed: number; date?: undefined }
) = $props();
const numberEmojis = ["0⃣", "1⃣", "2⃣", "3⃣", "4⃣"];
let shareText = $derived.by(() => {
const history = moves
.map((move) => {
switch (move.type) {
case "guess": {
return move.correct ? "✅" : "❎";
}
case "check": {
return numberEmojis[move.valid];
}
}
})
.join("");
const text = `#KRIPKE_game (${date ?? ""}${seed ? `seed ${seed}` : ""})
${history} ${status === "win" ? moves.length : "X"}/10
https://www.cannorin.net/kripke${seed ? `/random?seed=${seed}` : ""}`;
return encodeURIComponent(text);
});
</script>
<nav class="h-9 flex items-center justify-between gap-4 mr-auto">
<ul class="flex items-center gap-4">
<li>
<a href="https://x.com/intent/tweet?text={shareText}" target="_blank" rel="noopener noreferrer">
<SiX size={20} />
<span class="sr-only"> Share to X (Twitter)</span>
</a>
</li>
<li>
<a href="https://mastoshare.net/share?text={shareText}" target="_blank" rel="noopener noreferrer">
<SiMastodon size={20} />
<span class="sr-only"> Share to Mastodon</span>
</a>
</li>
<li>
<a href="https://misskeyshare.link/share.html?text={shareText}" target="_blank" rel="noopener noreferrer">
<SiMisskey size={20} />
<span class="sr-only"> Share to Misskey</span>
</a>
</li>
<li>
<a href="https://bsky.app/intent/compose?text={shareText}" target="_blank" rel="noopener noreferrer">
<SiBluesky size={20} />
<span class="sr-only"> Share to Bluesky</span>
</a>
</li>
</ul>
</nav>