From 927eb984ffcdf9f835d77c013cf6af737e6968c0 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Tue, 17 Dec 2024 03:44:51 -0500 Subject: [PATCH] [+] Upload to server --- bun.lockb | Bin 46236 -> 46985 bytes package.json | 1 + src/App.svelte | 30 +++++--- src/Launcher.svelte | 9 ++- src/app.sass | 38 ++-------- src/aqua.sass | 148 ++++++++++++++++++++++++++++++++++++++ src/lib/PuzzleInfo.svelte | 28 ++++++++ src/lib/Upload.svelte | 58 +++++++++++++++ src/main.ts | 1 + src/utils.ts | 7 +- 10 files changed, 278 insertions(+), 42 deletions(-) create mode 100644 src/aqua.sass create mode 100644 src/lib/PuzzleInfo.svelte create mode 100644 src/lib/Upload.svelte diff --git a/bun.lockb b/bun.lockb index d2053e0f78180af337adcd61ef3d426b253f51b9..4906c0a3e51700d85c07a09c2ea1bc0c337d3453 100644 GIT binary patch delta 6122 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 v;5NFrZZgHJj|q7$K>6EUm6ULjuk`E7Xj4p#&r boolean, op: (st: number) => number) { @@ -198,7 +201,7 @@ updateArea.forEach(([dx, dy, _]) => checkPos(x + dx, y + dy)) } - async function editModeReduce(zeros: boolean = false, n = 20) { + async function editModeReduce(zeros: boolean = false, n = 10) { [solutionHStates, solutionVStates] = [hStates.slice(), vStates.slice()] if (n === 0) return // On each iteration, randomly mask n numbers and try to solve the puzzle @@ -221,8 +224,9 @@ vertStates.some((st, idx) => (st === eStates.selected) !== (solutionVStates[idx] === eStates.selected))) { masked.forEach(idx => nMask[idx] = 0) console.log('Solution does not match') - setTimeout(() => editModeReduce(zeros, n - 1), 100) - } else setTimeout(() => editModeReduce(zeros, n), 100) + } + // setTimeout(() => editModeReduce(zeros, n - 1), 100) + // } else setTimeout(() => editModeReduce(zeros, n), 100) console.log(nMask) } @@ -284,14 +288,18 @@ Logo
- Welcome to SlitherLink! 🧩 The rules are simple: Draw lines between the dots to create one big loop (no crossings, no branches). The numbers are your hints – they tell you how many lines should surround them. Left-click to draw, right-click to mark with an X. Can you crack the perfect path? + Welcome to SlitherLink! 🧩 The rules are simple: Draw lines between the dots to create one big loop (no crossings, no branches). The numbers are your hints – they tell you how many lines should surround them. Left-click to draw, right-click to mark with an X. Can you crack the perfect path? YouTube Tutorial
{#if statusMsg}
{statusMsg}
{/if} - {#if !editMode}
{Fmt.duration(elapsed)}
{/if} +
+ {#if puzzleData && !editMode}
+ +
{Fmt.duration(elapsed)}
+
{/if}
dragging && clickDiv(e)} on:mouseup={() => dragging = false} @@ -313,6 +321,7 @@ {/each} {/each}
+
{#if editMode}
@@ -338,7 +347,6 @@ }}>Clear Numbers - {/if}
@@ -354,6 +362,10 @@ {/each} {/if} +
+ + +
{/if} @@ -386,4 +398,6 @@ {/if} + + {#if upload} upload = null}/> {/if} diff --git a/src/Launcher.svelte b/src/Launcher.svelte index a295256..d0198a2 100644 --- a/src/Launcher.svelte +++ b/src/Launcher.svelte @@ -5,15 +5,20 @@ // Get puzzle id from params const urlParams = new URLSearchParams(window.location.search); const puzzleId = urlParams.get("puzzle"); + const edit = urlParams.get("edit"); let puzzleData: Checkpoint | null = null; let error: string | null = null; - if (!puzzleId) window.location.href = "/?puzzle=meow"; - else Backend.get(puzzleId).then((data) => (puzzleData = data)).catch(e => (error = e)); + if (!edit) { + if (!puzzleId) window.location.href = "/?puzzle=meow"; + else Backend.get(puzzleId).then((data) => (puzzleData = data)).catch(e => (error = e)); + } {#if puzzleData !== null && puzzleId !== null} +{:else if edit} + {:else}
diff --git a/src/app.sass b/src/app.sass index c34cf68..f894451 100644 --- a/src/app.sass +++ b/src/app.sass @@ -89,8 +89,8 @@ main height: 1em .rules - max-width: 900px - opacity: 0.9 + font-size: 0.9em + max-width: 800px .btn-div display: flex @@ -118,9 +118,6 @@ main font-family: 'Teko', sans-serif font-size: 2em color: colors.$accent - margin-bottom: -24px - - // Larger gap between letters letter-spacing: 0.3em .status @@ -129,30 +126,9 @@ main &.error color: colors.$cross -.overlay - position: fixed - inset: 0 - - background-color: rgba(0, 0, 0, 0.5) - +.timer-line + // Two items, left and right display: flex - justify-content: center - align-items: center - - z-index: 1000 - backdrop-filter: blur(5px) - - h2, p - user-select: none - margin: 0 - - > div - background-color: colors.$background - padding: 2rem - border-radius: 8px - - display: flex - flex-direction: column - gap: 1rem - - max-width: 400px + justify-content: space-between + gap: 10px + margin-bottom: -12px diff --git a/src/aqua.sass b/src/aqua.sass new file mode 100644 index 0000000..6c55197 --- /dev/null +++ b/src/aqua.sass @@ -0,0 +1,148 @@ +@use "colors" + +$font: Quicksand, Inter, LXGW Wenkai, Microsoft YaHei, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Avenir, Helvetica, Arial, sans-serif +$c-main: #b3c6ff +$c-sub: rgba(0, 0, 0, 0.77) +$c-good: #b3ffb9 +$c-darker: #646cff +$c-bg: #242424 +$c-error: #ff6b6b +$c-shadow: rgba(0, 0, 0, 0.1) + +$ov-light: rgba(white, 0.04) +$ov-lighter: rgba(white, 0.08) +$ov-dark: rgba(black, 0.1) +$ov-darker: rgba(black, 0.18) + +$nav-height: 4rem +$w-mobile: 560px +$w-max: 900px + +$grad-special: linear-gradient(90deg, #ffee94, #ffb798, #ffa3e5, #ebff94) + +$border-radius: 12px +$gap: 20px + +$transition: all 0.25s + +.card + display: flex + flex-direction: column + + border-radius: $border-radius + padding: 12px 16px + background: $ov-light + +blockquote + $c1: rgba(255, 149, 149, 0.05) + $c2: rgba(255, 152, 152, 0.12) + background: repeating-linear-gradient(45deg, $c1, $c1 10px, $c2 10px, $c2 20px) + padding: 10px 20px 10px 20px + margin: 16px 0 + border-left: solid #ff7c7c 3px + border-radius: $border-radius + +input, textarea + border-radius: $border-radius + border: 1px solid transparent + padding: 0.6em 1.2em + font-size: 1em + font-weight: 500 + font-family: inherit + background-color: $ov-lighter + transition: $transition + box-sizing: border-box + resize: vertical + &:focus, &:focus-visible + border: 1px solid $c-main + outline: none + &.error + border: 1px solid $c-error + +select + border-radius: $border-radius + border: 1px solid transparent + padding: 0.6em 1.2em + font-size: 1em + font-weight: 500 + font-family: inherit + background-color: $ov-lighter + transition: $transition + box-sizing: border-box + +input[type="checkbox"] + width: 1.2em + height: 1.2em + margin: 0 + padding: 0 + border: 1px solid $c-main + background-color: $ov-lighter + appearance: none + cursor: pointer + flex-shrink: 0 + + &:checked + background-color: $c-main + border-color: $c-main + +label + cursor: pointer + +.overlay + position: fixed + inset: 0 + + background-color: rgba(0, 0, 0, 0.5) + + display: flex + justify-content: center + align-items: center + + z-index: 1000 + backdrop-filter: blur(5px) + + h2, p + user-select: none + margin: 0 + + > div + background-color: colors.$background + padding: 2rem + border-radius: 8px + + display: flex + flex-direction: column + gap: 1rem + + min-width: 300px + max-width: 400px + +.actions + display: flex + flex-direction: column + gap: 16px + + button + width: 100% + +.loading.overlay + font-size: 28rem + + :global(.icon) + opacity: 0.5 + + > span + position: absolute + inset: 0 + display: flex + justify-content: center + align-items: center + background: transparent + + letter-spacing: 20px + margin-left: 20px + + font-size: 1.5rem + +.error + color: $c-error \ No newline at end of file diff --git a/src/lib/PuzzleInfo.svelte b/src/lib/PuzzleInfo.svelte new file mode 100644 index 0000000..aea9725 --- /dev/null +++ b/src/lib/PuzzleInfo.svelte @@ -0,0 +1,28 @@ + + +
+
+ {puzzleData?.name} + by {puzzleData?.author} +
+
+ {puzzleData?.description} +
+
+ + \ No newline at end of file diff --git a/src/lib/Upload.svelte b/src/lib/Upload.svelte new file mode 100644 index 0000000..5cce9a6 --- /dev/null +++ b/src/lib/Upload.svelte @@ -0,0 +1,58 @@ + + +{#if data} +
+
+ {#if !id} +

Upload Puzzle

+ + {#if error} +

{error}

+ {:else} +

Upload your puzzle to the server

+ {/if} + + + + + + {:else} +

Uploaded 🥳

+ Your puzzle has been uploaded to the server + +

You can share it by the following link:

+

{url}

+

Thank you for your creation!

+ + {/if} +
+
+{/if} diff --git a/src/main.ts b/src/main.ts index c1e2e93..87ab958 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { mount } from 'svelte' import './app.sass' +import './aqua.sass' import './puzzle.sass' import './fonts/caveat.sass' import Launcher from './Launcher.svelte' diff --git a/src/utils.ts b/src/utils.ts index e2b721f..3b550c9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,6 +3,8 @@ export type i8s = Int8Array export interface Checkpoint { rows: number, cols: number, hStates: i8s, vStates: i8s, hColors: i8s, vColors: i8s, numbers: i8s, nMask: i8s, colors: string[] } +export interface MetaCheckpoint extends Checkpoint { name: string, author: string, description: string, date?: string } + export const cfg = { cellW: 20, lineW: 4, @@ -75,7 +77,10 @@ export const Backend = { if (!r.ok) throw new Error(`Failed to get puzzle: ${r.status}`) return r.text() }).then(JsonTy.parse), - post: (data: Checkpoint) => fetch(`${cfg.backend}/`, { method: "post", body: JsonTy.stringify(data) }), + post: (data: MetaCheckpoint) => fetch(`${cfg.backend}/`, { method: "post", body: JsonTy.stringify(data) }).then(r => { + if (!r.ok) throw new Error(`Failed to post puzzle: ${r.status}`) + return r.text() + }).then(JsonTy.parse), } export const Misc = {