feat: add better provider support, icons, regen, and a lot more

This commit is contained in:
Zoe
2026-02-12 14:56:13 +00:00
parent d5a5945c03
commit d29f95bacf
124 changed files with 6374 additions and 1861 deletions
+8 -16
View File
@@ -4,6 +4,8 @@ import type { ModelWithProvider, ProviderWithModels } from '~/composables/useMod
import type { Entity } from '@triplit/client';
import { schema } from '#triplit/schema';
const { allModels } = await useModels();
const inputRef = ref<HTMLTextAreaElement | null>(null);
let tempInput = '';
const inputValue = ref('');
@@ -23,17 +25,6 @@ const props = defineProps<{
// Model selection state
const selectedModel = ref<ModelWithProvider | null>(null);
// Get all available models from all providers
const allModels = computed(() => {
if (!props.providers) return [];
return props.providers.flatMap((provider) =>
provider.models.map((model) => ({
...model,
provider,
}))
);
});
// Initialize model selection based on agent's defaultModelId or first available
const initializeModel = () => {
if (selectedModel.value) return;
@@ -147,11 +138,12 @@ onBeforeMount(() => {
onMounted(() => {
inputValue.value = tempInput;
handleInput();
nextTick(() => {
handleInput();
});
});
</script>
<template>
<div :class="['w-full flex max-h-full', $attrs.class]">
<div class="relative w-full flex flex-shrink-1 flex-col gap-3 p-3 rounded-2xl border transition-border ease-in-out duration-300 bg-[var(--color-input)]
@@ -167,10 +159,10 @@ onMounted(() => {
</div>
<!-- Toolbar -->
<div class="flex items-center gap-2">
<div class="flex items-center justify-between gap-2">
<div class="flex-1">
<ModelSelector v-if="providers && providers.length > 0" v-model="selectedModel"
:providers="providers"></ModelSelector>
<ModelSelector v-if="providers !== undefined" v-model="selectedModel" :providers="providers">
</ModelSelector>
</div>
<!-- Send/Stop Button -->
<button aria-label="Send message" @click="handleSubmit" :disabled="!inputValue.trim() && !loading"
+1 -1
View File
@@ -90,7 +90,7 @@ useClickOutside(triggerRef, () => {
<slot name="item-after" :item="item"></slot>
</button>
</template>
<slot name="content"></slot>
<slot name="content" :toggle="toggle"></slot>
</div>
</Transition>
</div>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Arcee';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#f0529c";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'" fill-rule="evenodd"
style="flex: none; line-height: 1;" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M9.553 9.378H4.777V4.835H8.62c.513 0 .932-.42.932-.932V.058h4.544v4.777a4.542 4.542 0 01-4.544 4.543zm-4.776.467H0v4.543h3.845c.512 0 .932.42.932.932v3.845H9.32v-4.777a4.542 4.542 0 00-4.543-4.543zM20.05 9.61a.935.935 0 01-.932-.932V4.835h-4.543V9.61a4.542 4.542 0 004.543 4.544h4.777V9.612H20.05zM9.787 19.166v4.777h4.544v-3.845c0-.513.42-.932.932-.932h3.845V14.62H14.33a4.542 4.542 0 00-4.544 4.544z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'AI21';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#E91E63";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M6.47 17l-.367-1.189H2.718L2.35 17H0l3.398-9.789h2.026L8.864 17H6.47zm-2.052-6.993l-1.17 4.028H5.56l-1.142-4.028zm4.707-2.796h2.23V17h-2.23V7.211zM11.955 15c.1-.483.277-.946.524-1.37.214-.359.482-.68.795-.951.32-.273.658-.52 1.013-.741.28-.168.54-.33.781-.483.222-.14.433-.296.632-.468.172-.148.317-.325.428-.525.107-.199.16-.423.157-.65 0-.392-.104-.674-.313-.846a1.176 1.176 0 00-.775-.259 1.207 1.207 0 00-.863.329c-.231.219-.347.585-.347 1.098H11.8a3.387 3.387 0 01.224-1.245c.146-.377.371-.716.66-.993.306-.29.667-.514 1.06-.657A4.04 4.04 0 0115.183 7c.42-.002.84.057 1.244.175.376.107.73.287 1.04.531.305.246.55.562.714.923.185.419.275.875.265 1.335.005.39-.084.774-.259 1.12-.167.328-.38.63-.632.894-.246.259-.517.49-.808.693-.29.2-.554.37-.789.51-.326.224-.596.417-.809.58a3.872 3.872 0 00-.51.455 1.229 1.229 0 00-.265.434 1.633 1.633 0 00-.074.517h4.078V17h-6.606a9.24 9.24 0 01.183-2zM18.8 8.93a5.05 5.05 0 001.135-.105c.25-.049.484-.156.686-.314.163-.139.28-.324.34-.532.068-.25.1-.51.095-.77H23V17h-2.243v-6.475H18.8V8.93z" />
</svg>
</div>
</template>
File diff suppressed because one or more lines are too long
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Anthropic';
const AVATAR_SCALE = 0.75;
const BACKGROUND_COLOR = "#F1F0E8";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="avatar ? '#141413' : 'currentColor'" fill-rule="evenodd" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Arcee';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#008C8C";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'" fill-rule="evenodd"
style="flex: none; line-height: 1;" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M13.236 2.377L2.751 20.493H0L11.863 0l1.373 2.377zm3.554 6.156l-9.606 11.96H4.13L15.511 6.32l1.279 2.212zm6.908 11.96H14.05l8.406-2.151 1.242 2.15zm-3.42-5.922l-7.843 5.92H8.482l10.597-7.997 1.2 2.077z" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Aya';
const BACKGROUND_COLOR = "#416FDC";
const AVATAR_SCALE = 0.6;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M19.066.131c-.064-.106-.212-.17-.34-.106l-2.39 1.333c-1.206.678-1.968 1.397-2.434 2.772a4.677 4.677 0 00.339 3.746c.063.106.212.17.338.106l2.392-1.333c1.206-.678 1.968-1.397 2.433-2.772A4.677 4.677 0 0019.066.13zM1.926 5.421a.325.325 0 00-.318.318c0 1.714.74 3.258 1.905 4.316C4.867 11.283 6.136 11.6 7.872 11.6H11.3c.169 0 .317-.148.317-.317 0-1.714-.74-3.26-1.904-4.317C8.358 5.739 7.089 5.42 5.353 5.42H1.927zM23.826 10.542a.325.325 0 00-.317-.317v.02h-3.47c-1.757 0-3.047.34-4.423 1.567-1.185 1.036-1.946 2.623-1.946 4.359 0 .169.148.317.317.317h3.47c1.757 0 3.047-.339 4.423-1.566a5.893 5.893 0 001.947-4.38zM0 15.79c0-.233.19-.445.444-.445h4.804c2.433 0 4.21.466 6.115 2.18 1.63 1.46 2.645 3.64 2.666 6.03 0 .233-.19.445-.444.445H8.782c-2.434 0-4.211-.444-6.116-2.158C1.036 20.36.021 18.18 0 15.79z" />
</svg>
</div>
</template>
+29
View File
@@ -0,0 +1,29 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'BaiduCloud';
const BACKGROUND_COLOR = "#2468f2";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path :fill="!avatar && color ? '#5BCA87' : ''"
d="M21.715 5.61l-3.983 2.31a.903.903 0 01-.896 0L12.44 5.384a.903.903 0 00-.897 0L7.156 7.92a.903.903 0 01-.896 0L2.276 5.617 12.002 0l9.713 5.61z" />
<path :fill="!avatar && color ? '#5BCA87' : ''"
d="M18.641 9.467a.89.89 0 00-.438.77v5.072a.896.896 0 01-.445.77l-4.428 2.51a.884.884 0 00-.445.777v4.607l4.429-2.536 5.31-3.047V7.157l-3.983 2.31z" />
<path :fill="!avatar && color ? '#2468f2' : ''"
d="M10.98 18.941a.936.936 0 00-.305-.352l-4.429-2.516a.903.903 0 01-.431-.764v-5.078a.89.89 0 00-.452-.757l-.451-.26L1.38 7.158V18.39l5.311 3.047L11.126 24v-4.608a.881.881 0 00-.146-.45z" />
</svg>
</div>
</template>
+31
View File
@@ -0,0 +1,31 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'ByteDance';
const BACKGROUND_COLOR = "#325AB4";
const AVATAR_SCALE = 0.6;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path :fill="!avatar && color ? '#00C8D2' : ''" :fill-rule="!avatar && color ? 'nonzero' : 'evenodd'"
d="M14.944 18.587l-1.704-.445V10.01l1.824-.462c1-.254 1.84-.461 1.88-.453.032 0 .056 2.235.056 4.972v4.973l-.176-.008c-.104 0-.952-.207-1.88-.446z" />
<path :fill="!avatar && color ? '#3C8CFF' : ''" :fill-rule="!avatar && color ? 'nonzero' : 'evenodd'"
d="M7 16.542c0-2.736.024-4.98.064-4.98.032-.008.872.2 1.88.454l1.816.461-.016 4.05-.024 4.049-1.632.422c-.896.23-1.736.445-1.856.469L7 21.523v-4.98z" />
<path :fill="!avatar && color ? '#78E6DC' : ''" :fill-rule="!avatar && color ? 'nonzero' : 'evenodd'"
d="M19.24 12.477c0-9.03.008-9.515.144-9.475.072.024.784.207 1.576.406.792.207 1.576.405 1.744.445l.296.08-.016 8.56-.024 8.568-1.624.414c-.888.23-1.728.437-1.856.47l-.24.055v-9.523z" />
<path :fill="!avatar && color ? '#325AB4' : ''" :fill-rule="!avatar && color ? 'nonzero' : 'evenodd'"
d="M1 12.509c0-4.678.024-8.505.064-8.505.032 0 .872.207 1.872.454l1.824.461v7.582c0 4.16-.016 7.574-.032 7.574-.024 0-.872.215-1.88.47L1 21.013v-8.505z" />
</svg>
</div>
</template>
+41
View File
@@ -0,0 +1,41 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'ChatGLM';
const BACKGROUND_COLOR = "#4268FA";
const AVATAR_SCALE = 0.75;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<defs>
<linearGradient :id="fill!.id" x1="-18.756%" x2="70.894%" y1="49.371%" y2="90.944%">
<stop offset="0%" stop-color="#504AF4" />
<stop offset="100%" stop-color="#3485FF" />
</linearGradient>
</defs>
<path
d="M9.917 2c4.906 0 10.178 3.947 8.93 10.58-.014.07-.037.14-.057.21l-.003-.277c-.083-3-1.534-8.934-8.87-8.934-3.393 0-8.137 3.054-7.93 8.158-.04 4.778 3.555 8.4 7.95 8.332l.073-.001c1.2-.033 2.763-.429 3.1-1.657.063-.031.26.534.268.598.048.256.112.369.192.34.981-.348 2.286-1.222 1.952-2.38-.176-.61-1.775-.147-1.921-.347.418-.979 2.234-.926 3.153-.716.443.102.657.38 1.012.442.29.052.981-.2.96.242-1.5 3.042-4.893 5.41-8.808 5.41C3.654 22 0 16.574 0 11.737 0 5.947 4.959 2 9.917 2zM9.9 5.3c.484 0 1.125.225 1.38.585 3.669.145 4.313 2.686 4.694 5.444.255 1.838.315 2.3.182 1.387l.083.59c.068.448.554.737.982.516.144-.075.254-.231.328-.47a.2.2 0 01.258-.13l.625.22a.2.2 0 01.124.238 2.172 2.172 0 01-.51.92c-.878.917-2.757.664-3.08-.62-.14-.554-.055-.626-.345-1.242-.292-.621-1.238-.709-1.69-.295-.345.315-.407.805-.406 1.282L12.6 15.9a.9.9 0 01-.9.9h-1.4a.9.9 0 01-.9-.9v-.65a1.15 1.15 0 10-2.3 0v.65a.9.9 0 01-.9.9H4.8a.9.9 0 01-.9-.9l.035-3.239c.012-1.884.356-3.658 2.47-4.134.2-.045.252.13.29.342.025.154.043.252.053.294.701 3.058 1.75 4.299 3.144 3.722l.66-.331.254-.13c.158-.082.25-.131.276-.15.012-.01-.165-.206-.407-.464l-1.012-1.067a8.925 8.925 0 01-.199-.216c-.047-.034-.116.068-.208.306-.074.157-.251.252-.272.326-.013.058.108.298.362.72.164.288.22.508-.31.343-1.04-.8-1.518-2.273-1.684-3.725-.004-.035-.162-1.913-.162-1.913a1.2 1.2 0 011.113-1.281L9.9 5.3zm12.994 8.68c.037.697-.403.704-1.213.591l-1.783-.276c-.265-.053-.385-.099-.313-.147.47-.315 3.268-.93 3.31-.168zm-.915-.083l-.926.042c-.85.077-1.452.24.338.336l.103.003c.815.012 1.264-.359.485-.381zm1.667-3.601h.01c.79.398.067 1.03-.65 1.393-.14.07-.491.176-1.052.315-.241.04-.457.092-.333.16l.01.005c1.952.958-3.123 1.534-2.495 1.285l.38-.148c.68-.266 1.614-.682 1.666-1.337.038-.48 1.253-.442 1.493-.968.048-.106 0-.236-.144-.389-.05-.047-.094-.094-.107-.148-.073-.305.7-.431 1.222-.168zm-2.568-.474c-.135 1.198-2.479 4.192-1.949 2.863l.017-.042c.298-.717.376-2.221 1.337-3.221.25-.26.636.035.595.4zm-7.976-.253c.02-.694 1.002-.968 1.346-.347.01-1.274-1.941-.768-1.346.347z"
:fill="fill!.fill" fill-rule="evenodd" />
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M9.917 2c4.906 0 10.178 3.947 8.93 10.58-.014.07-.037.14-.057.21l-.003-.277c-.083-3-1.534-8.934-8.87-8.934-3.393 0-8.137 3.054-7.93 8.158-.04 4.778 3.555 8.4 7.95 8.332l.073-.001c1.2-.033 2.763-.429 3.1-1.657.063-.031.26.534.268.598.048.256.112.369.192.34.981-.348 2.286-1.222 1.952-2.38-.176-.61-1.775-.147-1.921-.347.418-.979 2.234-.926 3.153-.716.443.102.657.38 1.012.442.29.052.981-.2.96.242C17.226 19.632 13.833 22 9.918 22 3.654 22 0 16.574 0 11.737 0 5.947 4.959 2 9.917 2zM9.9 5.3c.484 0 1.125.225 1.38.585 3.669.145 4.313 2.686 4.694 5.444.255 1.838.315 2.3.182 1.387l.083.59c.068.448.554.737.982.516.144-.075.254-.231.328-.47a.2.2 0 01.258-.13l.625.22a.2.2 0 01.124.238 2.172 2.172 0 01-.51.92c-.878.917-2.757.664-3.08-.62-.14-.554-.055-.626-.345-1.242-.292-.621-1.238-.709-1.69-.295-.345.315-.407.805-.406 1.282L12.6 15.9a.9.9 0 01-.9.9h-1.4a.9.9 0 01-.9-.9v-.65a1.15 1.15 0 10-2.3 0v.65a.9.9 0 01-.9.9H4.8a.9.9 0 01-.9-.9l.035-3.239c.012-1.884.356-3.658 2.47-4.134.2-.045.252.13.29.342.025.154.043.252.053.294.701 3.058 1.75 4.299 3.144 3.722l.66-.331.254-.13c.158-.082.25-.131.276-.15.012-.01-.165-.206-.407-.464l-1.012-1.067a8.925 8.925 0 01-.199-.216c-.047-.034-.116.068-.208.306-.074.157-.251.252-.272.326-.013.058.108.298.362.72.164.288.22.508-.31.343-1.04-.8-1.518-2.273-1.684-3.725-.004-.035-.162-1.913-.162-1.913a1.2 1.2 0 011.113-1.281L9.9 5.3zm12.994 8.68c.037.697-.403.704-1.213.591l-1.783-.276c-.265-.053-.385-.099-.313-.147.47-.315 3.268-.93 3.31-.168zm-.915-.083l-.926.042c-.85.077-1.452.24.338.336l.103.003c.815.012 1.264-.359.485-.381zm1.667-3.601h.01c.79.398.067 1.03-.65 1.393-.14.07-.491.176-1.052.315-.241.04-.457.092-.333.16l.01.005c1.952.958-3.123 1.534-2.495 1.285l.38-.148c.68-.266 1.614-.682 1.666-1.337.038-.48 1.253-.442 1.493-.968.048-.106 0-.236-.144-.389-.05-.047-.094-.094-.107-.148-.073-.305.7-.431 1.222-.168zm-2.568-.474c-.135 1.198-2.479 4.192-1.949 2.863l.017-.042c.298-.717.376-2.221 1.337-3.221.25-.26.636.035.595.4zm-7.976-.253c.02-.694 1.002-.968 1.346-.347.01-1.274-1.941-.768-1.346.347z" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Claude';
const BACKGROUND_COLOR = "#D97757";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" />
</svg>
</div>
</template>
+30
View File
@@ -0,0 +1,30 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Cohere';
const BACKGROUND_COLOR = "#151617";
const AVATAR_SCALE = 0.6;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="!avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size"
style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path clip-rule="evenodd" :fill="color ? '#39594D' : ''"
d="M8.128 14.099c.592 0 1.77-.033 3.398-.703 1.897-.781 5.672-2.2 8.395-3.656 1.905-1.018 2.74-2.366 2.74-4.18A4.56 4.56 0 0018.1 1H7.549A6.55 6.55 0 001 7.55c0 3.617 2.745 6.549 7.128 6.549z" />
<path clip-rule="evenodd" :fill="color ? '#D18EE2' : ''"
d="M9.912 18.61a4.387 4.387 0 012.705-4.052l3.323-1.38c3.361-1.394 7.06 1.076 7.06 4.715a5.104 5.104 0 01-5.105 5.104l-3.597-.001a4.386 4.386 0 01-4.386-4.387z" />
<path :fill="color ? '#FF7759' : ''"
d="M4.776 14.962A3.775 3.775 0 001 18.738v.489a3.776 3.776 0 007.551 0v-.49a3.775 3.775 0 00-3.775-3.775z" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Deep Cogito';
const BACKGROUND_COLOR = "#4e81ee";
const AVATAR_SCALE = 0.7;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M19.74 21.618l4.213-10.534a.412.412 0 00.027-.1l.003-.03a.404.404 0 00-.02-.167l-.007-.026a.44.44 0 00-.045-.085l-.003-.005L16.528.13c-.002-.003 0-.006-.003-.01h.004v.173h-.01c-.102-.254-.277-.285-.445-.248l-.001-.003c-.001.001-.003-.003-.003-.003h-.006s0-.038-.002-.038L4.466 3.143c-.006.002-.012-.005-.018-.003-.022.007-.044.01-.064.021a.42.42 0 00-.039.022c-.013.008-.026.015-.037.025a.464.464 0 00-.118.154l-.01.017L0 13.919c-.004.013.023.026.023.04v.148c0 .019 0 .038.003.057l-.003.001v.006c0 .049-.011.096.016.139H.03c0 .002-.008.002-.008.002l-.01.001c.007.012.008.023.016.034l7.377 9.486v.005a.063.063 0 00.006.005c.006.008.013.013.02.02a.258.258 0 00.02.02c.008.008.015.017.023.023l.02.014a.564.564 0 00.104.053c.011.003.021.008.032.01.005.002.01.005.016.006a.445.445 0 00.172.004l11.597-2.108a.362.362 0 00.041-.01l.011.001h.001c.005 0 .01-.007.015-.009.01-.003.02-.008.03-.013a.248.248 0 00.025-.012l.01-.007a.448.448 0 00.096-.072c.004-.004.006-.01.01-.013a.419.419 0 00.069-.1l.005-.008.003-.006.001-.003.006-.015zm-.77-.894l-7.315-4.573 8.229-6.4-.915 10.973zm-7.82-5.27l-2.782-10.2 11.127 3.71-8.345 6.49zm9.611-5.883l2.267 1.512-3.022 7.554.755-9.066zm1.133-.278l-1.168-.778-1.556-3.113 2.724 3.891zm-2.35-1.22L8.862 4.514l7.122-3.56 3.56 7.12zM7.71 4.129L6.063 3.58 12.1 1.932 7.71 4.128zm-2.881-.053l2.201.734-5.137 6.605 2.936-7.34zm2.725 1.46l2.754 10.096-9.179-1.835 6.425-8.26zM10.3 16.508L7.633 22.73l-6.223-8 8.89 1.777zm.803.313l7.065 4.417-9.716 1.767 2.65-6.184z" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'DeepMind';
const BACKGROUND_COLOR = "#4285F4";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M5.988 1.622A8.539 8.539 0 003.45 8.446c.349 4.408 4.506 7.995 8.276 7.995 3.507 0 4.88-3.061 4.541-5.14a4.318 4.318 0 00-.95-2.073c.632.34 1.244.776 1.809 1.3 1.52 1.415 2.44 3.229 2.587 5.1C20.04 19.763 16.98 24 11.863 24c-1.695 0-3.48-.432-4.98-1.143C2.816 20.937 0 16.797 0 12.002 0 7.571 2.405 3.7 5.988 1.622zM12.136 0c1.696 0 3.481.432 4.98 1.143C21.186 3.063 24 7.203 24 11.998c0 4.431-2.405 8.303-5.988 10.38a8.539 8.539 0 002.538-6.824c-.349-4.408-4.506-7.995-8.276-7.995-3.507 0-4.88 3.061-4.541 5.14a4.3 4.3 0 00.953 2.073 8.723 8.723 0 01-1.81-1.3c-1.52-1.415-2.44-3.227-2.589-5.1C3.96 4.237 7.02 0 12.137 0z" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'DeepSeek';
const BACKGROUND_COLOR = "#4D6BFE";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" />
</svg>
</div>
</template>
+42
View File
@@ -0,0 +1,42 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Essential AI';
const BACKGROUND_COLOR = "linear-gradient(135deg, #5E38A5, #31018C 63%)";
const AVATAR_SCALE = 0.7;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M3.429 10.75c0 2.32.903 4.546 2.51 6.187A8.484 8.484 0 0012 19.5a8.484 8.484 0 006.06-2.563 8.843 8.843 0 002.511-6.187H24c0 3.249-1.264 6.365-3.515 8.662A11.877 11.877 0 0112 23c-3.183 0-6.235-1.29-8.485-3.588A12.38 12.38 0 010 10.75h3.429zm13.714 0a5.306 5.306 0 01-1.507 3.712A5.09 5.09 0 0112 16a5.09 5.09 0 01-3.637-1.538 5.306 5.306 0 01-1.506-3.712h10.286zM12 2c2.273 0 4.453.922 6.06 2.563a8.843 8.843 0 012.511 6.187h-3.428a5.306 5.306 0 00-1.507-3.712A5.09 5.09 0 0012 5.5a5.09 5.09 0 00-3.637 1.538 5.306 5.306 0 00-1.506 3.712H3.43c0-2.32.903-4.546 2.51-6.187A8.484 8.484 0 0112 2z"
:fill="fill!.fill" />
<defs>
<radialGradient cx="0" cy="0" gradientTransform="matrix(21.6991 27.0254 -47.9838 40.1491 2.3 -4.025)"
gradientUnits="userSpaceOnUse" :id="fill!.id" r="1">
<stop stop-color="#6A46AC" />
<stop offset=".63" stop-color="#31008C" />
</radialGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M3.429 10.75c0 2.32.903 4.546 2.51 6.187A8.484 8.484 0 0012 19.5a8.484 8.484 0 006.06-2.563 8.843 8.843 0 002.511-6.187H24c0 3.249-1.264 6.365-3.515 8.662A11.877 11.877 0 0112 23c-3.183 0-6.235-1.29-8.485-3.588A12.38 12.38 0 010 10.75h3.429zm13.714 0a5.306 5.306 0 01-1.507 3.712A5.09 5.09 0 0112 16a5.09 5.09 0 01-3.637-1.538 5.306 5.306 0 01-1.506-3.712h10.286zM12 2c2.273 0 4.453.922 6.06 2.563a8.843 8.843 0 012.511 6.187h-3.428a5.306 5.306 0 00-1.507-3.712A5.09 5.09 0 0012 5.5a5.09 5.09 0 00-3.637 1.538 5.306 5.306 0 00-1.506 3.712H3.43c0-2.32.903-4.546 2.51-6.187A8.484 8.484 0 0112 2z" />
</svg>
</div>
</template>
+54
View File
@@ -0,0 +1,54 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'GLM-V';
const BACKGROUND_COLOR = "#0039C6";
const AVATAR_SCALE = 0.7;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M19.028 3.853a1.31 1.31 0 101.309 1.309 1.31 1.31 0 00-1.31-1.31zm.594 1.987a.664.664 0 11.001-1.329.664.664 0 01-.001 1.329z"
fill="#0039C6" />
<path
d="M19.67 4.814a.348.348 0 000 .695.348.348 0 000-.695zM14.117 16.317a.037.037 0 00-.027-.061c-.314-.005-1.499-.093-2.105-1.082a2.088 2.088 0 01-.089-2.027.04.04 0 000-.033l-1.005-1.81c-.008-.016-.027-.02-.044-.017-2.018.396-3.194 2.104-2.838 3.788.096.458.285.864.578 1.237.748.95 1.859 1.26 2.675 1.235 1.69-.05 2.63-.977 2.856-1.23zM4.766 6.906a9.903 9.903 0 012.487-2.153L5.63 1.829a.58.58 0 00-.514-.298l-4.809.04a.31.31 0 00-.268.46l3.62 6.525c.41-.756.823-1.311 1.106-1.65zM17.52 10.93c-.422.01-.824-.01-1.044.027-.183.031-.657.121-1.14.574a.037.037 0 00.016.062c.329.087.633.237.928.416.63.383 1.205 1.346 1.383 1.8.01.03.051.032.066.004l1.72-3.27c.015-.03-.015-.064-.047-.051-.806.328-1.476.43-1.883.439h.001zM23.687 1.512l-4.546.037a.58.58 0 00-.507.306l-.126.27c.364-.009 2.426.152 4.158 2.308l1.298-2.467a.31.31 0 00-.277-.455v.001zM10.846 21.497l.843 1.517a.31.31 0 00.533.016l1.31-2.064a8.27 8.27 0 01-2.686.53z"
fill="#0039C6" />
<path
d="M22.327 5.379c-1.461-2.435-3.529-2.393-3.529-2.393-.951-.186-1.436-1.263-3.626-1.797-2.046-.5-3.188.112-2.368.451 1.322.548 2.075 2.73 2.462 4.374.494 2.1 1.569 2.14 2.758 2.011.924-.098 2.775-1.058 3.721-1.256 1.03-.216 1.353-.101.58-1.39h.002zm-3.373 1.538a1.757 1.757 0 11.001-3.514 1.757 1.757 0 010 3.514zM12.756 13.364c-.261.623.032 1.57 1.316 1.656.08 0 .12-.053.12-.12 0-.065-.058-.097-.12-.119-.062-.022-.654-.226-.524-.918.042-.23.248-.438.5-.533-.142-.265-.143-.622.06-.965-.343 0-1.015.194-1.353 1v-.001zM16.836 14.624c0-.684-.451-1.41-.805-1.64-.307-.107-.816.06-1.103.487.337.22.553.572.59 1.48.046 1.174-.844 2.184-2.035 2.76 1.764-.34 3.353-1.79 3.353-3.087zM6.201 16.591C2.802 12.248 4.841 6.44 9.795 4.472c-6.168 2.08-8.01 8.89-4.547 13.168 2.299 2.84 6.254 2.937 8.373 2.2-2.571-.213-5.33-.578-7.42-3.248zM15.09 10.445c.03-.012.03-.054 0-.067-.06-.028-.175-.05-.387.004-.411.102-1.42.264-1.34-.226.055-.349.614-.46 1.023-1.128.202-.33-.06-.875-.59-.875-.408 0-.828.186-1.285.67-.64.676-.766 1.31-.518 1.776.234.442.784.633 1.702.48.628-.103.82-.381 1.396-.635v.001z"
fill="#0039C6" />
<path
d="M14.685 12.364s-.492.177-.436.934c.183-.046.239-.028.488.092.114-.338.407-.623.84-.623.078 0 .155.009.23.024-.29-.219-.591-.4-1.12-.427h-.002zM18.253 9.573c-2.444.515-2.48-1.58-4.28-1.985-2.423-.544-5.526.905-6.888 3.545-.554 1.074-1.076 3.178.283 4.97-.854-2.323.434-4.032 1.264-4.702.782-.629 1.738-1.026 2.682-1.267a.035.035 0 00.027-.034c.016-.51.276-1.055.767-1.574.521-.552 1.055-.819 1.631-.819.422 0 .793.215.994.573.185.33.185.725 0 1.026-.11.18-.228.326-.345.447-.022.021-.006.06.024.06 1.132.071 2.063.347 2.893.284 3.134-.24 4.25-1.91 4.652-2.217.365-.278-2.385 1.413-3.706 1.69l.002.003z"
fill="#0039C6" />
<path
d="M15.159 16.452c-.916.916-2.416 2.208-5.318 1.665-2.105-.394-3.719-3.312-3.25-6.832.252-1.883 1.712-3.64 3.082-4.206 2.995-1.24 4.57.081 5.298.897.516.579 1.106 1.773 3.014 1.757 1.456-.012 3.978-1.726 4.678-2.372.24-.221-.021-.343-.418-.308-1.514.136-2.916 1.752-4.976 1.664-1.017-.043-2.298-.742-2.75-2.25-.542-1.812-.553-2.19-1.176-2.297-.653-.112-1.648-.173-3.087.275-1.863.583-4.784 3.436-4.97 6.84-.324 5.884 2.306 8.243 6.36 8.743.459.056 1.462-.066 1.909-.162a5.727 5.727 0 001.565-.967c1.01-.908 1.626-1.918 1.77-3.376.05-.49-.069-1.14-.135-1.425-.023.302-.623 1.376-1.597 2.35v.004z"
fill="#0039C6" />
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox=" 0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M19.028 3.853a1.31 1.31 0 101.309 1.309 1.31 1.31 0 00-1.31-1.31zm.594 1.987a.664.664 0 11.001-1.329.664.664 0 01-.001 1.329z" />
<path
d="M19.67 4.814a.348.348 0 000 .695.348.348 0 000-.695zM14.117 16.317a.037.037 0 00-.027-.061c-.314-.005-1.499-.093-2.105-1.082a2.088 2.088 0 01-.089-2.027.04.04 0 000-.033l-1.005-1.81c-.008-.016-.027-.02-.044-.017-2.018.396-3.194 2.104-2.838 3.788.096.458.285.864.578 1.237.748.95 1.859 1.26 2.675 1.235 1.69-.05 2.63-.977 2.856-1.23zM4.766 6.906a9.903 9.903 0 012.487-2.153L5.63 1.829a.58.58 0 00-.514-.298l-4.809.04a.31.31 0 00-.268.46l3.62 6.525c.41-.756.823-1.311 1.106-1.65zM17.52 10.93c-.422.01-.824-.01-1.044.027-.183.031-.657.121-1.14.574a.037.037 0 00.016.062c.329.087.633.237.928.416.63.383 1.205 1.346 1.383 1.8.01.03.051.032.066.004l1.72-3.27c.015-.03-.015-.064-.047-.051-.806.328-1.476.43-1.883.439h.001zM23.687 1.512l-4.546.037a.58.58 0 00-.507.306l-.126.27c.364-.009 2.426.152 4.158 2.308l1.298-2.467a.31.31 0 00-.277-.455v.001zM10.846 21.497l.843 1.517a.31.31 0 00.533.016l1.31-2.064a8.27 8.27 0 01-2.686.53z" />
<path
d="M22.327 5.379c-1.461-2.435-3.529-2.393-3.529-2.393-.951-.186-1.436-1.263-3.626-1.797-2.046-.5-3.188.112-2.368.451 1.322.548 2.075 2.73 2.462 4.374.494 2.1 1.569 2.14 2.758 2.011.924-.098 2.775-1.058 3.721-1.256 1.03-.216 1.353-.101.58-1.39h.002zm-3.373 1.538a1.757 1.757 0 11.001-3.514 1.757 1.757 0 010 3.514zM12.756 13.364c-.261.623.032 1.57 1.316 1.656.08 0 .12-.053.12-.12 0-.065-.058-.097-.12-.119-.062-.022-.654-.226-.524-.918.042-.23.248-.438.5-.533-.142-.265-.143-.622.06-.965-.343 0-1.015.194-1.353 1v-.001zM16.836 14.624c0-.684-.451-1.41-.805-1.64-.307-.107-.816.06-1.103.487.337.22.553.572.59 1.48.046 1.174-.844 2.184-2.035 2.76 1.764-.34 3.353-1.79 3.353-3.087zM6.201 16.591C2.802 12.248 4.841 6.44 9.795 4.472c-6.168 2.08-8.01 8.89-4.547 13.168 2.299 2.84 6.254 2.937 8.373 2.2-2.571-.213-5.33-.578-7.42-3.248zM15.09 10.445c.03-.012.03-.054 0-.067-.06-.028-.175-.05-.387.004-.411.102-1.42.264-1.34-.226.055-.349.614-.46 1.023-1.128.202-.33-.06-.875-.59-.875-.408 0-.828.186-1.285.67-.64.676-.766 1.31-.518 1.776.234.442.784.633 1.702.48.628-.103.82-.381 1.396-.635v.001z" />
<path
d="M14.685 12.364s-.492.177-.436.934c.183-.046.239-.028.488.092.114-.338.407-.623.84-.623.078 0 .155.009.23.024-.29-.219-.591-.4-1.12-.427h-.002zM18.253 9.573c-2.444.515-2.48-1.58-4.28-1.985-2.423-.544-5.526.905-6.888 3.545-.554 1.074-1.076 3.178.283 4.97-.854-2.323.434-4.032 1.264-4.702.782-.629 1.738-1.026 2.682-1.267a.035.035 0 00.027-.034c.016-.51.276-1.055.767-1.574.521-.552 1.055-.819 1.631-.819.422 0 .793.215.994.573.185.33.185.725 0 1.026-.11.18-.228.326-.345.447-.022.021-.006.06.024.06 1.132.071 2.063.347 2.893.284 3.134-.24 4.25-1.91 4.652-2.217.365-.278-2.385 1.413-3.706 1.69l.002.003z" />
<path
d="M15.159 16.452c-.916.916-2.416 2.208-5.318 1.665-2.105-.394-3.719-3.312-3.25-6.832.252-1.883 1.712-3.64 3.082-4.206 2.995-1.24 4.57.081 5.298.897.516.579 1.106 1.773 3.014 1.757 1.456-.012 3.978-1.726 4.678-2.372.24-.221-.021-.343-.418-.308-1.514.136-2.916 1.752-4.976 1.664-1.017-.043-2.298-.742-2.75-2.25-.542-1.812-.553-2.19-1.176-2.297-.653-.112-1.648-.173-3.087.275-1.863.583-4.784 3.436-4.97 6.84-.324 5.884 2.306 8.243 6.36 8.743.459.056 1.462-.066 1.909-.162a5.727 5.727 0 001.565-.967c1.01-.908 1.626-1.918 1.77-3.376.05-.49-.069-1.14-.135-1.425-.023.302-.623 1.376-1.597 2.35v.004z" />
</svg>
</div>
</template>
+9 -7
View File
@@ -12,15 +12,17 @@ const TITLE = 'Gemini';
const [a, b, c] = useFillIds(TITLE, 3);
const BACKGROUND_COLOR = "#fff";
const AVATAR_SCALE = 0.8;
const d = "M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z";
</script>
<template>
<div
:style="[`max-width: ${size}px; max-height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 9999px; padding: 0.25rem;` : '']">
<svg v-if="color" class="w-full h-full" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<!-- Base Layer -->
@@ -48,11 +50,11 @@ const d = "M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fillRule="evenodd" :height="size" style="flex: none; line-height: 1;"
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" />
<path :d="d" />
</svg>
</div>
</template>
+42
View File
@@ -0,0 +1,42 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Gemma';
const BACKGROUND_COLOR = "linear-gradient(45deg, #446EFF 14%, #2E96FF 40%, #B1C5FF 73%)";
const AVATAR_SCALE = 1;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<defs>
<linearGradient :id="fill!.id" x1="24.419%" x2="75.194%" y1="75.581%" y2="25.194%">
<stop offset="0%" stopColor="#446EFF" />
<stop offset="36.661%" stopColor="#2E96FF" />
<stop offset="83.221%" stopColor="#B1C5FF" />
</linearGradient>
</defs>
<path
d="M12.34 5.953a8.233 8.233 0 01-.247-1.125V3.72a8.25 8.25 0 015.562 2.232H12.34zm-.69 0c.113-.373.199-.755.257-1.145V3.72a8.25 8.25 0 00-5.562 2.232h5.304zm-5.433.187h5.373a7.98 7.98 0 01-.267.696 8.41 8.41 0 01-1.76 2.65L6.216 6.14zm-.264-.187H2.977v.187h2.915a8.436 8.436 0 00-2.357 5.767H0v.186h3.535a8.436 8.436 0 002.357 5.767H2.977v.186h2.976v2.977h.187v-2.915a8.436 8.436 0 005.767 2.357V24h.186v-3.535a8.436 8.436 0 005.767-2.357v2.915h.186v-2.977h2.977v-.186h-2.915a8.436 8.436 0 002.357-5.767H24v-.186h-3.535a8.436 8.436 0 00-2.357-5.767h2.915v-.187h-2.977V2.977h-.186v2.915a8.436 8.436 0 00-5.767-2.357V0h-.186v3.535A8.436 8.436 0 006.14 5.892V2.977h-.187v2.976zm6.14 14.326a8.25 8.25 0 005.562-2.233H12.34c-.108.367-.19.743-.247 1.126v1.107zm-.186-1.087a8.015 8.015 0 00-.258-1.146H6.345a8.25 8.25 0 005.562 2.233v-1.087zm-8.186-7.285h1.107a8.23 8.23 0 001.125-.247V6.345a8.25 8.25 0 00-2.232 5.562zm1.087.186H3.72a8.25 8.25 0 002.232 5.562v-5.304a8.012 8.012 0 00-1.145-.258zm15.47-.186a8.25 8.25 0 00-2.232-5.562v5.315c.367.108.743.19 1.126.247h1.107zm-1.086.186c-.39.058-.772.144-1.146.258v5.304a8.25 8.25 0 002.233-5.562h-1.087zm-1.332 5.69V12.41a7.97 7.97 0 00-.696.267 8.409 8.409 0 00-2.65 1.76l3.346 3.346zm0-6.18v-5.45l-.012-.013h-5.451c.076.235.162.468.26.696a8.698 8.698 0 001.819 2.688 8.698 8.698 0 002.688 1.82c.228.097.46.183.696.259zM6.14 17.848V12.41c.235.078.468.167.696.267a8.403 8.403 0 012.688 1.799 8.404 8.404 0 011.799 2.688c.1.228.19.46.267.696H6.152l-.012-.012zm0-6.245V6.326l3.29 3.29a8.716 8.716 0 01-2.594 1.728 8.14 8.14 0 01-.696.259zm6.257 6.257h5.277l-3.29-3.29a8.716 8.716 0 00-1.728 2.594 8.135 8.135 0 00-.259.696zm-2.347-7.81a9.435 9.435 0 01-2.88 1.96 9.14 9.14 0 012.88 1.94 9.14 9.14 0 011.94 2.88 9.435 9.435 0 011.96-2.88 9.14 9.14 0 012.88-1.94 9.435 9.435 0 01-2.88-1.96 9.434 9.434 0 01-1.96-2.88 9.14 9.14 0 01-1.94 2.88z"
:fill="fill!.fill" fill-rule="evenodd" />
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M12.34 5.953a8.233 8.233 0 01-.247-1.125V3.72a8.25 8.25 0 015.562 2.232H12.34zm-.69 0c.113-.373.199-.755.257-1.145V3.72a8.25 8.25 0 00-5.562 2.232h5.304zm-5.433.187h5.373a7.98 7.98 0 01-.267.696 8.41 8.41 0 01-1.76 2.65L6.216 6.14zm-.264-.187H2.977v.187h2.915a8.436 8.436 0 00-2.357 5.767H0v.186h3.535a8.436 8.436 0 002.357 5.767H2.977v.186h2.976v2.977h.187v-2.915a8.436 8.436 0 005.767 2.357V24h.186v-3.535a8.436 8.436 0 005.767-2.357v2.915h.186v-2.977h2.977v-.186h-2.915a8.436 8.436 0 002.357-5.767H24v-.186h-3.535a8.436 8.436 0 00-2.357-5.767h2.915v-.187h-2.977V2.977h-.186v2.915a8.436 8.436 0 00-5.767-2.357V0h-.186v3.535A8.436 8.436 0 006.14 5.892V2.977h-.187v2.976zm6.14 14.326a8.25 8.25 0 005.562-2.233H12.34c-.108.367-.19.743-.247 1.126v1.107zm-.186-1.087a8.015 8.015 0 00-.258-1.146H6.345a8.25 8.25 0 005.562 2.233v-1.087zm-8.186-7.285h1.107a8.23 8.23 0 001.125-.247V6.345a8.25 8.25 0 00-2.232 5.562zm1.087.186H3.72a8.25 8.25 0 002.232 5.562v-5.304a8.012 8.012 0 00-1.145-.258zm15.47-.186a8.25 8.25 0 00-2.232-5.562v5.315c.367.108.743.19 1.126.247h1.107zm-1.086.186c-.39.058-.772.144-1.146.258v5.304a8.25 8.25 0 002.233-5.562h-1.087zm-1.332 5.69V12.41a7.97 7.97 0 00-.696.267 8.409 8.409 0 00-2.65 1.76l3.346 3.346zm0-6.18v-5.45l-.012-.013h-5.451c.076.235.162.468.26.696a8.698 8.698 0 001.819 2.688 8.698 8.698 0 002.688 1.82c.228.097.46.183.696.259zM6.14 17.848V12.41c.235.078.468.167.696.267a8.403 8.403 0 012.688 1.799 8.404 8.404 0 011.799 2.688c.1.228.19.46.267.696H6.152l-.012-.012zm0-6.245V6.326l3.29 3.29a8.716 8.716 0 01-2.594 1.728 8.14 8.14 0 01-.696.259zm6.257 6.257h5.277l-3.29-3.29a8.716 8.716 0 00-1.728 2.594 8.135 8.135 0 00-.259.696zm-2.347-7.81a9.435 9.435 0 01-2.88 1.96 9.14 9.14 0 012.88 1.94 9.14 9.14 0 011.94 2.88 9.435 9.435 0 011.96-2.88 9.14 9.14 0 012.88-1.94 9.435 9.435 0 01-2.88-1.96 9.434 9.434 0 01-1.96-2.88 9.14 9.14 0 01-1.94 2.88z" />
</svg>
</div>
</template>
+32
View File
@@ -0,0 +1,32 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Google';
const BACKGROUND_COLOR = "#fff";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path :fill="color ? '#4285F4' : ''"
d="M23 12.245c0-.905-.075-1.565-.236-2.25h-10.54v4.083h6.186c-.124 1.014-.797 2.542-2.294 3.569l-.021.136 3.332 2.53.23.022C21.779 18.417 23 15.593 23 12.245z" />
<path :fill="color ? '#34A853' : ''"
d="M12.225 23c3.03 0 5.574-.978 7.433-2.665l-3.542-2.688c-.948.648-2.22 1.1-3.891 1.1a6.745 6.745 0 01-6.386-4.572l-.132.011-3.465 2.628-.045.124C4.043 20.531 7.835 23 12.225 23z" />
<path :fill="color ? '#FBBC05' : ''"
d="M5.84 14.175A6.65 6.65 0 015.463 12c0-.758.138-1.491.361-2.175l-.006-.147-3.508-2.67-.115.054A10.831 10.831 0 001 12c0 1.772.436 3.447 1.197 4.938l3.642-2.763z" />
<path :fill="color ? '#EB4335' : ''"
d="M12.225 5.253c2.108 0 3.529.892 4.34 1.638l3.167-3.031C17.787 2.088 15.255 1 12.225 1 7.834 1 4.043 3.469 2.197 7.062l3.63 2.763a6.77 6.77 0 016.398-4.572z" />
</svg>
</div>
</template>
+6 -4
View File
@@ -6,15 +6,17 @@ defineProps<{
}>();
const TITLE = 'Grok';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#000";
</script>
<template>
<div
:style="[`max-width: ${size}px; max-height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 9999px; padding: 0.25rem;` : '']">
<svg class="w-full h-full" fill="currentColor" fillRule="evenodd" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815" />
+44
View File
@@ -0,0 +1,44 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'ChatGLM';
const BACKGROUND_COLOR = "#0053e0";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<circle cx="12" cy="12" fill="#0055E9" r="12" />
<path
d="M12 0c.518 0 1.028.033 1.528.096A6.188 6.188 0 0112.12 12.28l-.12.001c-2.99 0-5.242 2.179-5.554 5.11-.223 2.086.353 4.412 2.242 6.146C3.672 22.1 0 17.479 0 12 0 5.373 5.373 0 12 0z"
fill="#A8DFF5" />
<path
d="M5.286 5a2.438 2.438 0 01.682 3.38c-3.962 5.966-3.215 10.743 2.648 15.136C3.636 22.056 0 17.452 0 12c0-1.787.39-3.482 1.09-5.006.253-.435.525-.872.817-1.311A2.438 2.438 0 015.286 5z"
fill="#0055E9" />
<path
d="M12.98.04c.272.021.543.053.81.093.583.106 1.117.254 1.538.44 6.638 2.927 8.07 10.052 1.748 15.642a4.125 4.125 0 01-5.822-.358c-1.51-1.706-1.3-4.184.357-5.822.858-.848 3.108-1.223 4.045-2.441 1.257-1.634 2.122-6.009-2.523-7.506L12.98.039z"
fill="#00BCFF" />
<path
d="M13.528.096A6.187 6.187 0 0112 12.281a5.75 5.75 0 00-1.71.255c.147-.905.595-1.784 1.321-2.501.858-.848 3.108-1.223 4.045-2.441 1.27-1.651 2.14-6.104-2.676-7.554.184.014.367.033.548.056z"
fill="#ECECEE" />
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0zm1.652 1.123l-.01-.001c.533.097 1.023.233 1.41.404 6.084 2.683 7.396 9.214 1.601 14.338a3.781 3.781 0 01-5.337-.328 3.654 3.654 0 01-.884-3.044c-1.934.6-3.295 2.305-3.524 4.45-.204 1.912.324 4.044 2.056 5.634l.245.067C10.1 22.876 11.036 23 12 23c6.075 0 11-4.925 11-11 0-5.513-4.056-10.08-9.348-10.877zM2.748 6.21c-.178.269-.348.536-.51.803l-.235.394.078-.167A10.957 10.957 0 001 12c0 4.919 3.228 9.083 7.682 10.49l.214.065C3.523 18.528 2.84 14.149 6.47 8.68A2.234 2.234 0 102.748 6.21zm10.157-5.172c4.408 1.33 3.61 5.41 2.447 6.924-.86 1.117-2.922 1.46-3.708 2.238-.666.657-1.077 1.462-1.212 2.291A5.303 5.303 0 0112 12.258a5.672 5.672 0 001.404-11.169 10.51 10.51 0 00-.5-.052z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'IBM';
const AVATAR_SCALE = 0.75;
const BACKGROUND_COLOR = "#0F62FE";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path clipRule="evenodd"
d="M24 16.333V17h-3.158v-.667H24zm-7.579 0V17h-3.158v-.667h3.158zm2.464 0L18.63 17l-.25-.667h.504zm-7.075 0a2.528 2.528 0 01-1.717.667h-5.04v-.667h6.757zm-7.389 0V17H0v-.667h4.421zm12-1.333v.667h-3.158V15h3.158zm2.958 0l-.246.667h-1L17.885 15h1.494zm-6.937 0c-.057.237-.148.46-.265.667H5.053V15h7.39zm-8.02 0v.667H0V15h4.421zM24 15v.667h-3.158V15H24zm-1.263-1.333v.666h-1.895v-.666h1.895zm-6.316 0v.666h-1.895v-.666h1.895zm3.453 0l-.248.666h-1.989l-.25-.666h2.487zm-7.52 0c.056.212.088.435.088.666h-2.337v-.666h2.249zm-4.143 0v.666H6.316v-.666H8.21zm-5.053 0v.666H1.263v-.666h1.895zm19.579-1.334V13h-1.895v-.667h1.895zm-6.316 0V13h-1.895v-.667h1.895zm3.948 0l-.247.667h-2.987l-.245-.667h3.48zm-8.792 0c.218.188.405.414.55.667H6.315v-.667h5.26zm-8.42 0V13H1.264v-.667h1.895zM18.456 11l.177.539.176-.539h3.929v.667h-1.895v-.613l-.215.613H16.63l-.209-.613v.613h-1.895V11h3.929zM3.158 11v.667H1.263V11h1.895zm8.968 0a2.555 2.555 0 01-.55.667h-5.26V11h5.81zm10.61-1.333v.666h-3.709l.224-.666h3.486zm-4.722 0l.224.666h-3.712v-.666h3.488zm-5.572 0c0 .23-.032.454-.088.666h-2.249v-.666h2.337zm-4.231 0v.666H6.316v-.666H8.21zm-5.053 0v.666H1.263v-.666h1.895zm14.419-1.334l.22.667h-4.534v-.667h4.314zm6.423 0V9h-4.536l.229-.667H24zm-11.823 0c.117.206.208.43.265.667h-7.39v-.667h7.125zm-7.756 0V9H0v-.667h4.421zM17.133 7l.224.667h-4.094V7h3.87zM24 7v.667h-4.089L20.13 7H24zM10.093 7c.662 0 1.264.253 1.717.667H5.053V7h5.04zM4.42 7v.667H0V7h4.421z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Inception';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#fff";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="avatar ? '#000' : 'currentColor'" fill-rule="evenodd" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M14.767 1H7.884L1 7.883v6.884h6.884V7.883h6.883V1zM9.234 23h6.882L23 16.116V9.233h-6.884v6.883H9.234V23z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Inflection';
const AVATAR_SCALE = 0.6;
const BACKGROUND_COLOR = "#038247";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M8.341 24c-.53 0-.841-.308-.841-.824v-.271c0-.514.248-.755.708-.926l1.025-.343c.708-.271.954-.583.954-1.303V3.667c0-.72-.246-1.029-.954-1.303L8.2 2.02c-.46-.171-.701-.408-.701-.926V.824C7.5.309 7.818 0 8.348 0h6.968c.531 0 .85.309.85.824v.271c0 .514-.249.755-.709.926l-1.031.34c-.743.272-.992.583-.992 1.303v16.664c0 .72.249 1.028.992 1.303l1.024.342c.46.172.708.408.708.926v.272c0 .515-.318.824-.85.824L8.342 24z" />
</svg>
</div>
</template>
+53
View File
@@ -0,0 +1,53 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'InternLM';
const BACKGROUND_COLOR = "#1B3882";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path :fill="color ? '#858599' : ''"
d="M5.54 19.662s2.24-.25 2.365-.29c.125-.042 2.45.082 2.45.082l1.493.374 1.37.498.373.125 2.033-.748 2.45-.373 1.659.041 1.286.166.54.166-.042 2.242-1.12-.291-2.159-.166-1.494.124-1.286.291-1.161.374-.83.332-1.744-.664-2.116-.416H7.158l-1.618.291v-2.158z"
fillOpacity=".5" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M13.46 23c-3.773-2.078-7.78-.832-7.82-.819l-.453.144v-2.86l.24-.078c.174-.056 4.324-1.354 8.366.871l-.333.607c-3.244-1.786-6.66-1.115-7.58-.885V21.4c1.203-.286 4.595-.834 7.913.993L13.46 23V23z" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M13.413 23l-.334-.607c3.319-1.827 6.71-1.279 7.914-.993v-1.419c-.92-.231-4.337-.9-7.58.885l-.334-.607c4.042-2.225 8.192-.927 8.366-.87l.24.077v2.862l-.452-.147c-.04-.013-4.046-1.258-7.82.819z" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M17.756 16.476a.904.904 0 00-.264-.651.822.822 0 00-.632-.255.997.997 0 00-.644.293c-.175.212-.26.433-.259.685a.874.874 0 00.256.632c.175.187.39.272.644.255.246 0 .459-.1.633-.293a.97.97 0 00.268-.667l-.002.001zM10.769 17.143a.928.928 0 00.264-.658.993.993 0 00-.262-.679.908.908 0 00-.633-.227 1.089 1.089 0 00-.644.274 1.005 1.005 0 00-.265.685c0 .25.089.465.264.643.17.187.383.271.636.255.26-.008.471-.1.639-.293z"
fillOpacity=".5" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M13.4 19.65c-.693 0-1.374-.058-1.895-.192-1.403-.36-2.52-1.01-3.413-1.986-.929-1.017-.988-1.996-1.05-3.033-.008-.133-.016-.267-.026-.405-.083-1.147-.626-2.941-.631-2.958l-.043-.141.086-.119A6.966 6.966 0 019.44 8.441c1.128-.47 2.47-.716 4.105-.752 2.721-.159 5.688.973 7.056 2.692l.086.109-.62 2.89c-.006.155-.052.968-.276 2.63-.263 1.947-2.668 3.061-3.903 3.377-.628.162-1.57.262-2.488.262zm-6.41-8.603c.134.46.538 1.912.613 2.944.01.139.018.277.026.412.06 1.01.109 1.809.896 2.672.815.89 1.837 1.484 3.125 1.814 1.092.28 3.061.194 4.091-.071.887-.228 3.24-1.19 3.468-2.889.24-1.786.272-2.593.273-2.6v-.026l.572-2.664c-.9-1.728-1.433-2.082-2.578-2.514-1.206-.455-2.677.075-3.903.148h-.01c-3.126.068-5.418-.935-6.574 2.773h.001z" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M19.545 10.862c.223.573.317.86.526 1.386 0 0 .552-.887.727-1.522.175-.641.068-1.458-.07-2.221-.344-1.282-.612-1.381-1.647-2.14a8.668 8.668 0 00-1.565-.84 12.303 12.303 0 00-3.83-.8 12.175 12.175 0 00-3.842.57 8.258 8.258 0 00-1.602.745c-1.075.695-1.823 1.674-2.24 2.932-.112.473-.29.927-.245 1.365.2 2.01 1.512 2.893 1.422 2.409-.117-.625-.042-.994.119-1.592.304-1.13 1.025-1.965 2.163-2.497h.004c.221-.062 1.344-.7 2.834.062.396.17 1.088.629 1.434.618.347-.01.846-.345 1.21-.572.683-.428 1.33-.574 2.184-.303 1.243.39 2.005 1.274 2.418 2.401z" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M1.217 9.86l.934.274 1.071-.055.77-.522.825-.742.604-.825.77-.797.741-.467 1.016-.22-.687 1.209-.577.576-1.209 1.676-.549.825-.632.44-.907.604s-.797.165-.879.192c-.081.028-.741.138-.741.138l-.577-.028-.44-.137.412-.825.056-.604v-.714l-.001.002z"
fillOpacity=".5" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M3.607 5.108l-.083.63-.22.441-.274.412-.688.412.825.412.907.357.934.193.412.028.99-.688 1.043-.934-.357-.11-1.29-.137-.963-.358-.687-.356-.55-.302z"
fillOpacity=".5" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M1.521 12.399c-.29 0-.596-.018-.917-.051L0 12.278l.298-.19.159-.173c.022-.033.085-.144.19-.377v-.002c.17-.365.228-.975.177-1.813l-.036-.564.435.36c.454.376 1.024.466 1.74.272.64-.182 1.337-.78 2.068-1.774.673-.968 1.64-1.624 2.88-1.952l.116-.031.783.61-.704-.05a2.182 2.182 0 00-.191.206l-.014.017-.018.016c-.404.718-.982 1.426-1.56 2.201l-.008.014c-.089.156-.18.317-.279.477v.009l-.032.051c-.592.962-1.243 1.683-1.935 2.145-.635.445-1.49.67-2.548.67v-.001zm-.5-.505c1.194.077 2.124-.112 2.77-.567l.005-.003c.626-.417 1.223-1.078 1.773-1.964a.244.244 0 01.041-.074c.098-.159.19-.321.28-.48v-.001c.392-.75.842-1.417 1.343-1.988-.748.33-1.341.82-1.8 1.481l-.004.006c-.808 1.101-1.572 1.742-2.333 1.958-.673.182-1.263.15-1.764-.095.01.69-.068 1.207-.237 1.573-.026.058-.05.108-.072.153h-.002z" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M1.577 12.399c-.29 0-.596-.018-.917-.051l.05-.486c1.371.145 2.425-.035 3.136-.535l.006-.003c.626-.417 1.222-1.078 1.772-1.963a.277.277 0 01.079-.116l.32.37a.22.22 0 00.072-.141l-.005.058-.03.052c-.592.962-1.243 1.683-1.935 2.145-.636.445-1.49.67-2.548.67zM5.751 7.717a9.381 9.381 0 01-.311.016c-.823.029-1.7-.2-2.615-.682.792-.512 1.013-1.096.982-1.621.189.15.386.281.591.393 1.345.758 2.766.878 5.195.198l-.165-.46c-2.308.637-3.558.532-4.793-.165a3.803 3.803 0 01-.967-.759l-.033-.036-.5-.542.094.697c.014.058.3 1.417-1.01 2.08l-.398.201.383.228c1.073.638 2.114.96 3.094.96l.453-.507v-.001zM11.93 15.21a.405.405 0 01-.366-.253c-.326-.745-.582-1.055-.969-1.525-.39-.474-1.223-1.277-1.36-1.393-.184-.156-.236-.368-.126-.515.055-.072.219-.219.556.005.683.452 2.198 1.812 2.608 2.904.15.397 0 .667-.201.749a.367.367 0 01-.143.028h.001zM14.877 15.23a.37.37 0 01-.146-.03c-.201-.085-.346-.357-.192-.752.424-1.086 1.957-2.427 2.645-2.87.34-.22.502-.072.556.002.109.147.054.358-.131.512-.139.115-.982.905-1.379 1.376-.392.465-.651.772-.988 1.513a.404.404 0 01-.364.25v-.002z" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M16.382 3.316c.01-.566-.265-1.054-.827-1.462-.57-.4-1.252-.602-2.045-.608-.792-.006-1.471.189-2.031.587-.563.401-.85.884-.857 1.445.002.176.017.336.04.486 2.1-.676 3.981-.62 5.64.167.045-.21.072-.414.08-.616v.001z"
fillOpacity=".5" />
<path :fill="color && !avatar ? '#858599' : ''"
d="M7.6 14.716l-.01-.024a1.824 1.824 0 01-.072-.192l-.059-.189-.388.013-.03.326c.002.013.009.044.016.1v.011c.003.02.004.036.008.052a.555.555 0 01-.164.431c-.112.12-.258.172-.45.164-.24-.01-.32-.105-.358-.176-.105-.19-.112-.291-.107-.336.015-.123.04-.254.077-.385.055-.206.013-.444-.126-.708l-.143-.272a1.787 1.787 0 01-.14-.384.974.974 0 01.022-.458.641.641 0 01.264-.374.695.695 0 01.496-.1l.015.002h.015c.192 0 .349.02.469.051a2.208 2.208 0 01-.027-.58 1.5 1.5 0 00-.38-.107H6.52l-.095-.008a1.369 1.369 0 00-.826.189c-.272.16-.456.394-.544.7a1.691 1.691 0 00-.03.815c.008.041.017.09.033.136.043.135.113.285.21.446.148.243.193.323.205.35l.005.009c.018.033.022.084.01.15l-.005.023a7.4 7.4 0 01-.034.172c-.115.298-.127.574-.036.815a.964.964 0 00.486.558c.134.066.288.108.46.125.094.01.192.01.294.005a.957.957 0 00.784-.454 1.13 1.13 0 00.17-.87l-.004-.026H7.6zM21.925 12.059a1.165 1.165 0 00-.556-.697h-.001a1.325 1.325 0 00-.797-.175l-.031.003-.093.011h-.008a1.553 1.553 0 00-.528.197 1.959 1.959 0 01-.016.563l.1-.04c.128-.053.302-.095.532-.13l.013-.002a.567.567 0 01.474.103c.155.113.26.254.32.428.05.164.057.305.022.43a2.77 2.77 0 01-.144.41 9.169 9.169 0 01-.191.383.908.908 0 00-.102.57c.024.176.05.322.078.446a.608.608 0 01-.036.329l-.003.006c-.037.117-.235.18-.35.208-.263.063-.381-.033-.451-.12a.78.78 0 01-.173-.51l.004-.053a.806.806 0 01.014-.093l-.008-.337-.431.01-.046.185a1.285 1.285 0 01-.064.188l-.011.027-.005.029c-.05.307.013.598.187.868l.004.006c.19.274.463.426.787.437.087.005.17.002.25-.004.192-.018.363-.069.509-.15a.992.992 0 00.459-.567c.085-.252.068-.527-.05-.816l-.03-.171-.002-.013a.306.306 0 01.015-.18c.045-.108.106-.233.181-.375.098-.173.162-.32.196-.444a.665.665 0 00.033-.152 1.75 1.75 0 00-.053-.806l.002-.002zM16.59 3.853c.033-.185.053-.359.06-.533.012-.648-.301-1.209-.93-1.667-.609-.427-1.344-.646-2.184-.653-.843-.008-1.575.207-2.176.633-.628.45-.951 1.002-.96 1.643v.006c.002.128.01.256.026.39a.38.38 0 00-.095.23v.01c0 .216.032.433.097.643l.001.006c.09.265.245.518.46.75l.063.068.567.063c.485.282 1.132.433 1.928.447h.103c.587 0 1.1-.072 1.527-.213l.011-.004c.16-.063.316-.138.465-.224.452.043.791-.18.965-.633l.043-.046-.002-.065c.067-.205.184-.608.03-.851h.001zM11.643 2.03c.514-.365 1.149-.547 1.888-.542.74.006 1.38.196 1.903.562.497.361.735.774.727 1.255-.004.089-.01.18-.023.274a6.882 6.882 0 00-2.685-.527c-.816 0-1.67.13-2.558.39a4.038 4.038 0 01-.006-.165c.006-.482.253-.89.754-1.247zm4.513 2.293l-.165.357s-.215.311-.45.272l-.092-.015-.078.049a2.724 2.724 0 01-.454.226c-.397.13-.89.192-1.462.184-.708-.014-1.276-.14-1.688-.38l-.037-.021-.048-.03-.491-.054c-.094-.11-.226-.34-.226-.34l-.135-.392-.008-.204c1.988-.626 3.79-.574 5.355.155.002.029-.02.193-.02.193zm.147.364v-.004l.003.004h-.003z" />
</svg>
</div>
</template>
+51
View File
@@ -0,0 +1,51 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Kwaipilot';
const BACKGROUND_COLOR = "#000";
const AVATAR_SCALE = 0.8;
const [a, b] = useFillIds(TITLE, 2);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M11.765.03C5.327.03.108 5.25.108 11.686c0 3.514 1.556 6.665 4.015 8.804L9.89 8.665h6.451L9.31 23.083c.807.173 1.63.26 2.455.26 6.438 0 11.657-5.22 11.657-11.658S18.202.028 11.765.028V.03z"
:fill="a!.fill" />
<path
d="M4.123 20.489l6.362-13.046c.017-.036.035-.073.055-.11l.086-.18h.005a6.697 6.697 0 015.913-3.551c2.784 0 5.171 1.7 6.184 4.116-1.622-4.485-5.92-7.69-10.963-7.69C5.327.028.108 5.247.108 11.685c0 3.514 1.556 6.666 4.015 8.804z"
:fill="b!.fill" />
<defs>
<linearGradient gradientUnits="userSpaceOnUse" :id="a!.id" x1="13.469" x2="12.557" y1="4.823"
y2="21.302">
<stop offset=".313" stop-color="#9EC0E0" />
<stop offset="1" stop-color="#fff" />
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" :id="b!.id" x1="13.739" x2="5.647" y1="4.229"
y2="17.386">
<stop stop-color="#fff" />
<stop offset="1" stop-color="#BCD5EC" />
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path clipRule="evenodd"
d="M11.765.03C5.327.03.108 5.25.108 11.686c0 3.514 1.556 6.665 4.015 8.804L9.89 8.665h6.451L9.31 23.083c.807.173 1.63.26 2.455.26 6.438 0 11.657-5.22 11.657-11.658S18.202.028 11.765.028V.03z" />
</svg>
</div>
</template>
+24
View File
@@ -0,0 +1,24 @@
All logo SVGs sourced from https://github.com/lobehub/lobe-icons under the MIT
license.
MIT License
Copyright (c) 2023 LobeHub
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Liquid';
const AVATAR_SCALE = 0.75;
const BACKGROUND_COLOR = "#fff";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="avatar ? '#000' : 'currentColor'" fill-rule="evenodd" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M12.028 8.546l-.008.005 3.03 5.25a3.94 3.94 0 01.643 2.162c0 .754-.212 1.46-.58 2.062l6.173-1.991L11.63 0 9.304 3.872l2.724 4.674zM6.837 24l4.85-4.053h-.013c-2.219 0-4.017-1.784-4.017-3.984 0-.794.235-1.534.64-2.156l2.865-4.976-2.381-4.087L2 16.034 6.83 24h.007zM13.737 19.382h-.001L8.222 24h8.182l4.148-6.769-6.815 2.151z" />
</svg>
</div>
</template>
+27
View File
@@ -0,0 +1,27 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'LongCat';
const BACKGROUND_COLOR = "#fff";
const AVATAR_SCALE = 0.7;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? '#000' : 'currentColor'" fill-rule="evenodd" :height="size"
style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path clipRule="evenodd" :fill="color ? '#29E154' : ''"
d="M.507 19.883a.507.507 0 01-.489-.642L4.29 3.745a1.013 1.013 0 011.533-.578l5.622 3.687a1.013 1.013 0 001.11 0L18.2 3.165a1.013 1.013 0 011.532.58l4.25 15.497a.506.506 0 01-.49.64H18.07a6.297 6.297 0 001.53-4.115v-.177a6.09 6.09 0 00-1.513-4.017l-.697-3.495a.438.438 0 00-.694-.266L14.07 9.781a.748.748 0 01-.654.121 5.156 5.156 0 00-2.833 0 .746.746 0 01-.653-.121L7.302 7.81a.435.435 0 00-.688.269l-.675 3.652a5.36 5.36 0 00-1.539 3.76v.333c0 1.474.527 2.9 1.488 4.02l.032.038H.507z" />
<path d="M9.213 16.843h1.52v-3.546h-1.29l-.23 3.546zm5.573 0h-1.52v-3.546h1.29l.23 3.546z" />
</svg>
</div>
</template>
+132
View File
@@ -0,0 +1,132 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Gemma';
const BACKGROUND_COLOR = "linear-gradient(45deg, #007FF8, #0668E1, #007FF8)";
const AVATAR_SCALE = 0.75;
const [a, b, c, d, e, f, g, h, i, j, k, l, m] = useFillIds(TITLE, 13);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M6.897 4h-.024l-.031 2.615h.022c1.715 0 3.046 1.357 5.94 6.246l.175.297.012.02 1.62-2.438-.012-.019a48.763 48.763 0 00-1.098-1.716 28.01 28.01 0 00-1.175-1.629C10.413 4.932 8.812 4 6.896 4z"
:fill="a!.fill" />
<path
d="M6.873 4C4.95 4.01 3.247 5.258 2.02 7.17a4.352 4.352 0 00-.01.017l2.254 1.231.011-.017c.718-1.083 1.61-1.774 2.568-1.785h.021L6.896 4h-.023z"
:fill="b!.fill" />
<path
d="M2.019 7.17l-.011.017C1.2 8.447.598 9.995.274 11.664l-.005.022 2.534.6.004-.022c.27-1.467.786-2.828 1.456-3.845l.011-.017L2.02 7.17z"
:fill="c!.fill" />
<path
d="M2.807 12.264l-2.533-.6-.005.022c-.177.918-.267 1.851-.269 2.786v.023l2.598.233v-.023a12.591 12.591 0 01.21-2.44z"
:fill="d!.fill" />
<path
d="M2.677 15.537a5.462 5.462 0 01-.079-.813v-.022L0 14.468v.024a8.89 8.89 0 00.146 1.652l2.535-.585a4.106 4.106 0 01-.004-.022z"
:fill="e!.fill" />
<path
d="M3.27 16.89c-.284-.31-.484-.756-.589-1.328l-.004-.021-2.535.585.004.021c.192 1.01.568 1.85 1.106 2.487l.014.017 2.018-1.745a2.106 2.106 0 01-.015-.016z"
:fill="f!.fill" />
<path
d="M10.78 9.654c-1.528 2.35-2.454 3.825-2.454 3.825-2.035 3.2-2.739 3.917-3.871 3.917a1.545 1.545 0 01-1.186-.508l-2.017 1.744.014.017C2.01 19.518 3.058 20 4.356 20c1.963 0 3.374-.928 5.884-5.33l1.766-3.13a41.283 41.283 0 00-1.227-1.886z"
fill="#0082FB" />
<path
d="M13.502 5.946l-.016.016c-.4.43-.786.908-1.16 1.416.378.483.768 1.024 1.175 1.63.48-.743.928-1.345 1.367-1.807l.016-.016-1.382-1.24z"
:fill="g!.fill" />
<path
d="M20.918 5.713C19.853 4.633 18.583 4 17.225 4c-1.432 0-2.637.787-3.723 1.944l-.016.016 1.382 1.24.016-.017c.715-.747 1.408-1.12 2.176-1.12.826 0 1.6.39 2.27 1.075l.015.016 1.589-1.425-.016-.016z"
fill="#0082FB" />
<path
d="M23.998 14.125c-.06-3.467-1.27-6.566-3.064-8.396l-.016-.016-1.588 1.424.015.016c1.35 1.392 2.277 3.98 2.361 6.971v.023h2.292v-.022z"
:fill="h!.fill" />
<path
d="M23.998 14.15v-.023h-2.292v.022c.004.14.006.282.006.424 0 .815-.121 1.474-.368 1.95l-.011.022 1.708 1.782.013-.02c.62-.96.946-2.293.946-3.91 0-.083 0-.165-.002-.247z"
:fill="i!.fill" />
<path
d="M21.344 16.52l-.011.02c-.214.402-.519.67-.917.787l.778 2.462a3.493 3.493 0 00.438-.182 3.558 3.558 0 001.366-1.218l.044-.065.012-.02-1.71-1.784z"
:fill="j!.fill" />
<path
d="M19.92 17.393c-.262 0-.492-.039-.718-.14l-.798 2.522c.449.153.927.222 1.46.222.492 0 .943-.073 1.352-.215l-.78-2.462c-.167.05-.341.075-.517.073z"
:fill="k!.fill" />
<path
d="M18.323 16.534l-.014-.017-1.836 1.914.016.017c.637.682 1.246 1.105 1.937 1.337l.797-2.52c-.291-.125-.573-.353-.9-.731z"
:fill="l!.fill" />
<path
d="M18.309 16.515c-.55-.642-1.232-1.712-2.303-3.44l-1.396-2.336-.011-.02-1.62 2.438.012.02.989 1.668c.959 1.61 1.74 2.774 2.493 3.585l.016.016 1.834-1.914a2.353 2.353 0 01-.014-.017z"
:fill="m!.fill" />
<defs>
<linearGradient id={a.id} x1="75.897%" x2="26.312%" y1="89.199%" y2="12.194%">
<stop offset=".06%" stopColor="#0867DF" />
<stop offset="45.39%" stopColor="#0668E1" />
<stop offset="85.91%" stopColor="#0064E0" />
</linearGradient>
<linearGradient :id="b!.id" x1="21.67%" x2="97.068%" y1="75.874%" y2="23.985%">
<stop offset="13.23%" stopColor="#0064DF" />
<stop offset="99.88%" stopColor="#0064E0" />
</linearGradient>
<linearGradient :id="c!.id" x1="38.263%" x2="60.895%" y1="89.127%" y2="16.131%">
<stop offset="1.47%" stopColor="#0072EC" />
<stop offset="68.81%" stopColor="#0064DF" />
</linearGradient>
<linearGradient :id="d!.id" x1="47.032%" x2="52.15%" y1="90.19%" y2="15.745%">
<stop offset="7.31%" stopColor="#007CF6" />
<stop offset="99.43%" stopColor="#0072EC" />
</linearGradient>
<linearGradient :id="e!.id" x1="52.155%" x2="47.591%" y1="58.301%" y2="37.004%">
<stop offset="7.31%" stopColor="#007FF9" />
<stop offset="100%" stopColor="#007CF6" />
</linearGradient>
<linearGradient :id="f!.id" x1="37.689%" x2="61.961%" y1="12.502%" y2="63.624%">
<stop offset="7.31%" stopColor="#007FF9" />
<stop offset="100%" stopColor="#0082FB" />
</linearGradient>
<linearGradient :id="g!.id" x1="34.808%" x2="62.313%" y1="68.859%" y2="23.174%">
<stop offset="27.99%" stopColor="#007FF8" />
<stop offset="91.41%" stopColor="#0082FB" />
</linearGradient>
<linearGradient :id="h!.id" x1="43.762%" x2="57.602%" y1="6.235%" y2="98.514%">
<stop offset="0%" stopColor="#0082FB" />
<stop offset="99.95%" stopColor="#0081FA" />
</linearGradient>
<linearGradient :id="i!.id" x1="60.055%" x2="39.88%" y1="4.661%" y2="69.077%">
<stop offset="6.19%" stopColor="#0081FA" />
<stop offset="100%" stopColor="#0080F9" />
</linearGradient>
<linearGradient :id="j!.id" x1="30.282%" x2="61.081%" y1="59.32%" y2="33.244%">
<stop offset="0%" stopColor="#027AF3" />
<stop offset="100%" stopColor="#0080F9" />
</linearGradient>
<linearGradient :id="k!.id" x1="20.433%" x2="82.112%" y1="50.001%" y2="50.001%">
<stop offset="0%" stopColor="#0377EF" />
<stop offset="99.94%" stopColor="#0279F1" />
</linearGradient>
<linearGradient :id="l!.id" x1="40.303%" x2="72.394%" y1="35.298%" y2="57.811%">
<stop offset=".19%" stopColor="#0471E9" />
<stop offset="100%" stopColor="#0377EF" />
</linearGradient>
<linearGradient :id="m!.id" x1="32.254%" x2="68.003%" y1="19.719%" y2="84.908%">
<stop offset="27.65%" stopColor="#0867DF" />
<stop offset="100%" stopColor="#0471E9" />
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M6.897 4c1.915 0 3.516.932 5.43 3.376l.282-.373c.19-.246.383-.484.58-.71l.313-.35C14.588 4.788 15.792 4 17.225 4c1.273 0 2.469.557 3.491 1.516l.218.213c1.73 1.765 2.917 4.71 3.053 8.026l.011.392.002.25c0 1.501-.28 2.759-.818 3.7l-.14.23-.108.153c-.301.42-.664.758-1.086 1.009l-.265.142-.087.04a3.493 3.493 0 01-.302.118 4.117 4.117 0 01-1.33.208c-.524 0-.996-.067-1.438-.215-.614-.204-1.163-.56-1.726-1.116l-.227-.235c-.753-.812-1.534-1.976-2.493-3.586l-1.43-2.41-.544-.895-1.766 3.13-.343.592C7.597 19.156 6.227 20 4.356 20c-1.21 0-2.205-.42-2.936-1.182l-.168-.184c-.484-.573-.837-1.311-1.043-2.189l-.067-.32a8.69 8.69 0 01-.136-1.288L0 14.468c.002-.745.06-1.49.174-2.23l.1-.573c.298-1.53.828-2.958 1.536-4.157l.209-.34c1.177-1.83 2.789-3.053 4.615-3.16L6.897 4zm-.033 2.615l-.201.01c-.83.083-1.606.673-2.252 1.577l-.138.199-.01.018c-.67 1.017-1.185 2.378-1.456 3.845l-.004.022a12.591 12.591 0 00-.207 2.254l.002.188c.004.18.017.36.04.54l.043.291c.092.503.257.908.486 1.208l.117.137c.303.323.698.492 1.17.492 1.1 0 1.796-.676 3.696-3.641l2.175-3.4.454-.701-.139-.198C9.11 7.3 8.084 6.616 6.864 6.616zm10.196-.552l-.176.007c-.635.048-1.223.359-1.82.933l-.196.198c-.439.462-.887 1.064-1.367 1.807l.266.398c.18.274.362.56.55.858l.293.475 1.396 2.335.695 1.114c.583.926 1.03 1.6 1.408 2.082l.213.262c.282.326.529.54.777.673l.102.05c.227.1.457.138.718.138.176.002.35-.023.518-.073.338-.104.61-.32.813-.637l.095-.163.077-.162c.194-.459.29-1.06.29-1.785l-.006-.449c-.08-2.871-.938-5.372-2.2-6.798l-.176-.189c-.67-.683-1.444-1.074-2.27-1.074z" />
</svg>
</div>
</template>
+27
View File
@@ -0,0 +1,27 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Microsoft';
const BACKGROUND_COLOR = "#2468f2";
const AVATAR_SCALE = 0.6;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path d="M11.49 2H2v9.492h9.492V2h-.002z" :fill="!avatar && color ? '#F25022' : ''" />
<path d="M22 2h-9.492v9.492H22V2z" :fill="!avatar && color ? '#7FBA00' : ''" />
<path d="M11.49 12.508H2V22h9.492v-9.492h-.002z" :fill="!avatar && color ? '#00A4EF' : ''" />
<path d="M22 12.508h-9.492V22H22v-9.492z" :fill="!avatar && color ? '#FFB900' : ''" />
</svg>
</div>
</template>
+41
View File
@@ -0,0 +1,41 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Minimax';
const BACKGROUND_COLOR = "linear-gradient(to right, #E2167E, #FE603C)";
const AVATAR_SCALE = 0.75;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<defs>
<linearGradient :id="fill!.id" x1="0%" x2="100.182%" y1="50.057%" y2="50.057%">
<stop offset="0%" stopColor="#E2167E" />
<stop offset="100%" stopColor="#FE603C" />
</linearGradient>
</defs>
<path
d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z"
:fill="fill!.fill" fillRule="nonzero" />
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z" />
</svg>
</div>
</template>
+37
View File
@@ -0,0 +1,37 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Mistral';
const BACKGROUND_COLOR = "#FA520F";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path d="M3.428 3.4h3.429v3.428H3.428V3.4zm13.714 0h3.43v3.428h-3.43V3.4z" fill="gold" />
<path d="M3.428 6.828h6.857v3.429H3.429V6.828zm10.286 0h6.857v3.429h-6.857V6.828z" fill="#FFAF00" />
<path d="M3.428 10.258h17.144v3.428H3.428v-3.428z" fill="#FF8205" />
<path
d="M3.428 13.686h3.429v3.428H3.428v-3.428zm6.858 0h3.429v3.428h-3.429v-3.428zm6.856 0h3.43v3.428h-3.43v-3.428z"
fill="#FA500F" />
<path d="M0 17.114h10.286v3.429H0v-3.429zm13.714 0H24v3.429H13.714v-3.429z" fill="#E10500" />
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path clip-rule="evenodd"
d="M3.428 3.4h3.429v3.428h3.429v3.429h-.002 3.431V6.828h3.427V3.4h3.43v13.714H24v3.429H13.714v-3.428h-3.428v-3.429h-3.43v3.428h3.43v3.429H0v-3.429h3.428V3.4zm10.286 13.715h3.428v-3.429h-3.427v3.429z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Moonshot';
const AVATAR_SCALE = 0.75;
const BACKGROUND_COLOR = "#16191E";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Morph';
const BACKGROUND_COLOR = "#000";
const AVATAR_SCALE = 0.7;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="avatar || color ? '#99d52a' : 'currentColor'" :fill-rule="color && !avatar ? 'evenodd' : 'nonzero'"
style="flex: none; line-height: 1;" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M7.941 2c.23 0 .452.073.638.21.186.136.325.328.397.55l.593 1.814c.073.221.212.413.397.55.186.136.409.21.638.21h2.791c.23 0 .452-.074.638-.21a1.11 1.11 0 00.397-.55l.594-1.815a1.11 1.11 0 01.397-.55c.185-.136.408-.209.637-.209h1.7c.23 0 .453.073.639.21.185.136.324.328.397.55l.652 1.994c.118.361.41.635.77.728l2.957.752c.236.06.446.199.596.394.15.195.23.436.231.684v9.376c0 .248-.081.488-.231.684a1.09 1.09 0 01-.595.394l-2.957.752a1.086 1.086 0 00-.477.263 1.114 1.114 0 00-.293.465l-.653 1.994a1.11 1.11 0 01-.396.55c-.186.136-.41.21-.638.21h-1.702c-.229 0-.452-.073-.637-.21a1.11 1.11 0 01-.397-.55l-.364-1.11a1.131 1.131 0 01.15-1.002 1.074 1.074 0 01.885-.462h2.85c.29 0 .567-.116.772-.325.204-.208.32-.49.32-.785V6.444c0-.294-.116-.577-.32-.785a1.08 1.08 0 00-.771-.326h-3.273c-.29 0-.567.117-.772.326-.204.208-.32.49-.32.785v7.778c0 .295-.114.578-.319.786a1.08 1.08 0 01-.771.325h-2.182a1.08 1.08 0 01-.771-.325 1.122 1.122 0 01-.32-.786V6.444c0-.294-.115-.577-.32-.785a1.081 1.081 0 00-.77-.326H5.454c-.29 0-.567.117-.772.326-.204.208-.32.49-.32.785v11.112c0 .294.116.577.32.785.205.209.482.326.772.326h2.85a1.075 1.075 0 01.885.461 1.122 1.122 0 01.15 1.001l-.364 1.112a1.11 1.11 0 01-.397.55c-.185.136-.408.209-.637.209H6.24c-.229 0-.452-.073-.638-.21a1.11 1.11 0 01-.397-.55l-.652-1.994a1.114 1.114 0 00-.294-.465 1.086 1.086 0 00-.477-.263l-2.956-.752a1.09 1.09 0 01-.595-.394A1.124 1.124 0 010 16.688V7.312c0-.248.081-.489.231-.684.15-.195.36-.334.595-.394l2.957-.753c.178-.045.342-.136.477-.263.134-.127.235-.287.293-.464l.653-1.995a1.11 1.11 0 01.397-.55C5.788 2.075 6.01 2 6.24 2h1.701z" />
</svg>
</div>
</template>
File diff suppressed because one or more lines are too long
+58
View File
@@ -0,0 +1,58 @@
<script setup lang="ts">
import { useFillIds } from '~/composables/useFillIds';
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Nova';
const [a, b, c] = useFillIds(TITLE, 3);
const BACKGROUND_COLOR = "linear-gradient(-45deg, #ff6200, #e433ff 39.9%, #6842ff 96%)";
const AVATAR_SCALE = 0.7;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 33 32" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<g :mask="a!.fill">
<mask height="32" :id="a!.id" maskUnits="userSpaceOnUse" style="mask-type: luminance;" width="32" x="0"
y="0">
<path d="M31.8 0H0v32h31.8z" fill="#fff" />
</mask>
<mask height="32" :id="b!.id" maskUnits="userSpaceOnUse" style="mask-type: alpha;" width="32" x="0"
y="0">
<path
d="m17.865 23.28 1.533 1.543c.07.07.092.175.055.267l-2.398 6.118A1.24 1.24 0 0 1 15.9 32c-.51 0-.969-.315-1.155-.793l-3.451-8.804-5.582 5.617a.246.246 0 0 1-.35 0l-1.407-1.415a.25.25 0 0 1 0-.352l6.89-6.932a1.3 1.3 0 0 1 .834-.398 1.25 1.25 0 0 1 1.232.79l2.992 7.63 1.557-3.977a.248.248 0 0 1 .408-.085zm8.224-19.3-5.583 5.617-3.45-8.805a1.24 1.24 0 0 0-1.43-.762c-.414.092-.744.407-.899.805l-2.38 6.072a.25.25 0 0 0 .055.267l1.533 1.543c.127.127.34.082.407-.085L15.9 4.655l2.991 7.629a1.24 1.24 0 0 0 2.035.425l6.922-6.965a.25.25 0 0 0 0-.352L26.44 3.977a.246.246 0 0 0-.35 0zM8.578 17.566l-3.953-1.567 7.582-3.01c.49-.195.815-.685.785-1.24a1.3 1.3 0 0 0-.395-.84l-6.886-6.93a.246.246 0 0 0-.35 0L3.954 5.395a.25.25 0 0 0 0 .353l5.583 5.617-8.75 3.472a1.25 1.25 0 0 0 0 2.325l6.079 2.412a.24.24 0 0 0 .266-.055l1.533-1.542a.25.25 0 0 0-.085-.41zm22.434-2.73-6.08-2.412a.24.24 0 0 0-.265.055l-1.533 1.542a.25.25 0 0 0 .084.41L27.172 16l-7.583 3.01a1.255 1.255 0 0 0-.785 1.24c.018.317.172.614.395.84l6.89 6.931a.246.246 0 0 0 .35 0l1.406-1.415a.25.25 0 0 0 0-.352l-5.582-5.617 8.75-3.472a1.25 1.25 0 0 0 0-2.325z"
fill="#fff" />
</mask>
<g :mask="b!.fill">
<path d="M-2.915 34.125h37.448V-2.109H-2.915z" :fill="c!.fill" />
</g>
</g>
<defs>
<linearGradient gradientUnits="userSpaceOnUse" :id="c!.id" x1="33.663" x2="-2.086" y1="33.633"
y2="-1.901">
<stop stopColor="#ff6200" />
<stop offset=".399" stopColor="#e433ff" />
<stop offset=".96" stopColor="#6842ff" />
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 33 32" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="m17.865 23.28 1.533 1.543c.07.07.092.175.055.267l-2.398 6.118A1.24 1.24 0 0 1 15.9 32c-.51 0-.969-.315-1.155-.793l-3.451-8.804-5.582 5.617a.246.246 0 0 1-.35 0l-1.407-1.415a.25.25 0 0 1 0-.352l6.89-6.932a1.3 1.3 0 0 1 .834-.398 1.25 1.25 0 0 1 1.232.79l2.992 7.63 1.557-3.977a.248.248 0 0 1 .408-.085zm8.224-19.3-5.583 5.617-3.45-8.805a1.24 1.24 0 0 0-1.43-.762c-.414.092-.744.407-.899.805l-2.38 6.072a.25.25 0 0 0 .055.267l1.533 1.543c.127.127.34.082.407-.085L15.9 4.655l2.991 7.629a1.24 1.24 0 0 0 2.035.425l6.922-6.965a.25.25 0 0 0 0-.352L26.44 3.977a.246.246 0 0 0-.35 0zM8.578 17.566l-3.953-1.567 7.582-3.01c.49-.195.815-.685.785-1.24a1.3 1.3 0 0 0-.395-.84l-6.886-6.93a.246.246 0 0 0-.35 0L3.954 5.395a.25.25 0 0 0 0 .353l5.583 5.617-8.75 3.472a1.25 1.25 0 0 0 0 2.325l6.079 2.412a.24.24 0 0 0 .266-.055l1.533-1.542a.25.25 0 0 0-.085-.41zm22.434-2.73-6.08-2.412a.24.24 0 0 0-.265.055l-1.533 1.542a.25.25 0 0 0 .084.41L27.172 16l-7.583 3.01a1.255 1.255 0 0 0-.785 1.24c.018.317.172.614.395.84l6.89 6.931a.246.246 0 0 0 .35 0l1.406-1.415a.25.25 0 0 0 0-.352l-5.582-5.617 8.75-3.472a1.25 1.25 0 0 0 0-2.325z"
fill="currentColor" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Nvidia';
const BACKGROUND_COLOR = "#74B71B";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="color && !avatar ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M10.212 8.976V7.62c.127-.01.256-.017.388-.021 3.596-.117 5.957 3.184 5.957 3.184s-2.548 3.647-5.282 3.647a3.227 3.227 0 01-1.063-.175v-4.109c1.4.174 1.681.812 2.523 2.258l1.873-1.627a4.905 4.905 0 00-3.67-1.846 6.594 6.594 0 00-.729.044m0-4.476v2.025c.13-.01.259-.019.388-.024 5.002-.174 8.261 4.226 8.261 4.226s-3.743 4.69-7.643 4.69c-.338 0-.675-.031-1.007-.092v1.25c.278.038.558.057.838.057 3.629 0 6.253-1.91 8.794-4.169.421.347 2.146 1.193 2.501 1.564-2.416 2.083-8.048 3.763-11.24 3.763-.308 0-.603-.02-.894-.048V19.5H24v-15H10.21zm0 9.756v1.068c-3.356-.616-4.287-4.21-4.287-4.21a7.173 7.173 0 014.287-2.138v1.172h-.005a3.182 3.182 0 00-2.502 1.178s.615 2.276 2.507 2.931m-5.961-3.3c1.436-1.935 3.604-3.148 5.961-3.336V6.523C5.81 6.887 2 10.723 2 10.723s2.158 6.427 8.21 7.015v-1.166C5.77 16 4.25 10.958 4.25 10.958h-.002z" />
</svg>
</div>
</template>
+52
View File
@@ -0,0 +1,52 @@
<script setup lang="ts">
const props = defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
type?: string;
}>();
const TITLE = 'OpenAI';
const COLOR_GPT_3 = '#19C37D';
const COLOR_GPT_4 = '#AB68FF';
const COLOR_GPT_5 = '#F86AA4';
const COLOR_O_1 = '#F9C322';
const COLOR_OSS = '#0099FF';
const BACKGROUND_COLOR = "#0000FE";
const AVATAR_SCALE = 0.7;
const background = computed(() => {
switch (props.type) {
case 'gpt3':
return COLOR_GPT_3;
case 'gpt4':
return COLOR_GPT_4;
case 'gpt5':
return COLOR_GPT_5;
case 'o3':
case 'o1':
return COLOR_O_1;
case 'oss':
return COLOR_OSS;
case 'platform':
return BACKGROUND_COLOR;
default:
return BACKGROUND_COLOR;
}
});
</script>
<template>
<div
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${background}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'OpenRouter';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#6566F1";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z" />
</svg>
</div>
</template>
+37
View File
@@ -0,0 +1,37 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'PaLM';
const BACKGROUND_COLOR = "#FFF";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path :fill="color ? '#F9AB00' : ''"
d="M12 22.926c.928 0 1.679-.752 1.679-1.68V6.696h-3.358v14.552c0 .927.751 1.679 1.679 1.679z" />
<path :fill="color ? '#5BB974' : ''"
d="M18.69 12.005A5.819 5.819 0 0012 10.904l7.188 7.188c.296.296.807.179.933-.22a5.815 5.815 0 00-1.431-5.867z" />
<path :fill="color ? '#129EAF' : ''"
d="M5.31 12.005A5.819 5.819 0 0112 10.904l-7.188 7.188a.562.562 0 01-.933-.22 5.815 5.815 0 011.431-5.867z" />
<path :fill="color ? '#AF5CF7' : ''"
d="M18.157 6.426c-2.86 0-5.288 1.875-6.157 4.478h11.367a.629.629 0 00.565-.908c-1.08-2.12-3.26-3.57-5.775-3.57z" />
<path :fill="color ? '#FF8BCB' : ''"
d="M13.188 3.384c-2.023 2.024-2.414 5.064-1.188 7.52l8.038-8.039a.629.629 0 00-.242-1.042c-2.264-.735-4.83-.217-6.608 1.561z" />
<path :fill="color ? '#FA7B17' : ''"
d="M10.812 3.384c2.023 2.024 2.414 5.064 1.188 7.52L3.962 2.865a.629.629 0 01.242-1.042c2.264-.735 4.83-.217 6.608 1.561z" />
<path :fill="color ? '#4285F4' : ''"
d="M5.843 6.426c2.86 0 5.288 1.875 6.157 4.478H.633a.629.629 0 01-.565-.908c1.08-2.12 3.26-3.57 5.775-3.57z" />
</svg>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Perplexity';
const BACKGROUND_COLOR = "#22B8CD";
const AVATAR_SCALE = 0.75;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
:fill="avatar ? '#000' : color ? BACKGROUND_COLOR : 'currentColor'"
:fill-rule="color && !avatar ? 'evenodd' : 'nonzero'" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M19.785 0v7.272H22.5V17.62h-2.935V24l-7.037-6.194v6.145h-1.091v-6.152L4.392 24v-6.465H1.5V7.188h2.884V0l7.053 6.494V.19h1.09v6.49L19.786 0zm-7.257 9.044v7.319l5.946 5.234V14.44l-5.946-5.397zm-1.099-.08l-5.946 5.398v7.235l5.946-5.234V8.965zm8.136 7.58h1.844V8.349H13.46l6.105 5.54v2.655zm-8.982-8.28H2.59v8.195h1.8v-2.576l6.192-5.62zM5.475 2.476v4.71h5.115l-5.115-4.71zm13.219 0l-5.115 4.71h5.115v-4.71z" />
</svg>
</div>
</template>
+39
View File
@@ -0,0 +1,39 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Qwen';
const AVATAR_SCALE = 0.75;
const BACKGROUND_COLOR = "#615ced";
const AVATAR_COLOR = '#fff';
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M12.604 1.34c.393.69.784 1.382 1.174 2.075a.18.18 0 00.157.091h5.552c.174 0 .322.11.446.327l1.454 2.57c.19.337.24.478.024.837-.26.43-.513.864-.76 1.3l-.367.658c-.106.196-.223.28-.04.512l2.652 4.637c.172.301.111.494-.043.77-.437.785-.882 1.564-1.335 2.34-.159.272-.352.375-.68.37-.777-.016-1.552-.01-2.327.016a.099.099 0 00-.081.05 575.097 575.097 0 01-2.705 4.74c-.169.293-.38.363-.725.364-.997.003-2.002.004-3.017.002a.537.537 0 01-.465-.271l-1.335-2.323a.09.09 0 00-.083-.049H4.982c-.285.03-.553-.001-.805-.092l-1.603-2.77a.543.543 0 01-.002-.54l1.207-2.12a.198.198 0 000-.197 550.951 550.951 0 01-1.875-3.272l-.79-1.395c-.16-.31-.173-.496.095-.965.465-.813.927-1.625 1.387-2.436.132-.234.304-.334.584-.335a338.3 338.3 0 012.589-.001.124.124 0 00.107-.063l2.806-4.895a.488.488 0 01.422-.246c.524-.001 1.053 0 1.583-.006L11.704 1c.341-.003.724.032.9.34zm-3.432.403a.06.06 0 00-.052.03L6.254 6.788a.157.157 0 01-.135.078H3.253c-.056 0-.07.025-.041.074l5.81 10.156c.025.042.013.062-.034.063l-2.795.015a.218.218 0 00-.2.116l-1.32 2.31c-.044.078-.021.118.068.118l5.716.008c.046 0 .08.02.104.061l1.403 2.454c.046.081.092.082.139 0l5.006-8.76.783-1.382a.055.055 0 01.096 0l1.424 2.53a.122.122 0 00.107.062l2.763-.02a.04.04 0 00.035-.02.041.041 0 000-.04l-2.9-5.086a.108.108 0 010-.113l.293-.507 1.12-1.977c.024-.041.012-.062-.035-.062H9.2c-.059 0-.073-.026-.043-.077l1.434-2.505a.107.107 0 000-.114L9.225 1.774a.06.06 0 00-.053-.031zm6.29 8.02c.046 0 .058.02.034.06l-.832 1.465-2.613 4.585a.056.056 0 01-.05.029.058.058 0 01-.05-.029L8.498 9.841c-.02-.034-.01-.052.028-.054l.216-.012 6.722-.012z"
:fill="avatar ? AVATAR_COLOR : 'currentColor'" fill-rule="nonzero" />
<defs>
<linearGradient id={id} x1="0%" x2="100%" y1="0%" y2="0%">
<stop offset="0%" stopColor="#6336E7" stopOpacity=".84" />
<stop offset="100%" stopColor="#6F69F7" stopOpacity=".84" />
</linearGradient>
</defs>
</svg>
<svg v-else class="w-full h-full" fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M12.604 1.34c.393.69.784 1.382 1.174 2.075a.18.18 0 00.157.091h5.552c.174 0 .322.11.446.327l1.454 2.57c.19.337.24.478.024.837-.26.43-.513.864-.76 1.3l-.367.658c-.106.196-.223.28-.04.512l2.652 4.637c.172.301.111.494-.043.77-.437.785-.882 1.564-1.335 2.34-.159.272-.352.375-.68.37-.777-.016-1.552-.01-2.327.016a.099.099 0 00-.081.05 575.097 575.097 0 01-2.705 4.74c-.169.293-.38.363-.725.364-.997.003-2.002.004-3.017.002a.537.537 0 01-.465-.271l-1.335-2.323a.09.09 0 00-.083-.049H4.982c-.285.03-.553-.001-.805-.092l-1.603-2.77a.543.543 0 01-.002-.54l1.207-2.12a.198.198 0 000-.197 550.951 550.951 0 01-1.875-3.272l-.79-1.395c-.16-.31-.173-.496.095-.965.465-.813.927-1.625 1.387-2.436.132-.234.304-.334.584-.335a338.3 338.3 0 012.589-.001.124.124 0 00.107-.063l2.806-4.895a.488.488 0 01.422-.246c.524-.001 1.053 0 1.583-.006L11.704 1c.341-.003.724.032.9.34zm-3.432.403a.06.06 0 00-.052.03L6.254 6.788a.157.157 0 01-.135.078H3.253c-.056 0-.07.025-.041.074l5.81 10.156c.025.042.013.062-.034.063l-2.795.015a.218.218 0 00-.2.116l-1.32 2.31c-.044.078-.021.118.068.118l5.716.008c.046 0 .08.02.104.061l1.403 2.454c.046.081.092.082.139 0l5.006-8.76.783-1.382a.055.055 0 01.096 0l1.424 2.53a.122.122 0 00.107.062l2.763-.02a.04.04 0 00.035-.02.041.041 0 000-.04l-2.9-5.086a.108.108 0 010-.113l.293-.507 1.12-1.977c.024-.041.012-.062-.035-.062H9.2c-.059 0-.073-.026-.043-.077l1.434-2.505a.107.107 0 000-.114L9.225 1.774a.06.06 0 00-.053-.031zm6.29 8.02c.046 0 .058.02.034.06l-.832 1.465-2.613 4.585a.056.056 0 01-.05.029.058.058 0 01-.05-.029L8.498 9.841c-.02-.034-.01-.052.028-.054l.216-.012 6.722-.012z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Relace';
const AVATAR_SCALE = 0.66;
const BACKGROUND_COLOR = "#000";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M23 23H1V1h22v22zM2.962 15.232c1.969.3 5.028.42 7.78-.171.904-.195 1.743-.46 2.49-.803-1.395-3.4-1.675-5.766-1.264-7.378.466-1.823 1.799-2.59 2.998-2.602h.01c.688 0 2.117.177 3.081 1.35 1.003 1.22 1.216 3.152.26 5.991-.504 1.493-1.437 2.616-2.594 3.456 1.323 2.993 2.11 4.498 2.588 5.284.223.367.372.564.467.679h2.26V2.962H2.962v12.27zm11.05.827c-.912.413-1.887.711-2.857.92-2.91.626-6.059.527-8.193.234v3.825h13.471c-.527-.92-1.287-2.424-2.421-4.98zm.97-9.82c-.323.005-.87.181-1.112 1.127-.25.975-.161 2.776 1.055 5.84.705-.598 1.233-1.329 1.531-2.213.868-2.576.455-3.67.086-4.119-.406-.494-1.09-.633-1.56-.634z" />
</svg>
</div>
</template>
+42
View File
@@ -0,0 +1,42 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Stepfun';
const BACKGROUND_COLOR = "#005AFF";
const AVATAR_SCALE = 0.6;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M22.012 0h1.032v.927H24v.968h-.956V3.78h-1.032V1.896h-1.878v-.97h1.878V0zM2.6 12.371V1.87h.969v10.502h-.97zm10.423.66h10.95v.918h-6.208v9.579h-4.742V13.03zM5.629 3.333v12.356H0v4.51h10.386V8L20.859 8l-.003-4.668-15.227.001z"
:fill="fill!.fill" fill-rule="evenodd" />
<defs>
<linearGradient gradientUnits="userSpaceOnUse" :id="fill!.id" x1="1.646" x2="18.342" y1="1.916"
y2="22.091">
<stop stop-color="#01A9FF" />
<stop offset="1" stop-color="#0160FF" />
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M22.012 0h1.032v.927H24v.968h-.956V3.78h-1.032V1.896h-1.878v-.97h1.878V0zM2.6 12.371V1.87h.969v10.502h-.97zm10.423.66h10.95v.918h-6.208v9.579h-4.742V13.03zM5.629 3.333v12.356H0v4.51h10.386V8L20.859 8l-.003-4.668-15.227.001z" />
</svg>
</div>
</template>
+50
View File
@@ -0,0 +1,50 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Upstage';
const BACKGROUND_COLOR = "linear-gradient(to bottom, #AEBCFE, #805DFA)";
const AVATAR_SCALE = 0.6;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M19.763 0l-.373 1.297h2.594L22.354 0h-2.591zM16.192 2.27l-.376 1.298h5.52l.37-1.298h-5.514zM12.897 4.54l-.376 1.298h8.166l.37-1.298h-8.16zM2.85 6.81l-.377 1.298h17.565l.37-1.297H2.848zM3.884 9.081l-.376 1.297H19.39l.37-1.297H3.882zM4.088 24l.376-1.297H1.866L1.5 24h2.588zM7.662 21.73l.376-1.297H2.515L2.15 21.73h5.513zM10.957 19.459l.376-1.297h-8.17l-.366 1.297h8.16zM21.005 17.189l.376-1.297H3.812l-.366 1.297h17.559zM19.967 14.919l.376-1.297H4.461l-.366 1.297h15.872zM18.786 12.649l.376-1.297H4.26l-.366 1.297h14.893z"
:fill="fill!.fill" />
<defs>
<linearGradient gradientUnits="userSpaceOnUse" :id="fill!.id" x1="11.927" x2="11.927" y2="24">
<stop offset="0" stop-color="#AEBCFE" />
<stop offset="1" stop-color="#805DFA" />
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path d="M19.763 0l-.373 1.297h2.594L22.354 0h-2.591z" />
<path d="M16.192 2.27l-.376 1.298h5.52l.37-1.298h-5.514z" />
<path d="M12.897 4.54l-.377 1.298h8.167l.37-1.297h-8.16z" />
<path d="M2.85 6.81l-.377 1.298h17.565l.37-1.297H2.85z" />
<path d="M3.884 9.081l-.376 1.297H19.39l.37-1.297H3.883z" />
<path d="M4.088 24l.376-1.297H1.866L1.5 24h2.588z" />
<path d="M7.662 21.73l.376-1.298H2.515L2.15 21.73h5.513z" />
<path d="M10.957 19.46l.377-1.298h-8.17l-.367 1.297h8.16z" />
<path d="M21.005 17.19l.376-1.298H3.812l-.366 1.297h17.559z" />
<path d="M19.967 14.919l.376-1.297H4.461l-.366 1.297h15.872z" />
<path d="M18.787 12.649l.376-1.298H4.26l-.366 1.298h14.893z" />
</svg>
</div>
</template>
+47
View File
@@ -0,0 +1,47 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'VertexAI';
const BACKGROUND_COLOR = "#4285F4";
const AVATAR_SCALE = 0.6;
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :fill="avatar ? 'currentColor' : ''" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M11.995 20.216a1.892 1.892 0 100 3.785 1.892 1.892 0 000-3.785zm0 2.806a.927.927 0 11.927-.914.914.914 0 01-.927.914z"
:fill="!avatar && color ? '#4285F4' : ''" />
<path clipRule="evenodd"
d="M21.687 14.144c.237.038.452.16.605.344a.978.978 0 01-.18 1.3l-8.24 6.082a1.892 1.892 0 00-1.147-1.508l8.28-6.08a.991.991 0 01.682-.138z"
:fill="!avatar && color ? '#669DF6' : ''" fillRule="evenodd" />
<path clipRule="evenodd"
d="M10.122 21.842l-8.217-6.066a.952.952 0 01-.206-1.287.978.978 0 011.287-.206l8.28 6.08a1.893 1.893 0 00-1.144 1.479z"
:fill="!avatar && color ? '#AECBFA' : ''" fillRule="evenodd" />
<path
d="M4.273 4.475a.978.978 0 01-.965-.965V1.09a.978.978 0 111.943 0v2.42a.978.978 0 01-.978.965zM4.247 13.034a.978.978 0 100-1.956.978.978 0 000 1.956zM4.247 10.19a.978.978 0 100-1.956.978.978 0 000 1.956zM4.247 7.332a.978.978 0 100-1.956.978.978 0 000 1.956z"
:fill="!avatar && color ? '#AECBFA' : ''" />
<path
d="M19.718 7.307a.978.978 0 01-.965-.979v-2.42a.965.965 0 011.93 0v2.42a.964.964 0 01-.965.979zM19.743 13.047a.978.978 0 100-1.956.978.978 0 000 1.956zM19.743 10.151a.978.978 0 100-1.956.978.978 0 000 1.956zM19.743 2.068a.978.978 0 100-1.956.978.978 0 000 1.956z"
:fill="!avatar && color ? '#4285F4' : ''" />
<path
d="M11.995 15.917a.978.978 0 01-.965-.965v-2.459a.978.978 0 011.943 0v2.433a.976.976 0 01-.978.991zM11.995 18.762a.978.978 0 100-1.956.978.978 0 000 1.956zM11.995 10.64a.978.978 0 100-1.956.978.978 0 000 1.956zM11.995 7.783a.978.978 0 100-1.956.978.978 0 000 1.956z"
:fill="!avatar && color ? '#669DF6' : ''" />
<path
d="M15.856 10.177a.978.978 0 01-.965-.965v-2.42a.977.977 0 011.702-.763.979.979 0 01.241.763v2.42a.978.978 0 01-.978.965zM15.869 4.913a.978.978 0 100-1.956.978.978 0 000 1.956zM15.869 15.853a.978.978 0 100-1.956.978.978 0 000 1.956zM15.869 12.996a.978.978 0 100-1.956.978.978 0 000 1.956z"
:fill="!avatar && color ? '#4285F4' : ''" />
<path
d="M8.121 15.853a.978.978 0 100-1.956.978.978 0 000 1.956zM8.121 7.783a.978.978 0 100-1.956.978.978 0 000 1.956zM8.121 4.913a.978.978 0 100-1.957.978.978 0 000 1.957zM8.134 12.996a.978.978 0 01-.978-.94V9.611a.965.965 0 011.93 0v2.445a.966.966 0 01-.952.94z"
:fill="!avatar && color ? '#AECBFA' : ''" />
</svg>
</div>
</template>
+44
View File
@@ -0,0 +1,44 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'ChatGLM';
const BACKGROUND_COLOR = "linear-gradient(to right, #0A51C3, #23A4FB)";
const AVATAR_SCALE = 0.75;
const [fill] = useFillIds(TITLE, 1);
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg v-if="color && !avatar"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M11.32 1.176a1.4 1.4 0 011.36 0l8.64 4.843c.421.234.68.67.68 1.141v9.68c0 .472-.259.908-.68 1.143l-8.64 4.84a1.4 1.4 0 01-1.36 0l-8.64-4.84A1.31 1.31 0 012 16.84V7.159c0-.471.259-.907.68-1.142l8.64-4.84zm7.42 13.839V8.227L12.002 12 12 19.551l6.059-3.394a1.31 1.31 0 00.68-1.142zM12.68 4.833a1.393 1.393 0 00-1.36 0L5.944 7.846c-.421.235-.68.67-.68 1.142v6.027c0 .47.259.905.68 1.142l2.795 1.566V11.09a1.546 1.546 0 00.221.79 1.527 1.527 0 01-.216-.834l.004-.094.02-.15.018-.084.017-.062.039-.117.062-.142.035-.065.081-.13.094-.122.084-.091.08-.075.125-.1.071-.048.134-.076 5.87-3.29-2.796-1.566z"
:fill="fill!.fill" />
<path
d="M12 11.088c0-.875-.73-1.584-1.631-1.584a1.66 1.66 0 00-.855.237c-.027.016-.055.033-.08.05a2.361 2.361 0 00-.123.093c-.022.02-.045.038-.066.059l-.048.045-.063.067c-.014.016-.028.031-.04.048a2.303 2.303 0 00-.094.125l-.042.069a1.7 1.7 0 00-.07.13l-.036.081a.764.764 0 00-.022.06c-.01.03-.02.058-.028.087l-.017.062a.883.883 0 00-.03.16c-.002.025-.007.05-.008.074a1.527 1.527 0 00.213.929c.302.508.85.792 1.414.792.277 0 .558-.068.814-.212l.815-.457v-.914L12 11.088z"
fill="#012F8D" />
<defs>
<linearGradient :id="fill!.id" x1="9.155%" x2="90.531%" y1="75.177%" y2="25.028%">
<stop offset="0%" stop-color="#0A51C3" />
<stop offset="100%" stop-color="#23A4FB" />
</linearGradient>
</defs>
</svg>
<svg v-else fill="currentColor" fill-rule="evenodd" :height="size" style="flex: none; line-height: 1;"
:style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
viewBox="0 0 24 24" :width="size" xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M11.32 1.176a1.4 1.4 0 011.36 0l8.64 4.843c.421.234.68.67.68 1.141v9.68c0 .472-.259.908-.68 1.143l-8.64 4.84a1.4 1.4 0 01-1.36 0l-8.64-4.84A1.31 1.31 0 012 16.84V7.159c0-.471.259-.907.68-1.142l8.64-4.84zm7.42 13.839V8.227L12.002 12 12 19.551l6.059-3.394a1.31 1.31 0 00.68-1.142zM12.68 4.833a1.393 1.393 0 00-1.36 0L5.944 7.846c-.421.235-.68.67-.68 1.142v6.027c0 .47.259.905.68 1.142l2.795 1.566V11.09a1.546 1.546 0 00.221.79 1.527 1.527 0 01-.216-.834l.004-.094.02-.15.018-.084.017-.062.039-.117.062-.142.035-.065.081-.13.094-.122.084-.091.08-.075.125-.1.071-.048.134-.076 5.87-3.29-2.796-1.566z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'XiaomiMiMo';
const AVATAR_SCALE = 0.7;
const BACKGROUND_COLOR = "#000";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M.958 15.936a.459.459 0 01.459.44v2.729a.46.46 0 01-.918 0v-2.729a.459.459 0 01.459-.44zm4.814-2.035a.46.46 0 01.553.45v4.754a.458.458 0 11-.918 0V15.48L3.74 17.202a.462.462 0 01-.655.016.462.462 0 01-.065-.082L.628 14.67a.459.459 0 01.658-.637l2.124 2.187 2.127-2.188a.46.46 0 01.235-.13zm2.068.004a.46.46 0 01.458.445v4.755a.46.46 0 01-.458.458.459.459 0 01-.458-.458V14.35a.459.459 0 01.458-.445zm1.973 2.014a.46.46 0 01.46.457v2.729a.46.46 0 01-.784.324.46.46 0 01-.134-.324v-2.729a.46.46 0 01.458-.458zm.002-2.045a.458.458 0 01.328.157l2.127 2.19 2.125-2.19a.459.459 0 01.784.318v4.756a.46.46 0 01-.455.458.46.46 0 01-.458-.458V15.48l-1.667 1.723a.46.46 0 01-.65.008l-.005-.005c0-.002-.002-.002-.004-.003l-2.455-2.534a.46.46 0 01-.008-.667.461.461 0 01.338-.128zm6.797 1.206a.46.46 0 01.53.651A1.966 1.966 0 0019.81 18.4a.462.462 0 01.623.18.46.46 0 01-.181.624 2.863 2.863 0 01-1.38.353l-.142-.004a2.88 2.88 0 01-2.393-4.263.461.461 0 01.274-.21zm.864-.931a2.884 2.884 0 013.915 3.914.46.46 0 01-.402.24l-.057-.004a.458.458 0 01-.164-.055.46.46 0 01-.182-.622 1.967 1.967 0 00-2.669-2.67.459.459 0 11-.441-.803zM9.59 6.368c1.481 0 1.696 1.202 1.696 1.654v2.648h-.917v-.432c-.26.346-.792.535-1.36.535-.133 0-1.289-.03-1.384-1.136-.082-.932.675-1.61 2.053-1.61h.691c0-.563-.367-.886-.983-.886-.44.013-.864.174-1.2.458l-.36-.664c.484-.379 1.012-.567 1.764-.567zm4.427.1c1.263 0 2.082.97 2.083 2.15 0 1.181-.824 2.154-2.083 2.154-1.26 0-2.084-.972-2.084-2.152 0-1.18.82-2.153 2.084-2.153zm6.801.015c.68 0 1.202.465 1.197 1.548v2.642H21.1V8.29c0-.312-.002-.98-.63-.98s-.628.667-.628.838v2.524h-.89V8.148c0-.17-.001-.838-.63-.838-.628 0-.628.668-.628.98v2.383h-.917v-4.03h.917V7a1.22 1.22 0 01.947-.516c.398 0 .76.193.982.686a1.321 1.321 0 011.195-.686zm-18.093.872l1.457-1.772H5.32L3.311 8.07l2.14 2.602H4.24L2.725 8.796 1.21 10.672H0L2.138 8.07.13 5.583h1.138l1.458 1.772zm4.149 3.317h-.916V6.644h.916v4.028zm16.99 0h-.916V6.644h.916v4.028zM9.925 8.71c-1.055 0-1.359.412-1.326.742.032.329.324.537.757.537a1.013 1.013 0 001.014-.968l.002-.31h-.447zM14.018 7.3c-.663 0-1.184.487-1.184 1.32 0 .832.52 1.32 1.184 1.32.662 0 1.182-.49 1.182-1.32 0-.832-.52-1.32-1.182-1.32zM6.417 5.001a.568.568 0 01.587.582.588.588 0 01-1.175 0A.57.57 0 016.417 5zm16.991 0a.57.57 0 01.592.582.588.588 0 01-1.174 0 .57.57 0 01.357-.542.572.572 0 01.225-.04z" />
</svg>
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup lang="ts">
defineProps<{
size?: string | number;
color?: boolean;
avatar?: boolean;
}>();
const TITLE = 'Z.ai';
const AVATAR_SCALE = 0.6;
const BACKGROUND_COLOR = "#000";
</script>
<template>
<div class="inline-flex items-center justify-center"
:style="[`width: ${size}px; height: ${size}px;`, avatar ? `background-color: ${BACKGROUND_COLOR}; border-radius: 0.375rem;` : '']">
<svg :style="[`width: ${size}px; height: ${size}px; flex: none; line-height: 1;`, avatar ? `transform: scale(${AVATAR_SCALE});` : '']"
fill="currentColor" fill-rule="evenodd" style="flex: none; line-height: 1;" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<title>{{ TITLE }}</title>
<path
d="M12.105 2L9.927 4.953H.653L2.83 2h9.276zM23.254 19.048L21.078 22h-9.242l2.174-2.952h9.244zM24 2L9.264 22H0L14.736 2H24z" />
</svg>
</div>
</template>
+15
View File
@@ -19,6 +19,21 @@ const render = () => {
}
}
if (node.tagName === 'table') {
return h('div', { class: 'table-wrapper' }, [
h(
'table',
node.properties,
node.children?.map((child: any, index: number) =>
h(resolveComponent('MarkdownAstNode'), {
node: child,
key: `table-child-${index}`
})
)
)
]);
}
return h(
node.tagName,
node.properties,
+15 -3
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import type { RootContent } from 'hast';
import MarkdownShikiHighlight from './ShikiHighlight.vue';
const props = defineProps<{
content: string;
@@ -92,13 +93,14 @@ watch(parts, async (newParts) => {
hastParts.value = stableBase.concat(latestHast.children);
}
})
</script>
<template>
<div class="prose-wrapper">
<article class="markdown-body">
<MarkdownAstNode v-for="(node, index) in hastParts" :key="`${id}-${index}`" :node="node" />
<template v-for="(node, index) in hastParts" :key="`${id}-${index}`">
<MarkdownAstNode :node="node" v-memo="[node.type === 'text' ? node.value : node.data]" />
</template>
</article>
</div>
</template>
@@ -227,11 +229,16 @@ blockquote {
padding-left: 0.5rem;
}
.table-wrapper {
display: block;
overflow-x: auto;
margin: calc(var(--spacing) * 4) 0;
}
/* TODO: make these tables better, this is literally the first attempt from Gemini 3 flash */
table {
width: 100%;
border-collapse: collapse;
margin: calc(var(--spacing) * 4) 0;
font-size: 0.95rem;
text-align: left;
background-color: var(--color-base);
@@ -283,6 +290,11 @@ label>span {
cursor: text;
}
hr {
margin-top: 1.25rem;
margin-bottom: 1.25rem;
}
.checkbox {
width: min-content;
}
+107 -13
View File
@@ -3,51 +3,138 @@ import { hashSync } from '~/utils/hash';
const props = defineProps<{ code: string; lang: string }>();
const renderId = hashSync(props.code + props.lang);
const codeBlockRef = ref<HTMLDivElement | null>(null);
const codeHeight: Ref<string | number> = ref('auto');
const copied = ref(false);
const collapsed = ref(false);
const { data: html } = useAsyncData<string>(`shiki-${renderId}`, async () => parseCode());
const { data: parsed } = useAsyncData(`shiki-${renderId}`, async () => {
const { html, displayLang } = await parseCode();
return { html, displayLang };
});
const lineNumberWidth = computed(() => {
if (!html.value) return 1;
if (!parsed.value?.html) return 1;
// Count newlines in the generated HTML or the source code
// Using props.code is safer and faster than parsing the HTML string
return props.code.split('\n').length.toString().length;
});
watch(() => props.code, async () => {
html.value = await parseCode();
const { html: codeHtml } = await parseCode();
parsed.value = { html: codeHtml, displayLang: parsed.value!.displayLang };
});
async function parseCode() {
const shiki = await getShikiHighlighter();
let lang = props.lang.toLowerCase();
let displayLang = lang;
try {
shiki.getLanguage(lang);
const shikiLang = shiki.getLanguage(lang);
displayLang = shikiLang.name;
} catch {
lang = 'text';
}
return shiki.codeToHtml(props.code.trim(), {
const html = shiki.codeToHtml(props.code.trim(), {
lang,
themes: { dark: 'vitesse-dark', light: 'vitesse-light' },
});
return { html, displayLang };
}
let copyTimeout: NodeJS.Timeout | null = null;
function copyCode() {
copied.value = true;
navigator.clipboard.writeText(props.code);
if (copyTimeout) clearTimeout(copyTimeout);
copyTimeout = setTimeout(() => {
copied.value = false;
copyTimeout = null;
}, 2000);
}
function collapseCode() {
if (!codeBlockRef.value) return;
collapsed.value = !collapsed.value;
if (collapsed.value) {
codeHeight.value = codeBlockRef.value.scrollHeight;
nextTick(() => {
// since we are changing the height of an element, even though its
// to its own height, we are triggering a reflow, which means that
// if we didnt use requestAnimationFrame, the height would be set
// to 0 before the reflow is complete, which would cause the reflow
// to be ignored, and another one to be triggered with the new height
// of zero, causing the codeblock to snap shut immediately rather than
// animating. Not requestAnimationFrame because it doesnt work
// on firefox, but setTimeout works on both chrome and firefox
setTimeout(() => {
codeHeight.value = 0;
});
});
} else {
codeBlockRef.value!.addEventListener('transitionend', () => {
if (collapsed.value) return;
codeHeight.value = 'auto';
}, { once: true })
const targetHeight = codeBlockRef.value.scrollHeight;
codeHeight.value = targetHeight;
}
}
const codeStyle = computed(() => {
if (typeof codeHeight.value === 'number') {
return `height: ${codeHeight.value}px;`;
} else {
return `height: ${codeHeight.value};`;
}
});
onUnmounted(() => {
if (copyTimeout) clearTimeout(copyTimeout);
});
</script>
<template>
<div class="rounded-xl overflow-hidden code-container" :style="`--line-number-width: ${lineNumberWidth}ch`"
:id="`code-${renderId}`" v-html="html">
<div class="flex flex-col my-2 rounded-xl overflow-hidden">
<div class="flex items-center pl-3 pr-1.5 py-1.5 text-sm font-sans bg-[var(--color-highlight)] justify-between">
<div class="capitalize">
{{ parsed?.displayLang }}
</div>
<div class="flex gap-2">
<button @click="copyCode()"
class="flex items-center px-1 gap-0.5 rounded-md hover:bg-[var(--color-highlight)] transition-colors duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
Copy
<Icon v-if="!copied" name="mynaui:copy" class="text-4 text-[var(--color-text-subtle)]" />
<Icon v-else name="mynaui:check" class="text-4 text-emerald-500" />
</button>
<button @click="collapseCode()"
class="flex items-center justify-center h-5.5 w-5.5 rounded-md hover:bg-[var(--color-highlight)] transition-colors duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<Icon name="mynaui:chevron-down"
:class="['text-4 h-4 w-4 text-[var(--color-text-subtle)] transition-transform duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]', collapsed ? '-rotate-90' : '']" />
</button>
</div>
</div>
<div ref="codeBlockRef"
class="font-mono overflow-hidden code-container transition-height duration-300 ease-in-out"
:style="`--line-number-width: ${lineNumberWidth}ch; ${codeStyle}`" :id="`code-${renderId}`"
v-html="parsed?.html">
</div>
</div>
</template>
<style>
.code-container {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.code-container>pre {
overflow-x: auto;
scrollbar-width: thin;
padding: 1rem;
line-height: 1.625;
line-height: 0;
counter-reset: lines;
}
@@ -67,4 +154,11 @@ async function parseCode() {
.light .code-container>pre>code .line::before {
color: rgba(0, 0, 0, 0.25);
}
.code-container>pre>code {
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
line-height: 0;
font-variant-ligatures: none;
}
</style>
+17 -5
View File
@@ -1,12 +1,24 @@
<script setup lang="ts">
import type { Entity } from '@triplit/client';
import type schema from '#triplit/schema';
import ShikiHighlight from '~/components/Markdown/ShikiHighlight.vue';
defineProps<{
error_part: Entity<typeof schema, 'message_parts'>;
const props = defineProps<{
error: string | null | undefined;
}>();
const code: Ref<string | null> = ref(null);
if (props.error !== null && props.error !== undefined) {
try {
code.value = JSON.stringify(JSON.parse(props.error!), null, 2);
} catch (e) {
code.value = props.error;
}
}
</script>
<template>
<span class="text-sm text-[var(--color-error)]">Generation failed</span>
<div class="text-sm">
<ShikiHighlight :code="code ?? 'An unknown error occurred'" lang="json" />
</div>
</template>
+49 -9
View File
@@ -9,7 +9,7 @@ const props = defineProps<{
const activeTab = ref('input');
const indicatorStyle = computed(() => {
const tabs = ['input', 'output', 'trace'];
const tabs = ['input', 'output', 'error', 'trace'];
const index = tabs.indexOf(activeTab.value);
// Each tab button is ~48px (40px height + 8px gap)
const offset = index * 48;
@@ -27,19 +27,26 @@ const lineNumberWidth = ref(1);
const input: ComputedRef<string> = computed(() => {
switch (activeTab.value) {
case 'input':
if (props.toolCall.input === null) return '';
if (props.toolCall.input === null || props.toolCall.input === undefined) return '';
if (props.toolCall.input!.type === 'json') {
return JSON.stringify(JSON.parse(props.toolCall.input!.value), null, 2);
}
return props.toolCall.input!.value;
case 'output':
if (props.toolCall.output === null) return '';
if (props.toolCall.output === null || props.toolCall.output === undefined) return '';
if (props.toolCall.output!.type === 'json') {
return JSON.stringify(JSON.parse(props.toolCall.output!.value), null, 2);
}
return props.toolCall.output!.value;
case 'error':
if (props.toolCall.error === null || props.toolCall.error === undefined) return '';
if (props.toolCall.error!.type === 'json') {
return JSON.stringify(JSON.parse(props.toolCall.error!.value), null, 2);
}
return props.toolCall.error!.value;
case 'trace': {
const traceObj: any = { ...props.toolCall };
if (traceObj === null) return '';
@@ -105,7 +112,7 @@ watch(
<div class="w-full border rounded-lg border-[var(--color-highlight)] flex flex-row h-80 overflow-hidden">
<div class="flex items-center gap-2 flex-col border-r border-[var(--color-highlight)] p-1 relative shrink-0">
<button @click="activeTab = 'input'" :class="[
'hover:bg-[var(--color-highlight)] p-2 rounded-lg flex gap-1 items-center w-full transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]',
'function-call-tab-selector',
activeTab === 'input' ? 'text-orange-6' : ''
]">
<div
@@ -114,18 +121,29 @@ watch(
</div>
Input
</button>
<button @click="activeTab = 'output'" :class="[
'hover:bg-[var(--color-highlight)] p-2 rounded-lg flex gap-1 items-center w-full transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]',
activeTab === 'output' ? 'text-orange-6' : ''
]">
<button :disabled="toolCall.output === undefined || toolCall.output === null" @click="activeTab = 'output'"
:class="[
'function-call-tab-selector',
activeTab === 'output' ? 'text-orange-6' : ''
]">
<div
class="w-[24px] h-[24px] flex-shrink-0 rounded-lg overflow-hidden flex items-center justify-center">
<Icon name="mynaui:arrow-down-square" class="w-4.5 h-4.5" />
</div>
Output
</button>
<button v-if="toolCall.error !== undefined && toolCall.error !== null" @click="activeTab = 'error'" :class="[
'function-call-tab-selector',
activeTab === 'error' ? 'text-orange-6' : ''
]">
<div
class="w-[24px] h-[24px] flex-shrink-0 rounded-lg overflow-hidden flex items-center justify-center">
<Icon name="mynaui:danger-triangle" class="w-4.5 h-4.5" />
</div>
Error
</button>
<button @click="activeTab = 'trace'" :class="[
'hover:bg-[var(--color-highlight)] p-2 rounded-lg flex gap-1 items-center w-full transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]',
'function-call-tab-selector',
activeTab === 'trace' ? 'text-orange-6' : ''
]">
<div
@@ -174,4 +192,26 @@ watch(
.light #function-call-container>pre>code .line::before {
color: rgba(0, 0, 0, 0.25);
}
.function-call-tab-selector {
padding: 0.5rem;
border-radius: 0.375rem;
display: flex;
gap: 0.25rem;
align-items: center;
width: 100%;
transition-property: color, background-color, border-color,
text-decoration-color, fill, stroke;
transition-duration: 200ms;
transition-timing-function: cubic-bezier(0.5, 1, 0.89, 1);
}
.function-call-tab-selector:hover:enabled {
background-color: var(--color-highlight);
}
.function-call-tab-selector:disabled {
opacity: 0.3;
cursor: not-allowed;
}
</style>
+5 -23
View File
@@ -12,28 +12,6 @@ const deubgToolCallOpen = ref(false);
const toggleDebugToolCall = () => {
deubgToolCallOpen.value = !deubgToolCallOpen.value;
};
const iconName = ref('mynaui:tool');
const iconColor = ref('var(--color-subtle)');
watch(
() => props.toolCall.status,
(status) => {
switch (status) {
case 'pending':
iconName.value = 'svg-spinners:180-ring-with-bg';
break;
case 'completed':
iconName.value = 'mynaui:tool';
break;
case 'failed':
iconName.value = 'mynaui:x-solid';
iconColor.value = '#ff3b3b';
break;
}
},
{ immediate: true },
);
</script>
<template>
@@ -44,8 +22,12 @@ watch(
<div class="flex items-center gap-1">
<div
class="w-[24px] h-[24px] flex-shrink-0 rounded-lg overflow-hidden bg-[var(--color-neutral)] flex items-center justify-center">
<Icon :name="iconName" :style="{ color: iconColor }"
<!-- Explicityly avoid setting the name via a reactive value, otherwise you risk corrupting the icon with that name globally -->
<Icon v-if="toolCall.status === 'pending'" name="svg-spinners:180-ring-with-bg"
class="w-3 h-3 text-[var(--color-subtle)]" />
<Icon v-else-if="toolCall.status === 'failed'" name="mynaui:x-solid"
class="w-3 h-3 text-[#ff3b3b]" />
<Icon v-else name="mynaui:tool" class="w-3 h-3 text-[var(--color-subtle)]" />
</div>
{{ toolCall.toolName }}
</div>
+27 -26
View File
@@ -1,19 +1,12 @@
<script setup lang="ts">
import type { Entity } from '@triplit/client';
import type schema from '#triplit/schema';
import ShikiHighlight from '~/components/Markdown/ShikiHighlight.vue';
import Error from './Error.vue';
import Reasoning from './Reasoning.vue';
import Text from './Text.vue';
import Tool from './Tool/index.vue';
import type { MessageEntity } from '~/composables/useChat';
defineProps<{
message: Readonly<
Entity<typeof schema, 'messages'> & {
parts: (Entity<typeof schema, 'message_parts'> & {
toolCall: Entity<typeof schema, 'tool_calls'> | null;
})[];
} & { generation: Entity<typeof schema, 'generations'> | null }
>;
message: MessageEntity
}>();
</script>
@@ -28,28 +21,36 @@ defineProps<{
Unhandled part type: {{ part.type }} {{ part }}
</div>
</div>
<span class="flex items-center gap-1"
v-if="message.generation?.status === 'pending' && (message.parts || []).length === 0">
<Icon name="svg-spinners:pulse-2" class="text-4" />
<span class="text-sm text-[var(--color-muted)]">
Preparing generating...
</span>
</span>
<div v-else-if="message.generation?.status === 'failed'">
<Error :error="message.generation.error" />
</div>
<div class="flex flex-row justify-between text-zinc-400 dark:text-zinc-600 text-xs"
v-if="message.generation && message.generation.status !== 'pending'">
<span class="flex items-center gap-1">
<ModelIcon :size="12" :model-id="message.generation.modelId" />
{{ message.generation.modelId }}
</span>
<span class="flex gap-1 items-center" v-if="message.generation.tokens?.output">
<Icon name="tabler:coins" />
{{ message.generation?.tokens?.output }}
</span>
</div>
</div>
<span v-if="message.generation?.status === 'pending' && message.parts.length === 0">
<span class="text-sm text-[var(--color-muted)] flex flex-row items-center">
<Icon name="svg-spinners:pulse-2" class="text-4" />
Preparing generating...
</span>
</span>
<div v-else-if="message.generation?.status === 'failed'">
<span class="text-sm text-[var(--color-error)]">Generation failed</span>
<div class="text-sm">
<ShikiHighlight :code="message.generation.error ?? 'An unknown error occurred'" lang="json" />
<div class="flex gap-2">
<span class="flex gap-1 items-center" v-if="message.generation.tokens?.output">
<Icon name="tabler:coins" />
{{ message.generation?.tokens?.output }}
</span>
<span v-if="message.generation.tokens?.tps">
{{ message.generation.tokens.tps.toFixed(1) }} tps
</span>
</div>
</div>
</div>
</template>
+141 -13
View File
@@ -1,21 +1,149 @@
<script setup lang="ts">
import type { Entity } from '@triplit/client';
import type schema from '#triplit/schema';
import type { Message } from '~/composables/useChat';
defineProps<{
message: Readonly<
Entity<typeof schema, 'messages'> & {
parts: (Entity<typeof schema, 'message_parts'> & {
toolCall: Entity<typeof schema, 'tool_calls'> | null;
})[];
} & { generation: Entity<typeof schema, 'generations'> | null }
>;
const triplit = useTriplitClient();
const { message } = defineProps<{
message: Message
}>();
const focusedIndex = computed({
get: () => {
return message.focusedIndex || 0;
},
set: (newValue) => {
triplit.update('messages', message.id, {
focusedIndex: newValue
});
}
});
watch(() => message.children.length, (newCount, oldCount) => {
if (newCount === oldCount) return;
// if we are deleting children, only move the focus index if its no longer valid
// e.g. if we we have 4 children, and are focused on the 2nd, if we delete it,
// we want to keep the focus on the 2nd index. It just makes me feel better
if (oldCount > newCount) {
if (message.deleted === true && focusedIndex.value === newCount) {
focusedIndex.value = Math.max(0, newCount - 1);
return;
}
if (focusedIndex.value > newCount) {
focusedIndex.value = newCount;
}
return;
}
if (message.deleted === true) {
focusedIndex.value = newCount - 1;
return;
}
focusedIndex.value = newCount;
});
const activeMessage = computed(() => {
if (message.children.length === 0 || (focusedIndex.value === 0 && !message.deleted)) {
return message;
}
if (message.deleted === true) {
return message.children[Math.min(focusedIndex.value, message.children.length - 1)];
}
return message.children[focusedIndex.value - 1];
});
const emit = defineEmits<{
regenerate: []
delete: []
}>();
const copied = ref(false);
const copyMessage = async () => {
if (activeMessage.value!.role === 'user') {
await navigator.clipboard.writeText(activeMessage.value!.content!);
} else {
let text = [];
for (const part of activeMessage.value!.parts || []) {
if (part.type === 'text') {
text.push(part.content);
}
}
await navigator.clipboard.writeText(text.join('\n\n'));
}
copied.value = true;
setTimeout(() => {
copied.value = false;
}, 2000);
}
const regenerateMessage = async () => {
emit('regenerate');
}
const deleteMessage = () => {
if (focusedIndex.value !== 0 && focusedIndex.value === message.children.length) {
focusedIndex.value = Math.max(0, focusedIndex.value - 1);
}
nextTick(() => {
emit('delete');
});
};
const messageCount = computed(() => {
if (message.deleted === true) {
return message.children.length;
}
return message.children.length + 1;
});
</script>
<template>
<div :class="['max-w-full mb-4', message.role === 'user' ? 'pl-9 flex justify-end' : '']">
<MessageUser v-if="message.role === 'user'" :message="message" />
<MessageAgent v-else :message="message" />
<div v-if="activeMessage" class="max-w-full mb-4 group flex flex-col">
<div :class="[message.role === 'user' ? 'pl-9 flex justify-end' : '']">
<MessageUser v-if="message.role === 'user'" :message="activeMessage!" />
<MessageAgent v-else :message="activeMessage" />
</div>
<div class="flex justify-between items-center">
<div>
<div v-if="messageCount > 1" class="flex gap-1">
<button :inert="focusedIndex === 0" @click="focusedIndex = focusedIndex! - 1">
<Icon name="mynaui:chevron-left" :class="['w-4 h-4', focusedIndex === 0 ? 'opacity-0' : '']" />
</button>
<span class="text-xs text-[var(--color-muted)]">
{{ focusedIndex + 1 }} / {{ messageCount }}
</span>
<button :inert="focusedIndex + 1 === messageCount" @click="focusedIndex = focusedIndex + 1">
<Icon name="mynaui:chevron-right"
:class="['w-4 h-4', focusedIndex + 1 === messageCount ? 'opacity-0' : '']" />
</button>
</div>
</div>
<div :class="activeMessage.generation?.status === 'pending' ? 'opacity-0!' : ''"
class="self-end mt-1 w-fit bg-[var(--color-highlight)] text-[var(--color-subtle)] gap-px flex items-center rounded-md overflow-hidden opacity-0 group-hover:opacity-100 transition-opacity duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<button @click="regenerateMessage"
class="flex justify-center items-center w-7 h-6 hover:bg-[var(--color-highlight)]">
<Icon name="mynaui:refresh" class="text-4.5" />
</button>
<button @click="copyMessage" :class="{ 'text-emerald-500': copied }"
class="flex justify-center items-center w-7 h-6 hover:bg-[var(--color-highlight)]">
<Icon :name="copied ? 'mynaui:check' : 'mynaui:copy'" class="text-4.5" />
</button>
<button @click="deleteMessage"
class="flex justify-center items-center w-7 h-6 text-red-500 hover:bg-[var(--color-highlight)]">
<Icon name="mynaui:trash" class="text-5" />
</button>
</div>
</div>
</div>
</template>
+7 -3
View File
@@ -12,9 +12,13 @@ const config = computed(() => getModelConfig(props.modelId));
</script>
<template>
<div class="inline-flex items-center justify-center">
<component :is="config.icon" v-if="config.icon" :avatar="avatar" :size="size" :color="variant === 'color'" />
<div class="inline-flex items-center justify-center" v-bind="$attrs">
<component v-if="config.Icon" :is="config.Icon" :avatar="avatar" :size="size" :color="variant === 'color'"
v-bind="config.props" />
<!-- Fallback if no logo matches -->
<div v-else :style="{ width: `${props.size}px`, height: `${props.size}px` }" class="bg-gray-200 rounded-full" />
<div v-else :style="{ width: `${props.size}px`, height: `${props.size}px` }"
class="bg-[var(--color-highlight)] rounded-md flex items-center justify-center">
<Icon name="tabler:brain" class="text-5 text-[var(--color-muted)]" />
</div>
</div>
</template>
+136
View File
@@ -0,0 +1,136 @@
<script setup lang="ts">
import type { Entity } from '@triplit/client';
import type schema from '#triplit/schema';
const props = withDefaults(defineProps<{
model: Entity<typeof schema, 'models'>;
size?: 'small' | 'normal';
showCost?: boolean;
showEdit?: boolean;
showExternalId?: boolean;
showReleaseDate?: boolean;
}>(), {
size: 'normal',
showCost: false,
showExternalId: false,
showReleaseDate: false,
showEdit: false,
});
const { Big } = await import('big.js');
const triplit = useTriplitClient();
const formatContextWindow = (window: number | null | undefined): string => {
if (!window) return '';
if (window >= 1000000) return `${(window / 1000000).toFixed(0)}M`;
if (window >= 1000) return `${(window / 1000).toFixed(0)}K`;
return window.toString();
};
const hasCapability = (capability: string): boolean => {
return props.model.attributes.capabilities.has(capability);
};
const hasInputModality = (modality: string): boolean => {
return (props.model.attributes.inputModalities as Readonly<Set<string>>).has(modality);
};
const formatBig = (bigValue: Big) => {
let str = bigValue.toString();
if (!str.includes('.')) return str + '.00';
if (str.split('.')[1]!.length === 1) return str + '0';
return str;
};
const showCost = computed(() => {
return props.showCost && (props.model.cost.prompt || props.model.cost.completion || props.model.cost.request);
});
const toggleModel = async (id: string) => {
await triplit.update('models', id, {
enabled: !props.model.enabled,
});
};
const deleteModel = async () => {
await triplit.delete('models', props.model.id);
};
</script>
<template>
<div class="flex items-center justify-between w-full" v-bind="$attrs">
<div class="flex items-center gap-2 min-w-0 flex-1">
<ModelIcon :class="size === 'normal' ? 'rounded-lg overflow-hidden' : ''" :avatar="true" variant="color"
:model-id="model.externalId" :size="size === 'small' ? '20' : '32'" />
<div class="flex flex-col gap-1 min-w-0 flex-1">
<div class="flex items-center gap-1 min-w-0">
<span class="text-sm font-medium text-[var(--color-text)] truncate min-w-0">
{{ model.name }}
</span>
<span v-if="showExternalId"
class="text-xs text-[var(--color-muted)] px-1 py-0.5 rounded bg-[var(--color-highlight)] whitespace-nowrap truncate max-w-[240px]">
{{ model.externalId }}
</span>
<div v-if="showEdit" class="flex items-center gap-2">
<button @click="deleteModel"
class="rounded h-5 w-5 flex items-center justify-center opacity-0 group-hover:opacity-100 hover:bg-red-500/30 text-red-500 trannsition-all duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<Icon name="mynaui:trash" class="text-3.5" />
</button>
</div>
</div>
<div v-if="size === 'normal'" class="flex items-center gap-1.5 flex-wrap">
<span v-if="showReleaseDate && model.releasedAt"
class="text-xs text-[var(--color-muted)] whitespace-nowrap">
Released on {{ model.releasedAt.toISOString().split('T')[0] }}
</span>
<template v-if="showCost">
<span v-if="model.cost.prompt"
class="text-xs text-[var(--color-muted)] whitespace-nowrap flex items-center">
<span class="w-1 h-1 rounded-full bg-[var(--color-muted)] inline-block mr-1"></span>
${{ formatBig(Big(model.cost.prompt).mul(1000000)) }}/M input
</span>
<span v-if="model.cost.completion"
class="text-xs text-[var(--color-muted)] whitespace-nowrap flex items-center">
<span class="w-1 h-1 rounded-full bg-[var(--color-muted)] inline-block mr-1"></span>
${{ formatBig(Big(model.cost.completion).mul(1000000)) }}/M output
</span>
<span v-if="model.cost.request && model.cost.request !== '0'"
class="text-xs text-[var(--color-muted)] whitespace-nowrap flex items-center">
<span class="w-1 h-1 rounded-full bg-[var(--color-muted)] inline-block mr-1"></span>
{{ model.cost.request }}/request
</span>
</template>
</div>
</div>
</div>
<div class="flex items-center gap-1 shrink-0">
<div class="flex items-center gap-0.5">
<div v-if="hasInputModality('image')"
class="w-4.5 h-4.5 bg-emerald/10 rounded flex items-center justify-center">
<Icon name="mynaui:image" class="text-2.5 text-emerald" title="Vision" />
</div>
<div v-if="hasCapability('reasoning')"
class="w-4.5 h-4.5 bg-[color-mix(in_srgb,_transparent_90%,_var(--reasoning-accent)_10%)] rounded flex items-center justify-center">
<Icon name="mynaui:atom" class="text-2.5 text-[var(--reasoning-accent)]" title="Reasoning" />
</div>
<div v-if="hasCapability('tools')"
class="w-4.5 h-4.5 bg-emerald/10 rounded flex items-center justify-center">
<Icon name="mynaui:tool" class="text-2.5 text-sky" title="Tools" />
</div>
</div>
<span v-if="model.attributes.contextWindow"
class="text-xs font-mono text-[var(--color-subtle)] px-1.5 py-0.5 rounded bg-[var(--color-highlight)]">
{{ formatContextWindow(model.attributes.contextWindow) }}
</span>
<Slider v-if="showEdit" :checked="model.enabled" @click="toggleModel(model.id)" />
</div>
</div>
</template>
+105 -89
View File
@@ -1,79 +1,111 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import type { ModelWithProvider, ProviderWithModels } from '~/composables/useModels';
import { schema } from '#triplit/schema';
import type { Entity } from '@triplit/client';
import type schema from '#triplit/schema';
const { setPage } = useSettings();
const { allModels } = await useModels();
const props = defineProps<{
modelValue: ModelWithProvider | null;
providers: ProviderWithModels[];
}>();
const emit = defineEmits<{
'update:modelValue': [model: ModelWithProvider | null];
}>();
const isOpen = ref(false);
const searchQuery = ref('');
const dropdownRef = ref<HTMLDivElement | null>(null);
const searchInputRef = ref<HTMLInputElement | null>(null);
const selectedModel = defineModel<ModelWithProvider | null>();
const selectedModel = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value),
});
const dropdownRef = ref<HTMLDivElement | null>(null);
const dropdownButton = ref<HTMLButtonElement | null>(null);
const searchInputRef = ref<HTMLInputElement | null>(null);
const dropdownDirection = ref<'up' | 'down'>('up');
const dropdownMaxHeight = ref<number | undefined>(undefined);
const findContainer = (startingElement: HTMLElement): HTMLElement | null => {
let container: HTMLElement | null = startingElement;
while (container) {
const computedStyle = getComputedStyle(container);
if (computedStyle.overflow !== 'visible') {
return container;
}
container = container.parentElement;
}
return null;
}
const calculateDropdownPosition = () => {
if (!dropdownButton.value) return;
const buttonRect = dropdownButton.value.getBoundingClientRect();
const container = findContainer(dropdownButton.value) || document.body;
const containerRect = container.getBoundingClientRect();
const spaceAbove = buttonRect.top - containerRect.top;
const spaceBelow = containerRect.bottom - buttonRect.bottom;
if (spaceAbove > spaceBelow) {
dropdownDirection.value = 'up';
dropdownMaxHeight.value = Math.min(spaceAbove - 20, 460);
} else {
dropdownDirection.value = 'down';
dropdownMaxHeight.value = Math.min(spaceBelow - 20, 460);
}
};
const searchQuery = ref('');
const filteredProviders = computed(() => {
if (!searchQuery.value.trim()) {
return props.providers;
}
const query = searchQuery.value.toLowerCase();
return props.providers
.map((provider) => ({
...provider,
models: provider.models.filter((model) =>
model.name.toLowerCase().includes(query)
),
}))
.filter((provider) => provider.models.length > 0);
return filterProvidersWithModel(props.providers, searchQuery.value).filter(p => p.enabled);
});
const formatContextWindow = (window: number | null | undefined): string => {
if (!window) return '';
if (window >= 1000000) return `${(window / 1000000).toFixed(0)}M`;
if (window >= 1000) return `${(window / 1000).toFixed(0)}K`;
return window.toString();
};
const hasCapability = (model: Entity<typeof schema, 'models'>, capability: string): boolean => {
return model.attributes.capabilities.has(capability);
};
const hasInputModality = (model: Entity<typeof schema, 'models'>, modality: string): boolean => {
return model.attributes.inputModalities.has(modality);
};
const selectModel = (model: Entity<typeof schema, 'models'>, provider: Entity<typeof schema, 'providers'>) => {
selectedModel.value = { ...model, provider };
isOpen.value = false;
searchQuery.value = '';
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
isOpen.value = false;
watch(() => props.providers, () => {
if (selectedModel.value) {
const modelStillExists = allModels.value.some(model => model.id === selectedModel.value?.id);
if (!modelStillExists) {
let firstEnabled: ModelWithProvider | null = null;
for (const provider of props.providers) {
const enabledModel = provider.models.find(m => m.enabled);
if (enabledModel) {
firstEnabled = { ...enabledModel, provider };
break;
}
}
selectedModel.value = firstEnabled;
}
}
};
})
watch(isOpen, (open) => {
if (open) {
calculateDropdownPosition();
nextTick(() => {
searchInputRef.value?.focus();
});
}
});
const handleInputKeypress = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
isOpen.value = false;
}
}
const handleKeyDown = (event: KeyboardEvent) => {
// TODO: in the settings page, pressing escape closes BOTH
// the settings modal and the model selector dropdown
if (event.key === 'Escape') {
isOpen.value = false;
}
};
useClickOutside(dropdownRef, () => {
isOpen.value = false;
});
@@ -89,16 +121,15 @@ onUnmounted(() => {
<template>
<div ref="dropdownRef" class="relative">
<button @click="isOpen = !isOpen"
<button @click="isOpen = !isOpen" ref="dropdownButton"
class="flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors duration-200"
:class="[
isOpen
? 'bg-[var(--color-highlight)] text-[var(--color-text)]'
: 'text-[var(--color-text-subtle)] hover:text-[var(--color-text)] hover:bg-[var(--color-highlight-low)]',
]">
<ModelIcon v-if="selectedModel" :avatar="true" variant="color" :model-id="selectedModel.externalId"
size="16" />
<Icon v-else name="mynaui:warning-circle" class="text-4" />
<ModelIcon v-if="selectedModel" class="text-white" :avatar="true" variant="color"
:model-id="selectedModel.externalId" size="22" />
<span class="max-w-[150px] truncate">
{{ selectedModel ? selectedModel.name : 'Select a model' }}
</span>
@@ -106,74 +137,59 @@ onUnmounted(() => {
:class="{ 'rotate-180': isOpen }" />
</button>
<Transition enter-active-class="transition-all duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]"
enter-from-class="opacity-0 scale-95 translate-y-1" enter-to-class="opacity-100 scale-100 translate-y-0"
leave-active-class="transition-all duration-100 ease-[cubic-bezier(0.5,_1,_0.89,_1)]"
leave-from-class="opacity-100 scale-100 translate-y-0" leave-to-class="opacity-0 scale-95 translate-y-1">
<div v-if="isOpen"
class="transform-origin-bottom-center absolute bottom-full left-0 mb-2 max-w-[420px] w-full max-h-[460px] flex flex-col rounded-xl border border-[var(--color-highlight)] bg-[var(--color-neutral)] shadow-lg overflow-hidden z-50">
<div v-show="isOpen" ref="dropdownContentRef" :class="[
dropdownDirection === 'up' ? 'bottom-full mb-2 origin-bottom' : 'top-full mt-2 origin-top',
]" :style="{ maxHeight: dropdownMaxHeight ? `${dropdownMaxHeight}px` : '460px', height: 'auto' }"
class="absolute left-0 max-w-[420px] w-full flex flex-col rounded-xl border border-[var(--color-highlight)] bg-[var(--color-neutral)] shadow-lg overflow-hidden z-50">
<div>
<div class="relative">
<Icon name="mynaui:search"
class="absolute left-3 top-1/2 -translate-y-1/2 text-4 text-[var(--color-text-subtle)]" />
<input ref="searchInputRef" v-model="searchQuery" type="text" placeholder="Search models..."
<input ref="searchInputRef" v-model="searchQuery" autocomplete="off" name="search"
@keypress="handleInputKeypress" type="text" placeholder="Search models..."
class="w-full pl-9 pr-3 py-2 text-sm text-[var(--color-text)] bg-transparent placeholder-[var(--color-text-subtle)] outline-none" />
</div>
</div>
<div class="flex-1 overflow-y-auto py-2 select-none">
<div class="flex-1 overflow-y-auto [scrollbar-width:thin] py-2 select-none max-w-full overflow-hidden">
<div v-if="filteredProviders.length === 0"
class="px-4 py-8 text-center text-sm text-[var(--color-muted)]">
No models found
</div>
<div v-for="provider in filteredProviders" :key="provider.id" class="mb-2">
<div class="px-4 py-1.5 text-xs font-medium text-[var(--color-muted)] uppercase tracking-wider">
<div
class="px-4 py-1.5 text-xs font-medium text-[var(--color-muted)] capitalize tracking-wider flex justify-between">
{{ provider.name }}
<button @click="isOpen = true; setPage('providers', provider.id)"
class="flex h-4.5 w-4.5 items-center justify-center hover:bg-[var(--color-highlight)] rounded transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<Icon name="mynaui:cog-four" class="text-3.5" />
</button>
</div>
<button v-for="model in provider.models.filter(m => m.enabled)" :key="model.id"
@click="selectModel(model, provider)"
class="w-full px-4 py-2 flex items-center gap-3 hover:bg-[var(--color-highlight-low)] transition-colors duration-150"
<button
v-for="model in provider.models.filter(m => m.enabled).sort((a, b) => b.releasedAt && a.releasedAt ? b.releasedAt.getTime() - a.releasedAt.getTime() : 0)"
:key="model.id" @click="selectModel(model, provider); isOpen = false"
class="text-white w-full min-h-9 px-4 py-2 flex items-center justify-between hover:bg-[var(--color-highlight-low)] transition-colors duration-150"
:class="{ 'bg-[var(--color-highlight-low)]': selectedModel?.id === model.id }">
<ModelIcon :avatar="true" variant="color" :model-id="model.externalId" size="20" />
<span class="flex-1 text-sm text-left text-[var(--color-text)] truncate">
{{ model.name }}
</span>
<div class="flex items-center gap-0.5">
<div v-if="hasInputModality(model, 'image')"
class="w-4.5 h-4.5 bg-emerald/10 rounded flex items-center justify-center">
<Icon name="mynaui:image" class="text-2.5 text-emerald" title="Vision" />
</div>
<div v-if="hasCapability(model, 'reasoning')"
class="w-4.5 h-4.5 bg-[color-mix(in_srgb,_transparent_90%,_var(--reasoning-accent)_10%)] rounded flex items-center justify-center">
<Icon name="mynaui:atom" class="text-2.5 text-[var(--reasoning-accent)]"
title="Reasoning" />
</div>
<div v-if="hasCapability(model, 'tools')"
class="w-4.5 h-4.5 bg-emerald/10 rounded flex items-center justify-center">
<Icon name="mynaui:tool" class="text-2.5 text-sky" title="Tools" />
</div>
</div>
<span v-if="model.attributes.contextWindow"
class="text-xs font-mono text-[var(--color-subtle)] px-1.5 py-0.5 rounded bg-[var(--color-highlight)]">
{{ formatContextWindow(model.attributes.contextWindow) }}
</span>
<ModelInfo :model="model" size="small" />
</button>
</div>
</div>
<div class="p-1 border-t border-[var(--color-highlight-low)]">
<NuxtLink to="/settings/providers"
class="flex items-center gap-2 px-3 py-2 text-sm text-[var(--color-text-subtle)] hover:text-[var(--color-text)] hover:bg-[var(--color-highlight-low)] rounded-lg transition-colors duration-150"
@click="isOpen = false">
<button
class="flex w-full items-center gap-2 px-3 py-2 text-sm text-[var(--color-text-subtle)] hover:text-[var(--color-text)] hover:bg-[var(--color-highlight-low)] rounded-lg transition-colors duration-150"
@click="isOpen = false; setPage('providers');">
<Icon name="mynaui:cog-four" class="text-4" />
<span>Manage Provider</span>
<span>Manage Providers</span>
<Icon name="mynaui:arrow-right" class="text-3.5 ml-auto" />
</NuxtLink>
</button>
</div>
</div>
</Transition>
+172 -104
View File
@@ -1,12 +1,15 @@
<script setup lang="ts">
import { encryptData, decrypt, uint8ArrayToBase64, base64ToUint8Array } from '~/utils/crypto';
import { providerBaseUrls } from '~/types/model';
import { providerBaseUrls, SupportedModalities, type Model } from '~/types/model';
import { useSettings } from '~/composables/useSettings';
import ModelItem from './ModelItem.vue';
// @ts-ignore
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
const triplit = useTriplitClient();
const { pageParams } = useSettings();
const { providers } = await useModels();
const { providers, unsubscribe: unsubscribeModels } = await useModels();
const provider = computed(() => {
if (pageParams.value.length === 0) return null;
@@ -21,12 +24,22 @@ watch(provider, async () => {
const apiKeyVisible = ref(false);
const apiKey = ref('');
const apiProxyUrl = ref(provider.value!.config.apiProxyUrl ?? '');
const apiProxyUrl = ref(provider.value?.config.apiProxyUrl ?? '');
const modelSearch = ref('');
const providerApiUrl = computed(() => apiProxyUrl.value === '' ? providerBaseUrls[provider.value!.type] : apiProxyUrl.value);
watch(pageParams, () => {
if (pageParams.value.length === 0) return;
apiKey.value = provider.value?.config.apiKey ?? '';
apiProxyUrl.value = provider.value?.config.apiProxyUrl ?? '';
modelSearch.value = '';
})
const decryptApiKey = async () => {
if (!provider.value?.config.apiKey) {
apiKey.value = '';
return
};
const key = await crypto.subtle.importKey(
"jwk",
JSON.parse(window.localStorage.getItem("encryptionKey")!),
@@ -70,6 +83,8 @@ const updateApiKey = async (value: string) => {
const updateProxyUrl = async (value: string) => {
if (!provider.value) return;
apiProxyUrl.value = value;
await triplit.update('providers', provider.value.id, {
config: {
...provider.value.config,
@@ -78,14 +93,6 @@ const updateProxyUrl = async (value: string) => {
});
};
const toggleModel = async (id: string) => {
if (!provider.value) return;
await triplit.update('models', id, {
enabled: !provider.value!.models.find(m => m.id === id)!.enabled,
});
};
const fetchingModels = ref(false);
const fetchModels = async () => {
@@ -95,12 +102,23 @@ const fetchModels = async () => {
try {
const [providerResponse, devDataResponse] = await Promise.all([
$fetch(`${providerApiUrl.value}/models`),
$fetch('https://models.dev/api.json')
$fetch(`/api/provider/${provider.value!.id}/models`, {
method: 'POST',
body: JSON.stringify({
providerApiKey: apiKey.value
})
}) as any,
$fetch('https://models.dev/api.json') as any
]);
const providerType = provider.value!.type;
// TODO: get model details correctly for ollama-cloud models
let providerType = provider.value!.type as string;
if (providerType === 'ollama') {
providerType = 'ollama-cloud';
}
const modelDetails = devDataResponse[providerType]?.models || {};
console.log(modelDetails);
const existingModelsMap = new Map(
(provider.value?.models || []).map((m: any) => [m.externalId, m])
@@ -109,11 +127,15 @@ const fetchModels = async () => {
const toInsert: any[] = [];
const toUpdate: { id: string, data: any }[] = [];
providerResponse.data.forEach((pModel: any) => {
const slug = pModel.id.toLowerCase();
const info = modelDetails[slug] || {};
providerResponse.models.forEach((pModel: any) => {
let slug: string = pModel.id.toLowerCase();
if (providerType === 'ollama-cloud') {
slug = slug.replace(/:cloud$/, '');
slug = slug.replace(/-cloud$/, '');
slug = slug.replace(/:latest$/, '');
}
console.log("INFO", info);
let info = modelDetails[slug] || {};
const capabilities = [];
@@ -125,14 +147,48 @@ const fetchModels = async () => {
capabilities.push('tools');
}
let inputModalities = info.modalities?.input.filter((m: string) => (SupportedModalities as Readonly<string[]>).includes(m));
if (inputModalities === undefined || inputModalities.length === 0) {
inputModalities = ['text'];
}
let outputModalities = info.modalities?.output.filter((m: string) => (SupportedModalities as Readonly<string[]>).includes(m));
if (outputModalities === undefined || outputModalities.length === 0) {
outputModalities = ['text'];
}
// merge pModel.attributes and info.modalities, with a preference for pModel.attributes
const attributes = {
inputModalities: new Set(info.modalities?.input.filter(m => ['text', 'image'].includes(m)) || ['text']),
outputModalities: new Set(info.modalities?.output.filter(m => ['text', 'image'].includes(m)) || ['text']),
inputModalities: new Set(inputModalities),
outputModalities: new Set(outputModalities),
capabilities,
contextWindow: pModel.context_length || info.limit?.context || null,
supported_parameters: new Set(pModel.supported_parameters || ["temperature", "max_tokens"]),
...(pModel.attributes || {}),
};
let cost;
if (pModel.pricing === undefined) {
cost = {}
} else {
cost = {
prompt: pModel.pricing.prompt || null,
completion: pModel.pricing.completion || null,
request: pModel.pricing.request || null,
image: pModel.pricing.image || null,
imageTokens: pModel.pricing.image_tokens || null,
imageOutput: pModel.pricing.image_output || null,
audio: pModel.pricing.audio || null,
audioOutput: pModel.pricing.audio_output || null,
inputAudioCache: pModel.pricing.input_audio_cache || null,
webSearch: pModel.pricing.web_search || null,
internalReasoning: pModel.pricing.internal_reasoning || null,
inputCacheRead: pModel.pricing.input_cache_read || null,
inputCacheWrite: pModel.pricing.input_cache_write || null,
discount: pModel.pricing.discount || null,
}
}
const existing = existingModelsMap.get(pModel.id);
if (existing) {
@@ -143,10 +199,10 @@ const fetchModels = async () => {
id: existing.id,
data: {
...existingWithoutId,
name: existing.name || info.name || pModel.name || pModel.id,
attributes: attributes, // Update tech specs
releasedAt: new Date(pModel.created * 1000),
updatedAt: new Date()
name: existing.name || pModel.name || info.name || pModel.id,
cost,
attributes, // Update tech specs
releasedAt: pModel.created ? new Date(pModel.created * 1000) : null,
}
});
} else {
@@ -155,22 +211,29 @@ const fetchModels = async () => {
userId: user.value?.id,
providerId: provider.value!.id,
externalId: pModel.id,
name: info.name || pModel.name || pModel.id,
name: pModel.name || info.name || pModel.id,
isCustom: false,
enabled: false,
attributes: attributes,
releasedAt: new Date(pModel.created * 1000),
cost,
attributes,
releasedAt: pModel.created ? new Date(pModel.created * 1000) : null,
createdAt: new Date(),
updatedAt: new Date()
});
}
});
// delete models that are not in the API response and are not custom models
const apiModelIds = new Set(providerResponse.models.map((p: any) => p.id));
const toDelete = (provider.value?.models || []).filter((m: any) =>
!m.isCustom && !apiModelIds.has(m.externalId)
);
console.log({ toUpdate, toInsert, toDelete });
await Promise.all([
...toInsert.map(item => triplit.insert('models', item)),
...toUpdate.map(item => triplit.update('models', item.id, (m) => {
Object.assign(m, item.data);
}))
...toUpdate.map(item => triplit.update('models', item.id, item.data)),
...toDelete.map(item => triplit.delete('models', item.id))
]);
} catch (error) {
console.error('Failed to fetch models:', error);
@@ -179,14 +242,34 @@ const fetchModels = async () => {
}
}
const deleteModels = async () => {
if (!provider.value) return;
await Promise.all(provider.value.models.map(m => triplit.delete('models', m.id)));
};
const enabledModels = computed(() =>
filterModels(provider.value?.models.filter(m => m.enabled === true) as Model[] || [], modelSearch.value)
.sort((a, b) => a.releasedAt && b.releasedAt ? b.releasedAt.getTime() - a.releasedAt.getTime() : 0)
)
const disabledModels = computed(() =>
filterModels(provider.value?.models.filter(m => m.enabled === false) as Model[] || [], modelSearch.value)
.sort((a, b) => a.releasedAt && b.releasedAt ? b.releasedAt.getTime() - a.releasedAt.getTime() : 0)
)
onUnmounted(() => {
unsubscribeModels?.();
});
defineEmits(['navigate']);
</script>
<template>
<div class="flex flex-col gap-4 mt-4">
<div class="flex flex-col gap-4 mt-4" v-if="provider">
<div class="flex flex-row justify-between gap-16">
<label class="whitespace-nowrap" for="provider-api-key">Enabled</label>
<Slider :checked="provider!.enabled" @click.stop="toggleProvider()" />
<Slider :checked="provider.enabled" @click.stop="toggleProvider()" />
</div>
<div class="flex flex-row justify-between gap-16">
@@ -194,11 +277,11 @@ defineEmits(['navigate']);
<div
class="text-sm font-mono flex flex-row rounded-md bg-[var(--color-highlight)] items-center gap-1 w-7/10">
<input class="w-full p-0 pl-2 py-1 bg-transparent" :type="apiKeyVisible ? 'text' : 'password'"
id="provider-api-key" :value="apiKey"
id="provider-api-key" :value="apiKey" autocomplete="false" spellcheck="false"
@input="updateApiKey(($event.target! as HTMLInputElement).value)" />
<button @click="apiKeyVisible = !apiKeyVisible"
class="text-sm p-2 text-[var(--color-muted)] hover:text-[var(--color-text)]">
<Icon :name="apiKeyVisible ? 'mynaui:eye' : 'mynaui:eye-slash'" class="text-4" />
<Icon :name="apiKeyVisible ? 'mynaui:eye' : 'mynaui:eye-slash'" class="text-4 min-h-4 min-w-4" />
</button>
</div>
</div>
@@ -207,8 +290,8 @@ defineEmits(['navigate']);
<label class="whitespace-nowrap" for="provider-api-key">API Proxy URL</label>
<div
class="text-sm font-mono flex flex-row rounded-md bg-[var(--color-highlight)] items-center gap-1 w-7/10">
<input :placeholder="providerBaseUrls[provider!.type]" class="w-full px-2 py-1 bg-transparent"
:type="apiKeyVisible ? 'text' : 'password'" id="provider-api-key" :value="apiProxyUrl"
<input :placeholder="providerBaseUrls[provider!.type] ?? ''" class="w-full px-2 py-1 bg-transparent"
type="text" id="provider-proxy-url" :value="apiProxyUrl"
@input="updateProxyUrl(($event.target! as HTMLInputElement).value)" />
</div>
</div>
@@ -221,17 +304,26 @@ defineEmits(['navigate']);
</div>
<div class="flex flex-col">
<div class="pt-5 justify-between w-full flex">
<h4 class="whitespace-nowrap m-0">
<div class="pt-5 justify-between w-full flex flex-wrap gap-y-1 items-center">
<h4 class="whitespace-nowrap m-0 flex gap-x-2 items-start">
Model List
<span class="text-sm text-[var(--color-muted)] font-normal text-xs">
{{ provider?.models.length }} models available
<span class="text-sm text-[var(--color-muted)] font-normal text-xs flex items-center gap-1">
{{ provider?.models.length }} models available <button @click="deleteModels">
<Icon name="mynaui:x-solid" />
</button>
</span>
</h4>
<div class="flex items-center gap-2">
<input v-model="modelSearch" type="text" class="px-2 py-1 bg-[var(--color-highlight)] text-xs"
placeholder="Search models..." />
<div
class="flex items-center justify-center px-2 py-1 bg-[var(--color-highlight)] text-xs rounded-md">
<input class="p-0 bg-transparent" v-model="modelSearch" type="text"
placeholder="Search models..." />
<button :class="modelSearch.length > 0 ? 'visible' : 'invisible'" @click="modelSearch = ''"
class="right-1 hover:bg-[var(--color-highlight)] rounded p-0.5">
<Icon name="mynaui:x" class="text-3.5 block text-[var(--color-subtle)]" />
</button>
</div>
<button @click="fetchModels"
class="whitespace-nowrap flex bg-[var(--color-highlight)] hover:bg-[var(--color-highlight-high)] text-sm rounded-md items-center px-2 py-0.5 gap-2 transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
@@ -248,70 +340,46 @@ defineEmits(['navigate']);
</span>
</div>
<div v-else class="flex flex-col gap-1 mt-2">
<span class="text-sm text-[var(--color-muted)]">
Enabled
</span>
<div class="flex flex-col gap-1">
<div class="p-3 flex items-center justify-between"
v-for="model in provider?.models.filter(m => m.enabled === true).filter(m => !modelSearch || m.name.toLowerCase().includes(modelSearch.toLowerCase()))"
:key="model.id">
<div class="flex flex-row items-center">
<div class="flex items-center">
<ModelIcon :avatar="true" variant="color" :model-id="model.externalId" size="32" />
</div>
<div class="flex flex-col gap-1 ml-2">
<div
class="text-sm font-medium text-ellipsis overflow-hidden text-[var(--color-text)] flex items-center gap-1">
{{ model.name }}
<span
class="text-xs text-[var(--color-muted)] px-1 py-0.5 rounded bg-[var(--color-highlight)]">
{{ model.externalId }}
</span>
</div>
<div class="text-xs text-[var(--color-muted)]">
Released on {{
model.releasedAt?.toISOString().split('T')[0] }}
</div>
</div>
</div>
<div>
<Slider :checked="model.enabled" @click="toggleModel(model.id)" />
</div>
</div>
<span class="text-sm text-[var(--color-muted)]">
Disabled
<ClientOnly v-else>
<div class="flex flex-col gap-1 mt-2">
<span v-if="enabledModels.length > 0" class="text-sm text-[var(--color-muted)]">
Enabled
</span>
<div class="flex flex-col gap-1">
<div class="p-3 flex items-center justify-between"
v-for="model in provider?.models.filter(m => m.enabled === false).filter(m => !modelSearch || m.name.toLowerCase().includes(modelSearch.toLowerCase()))"
:key="model.id">
<div class="flex flex-row items-center">
<div class="flex items-center">
<ModelIcon :avatar="true" variant="color" :model-id="model.externalId" size="32" />
</div>
<div class="flex flex-col gap-1 ml-2">
<div
class="text-sm font-medium text-ellipsis overflow-hidden text-[var(--color-text)] flex items-center gap-1">
{{ model.name }}
<span
class="text-xs text-[var(--color-muted)] px-1 py-0.5 rounded bg-[var(--color-highlight)]">
{{ model.externalId }}
</span>
</div>
<div class="text-xs text-[var(--color-muted)]">
Released on {{ model.releasedAt?.toISOString().split('T')[0] }}
</div>
</div>
</div>
<div>
<Slider :checked="model.enabled" @click="toggleModel(model.id)" />
</div>
<DynamicScroller class="scroller" page-mode :min-item-size="64" :buffer="640"
:items="enabledModels" key-field="id">
<template v-slot="{ item: model, index, active }">
<DynamicScrollerItem :item="model" :active="active" :size-dependencies="[
model.name,
model.externalId,
modelSearch
]" :data-index="index">
<ModelItem :model="model" />
</DynamicScrollerItem>
</template>
</DynamicScroller>
<span v-if="disabledModels.length > 0" class="text-sm text-[var(--color-muted)]">
Disabled
</span>
<div class="flex flex-col gap-1">
<DynamicScroller class="scroller" page-mode :min-item-size="64" :buffer="640"
:items="disabledModels" key-field="id">
<template v-slot="{ item: model, index, active }">
<DynamicScrollerItem :item="model" :active="active" :size-dependencies="[
model.name,
model.externalId,
model.cost,
modelSearch
]" :data-index="index">
<ModelItem :model="model" />
</DynamicScrollerItem>
</template>
</DynamicScroller>
</div>
</div>
</div>
</div>
</ClientOnly>
</div>
</div>
</template>
@@ -0,0 +1,3 @@
<template>
</template>
+19 -12
View File
@@ -2,13 +2,11 @@
import GeneralSettings from './GeneralSettings.vue';
import ProviderSettings from './ProviderSettings.vue';
import ProviderSidebar from './ProviderSidebar.vue';
import SystemAssistants from './SystemAssistants.vue';
import AppearanceSettings from './AppearanceSettings.vue';
import AIServiceProvider from './AIServiceProvider.vue';
const { providers } = await useModels();
const { currentPage, pageParams, open, setPage, close } = useSettings();
console.log(providers.value);
const { providers, unsubscribe: unsubscribeModels } = await useModels();
const PAGES_CONFIG = {
general: {
@@ -16,14 +14,26 @@ const PAGES_CONFIG = {
icon: 'mynaui:cog-four',
component: GeneralSettings
},
appearance: {
label: 'Appearance',
icon: 'tabler:palette',
component: AppearanceSettings
},
providers: {
label: 'AI Providers',
icon: 'mynaui:api',
component: ProviderSettings,
sidebar: ProviderSidebar
},
systemAssistants: {
label: 'System Assistants',
icon: 'mynaui:sparkles',
component: SystemAssistants
},
} as const;
const { currentPage, pageParams, open, setPage, close } = useSettings();
const runtimePage = computed(() => {
// 1. Get the base config (e.g., 'providers' or 'general')
const config = PAGES_CONFIG[currentPage.value as keyof typeof PAGES_CONFIG] || PAGES_CONFIG.general;
@@ -35,7 +45,6 @@ const runtimePage = computed(() => {
if (currentPage.value === 'providers' && pageParams.value.length > 0) {
component = AIServiceProvider;
const providerId = pageParams.value[0];
console.log("PROVIDERS", providers.value);
const provider = providers.value!.find(p => p.id === providerId);
label = provider ? provider.name : 'Unknown Provider';
}
@@ -72,6 +81,7 @@ onUnmounted(() => {
if (open.value) {
document.body.removeEventListener('keydown', handleKeyDown);
}
unsubscribeModels?.();
});
</script>
@@ -88,7 +98,7 @@ onUnmounted(() => {
<div v-if="open" class="z-50 fixed top-1/2 left-1/2 -translate-x-1/2 flex items-center justify-center">
<div class="absolute w-[85vw] max-w-6xl h-[70vh] bg-[var(--color-base)] rounded-xl shadow-2xl border border-[var(--color-highlight)]
overflow-hidden flex max-h-[90vh] p-2">
<nav class="w-64 flex flex-col gap-1 mr-2">
<nav class="w-64 flex flex-col gap-1 mr-2 overflow-y-auto">
<!-- If the page has a custom sidebar (for nested lists), show it; otherwise show default nav -->
<component v-if="runtimePage?.sidebar" :is="runtimePage.sidebar" @navigate="setPage" />
@@ -104,7 +114,7 @@ onUnmounted(() => {
<!-- DYNAMIC CONTENT -->
<main class="flex-1 flex flex-col overflow-hidden">
<div
class="flex-1 p-3 bg-[var(--color-neutral)] overflow-y-auto border rounded-lg border-[var(--color-highlight)]">
class="flex flex-col flex-1 p-3 bg-[var(--color-neutral)] overflow-y-auto border rounded-lg border-[var(--color-highlight)]">
<header class="flex items-center justify-between pl-2 pb-2 ">
<h2 class="text-lg font-semibold m-0">{{ runtimePage.label }}</h2>
<button
@@ -113,10 +123,7 @@ onUnmounted(() => {
<Icon name="mynaui:x-solid" />
</button>
</header>
<!-- KeepAlive preserves state if the user clicks back/forth between tabs -->
<KeepAlive>
<component @navigate="setPage" :is="runtimePage.component" />
</KeepAlive>
<component @navigate="setPage" :is="runtimePage.component" />
</div>
</main>
</div>
+15
View File
@@ -0,0 +1,15 @@
<script lang="ts" setup>
const triplit = useTriplitClient();
const props = defineProps<{
model: ModelWithProvider;
}>();
</script>
<template>
<div
class="p-3 text-white flex max-w-full items-center justify-between gap-2 group hover:bg-[var(--color-highlight-low)] transition duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<ModelInfo :model="model" :show-edit="true" :show-cost="true" :show-external-id="true"
:show-release-date="true" />
</div>
</template>
+23 -1
View File
@@ -1,6 +1,24 @@
<script setup lang="ts">
import { Providers } from '~/types/model';
const triplit = useTriplitClient();
const { providers } = await useModels();
const { providers, unsubscribe: unsubscribeModels } = await useModels();
if (providers.value === undefined) throw new Error('Providers not loaded');
const { user } = useAuth();
for (const provider of Providers) {
if (!providers.value?.find(p => p.type === provider)) {
// create a new provider
await triplit.insert('providers', {
name: provider,
userId: user.value!.id,
type: provider,
enabled: false,
config: {},
});
}
}
const toggleProvider = async (id: string) => {
const provider = providers.value!.find(p => p.id === id);
@@ -11,6 +29,10 @@ const toggleProvider = async (id: string) => {
});
};
onUnmounted(() => {
unsubscribeModels?.();
});
defineEmits(['navigate']);
</script>
+4 -2
View File
@@ -1,8 +1,10 @@
<script setup lang="ts">
const { pageParams } = useSettings();
const { providers } = await useModels();
const { providers, unsubscribe: unsubscribeModels } = await useModels();
console.log("PROVIDERS", providers.value);
onUnmounted(() => {
unsubscribeModels?.();
});
defineEmits(['navigate']);
</script>
@@ -0,0 +1,62 @@
<script setup lang="ts">
const triplit = useTriplitClient();
const { providers, unsubscribe: unsubscribeModels, allModels } = await useModels();
const { settings, unsubscribe: unsubscribeSettings } = await useUserSettings();
const toggle = async (key: string) => {
console.log(key);
await triplit.update('settings', settings.value.id, {
systemAssistants: {
...settings.value.systemAssistants,
[key]: {
// @ts-ignore
...settings.value.systemAssistants[key],
// @ts-ignore
enabled: !settings.value.systemAssistants[key].enabled
}
}
});
};
const updateModel = async (key: string, model: ModelWithProvider | undefined | null) => {
await triplit.update('settings', settings.value.id, {
systemAssistants: {
...settings.value.systemAssistants,
[key]: {
// @ts-ignore
...settings.value.systemAssistants[key],
modelId: model?.id ?? null
}
}
});
};
const getModel = (id: string | null | undefined) => {
if (!id) return null;
return allModels.value.find(m => m.id === id);
}
defineEmits(['navigate']);
onUnmounted(() => {
unsubscribeModels?.();
unsubscribeSettings?.();
});
</script>
<template>
<div class="flex flex-col gap-4 flex-grow">
<div class="flex flex-col" v-for="(systemAssistant, key) in settings.systemAssistants">
<label :for="`slider-${key}`" class="flex justify-between gap-4 items-center">
<h4 class="capitalize">{{ key }}</h4>
<Slider :id="`slider-${key}`" :checked="systemAssistant.enabled" @click="toggle(key)" />
</label>
<div class="flex-1 justify-between gap-4 items-center">
<ModelSelector :providers="providers" :model-value="getModel(systemAssistant.modelId)"
@update:model-value="(model) => updateModel(key, model)" />
</div>
</div>
</div>
</template>
+21 -13
View File
@@ -1,23 +1,31 @@
<script setup lang="ts">
import type { DropdownItem } from '~/types/dropdown';
import { authClient } from '~~/lib/auth-client';
import { assert } from '~~/utils/assert';
const triplit = useTriplitClient();
const { user } = await useAuth();
const { user, signOut } = useAuth();
// to prevent the user details from going blank for a
// single frame when the user logs out (yes I am that
// particular)
const cachedUser = ref(user.value);
watch(user, () => {
if (user.value === null) return;
cachedUser.value = user.value;
})
const { toggle: toggleSettings } = useSettings();
const hovering = defineModel<boolean>({ required: true });
const { isHovered } = useSidenavContext();
const profileOpen = ref(false);
const handleLogout = async () => {
await authClient.signOut();
if ('endSession' in triplit) {
await triplit.endSession();
}
clearNuxtData();
await signOut();
assert('disconnect' in triplit);
triplit.disconnect();
await navigateTo('/auth/login');
};
@@ -43,15 +51,15 @@ const profileItems: DropdownItem[] = [
@click="toggle">
<div
:class="['w-[28px] h-[28px] flex-shrink-0 rounded-lg overflow-hidden bg-[var(--color-neutral)] flex items-center justify-center', user?.image ? '' : 'border border-[var(--color-highlight-high)]']">
<img v-if="user?.image" :src="user.image" class="w-full h-full object-cover" />
<img v-if="cachedUser?.image" :src="cachedUser.image" class="w-full h-full object-cover" />
<Icon v-else name="mynaui:user" class="w-4 h-4 text-[var(--color-muted)]" />
</div>
<span
class="text-sm font-medium text-ellipsis overflow-hidden text-[var(--color-text)] whitespace-nowrap">{{
user!.name
cachedUser?.name
}}</span>
<div :class="['flex-shrink-0 w-4 h-4 text-[var(--color-muted)] transition-all duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)] overflow-hidden transform-origin-center-left',
hovering ? 'opacity-100 scale-100' : 'opacity-0 scale-x-0 scale-y-90'
<div :class="['transform-origin-left-center flex-shrink-0 w-4 h-4 text-[var(--color-muted)] transition-all duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)] overflow-hidden transform-origin-center-left',
isHovered ? 'opacity-100 scale-100' : 'opacity-0 scale-40'
]">
<Icon class="text-4" name="mynaui:chevron-down" />
</div>
+5 -38
View File
@@ -2,47 +2,14 @@
import type { DropdownItem } from '~/types/dropdown';
const route = useRoute();
const { agents, getAgent } = await useAgents();
const homeButtonRef = ref<HTMLElement | null>(null);
const { agents, getAgent, unsubscribe: unsubscribeAgents } = await useAgents();
const activeAgent = computed(() => getAgent(route.params.id as string));
const hovering = defineModel<boolean>({ required: true });
const initialized = ref(false);
let lastHovering: boolean | null = null;
onMounted(() => {
console.log(hovering.value);
if (hovering.value && homeButtonRef.value) {
const width = homeButtonRef.value.scrollWidth;
homeButtonRef.value.style.width = `calc(${width}px + 0.5rem)`;
}
watch(hovering, (value) => {
if (lastHovering === value) {
console.warn('Hovering value did not change, but watcher was triggered');
}
console.log(value, lastHovering);
lastHovering = value;
if (!initialized.value) {
initialized.value = true;
}
if (!homeButtonRef.value) return;
if (value) {
const width = homeButtonRef.value.scrollWidth;
homeButtonRef.value.style.width = `calc(${width}px + 0.5rem)`;
} else {
homeButtonRef.value.style.width = '0';
}
});
});
const { isHovered } = useSidenavContext();
onUnmounted(() => {
console.log('unmounted');
unsubscribeAgents?.();
});
const agentDropdownOpen = ref(false);
@@ -52,8 +19,8 @@ const agentItems: DropdownItem[] = [];
<template>
<header class="flex items-center rounded-lg overflow-hidden">
<div ref="homeButtonRef" style="width: 0;"
:class="['flex flex-shrink-0 items-center overflow-hidden', initialized ? 'transition-all duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]' : '', hovering ? 'opacity-100 scale-100' : 'opacity-0 scale-95']">
<div :style="isHovered ? 'width: 32px;' : 'width: 0px;'"
:class="['flex flex-shrink-0 transform-origin-left-center items-center overflow-hidden transition-all duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]', isHovered ? 'opacity-100 scale-100' : 'opacity-0 scale-95']">
<NuxtLink to="/"
class="flex hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] rounded-lg decoration-none transition-inherit text-[var(--color-muted)] p-1.5">
<Icon name="mynaui:chevron-left" class="w-4.5 h-4.5" />
+71 -32
View File
@@ -4,7 +4,7 @@ const topicsListRef = ref<HTMLElement | null>(null);
const topicsListHeight = ref('auto');
const topicsListOpacity = ref(1);
const topicsListScale = ref(1);
const { getAgent } = await useAgents();
const { getAgent, unsubscribe: unsubscribeAgents } = await useAgents();
const triplit = useTriplitClient();
@@ -88,6 +88,19 @@ const toggleAgentsList = () => {
requestAnimationFrame(animate);
};
const autoRenameTopic = async (topicId: string) => {
const { setPage } = useSettings();
const { autoRename } = useChat(route.params.id as string);
const firstMessage = await triplit.fetchOne(triplit.query('messages').Where('topicId', '=', topicId).Order('createdAt', 'ASC').Limit(1));
if (!firstMessage) return;
const success = await autoRename(topicId, firstMessage.content);
if (!success) {
setPage('systemAssistants');
}
}
const renameTopic = (topicId: string) => {
console.log('renameTopic', topicId);
};
@@ -101,7 +114,13 @@ const deleteTopic = async (topicId: string) => {
}
}
await triplit.delete('topics', topicId);
// TODO: deeply delete all messages, generations, and message_parts in the topic
};
onUnmounted(() => {
unsubscribeAgents?.();
});
</script>
<template>
@@ -119,38 +138,58 @@ const deleteTopic = async (topicId: string) => {
<Icon name="mynaui:chevron-down" :class="['w-4 h-4', topicsOpen ? '' : '-rotate-90']" />
</button>
<div ref="topicsListRef" :inert="!topicsOpen"
<div ref="topicsListRef" :inert="!topicsOpen" :class="{ 'overflow-y-hidden': !topicsOpen }"
:style="{ height: topicsListHeight, opacity: topicsListOpacity, transform: `scale(${topicsListScale})` }"
class="mt-1 gap-1 flex flex-col transform-origin-center-top overflow-y-hidden">
<SidenavItem draggable="false" class="[&>div>div>div>[dots]]:hover:opacity-100 relative"
v-if="activeAgent?.topics !== undefined" v-for="topic in topics"
:to="`/agent/${activeAgent.id}/topic/${topic.id}`" :active="topic.id === route.params.topicId"
:name="topic.name" :key="topic.id">
<Dropdown class="shrink-0" verticality="descending" placement="right">
<template #trigger="{ toggle, isOpen }">
<div dots @click.prevent.stop="toggle"
class="opacity-0 p-1 flex items-center justify-center rounded-md hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] transition-all duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"
viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ -->
<path fill="currentColor"
d="M7 12a2 2 0 1 1-4 0a2 2 0 0 1 4 0m7 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0m7 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
</svg>
</div>
</template>
<template #content>
<div class="shadow-lg rounded p-1 flex flex-col min-w-[120px] gap-1">
<button @click.prevent="renameTopic(topic.id)"
class="text-left px-3 py-1.5 text-sm hover:bg-[var(--color-highlight)] rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
Rename
</button>
<button @click.prevent="deleteTopic(topic.id)"
class="text-left px-3 py-1.5 text-sm text-red-600 hover:bg-red-600/20 rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
Delete
</button>
</div>
</template>
</Dropdown>
</SidenavItem>
class="mt-1 gap-1 flex flex-col transform-origin-center-top">
<NuxtLink v-if="activeAgent?.topics !== undefined" v-for="topic in topics"
:to="`/agent/${activeAgent.id}/topic/${topic.id}`" :aria-label="topic.name" :class="[
'group relative decoration-none text-[var(--color-muted)] flex justify-between items-center shrink-0 rounded-lg transition-colors cursor-pointer h-9',
'px-2',
topic.id === route.params.topicId
? 'text-[var(--color-text)] bg-[var(--color-highlight)] hover:bg-[var(--color-highlight-high)] focus-visible:bg-[var(--color-highlight-high)]'
: 'hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)]'
]">
<div class="flex items-center gap-2 max-w-full flex-1">
<div v-if="!topic.renaming" class="flex justify-between items-center w-full">
<span class="text-sm font-medium overflow-hidden text-ellipsis whitespace-nowrap">
{{ topic.name }}
</span>
<Dropdown class="shrink-0 text-[var(--color-text)]" verticality="descending"
placement="right">
<template #trigger="{ toggle }">
<div dots @click.prevent="toggle"
class="opacity-0 group-hover:opacity-100 p-1 flex items-center justify-center rounded-md hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] transition-all duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"
viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ -->
<path fill="currentColor"
d="M7 12a2 2 0 1 1-4 0a2 2 0 0 1 4 0m7 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0m7 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
</svg>
</div>
</template>
<template #content="{ toggle }">
<div class="shadow-lg rounded p-1 flex flex-col min-w-[120px] gap-1">
<button @click.prevent="autoRenameTopic(topic.id); toggle()"
class="text-left px-3 py-1.5 text-sm hover:bg-[var(--color-highlight)] rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
Auto Rename
</button>
<button @click.prevent="renameTopic(topic.id); toggle()"
class="text-left px-3 py-1.5 text-sm hover:bg-[var(--color-highlight)] rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
Rename
</button>
<button @click.prevent="deleteTopic(topic.id); toggle()"
class="text-left px-3 py-1.5 text-sm text-red-600 hover:bg-red-600/20 rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
Delete
</button>
</div>
</template>
</Dropdown>
</div>
<div v-else class="flex w-full">
<Icon name="svg-spinners:3-dots-fade" class="text-6" />
</div>
</div>
</NuxtLink>
</div>
</div>
</nav>
+7 -22
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
const { agents } = await useAgents();
const { agents, unsubscribe: unsubscribeAgents, createAgent } = await useAgents();
const agentsListRef = ref<HTMLElement | null>(null);
const agentsOpen = ref(true);
const agentsListHeight = ref('auto');
@@ -62,30 +62,15 @@ const newAgent = async () => {
if (!user.value) throw new Error('User not logged in');
const agent = await triplit.insert('agents', {
name: 'New Agent',
userId: user.value.id,
systemPrompt: 'You are a helpful assistant.',
imageUrl: null,
createdAt: new Date(),
});
console.log(agents.value, agent);
const agent = await createAgent();
if (!agent) throw new Error('Failed to create agent');
let agentExists: () => void;
const agentExistsPromise = new Promise<void>((resolve) => {
agentExists = resolve;
});
watch(agents, () => {
agentExists();
});
await agentExistsPromise;
navigateTo(`/agent/${agent.id}`);
return navigateTo(`/agent/${agent.id}`);
};
onUnmounted(() => {
unsubscribeAgents?.();
});
</script>
<template>
+75 -29
View File
@@ -6,8 +6,34 @@ const isResizing = ref(false);
const startX = ref(0);
const initialWidth = ref(0);
const isMouseOver = ref(false);
const isFocused = ref(false);
const lastInteraction = ref<'mouse' | 'keyboard' | null>(null);
const isHovering = computed(() =>
isMouseOver.value || (isFocused.value && lastInteraction.value === 'keyboard')
);
const sidenavRef = ref<HTMLElement | null>(null);
const closeSidenavRef = ref<HTMLElement | null>(null);
provideSidenavContext({
isHovered: isHovering,
sidebarWidth: sidebarWidth,
isOpen: open,
close: () => {
closeSidebar();
},
});
const trackInteraction = (interaction: 'mouse' | 'keyboard') => {
lastInteraction.value = interaction;
};
const onFocusOut = (e: FocusEvent) => {
const isMovingOutside = sidenavRef.value && !sidenavRef.value.contains(e.relatedTarget as Node);
if (isMovingOutside) {
isFocused.value = false;
}
};
const { toggle: toggleSettings } = useSettings();
@@ -46,63 +72,55 @@ onMounted(() => {
document.addEventListener('mousemove', onResizeMove);
document.addEventListener('mouseup', onResizeEnd);
watch(hovering, (value) => {
if (!closeSidenavRef.value) return;
if (value) {
const width = closeSidenavRef.value.scrollWidth;
closeSidenavRef.value.style.width = `${width}px`;
} else {
closeSidenavRef.value.style.width = '0';
}
// NEW: Track global interactions
document.addEventListener('mousedown', () => trackInteraction('mouse'));
document.addEventListener('keydown', (e) => {
if (e.key === 'Tab') trackInteraction('keyboard');
});
});
onUnmounted(() => {
document.removeEventListener('mousemove', onResizeMove);
document.removeEventListener('mouseup', onResizeEnd);
});
const hovering = ref(false);
// NEW: Cleanup listeners
document.removeEventListener('mousedown', () => trackInteraction('mouse'));
document.removeEventListener('keydown', (e) => {
if (e.key === 'Tab') trackInteraction('keyboard');
});
});
const navKind = computed(() => {
if (route.path === '/') return 'home';
if (route.path.startsWith('/agent/')) return 'agent';
return null;
});
const onFocusOut = (e: FocusEvent) => {
const isMovingOutside = sidenavRef.value && !sidenavRef.value.contains(e.relatedTarget as Node);
if (isMovingOutside) {
hovering.value = false;
}
};
</script>
<template>
<div class="relative">
<aside ref="sidenavRef" :class="[
'h-full max-w-fit bg-[var(--color-base)] overflow-hidden will-change-width text-[var(--color-muted)] select-none',
open ? 'w-full mr-2' : 'w-0 mr-0',
isResizing ? '' : 'transition-[width,margin] duration-250 ease-[cubic-bezier(0,0.55,0.45,1)]'
]" :style="open ? { width: `${sidebarWidth}px` } : {}" @mouseenter="hovering = true"
@mouseleave="hovering = false" @focusin="hovering = true" @focusout="onFocusOut">
'sidenav',
open ? 'sidenav--open' : 'sidenav--closed',
isResizing ? 'sidenav--resizing' : ''
]" :style="open ? { width: `${sidebarWidth}px` } : {}" @mouseenter="isMouseOver = true"
@mouseleave="isMouseOver = false" @focusin="isFocused = true" @focusout="onFocusOut">
<div :style="{ minWidth: `${sidebarWidth}px` }" class="flex flex-col h-full justify-between">
<div class="flex flex-col h-full max-h-full overflow-y-hidden">
<!-- Header -->
<div class="relative flex flex-row gap-2 justify-between items-center mb-1.5">
<SidenavHeader v-if="navKind === 'home'" v-model="hovering" />
<SidenavHeaderAgent v-else-if="navKind === 'agent'" v-model="hovering" />
<SidenavHeader v-if="navKind === 'home'" />
<SidenavHeaderAgent v-else-if="navKind === 'agent'" />
<div class="flex items-center justify-end text-[var(--color-muted)] gap-0.5">
<div ref="closeSidenavRef" style="width: 0;"
<div :style="isHovering ? 'width: 32px;' : 'width: 0px;'"
:class="['flex-shrink-0 overflow-hidden rounded-lg transition-all duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)] transform-origin-center-right']">
<button aria-label="close sidebar" @click="closeSidebar" :class="[
'flex text-5 h-8 w-8 items-center justify-center hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] scale-100 bg-transparent transition-inherit',
'flex text-5 h-8 w-8 items-center justify-center hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] bg-transparent transition-inherit',
]">
<Icon name="mynaui:panel-left-close"
:class="['transition-inherit', hovering ? 'opacity-100 scale-100' : 'opacity-0 scale-95']" />
:class="['transition-inherit transform-origin-right-center', isHovering ? 'opacity-100 scale-100' : 'opacity-0 scale-95']" />
</button>
</div>
<div v-if="navKind === 'agent'" class="flex-shrink-0 overflow-hidden rounded-lg">
@@ -143,3 +161,31 @@ const onFocusOut = (e: FocusEvent) => {
</div>
</div>
</template>
<style scoped>
.sidenav {
height: 100%;
max-width: fit-content;
background: var(--color-base);
overflow: hidden;
will-change: width;
color: var(--color-muted);
user-select: none;
transition: width 250ms cubic-bezier(0, 0.55, 0.45, 1),
margin 250ms cubic-bezier(0, 0.55, 0.45, 1);
}
.sidenav--open {
width: 100%;
margin-right: 0.5rem;
}
.sidenav--closed {
width: 0;
margin-right: 0;
}
.sidenav--resizing {
transition: none;
}
</style>
+15 -6
View File
@@ -25,15 +25,18 @@ watch(() => props.checked, (newValue) => {
</script>
<template>
<button role="switch" class="vl-toggle-switch" :aria-disabled="(props.disabled === true) ? 'true' : 'false'"
:aria-label="label" :aria-labelledby="id" :tabindex="(disabled) ? '-1' : '0'" @click="(e) => $emit('click', e)"
:aria-checked="active" :data-state="(active) ? 'checked' : 'unchecked'">
<button :id="id" role="switch" class="vl-toggle-switch"
:aria-disabled="(props.disabled === true) ? 'true' : 'false'" :aria-label="label" :aria-labelledby="id"
:tabindex="(disabled) ? '-1' : '0'" @click="(e) => $emit('click', e)" :aria-checked="active"
:data-state="(active) ? 'checked' : 'unchecked'">
<div></div>
</button>
</template>
<style scoped>
.vl-toggle-switch {
display: flex;
align-items: center;
font-size: inherit;
border: 0;
cursor: pointer;
@@ -52,12 +55,14 @@ watch(() => props.checked, (newValue) => {
}
.vl-toggle-switch div {
transform-origin: center left;
will-change: transform;
position: relative;
left: 0;
width: 1em;
height: 1em;
background: #f7f7f7;
border-radius: 90px;
border-radius: 9999px;
pointer-events: none;
transition: all 0.3s;
}
@@ -67,11 +72,15 @@ watch(() => props.checked, (newValue) => {
}
.vl-toggle-switch[data-state="checked"] div {
left: 100%;
transform: translateX(-100%);
transform-origin: center right;
transform: translateX(calc(2.5em - 1em - 0.5rem));
}
.vl-toggle-switch:active div {
width: 1.3em;
}
.vl-toggle-switch[data-state="checked"]:active div {
transform: translateX(calc(2.5em - 1.3em - 0.5rem));
}
</style>