feat: AI-powered prediction market (Hopium)

This commit is contained in:
Face 2025-05-28 16:44:30 +03:00
parent 4fcc55fa72
commit 2a92c37d26
33 changed files with 7009 additions and 4518 deletions

View file

@ -7,7 +7,6 @@
import {
Moon,
Sun,
ShieldAlert,
Home,
Store,
BriefcaseBusiness,
@ -24,7 +23,9 @@
Gift,
Shield,
Ticket,
BarChart3
PiggyBank,
ChartColumn,
TrendingUpDown
} from 'lucide-svelte';
import { mode, setMode } from 'mode-watcher';
import type { HTMLAttributes } from 'svelte/elements';
@ -37,15 +38,17 @@
import { signOut } from '$lib/auth-client';
import { formatValue, getPublicUrl } from '$lib/utils';
import { goto } from '$app/navigation';
import { liveTradesStore, isLoadingTrades, type LiveTrade } from '$lib/stores/websocket';
import { liveTradesStore, isLoadingTrades } from '$lib/stores/websocket';
const data = {
navMain: [
{ title: 'Home', url: '/', icon: Home },
{ title: 'Market', url: '/market', icon: Store },
{ title: 'Portfolio', url: '/portfolio', icon: BriefcaseBusiness },
{ title: 'Hopium', url: '/hopium', icon: TrendingUpDown },
{ title: 'Gambling', url: '/gambling', icon: PiggyBank },
{ title: 'Leaderboard', url: '/leaderboard', icon: Trophy },
{ title: 'Treemap', url: '/treemap', icon: BarChart3 },
{ title: 'Portfolio', url: '/portfolio', icon: BriefcaseBusiness },
{ title: 'Treemap', url: '/treemap', icon: ChartColumn },
{ title: 'Create coin', url: '/coin/create', icon: Coins }
]
};
@ -364,7 +367,12 @@
<Settings />
Settings
</DropdownMenu.Item>
<DropdownMenu.Item onclick={() => { showPromoCode = true; setOpenMobile(false); }}>
<DropdownMenu.Item
onclick={() => {
showPromoCode = true;
setOpenMobile(false);
}}
>
<Gift />
Promo code
</DropdownMenu.Item>

View file

@ -0,0 +1,194 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Separator } from '$lib/components/ui/separator';
import { Skeleton } from '$lib/components/ui/skeleton';
</script>
<!-- Header Section -->
<div class="flex items-center gap-3">
<div class="bg-muted rounded-lg p-4">
<Skeleton class="h-14 w-14" />
</div>
<div class="flex-1">
<Skeleton class="mb-2 h-8 w-3/4" />
<Skeleton class="h-4 w-1/3" />
</div>
</div>
<!-- Creator Info -->
<div class="mb-4 mt-3 flex flex-wrap items-center gap-1.5">
<Skeleton class="h-3 w-16" />
<Skeleton class="h-4 w-4 rounded-full" />
<Skeleton class="h-4 w-32" />
</div>
<div class="grid gap-8">
<!-- Main content grid -->
<div class="grid grid-cols-1 gap-8 lg:grid-cols-3">
<!-- Left: Chart (2/3 width) -->
<div class="lg:col-span-2">
<Card.Root class="shadow-sm">
<Card.Header>
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<Skeleton class="h-6 w-6" />
<Skeleton class="h-6 w-16" />
</div>
<div class="text-right">
<Skeleton class="mb-1 h-10 w-20" />
<Skeleton class="h-4 w-16" />
</div>
</div>
</Card.Header>
<Card.Content>
<Skeleton class="h-[400px] w-full rounded-lg" />
</Card.Content>
</Card.Root>
</div>
<!-- Right: Trading Controls (1/3 width) -->
<div class="space-y-6 lg:col-span-1">
<!-- Trading Card -->
<Card.Root>
<Card.Header>
<Skeleton class="h-6 w-20" />
</Card.Header>
<Card.Content class="space-y-6">
<!-- YES/NO Buttons -->
<div class="grid grid-cols-2 gap-4">
<Skeleton class="h-12 w-full rounded-md" />
<Skeleton class="h-12 w-full rounded-md" />
</div>
<!-- Amount Input -->
<Skeleton class="h-10 w-full rounded-md" />
<!-- Quick Amount Buttons -->
<div class="flex gap-2">
<Skeleton class="h-9 flex-1 rounded-md" />
<Skeleton class="h-9 flex-1 rounded-md" />
<Skeleton class="h-9 flex-1 rounded-md" />
<Skeleton class="h-9 flex-1 rounded-md" />
</div>
<!-- Win Estimation -->
<div class="space-y-2">
<div class="flex justify-between">
<Skeleton class="h-4 w-16" />
<Skeleton class="h-4 w-12" />
</div>
<div class="flex justify-between">
<Skeleton class="h-4 w-16" />
<Skeleton class="h-4 w-12" />
</div>
</div>
<!-- Pay Button -->
<Skeleton class="h-12 w-full rounded-md" />
</Card.Content>
</Card.Root>
</div>
</div>
<!-- Position and Stats Cards below chart -->
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<!-- Position Card -->
<Card.Root class="gap-1">
<Card.Header>
<div class="flex items-center gap-3">
<div class="bg-muted rounded-full p-2">
<Skeleton class="h-5 w-5" />
</div>
<Skeleton class="h-6 w-32" />
</div>
</Card.Header>
<Card.Content class="pb-4">
<div class="space-y-3">
<div class="flex items-center justify-between">
<div>
<Skeleton class="mb-1 h-4 w-16" />
<Skeleton class="h-3 w-20" />
</div>
<Skeleton class="h-6 w-16" />
</div>
<div class="flex items-center justify-between">
<div>
<Skeleton class="mb-1 h-4 w-14" />
<Skeleton class="h-3 w-20" />
</div>
<Skeleton class="h-6 w-16" />
</div>
<Separator />
<div class="flex items-center justify-between">
<Skeleton class="h-4 w-24" />
<Skeleton class="h-6 w-16" />
</div>
</div>
</Card.Content>
</Card.Root>
<!-- Market Stats Card -->
<Card.Root class="gap-1">
<Card.Header>
<div class="flex items-center gap-3">
<div class="bg-muted rounded-full p-2">
<Skeleton class="h-5 w-5" />
</div>
<Skeleton class="h-6 w-28" />
</div>
</Card.Header>
<Card.Content>
<div class="space-y-2">
<div class="flex justify-between">
<Skeleton class="h-4 w-20" />
<Skeleton class="h-4 w-16" />
</div>
<div class="flex justify-between">
<Skeleton class="h-4 w-16" />
<Skeleton class="h-4 w-8" />
</div>
<div class="flex justify-between">
<Skeleton class="h-4 w-14" />
<Skeleton class="h-4 w-20" />
</div>
<div class="flex justify-between">
<Skeleton class="h-4 w-16" />
<Skeleton class="h-4 w-20" />
</div>
</div>
</Card.Content>
</Card.Root>
</div>
<!-- Recent Activity Section -->
<Card.Root class="shadow-sm">
<Card.Header>
<div class="flex items-center gap-3">
<div class="bg-muted rounded-full p-2">
<Skeleton class="h-6 w-6" />
</div>
<Skeleton class="h-6 w-32" />
</div>
</Card.Header>
<Card.Content class="pb-6">
<div class="space-y-4">
{#each Array(3) as _}
<div class="flex items-center justify-between rounded-xl border p-4">
<div class="flex items-center gap-4">
<Skeleton class="h-10 w-10 rounded-full" />
<div>
<Skeleton class="mb-1 h-4 w-24" />
<Skeleton class="h-3 w-16" />
</div>
<Skeleton class="h-5 w-8 rounded-full" />
</div>
<div class="text-right">
<Skeleton class="mb-1 h-5 w-12" />
<Skeleton class="h-3 w-16" />
</div>
</div>
{/each}
</div>
</Card.Content>
</Card.Root>
</div>

View file

@ -0,0 +1,43 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Skeleton } from '$lib/components/ui/skeleton';
</script>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{#each Array(6) as _}
<Card.Root class="flex flex-col">
<Card.Header class="pb-4">
<div class="flex items-start justify-between gap-3">
<div class="flex-1 space-y-3">
<!-- Question title skeleton -->
<Skeleton class="h-6 w-full" />
<Skeleton class="h-6 w-3/4" />
</div>
<div class="flex flex-col items-end gap-2">
<!-- Probability meter skeleton -->
<div class="relative flex h-12 w-16 items-end justify-center">
<Skeleton class="h-10 w-16 rounded-full" />
<div class="absolute bottom-0">
<Skeleton class="h-4 w-8" />
</div>
</div>
</div>
</div>
<!-- Time and amount info skeleton -->
<div class="flex items-center gap-2">
<Skeleton class="h-4 w-24" />
<Skeleton class="h-1 w-1 rounded-full" />
<Skeleton class="h-4 w-16" />
</div>
<!-- Creator info skeleton -->
<div class="mb-2 mt-2 flex items-center gap-2">
<Skeleton class="h-5 w-5 rounded-full" />
<Skeleton class="h-4 w-20" />
</div>
</Card.Header>
</Card.Root>
{/each}
</div>

View file

@ -0,0 +1,16 @@
import Root from "./tabs.svelte";
import Content from "./tabs-content.svelte";
import List from "./tabs-list.svelte";
import Trigger from "./tabs-trigger.svelte";
export {
Root,
Content,
List,
Trigger,
//
Root as Tabs,
Content as TabsContent,
List as TabsList,
Trigger as TabsTrigger,
};

View file

@ -0,0 +1,17 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: TabsPrimitive.ContentProps = $props();
</script>
<TabsPrimitive.Content
bind:ref
data-slot="tabs-content"
class={cn("flex-1 outline-none", className)}
{...restProps}
/>

View file

@ -0,0 +1,20 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: TabsPrimitive.ListProps = $props();
</script>
<TabsPrimitive.List
bind:ref
data-slot="tabs-list"
class={cn(
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
className
)}
{...restProps}
/>

View file

@ -0,0 +1,20 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: TabsPrimitive.TriggerProps = $props();
</script>
<TabsPrimitive.Trigger
bind:ref
data-slot="tabs-trigger"
class={cn(
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-2 py-1 text-sm font-medium transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...restProps}
/>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
value = $bindable(""),
class: className,
...restProps
}: TabsPrimitive.RootProps = $props();
</script>
<TabsPrimitive.Root
bind:ref
bind:value
data-slot="tabs"
class={cn("flex flex-col gap-2", className)}
{...restProps}
/>