Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/lib/@types/BuffTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1053,9 +1053,18 @@ export namespace BuffTypes {
currency: string;
currency_symbol: string;
days: 7 | 30 | 180;
price_history: [number, number][];
lines: {
allow: boolean;
chart_type: 'price' | 'number';
color: string;
disabled_confirm: any | null;
key: string;
name: string;
points: [number, number][];
show: boolean;
}[];
price_type: 'Steam price' | 'BUFF price';
steam_price_currency: string;
steam_price_currency?: string;
}

export interface Response {
Expand Down
15 changes: 6 additions & 9 deletions src/lib/components/DarkMode.svelte
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
<script lang="ts">
import { onMount } from 'svelte';
import { cn } from '$lib/utils';
import MoonIcon from '../icons/moon.svg';
import SunIcon from '../icons/sun.svg';
import { ExtensionStorage } from '../util/storage';
import Badge from './ui/badge/badge.svelte';
import Button from './ui/button/button.svelte';

let darkMode = true;

$: isOn = darkMode;
let darkMode = $state(true);

onMount(async () => {
darkMode = await ExtensionStorage.darkMode.getValue();
});

const toggle = async () => {
isOn = !isOn;
await ExtensionStorage.darkMode.setValue(isOn);
darkMode = !darkMode;
await ExtensionStorage.darkMode.setValue(darkMode);
};
</script>

<div class="flex items-center gap-2">
<Button variant="ghost" class="flex items-center h-14 w-28 gap-2" on:click={toggle}>
<img src={isOn ? MoonIcon : SunIcon} class="h-12" alt="Dark Mode Icon" />
<Badge class={isOn ? "bg-[#00b6ff]" : "bg-[#ffbe00]"}>{isOn ? "ON" : "OFF"}</Badge>
<Button variant="ghost" class="flex items-center h-14 w-28 gap-2" onclick={toggle}>
<img src={darkMode ? MoonIcon : SunIcon} class="h-12" alt="Dark Mode Icon" />
<Badge class={darkMode ? "bg-[#00b6ff]" : "bg-[#ffbe00]"}>{darkMode ? "ON" : "OFF"}</Badge>
</Button>
</div>
4 changes: 2 additions & 2 deletions src/lib/components/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const version = `v. ${browser.runtime.getManifest().version}`;
<Badge variant="outline" class="border-muted text-muted-foreground">{version}</Badge>
</div>
<div class="flex gap-1">
<Button variant="ghost" size="icon" on:click={() => window.open(DISCORD_URL)} title={DISCORD_URL}>
<Button variant="ghost" size="icon" onclick={() => window.open(DISCORD_URL)} title={DISCORD_URL}>
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 256 256" {...$$props}>
<g fill="none">
<rect width="256" height="256" fill="#5865f2" rx="60" />
Expand All @@ -32,7 +32,7 @@ const version = `v. ${browser.runtime.getManifest().version}`;
</g>
</svg>
</Button>
<Button variant="ghost" size="icon" on:click={() => window.open(GITHUB_URL)} title={GITHUB_URL}>
<Button variant="ghost" size="icon" onclick={() => window.open(GITHUB_URL)} title={GITHUB_URL}>
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24"
><path
fill="white"
Expand Down
28 changes: 15 additions & 13 deletions src/lib/components/InfoTooltip.svelte
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<script lang="ts">
import Decimal from 'decimal.js';
import type { Snippet } from 'svelte';
import type { BuffTypes } from '../@types/BuffTypes';

export let data: BuffTypes.PriceHistory.Data;
let isHovered = false,
isClicked = false;
let { data, children }: { data: BuffTypes.PriceHistory.Data; children: Snippet } = $props();
let isHovered = $state(false),
isClicked = $state(false);

const minPrice = data.price_history.reduce((a, b) => Math.min(a, b[1]), Infinity);
const maxPrice = data.price_history.reduce((a, b) => Math.max(a, b[1]), -Infinity);
const priceChange = new Decimal(data.price_history[data.price_history.length - 1][1]).minus(data.price_history[0][1]);
const priceChangePercentage = priceChange.div(data.price_history[0][1]).times(100);
let listingLine = $derived(data.lines.find((line) => line.key === 'sell_min_price_history')?.points);
let minPrice = $derived(listingLine?.reduce((a, b) => Math.min(a, b[1]), Infinity));
let maxPrice = $derived(listingLine?.reduce((a, b) => Math.max(a, b[1]), -Infinity));
let priceChange = $derived(new Decimal(listingLine?.[listingLine.length - 1][1] ?? 0).minus(listingLine?.[0][1] ?? 0));
let priceChangePercentage = $derived(priceChange.div(listingLine?.[0][1] ?? 0).times(100));

const minElement = data.price_history.find((e) => e[1] === minPrice)!;
const maxElement = data.price_history.find((e) => e[1] === maxPrice)!;
let minElement = $derived(listingLine?.find((e) => e[1] === minPrice)!);
let maxElement = $derived(listingLine?.find((e) => e[1] === maxPrice)!);
</script>

<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div on:mouseover={() => (isHovered = true)} on:mouseleave={() => (isHovered = false)} on:click={() => (isClicked = !isClicked)} role="none">
<slot />
<div onmouseover={() => (isHovered = true)} onmouseleave={() => (isHovered = false)} onclick={() => (isClicked = !isClicked)} onfocus={() => (isHovered = true)} onblur={() => (isHovered = false)} role="none">
<!-- <slot /> -->
{@render children()}
</div>

{#if isHovered || isClicked}
Expand All @@ -39,7 +41,7 @@ const maxElement = data.price_history.find((e) => e[1] === maxPrice)!;
{priceChange.isNegative() ? "-" : "+"}{data.currency_symbol}{priceChange.absoluteValue().toSD(3)}
<span class="font-medium text-base">({priceChangePercentage.isNaN() ? "0" : priceChangePercentage.toSD(3)}%)</span>
</div>
<div class="stat-desc">{new Date(data.price_history[0][0]).toLocaleDateString()} - {new Date(data.price_history[data.price_history.length - 1][0]).toLocaleDateString()}</div>
<div class="stat-desc">{new Date(listingLine?.[0][0] ?? Date.now()).toLocaleDateString()} - {new Date(listingLine?.[listingLine.length - 1][0] ?? 0).toLocaleDateString()}</div>
</div>
</div>
</div>
Expand Down
6 changes: 2 additions & 4 deletions src/lib/components/NavLink.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<script lang="ts">
import { openLink } from '../util/svelteUtil';

export let src: string;
export let link: string;
export let imgStyle: string = '';
let { src, link, imgStyle }: { src: string; link: string; imgStyle?: string } = $props();
</script>
<button class="btn-ghost rounded-full" on:click={() => openLink(link)}>
<button class="btn-ghost rounded-full" onclick={() => openLink(link)}>
<img {src} alt="Social Link" class={imgStyle} />
</button>
104 changes: 52 additions & 52 deletions src/lib/components/PatternPopup.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { fade, slide } from 'svelte/transition';
import BetterBuffLogo from '../../public/icon/512.png';
import type { BetterBuff } from '../@types/BetterBuff';
Expand All @@ -9,19 +10,20 @@ type Filter = {
value: number;
};

export let weapon: string;
export let data: BetterBuff.CHPatterns['weapon'];
let { weapon, data, children }: { weapon: string; data: BetterBuff.CHPatterns['weapon']; children?: Snippet } = $props();

const patterns = [...Array(1000).keys()].map((i) => [i, `${data.playside[i]}/${data.backside[i]}`]);
let isClicked = false,
searchTerm = '',
extended = false,
filters: Filter[] = [],
newFilterSite: Filter['site'] = 'playside',
newFilterRange: Filter['range'] = 'min',
newFilterValue = 0,
filteredPatterns = patterns;

$: filterItems = filters.length;
let isClicked = $state(false);
let searchTerm = $state('');
let extended = $state(false);
let filters = $state<Filter[]>([]);
let newFilterSite = $state<Filter['site']>('playside');
let newFilterRange = $state<Filter['range']>('min');
let newFilterValue = $state(0);
let filteredPatterns = $state(patterns);

let filterItems = $derived(filters.length);

function filterPatterns() {
filteredPatterns = patterns.filter((pattern, index) => {
Expand Down Expand Up @@ -53,29 +55,27 @@ function addFilter() {
newFilterRange = 'min';
newFilterValue = 0;

filterItems += 1;
filterPatterns();
}

function removeFilter(index: number) {
filters.splice(index, 1);
filterItems -= 1;
filterPatterns();
}
</script>

<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:click={() => (isClicked = !isClicked)}>
<slot />
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div onclick={() => (isClicked = !isClicked)} role="button" tabindex="0">
{@render children?.()}
</div>

{#if isClicked}
<div class="absolute rounded-lg bg-[#191e24] p-4 pt-2 mt-2 z-50" transition:fade={{ delay: 0, duration: 200 }}>
<div class="absolute rounded-lg bg-[#191e24] p-4 pt-2 mt-2 z-50 text-white" transition:fade={{ delay: 0, duration: 200 }}>
<div class="flex justify-between items-center gap-2 pb-1.5">
<img src={BetterBuffLogo} class="w-8 h-8" alt="BetterBuff Logo" />
<h2 class="text-xl font-bold">Pattern Explorer</h2>
<button class="btn btn-circle btn-ghost btn-sm text-black" on:click={() => (isClicked = !isClicked)}>
<button class="btn btn-circle btn-ghost btn-sm" onclick={() => (isClicked = !isClicked)} aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"
><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg
>
Expand All @@ -86,43 +86,43 @@ function removeFilter(index: number) {
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="relative">
<span class="absolute inset-y-0 left-0 flex items-center pl-1">
<button type="submit" class="btn btn-ghost h-8 min-h-8 p-1 focus:outline-none focus:shadow-outline">
<button type="submit" class="btn btn-ghost h-8 min-h-8 p-1 focus:outline-none focus:shadow-outline" aria-label="Search">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-5 h-5"
><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg
>
</button>
</span>
<input
type="search"
name="q"
class="input input-bordered h-8 bg-gray-800 py-1 text-sm text-white rounded-md pl-10 pr-2 focus:outline-none"
placeholder="Search patterns..."
bind:value={searchTerm}
on:input={filterPatterns}
autocomplete="off"
/>
</div>
<button class="btn btn-ghost btn-sm p-1 ml-2" on:click={() => (extended = !extended)}>
{#if extended}
<svg class="h-6 w-6" viewBox="0 0 24 24"><path fill="currentColor" d="m12 10.8l-4.6 4.6L6 14l6-6l6 6l-1.4 1.4z" /></svg>
{:else}
<svg class="h-6 w-6" viewBox="0 0 24 24"><path fill="currentColor" d="m12 15.4l-6-6L7.4 8l4.6 4.6L16.6 8L18 9.4z" /></svg>
{/if}
</button>
<input
type="search"
name="q"
class="input input-bordered h-8 bg-gray-800 py-1 text-sm text-white rounded-md pl-10 pr-2 focus:outline-none"
placeholder="Search patterns..."
bind:value={searchTerm}
oninput={filterPatterns}
autocomplete="off"
/>
</div>
<button class="btn btn-ghost btn-sm p-1 ml-2" onclick={() => (extended = !extended)}>
{#if extended}
<svg class="h-6 w-6" viewBox="0 0 24 24"><path fill="currentColor" d="m12 10.8l-4.6 4.6L6 14l6-6l6 6l-1.4 1.4z" /></svg>
{:else}
<svg class="h-6 w-6" viewBox="0 0 24 24"><path fill="currentColor" d="m12 15.4l-6-6L7.4 8l4.6 4.6L16.6 8L18 9.4z" /></svg>
{/if}
</button>
</div>
{#key filterItems}
{#if filterItems > 0}
<div class="flex items-center flex-wrap pt-2 max-w-64 gap-2">
{#each filters as { site, range, value }, index}
<div class="badge badge-info whitespace-nowrap pl-0 pr-1" transition:fade={{ delay: 100, duration: 200 }}>
<button class="btn btn-circle btn-ghost btn-xs" on:click={() => removeFilter(index)}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-4 h-4 stroke-current"
><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg
>
</button>
<span class="pr-0.5">{site === "playside" ? "front" : "back"}-{range}: {value}%</span>
</div>
{/each}
{#each filters as { site, range, value }, index}
<div class="badge badge-info whitespace-nowrap pl-0 pr-1" transition:fade={{ delay: 100, duration: 200 }}>
<button class="btn btn-circle btn-ghost btn-xs" onclick={() => removeFilter(index)} aria-label="Remove filter">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-4 h-4 stroke-current"
><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg
>
</button>
<span class="pr-0.5">{site === "playside" ? "front" : "back"}-{range}: {value}%</span>
</div>
{/each}
</div>
{/if}
{/key}
Expand All @@ -136,10 +136,10 @@ function removeFilter(index: number) {
<option value="min">Min</option>
<option value="max">Max</option>
</select>
<input type="number" min="0" max="99" step="1" bind:value={newFilterValue} class="input input-sm w-10 pr-0 focus:outline-none" />
<button class="btn btn-square btn-outline btn-sm p-1 ml-2" on:click={addFilter}>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24"><path fill="#888888" d="M11 13H5v-2h6V5h2v6h6v2h-6v6h-2z" /></svg>
</button>
<input type="number" min="0" max="99" step="1" bind:value={newFilterValue} class="input input-sm w-10 pr-0 focus:outline-none" />
<button class="btn btn-square btn-outline btn-sm p-1 ml-2" onclick={addFilter} aria-label="Add filter">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24"><path fill="#888888" d="M11 13H5v-2h6V5h2v6h6v2h-6v6h-2z" /></svg>
</button>
</div>
{/if}
</form>
Expand All @@ -159,7 +159,7 @@ function removeFilter(index: number) {
<td class="py-1">{pattern[0]}</td>
<td class="py-1">{pattern[1]}</td>
<th class="py-1">
<a class="btn btn-square btn-ghost btn-xs" href="https://raw.githubusercontent.com/dnsmits/csbluegem_images/main/{weapon}/{weapon}_{pattern[0]}.png" target="_blank">
<a class="btn btn-square btn-ghost btn-xs" href="https://raw.githubusercontent.com/dnsmits/csbluegem_images/main/{weapon}/{weapon}_{pattern[0]}.png" target="_blank" aria-label="View pattern">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
><path
fill="#888888"
Expand Down
10 changes: 5 additions & 5 deletions src/lib/components/Popup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { DISCORD_URL } from '$lib/util/globals';
import BBlogo from '/icon/512.png';
import JoinDiscord from '/join_discord.png';
import { ExtensionStorage } from '../util/storage';
import BuyMeACoffee from './icons/BuyMeACoffee.svelte';
import { BuyMeACoffee } from './icons';
import ListingDifference from './SettingListingDifference.svelte';
import SettingListingOptions from './SettingListingOptions.svelte';

Expand Down Expand Up @@ -43,7 +43,7 @@ const version = browser.runtime.getManifest().version;
<ScrollArea class="h-[488px] rounded-md">
<div class="w-full mx-4 border border-base-300 bg-card/90 rounded-lg p-5">
<p class="text-sm text-center">Currency conversion is already integrated into Buff itself. You can change your display currency in your Buff account settings:</p>
<Button variant="outline" class="w-full mt-2" on:click={() => window.open("https://buff.163.com/user-center/profile")} title="Open Buff Account Settings">
<Button variant="outline" class="w-full mt-2" onclick={() => window.open("https://buff.163.com/user-center/profile")} title="Open Buff Account Settings">
Open Buff Account Settings
</Button>
</div>
Expand All @@ -60,12 +60,12 @@ const version = browser.runtime.getManifest().version;
<div class="flex flex-col items-center justify-center gap-4 py-2">
<div class="flex items-center gap-2">
<p class="scroll-m-20 text-sm font-medium text-gray-600">based on</p>
<Button variant="ghost" class="py-1 px-2 h-7" on:click={() => window.open("https://github.com/PenguiniVogel/BuffUtility")}>
<Button variant="ghost" class="py-1 px-2 h-7" onclick={() => window.open("https://github.com/PenguiniVogel/BuffUtility")}>
<img class="h-6" src={BUlogo} alt="BuffUtility" />
</Button>
</div>
<Badge variant="secondary" class="border-muted text-base">BETA - Version {version}</Badge>
<Button variant="ghost" class="p-0 py-4" on:click={() => window.open(DISCORD_URL)}>
<Button variant="ghost" class="p-0 py-4" onclick={() => window.open(DISCORD_URL)}>
<img class="h-10" src={JoinDiscord} alt="Join us on Discord" />
</Button>
</div>
Expand All @@ -82,7 +82,7 @@ const version = browser.runtime.getManifest().version;
<p>
Built with 🖤 in Munich by
<!-- svelte-ignore a11y_click_events_have_key_events -->
<span class="cursor-pointer text-green-900" on:click={() => window.open("https://github.com/GODrums")} role="link" tabindex="-1">Rums</span>
<span class="cursor-pointer text-green-900" onclick={() => window.open("https://github.com/GODrums")} role="link" tabindex="-1">Rums</span>
</p>
</footer>
</div>
Expand Down
11 changes: 7 additions & 4 deletions src/lib/components/PopupLayout.svelte
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import Header from './Header.svelte';

let m = { x: 0, y: 0 };
let m = $state({ x: 0, y: 0 });

function handleMousemove(event: any) {
function handleMousemove(event: MouseEvent) {
m.x = event.clientX;
m.y = event.clientY;
}

let { children }: { children: Snippet } = $props();
</script>

<div class="dark flex flex-col bg-card text-card-foreground justify-between h-[600px] w-[350px] overflow-hidden heroBG" on:mousemove={handleMousemove} role="document">
<div class="dark flex flex-col bg-card text-card-foreground justify-between h-[600px] w-[350px] overflow-hidden heroBG" onmousemove={handleMousemove} role="document">
<div class="absolute inset-0 pointer-events-none maskBG" style="mask-image: radial-gradient(100px at {m.x}px {m.y}px, black 0%, transparent 100%);"></div>
<Header />
<slot />
{@render children()}
</div>

<style>
Expand Down
Loading