gestion des providers
This commit is contained in:
1 parent
357bf9b41c
commit
4d72952b2c
46 files changed
+995
-150
No files matched your search
@@ -0,0 +1,13 @@
|
||||
import { api } from '../client';
|
||||
import type { Provider, Item } from "$lib/types/api"
|
||||
import { space } from "$lib/state/space.svelte"
|
||||
|
||||
export const providerApi = {
|
||||
providerTypes: () => api.get<Item[]>('/provider/providerTypes'),
|
||||
getProviders: () => api.get<Provider[]>(`/provider/${space.id}`),
|
||||
getProvider: (id: string) => api.get<Provider>(`/provider/${space.id}/${id}`),
|
||||
createProvider: (data: Partial<Provider>) => api.post<Provider>(`/provider/${space.id}`, data),
|
||||
updateProvider: (data: Partial<Provider>) => api.put<Provider>(`/provider/${space.id}`, data),
|
||||
deleteProvider: (id: string) => api.delete(`/provider/${space.id}/${id}`),
|
||||
getAvailableModels: (data: Partial<Provider>) => api.post<string[]>(`/provider/${space.id}/avalable-models`, data),
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
import { api } from '../client';
|
||||
import type { User, Address } from "$lib/types/api"
|
||||
import type { User, Address, UserWithTeams } from "$lib/types/api"
|
||||
|
||||
export const userApi = {
|
||||
me: () => api.get<User>('/user/me'),
|
||||
me: () => api.get<UserWithTeams>('/user/me'),
|
||||
update: (id: string, data: Partial<User>) => api.put<User>(`/user/${id}`, data),
|
||||
|
||||
listAddresses: (id: string) => api.get<Address[]>(`/user/${id}/address`),
|
||||
|
||||
@@ -8,6 +8,4 @@
|
||||
|
||||
{#if errors.has(key)}
|
||||
<span class="text-red-700 dark:text-red-400 text-xs">{errors.get(key)}</span>
|
||||
{:else}
|
||||
<span class="text-xs"> </span>
|
||||
{/if}
|
||||
@@ -3,6 +3,7 @@
|
||||
import Menu from "$lib/components/Menu.svelte"
|
||||
import { logout } from "$lib/state/auth.svelte"
|
||||
import logo from "$lib/assets/logo-txt.png"
|
||||
import { space } from '$lib/state/space.svelte'
|
||||
|
||||
let {handleMenuDesktop, handleMenuMobile} = $props();
|
||||
|
||||
@@ -12,7 +13,7 @@
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row italic mb-4">
|
||||
<div class="flex flex-row italic mb-2">
|
||||
{#if uiState.menuContentVisible}
|
||||
<div class="flex-1 p-3 font openonly">
|
||||
<img src={logo} class="h-5" alt="logo"/>
|
||||
@@ -25,24 +26,38 @@
|
||||
<i class="icon-menu pointer block p-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
{#if uiState.menuContentVisible}
|
||||
<div class="m-2">
|
||||
<div class="formelement">
|
||||
<select class="textinput">
|
||||
{#each space.spaces as s (s.id)}
|
||||
<option value={s.id}>{s.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div id="menu" class="flex-1 overflow-y-scroll">
|
||||
<Menu label="Conversation" icon="icon-chat" link="/"/>
|
||||
<Menu label="Tâches" icon="icon-tasks" link="/tasks"/>
|
||||
<Menu label="Nouveau chat" icon="icon-plus" link="/"/>
|
||||
<Menu label="Conversations" icon="icon-chat" link="/history"/>
|
||||
<Menu label="Planification" icon="icon-tasks" link="/tasks"/>
|
||||
<Menu label="Connaissances" icon="icon-graduation-cap" link="/kb"/>
|
||||
<Menu label="Historique" icon="icon-history" link="/history"/>
|
||||
<Menu label="Paramétrage" icon="icon-cog" link="/settings"/>
|
||||
<Menu label="Réglages" icon="icon-cog" link="/settings"/>
|
||||
</div>
|
||||
|
||||
<div id="profile" class="w-full flex flex-row items-center border-t border-stone-200 dark:border-stone-800">
|
||||
<div id="profile" class="w-full items-center border-t border-stone-200 dark:border-stone-800">
|
||||
{#if uiState.menuContentVisible}
|
||||
<div class="p-4 flex-1 profile">
|
||||
<div>Fabien Masson</div>
|
||||
<div class="flex">
|
||||
<a href="/profile" class="text-xs flex-1">Modifier</a>
|
||||
<div class="flex flex-row gap-2 p-2 ">
|
||||
<div class="py-1 flex-1 flex flex-row text-xs">
|
||||
<div class="w-6 h-6 rounded-full bg-cyan-600 text-white flex items-center justify-center font-medium">
|
||||
FM
|
||||
</div>
|
||||
<div class="flex-1 ml-2 inline-flex items-center">Fabien Masson</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<button class="w-full bt" onclick={handleLogout} title="Déconecter">
|
||||
<a href="/profile" class="bt small inline-flex items-center justify-center" title="Edition profil">
|
||||
<i class="icon-edit"></i>
|
||||
</a>
|
||||
<button class="bt small" onclick={handleLogout} title="Déconnecter">
|
||||
<i class="icon-logout"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { type User } from "$lib/types/api"
|
||||
import type { User } from "$lib/types/api"
|
||||
|
||||
type AuthState = {
|
||||
user: User | null;
|
||||
status: 'loading' | 'authenticated' | 'unauthenticated';
|
||||
//accessToken: string | null
|
||||
user: User | null
|
||||
status: 'loading' | 'authenticated' | 'unauthenticated'
|
||||
}
|
||||
|
||||
export const auth: AuthState = $state({
|
||||
user: null,
|
||||
status: 'loading',
|
||||
//accessToken: null
|
||||
})
|
||||
|
||||
export const logout = () => {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { Team } from "$lib/types/api"
|
||||
|
||||
export const space = $state({
|
||||
spaces: [] as Team[],
|
||||
id: "",
|
||||
});
|
||||
@@ -1,3 +1,8 @@
|
||||
export type Item = {
|
||||
id: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
firstname: string
|
||||
@@ -9,9 +14,54 @@ export interface User {
|
||||
_version: string
|
||||
}
|
||||
|
||||
export interface UserTeam {
|
||||
id: string
|
||||
userId: string
|
||||
teamId: string
|
||||
administrator: boolean
|
||||
_version: string
|
||||
}
|
||||
|
||||
export interface Team {
|
||||
id: string
|
||||
label: string
|
||||
defaultModelId: string
|
||||
_version: string
|
||||
}
|
||||
|
||||
export interface UserWithTeams {
|
||||
user: User
|
||||
userTeams: UserTeam[]
|
||||
teams: Team[]
|
||||
}
|
||||
|
||||
export interface Address {
|
||||
id: string
|
||||
type: string
|
||||
address: string
|
||||
_version: string
|
||||
}
|
||||
|
||||
export interface Provider {
|
||||
id: string
|
||||
teamId: string
|
||||
type: string
|
||||
name: string
|
||||
apiKey: string
|
||||
baseUrl: string
|
||||
models: string
|
||||
_date_created: string
|
||||
_date_updated: string
|
||||
_version: string
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
id: string
|
||||
teamId: string
|
||||
providerId: string
|
||||
modelName: string
|
||||
configuration: object
|
||||
_date_created: string
|
||||
_date_updated: string
|
||||
_version: string
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
const emailRegex: RegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
const urlRegex: RegExp = /^https?:\/\/([a-zA-Z0-9.-]+|\[[0-9a-fA-F:.]+\])(:\d+)?(\/[^\s]*)?$/;
|
||||
|
||||
export function validateEmail(input: string): boolean {
|
||||
return emailRegex.test(input);
|
||||
}
|
||||
|
||||
export function validateUrl(input: string): boolean {
|
||||
return urlRegex.test(input);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
|
||||
<div class="container">
|
||||
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
|
||||
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
|
||||
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
|
||||
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
|
||||
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
|
||||
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
|
||||
</div>
|
||||
@@ -3,6 +3,7 @@
|
||||
import { page } from '$app/state';
|
||||
import {uiState} from "$lib/state/ui.svelte"
|
||||
import { auth } from '$lib/state/auth.svelte'
|
||||
import { space } from '$lib/state/space.svelte'
|
||||
import Sidebar from "$lib/components/Sidebar.svelte"
|
||||
import { goto } from '$app/navigation'
|
||||
import { onMount } from "svelte";
|
||||
@@ -39,7 +40,9 @@
|
||||
onMount(async () => {
|
||||
handleResize()
|
||||
if (data.me) {
|
||||
auth.user = data.me
|
||||
auth.user = data.me.user
|
||||
space.spaces = data.me.teams
|
||||
space.id = data.me.teams[0].id
|
||||
themeToLocalStorage(auth.user.theme)
|
||||
applyTheme()
|
||||
auth.status = 'authenticated'
|
||||
@@ -64,7 +67,7 @@
|
||||
</div>
|
||||
<div id="main">
|
||||
<div class="flex flex-col h-screen ">
|
||||
<div class={["header", page.data.theme]}>
|
||||
<div class={["header", "container" , page.data.theme]}>
|
||||
<h1 class="flex">
|
||||
<span class="flex-1">{page.data.title}</span>
|
||||
{#if page.data.back}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import type { PageProps } from './$types';
|
||||
import type { User, Address } from '$lib/types/api'
|
||||
import type { User, Address, Item } from '$lib/types/api'
|
||||
import { userApi } from '$lib/api/endpoints/user'
|
||||
import { miscApi } from '$lib/api/endpoints/misc'
|
||||
import FormError from '$lib/components/FormError.svelte';
|
||||
@@ -13,15 +13,15 @@
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
let form : User = $state(data.me)
|
||||
let form : User = $state(data.me.user)
|
||||
let addresses : Address[] = $state([] as Address[])
|
||||
let newAddress : Address = $state({} as Address)
|
||||
let timezones = $state([] as string[])
|
||||
let timezones = $state([] as Item[])
|
||||
let errors = new SvelteMap<string,string>()
|
||||
|
||||
onMount(async ()=> {
|
||||
addresses = await userApi.listAddresses(form.id)
|
||||
timezones = await miscApi.listTimezones()
|
||||
timezones = (await miscApi.listTimezones()).map((it)=>{return {id:it, text:it}})
|
||||
})
|
||||
|
||||
$effect(()=>{
|
||||
@@ -64,24 +64,26 @@
|
||||
|
||||
</script>
|
||||
|
||||
<div class="mb-4 formgroup">
|
||||
<div class="mb-4 formgroup container">
|
||||
<h2>Mon compte</h2>
|
||||
|
||||
<form onsubmit={submitUser} onchange={()=>{errors.clear()}}>
|
||||
|
||||
<label for="email" class="block my-1 flex-1">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
bind:value={form.email}
|
||||
class="textinput"
|
||||
autofocus
|
||||
/>
|
||||
<FormError key="email" errors={errors} />
|
||||
<div class="formelement">
|
||||
<label for="email" class="my-1 flex-1">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
bind:value={form.email}
|
||||
class="textinput"
|
||||
autofocus
|
||||
/>
|
||||
<FormError key="email" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="flex-1 mr-2">
|
||||
<label for="firstname" class="block my-1 flex-1">Prénom</label>
|
||||
<div class="grid grid-cols-1 gap-x-4 md:grid-cols-2">
|
||||
<div class="formelement">
|
||||
<label for="firstname" class=" my-1 flex-1">Prénom</label>
|
||||
<input
|
||||
id="firstname"
|
||||
type="text"
|
||||
@@ -91,8 +93,8 @@
|
||||
<FormError key="firstname" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="flex-1 ml-2">
|
||||
<label for="lastname" class="block my-1 flex-1">Nom</label>
|
||||
<div class="formelement">
|
||||
<label for="lastname" class="my-1 flex-1">Nom</label>
|
||||
<input
|
||||
id="lastname"
|
||||
type="text"
|
||||
@@ -103,9 +105,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="grid grid-cols-1 gap-x-4 md:grid-cols-2">
|
||||
|
||||
<div class="flex-1 mr-2">
|
||||
<div class="formelement">
|
||||
<label for="lang" class="block my-1 flex-1">Langue</label>
|
||||
<select bind:value={form.lang} class="textinput">
|
||||
<option value="en">English</option>
|
||||
@@ -114,14 +116,14 @@
|
||||
<FormError key="lang" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="flex-1 ml-2">
|
||||
<label for="lang" class="block my-1 flex-1">Timezone</label>
|
||||
<div class="formelement">
|
||||
<label for="timezone" class="block my-1 flex-1">Timezone</label>
|
||||
<Select items={timezones} bind:value={form.timezone}/>
|
||||
<FormError key="lang" errors={errors} />
|
||||
<FormError key="timezone" errors={errors} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 mr-2">
|
||||
<div class="formelement">
|
||||
<label for="theme" class="block my-1 flex-1">Thème</label>
|
||||
<select bind:value={form.theme} class="textinput">
|
||||
<option value="auto">Automatique</option>
|
||||
@@ -137,14 +139,14 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mb-4 formgroup">
|
||||
<div class="mb-4 formgroup container">
|
||||
<h2>Canaux de communication</h2>
|
||||
|
||||
{#each addresses as address (address.id) }
|
||||
<div class="m-2">
|
||||
<div class="dark:bg-stone-700 bg-stone-300 rounded flex inline px-2 py-1">
|
||||
<div class="dark:bg-stone-700 bg-stone-300 rounded inline px-2 py-1 text-sm">
|
||||
<span class="inline-block w-20">{address.type} : </span>
|
||||
<span class="font-bold">{address.address}</span>
|
||||
<span class="">{address.address}</span>
|
||||
<button class="ml-4 pointer" onclick={()=>{handleDeleteAddress(address.id)}}>
|
||||
<i class="icon-cancel"></i>
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
|
||||
<div class="container grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
|
||||
|
||||
<a class="bt p-5 text-center"
|
||||
title="Modèles"
|
||||
|
||||
@@ -1 +1,157 @@
|
||||
<div>Modèles</div>
|
||||
<script lang="ts">
|
||||
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
import type { Provider} from '$lib/types/api'
|
||||
import type { PageProps } from './$types';
|
||||
import { providerApi } from "$lib/api/endpoints/provider"
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { validateUrl } from '$lib/utilities/validators'
|
||||
import ProviderForm from './ProviderForm.svelte'
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
let providers = $state([] as Provider[])
|
||||
let addingProvider = $state(false)
|
||||
let editingProvider = $state(null as string|null)
|
||||
let providerForm = $state({} as Provider)
|
||||
let errors = new SvelteMap<string,string>()
|
||||
|
||||
|
||||
onMount(async ()=>{
|
||||
providers = await providerApi.getProviders()
|
||||
})
|
||||
|
||||
const enterAddProviderHandler = () => {
|
||||
if (editingProvider) return
|
||||
errors.clear()
|
||||
providerForm = {} as Provider
|
||||
providerForm.models = ""
|
||||
addingProvider= true
|
||||
}
|
||||
|
||||
const cancelAddProviderHandler = () => {
|
||||
addingProvider= false
|
||||
}
|
||||
|
||||
const submitAddProvider = (p: Provider) => {
|
||||
|
||||
if (!p.name) {
|
||||
errors.set("name","Le nom est obligatoire !")
|
||||
}
|
||||
if (!p.type) {
|
||||
errors.set("type","Le type est obligatoire !")
|
||||
}
|
||||
if (p.baseUrl && !validateUrl(p.baseUrl)) {
|
||||
errors.set("baseUrl","Mauvaise URL !")
|
||||
}
|
||||
if (errors.size === 0) {
|
||||
providerApi.createProvider(p)
|
||||
.then((p)=>{
|
||||
providers.push(p)
|
||||
addingProvider= false
|
||||
providerForm = {} as Provider
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const cancelEditProviderHandler = () => {
|
||||
editingProvider = null
|
||||
providerForm = {} as Provider
|
||||
}
|
||||
|
||||
const submitEditProvider = (p: Provider) => {
|
||||
|
||||
if (!p.name) {
|
||||
errors.set("name","Le nom est obligatoire !")
|
||||
}
|
||||
if (!p.type) {
|
||||
errors.set("type","Le type est obligatoire !")
|
||||
}
|
||||
if (p.baseUrl && !validateUrl(p.baseUrl)) {
|
||||
errors.set("baseUrl","Mauvaise URL !")
|
||||
}
|
||||
if (errors.size === 0) {
|
||||
providerApi.updateProvider(p)
|
||||
.then((p)=>{
|
||||
providers = providers.map((it)=>{return p.id===it.id ? p : it})
|
||||
editingProvider = null
|
||||
providerForm = {} as Provider
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleEditProvider = async (id: string) => {
|
||||
errors.clear()
|
||||
if (addingProvider) return
|
||||
providerApi.getProvider(id)
|
||||
.then((data)=>{
|
||||
providers = providers.map((it)=>it.id===id ? data : it)
|
||||
editingProvider = id
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteProvider = (id: string) => {
|
||||
providerApi.deleteProvider(id)
|
||||
.then(() => {
|
||||
providers = providers.filter((it)=>it.id!==id)
|
||||
})
|
||||
.catch((err) => {console.log(err)})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
|
||||
{#if addingProvider}
|
||||
{#key "new"}
|
||||
<ProviderForm
|
||||
item={providerForm}
|
||||
errors={errors}
|
||||
providerTypes={data.providerTypes}
|
||||
onSave={submitAddProvider}
|
||||
onCancel={cancelAddProviderHandler}
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<div class="rounded border border-dashed p-2 border-stone-500 cursor-pointer" onclick={enterAddProviderHandler} tabindex="0">
|
||||
<i class="icon-plus"></i>
|
||||
Ajouter un fournisseur
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#each providers as p (p.id) }
|
||||
{#if editingProvider===p.id}
|
||||
{#key p.id}
|
||||
<ProviderForm
|
||||
item={p}
|
||||
errors={errors}
|
||||
providerTypes={data.providerTypes}
|
||||
onSave={submitEditProvider}
|
||||
onCancel={cancelEditProviderHandler}
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<div class="rounded border p-2 my-2 border-stone-500 flex">
|
||||
<div class="flex-1">
|
||||
<span>{ p.name }</span>
|
||||
<span class="text-stone-500">({ p.type })</span>
|
||||
</div>
|
||||
<button class="bt small mx-1" onclick={()=>handleEditProvider(p.id)}>
|
||||
<i class="icon-edit"></i>
|
||||
</button>
|
||||
<button class="bt small mx-1" onclick={()=>handleDeleteProvider(p.id)}>
|
||||
<i class="icon-trash-empty"></i>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
@@ -1,7 +1,11 @@
|
||||
export function load() {
|
||||
import { providerApi } from "$lib/api/endpoints/provider"
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
return {
|
||||
title: 'Paramétrage : Modèles',
|
||||
back: "/settings",
|
||||
theme: "settings"
|
||||
theme: "settings",
|
||||
providerTypes: await providerApi.providerTypes(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import FormError from '$lib/components/FormError.svelte';
|
||||
import Select from 'svelte-select/no-styles';
|
||||
import type { Provider, Item } from "$lib/types/api"
|
||||
import { providerApi } from "$lib/api/endpoints/provider"
|
||||
|
||||
type Props = {
|
||||
item: Provider
|
||||
errors: Map<string,string>
|
||||
providerTypes: Item[]
|
||||
onSave: (item: Provider) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
let {
|
||||
item,
|
||||
errors,
|
||||
providerTypes,
|
||||
onSave,
|
||||
onCancel,
|
||||
}: Props = $props();
|
||||
|
||||
type Selectable = {
|
||||
value: string
|
||||
selected: boolean
|
||||
}
|
||||
let selectedModels = $state(item.models.split(";"))
|
||||
let availableModels : Selectable[]= $state(item.models.split(";").map((it)=> {return {value:it, selected:true}}))
|
||||
let modelsErrors = new SvelteMap<string,string>()
|
||||
|
||||
const autofocus = (node: HTMLElement) => {
|
||||
node.focus();
|
||||
}
|
||||
|
||||
const handleSave = (e: Event) => {
|
||||
e.preventDefault()
|
||||
item.models = selectedModels.join(";")
|
||||
onSave(item)
|
||||
}
|
||||
|
||||
const handleCancel = (e: Event) => {
|
||||
e.preventDefault()
|
||||
onCancel()
|
||||
}
|
||||
|
||||
const fetchModels = (e: Event) => {
|
||||
e.preventDefault()
|
||||
modelsErrors.clear()
|
||||
providerApi.getAvailableModels(item)
|
||||
.then((data) => {
|
||||
availableModels = data.map((it)=>{
|
||||
return { value: it, selected: selectedModels.includes(it)}
|
||||
});
|
||||
})
|
||||
.catch((err) => { modelsErrors.set("models",err) })
|
||||
}
|
||||
|
||||
const handleModelChange = () => {
|
||||
selectedModels = availableModels.filter((it)=>it.selected).map((it)=>it.value)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="formgroup my-2">
|
||||
<form onsubmit={handleSave} onchange={()=>{errors.clear()}}>
|
||||
<div class="formelement">
|
||||
<label for="name">Nom</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
bind:value={item.name}
|
||||
class="textinput"
|
||||
placeholder="nom affiché"
|
||||
use:autofocus
|
||||
/>
|
||||
<FormError key="name" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="formelement">
|
||||
<label for="type" class="block my-1 flex-1">Type</label>
|
||||
<Select items={providerTypes}
|
||||
valueMode="id" itemId="id" label="text"
|
||||
bind:value={item.type}
|
||||
/>
|
||||
<FormError key="type" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="formelement">
|
||||
<label for="baseUrl">URL</label>
|
||||
<input
|
||||
id="baseUrl"
|
||||
type="text"
|
||||
bind:value={item.baseUrl}
|
||||
class="textinput"
|
||||
placeholder="http://127.0.0.1:8080/v1"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<FormError key="baseUrl" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="formelement">
|
||||
<label for="apiKey">Clé API</label>
|
||||
<input
|
||||
id="apiKey"
|
||||
type="password"
|
||||
bind:value={item.apiKey}
|
||||
class="textinput"
|
||||
placeholder="clé API (optionnel)"
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
<FormError key="apiKey" errors={errors} />
|
||||
</div>
|
||||
|
||||
<div class="formelement">
|
||||
<label for="apiKey">Models</label>
|
||||
<button class="bt small" onclick={fetchModels}>
|
||||
Afficher les modèles disponibles
|
||||
</button>
|
||||
<ol class="ml-2">
|
||||
{#each availableModels as model (model.value)}
|
||||
{#if model.value}
|
||||
<li>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={model.selected} onchange={handleModelChange}/>
|
||||
{model.value}
|
||||
</label>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ol>
|
||||
<div><FormError key="models" errors={modelsErrors} /></div>
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<button type="button" class="bt" onclick={handleCancel}>Annuler</button>
|
||||
<button type="submit" class="bt mx-4">Enregistrer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,35 +8,45 @@ html {
|
||||
@apply h-full;
|
||||
}
|
||||
body {
|
||||
@apply h-full bg-stone-100 text-stone-900 dark:bg-stone-900 dark:text-stone-200;
|
||||
@apply h-full bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100;
|
||||
}
|
||||
|
||||
label {
|
||||
.formelement > label {
|
||||
@apply text-xs text-stone-600 uppercase dark:text-stone-400;
|
||||
}
|
||||
hr {
|
||||
@apply border-stone-200 dark:border-stone-800;
|
||||
}
|
||||
h2 {
|
||||
@apply mb-4 font-bold;
|
||||
@apply mb-4 text-base font-bold;
|
||||
}
|
||||
|
||||
.container {
|
||||
@apply mx-auto max-w-3xl;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
.bt {
|
||||
appearance: button;
|
||||
@apply cursor-pointer rounded bg-stone-300 px-4 py-1 font-bold hover:bg-stone-200 dark:bg-stone-800 dark:hover:bg-stone-700;
|
||||
&.small {
|
||||
@apply px-2 py-1;
|
||||
}
|
||||
}
|
||||
.menuitem {
|
||||
@apply text-stone-800 text-stone-950 dark:text-stone-300 hover:dark:text-stone-50;
|
||||
@apply text-stone-950 dark:text-stone-300 hover:dark:text-stone-50;
|
||||
}
|
||||
|
||||
.header {
|
||||
@apply p-2 text-lg;
|
||||
@apply py-2 text-lg font-bold;
|
||||
|
||||
/*
|
||||
h1 {
|
||||
@apply rounded bg-stone-200 px-2 font-bold text-sky-600 dark:bg-stone-800 dark:text-sky-200;
|
||||
@apply rounded bg-stone-200 px-2 font-bold uppercase dark:bg-stone-800;
|
||||
}
|
||||
*/
|
||||
|
||||
&.settings {
|
||||
h1 {
|
||||
@@ -53,10 +63,6 @@ h2 {
|
||||
@apply text-sky-500;
|
||||
}
|
||||
|
||||
.formgroup {
|
||||
@apply rounded border border-stone-300 bg-stone-100 p-2 dark:border-stone-700 dark:bg-stone-900;
|
||||
}
|
||||
|
||||
#outer {
|
||||
@apply flex h-full flex-row;
|
||||
}
|
||||
@@ -94,7 +100,7 @@ h2 {
|
||||
@apply ml-10;
|
||||
}
|
||||
#sidebar {
|
||||
@apply fixed left-0 h-full w-full bg-stone-100 dark:bg-stone-900;
|
||||
@apply fixed left-0 h-full w-full bg-stone-100 dark:bg-stone-950;
|
||||
}
|
||||
.mobileMenuClosed {
|
||||
#sidebar {
|
||||
@@ -103,14 +109,24 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
/* svelte select */
|
||||
/* form */
|
||||
|
||||
.textinput {
|
||||
@apply h-8 w-full rounded border-stone-700 bg-white px-2 py-1 text-stone-900 dark:bg-stone-800 dark:text-stone-100;
|
||||
.formgroup {
|
||||
@apply rounded border border-stone-300 bg-stone-100 p-2 text-sm dark:border-stone-700 dark:bg-stone-900;
|
||||
}
|
||||
|
||||
.formelement {
|
||||
@apply mb-3;
|
||||
}
|
||||
|
||||
.textinput {
|
||||
@apply h-8 w-full rounded border-stone-700 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100;
|
||||
}
|
||||
|
||||
/* svelte select */
|
||||
|
||||
.svelte-select {
|
||||
@apply relative flex h-8 w-full border-none text-stone-900 dark:bg-stone-800 dark:text-stone-100;
|
||||
@apply relative flex h-8 w-full border-none text-stone-900 dark:bg-stone-950 dark:text-stone-100;
|
||||
}
|
||||
|
||||
.svelte-select .value-container {
|
||||
@@ -187,7 +203,7 @@ h2 {
|
||||
}
|
||||
|
||||
.svelte-select-list .list-group-title {
|
||||
@apply h-8 cursor-default px-5 text-sm leading-10 font-medium overflow-ellipsis whitespace-nowrap text-gray-400 uppercase;
|
||||
@apply h-8 cursor-default px-5 text-sm leading-10 font-medium text-ellipsis whitespace-nowrap text-gray-400 uppercase;
|
||||
}
|
||||
|
||||
.svelte-select-list .empty {
|
||||
@@ -195,7 +211,7 @@ h2 {
|
||||
}
|
||||
|
||||
.svelte-select-item {
|
||||
@apply h-8 cursor-default overflow-hidden px-5 leading-10 overflow-ellipsis whitespace-nowrap text-gray-800;
|
||||
@apply h-8 cursor-default overflow-hidden px-5 leading-10 text-ellipsis whitespace-nowrap text-gray-800;
|
||||
}
|
||||
|
||||
.item.group-item {
|
||||
@@ -223,7 +239,7 @@ h2 {
|
||||
}
|
||||
|
||||
.multi-item {
|
||||
@apply mt-1 mr-1 flex h-8 max-w-full cursor-default items-center overflow-hidden rounded-sm border border-gray-200 bg-gray-100 pr-1 pl-1 leading-8 overflow-ellipsis whitespace-nowrap;
|
||||
@apply mt-1 mr-1 flex h-8 max-w-full cursor-default items-center overflow-hidden rounded-sm border border-gray-200 bg-gray-100 pr-1 pl-1 leading-8 text-ellipsis whitespace-nowrap;
|
||||
}
|
||||
|
||||
.multi-item.disabled {
|
||||
|
||||
@@ -119,6 +119,18 @@
|
||||
"css": "cancel",
|
||||
"code": 59401,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
||||
"css": "plus",
|
||||
"code": 59402,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f48ae54adfb27d8ada53d0fd9e34ee10",
|
||||
"css": "trash-empty",
|
||||
"code": 59403,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
.icon-logout:before { content: '\e807'; } /* '' */
|
||||
.icon-left-big:before { content: '\e808'; } /* '' */
|
||||
.icon-cancel:before { content: '\e809'; } /* '' */
|
||||
.icon-plus:before { content: '\e80a'; } /* '' */
|
||||
.icon-trash-empty:before { content: '\e80b'; } /* '' */
|
||||
.icon-facebook:before { content: '\f09a'; } /* '' */
|
||||
.icon-tasks:before { content: '\f0ae'; } /* '' */
|
||||
.icon-menu:before { content: '\f0c9'; } /* '' */
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?82712141');
|
||||
src: url('../font/fontello.eot?82712141#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.svg?82712141#fontello') format('svg');
|
||||
src: url('../font/fontello.eot?20532310');
|
||||
src: url('../font/fontello.eot?20532310#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.svg?20532310#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('data:application/octet-stream;base64,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 truncated
|
||||
url('data:application/octet-stream;base64,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 truncated
|
||||
src: url('data:application/octet-stream;base64,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 truncated
|
||||
url('data:application/octet-stream;base64,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 truncated
|
||||
}
|
||||
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
||||
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
|
||||
@@ -17,7 +17,7 @@
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?82712141#fontello') format('svg');
|
||||
src: url('../font/fontello.svg?20532310#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -67,6 +67,8 @@
|
||||
.icon-logout:before { content: '\e807'; } /* '' */
|
||||
.icon-left-big:before { content: '\e808'; } /* '' */
|
||||
.icon-cancel:before { content: '\e809'; } /* '' */
|
||||
.icon-plus:before { content: '\e80a'; } /* '' */
|
||||
.icon-trash-empty:before { content: '\e80b'; } /* '' */
|
||||
.icon-facebook:before { content: '\f09a'; } /* '' */
|
||||
.icon-tasks:before { content: '\f0ae'; } /* '' */
|
||||
.icon-menu:before { content: '\f0c9'; } /* '' */
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
.icon-logout { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-left-big { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-trash-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-tasks { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
.icon-logout { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-left-big { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-trash-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-tasks { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
||||
+9
-7
@@ -1,11 +1,11 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?90952797');
|
||||
src: url('../font/fontello.eot?90952797#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?90952797') format('woff2'),
|
||||
url('../font/fontello.woff?90952797') format('woff'),
|
||||
url('../font/fontello.ttf?90952797') format('truetype'),
|
||||
url('../font/fontello.svg?90952797#fontello') format('svg');
|
||||
src: url('../font/fontello.eot?1806785');
|
||||
src: url('../font/fontello.eot?1806785#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?1806785') format('woff2'),
|
||||
url('../font/fontello.woff?1806785') format('woff'),
|
||||
url('../font/fontello.ttf?1806785') format('truetype'),
|
||||
url('../font/fontello.svg?1806785#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?90952797#fontello') format('svg');
|
||||
src: url('../font/fontello.svg?1806785#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -64,6 +64,8 @@
|
||||
.icon-logout:before { content: '\e807'; } /* '' */
|
||||
.icon-left-big:before { content: '\e808'; } /* '' */
|
||||
.icon-cancel:before { content: '\e809'; } /* '' */
|
||||
.icon-plus:before { content: '\e80a'; } /* '' */
|
||||
.icon-trash-empty:before { content: '\e80b'; } /* '' */
|
||||
.icon-facebook:before { content: '\f09a'; } /* '' */
|
||||
.icon-tasks:before { content: '\f0ae'; } /* '' */
|
||||
.icon-menu:before { content: '\f0c9'; } /* '' */
|
||||
|
||||
@@ -146,11 +146,11 @@
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('./font/fontello.eot?42653923');
|
||||
src: url('./font/fontello.eot?42653923#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?42653923') format('woff'),
|
||||
url('./font/fontello.ttf?42653923') format('truetype'),
|
||||
url('./font/fontello.svg?42653923#fontello') format('svg');
|
||||
src: url('./font/fontello.eot?13627011');
|
||||
src: url('./font/fontello.eot?13627011#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?13627011') format('woff'),
|
||||
url('./font/fontello.ttf?13627011') format('truetype'),
|
||||
url('./font/fontello.svg?13627011#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -245,34 +245,42 @@
|
||||
<div class="span3" title="Code: 0xe809">
|
||||
<i class="demo-icon icon-cancel"></i> <span class="i-name">icon-cancel</span><span class="i-code">0xe809</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xe80a">
|
||||
<i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe80a</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xe80b">
|
||||
<i class="demo-icon icon-trash-empty"></i> <span class="i-name">icon-trash-empty</span><span class="i-code">0xe80b</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf09a">
|
||||
<i class="demo-icon icon-facebook"></i> <span class="i-name">icon-facebook</span><span class="i-code">0xf09a</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf0ae">
|
||||
<i class="demo-icon icon-tasks"></i> <span class="i-name">icon-tasks</span><span class="i-code">0xf0ae</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf0c9">
|
||||
<i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf113">
|
||||
<i class="demo-icon icon-github"></i> <span class="i-name">icon-github</span><span class="i-code">0xf113</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf179">
|
||||
<i class="demo-icon icon-apple"></i> <span class="i-name">icon-apple</span><span class="i-code">0xf179</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf19d">
|
||||
<i class="demo-icon icon-graduation-cap"></i> <span class="i-name">icon-graduation-cap</span><span class="i-code">0xf19d</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf1a0">
|
||||
<i class="demo-icon icon-google"></i> <span class="i-name">icon-google</span><span class="i-code">0xf1a0</span>
|
||||
</div>
|
||||
<div class="span3" title="Code: 0xf1d8">
|
||||
<i class="demo-icon icon-paper-plane"></i> <span class="i-name">icon-paper-plane</span><span class="i-code">0xf1d8</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span3" title="Code: 0xf1da">
|
||||
<i class="demo-icon icon-history"></i> <span class="i-name">icon-history</span><span class="i-code">0xf1da</span>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
@@ -26,6 +26,10 @@
|
||||
|
||||
<glyph glyph-name="cancel" unicode="" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
|
||||
|
||||
<glyph glyph-name="plus" unicode="" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
||||
|
||||
<glyph glyph-name="trash-empty" unicode="" d="M286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15 6-5h464q2 0 6 5t8 15 4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q23 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
|
||||
|
||||
<glyph glyph-name="facebook" unicode="" d="M535 843v-147h-87q-48 0-65-20t-17-60v-106h164l-22-165h-142v-424h-171v424h-142v165h142v122q0 104 58 161t155 57q82 0 127-7z" horiz-adv-x="571.4" />
|
||||
|
||||
<glyph glyph-name="tasks" unicode="" d="M571 64h358v72h-358v-72z m-214 286h572v71h-572v-71z m357 286h215v71h-215v-71z m286-465v-142q0-15-11-25t-25-11h-928q-15 0-25 11t-11 25v142q0 15 11 26t25 10h928q15 0 25-10t11-26z m0 286v-143q0-14-11-25t-25-10h-928q-15 0-25 10t-11 25v143q0 15 11 25t25 11h928q15 0 25-11t11-25z m0 286v-143q0-14-11-25t-25-11h-928q-15 0-25 11t-11 25v143q0 14 11 25t25 11h928q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
||||
|
||||
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user