fixes, qol and stuff

This commit is contained in:
2026-07-21 12:30:53 +01:00
parent 8acc692b08
commit 5ffebe4895
22 changed files with 611 additions and 66 deletions
+23
View File
@@ -0,0 +1,23 @@
<svelte:options customElement="scubling-card" />
<script lang="ts">
export let title: string = "";
export let description: string = "";
</script>
<div class="scubling-card">
{#if title}
<h3>{title}</h3>
{/if}
{#if description}
<p>{description}</p>
{/if}
<slot />
</div>
<style>
.scubling-card {
padding: 1rem;
border: 2px solid currentColor;
}
</style>
+19 -8
View File
@@ -1,22 +1,32 @@
<script lang="ts">
let meow = "";
<script module lang="ts">
export { storeButton };
import gumroad from "./sidebar/gumroad.png";
import jinxxy from "./sidebar/jinxxy.png";
</script>
{#snippet StoreButton(store: string, url: string)}
<a aria-label="Store Page Button" href={url}"></a>
{#snippet storeButton(store: string, url: string)}
<a aria-label="Store Page Button" class="StoreButton" href={url}>
{#if store == "gumroad"}
<img src={gumroad} />
{:else if store == "jinxxy"}
<img src={jinxxy} />
{/if}
</a>
{/snippet}
<style lang="scss">
.StoreButton {
display: block;
width: fit-content;
height: 30px;
border: 2px outset #1da1f2;
width: 100%;
height: 45px;
border: 3px outset #1da1f2;
background: #1da1f2;
margin: 2px;
padding: 2px;
text-align: center;
img {
height: auto;
width: 45px;
}
}
.StoreButton:hover {
@@ -27,6 +37,7 @@ let meow = "";
border-style: inset;
img {
transform: translate(2px, 2px);
}
}
</style>
View File
+10
View File
@@ -0,0 +1,10 @@
<svelte:options customElement="hint-box" />
<script>
let { name = "world" } = $props();
import "./Commons.scss";
</script>
<h1>Hello {name}!</h1>
<a>meow</a>
<slot />
+4 -2
View File
@@ -1,21 +1,23 @@
.right-sidebar {
margin: 5px;
margin-right: 0px;
height: 800px;
width: 125px;
background-color: var(--secondary);
border: 2px, inset, var(--thirdly);
box-sizing: border-box;
min-height: 900px;
height: 100%;
}
.left-sidebar {
margin: 5px;
margin-left: 0px;
height: 800px;
width: 125px;
background-color: var(--secondary);
border: 2px, inset, var(--thirdly);
box-sizing: border-box;
min-height: 900px;
height: 100%;
}
.side-card {
+1 -1
View File
@@ -35,4 +35,4 @@
<hr style="width: 92%; border-radius: 100px" />
<footer>
<h2>Doloro, All Meows Reserved.</h2>
</footer>
</footer>
+3 -1
View File
@@ -1,4 +1,6 @@
<title>Doloro's Site (Which resides on the internet)</title>
<svelte:head>
<title>Doloro Website [Home]</title>
</svelte:head>
<div>
<h1>hi</h1>
+4
View File
@@ -6,6 +6,10 @@
import { commitCard } from "./commitCard.svelte";
</script>
<svelte:head>
<title>Doloro Website [Changelog]</title>
</svelte:head>
<table class="commitContainer">
<caption>Git Commits of the website repo</caption>
<tbody>
+8 -5
View File
@@ -1,14 +1,15 @@
<script lang="ts">
import ScublingCard from "$lib/ScublingCard.svelte";
const videos = import.meta.glob("./*.mp4", {
eager: true,
import: "default",
query: { enhanced: true },
});
console.log(videos);
const scub = videos["./scub.mp4"];
</script>
<svelte:head>
<title>Doloro Website [Scublings]</title>
<meta property="og:type" content="video.other" />
<meta property="og:video" content="https://doloro.co.uk{scub.video.src}" />
<meta property="og:video:type" content="video/mp4" />
@@ -16,12 +17,14 @@
<meta property="og:video:height" content="1080" />
<meta property="og:title" content="Scublings" />
<!-- Optional but helps Discord show a thumbnail -->
<meta
property="og:image"
content="https://yourdomain.com/scub-thumbnail.jpg"
/>
<!-- <meta -->
<!-- property="og:image" -->
<!-- content="https://yourdomain.com/scub-thumbnail.jpg" -->
<!-- /> -->
</svelte:head>
<ScublingCard title="Scublings" description="A scubling video." />
<div class="video-container">
<enhanced:video src={scub}> </enhanced:video>
</div>
+6 -4
View File
@@ -3,16 +3,18 @@ export const prerender = true;
import { execSync } from 'child_process';
export type StoreEntity = {
image: string,
images: string[],
name: string,
description: string,
short: string, // Short Description
category: string,
gumroad?: string,
jinxxy?: string
}
const Catalog: StoreEntity[] = [
{ name: "Magic Circle", image: "MagicCircle", short: "", gumroad: undefined, jinxxy: "" } // Placeholder
];
const Catalog: Record<string, StoreEntity> = {};
Catalog["MagicCircle"] = { images: ["MagicCircle"], name: "Magic Circle", description: "Mreowww", short: "magiccircle", category: "Amulets", gumroad: "https://doloro.gumroad.com/l/Amulet", jinxxy: "" } // Placeholder
export async function load() {
return {
+30 -3
View File
@@ -1,8 +1,35 @@
<script lang="ts">
let { data } = $props();
import { snackProduct } from "./snacks.svelte";
import type { StoreEntity } from "./+page.server.ts";
let { data } = $props();
function groupByCategory(products: Record<string, StoreEntity>) {
const groups: Map<string, string[]> = new Map();
for (const [key, product] of Object.entries(products)) {
const cat = product.category;
if (!groups.has(cat)) {
groups.set(cat, []);
}
groups.get(cat)!.push(key);
}
return groups;
}
const categorized = groupByCategory(data.catalog);
</script>
{#each data.catalog as item}
{@render snackProduct(item)}
<svelte:head>
<title>Doloro Website [Store]</title>
</svelte:head>
{#each Array.from(categorized.entries()) as [category, products]}
<section class="StoreCategory">
<h2 class="StoreCategoryTitle">{category}</h2>
<div class="SnackCatagoryContainer">
{#each products as key}
{@render snackProduct(data.catalog[key], key)}
{/each}
</div>
</section>
{/each}
+32
View File
@@ -0,0 +1,32 @@
export const prerender = true;
import { type StoreEntity } from "../+page.server.ts";
export type ProductPageData = {
product: StoreEntity;
slug: string;
url: string;
}
const Catalog: Record<string, StoreEntity> = {};
Catalog["MagicCircle"] = { images: ["MagicCircle"], name: "Magic Circle", description: "Mreowww", short: "magiccircle", category: "Amulets", gumroad: "https://doloro.gumroad.com/l/Amulet", jinxxy: "" }
export function entries() {
return Object.keys(Catalog).map((slug) => ({ slug }));
}
export async function load({ params }: { params: { slug: string } }) {
const slug = params.slug;
const product = Catalog[slug];
if (!product) {
throw new Error(`Product not found: ${slug}`);
}
return {
product,
slug,
url: `/store/${slug}`
};
}
+223
View File
@@ -0,0 +1,223 @@
<script lang="ts">
import { snackImage, snackImages } from "../func.svelte";
import { storeButton } from "$lib/StoreButtons.svelte";
import type { ProductPageData } from "./+page.server.ts";
let { data } = $props();
let currentImageIndex = 0;
function selectImage(index: number) {
currentImageIndex = index;
}
function prevImage() {
currentImageIndex = (currentImageIndex - 1 + data.product.images.length) % data.product.images.length;
}
function nextImage() {
currentImageIndex = (currentImageIndex + 1) % data.product.images.length;
}
</script>
<style lang="scss">
.ProductPage {
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
padding: 40px 20px;
}
.ProductPageImageGallery {
width: 100%;
max-width: 600px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.ProductPageImageMain {
width: 100%;
position: relative;
border-width: 4px;
border-style: groove;
border-color: var(--accent-2);
img {
width: 100%;
height: auto;
display: block;
}
.GalleryControls {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: space-between;
pointer-events: none;
.GalleryNav {
pointer-events: auto;
background: var(--primary);
border: 2px groove var(--accent-2);
color: white;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 1.2rem;
opacity: 0.7;
&:hover {
opacity: 1;
}
}
.GalleryCounter {
color: white;
font-size: 1rem;
background: rgba(0, 0, 0, 0.5);
padding: 4px 10px;
border: 1px groove var(--accent-2);
pointer-events: auto;
user-select: none;
}
}
}
.ProductPageThumbnails {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
.Thumbnail {
width: 60px;
height: 60px;
border: 2px groove var(--accent-2);
cursor: pointer;
opacity: 0.5;
transition: opacity 0.2s;
&:hover {
opacity: 0.8;
}
&.active {
opacity: 1;
border-color: var(--accent-1);
}
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
}
.ProductPageDetails {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
width: 100%;
max-width: 600px;
}
.ProductPageInfo {
text-align: center;
display: flex;
flex-direction: column;
gap: 12px;
}
.ProductPageCategory {
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--accent-1);
border-bottom: 2px groove var(--accent-2);
padding-bottom: 6px;
width: fit-content;
margin: 0 auto;
}
.ProductPageInfo h1 {
font-size: 2rem;
margin: 0;
}
.ProductPageInfo p {
font-size: 1.1rem;
margin: 0;
max-width: 450px;
}
.ProductPageButtons {
display: flex;
flex-direction: row;
gap: 12px;
padding: 10px;
width: 100%;
max-width: 400px;
justify-content: center;
}
</style>
<svelte:head>
<title>{data.product.name} | Doloro Store</title>
<meta name="description" content={data.product.description} />
<meta property="og:type" content="product" />
<meta property="og:title" content={data.product.name} />
<meta property="og:description" content={data.product.description} />
<meta property="og:url" content={data.url} />
</svelte:head>
<div class="ProductPage">
<div class="ProductPageImageGallery">
<div class="ProductPageImageMain">
{#if data.product.images.length > 1}
<div class="GalleryControls">
<button class="GalleryNav prev" on:click={prevImage}></button>
<span class="GalleryCounter">{currentImageIndex + 1} / {data.product.images.length}</span>
<button class="GalleryNav next" on:click={nextImage}></button>
</div>
{/if}
<enhanced:img src={snackImage(data.product.images[currentImageIndex])} alt={data.product.name} />
</div>
{#if data.product.images.length > 1}
<div class="ProductPageThumbnails">
{#each data.product.images as img, i}
<div class="Thumbnail {i === currentImageIndex ? 'active' : ''}" on:click={() => selectImage(i)}>
<enhanced:img src={snackImage(img)} alt={`${data.product.name} image ${i + 1}`} />
</div>
{/each}
</div>
{/if}
</div>
<div class="ProductPageDetails">
<div class="ProductPageInfo">
<span class="ProductPageCategory">{data.product.category}</span>
<h1>{data.product.name}</h1>
<p>{data.product.description}</p>
</div>
<div class="ProductPageButtons">
{#if data.product.gumroad != undefined}
{@render storeButton("gumroad", data.product.gumroad!)}
{/if}
{#if data.product.jinxxy != undefined}
{@render storeButton("jinxxy", data.product.jinxxy!)}
{/if}
</div>
</div>
</div>
+69
View File
@@ -0,0 +1,69 @@
.ProductPage {
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
padding: 40px 20px;
}
.ProductPageImage {
width: 100%;
max-width: 600px;
border-width: 4px;
border-style: groove;
border-color: blue;
img {
width: 100%;
height: auto;
display: block;
}
}
.ProductPageDetails {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
width: 100%;
max-width: 600px;
}
.ProductPageInfo {
text-align: center;
display: flex;
flex-direction: column;
gap: 12px;
}
.ProductPageCategory {
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 2px;
color: #888;
border-bottom: 2px groove blue;
padding-bottom: 6px;
width: fit-content;
margin: 0 auto;
}
.ProductPageInfo h1 {
font-size: 2rem;
margin: 0;
}
.ProductPageInfo p {
font-size: 1.1rem;
margin: 0;
max-width: 450px;
}
.ProductPageButtons {
display: flex;
flex-direction: row;
gap: 12px;
padding: 10px;
width: 100%;
max-width: 400px;
justify-content: center;
}
+5
View File
@@ -0,0 +1,5 @@
import type { StoreEntity } from './+page.server.ts';
export const Catalog: Record<string, StoreEntity> = {};
Catalog["MagicCircle"] = { name: "Magic Circle", description: "Mreowww", image: "MagicCircle", short: "magiccircle", category: "Amulets", gumroad: "https://doloro.gumroad.com/l/Amulet", jinxxy: "" }
+4
View File
@@ -19,4 +19,8 @@
export function snackImage(img: string) {
return trimmedImgs[img];
}
export function snackImages(imgs: string[]) {
return imgs.map(img => trimmedImgs[img]);
}
</script>
+79 -4
View File
@@ -4,29 +4,104 @@
border-width: 3px;
border-style: groove;
border-color: blue;
border-color: var(--accent-2);
width: 300px;
height: 500px;
min-height: 500px;
picture {
width: 300px;
width: 100%;
height: 300px;
}
img {
width: 300px;
width: 100%;
height: 300px;
object-fit: cover;
}
}
.SnackProductLink {
text-decoration: none;
color: inherit;
picture {
display: block;
}
}
.SnackProductName {
margin: 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
h1 {
margin: 0;
text-align: center;
}
}
.SnackProductPopOut {
display: inline-flex;
align-items: center;
color: var(--accent-1);
text-decoration: none;
svg {
stroke: currentColor;
}
&:hover {
color: var(--accent-2);
}
}
.SnackProductDetails {
display: flex;
flex-direction: column;
background-color: var(--card-field);
align-items: center;
justify-content: space-between;
height: 100%;
padding: 12px;
gap: 12px;
}
.SnackProductButtons {
display: flex;
width: 100%;
flex-direction: row;
padding: 10px;
box-sizing: border-box;
gap: 8px;
}
.SnackCatagoryContainer {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
row-gap: 25px;
column-gap: 20px;
padding: 10px 0;
}
.StoreCategory {
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
}
.StoreCategoryTitle {
font-family: inherit;
font-size: 1.8rem;
margin: 0 0 15px 0;
padding-bottom: 8px;
border-bottom: 3px groove var(--accent-2);
color: var(--card-raised);
}
+23 -4
View File
@@ -1,16 +1,35 @@
<script lang="ts" module>
import { type StoreEntity } from "./+page.server.ts";
import { snackImage } from "./func.svelte";
import { storeButton } from "$lib/StoreButtons.svelte";
import { ExternalLink } from "@lucide/svelte";
import "./snacks.scss";
export { snackProduct };
</script>
{#snippet snackProduct(product: StoreEntity)}
{#snippet snackProduct(product: StoreEntity, slug: string)}
<div class="SnackProductContainer">
<enhanced:img src={snackImage(product.image)} />
<a href="/store/{slug}" class="SnackProductLink">
<enhanced:img src={snackImage(product.images[0])} alt={product.name} />
</a>
<div class="SnackProductDetails">
<h1>{product.name}</h1>
<div class="SnackProductButtons"></div>
<div>
<h1 class="SnackProductName">
{product.name}
<a href="/store/{slug}" class="SnackProductPopOut">
<ExternalLink size={16} />
</a>
</h1>
<p>{product.description}</p>
</div>
<div class="SnackProductButtons">
{#if product.gumroad != undefined}
{@render storeButton("gumroad", product.gumroad!)}
{/if}
{#if product.jinxxy != undefined}
{@render storeButton("jinxxy", product.jinxxy!)}
{/if}
</div>
</div>
</div>
{/snippet}