mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
init commit
This commit is contained in:
27
src/app.vue
Normal file
27
src/app.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLayout :data-theme="currentTheme">
|
||||
<NuxtPage />
|
||||
<NuxtSnackbar />
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { currentTheme } = storeToRefs(useUiStore());
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fade-enter-active {
|
||||
transition: all 1s ease-out;
|
||||
}
|
||||
|
||||
.fade-leave-active {
|
||||
transition: all 1s ease-out reverse;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
155
src/components/haex/browser/index.vue
Normal file
155
src/components/haex/browser/index.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="browser">
|
||||
<div class="browser-controls">
|
||||
<button
|
||||
@click="$emit('goBack', activeTabId)"
|
||||
:disabled="!activeTabId"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
<button
|
||||
@click="$emit('goForward', activeTabId)"
|
||||
:disabled="!activeTabId"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
<button @click="$emit('createTab')">+</button>
|
||||
|
||||
<HaexBrowserUrlBar
|
||||
:url="activeTab?.url || ''"
|
||||
:isLoading="activeTab?.isLoading || false"
|
||||
@submit="handleUrlSubmit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<HaexBrowserTabBar
|
||||
:tabs="tabs"
|
||||
:activeTabId="activeTabId"
|
||||
@closeTab="$emit('closeTab', $event)"
|
||||
@activateTab="$emit('activateTab', $event)"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="browser-content"
|
||||
ref="contentRef"
|
||||
>
|
||||
<!-- Die eigentlichen Webview-Inhalte werden von Tauri verwaltet -->
|
||||
<div
|
||||
v-if="!activeTabId"
|
||||
class="empty-state"
|
||||
>
|
||||
<p>
|
||||
Kein Tab geöffnet. Erstellen Sie einen neuen Tab mit dem + Button.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { Window } from '@tauri-apps/api/window';
|
||||
import { getCurrentWebview, Webview } from '@tauri-apps/api/webview';
|
||||
/* const appWindow = new Window('uniqueLabel');
|
||||
const webview = new Webview(appWindow, 'theUniqueLabel', {
|
||||
url: 'https://www.google.de',
|
||||
x: 0,
|
||||
y: 0,
|
||||
height: 1000,
|
||||
width: 1000,
|
||||
});
|
||||
|
||||
webview.once('tauri://created', function () {
|
||||
console.log('create new webview');
|
||||
}); */
|
||||
|
||||
interface Tab {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
isLoading: boolean;
|
||||
isActive: boolean;
|
||||
window_label: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
tabs: Tab[];
|
||||
activeTabId: string | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'createTab'): void;
|
||||
(e: 'closeTab', tabId: string): void;
|
||||
(e: 'navigate', tabId: string, url: string): void;
|
||||
(e: 'goBack', tabId: string | null): void;
|
||||
(e: 'goForward', tabId: string | null): void;
|
||||
(e: 'activateTab', tabId: string | null): void;
|
||||
}>();
|
||||
|
||||
const { initializeAsync, processNavigation, injectContentScripts } =
|
||||
useBrowserExtensionStore();
|
||||
const contentRef = ref<HTMLDivElement | null>(null);
|
||||
//const extensionManager = ref<ExtensionManager>(new ExtensionManager());
|
||||
|
||||
const activeTab = computed(() =>
|
||||
props.tabs?.find((tab) => tab.id === props.activeTabId)
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
// Initialisiere das Erweiterungssystem
|
||||
await initializeAsync();
|
||||
// Aktualisiere die Webview-Größe
|
||||
await updateWebviewBoundsAsync();
|
||||
//window.addEventListener('resize', updateWebviewBounds);
|
||||
});
|
||||
|
||||
// Wenn ein neuer Tab aktiviert wird, injiziere Content-Scripts
|
||||
/* watch(
|
||||
() => props.activeTabId,
|
||||
async (newTabId) => {
|
||||
if (newTabId && props.tabs.length > 0) {
|
||||
const activeTab = props.tabs.find((tab) => tab.id === newTabId);
|
||||
if (activeTab) {
|
||||
// Warte kurz, bis die Seite geladen ist
|
||||
setTimeout(() => {
|
||||
injectContentScripts(activeTab.window_label);
|
||||
}, 500);
|
||||
|
||||
// Aktualisiere die Webview-Größe
|
||||
updateWebviewBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
); */
|
||||
|
||||
const handleUrlSubmit = (url: string) => {
|
||||
if (props.activeTabId) {
|
||||
// Prüfe URL mit Erweiterungen vor der Navigation
|
||||
if (processNavigation(url)) {
|
||||
emit('navigate', props.activeTabId, url);
|
||||
} else {
|
||||
console.log('Navigation blockiert durch Erweiterung');
|
||||
// Hier könnten Sie eine Benachrichtigung anzeigen
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateWebviewBoundsAsync = async () => {
|
||||
if (!contentRef.value) return;
|
||||
|
||||
const rect = contentRef.value.getBoundingClientRect();
|
||||
const bounds = {
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
};
|
||||
|
||||
/* await invoke('update_window_bounds', {
|
||||
contentBounds: { x: bounds.x, y: bounds.y },
|
||||
contentSize: { width: bounds.width, height: bounds.height },
|
||||
}); */
|
||||
};
|
||||
</script>
|
||||
43
src/components/haex/browser/tabBar.vue
Normal file
43
src/components/haex/browser/tabBar.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="tab-bar">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
class="tab"
|
||||
:class="{ active: tab.id === activeTabId }"
|
||||
@click="$emit('activateTab', tab.id)"
|
||||
>
|
||||
<span class="tab-title">
|
||||
{{ tab.title || 'Neuer Tab' }}
|
||||
</span>
|
||||
<button
|
||||
class="tab-close"
|
||||
@click.stop="$emit('closeTab', tab.id)"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Tab {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
isLoading: boolean;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
tabs: Tab[];
|
||||
activeTabId: string | null;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'closeTab', tabId: string): void;
|
||||
(e: 'activateTab', tabId: string): void;
|
||||
}>();
|
||||
</script>
|
||||
57
src/components/haex/browser/urlBar.vue
Normal file
57
src/components/haex/browser/urlBar.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<form
|
||||
class="url-bar"
|
||||
@submit.prevent="handleSubmit"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
v-model="inputValue"
|
||||
placeholder="URL eingeben"
|
||||
/>
|
||||
<span
|
||||
v-if="isLoading"
|
||||
class="loading-indicator"
|
||||
>Laden...</span
|
||||
>
|
||||
<button
|
||||
v-else
|
||||
type="submit"
|
||||
>
|
||||
Go
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['submit']);
|
||||
|
||||
const inputValue = ref(props.url);
|
||||
|
||||
watch(
|
||||
() => props.url,
|
||||
(newUrl) => {
|
||||
inputValue.value = newUrl;
|
||||
}
|
||||
);
|
||||
|
||||
const handleSubmit = () => {
|
||||
// URL validieren und ggf. Protokoll hinzufügen
|
||||
let processedUrl = inputValue.value.trim();
|
||||
if (processedUrl && !processedUrl.match(/^[a-zA-Z]+:\/\//)) {
|
||||
processedUrl = 'https://' + processedUrl;
|
||||
}
|
||||
|
||||
emit('submit', processedUrl);
|
||||
};
|
||||
</script>
|
||||
79
src/components/ui/button/action.vue
Normal file
79
src/components/ui/button/action.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="fixed z-10">
|
||||
<div
|
||||
class="dropdown relative inline-flex [--placement:top] [--strategy:absolute]"
|
||||
>
|
||||
<button
|
||||
:id
|
||||
class="dropdown-toggle btn btn-primary btn-lg btn-square dropdown-open:rotate-45"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Menu"
|
||||
>
|
||||
<Icon
|
||||
:name="icon"
|
||||
size="46"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60 rtl:left-0 bg-transparent"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
:aria-labelledby="id"
|
||||
>
|
||||
<ul
|
||||
class="dropdown-open:ease-in dropdown-open:translate-x-0 -translate-x-1 rtl:translate-x-1 transition duration-300 ease-out"
|
||||
data-dropdown-transition
|
||||
>
|
||||
<li
|
||||
v-for="link in menu"
|
||||
class="dropdown-item hover:bg-transparent"
|
||||
>
|
||||
<NuxtLinkLocale
|
||||
v-if="link.to"
|
||||
:to="link.to"
|
||||
class="btn btn-primary flex items-center no-underline rounded-lg flex-nowrap"
|
||||
>
|
||||
<Icon
|
||||
v-if="link.icon"
|
||||
:name="link.icon"
|
||||
class="me-3"
|
||||
/>
|
||||
{{ link.label }}
|
||||
</NuxtLinkLocale>
|
||||
|
||||
<button
|
||||
v-else
|
||||
@click="link.action"
|
||||
class="link hover:link-primary flex items-center no-underline w-full"
|
||||
>
|
||||
<Icon
|
||||
v-if="link.icon"
|
||||
:name="link.icon"
|
||||
class="me-3"
|
||||
/>
|
||||
{{ link.label }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IActionMenuItem } from './types';
|
||||
|
||||
defineProps({
|
||||
menu: {
|
||||
type: Array as PropType<IActionMenuItem[]>,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'mdi:plus',
|
||||
},
|
||||
});
|
||||
|
||||
const id = useId();
|
||||
</script>
|
||||
25
src/components/ui/button/index.vue
Normal file
25
src/components/ui/button/index.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<button
|
||||
class="btn join-item"
|
||||
:class="{
|
||||
'btn-sm':
|
||||
currentScreenSize === 'sm' ||
|
||||
currentScreenSize === '' ||
|
||||
currentScreenSize === 'xs',
|
||||
}"
|
||||
:type
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { currentScreenSize } = storeToRefs(useUiStore());
|
||||
|
||||
defineProps({
|
||||
type: {
|
||||
type: String as PropType<'reset' | 'submit' | 'button'>,
|
||||
default: 'button',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
8
src/components/ui/button/types.d.ts
vendored
Normal file
8
src/components/ui/button/types.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
export interface IActionMenuItem {
|
||||
label: string;
|
||||
icon?: string;
|
||||
action?: () => Promise<unknown>;
|
||||
to?: RouteLocationRaw;
|
||||
}
|
||||
121
src/components/ui/dialog/index.vue
Normal file
121
src/components/ui/dialog/index.vue
Normal file
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<slot
|
||||
name="trigger"
|
||||
:id
|
||||
>
|
||||
</slot>
|
||||
|
||||
<div
|
||||
:id
|
||||
class="overlay modal overlay-open:opacity-100 hidden modal-middle [--tab-accessibility-limited:false] overflow-scroll p-0 sm:p-4"
|
||||
role="dialog"
|
||||
ref="modalRef"
|
||||
>
|
||||
<div
|
||||
class="overlay-animation-target overlay-open:mt-4 overlay-open:duration-500 mt-12 transition-all ease-out modal-dialog overlay-open:opacity-100"
|
||||
>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<slot name="title">
|
||||
<h3
|
||||
v-if="title"
|
||||
class="modal-title text-base sm:text-lg"
|
||||
>
|
||||
{{ title }}
|
||||
</h3>
|
||||
</slot>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
|
||||
:aria-label="t('close')"
|
||||
@click="open = false"
|
||||
tabindex="1"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:close"
|
||||
size="18"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-sm sm:text-base py-1">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="modal-footer flex-wrap">
|
||||
<slot name="buttons" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { HSOverlay } from 'flyonui/flyonui';
|
||||
|
||||
export interface IDom {
|
||||
class?: String;
|
||||
text: String;
|
||||
}
|
||||
|
||||
const id = useId();
|
||||
|
||||
defineProps({
|
||||
trigger: {
|
||||
type: Object as PropType<IDom>,
|
||||
default: () => ({
|
||||
class: '',
|
||||
text: '',
|
||||
}),
|
||||
},
|
||||
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
description: {
|
||||
type: Object as PropType<IDom>,
|
||||
default: () => ({
|
||||
class: '',
|
||||
text: '',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
|
||||
const open = defineModel<boolean>('open', { default: false });
|
||||
|
||||
const { t } = useI18n();
|
||||
const modalRef = useTemplateRef('modalRef');
|
||||
const modal = ref<HSOverlay>();
|
||||
|
||||
watch(open, async () => {
|
||||
if (open.value) {
|
||||
//console.log('open modal', modal.value?.open);
|
||||
await modal.value?.open();
|
||||
} else {
|
||||
await modal.value?.close(true);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (!modalRef.value) return;
|
||||
modal.value = new HSOverlay(modalRef.value, { isClosePrev: true });
|
||||
|
||||
modal.value.on('close', () => {
|
||||
console.log('close it from event', open.value);
|
||||
open.value = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"close": "Schließen"
|
||||
},
|
||||
"en": {
|
||||
"close": "Close"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
58
src/components/ui/dialog/test.vue
Normal file
58
src/components/ui/dialog/test.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded="false"
|
||||
aria-controls="basic-modal"
|
||||
data-overlay="#basic-modal"
|
||||
>
|
||||
Open modal
|
||||
</button>
|
||||
|
||||
<div
|
||||
id="basic-modal"
|
||||
class="overlay modal overlay-open:opacity-100 hidden"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="modal-dialog overlay-open:opacity-100">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Dialog Title</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
|
||||
aria-label="Close"
|
||||
data-overlay="#basic-modal"
|
||||
>
|
||||
<span class="icon-[tabler--x] size-4"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
This is some placeholder content to show the scrolling behavior for
|
||||
modals. Instead of repeating the text in the modal, we use an inline
|
||||
style to set a minimum height, thereby extending the length of the
|
||||
overall modal and demonstrating the overflow scrolling. When content
|
||||
becomes longer than the height of the viewport, scrolling will move
|
||||
the modal as needed.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-soft btn-secondary"
|
||||
data-overlay="#basic-modal"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
Save changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
5
src/components/ui/dialog/title.vue
Normal file
5
src/components/ui/dialog/title.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<h3 class="modal-title">
|
||||
<slot />
|
||||
</h3>
|
||||
</template>
|
||||
199
src/components/ui/input/index.vue
Normal file
199
src/components/ui/input/index.vue
Normal file
@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<span>
|
||||
<fieldset class="join w-full">
|
||||
<slot name="prepend" />
|
||||
|
||||
<span class="input-group join-item">
|
||||
<span
|
||||
v-if="prependIcon || prependLabel"
|
||||
class="input-group-text"
|
||||
>
|
||||
<label v-if="prependLabel">
|
||||
{{ prependLabel }}
|
||||
</label>
|
||||
<Icon :name="prependIcon" />
|
||||
</span>
|
||||
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
:id
|
||||
:name="name ?? id"
|
||||
:placeholder="placeholder || label"
|
||||
:type
|
||||
:autofocus
|
||||
class="input input-floating peer join-item"
|
||||
:class="{
|
||||
'input-sm':
|
||||
currentScreenSize === 'sm' ||
|
||||
currentScreenSize === '' ||
|
||||
currentScreenSize === 'xs',
|
||||
}"
|
||||
v-bind="$attrs"
|
||||
v-model="input"
|
||||
ref="inputRef"
|
||||
:readonly="read_only"
|
||||
/>
|
||||
|
||||
<label
|
||||
v-if="label"
|
||||
:for="id"
|
||||
class="input-floating-label"
|
||||
>
|
||||
{{ label }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="appendIcon || appendLabel"
|
||||
class="input-group-text"
|
||||
>
|
||||
<label
|
||||
v-if="appendLabel"
|
||||
class=""
|
||||
>
|
||||
{{ appendLabel }}
|
||||
</label>
|
||||
<Icon :name="appendIcon" />
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<slot name="append" />
|
||||
|
||||
<UiButton
|
||||
v-if="withCopyButton"
|
||||
class="btn-outline btn-accent h-auto"
|
||||
@click="copy(`${input}`)"
|
||||
>
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</UiButton>
|
||||
<!-- <button
|
||||
v-if="withCopyButton"
|
||||
class="btn btn-outline btn-accent join-item h-auto"
|
||||
:class="{
|
||||
'btn-sm':
|
||||
currentScreenSize === 'sm' ||
|
||||
currentScreenSize === '' ||
|
||||
currentScreenSize === 'xs',
|
||||
}"
|
||||
@click="copy(`${input}`)"
|
||||
type="button"
|
||||
>
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</button> -->
|
||||
</fieldset>
|
||||
|
||||
<span
|
||||
class="flex flex-col px-2 pt-0.5"
|
||||
v-show="errors"
|
||||
>
|
||||
<span
|
||||
v-for="error in errors"
|
||||
class="label-text-alt text-error"
|
||||
>
|
||||
{{ error }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type ZodSchema } from 'zod';
|
||||
|
||||
const inputRef = useTemplateRef('inputRef');
|
||||
defineExpose({ inputRef });
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<
|
||||
| 'button'
|
||||
| 'checkbox'
|
||||
| 'color'
|
||||
| 'date'
|
||||
| 'datetime-local'
|
||||
| 'email'
|
||||
| 'file'
|
||||
| 'hidden'
|
||||
| 'image'
|
||||
| 'month'
|
||||
| 'number'
|
||||
| 'password'
|
||||
| 'radio'
|
||||
| 'range'
|
||||
| 'reset'
|
||||
| 'search'
|
||||
| 'submit'
|
||||
| 'tel'
|
||||
| 'text'
|
||||
| 'time'
|
||||
| 'url'
|
||||
| 'week'
|
||||
>,
|
||||
default: 'text',
|
||||
},
|
||||
label: String,
|
||||
name: String,
|
||||
prependIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
prependLabel: String,
|
||||
appendIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
appendLabel: String,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
checkInput: Boolean,
|
||||
withCopyButton: Boolean,
|
||||
autofocus: Boolean,
|
||||
read_only: Boolean,
|
||||
});
|
||||
|
||||
const input = defineModel<string | number | undefined | null>({
|
||||
default: '',
|
||||
required: true,
|
||||
});
|
||||
|
||||
const { currentScreenSize } = storeToRefs(useUiStore());
|
||||
onMounted(() => {
|
||||
if (props.autofocus && inputRef.value) inputRef.value.focus();
|
||||
});
|
||||
|
||||
const errors = defineModel<string[] | undefined>('errors');
|
||||
|
||||
const id = useId();
|
||||
|
||||
watch(input, () => checkInput());
|
||||
|
||||
watch(
|
||||
() => props.checkInput,
|
||||
() => {
|
||||
checkInput();
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['error']);
|
||||
|
||||
const checkInput = () => {
|
||||
if (props.rules) {
|
||||
const result = props.rules.safeParse(input.value);
|
||||
//console.log('check result', result.error, props.rules);
|
||||
if (!result.success) {
|
||||
errors.value = result.error.errors.map((error) => error.message);
|
||||
emit('error', errors.value);
|
||||
} else {
|
||||
errors.value = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const { copy, copied } = useClipboard();
|
||||
</script>
|
||||
54
src/components/ui/input/password.vue
Normal file
54
src/components/ui/input/password.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<UiInput
|
||||
:check-input
|
||||
:label="label || t('password')"
|
||||
:placeholder="placeholder || t('password')"
|
||||
:rules
|
||||
:type="type"
|
||||
:autofocus
|
||||
v-model="value"
|
||||
>
|
||||
<template #append>
|
||||
<UiButton
|
||||
class="btn-outline btn-accent h-auto"
|
||||
@click="tooglePasswordType"
|
||||
>
|
||||
<Icon :name="type === 'password' ? 'mdi:eye' : 'mdi:eye-off'" />
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiInput>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ZodSchema } from 'zod';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { currentScreenSize } = storeToRefs(useUiStore());
|
||||
|
||||
const value = defineModel<string | number | null | undefined>();
|
||||
|
||||
defineProps({
|
||||
label: String,
|
||||
placeholder: String,
|
||||
checkInput: Boolean,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
autofocus: Boolean,
|
||||
});
|
||||
|
||||
const type = ref<'password' | 'text'>('password');
|
||||
|
||||
const tooglePasswordType = () => {
|
||||
type.value = type.value === 'password' ? 'text' : 'password';
|
||||
};
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"password": "Passwort"
|
||||
},
|
||||
"en": {
|
||||
"password": "Password"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
56
src/components/ui/input/url.vue
Normal file
56
src/components/ui/input/url.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<UiInput
|
||||
:autofocus
|
||||
:check-input="checkInput"
|
||||
:label="label || t('url')"
|
||||
:placeholder="placeholder || t('url')"
|
||||
:read_only
|
||||
:rules
|
||||
:with-copy-button
|
||||
v-model.trim="value"
|
||||
>
|
||||
<template #append>
|
||||
<UiButton
|
||||
v-if="read_only"
|
||||
@click="openUrl(`${value}`)"
|
||||
class="btn-outline btn-accent h-auto"
|
||||
:class="{
|
||||
disabled: !value?.length,
|
||||
}"
|
||||
>
|
||||
<Icon name="streamline:web" />
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiInput>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ZodSchema } from 'zod';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { currentScreenSize } = storeToRefs(useUiStore());
|
||||
|
||||
const value = defineModel<string | null | undefined>();
|
||||
|
||||
defineProps({
|
||||
label: String,
|
||||
placeholder: String,
|
||||
checkInput: Boolean,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
autofocus: Boolean,
|
||||
withCopyButton: Boolean,
|
||||
read_only: Boolean,
|
||||
});
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"url": "Url"
|
||||
},
|
||||
"en": {
|
||||
"url": "Url"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
91
src/components/ui/logo/itemis.vue
Normal file
91
src/components/ui/logo/itemis.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<svg
|
||||
id="logo"
|
||||
class="fill-current stroke-current w-[160px]"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 286.3 85"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<switch>
|
||||
<g>
|
||||
<g class="logo-imagesss">
|
||||
<circle
|
||||
fill="white"
|
||||
cx="42.5"
|
||||
cy="42.5"
|
||||
r="40"
|
||||
></circle>
|
||||
<path
|
||||
d="M42.3,83.4c-22.6,0-40.9-18.4-40.9-40.9c0-22.6,18.4-40.9,40.9-40.9c22.6,0,40.9,18.4,40.9,40.9
|
||||
C83.3,65.1,64.9,83.4,42.3,83.4z M42.3,5.8C22.1,5.8,5.7,22.3,5.7,42.5s16.5,36.7,36.7,36.7S79,62.7,79,42.5S62.6,5.8,42.3,5.8z
|
||||
"
|
||||
></path>
|
||||
<g>
|
||||
<g>
|
||||
<polygon
|
||||
points="38.8,69.8 38.8,31.7 22.3,31.7 22.3,38.5 29.8,38.5 29.8,69.8 "
|
||||
></polygon>
|
||||
<path
|
||||
d="M34.1,13.2c-3.3,0-6,2.6-6,5.9c0,3.3,2.6,6,5.9,6c3.3,0,6-2.6,6-6
|
||||
C39.9,15.9,37.3,13.2,34.1,13.2z"
|
||||
></path>
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="45.9,69.8 45.9,31.7 62.4,31.7 62.4,38.5 54.9,38.5 54.9,69.8 "
|
||||
></polygon>
|
||||
<path
|
||||
d="M50.6,13.2c3.3,0,6,2.6,6,5.9c0,3.3-2.6,6-5.9,6c-3.3,0-6-2.6-6-6
|
||||
C44.8,15.9,47.4,13.2,50.6,13.2z"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g class="logo-textsss">
|
||||
<path
|
||||
d="M136.1,63.6c-4,0-5.3-2.6-5.3-6V38.5h10.6v-6.7h-10.6v-6.7h-9c0,7,0,29.1,0,32.7
|
||||
c0,4.2,1.6,7.5,3.8,9.7c2.3,2.2,5.6,3.3,9.8,3.3c5.1,0,8.4-1.8,10.6-4.2l-4.7-6C140.2,62.1,138.5,63.6,136.1,63.6z"
|
||||
></path>
|
||||
<path
|
||||
d="M217.7,30.7c-4.9,0-8.2,1.6-10.4,3.8c-2.2-2.2-5.5-3.8-10.4-3.8c-15,0-14.9,12.1-14.9,15
|
||||
s0,24.1,0,24.1h9V45.7c0-8.5,4.9-8.3,5.9-8.3c1,0,5.9-0.3,5.9,8.3v24.1h0h9h0V45.7c0-8.5,4.9-8.3,5.9-8.3c1,0,5.9-0.3,5.9,8.3
|
||||
v24.1h9c0,0,0-21.2,0-24.1C232.6,42.8,232.7,30.7,217.7,30.7z"
|
||||
></path>
|
||||
<path
|
||||
d="M273.2,46.4c-4.3-1.4-6-2.5-6-5.2c0-2,1.1-3.8,4.3-3.8c3.2,0,4.5,3.3,5.1,4.8
|
||||
c2.7-1.5,5.3-2.9,6.6-3.6c-2.5-6-6.3-7.9-12-7.9c-8,0-11.7,5.5-11.7,10.6c0,6.5,2.9,9.8,11.2,12.2c6,1.8,6.5,4.7,6.2,6.2
|
||||
c-0.3,1.7-1.6,3.6-5.3,3.6c-3.6,0-5.8-3.8-6.8-5.4c-1.8,1.1-3.4,2.1-6.4,3.8c2.1,5,6.8,9.1,13.5,9.1c7.9,0,12.9-5.1,12.9-12.1
|
||||
C284.9,51,279.6,48.5,273.2,46.4z"
|
||||
></path>
|
||||
<g>
|
||||
<polygon
|
||||
points="239.7,69.8 239.7,31.7 256.2,31.7 256.2,38.5 248.7,38.5 248.7,69.8 "
|
||||
></polygon>
|
||||
<path
|
||||
d="M244.4,13.2c3.3,0,6,2.6,6,5.9c0,3.3-2.6,6-5.9,6c-3.3,0-6-2.6-6-6
|
||||
C238.6,15.9,241.2,13.2,244.4,13.2z"
|
||||
></path>
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="114.7,69.8 114.7,31.7 98.1,31.7 98.1,38.5 105.7,38.5 105.7,69.8 "
|
||||
></polygon>
|
||||
<path
|
||||
d="M110,13.2c-3.3,0-6,2.6-6,5.9c0,3.3,2.6,6,5.9,6c3.3,0,6-2.6,6-6C115.8,15.9,113.2,13.2,110,13.2
|
||||
z"
|
||||
></path>
|
||||
</g>
|
||||
<path
|
||||
d="M176.4,52.4v-3.7c0-12.3-4.7-18-14.8-18c-9.3,0-14.7,6.6-14.7,18v4c0,11.5,5.8,18.2,15.8,18.2
|
||||
c6.6,0,10.8-3.7,12.7-7.9c-2.2-1.4-4.6-2.8-6.1-3.8c-1,1.7-2.9,4.4-6.7,4.4c-5.8,0-7-5.9-7-10.9v-0.2H176.4z M155.7,45.7
|
||||
c0.2-7.1,3.3-8.2,6-8.2c2.6,0,5.9,1,6,8.2H155.7z"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</switch>
|
||||
</svg>
|
||||
</template>
|
||||
24
src/components/ui/sidebar/button.vue
Normal file
24
src/components/ui/sidebar/button.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<UiTooltip
|
||||
:tooltip="tooltip ?? label"
|
||||
direction="right-end"
|
||||
>
|
||||
<button
|
||||
class="link flex items-center justify-center py-3 hover:text-primary tooltip-toogle bg w-full"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<Icon
|
||||
:name="icon"
|
||||
class="size-8"
|
||||
/>
|
||||
</button>
|
||||
</UiTooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
label: string;
|
||||
tooltip?: string;
|
||||
icon: string;
|
||||
}>();
|
||||
</script>
|
||||
109
src/components/ui/sidebar/index.vue
Normal file
109
src/components/ui/sidebar/index.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<aside
|
||||
class="flex shrink-0 transition-[width] ease-in duration-300 z-30 h-full overflow-hidden fixed sm:relative left-0 shadow border-r border-base-300"
|
||||
>
|
||||
<div class="sm:flex flex-col w-14 bg-base-200 shrink-0 h-full hidden">
|
||||
<img
|
||||
src="/logo.svg"
|
||||
class="bg-primary p-3 size-16"
|
||||
/>
|
||||
|
||||
<div class="flex flex-col justify-between h-full overflow-y-scroll z-10">
|
||||
<div class="flex flex-col space-y-2 text-base-content/90">
|
||||
<template v-for="item in menu.top">
|
||||
<UiSidebarLink
|
||||
v-if="item.to"
|
||||
:to="item.to ?? ''"
|
||||
:icon="item.icon"
|
||||
:label="$t(item.label)"
|
||||
/>
|
||||
|
||||
<UiSidebarButton
|
||||
v-else
|
||||
:icon="item.icon"
|
||||
:label="$t(item.label)"
|
||||
@click="item.click"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-2 text-base-content/90">
|
||||
<template v-for="item in menu.bottom">
|
||||
<UiSidebarLink
|
||||
v-if="item.to"
|
||||
:to="item.to ?? ''"
|
||||
:icon="item.icon"
|
||||
:label="$t(item.label)"
|
||||
/>
|
||||
|
||||
<UiSidebarButton
|
||||
v-else
|
||||
:icon="item.icon"
|
||||
:label="$t(item.label)"
|
||||
@click="item.click"
|
||||
/>
|
||||
</template>
|
||||
<!-- <UiSidebarLink
|
||||
v-for="item in menu.bottom"
|
||||
:icon="item.icon"
|
||||
:to="item.to ?? ''"
|
||||
:label="item.label"
|
||||
@click="item.click"
|
||||
/> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="bg-base-100 flex flex-col w-full overflow-clip">
|
||||
<div
|
||||
class="h-16 flex items-center sm:justify-center justify-end md:justify-start bg-base-300 shrink-0"
|
||||
>
|
||||
<button
|
||||
class="top-3 left-2 absolute z-30 duration-1000 btn btn-square btn-primary transition-opacity btn-outline sm:hidden"
|
||||
@click="show = !show"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:menu"
|
||||
size="28"
|
||||
/>
|
||||
</button>
|
||||
<span
|
||||
class="px-4 font-semibold text-base-content shrink-0 sm:bg-transparent bg-primary h-full flex items-center rounded-l-lg"
|
||||
>
|
||||
<p>Haex Vault</p>
|
||||
</span>
|
||||
|
||||
<img
|
||||
src="/logo.svg"
|
||||
class="bg-primary p-3 size-16 shrink-0 sm:hidden rounded-r-lg"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="btn btn-square btn-primary btn-outline mr-2 ml-auto hidden sm:flex"
|
||||
@click="show = false"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:close"
|
||||
size="28"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="overflow-scroll flex pb-4 relative">
|
||||
<slot />
|
||||
</div>
|
||||
</div> -->
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
menu: {
|
||||
type: Object as PropType<ISidebarMenu>,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
//const show = ref(true);
|
||||
const { show } = storeToRefs(useSidebarStore());
|
||||
</script>
|
||||
61
src/components/ui/sidebar/link.vue
Normal file
61
src/components/ui/sidebar/link.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<li
|
||||
@click="triggerNavigate"
|
||||
class="hover:text-primary"
|
||||
>
|
||||
<UiTooltip
|
||||
:tooltip="tooltip ?? name"
|
||||
direction="right-end"
|
||||
>
|
||||
<NuxtLinkLocale
|
||||
:class="{ ['bg-base-300']: isActive }"
|
||||
:to="{
|
||||
name: type === 'browser' ? 'haexBrowser' : 'haexExtension',
|
||||
params: type === 'browser' ? {} : { extensionId: id },
|
||||
}"
|
||||
class="flex items-center justify-center cursor-pointer tooltip-toogle"
|
||||
ref="link"
|
||||
>
|
||||
<Icon
|
||||
:name="icon"
|
||||
class="shrink-0 size-6"
|
||||
/>
|
||||
</NuxtLinkLocale>
|
||||
</UiTooltip>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type ISidebarItem } from '#imports';
|
||||
|
||||
const props = defineProps<ISidebarItem>();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const isActive = computed(() => {
|
||||
if (props.type === 'browser') {
|
||||
return router.currentRoute.value.name === 'haexBrowser';
|
||||
} else if (props.type === 'extension') {
|
||||
return (
|
||||
router.currentRoute.value.name === 'haexExtension' &&
|
||||
getSingleRouteParam(router.currentRoute.value.params.extensionId) ===
|
||||
props.id
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const link = useTemplateRef('link');
|
||||
|
||||
const triggerNavigate = () => link.value?.$el.click();
|
||||
|
||||
/* computed(() => {
|
||||
const found = useRouter()
|
||||
.getRoutes()
|
||||
.find((route) => route.name === useLocaleRoute()(props.to)?.name);
|
||||
console.log('found route', found, useRoute());
|
||||
return (
|
||||
found?.name === useRoute().name ||
|
||||
found?.children.some((child) => child.name === useRoute().name)
|
||||
);
|
||||
}); */
|
||||
</script>
|
||||
268
src/components/ui/sidebar/test.vue
Normal file
268
src/components/ui/sidebar/test.vue
Normal file
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<nav
|
||||
class="navbar bg-base-100 max-sm:rounded-box max-sm:shadow sm:border-b border-base-content/25 sm:z-[1] relative"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-text max-sm:btn-square sm:hidden me-2"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded="false"
|
||||
aria-controls="sidebar"
|
||||
data-overlay="#sidebar"
|
||||
>
|
||||
<span class="icon-[tabler--menu-2] size-5"></span>
|
||||
</button>
|
||||
<div class="flex flex-1 items-center">
|
||||
<a
|
||||
class="link text-base-content link-neutral text-xl font-semibold no-underline"
|
||||
href="#"
|
||||
>
|
||||
<UiTextGradient>Haex Hub</UiTextGradient>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-end flex items-center gap-4">
|
||||
<div
|
||||
class="dropdown relative inline-flex [--auto-close:inside] [--offset:8] [--placement:bottom-end]"
|
||||
>
|
||||
<button
|
||||
id="dropdown-scrollable"
|
||||
type="button"
|
||||
class="dropdown-toggle btn btn-text btn-circle dropdown-open:bg-base-content/10 size-10"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Dropdown"
|
||||
>
|
||||
<div class="indicator">
|
||||
<span
|
||||
v-show="notifications.length"
|
||||
class="indicator-item bg-error size-2 rounded-full text-sm"
|
||||
></span>
|
||||
<span
|
||||
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
|
||||
></span>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="dropdown-scrollable"
|
||||
>
|
||||
<div class="dropdown-header justify-center">
|
||||
<h6 class="text-base-content text-base">
|
||||
{{ t('notifications.label') }}
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto max-md:max-w-60"
|
||||
>
|
||||
<div
|
||||
class="dropdown-item"
|
||||
v-for="notification in notifications"
|
||||
>
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img
|
||||
v-if="notification.image"
|
||||
:src="notification.image"
|
||||
:alt="notification.alt ?? 'notification avatar'"
|
||||
/>
|
||||
<Icon
|
||||
v-else-if="notification.icon"
|
||||
:name="notification.icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-60">
|
||||
<h6 class="truncate text-base">
|
||||
{{ notification.title }}
|
||||
</h6>
|
||||
<small class="text-base-content/50 truncate">
|
||||
{{ notification.description }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href="#"
|
||||
class="dropdown-footer justify-center gap-1"
|
||||
>
|
||||
<span class="icon-[tabler--eye] size-4"></span>
|
||||
{{ t('notifications.view_all') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="dropdown relative inline-flex [--auto-close:inside] [--offset:8] [--placement:bottom-end]"
|
||||
>
|
||||
<button
|
||||
id="dropdown-scrollable"
|
||||
type="button"
|
||||
class="dropdown-toggle flex items-center"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Dropdown"
|
||||
>
|
||||
<div class="avatar">
|
||||
<div class="size-9.5 rounded-full">
|
||||
<img
|
||||
src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png"
|
||||
alt="avatar 1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="dropdown-avatar"
|
||||
>
|
||||
<li class="dropdown-header gap-2">
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img
|
||||
src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png"
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="text-base-content text-base font-semibold">
|
||||
John Doe
|
||||
</h6>
|
||||
<small class="text-base-content/50">Admin</small>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--user]"></span>
|
||||
My Profile
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--settings]"></span>
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--receipt-rupee]"></span>
|
||||
Billing
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--help-triangle]"></span>
|
||||
FAQs
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown-footer gap-2">
|
||||
<a
|
||||
class="btn btn-error btn-soft btn-block"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--logout]"></span>
|
||||
Sign out
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<aside
|
||||
id="sidebar"
|
||||
class="overlay sm:shadow-none overlay-open:translate-x-0 drawer drawer-start hidden max-w-64 sm:absolute sm:z-0 sm:flex sm:translate-x-0 pt-16"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="drawer-body px-2 pt-4">
|
||||
<ul class="menu p-0">
|
||||
|
||||
|
||||
|
||||
<li v-for="item in menu">
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--home] size-5"></span>
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--user] size-5"></span>
|
||||
Account
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--message] size-5"></span>
|
||||
Notifications
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--mail] size-5"></span>
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--calendar] size-5"></span>
|
||||
Calendar
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--shopping-bag] size-5"></span>
|
||||
Product
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--login] size-5"></span>
|
||||
Sign In
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--logout-2] size-5"></span>
|
||||
Sign Out
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n();
|
||||
|
||||
const { notifications } = storeToRefs(useNotificationStore());
|
||||
|
||||
const { menu } = storeToRefs(useSidebarStore());
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
notifications:
|
||||
label: Benachrichtigungen
|
||||
view_all: Alle ansehen
|
||||
en:
|
||||
notifications:
|
||||
label: Notifications
|
||||
view_all: View all
|
||||
</i18n>
|
||||
7
src/components/ui/text/gradient.vue
Normal file
7
src/components/ui/text/gradient.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<p
|
||||
class="bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent font-black"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
61
src/components/ui/tooltip/index.vue
Normal file
61
src/components/ui/tooltip/index.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="tooltip [--prevent-popper:false]">
|
||||
<div
|
||||
class="tooltip-toggle"
|
||||
aria-label="Tooltip"
|
||||
>
|
||||
<slot>
|
||||
<button class="btn btn-square">
|
||||
<Icon name="mdi:chevron-up-box-outline" />
|
||||
</button>
|
||||
</slot>
|
||||
|
||||
<span
|
||||
class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible z-40"
|
||||
role="tooltip"
|
||||
>
|
||||
<span
|
||||
class="tooltip-body"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
{{ tooltip }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
direction: {
|
||||
type: String as PropType<
|
||||
| 'top'
|
||||
| 'top-start'
|
||||
| 'top-end'
|
||||
| 'bottom'
|
||||
| 'bottom-start'
|
||||
| 'bottom-end'
|
||||
| 'right'
|
||||
| 'right-start'
|
||||
| 'right-end'
|
||||
| 'left'
|
||||
| 'left-start'
|
||||
| 'left-end'
|
||||
>,
|
||||
default: 'top',
|
||||
},
|
||||
|
||||
tooltip: String,
|
||||
|
||||
trigger: {
|
||||
type: String as PropType<'focus' | 'hover' | 'click'>,
|
||||
default: 'hover',
|
||||
},
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
</script>
|
||||
165
src/components/vault/button/create.vue
Normal file
165
src/components/vault/button/create.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<UiDialog
|
||||
:title="t('title')"
|
||||
v-model:open="open"
|
||||
>
|
||||
<template #trigger="{ id }">
|
||||
<button
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1 whitespace-nowrap flex-nowrap"
|
||||
@click="open = true"
|
||||
>
|
||||
<Icon name="mdi:plus" />
|
||||
{{ t('database.create') }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<form
|
||||
class="flex flex-col gap-4"
|
||||
@submit="onCreateAsync"
|
||||
>
|
||||
<!-- @keyup.enter="onCreateAsync" -->
|
||||
<UiInput
|
||||
:check-input="check"
|
||||
:label="t('database.label')"
|
||||
:placeholder="t('database.placeholder')"
|
||||
:rules="vaultDatabaseSchema.name"
|
||||
autofocus
|
||||
prepend-icon="mdi:safe"
|
||||
v-model="database.name"
|
||||
/>
|
||||
|
||||
<UiInputPassword
|
||||
:check-input="check"
|
||||
:rules="vaultDatabaseSchema.password"
|
||||
prepend-icon="mdi:key-outline"
|
||||
v-model="database.password"
|
||||
/>
|
||||
</form>
|
||||
|
||||
<template #buttons>
|
||||
<UiButton
|
||||
class="btn-error"
|
||||
@click="onClose"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
@click="onCreateAsync"
|
||||
>
|
||||
{{ t('create') }}
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import { useVaultStore } from '~/stores/vault';
|
||||
import { vaultDatabaseSchema } from './schema';
|
||||
|
||||
const check = ref(false);
|
||||
const open = ref();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const database = reactive<{
|
||||
name: string;
|
||||
password: string;
|
||||
path: string | null;
|
||||
type: 'password' | 'text';
|
||||
}>({
|
||||
name: '',
|
||||
password: '',
|
||||
path: '',
|
||||
type: 'password',
|
||||
});
|
||||
|
||||
const initDatabase = () => {
|
||||
database.name = t('database.name');
|
||||
database.password = '';
|
||||
database.path = '';
|
||||
database.type = 'password';
|
||||
};
|
||||
|
||||
initDatabase();
|
||||
|
||||
const { add } = useSnackbar();
|
||||
const { createAsync } = useVaultStore();
|
||||
//const { show } = storeToRefs(useSidebarStore());
|
||||
|
||||
const onCreateAsync = async () => {
|
||||
check.value = true;
|
||||
|
||||
const nameCheck = vaultDatabaseSchema.name.safeParse(database.name);
|
||||
const passwordCheck = vaultDatabaseSchema.password.safeParse(
|
||||
database.password
|
||||
);
|
||||
|
||||
console.log(
|
||||
'checks',
|
||||
database.name,
|
||||
nameCheck,
|
||||
database.password,
|
||||
passwordCheck
|
||||
);
|
||||
if (!nameCheck.success || !passwordCheck.success) return;
|
||||
|
||||
open.value = false;
|
||||
try {
|
||||
database.path = await save({ defaultPath: `${database.name}.db` });
|
||||
|
||||
console.log('data', database);
|
||||
if (database.path && database.password) {
|
||||
const vaultId = await createAsync({
|
||||
path: database.path,
|
||||
password: database.password,
|
||||
});
|
||||
//show.value = true;
|
||||
|
||||
await navigateTo(
|
||||
useLocaleRoute()({ name: 'vault', params: { vaultId } })
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
add({ type: 'error', text: JSON.stringify(error) });
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
open.value = false;
|
||||
initDatabase();
|
||||
};
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"database": {
|
||||
"label": "Datenbankname",
|
||||
"placeholder": "Passwörter",
|
||||
"create": "Neue Vault anlegen",
|
||||
"name": "Passwörter"
|
||||
},
|
||||
"title": "Neue Datenbank anlegen",
|
||||
"create": "Erstellen",
|
||||
"abort": "Abbrechen",
|
||||
"description": "Haex Vault für deine geheimsten Geheimnisse"
|
||||
},
|
||||
|
||||
"en": {
|
||||
"database": {
|
||||
"label": "Databasename",
|
||||
"placeholder": "Databasename",
|
||||
"create": "Create new Vault",
|
||||
"name": "Passwords"
|
||||
},
|
||||
"title": "Create New Database",
|
||||
"create": "Create",
|
||||
"abort": "Abort",
|
||||
"description": "Haex Vault for your most secret secrets"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
179
src/components/vault/button/open.vue
Normal file
179
src/components/vault/button/open.vue
Normal file
@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<UiDialog v-model:open="isOpen">
|
||||
<!-- @close="initDatabase" -->
|
||||
<template #trigger>
|
||||
<button
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1"
|
||||
@click="onLoadDatabase"
|
||||
>
|
||||
<Icon name="mdi:folder-open-outline" />
|
||||
{{ t('database.open') }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<UiInputPassword
|
||||
:check-input="check"
|
||||
:rules="vaultDatabaseSchema.password"
|
||||
@keyup.enter="onOpenDatabase"
|
||||
autofocus
|
||||
prepend-icon="mdi:key-outline"
|
||||
v-model="database.password"
|
||||
/>
|
||||
|
||||
<template #buttons>
|
||||
<UiButton
|
||||
class="btn-error"
|
||||
@click="onClose"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
type="submit"
|
||||
class="btn-primary"
|
||||
@click="onOpenDatabase"
|
||||
>
|
||||
{{ t('open') }}
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import { vaultDatabaseSchema } from './schema';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const isOpen = defineModel('isOpen', { type: Boolean });
|
||||
|
||||
const props = defineProps({
|
||||
path: String,
|
||||
});
|
||||
|
||||
const check = ref(false);
|
||||
|
||||
const database = reactive<{
|
||||
name: string;
|
||||
password: string;
|
||||
path: string | null;
|
||||
type: 'password' | 'text';
|
||||
}>({
|
||||
name: '',
|
||||
password: '',
|
||||
path: '',
|
||||
type: 'password',
|
||||
});
|
||||
|
||||
const initDatabase = () => {
|
||||
database.name = '';
|
||||
database.password = '';
|
||||
database.path = '';
|
||||
database.type = 'password';
|
||||
};
|
||||
|
||||
initDatabase();
|
||||
|
||||
const { add } = useSnackbar();
|
||||
|
||||
const handleError = (error: unknown) => {
|
||||
isOpen.value = false;
|
||||
add({ type: 'error', text: JSON.stringify(error) });
|
||||
//console.error(error);
|
||||
};
|
||||
|
||||
const { openAsync } = useVaultStore();
|
||||
//const { show } = storeToRefs(useSidebarStore());
|
||||
|
||||
const onLoadDatabase = async () => {
|
||||
try {
|
||||
database.path = await open({
|
||||
multiple: false,
|
||||
directory: false,
|
||||
filters: [
|
||||
{
|
||||
name: 'HaexVault',
|
||||
extensions: ['db'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!database.path) return;
|
||||
|
||||
isOpen.value = true;
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const localePath = useLocalePath();
|
||||
const onOpenDatabase = async () => {
|
||||
try {
|
||||
check.value = true;
|
||||
const path = database.path || props.path;
|
||||
const pathCheck = vaultDatabaseSchema.path.safeParse(path);
|
||||
const passwordCheck = vaultDatabaseSchema.password.safeParse(
|
||||
database.password
|
||||
);
|
||||
|
||||
if (!pathCheck.success || !passwordCheck.success || !path) {
|
||||
add({ type: 'error', text: 'params falsch' });
|
||||
return;
|
||||
}
|
||||
|
||||
//console.log('try to open', path);
|
||||
|
||||
const vaultId = await openAsync({
|
||||
path,
|
||||
password: database.password,
|
||||
});
|
||||
|
||||
if (!vaultId) {
|
||||
add({ type: 'error', text: 'Vault konnte nicht geöffnet werden' });
|
||||
return;
|
||||
}
|
||||
|
||||
onClose();
|
||||
|
||||
/* await navigateTo(
|
||||
localePath({
|
||||
name: 'vaultGroup',
|
||||
params: {
|
||||
vaultId,
|
||||
},
|
||||
query: {
|
||||
showSidebar: 'true',
|
||||
},
|
||||
})
|
||||
); */
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
handleError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
initDatabase();
|
||||
isOpen.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"open": "Öffnen",
|
||||
"abort": "Abbrechen",
|
||||
"database": {
|
||||
"open": "Vault öffnen"
|
||||
}
|
||||
},
|
||||
|
||||
"en": {
|
||||
"open": "Open",
|
||||
"abort": "Abort",
|
||||
"database": {
|
||||
"open": "Open Vault"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
7
src/components/vault/button/schema.ts
Normal file
7
src/components/vault/button/schema.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const vaultDatabaseSchema = {
|
||||
password: z.string().min(6).max(255),
|
||||
name: z.string().min(1).max(255),
|
||||
path: z.string().min(4).endsWith('.db'),
|
||||
};
|
||||
250
src/components/vault/card/edit.vue
Normal file
250
src/components/vault/card/edit.vue
Normal file
@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<VaultCard
|
||||
@close="onClose"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex flex-wrap items-center justify-between w-full px-2 py-3">
|
||||
<div class="w-full flex gap-2 justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="btn btn-square btn-primary btn-outline"
|
||||
@click="onBack"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:chevron-left"
|
||||
size="32"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn btn-square btn-error btn-outline"
|
||||
@click="onBack"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:trash-can-outline"
|
||||
size="28"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<slot name="buttons">
|
||||
<div
|
||||
v-if="read_only"
|
||||
class="h-full"
|
||||
>
|
||||
<button
|
||||
class="btn btn-square btn-primary btn-outline"
|
||||
@click="read_only = false"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:pencil-outline"
|
||||
size="24"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="gap-2 h-full hidden md:flex"
|
||||
>
|
||||
<button
|
||||
class="btn btn-square btn-error btn-outline"
|
||||
@click="onClose"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
<span class="hidden"> {{ t('abort') }} </span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-square btn-success btn-outline"
|
||||
@click="onSubmit"
|
||||
>
|
||||
<Icon name="mdi:check" />
|
||||
<span class="hidden"> {{ t('create') }} </span>
|
||||
</button>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center w-full min-h-14 gap-2 py-1"
|
||||
:class="{ '-ml-6': !show }"
|
||||
:style="{ color }"
|
||||
>
|
||||
<Icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
size="28"
|
||||
/>
|
||||
|
||||
<h5
|
||||
v-show="read_only"
|
||||
class="overflow-hidden whitespace-nowrap"
|
||||
>
|
||||
{{ title }}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="h-full">
|
||||
<slot />
|
||||
<div
|
||||
v-show="!read_only"
|
||||
class="fixed bottom-2 left-0 w-full flex items-center justify-between px-4 md:hidden"
|
||||
>
|
||||
<div
|
||||
class="transition-all duration-500"
|
||||
:class="{ 'pl-96': show }"
|
||||
>
|
||||
<button
|
||||
class="btn btn-square btn-error btn-outline"
|
||||
@click="onClose"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
<span class="hidden"> {{ t('abort') }} </span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-square btn-success"
|
||||
@click="onSubmit"
|
||||
>
|
||||
<Icon name="mdi:check" />
|
||||
<span class="hidden"> {{ t('create') }} </span>
|
||||
</button>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- <UiButtonAction
|
||||
class=""
|
||||
icon="mdi:content-save-outline"
|
||||
><Icon name="mdi:content-save-outline" />
|
||||
</UiButtonAction> -->
|
||||
</div>
|
||||
</VaultCard>
|
||||
<VaultModalSaveChanges
|
||||
v-model="showConfirmation"
|
||||
@reject="onReject"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { show } = storeToRefs(useSidebarStore());
|
||||
|
||||
const read_only = defineModel<boolean>('read_only', { default: false });
|
||||
|
||||
const props = defineProps({
|
||||
color: String,
|
||||
hasChanges: Boolean,
|
||||
icon: String,
|
||||
title: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
back: [void];
|
||||
close: [void];
|
||||
reject: [to?: RouteLocationNormalizedLoadedGeneric];
|
||||
submit: [to?: RouteLocationNormalizedLoadedGeneric];
|
||||
}>();
|
||||
|
||||
const showConfirmation = ref(false);
|
||||
|
||||
const to = ref<RouteLocationNormalizedLoadedGeneric>();
|
||||
|
||||
const isApprovedForLeave = ref(false);
|
||||
|
||||
const wantToGoBack = ref(false);
|
||||
|
||||
const onSubmit = () => {
|
||||
showConfirmation.value = false;
|
||||
isApprovedForLeave.value = true;
|
||||
if (wantToGoBack.value) {
|
||||
wantToGoBack.value = false;
|
||||
read_only.value = true;
|
||||
emit('submit');
|
||||
} else {
|
||||
emit('submit', to.value);
|
||||
}
|
||||
};
|
||||
|
||||
const onReject = () => {
|
||||
showConfirmation.value = false;
|
||||
isApprovedForLeave.value = true;
|
||||
read_only.value = true;
|
||||
|
||||
if (wantToGoBack.value) {
|
||||
wantToGoBack.value = false;
|
||||
emit('back');
|
||||
} else emit('reject', to.value);
|
||||
};
|
||||
|
||||
const onBack = () => {
|
||||
if (props.hasChanges) {
|
||||
wantToGoBack.value = true;
|
||||
showConfirmation.value = true;
|
||||
} else {
|
||||
emit('back');
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
if (props.hasChanges) {
|
||||
showConfirmation.value = true;
|
||||
} else {
|
||||
emit('close'); //read_only.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = () => {};
|
||||
onBeforeRouteLeave((_to, _from, next) => {
|
||||
//console.log('check before leave', _to, _from);
|
||||
to.value = _to;
|
||||
if (isApprovedForLeave.value) {
|
||||
isApprovedForLeave.value = false;
|
||||
next();
|
||||
} else if (props.hasChanges) {
|
||||
showConfirmation.value = true;
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"create": "Anlegen",
|
||||
"abort": "Abbrechen",
|
||||
"entry": {
|
||||
"title": "Titel",
|
||||
"username": "Nutzername",
|
||||
"password": "Passwort",
|
||||
"url": "Url"
|
||||
},
|
||||
"tab": {
|
||||
"details": "Details",
|
||||
"keyValue": "Extra",
|
||||
"history": "Verlauf"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"create": "Create",
|
||||
"abort": "Abort",
|
||||
"entry": {
|
||||
"title": "Title",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"url": "Url"
|
||||
},
|
||||
"tab": {
|
||||
"details": "Details",
|
||||
"keyValue": "Extra",
|
||||
"history": "History"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
42
src/components/vault/card/index.vue
Normal file
42
src/components/vault/card/index.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-base-100 w-full mx-auto shadow h-full overflow-hidden pt-[7.5rem]"
|
||||
>
|
||||
<div
|
||||
class="fixed top-0 right-0 z-10 transition-all duration-700 w-full font-semibold text-lg h-[7.5rem]"
|
||||
:class="{ 'pl-96': show }"
|
||||
>
|
||||
<div
|
||||
class="justify-center items-center flex flex-wrap border-b rounded-b border-secondary h-full"
|
||||
:class="{ 'pl-12': !show }"
|
||||
>
|
||||
<slot name="header" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-full overflow-scroll bg-base-200">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { show } = storeToRefs(useSidebarStore());
|
||||
const emit = defineEmits(['close', 'submit']);
|
||||
|
||||
const { escape, enter } = useMagicKeys();
|
||||
|
||||
watchEffect(async () => {
|
||||
if (escape.value) {
|
||||
await nextTick();
|
||||
emit('close');
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(async () => {
|
||||
if (enter.value) {
|
||||
await nextTick();
|
||||
emit('submit');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
32
src/components/vault/dialog/item/create.vue
Normal file
32
src/components/vault/dialog/item/create.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<UiDialog :title="t('title')">
|
||||
<form class="flex flex-col">
|
||||
<UiInput v-model="vaultItem.title" />
|
||||
<UiInput v-model="vaultItem.username" />
|
||||
<UiInput v-model="vaultItem.password" />
|
||||
<UiInput v-model="vaultItem.note" />
|
||||
</form>
|
||||
</UiDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n();
|
||||
|
||||
const vaultItem = reactive({
|
||||
title: '',
|
||||
username: '',
|
||||
password: '',
|
||||
note: '',
|
||||
});
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"title": "Eintrag erstellen"
|
||||
},
|
||||
"en": {
|
||||
"title": "Create Entry"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
117
src/components/vault/group/index.vue
Normal file
117
src/components/vault/group/index.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<VaultCardEdit
|
||||
v-if="vaultGroup"
|
||||
:color="vaultGroup.color ?? 'text-base-content'"
|
||||
:has-changes="hasChanges"
|
||||
:icon="vaultGroup.icon ?? 'mdi:folder-outline'"
|
||||
:title="vaultGroup.name ?? ''"
|
||||
@back="$emit('back')"
|
||||
@close="$emit('close')"
|
||||
@reject="(to) => $emit('reject', to)"
|
||||
@submit="(to) => $emit('submit', to)"
|
||||
v-model:read_only="read_only"
|
||||
>
|
||||
<div class="flex flex-col gap-4 w-full p-4">
|
||||
<UiInput
|
||||
v-show="!read_only"
|
||||
:label="t('vaultGroup.name')"
|
||||
:placeholder="t('vaultGroup.name')"
|
||||
:rules="vaultGroupSchema.name"
|
||||
:with-copy-button="read_only"
|
||||
:read_only
|
||||
autofocus
|
||||
v-model.trim="vaultGroup.name"
|
||||
/>
|
||||
|
||||
<UiInput
|
||||
v-show="!read_only || vaultGroup.description?.length"
|
||||
:read_only
|
||||
:label="t('vaultGroup.description')"
|
||||
:placeholder="t('vaultGroup.description')"
|
||||
:rules="vaultGroupSchema.description"
|
||||
:with-copy-button="read_only"
|
||||
v-model.trim="vaultGroup.description"
|
||||
/>
|
||||
|
||||
<UiColorPicker
|
||||
:read_only
|
||||
:label="t('vaultGroup.color')"
|
||||
:placeholder="t('vaultGroup.color')"
|
||||
v-model="vaultGroup.color"
|
||||
/>
|
||||
|
||||
<UiIconPicker
|
||||
:read_only
|
||||
:label="t('vaultGroup.icon')"
|
||||
:placeholder="t('vaultGroup.icon')"
|
||||
v-model="vaultGroup.icon"
|
||||
/>
|
||||
</div>
|
||||
</VaultCardEdit>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
||||
import {
|
||||
vaultGroupSchema,
|
||||
type SelectVaultGroup,
|
||||
} from '~/database/schemas/vault';
|
||||
|
||||
const { t } = useI18n();
|
||||
const showConfirmation = ref(false);
|
||||
const vaultGroup = defineModel<SelectVaultGroup>({ required: true });
|
||||
const read_only = defineModel<boolean>('read_only');
|
||||
const props = defineProps({
|
||||
originally: Object as PropType<SelectVaultGroup>,
|
||||
});
|
||||
|
||||
defineEmits<{
|
||||
submit: [to?: RouteLocationNormalizedLoadedGeneric];
|
||||
close: [void];
|
||||
back: [void];
|
||||
reject: [to?: RouteLocationNormalizedLoadedGeneric];
|
||||
}>();
|
||||
|
||||
const hasChanges = computed(() => {
|
||||
console.log('group has changes', props.originally, vaultGroup.value);
|
||||
if (!props.originally) {
|
||||
if (
|
||||
vaultGroup.value.color?.length ||
|
||||
vaultGroup.value.description?.length ||
|
||||
vaultGroup.value.icon?.length ||
|
||||
vaultGroup.value.name?.length
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(props.originally) !== JSON.stringify(vaultGroup.value);
|
||||
});
|
||||
|
||||
/* const onClose = () => {
|
||||
if (props.originally) vaultGroup.value = { ...props.originally };
|
||||
emit('close');
|
||||
}; */
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"vaultGroup": {
|
||||
"name": "Name",
|
||||
"description": "Beschreibung",
|
||||
"icon": "Icon",
|
||||
"color": "Farbe"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"vaultGroup": {
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"icon": "Icon",
|
||||
"color": "Color"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
42
src/components/vault/list/entry.vue
Normal file
42
src/components/vault/list/entry.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<UiListButton
|
||||
v-if="entry"
|
||||
:key="entry.id"
|
||||
@click="navigateToEntryAsync(entry.id)"
|
||||
class="text-base-content"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8">
|
||||
<Icon
|
||||
v-if="entry.icon || groupIcon"
|
||||
:name="entry.icon || groupIcon!"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col items-start">
|
||||
<div v-if="!entry.title && !entry.username && !entry.url">
|
||||
{{ entry.id }}
|
||||
</div>
|
||||
<div class="font-semibold">
|
||||
{{ entry.title }}
|
||||
</div>
|
||||
<span class="text-sm">
|
||||
{{ entry.username }}
|
||||
</span>
|
||||
<span class="text-sm">
|
||||
{{ entry.url }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</UiListButton>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectVaultEntry } from '~/database/schemas/vault';
|
||||
|
||||
defineProps({
|
||||
entry: Object as PropType<SelectVaultEntry>,
|
||||
groupIcon: [String, null],
|
||||
});
|
||||
|
||||
const { navigateToEntryAsync } = useVaultEntryStore();
|
||||
</script>
|
||||
7
src/components/vault/list/index.vue
Normal file
7
src/components/vault/list/index.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<UiList>
|
||||
<slot />
|
||||
</UiList>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
78
src/components/vault/modal/saveChanges.vue
Normal file
78
src/components/vault/modal/saveChanges.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<UiDialog
|
||||
v-model:open="showConfirmation"
|
||||
:title="t('dialog.title')"
|
||||
>
|
||||
{{ t('dialog.question') }}
|
||||
<template #buttons>
|
||||
<UiButton
|
||||
class="btn-outline btn-error focus:bg-primary"
|
||||
tabindex="10"
|
||||
@click="$emit('reject')"
|
||||
>
|
||||
<Icon name="mdi:cancel" />
|
||||
<span class="hidden sm:block">
|
||||
{{ t('dialog.reject') }}
|
||||
</span>
|
||||
</UiButton>
|
||||
<UiButton
|
||||
class="btn-outline focus:bg-primary"
|
||||
tabindex="11"
|
||||
ref="abortButtonRef"
|
||||
@click="showConfirmation = false"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
<span class="hidden sm:block">
|
||||
{{ t('dialog.abort') }}
|
||||
</span>
|
||||
</UiButton>
|
||||
<UiButton
|
||||
class="btn-outline btn-success"
|
||||
tabindex="12"
|
||||
@click="$emit('submit')"
|
||||
>
|
||||
<Icon name="mdi:check" />
|
||||
<span class="hidden sm:block">
|
||||
{{ t('dialog.save') }}
|
||||
</span>
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const showConfirmation = defineModel<boolean>();
|
||||
const abortButtonRef = useTemplateRef('abortButtonRef');
|
||||
|
||||
const { t } = useI18n();
|
||||
const { currentScreenSize } = storeToRefs(useUiStore());
|
||||
|
||||
onUpdated(() => {
|
||||
abortButtonRef.value?.$el.focus();
|
||||
});
|
||||
|
||||
defineEmits(['submit', 'reject']);
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"dialog": {
|
||||
"title": "Ungespeicherte Änderungen",
|
||||
"question": "Möchten Sie die Änderungen speichern?",
|
||||
"reject": "Verwerfen",
|
||||
"abort": "Abbrechen",
|
||||
"save": "Speichern"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"dialog": {
|
||||
"title": "Unsaved Changes",
|
||||
"question": "Would you like to save the changes?",
|
||||
"reject": "Reject",
|
||||
"abort": "Abort",
|
||||
"save": "Save"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
96
src/composables/helper.ts
Normal file
96
src/composables/helper.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { H3Error } from 'h3';
|
||||
|
||||
export const bytesToBase64DataUrlAsync = async (
|
||||
bytes: Uint8Array,
|
||||
type = 'application/octet-stream'
|
||||
) => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const reader = Object.assign(new FileReader(), {
|
||||
onload: () => resolve(reader.result),
|
||||
onerror: () => reject(reader.error),
|
||||
});
|
||||
reader.readAsDataURL(new File([new Blob([bytes])], '', { type }));
|
||||
});
|
||||
};
|
||||
|
||||
export const blobToImageAsync = (blob: Blob): Promise<HTMLImageElement> => {
|
||||
return new Promise((resolve) => {
|
||||
console.log('transform blob', blob);
|
||||
const url = URL.createObjectURL(blob);
|
||||
let img = new Image();
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
resolve(img);
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
};
|
||||
|
||||
export const deepToRaw = <T extends Record<string, any>>(sourceObj: T): T => {
|
||||
const objectIterator = (input: any): any => {
|
||||
if (Array.isArray(input)) {
|
||||
return input.map((item) => objectIterator(item));
|
||||
}
|
||||
if (isRef(input) || isReactive(input) || isProxy(input)) {
|
||||
return objectIterator(toRaw(input));
|
||||
}
|
||||
if (input && typeof input === 'object') {
|
||||
return Object.keys(input).reduce((acc, key) => {
|
||||
acc[key as keyof typeof acc] = objectIterator(input[key]);
|
||||
return acc;
|
||||
}, {} as T);
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
return objectIterator(sourceObj);
|
||||
};
|
||||
|
||||
export const readableFileSize = (sizeInByte: number | string = 0) => {
|
||||
if (!sizeInByte) {
|
||||
return '0 KB';
|
||||
}
|
||||
const size =
|
||||
typeof sizeInByte === 'string' ? parseInt(sizeInByte) : sizeInByte;
|
||||
const sizeInKb = size / 1024;
|
||||
const sizeInMb = sizeInKb / 1024;
|
||||
const sizeInGb = sizeInMb / 1024;
|
||||
const sizeInTb = sizeInGb / 1024;
|
||||
|
||||
if (sizeInTb > 1) return `${sizeInTb.toFixed(2)} TB`;
|
||||
if (sizeInGb > 1) return `${sizeInGb.toFixed(2)} GB`;
|
||||
if (sizeInMb > 1) return `${sizeInMb.toFixed(2)} MB`;
|
||||
|
||||
return `${sizeInKb.toFixed(2)} KB`;
|
||||
};
|
||||
|
||||
import type { LocationQueryValue, RouteLocationRawI18n } from 'vue-router';
|
||||
|
||||
export const getSingleRouteParam = (
|
||||
param: string | string[] | LocationQueryValue | LocationQueryValue[]
|
||||
): string => {
|
||||
const _param = Array.isArray(param) ? param.at(0) ?? '' : param ?? '';
|
||||
//console.log('found param', _param, param);
|
||||
return _param;
|
||||
};
|
||||
|
||||
export const isRouteActive = (
|
||||
to: RouteLocationRawI18n,
|
||||
exact: boolean = false
|
||||
) =>
|
||||
computed(() => {
|
||||
const found = useRouter()
|
||||
.getRoutes()
|
||||
.find((route) => route.name === useLocaleRoute()(to)?.name);
|
||||
//console.log('found route', found, useRouter().currentRoute.value, to);
|
||||
return exact
|
||||
? found?.name === useRouter().currentRoute.value.name
|
||||
: found?.name === useRouter().currentRoute.value.name ||
|
||||
found?.children.some(
|
||||
(child) => child.name === useRouter().currentRoute.value.name
|
||||
);
|
||||
});
|
||||
|
||||
export const isKey = <T extends object>(x: T, k: PropertyKey): k is keyof T => {
|
||||
return k in x;
|
||||
};
|
||||
16
src/i18n/i18n.config.ts
Normal file
16
src/i18n/i18n.config.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/* import de from '@/stores/sidebar/de.json';
|
||||
import en from '@/stores/sidebar/en.json'; */
|
||||
|
||||
export default defineI18nConfig(() => {
|
||||
return {
|
||||
legacy: false,
|
||||
messages: {
|
||||
de: {
|
||||
//sidebar: de,
|
||||
},
|
||||
en: {
|
||||
//sidebar: en,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
271
src/layouts/app.vue
Normal file
271
src/layouts/app.vue
Normal file
@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col">
|
||||
<nav
|
||||
class="navbar bg-base-100 max-sm:rounded-box max-sm:shadow sm:border-b border-base-content/25 sm:z-20 relative"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-text max-sm:btn-square sm:hidden me-2"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded="false"
|
||||
aria-controls="sidebar"
|
||||
data-overlay="#sidebar"
|
||||
>
|
||||
<span class="icon-[tabler--menu-2] size-5"></span>
|
||||
</button>
|
||||
<div class="flex flex-1 items-center">
|
||||
<a
|
||||
class="link text-base-content link-neutral text-xl font-semibold no-underline"
|
||||
href="#"
|
||||
>
|
||||
<UiTextGradient>Haex Hub</UiTextGradient>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-end flex items-center gap-4">
|
||||
<div
|
||||
class="dropdown relative inline-flex [--auto-close:inside] [--offset:8] [--placement:bottom-end]"
|
||||
>
|
||||
<button
|
||||
id="dropdown-scrollable"
|
||||
type="button"
|
||||
class="dropdown-toggle btn btn-text btn-circle dropdown-open:bg-base-content/10 size-10"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Dropdown"
|
||||
>
|
||||
<div class="indicator">
|
||||
<span
|
||||
v-show="notifications.length"
|
||||
class="indicator-item bg-error size-2 rounded-full text-sm"
|
||||
></span>
|
||||
<span
|
||||
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
|
||||
></span>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="dropdown-scrollable"
|
||||
>
|
||||
<div class="dropdown-header justify-center">
|
||||
<h6 class="text-base-content text-base">
|
||||
{{ t('notifications.label') }}
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto max-md:max-w-60"
|
||||
>
|
||||
<div
|
||||
class="dropdown-item"
|
||||
v-for="notification in notifications"
|
||||
>
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img
|
||||
v-if="notification.image"
|
||||
:src="notification.image"
|
||||
:alt="notification.alt ?? 'notification avatar'"
|
||||
/>
|
||||
<Icon
|
||||
v-else-if="notification.icon"
|
||||
:name="notification.icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-60">
|
||||
<h6 class="truncate text-base">
|
||||
{{ notification.title }}
|
||||
</h6>
|
||||
<small class="text-base-content/50 truncate">
|
||||
{{ notification.description }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href="#"
|
||||
class="dropdown-footer justify-center gap-1"
|
||||
>
|
||||
<span class="icon-[tabler--eye] size-4"></span>
|
||||
{{ t('notifications.view_all') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="dropdown relative inline-flex [--auto-close:inside] [--offset:8] [--placement:bottom-end]"
|
||||
>
|
||||
<button
|
||||
id="dropdown-scrollable"
|
||||
type="button"
|
||||
class="dropdown-toggle flex items-center"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Dropdown"
|
||||
>
|
||||
<div class="avatar">
|
||||
<div class="size-9.5 rounded-full">
|
||||
<img
|
||||
src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png"
|
||||
alt="avatar 1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="dropdown-avatar"
|
||||
>
|
||||
<li class="dropdown-header gap-2">
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img
|
||||
src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png"
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="text-base-content text-base font-semibold">
|
||||
John Doe
|
||||
</h6>
|
||||
<small class="text-base-content/50">Admin</small>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--user]"></span>
|
||||
My Profile
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--settings]"></span>
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--receipt-rupee]"></span>
|
||||
Billing
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
>
|
||||
<span class="icon-[tabler--help-triangle]"></span>
|
||||
FAQs
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown-footer gap-2">
|
||||
<button
|
||||
class="btn btn-error btn-soft btn-block"
|
||||
@click="onVaultCloseAsync"
|
||||
>
|
||||
<span class="icon-[tabler--logout]"></span>
|
||||
{{ t('vault.close') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<aside
|
||||
id="sidebar"
|
||||
class="overlay sm:shadow-none overlay-open:translate-x-0 drawer drawer-start hidden sm:absolute max-w-14 sm:flex sm:translate-x-0 sm:pt-16 z-10"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="drawer-body px-0 pt-4">
|
||||
<ul class="menu p-0">
|
||||
<!-- <li
|
||||
|
||||
|
||||
|
||||
> -->
|
||||
<UiSidebarLink
|
||||
v-bind="item"
|
||||
v-for="item in menu"
|
||||
:key="item.id"
|
||||
/>
|
||||
<!-- <UiTooltip
|
||||
:tooltip="item.tooltip || item.name"
|
||||
direction="right-start"
|
||||
>
|
||||
<NuxtLinkLocale
|
||||
:class="{ ['bg-base-300']: isActive(item.id).value }"
|
||||
:to="{
|
||||
name: 'extension',
|
||||
params: { extensionId: item.id, vaultId: 'test' },
|
||||
}"
|
||||
class="flex items-center justify-start hover:text-primary cursor-pointer tooltip-toogle"
|
||||
>
|
||||
<Icon
|
||||
:name="item.icon"
|
||||
class="size-6"
|
||||
/>
|
||||
<i
|
||||
:class="item.icon"
|
||||
class="shrink-0 size-8"
|
||||
/>
|
||||
</NuxtLinkLocale>
|
||||
</UiTooltip> -->
|
||||
<!-- </li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="overflow-hidden transition-all relative w-full">
|
||||
<div class="h-full overflow-scroll sm:pl-14">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <main class="sm:pl-14">
|
||||
<NuxtPage />
|
||||
</main> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n();
|
||||
|
||||
const { notifications } = storeToRefs(useNotificationStore());
|
||||
|
||||
const { menu } = storeToRefs(useSidebarStore());
|
||||
const { isActive } = useExtensionsStore();
|
||||
|
||||
const onExtensionSelectAsync = async (id: string) => {};
|
||||
|
||||
const onVaultCloseAsync = async () => {};
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
notifications:
|
||||
label: Benachrichtigungen
|
||||
view_all: Alle ansehen
|
||||
vault:
|
||||
close: Vault schließen
|
||||
en:
|
||||
notifications:
|
||||
label: Notifications
|
||||
view_all: View all
|
||||
vault:
|
||||
close: Close vault
|
||||
</i18n>
|
||||
5
src/layouts/default.vue
Normal file
5
src/layouts/default.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="w-screen h-screen overflow-scroll bg-base-200">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
155
src/pages/index.vue
Normal file
155
src/pages/index.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="items-center justify-center min-h-full flex w-full">
|
||||
<div class="flex flex-col justify-center items-center gap-4 max-w-3xl">
|
||||
<img
|
||||
src="/logo.svg"
|
||||
class="bg-primary p-3 size-16 rounded-full"
|
||||
alt="HaexVault Logo"
|
||||
/>
|
||||
|
||||
<span
|
||||
class="flex flex-wrap font-bold text-pretty text-xl gap-2 justify-center"
|
||||
>
|
||||
<p class="whitespace-nowrap">
|
||||
{{ t('welcome') }}
|
||||
</p>
|
||||
<UiTextGradient>Haex Hub</UiTextGradient>
|
||||
</span>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4 w-full">
|
||||
<VaultButtonCreate />
|
||||
|
||||
<!-- <VaultButtonOpen
|
||||
v-model:isOpen="passwordPromptOpen"
|
||||
:path="vaultPath"
|
||||
/> -->
|
||||
<NuxtLinkLocale
|
||||
:to="{
|
||||
name: 'haexBrowser',
|
||||
params: { vaultId: 'test' },
|
||||
}"
|
||||
>test link</NuxtLinkLocale
|
||||
>
|
||||
<!-- <button @click="test">test</button>
|
||||
<NuxtLinkLocale
|
||||
:to="{ name: 'vaultGroup', params: { vaultId: 'test' } }"
|
||||
>test link</NuxtLinkLocale
|
||||
> -->
|
||||
|
||||
<!-- <UiTreeFolder
|
||||
@edit="test"
|
||||
:value="tests"
|
||||
v-for="tests in [1, 2, 3]"
|
||||
/>
|
||||
<UiTreeFolder
|
||||
@edit="test"
|
||||
value="test123"
|
||||
/> -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-show="lastVaults.length"
|
||||
class="w-full"
|
||||
>
|
||||
<div class="font-thin text-sm justify-start px-2 pb-1">
|
||||
{{ t('lastUsed') }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="relative border-base-content/25 divide-base-content/25 flex w-full flex-col divide-y rounded-md border first:*:rounded-t-md last:*:rounded-b-md overflow-scroll"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between group h-12 overflow-x-scroll"
|
||||
v-for="vault in lastVaults"
|
||||
:key="vault.path"
|
||||
>
|
||||
<button
|
||||
class="link link-accent flex items-center no-underline justify-between text-nowrap text-xs md:text-base shrink w-full py-2 px-4"
|
||||
@click="
|
||||
passwordPromptOpen = true;
|
||||
vaultPath = vault.path;
|
||||
"
|
||||
>
|
||||
<span class="block md:hidden">
|
||||
{{ vault.name }}
|
||||
</span>
|
||||
<span class="hidden md:block">
|
||||
{{ vault.path }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="absolute right-2 btn btn-square btn-error btn-xs hidden group-hover:flex min-w-6"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:trash-can-outline"
|
||||
@click="removeVaultAsync(vault.path)"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<h4>{{ t('sponsors') }}</h4>
|
||||
<div>
|
||||
<button @click="openUrl('https://itemis.com')">
|
||||
<UiLogoItemis class="text-[#00457C]" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
|
||||
const passwordPromptOpen = ref(false);
|
||||
const vaultPath = ref('');
|
||||
|
||||
definePageMeta({
|
||||
name: 'vaultOpen',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore();
|
||||
const { lastVaults } = storeToRefs(useLastVaultStore());
|
||||
|
||||
await syncLastVaultsAsync();
|
||||
|
||||
/* const { $pluginManager } = useNuxtApp();
|
||||
|
||||
console.log('$pluginManager', $pluginManager);
|
||||
async function loadModule() {
|
||||
try {
|
||||
// Dynamisches Laden des Moduls
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
directory: false,
|
||||
});
|
||||
const moduleUrl =
|
||||
'/home/haex/Projekte/haex-vault-2/haex-vault/src/extensions/test/testPlugin.ts'; // Pfad relativ zum Server-Root
|
||||
await $pluginManager.loadDynamicModule(file);
|
||||
console.log('Modul erfolgreich geladen');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden des Moduls:', error);
|
||||
}
|
||||
}
|
||||
//await loadModule(); */
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"welcome": "Viel Spass mit",
|
||||
"lastUsed": "Zuletzt verwendete Vaults",
|
||||
"sponsors": "Powered by"
|
||||
},
|
||||
"en": {
|
||||
"welcome": "Have fun with",
|
||||
"lastUsed": "Last used Vaults",
|
||||
"sponsors": "Powered by"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
10
src/pages/test.vue
Normal file
10
src/pages/test.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<UiSidebarTest />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'test',
|
||||
});
|
||||
|
||||
</script>
|
||||
7
src/pages/vault.vue
Normal file
7
src/pages/vault.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLayout name="app">
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
||||
127
src/pages/vault/[vaultId]/browser.vue
Normal file
127
src/pages/vault/[vaultId]/browser.vue
Normal file
@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div>
|
||||
browser
|
||||
<HaexBrowser
|
||||
:tabs="tabs"
|
||||
:activeTabId="activeTabId"
|
||||
@createTab="createNewTab"
|
||||
@closeTab="closeTab"
|
||||
@navigate="navigateToUrl"
|
||||
@goBack="goBack"
|
||||
@goForward="goForward"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { Window, getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import { Webview } from '@tauri-apps/api/webview';
|
||||
|
||||
definePageMeta({
|
||||
name: 'haexBrowser',
|
||||
});
|
||||
|
||||
interface Tab {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
isLoading: boolean;
|
||||
isActive: boolean;
|
||||
window_label: string;
|
||||
}
|
||||
|
||||
const tabs = ref<Tab[]>([]);
|
||||
const activeTabId = ref<string | null>(null);
|
||||
|
||||
let unlistenTabCreated: UnlistenFn | null = null;
|
||||
let unlistenTabClosed: UnlistenFn | null = null;
|
||||
|
||||
onMounted(async () => {
|
||||
// Erstelle einen ersten Tab beim Start
|
||||
createNewTab('https://www.google.com');
|
||||
|
||||
// Höre auf Tab-Events
|
||||
unlistenTabCreated = await listen('tab-created', (event) => {
|
||||
const newTab = event.payload as Tab;
|
||||
|
||||
tabs.value = tabs.value.map((tab) => ({
|
||||
...tab,
|
||||
isActive: tab.id === newTab.id,
|
||||
}));
|
||||
|
||||
if (!tabs.value.some((tab) => tab.id === newTab.id)) {
|
||||
tabs.value.push(newTab);
|
||||
}
|
||||
|
||||
activeTabId.value = newTab.id;
|
||||
});
|
||||
|
||||
unlistenTabClosed = await listen('tab-closed', (event) => {
|
||||
const closedTabId = event.payload as string;
|
||||
tabs.value = tabs.value.filter((tab) => tab.id !== closedTabId);
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (unlistenTabCreated) unlistenTabCreated();
|
||||
if (unlistenTabClosed) unlistenTabClosed();
|
||||
});
|
||||
|
||||
const createNewTab = async (url: string = 'about:blank') => {
|
||||
try {
|
||||
/* const appWindow = new Window('uniqueLabel111', {
|
||||
fullscreen: true,
|
||||
});
|
||||
*/
|
||||
/* const appWindow = getCurrentWindow();
|
||||
|
||||
const webview = new Webview(appWindow, 'theUniqueLabel', {
|
||||
url: 'https://github.com/tauri-apps/tauri',
|
||||
height: 1000,
|
||||
width: 1000,
|
||||
x: 110,
|
||||
y: 0,
|
||||
});
|
||||
await webview.show(); */
|
||||
//console.log('create webview', webview);
|
||||
const tab_id = 'foo';
|
||||
await invoke('create_tab', { url, tabId: 'foo' });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen des Tabs:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const closeTab = async (tabId: string) => {
|
||||
try {
|
||||
//await invoke('close_tab', { tabId });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Schließen des Tabs:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const navigateToUrl = async (tabId: string, url: string) => {
|
||||
try {
|
||||
//await invoke('navigate_to_url', { tabId, url });
|
||||
} catch (error) {
|
||||
console.error('Fehler bei der Navigation:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const goBack = async (tabId: string | null) => {
|
||||
try {
|
||||
//await invoke('go_back', { tabId });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Zurückgehen:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const goForward = async (tabId: string | null) => {
|
||||
try {
|
||||
//await invoke('go_forward', { tabId });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Vorwärtsgehen:', error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
13
src/pages/vault/[vaultId]/extensions/[extensionId].vue
Normal file
13
src/pages/vault/[vaultId]/extensions/[extensionId].vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
hier kommt die erweiterung
|
||||
{{ useRouter().currentRoute.value.params.extensionId }}
|
||||
<iframe></iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'haexExtension',
|
||||
});
|
||||
</script>
|
||||
9
src/pages/vault/[vaultId]/extensions/index.vue
Normal file
9
src/pages/vault/[vaultId]/extensions/index.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>ad extension</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'extensionAdd',
|
||||
});
|
||||
</script>
|
||||
9
src/pages/vault/[vaultId]/index.vue
Normal file
9
src/pages/vault/[vaultId]/index.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>vault</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'vaultOverview',
|
||||
});
|
||||
</script>
|
||||
13
src/plugins/flyonui.client.ts
Normal file
13
src/plugins/flyonui.client.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import 'flyonui/flyonui';
|
||||
import { type IStaticMethods } from 'flyonui/flyonui';
|
||||
declare global {
|
||||
interface Window {
|
||||
HSStaticMethods: IStaticMethods;
|
||||
}
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.hook('page:finish', () => {
|
||||
window.HSStaticMethods.autoInit();
|
||||
});
|
||||
});
|
||||
BIN
src/public/favicon.ico
Normal file
BIN
src/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
48
src/public/itemis.svg
Normal file
48
src/public/itemis.svg
Normal file
@ -0,0 +1,48 @@
|
||||
<svg id="logo" class="fill-current w-[160px]" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 286.3 85" xml:space="preserve">
|
||||
<switch>
|
||||
<g>
|
||||
<g class="logo-image">
|
||||
<circle fill="white" cx="42.5" cy="42.5" r="40"></circle>
|
||||
<path d="M42.3,83.4c-22.6,0-40.9-18.4-40.9-40.9c0-22.6,18.4-40.9,40.9-40.9c22.6,0,40.9,18.4,40.9,40.9
|
||||
C83.3,65.1,64.9,83.4,42.3,83.4z M42.3,5.8C22.1,5.8,5.7,22.3,5.7,42.5s16.5,36.7,36.7,36.7S79,62.7,79,42.5S62.6,5.8,42.3,5.8z
|
||||
"></path>
|
||||
<g>
|
||||
<g>
|
||||
<polygon points="38.8,69.8 38.8,31.7 22.3,31.7 22.3,38.5 29.8,38.5 29.8,69.8 "></polygon>
|
||||
<path d="M34.1,13.2c-3.3,0-6,2.6-6,5.9c0,3.3,2.6,6,5.9,6c3.3,0,6-2.6,6-6
|
||||
C39.9,15.9,37.3,13.2,34.1,13.2z"></path>
|
||||
</g>
|
||||
<g>
|
||||
<polygon points="45.9,69.8 45.9,31.7 62.4,31.7 62.4,38.5 54.9,38.5 54.9,69.8 "></polygon>
|
||||
<path d="M50.6,13.2c3.3,0,6,2.6,6,5.9c0,3.3-2.6,6-5.9,6c-3.3,0-6-2.6-6-6
|
||||
C44.8,15.9,47.4,13.2,50.6,13.2z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g class="logo-text">
|
||||
<path d="M136.1,63.6c-4,0-5.3-2.6-5.3-6V38.5h10.6v-6.7h-10.6v-6.7h-9c0,7,0,29.1,0,32.7
|
||||
c0,4.2,1.6,7.5,3.8,9.7c2.3,2.2,5.6,3.3,9.8,3.3c5.1,0,8.4-1.8,10.6-4.2l-4.7-6C140.2,62.1,138.5,63.6,136.1,63.6z"></path>
|
||||
<path d="M217.7,30.7c-4.9,0-8.2,1.6-10.4,3.8c-2.2-2.2-5.5-3.8-10.4-3.8c-15,0-14.9,12.1-14.9,15
|
||||
s0,24.1,0,24.1h9V45.7c0-8.5,4.9-8.3,5.9-8.3c1,0,5.9-0.3,5.9,8.3v24.1h0h9h0V45.7c0-8.5,4.9-8.3,5.9-8.3c1,0,5.9-0.3,5.9,8.3
|
||||
v24.1h9c0,0,0-21.2,0-24.1C232.6,42.8,232.7,30.7,217.7,30.7z"></path>
|
||||
<path d="M273.2,46.4c-4.3-1.4-6-2.5-6-5.2c0-2,1.1-3.8,4.3-3.8c3.2,0,4.5,3.3,5.1,4.8
|
||||
c2.7-1.5,5.3-2.9,6.6-3.6c-2.5-6-6.3-7.9-12-7.9c-8,0-11.7,5.5-11.7,10.6c0,6.5,2.9,9.8,11.2,12.2c6,1.8,6.5,4.7,6.2,6.2
|
||||
c-0.3,1.7-1.6,3.6-5.3,3.6c-3.6,0-5.8-3.8-6.8-5.4c-1.8,1.1-3.4,2.1-6.4,3.8c2.1,5,6.8,9.1,13.5,9.1c7.9,0,12.9-5.1,12.9-12.1
|
||||
C284.9,51,279.6,48.5,273.2,46.4z"></path>
|
||||
<g>
|
||||
<polygon points="239.7,69.8 239.7,31.7 256.2,31.7 256.2,38.5 248.7,38.5 248.7,69.8 "></polygon>
|
||||
<path d="M244.4,13.2c3.3,0,6,2.6,6,5.9c0,3.3-2.6,6-5.9,6c-3.3,0-6-2.6-6-6
|
||||
C238.6,15.9,241.2,13.2,244.4,13.2z"></path>
|
||||
</g>
|
||||
<g>
|
||||
<polygon points="114.7,69.8 114.7,31.7 98.1,31.7 98.1,38.5 105.7,38.5 105.7,69.8 "></polygon>
|
||||
<path d="M110,13.2c-3.3,0-6,2.6-6,5.9c0,3.3,2.6,6,5.9,6c3.3,0,6-2.6,6-6C115.8,15.9,113.2,13.2,110,13.2
|
||||
z"></path>
|
||||
</g>
|
||||
<path d="M176.4,52.4v-3.7c0-12.3-4.7-18-14.8-18c-9.3,0-14.7,6.6-14.7,18v4c0,11.5,5.8,18.2,15.8,18.2
|
||||
c6.6,0,10.8-3.7,12.7-7.9c-2.2-1.4-4.6-2.8-6.1-3.8c-1,1.7-2.9,4.4-6.7,4.4c-5.8,0-7-5.9-7-10.9v-0.2H176.4z M155.7,45.7
|
||||
c0.2-7.1,3.3-8.2,6-8.2c2.6,0,5.9,1,6,8.2H155.7z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
207
src/public/logo.svg
Normal file
207
src/public/logo.svg
Normal file
@ -0,0 +1,207 @@
|
||||
<svg viewBox="122 107 263 292" xmlns="http://www.w3.org/2000/svg" style="max-height: 500px" width="263" height="292">
|
||||
<g stroke-width="0.3" stroke-opacity="0.00" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 0.00 500.00 
L 500.00 500.00
L 500.00 0.00
L 0.00 0.00
L 0.00 500.00
M 224.00 111.00 
C 231.64 114.34 231.15 125.27 233.78 133.22 C 236.40 141.18 233.32 153.32 229.08 160.08 C 224.84 166.83 229.06 176.15 231.33 183.67 C 233.59 191.20 237.83 198.83 244.00 204.00
C 266.37 217.28 288.30 227.00 309.00 244.00
C 302.67 236.14 297.53 229.03 290.75 221.25 C 283.96 213.48 279.34 206.78 272.70 198.30 C 266.05 189.82 261.91 183.96 256.69 173.31 C 251.48 162.65 249.83 154.72 248.00 141.00 C 246.17 127.28 259.55 133.29 268.08 139.92 C 276.61 146.55 285.22 149.49 293.75 155.25 C 302.27 161.02 311.02 165.51 319.00 172.00
C 333.96 180.10 351.42 199.09 360.00 215.00
C 364.03 222.47 368.67 227.83 371.77 236.23 C 374.87 244.63 375.48 254.80 378.33 263.67 C 381.18 272.54 380.34 286.05 379.22 295.22 C 378.11 304.40 376.21 314.80 373.02 323.02 C 369.84 331.25 366.25 338.84 362.23 346.23 C 358.20 353.62 353.47 359.98 348.25 366.25 C 343.03 372.51 336.37 377.44 331.08 379.86
C 327.43 381.21 324.03 383.93 320.00 385.00
C 320.86 388.48 313.78 392.73 306.78 393.78 C 299.78 394.82 288.01 393.70 280.00 394.00 C 271.99 394.30 261.37 393.91 253.00 394.00 C 244.63 394.09 235.43 394.10 227.00 394.00 C 218.57 393.90 208.23 394.32 200.00 394.00 C 191.77 393.68 182.87 396.59 178.00 389.00
C 175.90 386.88 176.08 388.00 175.00 385.00
C 173.90 381.94 174.40 379.91 175.00 377.00
C 175.83 372.99 176.48 370.39 179.00 367.00
C 181.54 363.59 184.21 361.67 188.00 360.00
C 194.80 357.00 202.48 358.16 210.00 358.00
C 204.18 349.77 197.87 341.27 197.00 331.00
C 196.59 329.08 195.21 326.36 195.00 324.00
C 194.72 320.78 194.25 315.36 196.00 313.00
C 196.00 312.50 196.00 312.00 196.00 312.00
C 196.30 310.37 198.29 306.24 198.00 307.00
C 198.19 306.12 198.11 304.33 199.00 303.00
C 191.11 300.89 179.04 304.79 170.00 303.00 C 160.96 301.21 153.07 308.94 145.00 309.00 C 136.93 309.06 130.06 302.11 126.00 296.00
C 123.16 291.73 121.53 286.73 123.00 281.00
C 123.82 277.81 125.91 276.29 128.00 274.00
C 131.09 269.45 136.40 267.32 142.00 266.00
C 144.65 265.37 147.06 264.79 150.00 265.00
C 150.50 265.03 151.50 265.00 152.00 265.00
C 154.50 263.94 157.04 264.47 160.00 264.00
C 169.80 261.15 164.14 257.96 165.00 250.00
C 165.05 249.51 165.00 249.00 165.00 249.00
C 163.47 246.93 163.50 243.03 165.00 241.00
C 165.00 240.50 165.00 240.00 165.00 240.00
C 164.66 236.86 165.54 233.27 166.00 231.00
C 166.97 226.20 168.52 218.81 172.00 215.00
C 163.08 216.93 154.33 208.76 146.92 206.08 C 139.50 203.40 129.72 198.14 129.00 189.00
C 128.94 188.24 129.32 186.68 129.00 186.00
C 126.69 186.39 128.17 187.06 127.00 185.00
C 125.33 182.05 124.95 177.75 125.00 174.00
C 127.20 174.74 124.69 178.33 127.00 179.00
C 126.92 177.05 125.64 173.65 126.00 172.00
C 126.57 169.37 130.73 167.21 133.00 167.00
C 133.50 166.95 134.00 167.00 134.00 167.00
C 134.67 165.13 137.08 162.88 137.00 163.00
C 138.55 160.15 138.62 158.02 141.00 155.00
C 143.80 151.45 148.11 148.46 150.00 147.00
C 157.03 139.76 167.00 138.77 177.01 136.01 C 187.01 133.25 192.68 124.37 201.00 119.00
C 204.06 116.44 206.52 113.70 211.00 112.00
C 216.35 109.98 220.25 109.36 224.00 111.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.97" stroke="#1F1F1F" fill-opacity="0.0" fill="None"><path d="M 211.00 112.00 
C 207.73 113.57 204.34 116.85 201.00 119.00
C 192.68 124.37 187.01 133.25 177.01 136.01 C 167.00 138.77 157.03 139.76 150.00 147.00
C 147.22 149.86 143.35 152.60 141.00 155.00
C 138.62 158.02 138.55 160.15 137.00 163.00
C 136.21 164.44 135.13 166.05 134.00 167.00
C 134.00 167.00 133.50 166.95 133.00 167.00
C 130.91 168.68 128.16 170.13 126.00 172.00
C 125.64 173.65 126.92 177.05 127.00 179.00
C 127.08 180.85 126.54 183.21 127.00 185.00
C 128.17 187.06 126.69 186.39 129.00 186.00
C 139.58 184.19 139.28 173.57 145.30 166.30 C 151.33 159.04 159.11 154.07 168.00 152.00
C 171.54 151.18 174.52 151.23 178.00 152.00
C 198.43 156.53 210.55 180.87 193.25 196.25 C 175.95 211.63 151.55 202.15 136.00 191.00
C 134.75 191.30 134.29 192.83 133.00 193.00
C 141.03 202.59 157.50 209.93 171.00 209.00
C 165.13 212.19 180.28 209.95 174.00 214.00
C 171.29 219.69 168.04 225.37 166.00 231.00
C 165.54 233.27 164.66 236.86 165.00 240.00
C 167.63 242.52 165.14 247.56 165.00 250.00
C 164.14 257.96 169.80 261.15 160.00 264.00
C 157.59 264.70 155.01 265.00 152.00 265.00
C 151.50 265.00 150.50 265.03 150.00 265.00
C 147.41 265.74 144.01 265.77 142.00 266.00
C 136.40 267.32 131.09 269.45 128.00 274.00
C 126.55 276.13 123.72 279.04 123.00 281.00
C 121.53 286.73 123.16 291.73 126.00 296.00
C 129.31 299.51 133.40 300.99 138.00 305.00
C 139.13 305.57 141.66 305.69 143.00 306.00
C 147.25 306.00 149.12 302.05 151.77 301.42 C 154.42 300.80 158.84 299.47 163.00 299.00
C 167.21 298.53 171.76 299.00 176.00 299.00
C 178.68 299.00 182.08 299.89 184.00 300.00
C 191.28 299.72 196.47 297.98 203.00 296.00
C 203.30 297.57 202.75 299.61 201.00 300.00
C 200.53 300.72 200.61 301.25 200.00 302.00
C 199.77 302.28 199.18 302.73 199.00 303.00
C 198.11 304.33 198.19 306.12 198.00 307.00
C 197.52 309.20 198.55 310.92 196.00 312.00
C 196.00 312.00 196.00 312.50 196.00 313.00
C 196.00 313.50 196.00 314.50 196.00 315.00
C 197.34 318.15 197.00 322.19 197.00 326.00
C 197.00 327.66 196.94 329.34 197.00 331.00
C 197.87 341.27 204.18 349.77 210.00 358.00
C 202.48 358.16 194.80 357.00 188.00 360.00
C 186.82 360.87 186.62 362.45 185.00 363.00
C 185.65 362.60 182.90 364.43 184.00 363.00
C 182.50 363.73 180.03 365.82 179.00 367.00
C 176.48 370.39 175.83 372.99 175.00 377.00
C 174.79 379.31 174.51 382.75 175.00 385.00
C 176.08 388.00 175.90 386.88 178.00 389.00
C 179.28 389.54 182.52 389.85 184.00 390.00
C 183.44 380.93 188.33 375.54 195.08 370.08 C 201.83 364.62 214.17 366.00 222.00 367.00 C 229.83 367.99 239.80 375.93 244.00 369.00
C 233.64 367.84 224.41 364.16 217.25 355.75 C 210.08 347.35 207.42 335.10 208.00 324.00 C 208.58 312.90 214.05 304.64 223.23 298.23 C 232.41 291.82 242.58 289.26 254.01 288.01 C 265.44 286.76 278.78 290.03 288.69 294.31 C 298.61 298.58 302.42 309.65 311.00 311.00
C 292.98 277.80 244.73 276.83 217.00 297.00
C 209.30 289.11 224.66 284.69 227.75 277.75 C 230.85 270.82 239.46 258.34 233.00 253.00
C 230.18 263.89 227.65 272.58 218.75 280.75 C 209.85 288.92 200.91 290.73 188.93 293.93 C 176.95 297.12 160.44 288.18 148.93 296.93 C 137.41 305.67 132.86 287.36 144.08 281.08 C 155.30 274.80 164.62 276.35 175.25 269.25 C 185.88 262.14 188.73 250.71 187.00 238.00
C 178.32 241.41 187.54 262.01 173.00 263.00
C 169.98 252.46 170.62 238.53 173.32 228.32 C 176.03 218.12 181.89 210.83 192.22 208.22 C 202.56 205.61 207.52 199.40 214.25 191.25 C 220.99 183.11 213.85 167.29 220.00 162.00
C 234.12 146.99 229.05 129.74 225.00 114.00
C 224.85 113.42 224.66 111.92 224.00 111.00
C 220.25 109.36 216.35 109.98 211.00 112.00
M 152.00 273.00 
C 142.20 277.14 131.12 280.81 131.00 294.00
C 120.27 284.76 135.80 269.65 147.00 272.00
C 149.02 271.71 152.29 270.15 155.00 271.00
C 154.45 272.01 153.02 272.44 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.19" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 225.00 114.00 
C 229.89 122.33 229.91 132.23 232.00 142.00 C 234.08 151.77 221.37 159.93 223.93 168.07 C 226.48 176.22 228.75 187.50 232.00 194.00
C 235.40 198.53 239.35 201.24 244.00 204.00
C 237.83 198.83 233.59 191.20 231.33 183.67 C 229.06 176.15 224.84 166.83 229.08 160.08 C 233.32 153.32 236.40 141.18 233.78 133.22 C 231.15 125.27 231.64 114.34 224.00 111.00
C 224.66 111.92 224.85 113.42 225.00 114.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.09" stroke="#B0B0B0" fill-opacity="0.0" fill="None"><path d="M 201.00 119.00 
C 204.34 116.85 207.73 113.57 211.00 112.00
C 206.52 113.70 204.06 116.44 201.00 119.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.48" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 220.00 162.00 
C 219.66 166.74 225.00 173.64 225.32 178.68 C 225.64 183.72 229.13 190.18 232.00 194.00
C 228.75 187.50 226.48 176.22 223.93 168.07 C 221.37 159.93 234.08 151.77 232.00 142.00 C 229.91 132.23 229.89 122.33 225.00 114.00
C 229.05 129.74 234.12 146.99 220.00 162.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.99" stroke="#BEBEBE" fill-opacity="0.0" fill="None"><path d="M 309.00 244.00 
C 319.62 252.72 326.67 266.01 332.31 278.69 C 337.94 291.38 343.27 312.99 343.00 327.00
C 348.86 307.69 357.88 289.27 376.00 278.00
C 375.67 274.36 376.25 270.64 376.00 267.00
C 374.20 240.59 358.93 215.59 343.00 195.00
C 335.15 187.46 327.36 178.80 319.00 172.00
C 311.02 165.51 302.27 161.02 293.75 155.25 C 285.22 149.49 276.61 146.55 268.08 139.92 C 259.55 133.29 246.17 127.28 248.00 141.00 C 249.83 154.72 251.48 162.65 256.69 173.31 C 261.91 183.96 266.05 189.82 272.70 198.30 C 279.34 206.78 283.96 213.48 290.75 221.25 C 297.53 229.03 302.67 236.14 309.00 244.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.13" stroke="#858585" fill-opacity="0.0" fill="None"><path d="M 141.00 155.00 
C 143.35 152.60 147.22 149.86 150.00 147.00
C 148.11 148.46 143.80 151.45 141.00 155.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#868686" fill-opacity="0.0" fill="None"><path d="M 168.00 152.00 
C 171.08 151.66 174.91 151.68 178.00 152.00
C 174.52 151.23 171.54 151.18 168.00 152.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#F6F6F6" fill-opacity="0.0" fill="None"><path d="M 129.00 186.00 
C 129.32 186.68 128.94 188.24 129.00 189.00
C 129.45 190.21 132.14 191.97 133.00 193.00
C 134.29 192.83 134.75 191.30 136.00 191.00
C 151.55 202.15 175.95 211.63 193.25 196.25 C 210.55 180.87 198.43 156.53 178.00 152.00
C 174.91 151.68 171.08 151.66 168.00 152.00
C 159.11 154.07 151.33 159.04 145.30 166.30 C 139.28 173.57 139.58 184.19 129.00 186.00
M 175.00 161.00 
C 186.50 162.02 186.34 180.76 173.98 179.98 C 161.61 179.20 162.30 159.88 175.00 161.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#232323" fill-opacity="0.0" fill="None"><path d="M 175.00 161.00 
C 162.30 159.88 161.61 179.20 173.98 179.98 C 186.34 180.76 186.50 162.02 175.00 161.00
M 168.00 171.00 
C 167.10 158.05 181.72 171.06 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.16" stroke="#A4A4A4" fill-opacity="0.0" fill="None"><path d="M 134.00 167.00 
C 135.13 166.05 136.21 164.44 137.00 163.00
C 137.08 162.88 134.67 165.13 134.00 167.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#F8F8F8" fill-opacity="0.0" fill="None"><path d="M 184.00 390.00 
C 200.03 391.64 215.88 388.64 232.00 390.00 C 248.12 391.37 264.82 388.01 281.00 390.00 C 297.18 391.99 307.70 387.80 321.00 381.00
C 334.96 366.76 337.54 345.00 343.00 327.00
C 343.27 312.99 337.94 291.38 332.31 278.69 C 326.67 266.01 319.62 252.72 309.00 244.00
C 288.30 227.00 266.37 217.28 244.00 204.00
C 239.35 201.24 235.40 198.53 232.00 194.00
C 229.13 190.18 225.64 183.72 225.32 178.68 C 225.00 173.64 219.66 166.74 220.00 162.00
C 213.85 167.29 220.99 183.11 214.25 191.25 C 207.52 199.40 202.56 205.61 192.22 208.22 C 181.89 210.83 176.03 218.12 173.32 228.32 C 170.62 238.53 169.98 252.46 173.00 263.00
C 187.54 262.01 178.32 241.41 187.00 238.00
C 188.73 250.71 185.88 262.14 175.25 269.25 C 164.62 276.35 155.30 274.80 144.08 281.08 C 132.86 287.36 137.41 305.67 148.93 296.93 C 160.44 288.18 176.95 297.12 188.93 293.93 C 200.91 290.73 209.85 288.92 218.75 280.75 C 227.65 272.58 230.18 263.89 233.00 253.00
C 239.46 258.34 230.85 270.82 227.75 277.75 C 224.66 284.69 209.30 289.11 217.00 297.00
C 244.73 276.83 292.98 277.80 311.00 311.00
C 302.42 309.65 298.61 298.58 288.69 294.31 C 278.78 290.03 265.44 286.76 254.01 288.01 C 242.58 289.26 232.41 291.82 223.23 298.23 C 214.05 304.64 208.58 312.90 208.00 324.00 C 207.42 335.10 210.08 347.35 217.25 355.75 C 224.41 364.16 233.64 367.84 244.00 369.00
C 239.80 375.93 229.83 367.99 222.00 367.00 C 214.17 366.00 201.83 364.62 195.08 370.08 C 188.33 375.54 183.44 380.93 184.00 390.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#E7E7E7" fill-opacity="0.0" fill="None"><path d="M 168.00 171.00 
C 181.72 171.06 167.10 158.05 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.06" stroke="#D5D6D5" fill-opacity="0.0" fill="None"><path d="M 126.00 172.00 
C 128.16 170.13 130.91 168.68 133.00 167.00
C 130.73 167.21 126.57 169.37 126.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.17" stroke="#010101" fill-opacity="0.0" fill="None"><path d="M 319.00 172.00 
C 327.36 178.80 335.15 187.46 343.00 195.00
C 349.36 201.12 355.23 207.97 360.00 215.00
C 351.42 199.09 333.96 180.10 319.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.19" stroke="#6A6A6A" fill-opacity="0.0" fill="None"><path d="M 127.00 185.00 
C 126.54 183.21 127.08 180.85 127.00 179.00
C 124.69 178.33 127.20 174.74 125.00 174.00
C 124.95 177.75 125.33 182.05 127.00 185.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.26" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 172.00 215.00 
C 172.45 214.90 173.63 214.23 174.00 214.00
C 180.28 209.95 165.13 212.19 171.00 209.00
C 157.50 209.93 141.03 202.59 133.00 193.00
C 132.14 191.97 129.45 190.21 129.00 189.00
C 129.72 198.14 139.50 203.40 146.92 206.08 C 154.33 208.76 163.08 216.93 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.33" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 376.00 267.00 
C 376.19 267.61 376.87 267.94 377.00 269.00
C 382.28 281.94 376.22 302.91 373.67 315.67 C 371.13 328.44 363.16 341.06 357.08 352.08 C 351.00 363.10 341.62 372.42 331.00 379.00
C 336.37 377.44 343.03 372.51 348.25 366.25 C 353.47 359.98 358.20 353.62 362.23 346.23 C 366.25 338.84 369.84 331.25 373.02 323.02 C 376.21 314.80 378.11 304.40 379.22 295.22 C 380.34 286.05 381.18 272.54 378.33 263.67 C 375.48 254.80 374.87 244.63 371.77 236.23 C 368.67 227.83 364.03 222.47 360.00 215.00
C 355.23 207.97 349.36 201.12 343.00 195.00
C 358.93 215.59 374.20 240.59 376.00 267.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.17" stroke="#8A8A8A" fill-opacity="0.0" fill="None"><path d="M 172.00 215.00 
C 168.52 218.81 166.97 226.20 166.00 231.00
C 168.04 225.37 171.29 219.69 174.00 214.00
C 173.63 214.23 172.45 214.90 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.60" stroke="#101010" fill-opacity="0.0" fill="None"><path d="M 165.00 241.00 
C 165.00 243.67 165.00 246.33 165.00 249.00
C 165.00 249.00 165.05 249.51 165.00 250.00
C 165.14 247.56 167.63 242.52 165.00 240.00
C 165.00 240.00 165.00 240.50 165.00 241.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.03" stroke="#7E7E7E" fill-opacity="0.0" fill="None"><path d="M 165.00 249.00 
C 165.00 246.33 165.00 243.67 165.00 241.00
C 163.50 243.03 163.47 246.93 165.00 249.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.04" stroke="#777777" fill-opacity="0.0" fill="None"><path d="M 152.00 265.00 
C 155.01 265.00 157.59 264.70 160.00 264.00
C 157.04 264.47 154.50 263.94 152.00 265.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.29" stroke="#7B7B7B" fill-opacity="0.0" fill="None"><path d="M 142.00 266.00 
C 144.01 265.77 147.41 265.74 150.00 265.00
C 147.06 264.79 144.65 265.37 142.00 266.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.68" stroke="#4F4F4F" fill-opacity="0.0" fill="None"><path d="M 376.00 278.00 
C 376.36 277.78 377.00 278.00 377.00 278.00
C 377.06 275.10 377.35 271.88 377.00 269.00
C 376.87 267.94 376.19 267.61 376.00 267.00
C 376.25 270.64 375.67 274.36 376.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.46" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 377.00 278.00 
C 376.24 316.06 358.69 361.73 321.00 381.00
C 307.70 387.80 297.18 391.99 281.00 390.00 C 264.82 388.01 248.12 391.37 232.00 390.00 C 215.88 388.64 200.03 391.64 184.00 390.00
C 182.52 389.85 179.28 389.54 178.00 389.00
C 182.87 396.59 191.77 393.68 200.00 394.00 C 208.23 394.32 218.57 393.90 227.00 394.00 C 235.43 394.10 244.63 394.09 253.00 394.00 C 261.37 393.91 271.99 394.30 280.00 394.00 C 288.01 393.70 299.78 394.82 306.78 393.78 C 313.78 392.73 320.86 388.48 320.00 385.00
C 324.03 383.93 327.43 381.21 331.00 379.00
C 341.62 372.42 351.00 363.10 357.08 352.08 C 363.16 341.06 371.13 328.44 373.67 315.67 C 376.22 302.91 382.28 281.94 377.00 269.00
C 377.35 271.88 377.06 275.10 377.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#767676" fill-opacity="0.0" fill="None"><path d="M 147.00 272.00 
C 148.46 272.02 151.35 273.02 152.00 273.00
C 153.02 272.44 154.45 272.01 155.00 271.00
C 152.29 270.15 149.02 271.71 147.00 272.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#E3E3E3" fill-opacity="0.0" fill="None"><path d="M 152.00 273.00 
C 151.35 273.02 148.46 272.02 147.00 272.00
C 135.80 269.65 120.27 284.76 131.00 294.00
C 131.12 280.81 142.20 277.14 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.20" stroke="#8E8E8E" fill-opacity="0.0" fill="None"><path d="M 123.00 281.00 
C 123.72 279.04 126.55 276.13 128.00 274.00
C 125.91 276.29 123.82 277.81 123.00 281.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.99" stroke="#1D1D1D" fill-opacity="0.0" fill="None"><path d="M 343.00 327.00 
C 337.54 345.00 334.96 366.76 321.00 381.00
C 358.69 361.73 376.24 316.06 377.00 278.00
C 377.00 278.00 376.36 277.78 376.00 278.00
C 357.88 289.27 348.86 307.69 343.00 327.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.32" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 126.00 296.00 
C 130.06 302.11 136.93 309.06 145.00 309.00 C 153.07 308.94 160.96 301.21 170.00 303.00 C 179.04 304.79 191.11 300.89 199.00 303.00
C 199.18 302.73 199.77 302.28 200.00 302.00
C 197.55 298.03 190.28 303.63 184.00 302.00 C 177.72 300.37 175.48 302.79 166.00 302.00 C 156.52 301.21 146.75 312.62 138.00 305.00
C 133.40 300.99 129.31 299.51 126.00 296.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.59" stroke="#010101" fill-opacity="0.0" fill="None"><path d="M 184.00 300.00 
C 181.34 300.10 178.66 300.00 176.00 300.00
C 171.88 300.00 166.28 298.95 163.00 299.00
C 158.84 299.47 154.42 300.80 151.77 301.42 C 149.12 302.05 147.25 306.00 143.00 306.00
C 147.64 307.07 150.96 305.59 155.08 303.08 C 159.19 300.56 165.50 300.79 170.00 301.00 C 174.50 301.21 183.31 300.15 187.00 301.00 C 190.69 301.85 200.07 297.39 201.00 300.00
C 202.75 299.61 203.30 297.57 203.00 296.00
C 196.47 297.98 191.28 299.72 184.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.75" stroke="#474747" fill-opacity="0.0" fill="None"><path d="M 176.00 300.00 
C 175.96 300.00 176.00 299.00 176.00 299.00
C 171.76 299.00 167.21 298.53 163.00 299.00
C 166.28 298.95 171.88 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.77" stroke="#090909" fill-opacity="0.0" fill="None"><path d="M 176.00 300.00 
C 178.66 300.00 181.34 300.10 184.00 300.00
C 182.08 299.89 178.68 299.00 176.00 299.00
C 176.00 299.00 175.96 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.44" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 143.00 306.00 
C 141.66 305.69 139.13 305.57 138.00 305.00
C 146.75 312.62 156.52 301.21 166.00 302.00 C 175.48 302.79 177.72 300.37 184.00 302.00 C 190.28 303.63 197.55 298.03 200.00 302.00
C 200.61 301.25 200.53 300.72 201.00 300.00
C 200.07 297.39 190.69 301.85 187.00 301.00 C 183.31 300.15 174.50 301.21 170.00 301.00 C 165.50 300.79 159.19 300.56 155.08 303.08 C 150.96 305.59 147.64 307.07 143.00 306.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.29" stroke="#757575" fill-opacity="0.0" fill="None"><path d="M 196.00 312.00 
C 198.55 310.92 197.52 309.20 198.00 307.00
C 198.29 306.24 196.30 310.37 196.00 312.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.03" stroke="#AEAEAE" fill-opacity="0.0" fill="None"><path d="M 195.00 324.00 
C 195.00 324.05 196.00 324.00 196.00 324.00
C 195.64 321.10 196.00 317.92 196.00 315.00
C 196.00 314.50 196.00 313.50 196.00 313.00
C 194.25 315.36 194.72 320.78 195.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.66" stroke="#111111" fill-opacity="0.0" fill="None"><path d="M 196.00 324.00 
C 196.15 325.26 196.76 325.23 197.00 326.00
C 197.00 322.19 197.34 318.15 196.00 315.00
C 196.00 317.92 195.64 321.10 196.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.26" stroke="#484848" fill-opacity="0.0" fill="None"><path d="M 197.00 331.00 
C 196.94 329.34 197.00 327.66 197.00 326.00
C 196.76 325.23 196.15 325.26 196.00 324.00
C 196.00 324.00 195.00 324.05 195.00 324.00
C 195.21 326.36 196.59 329.08 197.00 331.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.11" stroke="#B4B4B4" fill-opacity="0.0" fill="None"><path d="M 179.00 367.00 
C 180.03 365.82 182.50 363.73 184.00 363.00
C 184.40 362.80 185.00 363.00 185.00 363.00
C 186.62 362.45 186.82 360.87 188.00 360.00
C 184.21 361.67 181.54 363.59 179.00 367.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.78" stroke="#0E0E0E" fill-opacity="0.0" fill="None"><path d="M 185.00 363.00 
C 185.00 363.00 184.40 362.80 184.00 363.00
C 182.90 364.43 185.65 362.60 185.00 363.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.20" stroke="#868686" fill-opacity="0.0" fill="None"><path d="M 175.00 385.00 
C 174.51 382.75 174.79 379.31 175.00 377.00
C 174.40 379.91 173.90 381.94 175.00 385.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.00" fill="#000000">
|
||||
<path d="M 0.00 500.00 
L 500.00 500.00
L 500.00 0.00
L 0.00 0.00
L 0.00 500.00
M 224.00 111.00 
C 231.64 114.34 231.15 125.27 233.78 133.22 C 236.40 141.18 233.32 153.32 229.08 160.08 C 224.84 166.83 229.06 176.15 231.33 183.67 C 233.59 191.20 237.83 198.83 244.00 204.00
C 266.37 217.28 288.30 227.00 309.00 244.00
C 302.67 236.14 297.53 229.03 290.75 221.25 C 283.96 213.48 279.34 206.78 272.70 198.30 C 266.05 189.82 261.91 183.96 256.69 173.31 C 251.48 162.65 249.83 154.72 248.00 141.00 C 246.17 127.28 259.55 133.29 268.08 139.92 C 276.61 146.55 285.22 149.49 293.75 155.25 C 302.27 161.02 311.02 165.51 319.00 172.00
C 333.96 180.10 351.42 199.09 360.00 215.00
C 364.03 222.47 368.67 227.83 371.77 236.23 C 374.87 244.63 375.48 254.80 378.33 263.67 C 381.18 272.54 380.34 286.05 379.22 295.22 C 378.11 304.40 376.21 314.80 373.02 323.02 C 369.84 331.25 366.25 338.84 362.23 346.23 C 358.20 353.62 353.47 359.98 348.25 366.25 C 343.03 372.51 336.37 377.44 331.08 379.86
C 327.43 381.21 324.03 383.93 320.00 385.00
C 320.86 388.48 313.78 392.73 306.78 393.78 C 299.78 394.82 288.01 393.70 280.00 394.00 C 271.99 394.30 261.37 393.91 253.00 394.00 C 244.63 394.09 235.43 394.10 227.00 394.00 C 218.57 393.90 208.23 394.32 200.00 394.00 C 191.77 393.68 182.87 396.59 178.00 389.00
C 175.90 386.88 176.08 388.00 175.00 385.00
C 173.90 381.94 174.40 379.91 175.00 377.00
C 175.83 372.99 176.48 370.39 179.00 367.00
C 181.54 363.59 184.21 361.67 188.00 360.00
C 194.80 357.00 202.48 358.16 210.00 358.00
C 204.18 349.77 197.87 341.27 197.00 331.00
C 196.59 329.08 195.21 326.36 195.00 324.00
C 194.72 320.78 194.25 315.36 196.00 313.00
C 196.00 312.50 196.00 312.00 196.00 312.00
C 196.30 310.37 198.29 306.24 198.00 307.00
C 198.19 306.12 198.11 304.33 199.00 303.00
C 191.11 300.89 179.04 304.79 170.00 303.00 C 160.96 301.21 153.07 308.94 145.00 309.00 C 136.93 309.06 130.06 302.11 126.00 296.00
C 123.16 291.73 121.53 286.73 123.00 281.00
C 123.82 277.81 125.91 276.29 128.00 274.00
C 131.09 269.45 136.40 267.32 142.00 266.00
C 144.65 265.37 147.06 264.79 150.00 265.00
C 150.50 265.03 151.50 265.00 152.00 265.00
C 154.50 263.94 157.04 264.47 160.00 264.00
C 169.80 261.15 164.14 257.96 165.00 250.00
C 165.05 249.51 165.00 249.00 165.00 249.00
C 163.47 246.93 163.50 243.03 165.00 241.00
C 165.00 240.50 165.00 240.00 165.00 240.00
C 164.66 236.86 165.54 233.27 166.00 231.00
C 166.97 226.20 168.52 218.81 172.00 215.00
C 163.08 216.93 154.33 208.76 146.92 206.08 C 139.50 203.40 129.72 198.14 129.00 189.00
C 128.94 188.24 129.32 186.68 129.00 186.00
C 126.69 186.39 128.17 187.06 127.00 185.00
C 125.33 182.05 124.95 177.75 125.00 174.00
C 127.20 174.74 124.69 178.33 127.00 179.00
C 126.92 177.05 125.64 173.65 126.00 172.00
C 126.57 169.37 130.73 167.21 133.00 167.00
C 133.50 166.95 134.00 167.00 134.00 167.00
C 134.67 165.13 137.08 162.88 137.00 163.00
C 138.55 160.15 138.62 158.02 141.00 155.00
C 143.80 151.45 148.11 148.46 150.00 147.00
C 157.03 139.76 167.00 138.77 177.01 136.01 C 187.01 133.25 192.68 124.37 201.00 119.00
C 204.06 116.44 206.52 113.70 211.00 112.00
C 216.35 109.98 220.25 109.36 224.00 111.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.97" fill="#1F1F1F">
|
||||
<path d="M 211.00 112.00 
C 207.73 113.57 204.34 116.85 201.00 119.00
C 192.68 124.37 187.01 133.25 177.01 136.01 C 167.00 138.77 157.03 139.76 150.00 147.00
C 147.22 149.86 143.35 152.60 141.00 155.00
C 138.62 158.02 138.55 160.15 137.00 163.00
C 136.21 164.44 135.13 166.05 134.00 167.00
C 134.00 167.00 133.50 166.95 133.00 167.00
C 130.91 168.68 128.16 170.13 126.00 172.00
C 125.64 173.65 126.92 177.05 127.00 179.00
C 127.08 180.85 126.54 183.21 127.00 185.00
C 128.17 187.06 126.69 186.39 129.00 186.00
C 139.58 184.19 139.28 173.57 145.30 166.30 C 151.33 159.04 159.11 154.07 168.00 152.00
C 171.54 151.18 174.52 151.23 178.00 152.00
C 198.43 156.53 210.55 180.87 193.25 196.25 C 175.95 211.63 151.55 202.15 136.00 191.00
C 134.75 191.30 134.29 192.83 133.00 193.00
C 141.03 202.59 157.50 209.93 171.00 209.00
C 165.13 212.19 180.28 209.95 174.00 214.00
C 171.29 219.69 168.04 225.37 166.00 231.00
C 165.54 233.27 164.66 236.86 165.00 240.00
C 167.63 242.52 165.14 247.56 165.00 250.00
C 164.14 257.96 169.80 261.15 160.00 264.00
C 157.59 264.70 155.01 265.00 152.00 265.00
C 151.50 265.00 150.50 265.03 150.00 265.00
C 147.41 265.74 144.01 265.77 142.00 266.00
C 136.40 267.32 131.09 269.45 128.00 274.00
C 126.55 276.13 123.72 279.04 123.00 281.00
C 121.53 286.73 123.16 291.73 126.00 296.00
C 129.31 299.51 133.40 300.99 138.00 305.00
C 139.13 305.57 141.66 305.69 143.00 306.00
C 147.25 306.00 149.12 302.05 151.77 301.42 C 154.42 300.80 158.84 299.47 163.00 299.00
C 167.21 298.53 171.76 299.00 176.00 299.00
C 178.68 299.00 182.08 299.89 184.00 300.00
C 191.28 299.72 196.47 297.98 203.00 296.00
C 203.30 297.57 202.75 299.61 201.00 300.00
C 200.53 300.72 200.61 301.25 200.00 302.00
C 199.77 302.28 199.18 302.73 199.00 303.00
C 198.11 304.33 198.19 306.12 198.00 307.00
C 197.52 309.20 198.55 310.92 196.00 312.00
C 196.00 312.00 196.00 312.50 196.00 313.00
C 196.00 313.50 196.00 314.50 196.00 315.00
C 197.34 318.15 197.00 322.19 197.00 326.00
C 197.00 327.66 196.94 329.34 197.00 331.00
C 197.87 341.27 204.18 349.77 210.00 358.00
C 202.48 358.16 194.80 357.00 188.00 360.00
C 186.82 360.87 186.62 362.45 185.00 363.00
C 185.65 362.60 182.90 364.43 184.00 363.00
C 182.50 363.73 180.03 365.82 179.00 367.00
C 176.48 370.39 175.83 372.99 175.00 377.00
C 174.79 379.31 174.51 382.75 175.00 385.00
C 176.08 388.00 175.90 386.88 178.00 389.00
C 179.28 389.54 182.52 389.85 184.00 390.00
C 183.44 380.93 188.33 375.54 195.08 370.08 C 201.83 364.62 214.17 366.00 222.00 367.00 C 229.83 367.99 239.80 375.93 244.00 369.00
C 233.64 367.84 224.41 364.16 217.25 355.75 C 210.08 347.35 207.42 335.10 208.00 324.00 C 208.58 312.90 214.05 304.64 223.23 298.23 C 232.41 291.82 242.58 289.26 254.01 288.01 C 265.44 286.76 278.78 290.03 288.69 294.31 C 298.61 298.58 302.42 309.65 311.00 311.00
C 292.98 277.80 244.73 276.83 217.00 297.00
C 209.30 289.11 224.66 284.69 227.75 277.75 C 230.85 270.82 239.46 258.34 233.00 253.00
C 230.18 263.89 227.65 272.58 218.75 280.75 C 209.85 288.92 200.91 290.73 188.93 293.93 C 176.95 297.12 160.44 288.18 148.93 296.93 C 137.41 305.67 132.86 287.36 144.08 281.08 C 155.30 274.80 164.62 276.35 175.25 269.25 C 185.88 262.14 188.73 250.71 187.00 238.00
C 178.32 241.41 187.54 262.01 173.00 263.00
C 169.98 252.46 170.62 238.53 173.32 228.32 C 176.03 218.12 181.89 210.83 192.22 208.22 C 202.56 205.61 207.52 199.40 214.25 191.25 C 220.99 183.11 213.85 167.29 220.00 162.00
C 234.12 146.99 229.05 129.74 225.00 114.00
C 224.85 113.42 224.66 111.92 224.00 111.00
C 220.25 109.36 216.35 109.98 211.00 112.00
M 152.00 273.00 
C 142.20 277.14 131.12 280.81 131.00 294.00
C 120.27 284.76 135.80 269.65 147.00 272.00
C 149.02 271.71 152.29 270.15 155.00 271.00
C 154.45 272.01 153.02 272.44 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.19" fill="#000000">
|
||||
<path d="M 225.00 114.00 
C 229.89 122.33 229.91 132.23 232.00 142.00 C 234.08 151.77 221.37 159.93 223.93 168.07 C 226.48 176.22 228.75 187.50 232.00 194.00
C 235.40 198.53 239.35 201.24 244.00 204.00
C 237.83 198.83 233.59 191.20 231.33 183.67 C 229.06 176.15 224.84 166.83 229.08 160.08 C 233.32 153.32 236.40 141.18 233.78 133.22 C 231.15 125.27 231.64 114.34 224.00 111.00
C 224.66 111.92 224.85 113.42 225.00 114.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.09" fill="#B0B0B0">
|
||||
<path d="M 201.00 119.00 
C 204.34 116.85 207.73 113.57 211.00 112.00
C 206.52 113.70 204.06 116.44 201.00 119.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.48" fill="#000000">
|
||||
<path d="M 220.00 162.00 
C 219.66 166.74 225.00 173.64 225.32 178.68 C 225.64 183.72 229.13 190.18 232.00 194.00
C 228.75 187.50 226.48 176.22 223.93 168.07 C 221.37 159.93 234.08 151.77 232.00 142.00 C 229.91 132.23 229.89 122.33 225.00 114.00
C 229.05 129.74 234.12 146.99 220.00 162.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.99" fill="#BEBEBE">
|
||||
<path d="M 309.00 244.00 
C 319.62 252.72 326.67 266.01 332.31 278.69 C 337.94 291.38 343.27 312.99 343.00 327.00
C 348.86 307.69 357.88 289.27 376.00 278.00
C 375.67 274.36 376.25 270.64 376.00 267.00
C 374.20 240.59 358.93 215.59 343.00 195.00
C 335.15 187.46 327.36 178.80 319.00 172.00
C 311.02 165.51 302.27 161.02 293.75 155.25 C 285.22 149.49 276.61 146.55 268.08 139.92 C 259.55 133.29 246.17 127.28 248.00 141.00 C 249.83 154.72 251.48 162.65 256.69 173.31 C 261.91 183.96 266.05 189.82 272.70 198.30 C 279.34 206.78 283.96 213.48 290.75 221.25 C 297.53 229.03 302.67 236.14 309.00 244.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.13" fill="#858585">
|
||||
<path d="M 141.00 155.00 
C 143.35 152.60 147.22 149.86 150.00 147.00
C 148.11 148.46 143.80 151.45 141.00 155.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#868686">
|
||||
<path d="M 168.00 152.00 
C 171.08 151.66 174.91 151.68 178.00 152.00
C 174.52 151.23 171.54 151.18 168.00 152.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#F6F6F6">
|
||||
<path d="M 129.00 186.00 
C 129.32 186.68 128.94 188.24 129.00 189.00
C 129.45 190.21 132.14 191.97 133.00 193.00
C 134.29 192.83 134.75 191.30 136.00 191.00
C 151.55 202.15 175.95 211.63 193.25 196.25 C 210.55 180.87 198.43 156.53 178.00 152.00
C 174.91 151.68 171.08 151.66 168.00 152.00
C 159.11 154.07 151.33 159.04 145.30 166.30 C 139.28 173.57 139.58 184.19 129.00 186.00
M 175.00 161.00 
C 186.50 162.02 186.34 180.76 173.98 179.98 C 161.61 179.20 162.30 159.88 175.00 161.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#232323">
|
||||
<path d="M 175.00 161.00 
C 162.30 159.88 161.61 179.20 173.98 179.98 C 186.34 180.76 186.50 162.02 175.00 161.00
M 168.00 171.00 
C 167.10 158.05 181.72 171.06 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.16" fill="#A4A4A4">
|
||||
<path d="M 134.00 167.00 
C 135.13 166.05 136.21 164.44 137.00 163.00
C 137.08 162.88 134.67 165.13 134.00 167.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#F8F8F8">
|
||||
<path d="M 184.00 390.00 
C 200.03 391.64 215.88 388.64 232.00 390.00 C 248.12 391.37 264.82 388.01 281.00 390.00 C 297.18 391.99 307.70 387.80 321.00 381.00
C 334.96 366.76 337.54 345.00 343.00 327.00
C 343.27 312.99 337.94 291.38 332.31 278.69 C 326.67 266.01 319.62 252.72 309.00 244.00
C 288.30 227.00 266.37 217.28 244.00 204.00
C 239.35 201.24 235.40 198.53 232.00 194.00
C 229.13 190.18 225.64 183.72 225.32 178.68 C 225.00 173.64 219.66 166.74 220.00 162.00
C 213.85 167.29 220.99 183.11 214.25 191.25 C 207.52 199.40 202.56 205.61 192.22 208.22 C 181.89 210.83 176.03 218.12 173.32 228.32 C 170.62 238.53 169.98 252.46 173.00 263.00
C 187.54 262.01 178.32 241.41 187.00 238.00
C 188.73 250.71 185.88 262.14 175.25 269.25 C 164.62 276.35 155.30 274.80 144.08 281.08 C 132.86 287.36 137.41 305.67 148.93 296.93 C 160.44 288.18 176.95 297.12 188.93 293.93 C 200.91 290.73 209.85 288.92 218.75 280.75 C 227.65 272.58 230.18 263.89 233.00 253.00
C 239.46 258.34 230.85 270.82 227.75 277.75 C 224.66 284.69 209.30 289.11 217.00 297.00
C 244.73 276.83 292.98 277.80 311.00 311.00
C 302.42 309.65 298.61 298.58 288.69 294.31 C 278.78 290.03 265.44 286.76 254.01 288.01 C 242.58 289.26 232.41 291.82 223.23 298.23 C 214.05 304.64 208.58 312.90 208.00 324.00 C 207.42 335.10 210.08 347.35 217.25 355.75 C 224.41 364.16 233.64 367.84 244.00 369.00
C 239.80 375.93 229.83 367.99 222.00 367.00 C 214.17 366.00 201.83 364.62 195.08 370.08 C 188.33 375.54 183.44 380.93 184.00 390.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#E7E7E7">
|
||||
<path d="M 168.00 171.00 
C 181.72 171.06 167.10 158.05 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.06" fill="#D5D6D5">
|
||||
<path d="M 126.00 172.00 
C 128.16 170.13 130.91 168.68 133.00 167.00
C 130.73 167.21 126.57 169.37 126.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.17" fill="#010101">
|
||||
<path d="M 319.00 172.00 
C 327.36 178.80 335.15 187.46 343.00 195.00
C 349.36 201.12 355.23 207.97 360.00 215.00
C 351.42 199.09 333.96 180.10 319.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.19" fill="#6A6A6A">
|
||||
<path d="M 127.00 185.00 
C 126.54 183.21 127.08 180.85 127.00 179.00
C 124.69 178.33 127.20 174.74 125.00 174.00
C 124.95 177.75 125.33 182.05 127.00 185.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.26" fill="#000000">
|
||||
<path d="M 172.00 215.00 
C 172.45 214.90 173.63 214.23 174.00 214.00
C 180.28 209.95 165.13 212.19 171.00 209.00
C 157.50 209.93 141.03 202.59 133.00 193.00
C 132.14 191.97 129.45 190.21 129.00 189.00
C 129.72 198.14 139.50 203.40 146.92 206.08 C 154.33 208.76 163.08 216.93 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.33" fill="#000000">
|
||||
<path d="M 376.00 267.00 
C 376.19 267.61 376.87 267.94 377.00 269.00
C 382.28 281.94 376.22 302.91 373.67 315.67 C 371.13 328.44 363.16 341.06 357.08 352.08 C 351.00 363.10 341.62 372.42 331.00 379.00
C 336.37 377.44 343.03 372.51 348.25 366.25 C 353.47 359.98 358.20 353.62 362.23 346.23 C 366.25 338.84 369.84 331.25 373.02 323.02 C 376.21 314.80 378.11 304.40 379.22 295.22 C 380.34 286.05 381.18 272.54 378.33 263.67 C 375.48 254.80 374.87 244.63 371.77 236.23 C 368.67 227.83 364.03 222.47 360.00 215.00
C 355.23 207.97 349.36 201.12 343.00 195.00
C 358.93 215.59 374.20 240.59 376.00 267.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.17" fill="#8A8A8A">
|
||||
<path d="M 172.00 215.00 
C 168.52 218.81 166.97 226.20 166.00 231.00
C 168.04 225.37 171.29 219.69 174.00 214.00
C 173.63 214.23 172.45 214.90 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.60" fill="#101010">
|
||||
<path d="M 165.00 241.00 
C 165.00 243.67 165.00 246.33 165.00 249.00
C 165.00 249.00 165.05 249.51 165.00 250.00
C 165.14 247.56 167.63 242.52 165.00 240.00
C 165.00 240.00 165.00 240.50 165.00 241.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.03" fill="#7E7E7E">
|
||||
<path d="M 165.00 249.00 
C 165.00 246.33 165.00 243.67 165.00 241.00
C 163.50 243.03 163.47 246.93 165.00 249.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.04" fill="#777777">
|
||||
<path d="M 152.00 265.00 
C 155.01 265.00 157.59 264.70 160.00 264.00
C 157.04 264.47 154.50 263.94 152.00 265.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.29" fill="#7B7B7B">
|
||||
<path d="M 142.00 266.00 
C 144.01 265.77 147.41 265.74 150.00 265.00
C 147.06 264.79 144.65 265.37 142.00 266.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.68" fill="#4F4F4F">
|
||||
<path d="M 376.00 278.00 
C 376.36 277.78 377.00 278.00 377.00 278.00
C 377.06 275.10 377.35 271.88 377.00 269.00
C 376.87 267.94 376.19 267.61 376.00 267.00
C 376.25 270.64 375.67 274.36 376.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.46" fill="#000000">
|
||||
<path d="M 377.00 278.00 
C 376.24 316.06 358.69 361.73 321.00 381.00
C 307.70 387.80 297.18 391.99 281.00 390.00 C 264.82 388.01 248.12 391.37 232.00 390.00 C 215.88 388.64 200.03 391.64 184.00 390.00
C 182.52 389.85 179.28 389.54 178.00 389.00
C 182.87 396.59 191.77 393.68 200.00 394.00 C 208.23 394.32 218.57 393.90 227.00 394.00 C 235.43 394.10 244.63 394.09 253.00 394.00 C 261.37 393.91 271.99 394.30 280.00 394.00 C 288.01 393.70 299.78 394.82 306.78 393.78 C 313.78 392.73 320.86 388.48 320.00 385.00
C 324.03 383.93 327.43 381.21 331.00 379.00
C 341.62 372.42 351.00 363.10 357.08 352.08 C 363.16 341.06 371.13 328.44 373.67 315.67 C 376.22 302.91 382.28 281.94 377.00 269.00
C 377.35 271.88 377.06 275.10 377.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#767676">
|
||||
<path d="M 147.00 272.00 
C 148.46 272.02 151.35 273.02 152.00 273.00
C 153.02 272.44 154.45 272.01 155.00 271.00
C 152.29 270.15 149.02 271.71 147.00 272.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#E3E3E3">
|
||||
<path d="M 152.00 273.00 
C 151.35 273.02 148.46 272.02 147.00 272.00
C 135.80 269.65 120.27 284.76 131.00 294.00
C 131.12 280.81 142.20 277.14 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.20" fill="#8E8E8E">
|
||||
<path d="M 123.00 281.00 
C 123.72 279.04 126.55 276.13 128.00 274.00
C 125.91 276.29 123.82 277.81 123.00 281.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.99" fill="#1D1D1D">
|
||||
<path d="M 343.00 327.00 
C 337.54 345.00 334.96 366.76 321.00 381.00
C 358.69 361.73 376.24 316.06 377.00 278.00
C 377.00 278.00 376.36 277.78 376.00 278.00
C 357.88 289.27 348.86 307.69 343.00 327.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.32" fill="#000000">
|
||||
<path d="M 126.00 296.00 
C 130.06 302.11 136.93 309.06 145.00 309.00 C 153.07 308.94 160.96 301.21 170.00 303.00 C 179.04 304.79 191.11 300.89 199.00 303.00
C 199.18 302.73 199.77 302.28 200.00 302.00
C 197.55 298.03 190.28 303.63 184.00 302.00 C 177.72 300.37 175.48 302.79 166.00 302.00 C 156.52 301.21 146.75 312.62 138.00 305.00
C 133.40 300.99 129.31 299.51 126.00 296.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.59" fill="#010101">
|
||||
<path d="M 184.00 300.00 
C 181.34 300.10 178.66 300.00 176.00 300.00
C 171.88 300.00 166.28 298.95 163.00 299.00
C 158.84 299.47 154.42 300.80 151.77 301.42 C 149.12 302.05 147.25 306.00 143.00 306.00
C 147.64 307.07 150.96 305.59 155.08 303.08 C 159.19 300.56 165.50 300.79 170.00 301.00 C 174.50 301.21 183.31 300.15 187.00 301.00 C 190.69 301.85 200.07 297.39 201.00 300.00
C 202.75 299.61 203.30 297.57 203.00 296.00
C 196.47 297.98 191.28 299.72 184.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.75" fill="#474747">
|
||||
<path d="M 176.00 300.00 
C 175.96 300.00 176.00 299.00 176.00 299.00
C 171.76 299.00 167.21 298.53 163.00 299.00
C 166.28 298.95 171.88 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.77" fill="#090909">
|
||||
<path d="M 176.00 300.00 
C 178.66 300.00 181.34 300.10 184.00 300.00
C 182.08 299.89 178.68 299.00 176.00 299.00
C 176.00 299.00 175.96 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.44" fill="#000000">
|
||||
<path d="M 143.00 306.00 
C 141.66 305.69 139.13 305.57 138.00 305.00
C 146.75 312.62 156.52 301.21 166.00 302.00 C 175.48 302.79 177.72 300.37 184.00 302.00 C 190.28 303.63 197.55 298.03 200.00 302.00
C 200.61 301.25 200.53 300.72 201.00 300.00
C 200.07 297.39 190.69 301.85 187.00 301.00 C 183.31 300.15 174.50 301.21 170.00 301.00 C 165.50 300.79 159.19 300.56 155.08 303.08 C 150.96 305.59 147.64 307.07 143.00 306.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.29" fill="#757575">
|
||||
<path d="M 196.00 312.00 
C 198.55 310.92 197.52 309.20 198.00 307.00
C 198.29 306.24 196.30 310.37 196.00 312.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.03" fill="#AEAEAE">
|
||||
<path d="M 195.00 324.00 
C 195.00 324.05 196.00 324.00 196.00 324.00
C 195.64 321.10 196.00 317.92 196.00 315.00
C 196.00 314.50 196.00 313.50 196.00 313.00
C 194.25 315.36 194.72 320.78 195.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.66" fill="#111111">
|
||||
<path d="M 196.00 324.00 
C 196.15 325.26 196.76 325.23 197.00 326.00
C 197.00 322.19 197.34 318.15 196.00 315.00
C 196.00 317.92 195.64 321.10 196.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.26" fill="#484848">
|
||||
<path d="M 197.00 331.00 
C 196.94 329.34 197.00 327.66 197.00 326.00
C 196.76 325.23 196.15 325.26 196.00 324.00
C 196.00 324.00 195.00 324.05 195.00 324.00
C 195.21 326.36 196.59 329.08 197.00 331.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.11" fill="#B4B4B4">
|
||||
<path d="M 179.00 367.00 
C 180.03 365.82 182.50 363.73 184.00 363.00
C 184.40 362.80 185.00 363.00 185.00 363.00
C 186.62 362.45 186.82 360.87 188.00 360.00
C 184.21 361.67 181.54 363.59 179.00 367.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.78" fill="#0E0E0E">
|
||||
<path d="M 185.00 363.00 
C 185.00 363.00 184.40 362.80 184.00 363.00
C 182.90 364.43 185.65 362.60 185.00 363.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.20" fill="#868686">
|
||||
<path d="M 175.00 385.00 
C 174.51 382.75 174.79 379.31 175.00 377.00
C 174.40 379.91 173.90 381.94 175.00 385.00 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 42 KiB |
207
src/public/logo_square.svg
Normal file
207
src/public/logo_square.svg
Normal file
@ -0,0 +1,207 @@
|
||||
<svg viewBox="122 107 263 292" xmlns="http://www.w3.org/2000/svg" style="max-height: 500px" width="295" height="295">
|
||||
<g stroke-width="0.3" stroke-opacity="0.00" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 0.00 500.00 
L 500.00 500.00
L 500.00 0.00
L 0.00 0.00
L 0.00 500.00
M 224.00 111.00 
C 231.64 114.34 231.15 125.27 233.78 133.22 C 236.40 141.18 233.32 153.32 229.08 160.08 C 224.84 166.83 229.06 176.15 231.33 183.67 C 233.59 191.20 237.83 198.83 244.00 204.00
C 266.37 217.28 288.30 227.00 309.00 244.00
C 302.67 236.14 297.53 229.03 290.75 221.25 C 283.96 213.48 279.34 206.78 272.70 198.30 C 266.05 189.82 261.91 183.96 256.69 173.31 C 251.48 162.65 249.83 154.72 248.00 141.00 C 246.17 127.28 259.55 133.29 268.08 139.92 C 276.61 146.55 285.22 149.49 293.75 155.25 C 302.27 161.02 311.02 165.51 319.00 172.00
C 333.96 180.10 351.42 199.09 360.00 215.00
C 364.03 222.47 368.67 227.83 371.77 236.23 C 374.87 244.63 375.48 254.80 378.33 263.67 C 381.18 272.54 380.34 286.05 379.22 295.22 C 378.11 304.40 376.21 314.80 373.02 323.02 C 369.84 331.25 366.25 338.84 362.23 346.23 C 358.20 353.62 353.47 359.98 348.25 366.25 C 343.03 372.51 336.37 377.44 331.08 379.86
C 327.43 381.21 324.03 383.93 320.00 385.00
C 320.86 388.48 313.78 392.73 306.78 393.78 C 299.78 394.82 288.01 393.70 280.00 394.00 C 271.99 394.30 261.37 393.91 253.00 394.00 C 244.63 394.09 235.43 394.10 227.00 394.00 C 218.57 393.90 208.23 394.32 200.00 394.00 C 191.77 393.68 182.87 396.59 178.00 389.00
C 175.90 386.88 176.08 388.00 175.00 385.00
C 173.90 381.94 174.40 379.91 175.00 377.00
C 175.83 372.99 176.48 370.39 179.00 367.00
C 181.54 363.59 184.21 361.67 188.00 360.00
C 194.80 357.00 202.48 358.16 210.00 358.00
C 204.18 349.77 197.87 341.27 197.00 331.00
C 196.59 329.08 195.21 326.36 195.00 324.00
C 194.72 320.78 194.25 315.36 196.00 313.00
C 196.00 312.50 196.00 312.00 196.00 312.00
C 196.30 310.37 198.29 306.24 198.00 307.00
C 198.19 306.12 198.11 304.33 199.00 303.00
C 191.11 300.89 179.04 304.79 170.00 303.00 C 160.96 301.21 153.07 308.94 145.00 309.00 C 136.93 309.06 130.06 302.11 126.00 296.00
C 123.16 291.73 121.53 286.73 123.00 281.00
C 123.82 277.81 125.91 276.29 128.00 274.00
C 131.09 269.45 136.40 267.32 142.00 266.00
C 144.65 265.37 147.06 264.79 150.00 265.00
C 150.50 265.03 151.50 265.00 152.00 265.00
C 154.50 263.94 157.04 264.47 160.00 264.00
C 169.80 261.15 164.14 257.96 165.00 250.00
C 165.05 249.51 165.00 249.00 165.00 249.00
C 163.47 246.93 163.50 243.03 165.00 241.00
C 165.00 240.50 165.00 240.00 165.00 240.00
C 164.66 236.86 165.54 233.27 166.00 231.00
C 166.97 226.20 168.52 218.81 172.00 215.00
C 163.08 216.93 154.33 208.76 146.92 206.08 C 139.50 203.40 129.72 198.14 129.00 189.00
C 128.94 188.24 129.32 186.68 129.00 186.00
C 126.69 186.39 128.17 187.06 127.00 185.00
C 125.33 182.05 124.95 177.75 125.00 174.00
C 127.20 174.74 124.69 178.33 127.00 179.00
C 126.92 177.05 125.64 173.65 126.00 172.00
C 126.57 169.37 130.73 167.21 133.00 167.00
C 133.50 166.95 134.00 167.00 134.00 167.00
C 134.67 165.13 137.08 162.88 137.00 163.00
C 138.55 160.15 138.62 158.02 141.00 155.00
C 143.80 151.45 148.11 148.46 150.00 147.00
C 157.03 139.76 167.00 138.77 177.01 136.01 C 187.01 133.25 192.68 124.37 201.00 119.00
C 204.06 116.44 206.52 113.70 211.00 112.00
C 216.35 109.98 220.25 109.36 224.00 111.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.97" stroke="#1F1F1F" fill-opacity="0.0" fill="None"><path d="M 211.00 112.00 
C 207.73 113.57 204.34 116.85 201.00 119.00
C 192.68 124.37 187.01 133.25 177.01 136.01 C 167.00 138.77 157.03 139.76 150.00 147.00
C 147.22 149.86 143.35 152.60 141.00 155.00
C 138.62 158.02 138.55 160.15 137.00 163.00
C 136.21 164.44 135.13 166.05 134.00 167.00
C 134.00 167.00 133.50 166.95 133.00 167.00
C 130.91 168.68 128.16 170.13 126.00 172.00
C 125.64 173.65 126.92 177.05 127.00 179.00
C 127.08 180.85 126.54 183.21 127.00 185.00
C 128.17 187.06 126.69 186.39 129.00 186.00
C 139.58 184.19 139.28 173.57 145.30 166.30 C 151.33 159.04 159.11 154.07 168.00 152.00
C 171.54 151.18 174.52 151.23 178.00 152.00
C 198.43 156.53 210.55 180.87 193.25 196.25 C 175.95 211.63 151.55 202.15 136.00 191.00
C 134.75 191.30 134.29 192.83 133.00 193.00
C 141.03 202.59 157.50 209.93 171.00 209.00
C 165.13 212.19 180.28 209.95 174.00 214.00
C 171.29 219.69 168.04 225.37 166.00 231.00
C 165.54 233.27 164.66 236.86 165.00 240.00
C 167.63 242.52 165.14 247.56 165.00 250.00
C 164.14 257.96 169.80 261.15 160.00 264.00
C 157.59 264.70 155.01 265.00 152.00 265.00
C 151.50 265.00 150.50 265.03 150.00 265.00
C 147.41 265.74 144.01 265.77 142.00 266.00
C 136.40 267.32 131.09 269.45 128.00 274.00
C 126.55 276.13 123.72 279.04 123.00 281.00
C 121.53 286.73 123.16 291.73 126.00 296.00
C 129.31 299.51 133.40 300.99 138.00 305.00
C 139.13 305.57 141.66 305.69 143.00 306.00
C 147.25 306.00 149.12 302.05 151.77 301.42 C 154.42 300.80 158.84 299.47 163.00 299.00
C 167.21 298.53 171.76 299.00 176.00 299.00
C 178.68 299.00 182.08 299.89 184.00 300.00
C 191.28 299.72 196.47 297.98 203.00 296.00
C 203.30 297.57 202.75 299.61 201.00 300.00
C 200.53 300.72 200.61 301.25 200.00 302.00
C 199.77 302.28 199.18 302.73 199.00 303.00
C 198.11 304.33 198.19 306.12 198.00 307.00
C 197.52 309.20 198.55 310.92 196.00 312.00
C 196.00 312.00 196.00 312.50 196.00 313.00
C 196.00 313.50 196.00 314.50 196.00 315.00
C 197.34 318.15 197.00 322.19 197.00 326.00
C 197.00 327.66 196.94 329.34 197.00 331.00
C 197.87 341.27 204.18 349.77 210.00 358.00
C 202.48 358.16 194.80 357.00 188.00 360.00
C 186.82 360.87 186.62 362.45 185.00 363.00
C 185.65 362.60 182.90 364.43 184.00 363.00
C 182.50 363.73 180.03 365.82 179.00 367.00
C 176.48 370.39 175.83 372.99 175.00 377.00
C 174.79 379.31 174.51 382.75 175.00 385.00
C 176.08 388.00 175.90 386.88 178.00 389.00
C 179.28 389.54 182.52 389.85 184.00 390.00
C 183.44 380.93 188.33 375.54 195.08 370.08 C 201.83 364.62 214.17 366.00 222.00 367.00 C 229.83 367.99 239.80 375.93 244.00 369.00
C 233.64 367.84 224.41 364.16 217.25 355.75 C 210.08 347.35 207.42 335.10 208.00 324.00 C 208.58 312.90 214.05 304.64 223.23 298.23 C 232.41 291.82 242.58 289.26 254.01 288.01 C 265.44 286.76 278.78 290.03 288.69 294.31 C 298.61 298.58 302.42 309.65 311.00 311.00
C 292.98 277.80 244.73 276.83 217.00 297.00
C 209.30 289.11 224.66 284.69 227.75 277.75 C 230.85 270.82 239.46 258.34 233.00 253.00
C 230.18 263.89 227.65 272.58 218.75 280.75 C 209.85 288.92 200.91 290.73 188.93 293.93 C 176.95 297.12 160.44 288.18 148.93 296.93 C 137.41 305.67 132.86 287.36 144.08 281.08 C 155.30 274.80 164.62 276.35 175.25 269.25 C 185.88 262.14 188.73 250.71 187.00 238.00
C 178.32 241.41 187.54 262.01 173.00 263.00
C 169.98 252.46 170.62 238.53 173.32 228.32 C 176.03 218.12 181.89 210.83 192.22 208.22 C 202.56 205.61 207.52 199.40 214.25 191.25 C 220.99 183.11 213.85 167.29 220.00 162.00
C 234.12 146.99 229.05 129.74 225.00 114.00
C 224.85 113.42 224.66 111.92 224.00 111.00
C 220.25 109.36 216.35 109.98 211.00 112.00
M 152.00 273.00 
C 142.20 277.14 131.12 280.81 131.00 294.00
C 120.27 284.76 135.80 269.65 147.00 272.00
C 149.02 271.71 152.29 270.15 155.00 271.00
C 154.45 272.01 153.02 272.44 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.19" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 225.00 114.00 
C 229.89 122.33 229.91 132.23 232.00 142.00 C 234.08 151.77 221.37 159.93 223.93 168.07 C 226.48 176.22 228.75 187.50 232.00 194.00
C 235.40 198.53 239.35 201.24 244.00 204.00
C 237.83 198.83 233.59 191.20 231.33 183.67 C 229.06 176.15 224.84 166.83 229.08 160.08 C 233.32 153.32 236.40 141.18 233.78 133.22 C 231.15 125.27 231.64 114.34 224.00 111.00
C 224.66 111.92 224.85 113.42 225.00 114.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.09" stroke="#B0B0B0" fill-opacity="0.0" fill="None"><path d="M 201.00 119.00 
C 204.34 116.85 207.73 113.57 211.00 112.00
C 206.52 113.70 204.06 116.44 201.00 119.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.48" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 220.00 162.00 
C 219.66 166.74 225.00 173.64 225.32 178.68 C 225.64 183.72 229.13 190.18 232.00 194.00
C 228.75 187.50 226.48 176.22 223.93 168.07 C 221.37 159.93 234.08 151.77 232.00 142.00 C 229.91 132.23 229.89 122.33 225.00 114.00
C 229.05 129.74 234.12 146.99 220.00 162.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.99" stroke="#BEBEBE" fill-opacity="0.0" fill="None"><path d="M 309.00 244.00 
C 319.62 252.72 326.67 266.01 332.31 278.69 C 337.94 291.38 343.27 312.99 343.00 327.00
C 348.86 307.69 357.88 289.27 376.00 278.00
C 375.67 274.36 376.25 270.64 376.00 267.00
C 374.20 240.59 358.93 215.59 343.00 195.00
C 335.15 187.46 327.36 178.80 319.00 172.00
C 311.02 165.51 302.27 161.02 293.75 155.25 C 285.22 149.49 276.61 146.55 268.08 139.92 C 259.55 133.29 246.17 127.28 248.00 141.00 C 249.83 154.72 251.48 162.65 256.69 173.31 C 261.91 183.96 266.05 189.82 272.70 198.30 C 279.34 206.78 283.96 213.48 290.75 221.25 C 297.53 229.03 302.67 236.14 309.00 244.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.13" stroke="#858585" fill-opacity="0.0" fill="None"><path d="M 141.00 155.00 
C 143.35 152.60 147.22 149.86 150.00 147.00
C 148.11 148.46 143.80 151.45 141.00 155.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#868686" fill-opacity="0.0" fill="None"><path d="M 168.00 152.00 
C 171.08 151.66 174.91 151.68 178.00 152.00
C 174.52 151.23 171.54 151.18 168.00 152.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#F6F6F6" fill-opacity="0.0" fill="None"><path d="M 129.00 186.00 
C 129.32 186.68 128.94 188.24 129.00 189.00
C 129.45 190.21 132.14 191.97 133.00 193.00
C 134.29 192.83 134.75 191.30 136.00 191.00
C 151.55 202.15 175.95 211.63 193.25 196.25 C 210.55 180.87 198.43 156.53 178.00 152.00
C 174.91 151.68 171.08 151.66 168.00 152.00
C 159.11 154.07 151.33 159.04 145.30 166.30 C 139.28 173.57 139.58 184.19 129.00 186.00
M 175.00 161.00 
C 186.50 162.02 186.34 180.76 173.98 179.98 C 161.61 179.20 162.30 159.88 175.00 161.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#232323" fill-opacity="0.0" fill="None"><path d="M 175.00 161.00 
C 162.30 159.88 161.61 179.20 173.98 179.98 C 186.34 180.76 186.50 162.02 175.00 161.00
M 168.00 171.00 
C 167.10 158.05 181.72 171.06 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.16" stroke="#A4A4A4" fill-opacity="0.0" fill="None"><path d="M 134.00 167.00 
C 135.13 166.05 136.21 164.44 137.00 163.00
C 137.08 162.88 134.67 165.13 134.00 167.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#F8F8F8" fill-opacity="0.0" fill="None"><path d="M 184.00 390.00 
C 200.03 391.64 215.88 388.64 232.00 390.00 C 248.12 391.37 264.82 388.01 281.00 390.00 C 297.18 391.99 307.70 387.80 321.00 381.00
C 334.96 366.76 337.54 345.00 343.00 327.00
C 343.27 312.99 337.94 291.38 332.31 278.69 C 326.67 266.01 319.62 252.72 309.00 244.00
C 288.30 227.00 266.37 217.28 244.00 204.00
C 239.35 201.24 235.40 198.53 232.00 194.00
C 229.13 190.18 225.64 183.72 225.32 178.68 C 225.00 173.64 219.66 166.74 220.00 162.00
C 213.85 167.29 220.99 183.11 214.25 191.25 C 207.52 199.40 202.56 205.61 192.22 208.22 C 181.89 210.83 176.03 218.12 173.32 228.32 C 170.62 238.53 169.98 252.46 173.00 263.00
C 187.54 262.01 178.32 241.41 187.00 238.00
C 188.73 250.71 185.88 262.14 175.25 269.25 C 164.62 276.35 155.30 274.80 144.08 281.08 C 132.86 287.36 137.41 305.67 148.93 296.93 C 160.44 288.18 176.95 297.12 188.93 293.93 C 200.91 290.73 209.85 288.92 218.75 280.75 C 227.65 272.58 230.18 263.89 233.00 253.00
C 239.46 258.34 230.85 270.82 227.75 277.75 C 224.66 284.69 209.30 289.11 217.00 297.00
C 244.73 276.83 292.98 277.80 311.00 311.00
C 302.42 309.65 298.61 298.58 288.69 294.31 C 278.78 290.03 265.44 286.76 254.01 288.01 C 242.58 289.26 232.41 291.82 223.23 298.23 C 214.05 304.64 208.58 312.90 208.00 324.00 C 207.42 335.10 210.08 347.35 217.25 355.75 C 224.41 364.16 233.64 367.84 244.00 369.00
C 239.80 375.93 229.83 367.99 222.00 367.00 C 214.17 366.00 201.83 364.62 195.08 370.08 C 188.33 375.54 183.44 380.93 184.00 390.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#E7E7E7" fill-opacity="0.0" fill="None"><path d="M 168.00 171.00 
C 181.72 171.06 167.10 158.05 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.06" stroke="#D5D6D5" fill-opacity="0.0" fill="None"><path d="M 126.00 172.00 
C 128.16 170.13 130.91 168.68 133.00 167.00
C 130.73 167.21 126.57 169.37 126.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.17" stroke="#010101" fill-opacity="0.0" fill="None"><path d="M 319.00 172.00 
C 327.36 178.80 335.15 187.46 343.00 195.00
C 349.36 201.12 355.23 207.97 360.00 215.00
C 351.42 199.09 333.96 180.10 319.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.19" stroke="#6A6A6A" fill-opacity="0.0" fill="None"><path d="M 127.00 185.00 
C 126.54 183.21 127.08 180.85 127.00 179.00
C 124.69 178.33 127.20 174.74 125.00 174.00
C 124.95 177.75 125.33 182.05 127.00 185.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.26" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 172.00 215.00 
C 172.45 214.90 173.63 214.23 174.00 214.00
C 180.28 209.95 165.13 212.19 171.00 209.00
C 157.50 209.93 141.03 202.59 133.00 193.00
C 132.14 191.97 129.45 190.21 129.00 189.00
C 129.72 198.14 139.50 203.40 146.92 206.08 C 154.33 208.76 163.08 216.93 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.33" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 376.00 267.00 
C 376.19 267.61 376.87 267.94 377.00 269.00
C 382.28 281.94 376.22 302.91 373.67 315.67 C 371.13 328.44 363.16 341.06 357.08 352.08 C 351.00 363.10 341.62 372.42 331.00 379.00
C 336.37 377.44 343.03 372.51 348.25 366.25 C 353.47 359.98 358.20 353.62 362.23 346.23 C 366.25 338.84 369.84 331.25 373.02 323.02 C 376.21 314.80 378.11 304.40 379.22 295.22 C 380.34 286.05 381.18 272.54 378.33 263.67 C 375.48 254.80 374.87 244.63 371.77 236.23 C 368.67 227.83 364.03 222.47 360.00 215.00
C 355.23 207.97 349.36 201.12 343.00 195.00
C 358.93 215.59 374.20 240.59 376.00 267.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.17" stroke="#8A8A8A" fill-opacity="0.0" fill="None"><path d="M 172.00 215.00 
C 168.52 218.81 166.97 226.20 166.00 231.00
C 168.04 225.37 171.29 219.69 174.00 214.00
C 173.63 214.23 172.45 214.90 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.60" stroke="#101010" fill-opacity="0.0" fill="None"><path d="M 165.00 241.00 
C 165.00 243.67 165.00 246.33 165.00 249.00
C 165.00 249.00 165.05 249.51 165.00 250.00
C 165.14 247.56 167.63 242.52 165.00 240.00
C 165.00 240.00 165.00 240.50 165.00 241.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.03" stroke="#7E7E7E" fill-opacity="0.0" fill="None"><path d="M 165.00 249.00 
C 165.00 246.33 165.00 243.67 165.00 241.00
C 163.50 243.03 163.47 246.93 165.00 249.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.04" stroke="#777777" fill-opacity="0.0" fill="None"><path d="M 152.00 265.00 
C 155.01 265.00 157.59 264.70 160.00 264.00
C 157.04 264.47 154.50 263.94 152.00 265.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.29" stroke="#7B7B7B" fill-opacity="0.0" fill="None"><path d="M 142.00 266.00 
C 144.01 265.77 147.41 265.74 150.00 265.00
C 147.06 264.79 144.65 265.37 142.00 266.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.68" stroke="#4F4F4F" fill-opacity="0.0" fill="None"><path d="M 376.00 278.00 
C 376.36 277.78 377.00 278.00 377.00 278.00
C 377.06 275.10 377.35 271.88 377.00 269.00
C 376.87 267.94 376.19 267.61 376.00 267.00
C 376.25 270.64 375.67 274.36 376.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.46" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 377.00 278.00 
C 376.24 316.06 358.69 361.73 321.00 381.00
C 307.70 387.80 297.18 391.99 281.00 390.00 C 264.82 388.01 248.12 391.37 232.00 390.00 C 215.88 388.64 200.03 391.64 184.00 390.00
C 182.52 389.85 179.28 389.54 178.00 389.00
C 182.87 396.59 191.77 393.68 200.00 394.00 C 208.23 394.32 218.57 393.90 227.00 394.00 C 235.43 394.10 244.63 394.09 253.00 394.00 C 261.37 393.91 271.99 394.30 280.00 394.00 C 288.01 393.70 299.78 394.82 306.78 393.78 C 313.78 392.73 320.86 388.48 320.00 385.00
C 324.03 383.93 327.43 381.21 331.00 379.00
C 341.62 372.42 351.00 363.10 357.08 352.08 C 363.16 341.06 371.13 328.44 373.67 315.67 C 376.22 302.91 382.28 281.94 377.00 269.00
C 377.35 271.88 377.06 275.10 377.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#767676" fill-opacity="0.0" fill="None"><path d="M 147.00 272.00 
C 148.46 272.02 151.35 273.02 152.00 273.00
C 153.02 272.44 154.45 272.01 155.00 271.00
C 152.29 270.15 149.02 271.71 147.00 272.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="1.00" stroke="#E3E3E3" fill-opacity="0.0" fill="None"><path d="M 152.00 273.00 
C 151.35 273.02 148.46 272.02 147.00 272.00
C 135.80 269.65 120.27 284.76 131.00 294.00
C 131.12 280.81 142.20 277.14 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.20" stroke="#8E8E8E" fill-opacity="0.0" fill="None"><path d="M 123.00 281.00 
C 123.72 279.04 126.55 276.13 128.00 274.00
C 125.91 276.29 123.82 277.81 123.00 281.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.99" stroke="#1D1D1D" fill-opacity="0.0" fill="None"><path d="M 343.00 327.00 
C 337.54 345.00 334.96 366.76 321.00 381.00
C 358.69 361.73 376.24 316.06 377.00 278.00
C 377.00 278.00 376.36 277.78 376.00 278.00
C 357.88 289.27 348.86 307.69 343.00 327.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.32" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 126.00 296.00 
C 130.06 302.11 136.93 309.06 145.00 309.00 C 153.07 308.94 160.96 301.21 170.00 303.00 C 179.04 304.79 191.11 300.89 199.00 303.00
C 199.18 302.73 199.77 302.28 200.00 302.00
C 197.55 298.03 190.28 303.63 184.00 302.00 C 177.72 300.37 175.48 302.79 166.00 302.00 C 156.52 301.21 146.75 312.62 138.00 305.00
C 133.40 300.99 129.31 299.51 126.00 296.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.59" stroke="#010101" fill-opacity="0.0" fill="None"><path d="M 184.00 300.00 
C 181.34 300.10 178.66 300.00 176.00 300.00
C 171.88 300.00 166.28 298.95 163.00 299.00
C 158.84 299.47 154.42 300.80 151.77 301.42 C 149.12 302.05 147.25 306.00 143.00 306.00
C 147.64 307.07 150.96 305.59 155.08 303.08 C 159.19 300.56 165.50 300.79 170.00 301.00 C 174.50 301.21 183.31 300.15 187.00 301.00 C 190.69 301.85 200.07 297.39 201.00 300.00
C 202.75 299.61 203.30 297.57 203.00 296.00
C 196.47 297.98 191.28 299.72 184.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.75" stroke="#474747" fill-opacity="0.0" fill="None"><path d="M 176.00 300.00 
C 175.96 300.00 176.00 299.00 176.00 299.00
C 171.76 299.00 167.21 298.53 163.00 299.00
C 166.28 298.95 171.88 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.77" stroke="#090909" fill-opacity="0.0" fill="None"><path d="M 176.00 300.00 
C 178.66 300.00 181.34 300.10 184.00 300.00
C 182.08 299.89 178.68 299.00 176.00 299.00
C 176.00 299.00 175.96 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.44" stroke="#000000" fill-opacity="0.0" fill="None"><path d="M 143.00 306.00 
C 141.66 305.69 139.13 305.57 138.00 305.00
C 146.75 312.62 156.52 301.21 166.00 302.00 C 175.48 302.79 177.72 300.37 184.00 302.00 C 190.28 303.63 197.55 298.03 200.00 302.00
C 200.61 301.25 200.53 300.72 201.00 300.00
C 200.07 297.39 190.69 301.85 187.00 301.00 C 183.31 300.15 174.50 301.21 170.00 301.00 C 165.50 300.79 159.19 300.56 155.08 303.08 C 150.96 305.59 147.64 307.07 143.00 306.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.29" stroke="#757575" fill-opacity="0.0" fill="None"><path d="M 196.00 312.00 
C 198.55 310.92 197.52 309.20 198.00 307.00
C 198.29 306.24 196.30 310.37 196.00 312.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.03" stroke="#AEAEAE" fill-opacity="0.0" fill="None"><path d="M 195.00 324.00 
C 195.00 324.05 196.00 324.00 196.00 324.00
C 195.64 321.10 196.00 317.92 196.00 315.00
C 196.00 314.50 196.00 313.50 196.00 313.00
C 194.25 315.36 194.72 320.78 195.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.66" stroke="#111111" fill-opacity="0.0" fill="None"><path d="M 196.00 324.00 
C 196.15 325.26 196.76 325.23 197.00 326.00
C 197.00 322.19 197.34 318.15 196.00 315.00
C 196.00 317.92 195.64 321.10 196.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.26" stroke="#484848" fill-opacity="0.0" fill="None"><path d="M 197.00 331.00 
C 196.94 329.34 197.00 327.66 197.00 326.00
C 196.76 325.23 196.15 325.26 196.00 324.00
C 196.00 324.00 195.00 324.05 195.00 324.00
C 195.21 326.36 196.59 329.08 197.00 331.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.11" stroke="#B4B4B4" fill-opacity="0.0" fill="None"><path d="M 179.00 367.00 
C 180.03 365.82 182.50 363.73 184.00 363.00
C 184.40 362.80 185.00 363.00 185.00 363.00
C 186.62 362.45 186.82 360.87 188.00 360.00
C 184.21 361.67 181.54 363.59 179.00 367.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.78" stroke="#0E0E0E" fill-opacity="0.0" fill="None"><path d="M 185.00 363.00 
C 185.00 363.00 184.40 362.80 184.00 363.00
C 182.90 364.43 185.65 362.60 185.00 363.00 Z"/>
|
||||
</g>
|
||||
<g stroke-width="0.3" stroke-opacity="0.20" stroke="#868686" fill-opacity="0.0" fill="None"><path d="M 175.00 385.00 
C 174.51 382.75 174.79 379.31 175.00 377.00
C 174.40 379.91 173.90 381.94 175.00 385.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.00" fill="#000000">
|
||||
<path d="M 0.00 500.00 
L 500.00 500.00
L 500.00 0.00
L 0.00 0.00
L 0.00 500.00
M 224.00 111.00 
C 231.64 114.34 231.15 125.27 233.78 133.22 C 236.40 141.18 233.32 153.32 229.08 160.08 C 224.84 166.83 229.06 176.15 231.33 183.67 C 233.59 191.20 237.83 198.83 244.00 204.00
C 266.37 217.28 288.30 227.00 309.00 244.00
C 302.67 236.14 297.53 229.03 290.75 221.25 C 283.96 213.48 279.34 206.78 272.70 198.30 C 266.05 189.82 261.91 183.96 256.69 173.31 C 251.48 162.65 249.83 154.72 248.00 141.00 C 246.17 127.28 259.55 133.29 268.08 139.92 C 276.61 146.55 285.22 149.49 293.75 155.25 C 302.27 161.02 311.02 165.51 319.00 172.00
C 333.96 180.10 351.42 199.09 360.00 215.00
C 364.03 222.47 368.67 227.83 371.77 236.23 C 374.87 244.63 375.48 254.80 378.33 263.67 C 381.18 272.54 380.34 286.05 379.22 295.22 C 378.11 304.40 376.21 314.80 373.02 323.02 C 369.84 331.25 366.25 338.84 362.23 346.23 C 358.20 353.62 353.47 359.98 348.25 366.25 C 343.03 372.51 336.37 377.44 331.08 379.86
C 327.43 381.21 324.03 383.93 320.00 385.00
C 320.86 388.48 313.78 392.73 306.78 393.78 C 299.78 394.82 288.01 393.70 280.00 394.00 C 271.99 394.30 261.37 393.91 253.00 394.00 C 244.63 394.09 235.43 394.10 227.00 394.00 C 218.57 393.90 208.23 394.32 200.00 394.00 C 191.77 393.68 182.87 396.59 178.00 389.00
C 175.90 386.88 176.08 388.00 175.00 385.00
C 173.90 381.94 174.40 379.91 175.00 377.00
C 175.83 372.99 176.48 370.39 179.00 367.00
C 181.54 363.59 184.21 361.67 188.00 360.00
C 194.80 357.00 202.48 358.16 210.00 358.00
C 204.18 349.77 197.87 341.27 197.00 331.00
C 196.59 329.08 195.21 326.36 195.00 324.00
C 194.72 320.78 194.25 315.36 196.00 313.00
C 196.00 312.50 196.00 312.00 196.00 312.00
C 196.30 310.37 198.29 306.24 198.00 307.00
C 198.19 306.12 198.11 304.33 199.00 303.00
C 191.11 300.89 179.04 304.79 170.00 303.00 C 160.96 301.21 153.07 308.94 145.00 309.00 C 136.93 309.06 130.06 302.11 126.00 296.00
C 123.16 291.73 121.53 286.73 123.00 281.00
C 123.82 277.81 125.91 276.29 128.00 274.00
C 131.09 269.45 136.40 267.32 142.00 266.00
C 144.65 265.37 147.06 264.79 150.00 265.00
C 150.50 265.03 151.50 265.00 152.00 265.00
C 154.50 263.94 157.04 264.47 160.00 264.00
C 169.80 261.15 164.14 257.96 165.00 250.00
C 165.05 249.51 165.00 249.00 165.00 249.00
C 163.47 246.93 163.50 243.03 165.00 241.00
C 165.00 240.50 165.00 240.00 165.00 240.00
C 164.66 236.86 165.54 233.27 166.00 231.00
C 166.97 226.20 168.52 218.81 172.00 215.00
C 163.08 216.93 154.33 208.76 146.92 206.08 C 139.50 203.40 129.72 198.14 129.00 189.00
C 128.94 188.24 129.32 186.68 129.00 186.00
C 126.69 186.39 128.17 187.06 127.00 185.00
C 125.33 182.05 124.95 177.75 125.00 174.00
C 127.20 174.74 124.69 178.33 127.00 179.00
C 126.92 177.05 125.64 173.65 126.00 172.00
C 126.57 169.37 130.73 167.21 133.00 167.00
C 133.50 166.95 134.00 167.00 134.00 167.00
C 134.67 165.13 137.08 162.88 137.00 163.00
C 138.55 160.15 138.62 158.02 141.00 155.00
C 143.80 151.45 148.11 148.46 150.00 147.00
C 157.03 139.76 167.00 138.77 177.01 136.01 C 187.01 133.25 192.68 124.37 201.00 119.00
C 204.06 116.44 206.52 113.70 211.00 112.00
C 216.35 109.98 220.25 109.36 224.00 111.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.97" fill="#1F1F1F">
|
||||
<path d="M 211.00 112.00 
C 207.73 113.57 204.34 116.85 201.00 119.00
C 192.68 124.37 187.01 133.25 177.01 136.01 C 167.00 138.77 157.03 139.76 150.00 147.00
C 147.22 149.86 143.35 152.60 141.00 155.00
C 138.62 158.02 138.55 160.15 137.00 163.00
C 136.21 164.44 135.13 166.05 134.00 167.00
C 134.00 167.00 133.50 166.95 133.00 167.00
C 130.91 168.68 128.16 170.13 126.00 172.00
C 125.64 173.65 126.92 177.05 127.00 179.00
C 127.08 180.85 126.54 183.21 127.00 185.00
C 128.17 187.06 126.69 186.39 129.00 186.00
C 139.58 184.19 139.28 173.57 145.30 166.30 C 151.33 159.04 159.11 154.07 168.00 152.00
C 171.54 151.18 174.52 151.23 178.00 152.00
C 198.43 156.53 210.55 180.87 193.25 196.25 C 175.95 211.63 151.55 202.15 136.00 191.00
C 134.75 191.30 134.29 192.83 133.00 193.00
C 141.03 202.59 157.50 209.93 171.00 209.00
C 165.13 212.19 180.28 209.95 174.00 214.00
C 171.29 219.69 168.04 225.37 166.00 231.00
C 165.54 233.27 164.66 236.86 165.00 240.00
C 167.63 242.52 165.14 247.56 165.00 250.00
C 164.14 257.96 169.80 261.15 160.00 264.00
C 157.59 264.70 155.01 265.00 152.00 265.00
C 151.50 265.00 150.50 265.03 150.00 265.00
C 147.41 265.74 144.01 265.77 142.00 266.00
C 136.40 267.32 131.09 269.45 128.00 274.00
C 126.55 276.13 123.72 279.04 123.00 281.00
C 121.53 286.73 123.16 291.73 126.00 296.00
C 129.31 299.51 133.40 300.99 138.00 305.00
C 139.13 305.57 141.66 305.69 143.00 306.00
C 147.25 306.00 149.12 302.05 151.77 301.42 C 154.42 300.80 158.84 299.47 163.00 299.00
C 167.21 298.53 171.76 299.00 176.00 299.00
C 178.68 299.00 182.08 299.89 184.00 300.00
C 191.28 299.72 196.47 297.98 203.00 296.00
C 203.30 297.57 202.75 299.61 201.00 300.00
C 200.53 300.72 200.61 301.25 200.00 302.00
C 199.77 302.28 199.18 302.73 199.00 303.00
C 198.11 304.33 198.19 306.12 198.00 307.00
C 197.52 309.20 198.55 310.92 196.00 312.00
C 196.00 312.00 196.00 312.50 196.00 313.00
C 196.00 313.50 196.00 314.50 196.00 315.00
C 197.34 318.15 197.00 322.19 197.00 326.00
C 197.00 327.66 196.94 329.34 197.00 331.00
C 197.87 341.27 204.18 349.77 210.00 358.00
C 202.48 358.16 194.80 357.00 188.00 360.00
C 186.82 360.87 186.62 362.45 185.00 363.00
C 185.65 362.60 182.90 364.43 184.00 363.00
C 182.50 363.73 180.03 365.82 179.00 367.00
C 176.48 370.39 175.83 372.99 175.00 377.00
C 174.79 379.31 174.51 382.75 175.00 385.00
C 176.08 388.00 175.90 386.88 178.00 389.00
C 179.28 389.54 182.52 389.85 184.00 390.00
C 183.44 380.93 188.33 375.54 195.08 370.08 C 201.83 364.62 214.17 366.00 222.00 367.00 C 229.83 367.99 239.80 375.93 244.00 369.00
C 233.64 367.84 224.41 364.16 217.25 355.75 C 210.08 347.35 207.42 335.10 208.00 324.00 C 208.58 312.90 214.05 304.64 223.23 298.23 C 232.41 291.82 242.58 289.26 254.01 288.01 C 265.44 286.76 278.78 290.03 288.69 294.31 C 298.61 298.58 302.42 309.65 311.00 311.00
C 292.98 277.80 244.73 276.83 217.00 297.00
C 209.30 289.11 224.66 284.69 227.75 277.75 C 230.85 270.82 239.46 258.34 233.00 253.00
C 230.18 263.89 227.65 272.58 218.75 280.75 C 209.85 288.92 200.91 290.73 188.93 293.93 C 176.95 297.12 160.44 288.18 148.93 296.93 C 137.41 305.67 132.86 287.36 144.08 281.08 C 155.30 274.80 164.62 276.35 175.25 269.25 C 185.88 262.14 188.73 250.71 187.00 238.00
C 178.32 241.41 187.54 262.01 173.00 263.00
C 169.98 252.46 170.62 238.53 173.32 228.32 C 176.03 218.12 181.89 210.83 192.22 208.22 C 202.56 205.61 207.52 199.40 214.25 191.25 C 220.99 183.11 213.85 167.29 220.00 162.00
C 234.12 146.99 229.05 129.74 225.00 114.00
C 224.85 113.42 224.66 111.92 224.00 111.00
C 220.25 109.36 216.35 109.98 211.00 112.00
M 152.00 273.00 
C 142.20 277.14 131.12 280.81 131.00 294.00
C 120.27 284.76 135.80 269.65 147.00 272.00
C 149.02 271.71 152.29 270.15 155.00 271.00
C 154.45 272.01 153.02 272.44 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.19" fill="#000000">
|
||||
<path d="M 225.00 114.00 
C 229.89 122.33 229.91 132.23 232.00 142.00 C 234.08 151.77 221.37 159.93 223.93 168.07 C 226.48 176.22 228.75 187.50 232.00 194.00
C 235.40 198.53 239.35 201.24 244.00 204.00
C 237.83 198.83 233.59 191.20 231.33 183.67 C 229.06 176.15 224.84 166.83 229.08 160.08 C 233.32 153.32 236.40 141.18 233.78 133.22 C 231.15 125.27 231.64 114.34 224.00 111.00
C 224.66 111.92 224.85 113.42 225.00 114.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.09" fill="#B0B0B0">
|
||||
<path d="M 201.00 119.00 
C 204.34 116.85 207.73 113.57 211.00 112.00
C 206.52 113.70 204.06 116.44 201.00 119.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.48" fill="#000000">
|
||||
<path d="M 220.00 162.00 
C 219.66 166.74 225.00 173.64 225.32 178.68 C 225.64 183.72 229.13 190.18 232.00 194.00
C 228.75 187.50 226.48 176.22 223.93 168.07 C 221.37 159.93 234.08 151.77 232.00 142.00 C 229.91 132.23 229.89 122.33 225.00 114.00
C 229.05 129.74 234.12 146.99 220.00 162.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.99" fill="#BEBEBE">
|
||||
<path d="M 309.00 244.00 
C 319.62 252.72 326.67 266.01 332.31 278.69 C 337.94 291.38 343.27 312.99 343.00 327.00
C 348.86 307.69 357.88 289.27 376.00 278.00
C 375.67 274.36 376.25 270.64 376.00 267.00
C 374.20 240.59 358.93 215.59 343.00 195.00
C 335.15 187.46 327.36 178.80 319.00 172.00
C 311.02 165.51 302.27 161.02 293.75 155.25 C 285.22 149.49 276.61 146.55 268.08 139.92 C 259.55 133.29 246.17 127.28 248.00 141.00 C 249.83 154.72 251.48 162.65 256.69 173.31 C 261.91 183.96 266.05 189.82 272.70 198.30 C 279.34 206.78 283.96 213.48 290.75 221.25 C 297.53 229.03 302.67 236.14 309.00 244.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.13" fill="#858585">
|
||||
<path d="M 141.00 155.00 
C 143.35 152.60 147.22 149.86 150.00 147.00
C 148.11 148.46 143.80 151.45 141.00 155.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#868686">
|
||||
<path d="M 168.00 152.00 
C 171.08 151.66 174.91 151.68 178.00 152.00
C 174.52 151.23 171.54 151.18 168.00 152.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#F6F6F6">
|
||||
<path d="M 129.00 186.00 
C 129.32 186.68 128.94 188.24 129.00 189.00
C 129.45 190.21 132.14 191.97 133.00 193.00
C 134.29 192.83 134.75 191.30 136.00 191.00
C 151.55 202.15 175.95 211.63 193.25 196.25 C 210.55 180.87 198.43 156.53 178.00 152.00
C 174.91 151.68 171.08 151.66 168.00 152.00
C 159.11 154.07 151.33 159.04 145.30 166.30 C 139.28 173.57 139.58 184.19 129.00 186.00
M 175.00 161.00 
C 186.50 162.02 186.34 180.76 173.98 179.98 C 161.61 179.20 162.30 159.88 175.00 161.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#232323">
|
||||
<path d="M 175.00 161.00 
C 162.30 159.88 161.61 179.20 173.98 179.98 C 186.34 180.76 186.50 162.02 175.00 161.00
M 168.00 171.00 
C 167.10 158.05 181.72 171.06 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.16" fill="#A4A4A4">
|
||||
<path d="M 134.00 167.00 
C 135.13 166.05 136.21 164.44 137.00 163.00
C 137.08 162.88 134.67 165.13 134.00 167.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#F8F8F8">
|
||||
<path d="M 184.00 390.00 
C 200.03 391.64 215.88 388.64 232.00 390.00 C 248.12 391.37 264.82 388.01 281.00 390.00 C 297.18 391.99 307.70 387.80 321.00 381.00
C 334.96 366.76 337.54 345.00 343.00 327.00
C 343.27 312.99 337.94 291.38 332.31 278.69 C 326.67 266.01 319.62 252.72 309.00 244.00
C 288.30 227.00 266.37 217.28 244.00 204.00
C 239.35 201.24 235.40 198.53 232.00 194.00
C 229.13 190.18 225.64 183.72 225.32 178.68 C 225.00 173.64 219.66 166.74 220.00 162.00
C 213.85 167.29 220.99 183.11 214.25 191.25 C 207.52 199.40 202.56 205.61 192.22 208.22 C 181.89 210.83 176.03 218.12 173.32 228.32 C 170.62 238.53 169.98 252.46 173.00 263.00
C 187.54 262.01 178.32 241.41 187.00 238.00
C 188.73 250.71 185.88 262.14 175.25 269.25 C 164.62 276.35 155.30 274.80 144.08 281.08 C 132.86 287.36 137.41 305.67 148.93 296.93 C 160.44 288.18 176.95 297.12 188.93 293.93 C 200.91 290.73 209.85 288.92 218.75 280.75 C 227.65 272.58 230.18 263.89 233.00 253.00
C 239.46 258.34 230.85 270.82 227.75 277.75 C 224.66 284.69 209.30 289.11 217.00 297.00
C 244.73 276.83 292.98 277.80 311.00 311.00
C 302.42 309.65 298.61 298.58 288.69 294.31 C 278.78 290.03 265.44 286.76 254.01 288.01 C 242.58 289.26 232.41 291.82 223.23 298.23 C 214.05 304.64 208.58 312.90 208.00 324.00 C 207.42 335.10 210.08 347.35 217.25 355.75 C 224.41 364.16 233.64 367.84 244.00 369.00
C 239.80 375.93 229.83 367.99 222.00 367.00 C 214.17 366.00 201.83 364.62 195.08 370.08 C 188.33 375.54 183.44 380.93 184.00 390.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#E7E7E7">
|
||||
<path d="M 168.00 171.00 
C 181.72 171.06 167.10 158.05 168.00 171.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.06" fill="#D5D6D5">
|
||||
<path d="M 126.00 172.00 
C 128.16 170.13 130.91 168.68 133.00 167.00
C 130.73 167.21 126.57 169.37 126.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.17" fill="#010101">
|
||||
<path d="M 319.00 172.00 
C 327.36 178.80 335.15 187.46 343.00 195.00
C 349.36 201.12 355.23 207.97 360.00 215.00
C 351.42 199.09 333.96 180.10 319.00 172.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.19" fill="#6A6A6A">
|
||||
<path d="M 127.00 185.00 
C 126.54 183.21 127.08 180.85 127.00 179.00
C 124.69 178.33 127.20 174.74 125.00 174.00
C 124.95 177.75 125.33 182.05 127.00 185.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.26" fill="#000000">
|
||||
<path d="M 172.00 215.00 
C 172.45 214.90 173.63 214.23 174.00 214.00
C 180.28 209.95 165.13 212.19 171.00 209.00
C 157.50 209.93 141.03 202.59 133.00 193.00
C 132.14 191.97 129.45 190.21 129.00 189.00
C 129.72 198.14 139.50 203.40 146.92 206.08 C 154.33 208.76 163.08 216.93 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.33" fill="#000000">
|
||||
<path d="M 376.00 267.00 
C 376.19 267.61 376.87 267.94 377.00 269.00
C 382.28 281.94 376.22 302.91 373.67 315.67 C 371.13 328.44 363.16 341.06 357.08 352.08 C 351.00 363.10 341.62 372.42 331.00 379.00
C 336.37 377.44 343.03 372.51 348.25 366.25 C 353.47 359.98 358.20 353.62 362.23 346.23 C 366.25 338.84 369.84 331.25 373.02 323.02 C 376.21 314.80 378.11 304.40 379.22 295.22 C 380.34 286.05 381.18 272.54 378.33 263.67 C 375.48 254.80 374.87 244.63 371.77 236.23 C 368.67 227.83 364.03 222.47 360.00 215.00
C 355.23 207.97 349.36 201.12 343.00 195.00
C 358.93 215.59 374.20 240.59 376.00 267.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.17" fill="#8A8A8A">
|
||||
<path d="M 172.00 215.00 
C 168.52 218.81 166.97 226.20 166.00 231.00
C 168.04 225.37 171.29 219.69 174.00 214.00
C 173.63 214.23 172.45 214.90 172.00 215.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.60" fill="#101010">
|
||||
<path d="M 165.00 241.00 
C 165.00 243.67 165.00 246.33 165.00 249.00
C 165.00 249.00 165.05 249.51 165.00 250.00
C 165.14 247.56 167.63 242.52 165.00 240.00
C 165.00 240.00 165.00 240.50 165.00 241.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.03" fill="#7E7E7E">
|
||||
<path d="M 165.00 249.00 
C 165.00 246.33 165.00 243.67 165.00 241.00
C 163.50 243.03 163.47 246.93 165.00 249.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.04" fill="#777777">
|
||||
<path d="M 152.00 265.00 
C 155.01 265.00 157.59 264.70 160.00 264.00
C 157.04 264.47 154.50 263.94 152.00 265.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.29" fill="#7B7B7B">
|
||||
<path d="M 142.00 266.00 
C 144.01 265.77 147.41 265.74 150.00 265.00
C 147.06 264.79 144.65 265.37 142.00 266.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.68" fill="#4F4F4F">
|
||||
<path d="M 376.00 278.00 
C 376.36 277.78 377.00 278.00 377.00 278.00
C 377.06 275.10 377.35 271.88 377.00 269.00
C 376.87 267.94 376.19 267.61 376.00 267.00
C 376.25 270.64 375.67 274.36 376.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.46" fill="#000000">
|
||||
<path d="M 377.00 278.00 
C 376.24 316.06 358.69 361.73 321.00 381.00
C 307.70 387.80 297.18 391.99 281.00 390.00 C 264.82 388.01 248.12 391.37 232.00 390.00 C 215.88 388.64 200.03 391.64 184.00 390.00
C 182.52 389.85 179.28 389.54 178.00 389.00
C 182.87 396.59 191.77 393.68 200.00 394.00 C 208.23 394.32 218.57 393.90 227.00 394.00 C 235.43 394.10 244.63 394.09 253.00 394.00 C 261.37 393.91 271.99 394.30 280.00 394.00 C 288.01 393.70 299.78 394.82 306.78 393.78 C 313.78 392.73 320.86 388.48 320.00 385.00
C 324.03 383.93 327.43 381.21 331.00 379.00
C 341.62 372.42 351.00 363.10 357.08 352.08 C 363.16 341.06 371.13 328.44 373.67 315.67 C 376.22 302.91 382.28 281.94 377.00 269.00
C 377.35 271.88 377.06 275.10 377.00 278.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#767676">
|
||||
<path d="M 147.00 272.00 
C 148.46 272.02 151.35 273.02 152.00 273.00
C 153.02 272.44 154.45 272.01 155.00 271.00
C 152.29 270.15 149.02 271.71 147.00 272.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="1.00" fill="#E3E3E3">
|
||||
<path d="M 152.00 273.00 
C 151.35 273.02 148.46 272.02 147.00 272.00
C 135.80 269.65 120.27 284.76 131.00 294.00
C 131.12 280.81 142.20 277.14 152.00 273.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.20" fill="#8E8E8E">
|
||||
<path d="M 123.00 281.00 
C 123.72 279.04 126.55 276.13 128.00 274.00
C 125.91 276.29 123.82 277.81 123.00 281.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.99" fill="#1D1D1D">
|
||||
<path d="M 343.00 327.00 
C 337.54 345.00 334.96 366.76 321.00 381.00
C 358.69 361.73 376.24 316.06 377.00 278.00
C 377.00 278.00 376.36 277.78 376.00 278.00
C 357.88 289.27 348.86 307.69 343.00 327.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.32" fill="#000000">
|
||||
<path d="M 126.00 296.00 
C 130.06 302.11 136.93 309.06 145.00 309.00 C 153.07 308.94 160.96 301.21 170.00 303.00 C 179.04 304.79 191.11 300.89 199.00 303.00
C 199.18 302.73 199.77 302.28 200.00 302.00
C 197.55 298.03 190.28 303.63 184.00 302.00 C 177.72 300.37 175.48 302.79 166.00 302.00 C 156.52 301.21 146.75 312.62 138.00 305.00
C 133.40 300.99 129.31 299.51 126.00 296.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.59" fill="#010101">
|
||||
<path d="M 184.00 300.00 
C 181.34 300.10 178.66 300.00 176.00 300.00
C 171.88 300.00 166.28 298.95 163.00 299.00
C 158.84 299.47 154.42 300.80 151.77 301.42 C 149.12 302.05 147.25 306.00 143.00 306.00
C 147.64 307.07 150.96 305.59 155.08 303.08 C 159.19 300.56 165.50 300.79 170.00 301.00 C 174.50 301.21 183.31 300.15 187.00 301.00 C 190.69 301.85 200.07 297.39 201.00 300.00
C 202.75 299.61 203.30 297.57 203.00 296.00
C 196.47 297.98 191.28 299.72 184.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.75" fill="#474747">
|
||||
<path d="M 176.00 300.00 
C 175.96 300.00 176.00 299.00 176.00 299.00
C 171.76 299.00 167.21 298.53 163.00 299.00
C 166.28 298.95 171.88 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.77" fill="#090909">
|
||||
<path d="M 176.00 300.00 
C 178.66 300.00 181.34 300.10 184.00 300.00
C 182.08 299.89 178.68 299.00 176.00 299.00
C 176.00 299.00 175.96 300.00 176.00 300.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.44" fill="#000000">
|
||||
<path d="M 143.00 306.00 
C 141.66 305.69 139.13 305.57 138.00 305.00
C 146.75 312.62 156.52 301.21 166.00 302.00 C 175.48 302.79 177.72 300.37 184.00 302.00 C 190.28 303.63 197.55 298.03 200.00 302.00
C 200.61 301.25 200.53 300.72 201.00 300.00
C 200.07 297.39 190.69 301.85 187.00 301.00 C 183.31 300.15 174.50 301.21 170.00 301.00 C 165.50 300.79 159.19 300.56 155.08 303.08 C 150.96 305.59 147.64 307.07 143.00 306.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.29" fill="#757575">
|
||||
<path d="M 196.00 312.00 
C 198.55 310.92 197.52 309.20 198.00 307.00
C 198.29 306.24 196.30 310.37 196.00 312.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.03" fill="#AEAEAE">
|
||||
<path d="M 195.00 324.00 
C 195.00 324.05 196.00 324.00 196.00 324.00
C 195.64 321.10 196.00 317.92 196.00 315.00
C 196.00 314.50 196.00 313.50 196.00 313.00
C 194.25 315.36 194.72 320.78 195.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.66" fill="#111111">
|
||||
<path d="M 196.00 324.00 
C 196.15 325.26 196.76 325.23 197.00 326.00
C 197.00 322.19 197.34 318.15 196.00 315.00
C 196.00 317.92 195.64 321.10 196.00 324.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.26" fill="#484848">
|
||||
<path d="M 197.00 331.00 
C 196.94 329.34 197.00 327.66 197.00 326.00
C 196.76 325.23 196.15 325.26 196.00 324.00
C 196.00 324.00 195.00 324.05 195.00 324.00
C 195.21 326.36 196.59 329.08 197.00 331.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.11" fill="#B4B4B4">
|
||||
<path d="M 179.00 367.00 
C 180.03 365.82 182.50 363.73 184.00 363.00
C 184.40 362.80 185.00 363.00 185.00 363.00
C 186.62 362.45 186.82 360.87 188.00 360.00
C 184.21 361.67 181.54 363.59 179.00 367.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.78" fill="#0E0E0E">
|
||||
<path d="M 185.00 363.00 
C 185.00 363.00 184.40 362.80 184.00 363.00
C 182.90 364.43 185.65 362.60 185.00 363.00 Z"/>
|
||||
</g>
|
||||
<g stroke="None" fill-opacity="0.20" fill="#868686">
|
||||
<path d="M 175.00 385.00 
C 174.51 382.75 174.79 379.31 175.00 377.00
C 174.40 379.91 173.90 381.94 175.00 385.00 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 42 KiB |
1
src/public/robots.txt
Normal file
1
src/public/robots.txt
Normal file
@ -0,0 +1 @@
|
||||
|
||||
90
src/stores/browser/extensions.ts
Normal file
90
src/stores/browser/extensions.ts
Normal file
@ -0,0 +1,90 @@
|
||||
export interface ResourceRequestDetails {
|
||||
url: string;
|
||||
resourceType: string;
|
||||
tabId?: string;
|
||||
frameId?: number;
|
||||
}
|
||||
|
||||
export interface ResourceRequestResult {
|
||||
cancel: boolean;
|
||||
redirectUrl?: string;
|
||||
}
|
||||
|
||||
export interface ContentScript {
|
||||
code: string;
|
||||
matches?: string[];
|
||||
runAt?: 'document_start' | 'document_end' | 'document_idle';
|
||||
}
|
||||
|
||||
export interface Extension {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
processNavigation?: (url: string) => boolean;
|
||||
processResourceRequest?: (
|
||||
details: ResourceRequestDetails
|
||||
) => ResourceRequestResult;
|
||||
contentScripts?: ContentScript[];
|
||||
}
|
||||
|
||||
export const useBrowserExtensionStore = defineStore(
|
||||
'useBrowserExtensionStore',
|
||||
() => {
|
||||
const extensions = ref<Extension[]>([]);
|
||||
const isInitialized = ref<boolean>(false);
|
||||
|
||||
return {
|
||||
extensions,
|
||||
isInitialized,
|
||||
initializeAsync,
|
||||
processNavigation,
|
||||
injectContentScripts,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const initializeAsync = async () => {
|
||||
const { isInitialized } = storeToRefs(useBrowserExtensionStore());
|
||||
if (isInitialized.value) return;
|
||||
|
||||
// Lade Erweiterungen aus dem Erweiterungsverzeichnis
|
||||
try {
|
||||
const extensions = await loadExtensionsAsync();
|
||||
for (const extension of extensions) {
|
||||
registerExtension(extension);
|
||||
}
|
||||
|
||||
isInitialized.value = true;
|
||||
console.log(`${extensions.length} Erweiterungen geladen`);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Erweiterungen:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadExtensionsAsync = async (): Promise<Extension[]> => {
|
||||
// In einer realen Implementierung würden Sie hier Erweiterungen aus einem Verzeichnis laden
|
||||
// Für dieses Beispiel verwenden wir hartcodierte Erweiterungen
|
||||
/* const adBlocker = (await import('./ad-blocker')).default;
|
||||
const trackerBlocker = (await import('./tracker-blocker')).default; */
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const registerExtension = (extension: Extension): boolean => {
|
||||
const { extensions } = storeToRefs(useBrowserExtensionStore());
|
||||
if (!extension.id || !extension.name) {
|
||||
console.error('Ungültige Erweiterung:', extension);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`Erweiterung registriert: ${extension.name}`);
|
||||
extensions.value.push(extension);
|
||||
return true;
|
||||
};
|
||||
|
||||
const processNavigation = (url: string) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const injectContentScripts = (t: string) => {};
|
||||
0
src/stores/browser/index.ts
Normal file
0
src/stores/browser/index.ts
Normal file
57
src/stores/extensions/index.ts
Normal file
57
src/stores/extensions/index.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { getSingleRouteParam } from '~/composables/helper';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
export interface IExtensionLink {
|
||||
name: string;
|
||||
icon: string;
|
||||
tooltip?: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const useExtensionsStore = defineStore('extensionsStore', () => {
|
||||
const extensions = ref<IExtensionLink[]>([
|
||||
{
|
||||
id: 'haex-browser',
|
||||
name: 'Haex Browser',
|
||||
icon: 'solar:global-outline',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'extensions',
|
||||
name: 'sidebar.extensions',
|
||||
icon: 'gg:extension',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'settings',
|
||||
name: 'sidebar.settings',
|
||||
icon: 'ph:gear-six',
|
||||
},
|
||||
]);
|
||||
|
||||
const currentRoute = useRouter().currentRoute.value;
|
||||
|
||||
const isActive = (id: string) =>
|
||||
computed(
|
||||
() =>
|
||||
currentRoute.name === 'extension' &&
|
||||
currentRoute.params.extensionId === id
|
||||
);
|
||||
|
||||
const loadAsync = async (id: string) => {
|
||||
extensions.value.some(async (extension) => {
|
||||
if (extension.id === id) {
|
||||
await navigateTo(
|
||||
useLocalePath()({ name: 'extension', params: { extensionId: id } })
|
||||
);
|
||||
} else {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
extensions,
|
||||
loadAsync,
|
||||
isActive,
|
||||
};
|
||||
});
|
||||
5
src/stores/ui/de.json
Normal file
5
src/stores/ui/de.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"soft": "Soft"
|
||||
}
|
||||
5
src/stores/ui/en.json
Normal file
5
src/stores/ui/en.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"soft": "Soft"
|
||||
}
|
||||
41
src/stores/ui/index.ts
Normal file
41
src/stores/ui/index.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
|
||||
import de from './de.json';
|
||||
import en from './en.json';
|
||||
|
||||
export const useUiStore = defineStore('uiStore', () => {
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||
|
||||
const currentScreenSize = computed(() =>
|
||||
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs'
|
||||
);
|
||||
|
||||
const { t } = useI18n({
|
||||
messages: {
|
||||
de: { ui: de },
|
||||
en: { ui: en },
|
||||
},
|
||||
});
|
||||
|
||||
const availableThemes = ref([
|
||||
{
|
||||
value: 'dark',
|
||||
name: t('ui.dark'),
|
||||
icon: 'line-md:moon-rising-alt-loop',
|
||||
},
|
||||
{
|
||||
value: 'light',
|
||||
name: t('ui.light'),
|
||||
icon: 'line-md:moon-to-sunny-outline-loop-transition',
|
||||
},
|
||||
{ value: 'soft', name: t('ui.soft'), icon: 'line-md:paint-drop' },
|
||||
]);
|
||||
|
||||
const currentTheme = ref(availableThemes.value[0].value);
|
||||
|
||||
return {
|
||||
breakpoints,
|
||||
currentScreenSize,
|
||||
currentTheme,
|
||||
availableThemes,
|
||||
};
|
||||
});
|
||||
22
src/stores/ui/notifications.ts
Normal file
22
src/stores/ui/notifications.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export interface IHaexNotication {
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
image?: string;
|
||||
alt?: string;
|
||||
}
|
||||
|
||||
export const useNotificationStore = defineStore('notificationStore', () => {
|
||||
const notifications = ref<IHaexNotication[]>([
|
||||
{
|
||||
title: 'huhu',
|
||||
alt: 'test',
|
||||
description: 'Ganz was tolles',
|
||||
image: 'https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png',
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
notifications,
|
||||
};
|
||||
});
|
||||
44
src/stores/ui/sidebar.ts
Normal file
44
src/stores/ui/sidebar.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { getSingleRouteParam } from '~/composables/helper';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
export interface ISidebarItem {
|
||||
name: string;
|
||||
icon: string;
|
||||
tooltip?: string;
|
||||
id: string;
|
||||
type: 'browser' | 'extension';
|
||||
}
|
||||
|
||||
export const useSidebarStore = defineStore('sidebarStore', () => {
|
||||
const menu = ref<ISidebarItem[]>([
|
||||
{
|
||||
id: 'haex-browser',
|
||||
name: 'Haex Browser',
|
||||
icon: 'solar:global-outline',
|
||||
type: 'browser',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'haex-vault',
|
||||
name: 'Haex Vault',
|
||||
icon: 'gg:extension',
|
||||
type: 'extension',
|
||||
},
|
||||
]);
|
||||
|
||||
/* const loadAsync = async (id: string) => {
|
||||
extensions.value.some(async (extension) => {
|
||||
if (extension.id === id) {
|
||||
await navigateTo(
|
||||
useLocalePath()({ name: 'extension', params: { extensionId: id } })
|
||||
);
|
||||
} else {
|
||||
}
|
||||
});
|
||||
}; */
|
||||
|
||||
return {
|
||||
menu,
|
||||
//loadAsync,
|
||||
};
|
||||
});
|
||||
231
src/stores/vault/index.ts
Normal file
231
src/stores/vault/index.ts
Normal file
@ -0,0 +1,231 @@
|
||||
//import Database from '@tauri-apps/plugin-sql';
|
||||
import { drizzle, SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy';
|
||||
//import Database from "tauri-plugin-sql-api";
|
||||
import * as schema from '@/../src-tauri/database/schemas/vault';
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { count } from 'drizzle-orm';
|
||||
import { platform } from '@tauri-apps/plugin-os';
|
||||
|
||||
interface IVault {
|
||||
//database: Database;
|
||||
path: string;
|
||||
password: string;
|
||||
name: string;
|
||||
drizzle: SqliteRemoteDatabase<typeof schema>;
|
||||
}
|
||||
interface IOpenVaults {
|
||||
[vaultPath: string]: IVault;
|
||||
}
|
||||
|
||||
export const useVaultStore = defineStore('vaultStore', () => {
|
||||
const currentVaultId = computed<string | undefined>({
|
||||
get: () =>
|
||||
getSingleRouteParam(useRouter().currentRoute.value.params.vaultId),
|
||||
set: (newVaultId) => {
|
||||
useRouter().currentRoute.value.params.vaultId = newVaultId ?? '';
|
||||
},
|
||||
});
|
||||
|
||||
const read_only = computed<boolean>({
|
||||
get: () => {
|
||||
console.log(
|
||||
'query showSidebar',
|
||||
useRouter().currentRoute.value.query.readonly
|
||||
);
|
||||
return JSON.parse(
|
||||
getSingleRouteParam(useRouter().currentRoute.value.query.readonly) ||
|
||||
'false'
|
||||
);
|
||||
},
|
||||
set: (readonly) => {
|
||||
const router = useRouter();
|
||||
router.replace({
|
||||
query: {
|
||||
...router.currentRoute.value.query,
|
||||
readonly: JSON.stringify(readonly ? true : false),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const openVaults = ref<IOpenVaults | undefined>();
|
||||
|
||||
const currentVault = ref<IVault | undefined>();
|
||||
|
||||
/* computed(() => {
|
||||
console.log('compute currentVault', currentVaultId.value, openVaults.value);
|
||||
return openVaults.value?.[currentVaultId.value ?? ''];
|
||||
}); */
|
||||
|
||||
watch(
|
||||
currentVaultId,
|
||||
async () => {
|
||||
/* if (!openVaults.value?.[currentVaultId.value ?? '']) {
|
||||
console.log(
|
||||
'no vaultId',
|
||||
currentVault.value,
|
||||
openVaults.value?.[currentVaultId.value ?? '']
|
||||
);
|
||||
return await navigateTo(useLocalePath()({ name: 'vaultOpen' }));
|
||||
} else */
|
||||
currentVault.value = openVaults.value?.[currentVaultId.value ?? ''];
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const openAsync = async ({
|
||||
path = '',
|
||||
password,
|
||||
}: {
|
||||
path: string;
|
||||
password: string;
|
||||
}) => {
|
||||
//const sqlitePath = path?.startsWith('sqlite:') ? path : `sqlite:${path}`;
|
||||
|
||||
console.log('try to open db', path, password);
|
||||
|
||||
const result = await invoke<string>('open_encrypted_database', {
|
||||
path,
|
||||
key: password,
|
||||
});
|
||||
|
||||
console.log('open vault from store', result);
|
||||
if (!(await testDatabaseReadAsync())) throw new Error('Passwort falsch');
|
||||
//const db = await Database.load(sqlitePath);
|
||||
|
||||
const vaultId = await getVaultIdAsync(path);
|
||||
const seperator = platform() === 'windows' ? '\\' : '/';
|
||||
const fileName = path.split(seperator).pop();
|
||||
console.log('opened db fileName', fileName, vaultId);
|
||||
|
||||
openVaults.value = {
|
||||
...openVaults.value,
|
||||
[vaultId]: {
|
||||
//database: db,
|
||||
path,
|
||||
password,
|
||||
name: fileName ?? path,
|
||||
drizzle: drizzle<typeof schema>(
|
||||
async (sql, params, method) => {
|
||||
let rows: any = [];
|
||||
let results = [];
|
||||
|
||||
// If the query is a SELECT, use the select method
|
||||
if (isSelectQuery(sql)) {
|
||||
rows = await invoke('db_select', { sql, params }).catch((e) => {
|
||||
console.error('SQL Error:', e);
|
||||
return [];
|
||||
});
|
||||
} else {
|
||||
// Otherwise, use the execute method
|
||||
rows = await invoke('db_execute', { sql, params }).catch((e) => {
|
||||
console.error('SQL Error:', e);
|
||||
return [];
|
||||
});
|
||||
return { rows: [] };
|
||||
}
|
||||
|
||||
rows = rows.map((row: any) => {
|
||||
return Object.values(row);
|
||||
});
|
||||
|
||||
// If the method is "all", return all rows
|
||||
results = method === 'all' ? rows : rows[0];
|
||||
|
||||
return { rows: results };
|
||||
},
|
||||
// Pass the schema to the drizzle instance
|
||||
{ schema: schema, logger: true }
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
const { addVaultAsync } = useLastVaultStore();
|
||||
await addVaultAsync({ path });
|
||||
|
||||
return vaultId;
|
||||
};
|
||||
|
||||
const testDatabaseReadAsync = async () => {
|
||||
try {
|
||||
currentVault.value?.drizzle
|
||||
.select({ count: count() })
|
||||
.from(schema.haexExtensions);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshDatabaseAsync = async () => {
|
||||
console.log('refreshDatabaseAsync');
|
||||
/* if (!currentVault.value?.database.close) {
|
||||
return navigateTo(useLocaleRoute()({ name: 'vaultOpen' }));
|
||||
} */
|
||||
};
|
||||
|
||||
const createAsync = async ({
|
||||
path,
|
||||
password,
|
||||
}: {
|
||||
path: string;
|
||||
password: string;
|
||||
}) => {
|
||||
/* const existDb = await exists('default.db', {
|
||||
baseDir: BaseDirectory.Resource,
|
||||
}); */
|
||||
|
||||
/* const existDb = await resolveResource('resources/default.db');
|
||||
if (!existDb) throw new Error('Keine Datenbank da');
|
||||
await copyFile(existDb, path); */
|
||||
const result = await invoke('create_encrypted_database', {
|
||||
path,
|
||||
key: password,
|
||||
});
|
||||
console.log('create_encrypted_database', result);
|
||||
return 'aaaaa'; //await openAsync({ path, password });
|
||||
};
|
||||
|
||||
const closeAsync = async () => {
|
||||
if (!currentVaultId.value) return;
|
||||
|
||||
/* if (
|
||||
typeof openVaults.value?.[currentVaultId.value]?.database?.close ===
|
||||
'function'
|
||||
) {
|
||||
console.log('close db', openVaults.value?.[currentVaultId.value]);
|
||||
return openVaults.value?.[currentVaultId.value]?.database?.close();
|
||||
} */
|
||||
delete openVaults.value?.[currentVaultId.value];
|
||||
};
|
||||
|
||||
return {
|
||||
closeAsync,
|
||||
createAsync,
|
||||
currentVault,
|
||||
currentVaultId,
|
||||
openAsync,
|
||||
openVaults,
|
||||
refreshDatabaseAsync,
|
||||
read_only,
|
||||
};
|
||||
});
|
||||
|
||||
const getVaultIdAsync = async (path: string) => {
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(path);
|
||||
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
|
||||
const hashHex = hashArray
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join(''); // convert bytes to hex string
|
||||
console.log('vaultId', hashHex);
|
||||
return hashHex;
|
||||
};
|
||||
|
||||
function isSelectQuery(sql: string): boolean {
|
||||
const selectRegex = /^\s*SELECT\b/i;
|
||||
return selectRegex.test(sql);
|
||||
}
|
||||
80
src/stores/vault/lastVaults.ts
Normal file
80
src/stores/vault/lastVaults.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { load, Store } from '@tauri-apps/plugin-store';
|
||||
|
||||
interface ILastVault {
|
||||
lastUsed: Date;
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export const useLastVaultStore = defineStore('lastVaultStore', () => {
|
||||
const {
|
||||
public: { haexVault },
|
||||
} = useRuntimeConfig();
|
||||
|
||||
const lastVaults = ref<ILastVault[]>([]);
|
||||
|
||||
const keyName = 'lastVaults';
|
||||
|
||||
const getStoreAsync = async () => {
|
||||
return await load(haexVault.lastVaultFileName);
|
||||
};
|
||||
|
||||
const syncLastVaultsAsync = async () => {
|
||||
const store = await getStoreAsync();
|
||||
lastVaults.value =
|
||||
(await store.get<ILastVault[]>(keyName))?.sort(
|
||||
(a, b) => +new Date(b.lastUsed) - +new Date(a.lastUsed)
|
||||
) ?? [];
|
||||
|
||||
return lastVaults.value;
|
||||
};
|
||||
|
||||
const addVaultAsync = async ({
|
||||
name,
|
||||
path,
|
||||
}: {
|
||||
name?: string;
|
||||
path: string;
|
||||
}) => {
|
||||
if (!lastVaults.value) await syncLastVaultsAsync();
|
||||
|
||||
const saveName = name || getFileNameFromPath(path);
|
||||
lastVaults.value = lastVaults.value.filter((vault) => vault.path !== path);
|
||||
lastVaults.value.push({ lastUsed: new Date(), name: saveName, path });
|
||||
await saveLastVaultsAsync();
|
||||
};
|
||||
|
||||
const removeVaultAsync = async (vaultPath: string) => {
|
||||
console.log('remove', vaultPath, lastVaults.value);
|
||||
lastVaults.value = lastVaults.value.filter(
|
||||
(vault) => vault.path !== vaultPath
|
||||
);
|
||||
await saveLastVaultsAsync();
|
||||
};
|
||||
|
||||
const saveLastVaultsAsync = async () => {
|
||||
const store = await getStoreAsync();
|
||||
console.log('save lastVaults', keyName, lastVaults.value);
|
||||
await store.set(keyName, lastVaults.value);
|
||||
await syncLastVaultsAsync();
|
||||
};
|
||||
|
||||
return {
|
||||
addVaultAsync,
|
||||
syncLastVaultsAsync,
|
||||
lastVaults,
|
||||
removeVaultAsync,
|
||||
saveLastVaultsAsync,
|
||||
};
|
||||
});
|
||||
|
||||
const getFileNameFromPath = (path: string) => {
|
||||
const lastBackslashIndex = path.lastIndexOf('\\');
|
||||
const lastSlashIndex = path.lastIndexOf('/');
|
||||
|
||||
const lastIndex = Math.max(lastBackslashIndex, lastSlashIndex);
|
||||
|
||||
const fileName = path.substring(lastIndex + 1);
|
||||
|
||||
return fileName;
|
||||
};
|
||||
60
src/utils/webview-bridge.js
Normal file
60
src/utils/webview-bridge.js
Normal file
@ -0,0 +1,60 @@
|
||||
(function () {
|
||||
// Überwache das Erstellen von Elementen, die Ressourcen laden
|
||||
const originalCreateElement = document.createElement;
|
||||
document.createElement = function (tagName) {
|
||||
const element = originalCreateElement.call(document, tagName);
|
||||
|
||||
if (
|
||||
tagName.toLowerCase() === 'img' ||
|
||||
tagName.toLowerCase() === 'script' ||
|
||||
tagName.toLowerCase() === 'iframe'
|
||||
) {
|
||||
// Überwache das Setzen des src-Attributs
|
||||
const originalSetAttribute = element.setAttribute;
|
||||
element.setAttribute = function (name, value) {
|
||||
if (name === 'src') {
|
||||
// Prüfe, ob die Ressource blockiert werden soll
|
||||
window.__TAURI__
|
||||
.invoke('block_resource_request', {
|
||||
url: value,
|
||||
resourceType: tagName.toLowerCase(),
|
||||
})
|
||||
.then((shouldBlock) => {
|
||||
if (shouldBlock) {
|
||||
console.log(`Ressourcenanfrage blockiert: ${value}`);
|
||||
return;
|
||||
}
|
||||
originalSetAttribute.call(element, name, value);
|
||||
});
|
||||
} else {
|
||||
originalSetAttribute.call(element, name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
// Wenn die Tauri HTTP API verwendet wird, können wir sie hier überwachen
|
||||
if (window.__TAURI__ && window.__TAURI__.http) {
|
||||
const originalFetch = window.__TAURI__.http.fetch;
|
||||
window.__TAURI__.http.fetch = async function (options) {
|
||||
// Prüfe, ob die Ressource blockiert werden soll
|
||||
const shouldBlock = await window.__TAURI__.invoke(
|
||||
'block_resource_request',
|
||||
{
|
||||
url: options.url,
|
||||
resourceType: 'tauri-fetch',
|
||||
}
|
||||
);
|
||||
|
||||
if (shouldBlock) {
|
||||
throw new Error(`Ressourcenanfrage blockiert: ${options.url}`);
|
||||
}
|
||||
|
||||
return originalFetch.call(this, options);
|
||||
};
|
||||
}
|
||||
|
||||
console.log('Webview-Bridge geladen');
|
||||
})();
|
||||
Reference in New Issue
Block a user