feat(stage-web): component for links
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
description: string
|
||||
icon?: string
|
||||
iconColor?: string
|
||||
iconImage?: string
|
||||
to: string
|
||||
configured: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="neutral-200/50 dark:neutral-700"
|
||||
border="neutral-100 dark:neutral-700 hover:primary-500 dark:hover:primary-400 solid 2"
|
||||
drop-shadow="none hover:[0px_4px_4px_rgba(220,220,220,0.4)] active:[0px_0px_0px_rgba(220,220,220,0.25)] dark:hover:none"
|
||||
class="menu-icon-status-item"
|
||||
transition="all ease-in-out duration-200"
|
||||
w-full of-hidden rounded-xl
|
||||
>
|
||||
<RouterLink
|
||||
flex="~ row"
|
||||
bg="neutral-50 dark:neutral-800"
|
||||
transition="all ease-in-out duration-200"
|
||||
relative w-full items-center overflow-hidden rounded-lg p-5 text-left
|
||||
:to=" props.to"
|
||||
>
|
||||
<div z-1 flex-1>
|
||||
<div
|
||||
text-lg font-bold
|
||||
class="menu-icon-status-item-title"
|
||||
transition="all ease-in-out duration-200"
|
||||
>
|
||||
{{ props.title }}
|
||||
</div>
|
||||
<div
|
||||
text="sm neutral-500 dark:neutral-400"
|
||||
class="menu-icon-status-item-description"
|
||||
transition="all ease-in-out duration-200"
|
||||
>
|
||||
<span>{{ props.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if=" props.icon">
|
||||
<div
|
||||
class="menu-icon-status-item-icon"
|
||||
transition="all ease-in-out duration-500"
|
||||
absolute right-0 size-16 translate-y-2
|
||||
text="neutral-400/50 dark:neutral-600/50"
|
||||
grayscale-100
|
||||
:class="[props.icon]"
|
||||
/>
|
||||
</template>
|
||||
<template v-if=" props.iconColor">
|
||||
<div
|
||||
class="menu-icon-status-item-icon-color"
|
||||
transition="all ease-in-out duration-500"
|
||||
absolute right-0 size-16 translate-y-2
|
||||
text="neutral-400/50 dark:neutral-600/50"
|
||||
grayscale-100
|
||||
:class="[props.iconColor]"
|
||||
/>
|
||||
</template>
|
||||
<template v-if=" props.iconImage">
|
||||
<img
|
||||
:src=" props.iconImage"
|
||||
class="menu-icon-status-item-icon-image"
|
||||
transition="all ease-in-out duration-500"
|
||||
absolute right-0 size-16 translate-y-2
|
||||
grayscale-100
|
||||
>
|
||||
</template>
|
||||
</RouterLink>
|
||||
<div p-2>
|
||||
<div v-if=" props.configured" size-3 bg="green-500 dark:green-600" rounded-full />
|
||||
<div v-else size-3 bg="neutral-400 dark:neutral-500" rounded-full />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.menu-icon-status-item::after {
|
||||
--at-apply: 'bg-dotted-[neutral-200] hover:bg-dotted-[primary-300/50] dark:bg-dotted-[neutral-700/80] dark:hover:bg-dotted-[primary-200/20]';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 10px 10px;
|
||||
content: '';
|
||||
mask-image: linear-gradient(165deg, white 30%, transparent 50%);
|
||||
}
|
||||
|
||||
.menu-icon-status-item:hover .menu-icon-status-item-title {
|
||||
--at-apply: text-primary-500;
|
||||
}
|
||||
|
||||
.menu-icon-status-item:hover .menu-icon-status-item-description {
|
||||
--at-apply: text-primary-500;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.menu-icon-status-item:hover .menu-icon-status-item-icon {
|
||||
--at-apply: text-primary-500;
|
||||
scale: 1.2;
|
||||
opacity: 0.2;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.menu-icon-status-item:hover .menu-icon-status-item-icon-color {
|
||||
scale: 1.2;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.menu-icon-status-item:hover .menu-icon-status-item-icon-image {
|
||||
scale: 1.2;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.dark .menu-icon-status-item:hover .menu-icon-status-item-title {
|
||||
--at-apply: text-primary-400;
|
||||
}
|
||||
|
||||
.dark .menu-icon-status-item:hover .menu-icon-status-item-description {
|
||||
--at-apply: text-primary-400;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.dark .menu-icon-status-item:hover .menu-icon-status-item-icon {
|
||||
--at-apply: text-primary-400;
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@ import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import FactorioIcon from '../../../assets/icons/modules/games/factorio.png'
|
||||
import IconStatusItem from '../../../components/Menu/IconStatusItem.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -109,74 +110,17 @@ const modulesList = computed<Module[]>(() => [
|
||||
</h1>
|
||||
</div>
|
||||
<div grid="~ cols-1 sm:cols-2 gap-4">
|
||||
<div
|
||||
v-for="module in modulesList" :key="module.id"
|
||||
flex="~ col"
|
||||
bg="neutral-200/50 dark:neutral-700"
|
||||
border="neutral-100 dark:neutral-700 hover:primary-500 dark:hover:primary-400 solid 2"
|
||||
drop-shadow="none hover:[0px_4px_4px_rgba(220,220,220,0.4)] active:[0px_0px_0px_rgba(220,220,220,0.25)] dark:hover:none"
|
||||
class="menu-icon-item-modules"
|
||||
transition="all ease-in-out duration-200"
|
||||
w-full of-hidden rounded-xl
|
||||
>
|
||||
<RouterLink
|
||||
flex="~ row"
|
||||
bg="neutral-50 dark:neutral-800"
|
||||
transition="all ease-in-out duration-200"
|
||||
relative w-full items-center overflow-hidden rounded-lg p-5 text-left
|
||||
:to="module.to"
|
||||
>
|
||||
<div z-1 flex-1>
|
||||
<div
|
||||
text-lg font-bold
|
||||
class="menu-icon-item-modules-title"
|
||||
transition="all ease-in-out duration-200"
|
||||
>
|
||||
{{ module.name }}
|
||||
</div>
|
||||
<div
|
||||
text="sm neutral-500 dark:neutral-400"
|
||||
class="menu-icon-item-modules-description"
|
||||
transition="all ease-in-out duration-200"
|
||||
>
|
||||
<span>{{ module.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="module.icon">
|
||||
<div
|
||||
class="menu-icon-item-modules-icon"
|
||||
transition="all ease-in-out duration-500"
|
||||
absolute right-0 size-16 translate-y-2
|
||||
text="neutral-400/50 dark:neutral-600/50"
|
||||
grayscale-100
|
||||
:class="[module.icon]"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="module.iconColor">
|
||||
<div
|
||||
class="menu-icon-item-modules-icon-color"
|
||||
transition="all ease-in-out duration-500"
|
||||
absolute right-0 size-16 translate-y-2
|
||||
text="neutral-400/50 dark:neutral-600/50"
|
||||
grayscale-100
|
||||
:class="[module.iconColor]"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="module.iconImage">
|
||||
<img
|
||||
:src="module.iconImage"
|
||||
class="menu-icon-item-modules-icon-image"
|
||||
transition="all ease-in-out duration-500"
|
||||
absolute right-0 size-16 translate-y-2
|
||||
grayscale-100
|
||||
>
|
||||
</template>
|
||||
</RouterLink>
|
||||
<div p-2>
|
||||
<div v-if="module.configured" size-3 bg="green-500 dark:green-600" rounded-full />
|
||||
<div v-else size-3 bg="neutral-400 dark:neutral-500" rounded-full />
|
||||
</div>
|
||||
</div>
|
||||
<IconStatusItem
|
||||
v-for="module in modulesList"
|
||||
:key="module.id"
|
||||
:title="module.name"
|
||||
:description="module.description"
|
||||
:icon="module.icon"
|
||||
:icon-color="module.iconColor"
|
||||
:icon-image="module.iconImage"
|
||||
:to="module.to"
|
||||
:configured="module.configured"
|
||||
/>
|
||||
</div>
|
||||
<div fixed bottom-0 right-0 z--1 text="neutral-100/80 dark:neutral-500/20">
|
||||
<div text="40" i-lucide:blocks translate-x-10 translate-y-10 />
|
||||
@@ -188,57 +132,3 @@ meta:
|
||||
stageTransition:
|
||||
name: slide
|
||||
</route>
|
||||
|
||||
<style scoped>
|
||||
.menu-icon-item-modules::after {
|
||||
--at-apply: 'bg-dotted-[neutral-200] hover:bg-dotted-[primary-300/50] dark:bg-dotted-[neutral-700/80] dark:hover:bg-dotted-[primary-200/20]';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 10px 10px;
|
||||
content: '';
|
||||
mask-image: linear-gradient(165deg, white 30%, transparent 50%);
|
||||
}
|
||||
|
||||
.menu-icon-item-modules:hover .menu-icon-item-modules-title {
|
||||
--at-apply: text-primary-500;
|
||||
}
|
||||
|
||||
.menu-icon-item-modules:hover .menu-icon-item-modules-description {
|
||||
--at-apply: text-primary-500;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.menu-icon-item-modules:hover .menu-icon-item-modules-icon {
|
||||
--at-apply: text-primary-500;
|
||||
scale: 1.2;
|
||||
opacity: 0.2;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.menu-icon-item-modules:hover .menu-icon-item-modules-icon-color {
|
||||
scale: 1.2;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.menu-icon-item-modules:hover .menu-icon-item-modules-icon-image {
|
||||
scale: 1.2;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.dark .menu-icon-item-modules:hover .menu-icon-item-modules-title {
|
||||
--at-apply: text-primary-400;
|
||||
}
|
||||
|
||||
.dark .menu-icon-item-modules:hover .menu-icon-item-modules-description {
|
||||
--at-apply: text-primary-400;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.dark .menu-icon-item-modules:hover .menu-icon-item-modules-icon {
|
||||
--at-apply: text-primary-400;
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink, useRouter } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import IconStatusItem from '../../../components/Menu/IconStatusItem.vue'
|
||||
|
||||
interface ModelProvider {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
icon?: string
|
||||
iconColor?: string
|
||||
iconImage?: string
|
||||
to: string
|
||||
configured: boolean
|
||||
}
|
||||
@@ -20,6 +25,7 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'openrouter-ai',
|
||||
name: 'OpenRouter',
|
||||
description: 'openrouter.ai',
|
||||
icon: 'i-lobe-icons:openrouter',
|
||||
to: '/settings/providers/openrouter',
|
||||
configured: configuredForOpenRouter.value,
|
||||
@@ -27,6 +33,7 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'openai',
|
||||
name: 'OpenAI',
|
||||
description: 'openai.com',
|
||||
icon: 'i-lobe-icons:openai',
|
||||
to: '/settings/providers/openai',
|
||||
configured: false,
|
||||
@@ -34,6 +41,7 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'ollama-ai',
|
||||
name: 'Ollama',
|
||||
description: 'ollama.com',
|
||||
icon: 'i-lobe-icons:ollama',
|
||||
to: '/settings/providers/ollama',
|
||||
configured: false,
|
||||
@@ -41,13 +49,15 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'vllm',
|
||||
name: 'vLLM',
|
||||
icon: 'i-lobe-icons:vllm-color',
|
||||
description: 'vllm.ai',
|
||||
iconColor: 'i-lobe-icons:vllm-color',
|
||||
to: '/settings/providers/vllm',
|
||||
configured: false,
|
||||
},
|
||||
{
|
||||
id: 'elevenlabs',
|
||||
name: 'ElevenLabs',
|
||||
description: 'elevenlabs.io',
|
||||
icon: 'i-simple-icons:elevenlabs',
|
||||
to: '/settings/providers/elevenlabs',
|
||||
configured: false,
|
||||
@@ -55,6 +65,7 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'xai',
|
||||
name: 'xAI',
|
||||
description: 'x.ai',
|
||||
icon: 'i-lobe-icons:xai',
|
||||
to: '/settings/providers/xai',
|
||||
configured: false,
|
||||
@@ -62,27 +73,31 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'deepseek',
|
||||
name: 'DeepSeek',
|
||||
icon: 'i-lobe-icons:deepseek-color',
|
||||
description: 'deepseek.com',
|
||||
iconColor: 'i-lobe-icons:deepseek-color',
|
||||
to: '/settings/providers/deepseek',
|
||||
configured: false,
|
||||
},
|
||||
{
|
||||
id: 'together-ai',
|
||||
name: 'Together.ai',
|
||||
icon: 'i-lobe-icons:together-color',
|
||||
description: 'together.ai',
|
||||
iconColor: 'i-lobe-icons:together-color',
|
||||
to: '/settings/providers/together',
|
||||
configured: false,
|
||||
},
|
||||
{
|
||||
id: 'novita-ai',
|
||||
name: 'Novita',
|
||||
icon: 'i-lobe-icons:novita-color',
|
||||
description: 'novita.ai',
|
||||
iconColor: 'i-lobe-icons:novita-color',
|
||||
to: '/settings/providers/novita',
|
||||
configured: false,
|
||||
},
|
||||
{
|
||||
id: 'fireworks-ai',
|
||||
name: 'Fireworks.ai',
|
||||
description: 'fireworks.ai',
|
||||
icon: 'i-lobe-icons:fireworks',
|
||||
to: '/settings/providers/fireworks',
|
||||
configured: false,
|
||||
@@ -90,20 +105,23 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
{
|
||||
id: 'cloudflare-workers-ai',
|
||||
name: 'Cloudflare Workers AI',
|
||||
icon: 'i-lobe-icons:cloudflare-color',
|
||||
description: 'cloudflare.com',
|
||||
iconColor: 'i-lobe-icons:cloudflare-color',
|
||||
to: '/settings/providers/cloudflare',
|
||||
configured: false,
|
||||
},
|
||||
{
|
||||
id: 'mistral-ai',
|
||||
name: 'Mistral',
|
||||
icon: 'i-lobe-icons:mistral-color',
|
||||
description: 'mistral.ai',
|
||||
iconColor: 'i-lobe-icons:mistral-color',
|
||||
to: '/settings/providers/mistral',
|
||||
configured: false,
|
||||
},
|
||||
{
|
||||
id: 'moonshot-ai',
|
||||
name: 'Moonshot AI',
|
||||
description: 'moonshot.ai',
|
||||
icon: 'i-lobe-icons:moonshot',
|
||||
to: '/settings/providers/moonshot',
|
||||
configured: false,
|
||||
@@ -125,35 +143,18 @@ const providersList = computed<ModelProvider[]>(() => [
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
<div grid="~ cols-3 gap-2">
|
||||
<div
|
||||
v-for="provider in providersList" :key="provider.id"
|
||||
bg="neutral-300/50 dark:neutral-600" w-full of-hidden rounded-xl
|
||||
flex="~ col 1"
|
||||
>
|
||||
<RouterLink
|
||||
:to="provider.to"
|
||||
bg="neutral-100 dark:neutral-800"
|
||||
hover="bg-neutral-200 dark:bg-neutral-700"
|
||||
transition="all ease-in-out duration-250"
|
||||
h-full w-full flex items-center gap-1.5 rounded-lg px-4 py-5 outline-none
|
||||
class="[&_.provider-icon]:grayscale-100 [&_.provider-icon]:hover:grayscale-0"
|
||||
>
|
||||
<div flex="~ col 1" gap-1.5>
|
||||
<div
|
||||
:class="provider.icon" class="provider-icon size-10"
|
||||
transition="filter duration-250 ease-in-out"
|
||||
/>
|
||||
<div>
|
||||
{{ provider.name }}
|
||||
</div>
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div p-2>
|
||||
<div v-if="provider.configured" size-3 bg="green-500 dark:green-600" rounded-full />
|
||||
<div v-else size-3 bg="neutral-400 dark:neutral-500" rounded-full />
|
||||
</div>
|
||||
</div>
|
||||
<div grid="~ cols-2 gap-2">
|
||||
<IconStatusItem
|
||||
v-for="provider in providersList"
|
||||
:key="provider.id"
|
||||
:title="provider.name"
|
||||
:description="provider.description"
|
||||
:icon="provider.icon"
|
||||
:icon-color="provider.iconColor"
|
||||
:icon-image="provider.iconImage"
|
||||
:to="provider.to"
|
||||
:configured="provider.configured"
|
||||
/>
|
||||
</div>
|
||||
<div fixed bottom-0 right-0 z--1 text="neutral-100/80 dark:neutral-500/20">
|
||||
<div text="40" i-lucide:brain translate-x-10 translate-y-10 />
|
||||
|
||||
Reference in New Issue
Block a user