added settings page, cleanup

This commit is contained in:
Martin Drechsel
2025-05-19 12:29:37 +02:00
parent 6a1351752b
commit 0699dbef31
35 changed files with 889 additions and 653 deletions

View File

@ -22,11 +22,11 @@ export default defineNuxtConfig({
provider: 'server',
mode: "svg",
clientBundle: {
icons: ["solar:global-outline", "gg:extension"],
icons: ["solar:global-outline", "gg:extension", "hugeicons:corporate"],
scan: true,
includeCustomCollections: true,
},
serverBundle: { collections: ["mdi", "line-md", "solar", "gg"] }
serverBundle: { collections: ["mdi", "line-md", "solar", "gg", "emojione"] }
//collections: ["mdi", "line-md"]
},

View File

@ -25,6 +25,7 @@
"opener:allow-open-url",
"opener:default",
"os:default",
"os:allow-hostname",
"store:default"
]
}

View File

@ -0,0 +1,4 @@
ALTER TABLE `haex_settings` RENAME COLUMN "value_text" TO "value";--> statement-breakpoint
DROP TABLE `testTable`;--> statement-breakpoint
ALTER TABLE `haex_settings` DROP COLUMN `value_json`;--> statement-breakpoint
ALTER TABLE `haex_settings` DROP COLUMN `value_number`;

View File

@ -0,0 +1,180 @@
{
"version": "6",
"dialect": "sqlite",
"id": "ea3507ca-77bc-4f3c-a605-8426614f5803",
"prevId": "6fb5396b-9f87-4fb5-87a2-22d4eecaa11e",
"tables": {
"haex_extensions": {
"name": "haex_extensions",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"author": {
"name": "author",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"enabled": {
"name": "enabled",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"icon": {
"name": "icon",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"version": {
"name": "version",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"haex_extensions_permissions": {
"name": "haex_extensions_permissions",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"extension_id": {
"name": "extension_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"resource": {
"name": "resource",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"operation": {
"name": "operation",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
"columns": [
"extension_id",
"resource",
"operation",
"path"
],
"isUnique": true
}
},
"foreignKeys": {
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
"tableFrom": "haex_extensions_permissions",
"tableTo": "haex_extensions",
"columnsFrom": [
"extension_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"haex_settings": {
"name": "haex_settings",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"key": {
"name": "key",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {
"\"haex_settings\".\"value_text\"": "\"haex_settings\".\"value\""
}
},
"internal": {
"indexes": {}
}
}

View File

@ -15,6 +15,13 @@
"when": 1746281577722,
"tag": "0001_wealthy_thaddeus_ross",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1747583956679,
"tag": "0002_married_bushwacker",
"breakpoints": true
}
]
}

View File

@ -1,18 +1,15 @@
import {
integer,
numeric,
sqliteTable,
text,
type AnySQLiteColumn,
unique,
type AnySQLiteColumn
} from "drizzle-orm/sqlite-core";
export const haexSettings = sqliteTable("haex_settings", {
id: text().primaryKey(),
key: text(),
value_text: text(),
value_json: text({ mode: "json" }),
value_number: numeric(),
value: text(),
});
export const haexExtensions = sqliteTable("haex_extensions", {
@ -25,18 +22,13 @@ export const haexExtensions = sqliteTable("haex_extensions", {
version: text(),
});
export const testTable = sqliteTable("testTable", {
id: text().primaryKey(),
author: text(),
test: text(),
});
export const haexExtensionsPermissions = sqliteTable(
"haex_extensions_permissions",
{
id: text().primaryKey(),
extensionId: text("extension_id").references((): AnySQLiteColumn => haexExtensions.id),
resource: text({ enum: ["fs", "http", "database"] }),
resource: text({ enum: ["fs", "http", "db", "shell"] }),
operation: text({ enum: ["read", "write", "create"] }),
path: text(),
},

Binary file not shown.

View File

@ -41,7 +41,6 @@ pub fn run() {
})
}
}
//extension::core::extension_protocol_handler(&context, &request)
})
.plugin(tauri_plugin_http::init())
.manage(DbConnection(Mutex::new(None)))

View File

@ -19,7 +19,7 @@
],
"security": {
"csp": {
"default-src": ["'self'", "haex-extension: data: blob: asset:"],
"default-src": "'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost; default-src 'self' asset: http://asset.localhost",
"script-src": ["'self'", "haex-extension:"],
"style-src": ["'self'", "haex-extension:"],
"connect-src": ["'self'", "haex-extension:"],
@ -29,7 +29,7 @@
},
"assetProtocol": {
"enable": true,
"scope": ["$RESOURCE/extensions/**"]
"scope": ["$RESOURCE/**", "$APPDATA/**"]
}
}
},

View File

@ -1,6 +1,6 @@
<template>
<div>
<NuxtLayout :data-theme="currentTheme">
<NuxtLayout :data-theme="currentTheme.value">
<NuxtPage />
<NuxtSnackbar />
</NuxtLayout>
@ -8,7 +8,7 @@
</template>
<script setup lang="ts">
const { currentTheme } = storeToRefs(useUiStore());
const { currentTheme } = storeToRefs(useUiStore())
</script>
<style>

View File

@ -1,16 +1,10 @@
<template>
<div class="browser">
<div class="browser-controls">
<button
@click="$emit('goBack', activeTabId)"
:disabled="!activeTabId"
>
<button @click="$emit('goBack', activeTabId)" :disabled="!activeTabId">
</button>
<button
@click="$emit('goForward', activeTabId)"
:disabled="!activeTabId"
>
<button @click="$emit('goForward', activeTabId)" :disabled="!activeTabId">
</button>
<button @click="$emit('createTab')">+</button>
@ -29,15 +23,9 @@
@activateTab="$emit('activateTab', $event)"
/>
<div
class="browser-content"
ref="contentRef"
>
<div class="browser-content" ref="contentRef">
<!-- Die eigentlichen Webview-Inhalte werden von Tauri verwaltet -->
<div
v-if="!activeTabId"
class="empty-state"
>
<div v-if="!activeTabId" class="empty-state">
<p>
Kein Tab geöffnet. Erstellen Sie einen neuen Tab mit dem + Button.
</p>
@ -47,9 +35,8 @@
</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';
import { Webview } from '@tauri-apps/api/webview'
import { Window } from '@tauri-apps/api/window'
/* const appWindow = new Window('uniqueLabel');
const webview = new Webview(appWindow, 'theUniqueLabel', {
url: 'https://www.google.de',
@ -64,46 +51,46 @@ webview.once('tauri://created', function () {
}); */
interface Tab {
id: string;
title: string;
url: string;
isLoading: boolean;
isActive: boolean;
window_label: string;
id: string
title: string
url: string
isLoading: boolean
isActive: boolean
window_label: string
}
interface Props {
tabs: Tab[];
activeTabId: string | null;
tabs: Tab[]
activeTabId: string | null
}
const props = defineProps<Props>();
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;
}>();
(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);
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();
await initializeAsync()
// Aktualisiere die Webview-Größe
await updateWebviewBoundsAsync();
await updateWebviewBoundsAsync()
//window.addEventListener('resize', updateWebviewBounds);
});
})
// Wenn ein neuer Tab aktiviert wird, injiziere Content-Scripts
/* watch(
@ -124,32 +111,46 @@ onMounted(async () => {
}
); */
const createNewTabAsync = async () => {
const appWindow = new Window(crypto.randomUUID())
appWindow.setAlwaysOnTop(true)
appWindow.setDecorations(false)
const webview = new Webview(appWindow, 'theUniqueLabel', {
url: 'https://www.google.de',
x: 0,
y: 0,
height: 1000,
width: 1000,
})
}
const handleUrlSubmit = (url: string) => {
createNewTabAsync()
if (props.activeTabId) {
// Prüfe URL mit Erweiterungen vor der Navigation
if (processNavigation(url)) {
emit('navigate', props.activeTabId, url);
/* if (processNavigation(url)) {
//emit('navigate', props.activeTabId, url);
} else {
console.log('Navigation blockiert durch Erweiterung');
console.log('Navigation blockiert durch Erweiterung')
// Hier könnten Sie eine Benachrichtigung anzeigen
}
} */
}
};
}
const updateWebviewBoundsAsync = async () => {
if (!contentRef.value) return;
if (!contentRef.value) return
const rect = contentRef.value.getBoundingClientRect();
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>

View File

@ -10,10 +10,7 @@
<span class="tab-title">
{{ tab.title || 'Neuer Tab' }}
</span>
<button
class="tab-close"
@click.stop="$emit('closeTab', tab.id)"
>
<button class="tab-close" @click.stop="$emit('closeTab', tab.id)">
×
</button>
</div>
@ -22,22 +19,22 @@
<script setup lang="ts">
interface Tab {
id: string;
title: string;
url: string;
isLoading: boolean;
isActive: boolean;
id: string
title: string
url: string
isLoading: boolean
isActive: boolean
}
interface Props {
tabs: Tab[];
activeTabId: string | null;
tabs: Tab[]
activeTabId: string | null
}
defineProps<Props>();
defineProps<Props>()
defineEmits<{
(e: 'closeTab', tabId: string): void;
(e: 'activateTab', tabId: string): void;
}>();
(e: 'closeTab', tabId: string): void
(e: 'activateTab', tabId: string): void
}>()
</script>

View File

@ -1,24 +1,8 @@
<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 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>
@ -32,26 +16,26 @@ const props = defineProps({
type: Boolean,
default: false,
},
});
})
const emit = defineEmits(['submit']);
const emit = defineEmits(['submit'])
const inputValue = ref(props.url);
const inputValue = ref(props.url)
watch(
() => props.url,
(newUrl) => {
inputValue.value = newUrl;
inputValue.value = newUrl
}
);
)
const handleSubmit = () => {
// URL validieren und ggf. Protokoll hinzufügen
let processedUrl = inputValue.value.trim();
let processedUrl = inputValue.value.trim()
if (processedUrl && !processedUrl.match(/^[a-zA-Z]+:\/\//)) {
processedUrl = 'https://' + processedUrl;
processedUrl = 'https://' + processedUrl
}
emit('submit', processedUrl);
};
emit('submit', processedUrl)
}
</script>

View File

@ -0,0 +1,59 @@
<template>
<UiDropdown activator-class="btn btn-text btn-circle">
<template #activator>
<div
class="size-9.5 rounded-full items-center justify-center text-base-content text-base"
>
<Icon name="mdi:format-list-bulleted" class="size-full p-2" />
</div>
</template>
<template #items>
<ul
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60"
role="menu"
aria-orientation="vertical"
aria-labelledby="dropdown-avatar"
>
<li>
<NuxtLinkLocale class="dropdown-item" :to="{ name: 'haexSettings' }">
<span class="icon-[tabler--settings]"></span>
{{ t('settings') }}
</NuxtLinkLocale>
</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>
</template>
</UiDropdown>
</template>
<script setup lang="ts">
const { t } = useI18n()
const { closeAsync } = useVaultStore()
const onVaultCloseAsync = async () => {
await closeAsync()
await navigateTo(useLocalePath()({ name: 'vaultOpen' }))
}
</script>
<i18n lang="yaml">
de:
settings: 'Einstellungen'
vault:
close: 'Vault schließen'
en:
settings: 'Settings'
vault:
close: 'Close Vault'
</i18n>

View File

@ -0,0 +1,54 @@
<template>
<div class="dropdown relative inline-flex">
<button
:id
class="dropdown-toggle"
:class="activatorClass"
aria-haspopup="menu"
aria-expanded="false"
:aria-label="label"
>
<slot name="activator">
<slot name="label">
{{ label }}
</slot>
<span
class="icon-[tabler--chevron-down] dropdown-open:rotate-180 size-4"
>
</span>
</slot>
</button>
<slot name="items" :items>
<ul
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28"
role="menu"
aria-orientation="vertical"
:aria-labelledby="id"
>
<component
:is="itemIs"
class="dropdown-item"
v-for="item in items"
@click="$emit('select', item)"
>
<slot name="item" :item>
{{ item }}
</slot>
</component>
</ul>
</slot>
</div>
</template>
<script setup lang="ts" generic="T">
const { itemIs = 'li' } = defineProps<{
label?: string
items?: T[]
itemIs?: string
activatorClass?: string
}>()
defineEmits<{ select: [T] }>()
const id = useId()
</script>

View File

@ -0,0 +1,33 @@
<template>
<UiDropdown
:items="availableLocales"
@select="(locale) => $emit('select', locale)"
activator-class="btn btn-primary"
>
<template #label>
<Icon :name="flags[locale]" />
</template>
<template #item="{ item }">
<div class="flex gap-2 justify-center">
<Icon :name="flags[item]" class="my-auto" />
<p>
{{ item }}
</p>
</div>
</template>
</UiDropdown>
</template>
<script setup lang="ts">
import { type Locale } from 'vue-i18n'
const flags = {
de: 'emojione:flag-for-germany',
en: 'emojione:flag-for-united-kingdom',
}
const { availableLocales, locale } = useI18n()
defineEmits<{ select: [Locale] }>()
</script>

View File

@ -0,0 +1,26 @@
<template>
<UiDropdown
:items="availableThemes"
@select="(theme) => $emit('select', theme)"
activator-class="btn btn-primary"
>
<template #label>
<Icon :name="currentTheme.icon" />
</template>
<template #item="{ item }">
<div class="flex gap-2 justify-center">
<Icon :name="item.icon" class="my-auto" />
<p>
{{ item.name }}
</p>
</div>
</template>
</UiDropdown>
</template>
<script setup lang="ts">
const { availableThemes, currentTheme } = storeToRefs(useUiStore())
defineEmits<{ select: [ITheme] }>()
</script>

View File

@ -1,78 +1,10 @@
<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>
</fieldset> -->
<fieldset class="join w-full p-1">
<div>
<fieldset class="join w-full">
<slot name="prepend" />
<div class="input join-item">
<Icon :name="prependIcon" class="my-auto shrink-0" />
<Icon v-if="prependIcon" :name="prependIcon" class="my-auto shrink-0" />
<div class="input-floating grow">
<input
@ -90,12 +22,13 @@
<label class="input-floating-label" :for="id">{{ label }}</label>
</div>
<Icon :name="appendIcon" class="my-auto shrink-0" />
<Icon v-if="appendIcon" :name="appendIcon" class="my-auto shrink-0" />
</div>
<slot name="append" class="h-auto" />
<UiButton
v-if="withCopyButton"
class="btn-outline btn-accent btn-square join-item h-auto"
@click="copy(`${input}`)"
>
@ -108,61 +41,61 @@
{{ error }}
</span>
</span>
</span>
</div>
</template>
<script setup lang="ts">
import { type ZodSchema } from "zod";
import { type ZodSchema } from 'zod'
const inputRef = useTemplateRef("inputRef");
defineExpose({ inputRef });
const inputRef = useTemplateRef('inputRef')
defineExpose({ inputRef })
defineOptions({
inheritAttrs: false,
});
})
const props = defineProps({
placeholder: {
type: String,
default: "",
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"
| '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",
default: 'text',
},
label: String,
name: String,
prependIcon: {
type: String,
default: "",
default: '',
},
prependLabel: String,
appendIcon: {
type: String,
default: "",
default: '',
},
appendLabel: String,
rules: Object as PropType<ZodSchema>,
@ -170,45 +103,45 @@ const props = defineProps({
withCopyButton: Boolean,
autofocus: Boolean,
read_only: Boolean,
});
})
const input = defineModel<string | number | undefined | null>({
default: "",
default: '',
required: true,
});
})
const { currentScreenSize } = storeToRefs(useUiStore());
const { currentScreenSize } = storeToRefs(useUiStore())
onMounted(() => {
if (props.autofocus && inputRef.value) inputRef.value.focus();
});
if (props.autofocus && inputRef.value) inputRef.value.focus()
})
const errors = defineModel<string[] | undefined>("errors");
const errors = defineModel<string[] | undefined>('errors')
const id = useId();
const id = useId()
watch(input, () => checkInput());
watch(input, () => checkInput())
watch(
() => props.checkInput,
() => {
checkInput();
checkInput()
}
);
)
const emit = defineEmits(["error"]);
const emit = defineEmits(['error'])
const checkInput = () => {
if (props.rules) {
const result = props.rules.safeParse(input.value);
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);
errors.value = result.error.errors.map((error) => error.message)
emit('error', errors.value)
} else {
errors.value = [];
errors.value = []
}
}
};
}
const { copy, copied } = useClipboard();
const { copy, copied } = useClipboard()
</script>

View File

@ -9,20 +9,22 @@
v-model="value"
>
<template #append>
<UiButton class="btn-outline btn-accent btn-square h-auto" @click="tooglePasswordType">
<Icon :name="type === 'password' ? 'mdi:eye' : 'mdi:eye-off'" />
<UiButton
class="btn-outline btn-accent btn-square h-auto"
@click="tooglePasswordType"
>
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
</UiButton>
</template>
</UiInput>
</template>
<script setup lang="ts">
import type { ZodSchema } from "zod";
import type { ZodSchema } from 'zod'
const { t } = useI18n();
const { currentScreenSize } = storeToRefs(useUiStore());
const { t } = useI18n()
const value = defineModel<string | number | null | undefined>();
const value = defineModel<string | number | null | undefined>()
defineProps({
label: String,
@ -30,13 +32,13 @@ defineProps({
checkInput: Boolean,
rules: Object as PropType<ZodSchema>,
autofocus: Boolean,
});
})
const type = ref<"password" | "text">("password");
const type = ref<'password' | 'text'>('password')
const tooglePasswordType = () => {
type.value = type.value === "password" ? "text" : "password";
};
type.value = type.value === 'password' ? 'text' : 'password'
}
</script>
<i18n lang="json">

View File

@ -6,12 +6,11 @@
@click="open = true"
>
<Icon name="mdi:plus" />
{{ t("database.create") }}
{{ 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')"
@ -32,98 +31,116 @@
<template #buttons>
<UiButton class="btn-error" @click="onClose">
{{ t("abort") }}
{{ t('abort') }}
</UiButton>
<UiButton class="btn-primary" @click="onCreateAsync">
{{ t("create") }}
{{ 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";
import { save } from '@tauri-apps/plugin-dialog'
import { useVaultStore } from '~/stores/vault'
import { vaultDatabaseSchema } from './schema'
import { onKeyStroke } from '@vueuse/core'
const check = ref(false);
const open = ref();
onKeyStroke('Enter', (e) => {
e.preventDefault()
onCreateAsync()
})
const { t } = useI18n();
const check = ref(false)
const open = ref()
const { t } = useI18n()
const database = reactive<{
name: string;
password: string;
path: string | null;
type: "password" | "text";
name: string
password: string
path: string | null
type: 'password' | 'text'
}>({
name: "",
password: "",
path: "",
type: "password",
});
name: '',
password: '',
path: '',
type: 'password',
})
const initDatabase = () => {
database.name = t("database.name");
database.password = "";
database.path = "";
database.type = "password";
};
database.name = t('database.name')
database.password = ''
database.path = ''
database.type = 'password'
}
initDatabase();
initDatabase()
const { add } = useSnackbar();
const { createAsync } = useVaultStore();
const { add } = useSnackbar()
const { createAsync } = useVaultStore()
const onCreateAsync = async () => {
check.value = true;
check.value = true
const nameCheck = vaultDatabaseSchema.name.safeParse(database.name);
const passwordCheck = vaultDatabaseSchema.password.safeParse(database.password);
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;
console.log(
'checks',
database.name,
nameCheck,
database.password,
passwordCheck
)
if (!nameCheck.success || !passwordCheck.success) return
open.value = false;
open.value = false
try {
database.path = await save({
defaultPath: database.name.endsWith(".db") ? database.name : `${database.name}.db`,
});
defaultPath: database.name.endsWith('.db')
? database.name
: `${database.name}.db`,
})
console.log("data", database);
console.log('data', database)
if (database.path && database.password) {
const vaultId = await createAsync({
path: database.path,
password: database.password,
});
})
console.log("vaultId", vaultId);
console.log('vaultId', vaultId)
if (vaultId) {
await navigateTo(useLocaleRoute()({ name: "vaultOverview", params: { vaultId } }));
await navigateTo(
useLocaleRoute()({ name: 'vaultOverview', params: { vaultId } })
)
}
}
} catch (error) {
console.error(error);
add({ type: "error", text: JSON.stringify(error) });
console.error(error)
add({ type: 'error', text: JSON.stringify(error) })
}
};
}
const onClose = () => {
open.value = false;
initDatabase();
};
open.value = false
initDatabase()
}
</script>
<i18n lang="json">
{
"de": {
"database": {
"label": "Datenbankname",
"placeholder": "Passwörter",
"label": "Vaultname",
"placeholder": "Vaultname",
"create": "Neue Vault anlegen",
"name": "Passwörter"
"name": "HaexVault"
},
"title": "Neue Datenbank anlegen",
"create": "Erstellen",
@ -133,10 +150,10 @@ const onClose = () => {
"en": {
"database": {
"label": "Databasename",
"placeholder": "Databasename",
"label": "Vaultname",
"placeholder": "Vaultname",
"create": "Create new Vault",
"name": "Passwords"
"name": "HaexVault"
},
"title": "Create New Database",
"create": "Create",

View File

@ -0,0 +1 @@
<template><div>first time</div></template>

View File

@ -1,5 +1,5 @@
<template>
<div class="w-full h-full flex flex-col min-w-min">
<div class="w-full h-full flex flex-col min-w-min relative overflow-hidden">
<nav
class="navbar bg-base-100 rounded-b max-sm:shadow border-b border-base-content/25 sm:z-20 relative px-2"
>
@ -14,7 +14,11 @@
ref="sidebarToogleRef"
>
<Icon
:name="isVisible ? 'tabler:layout-sidebar' : 'tabler:layout-sidebar-filled'"
:name="
isVisible
? 'tabler:layout-sidebar-filled'
: 'tabler:layout-sidebar'
"
size="28"
/>
</button>
@ -25,9 +29,12 @@
class="link text-base-content link-neutral text-xl font-semibold no-underline"
:to="{ name: 'vaultOverview' }"
>
<UiTextGradient class="text-nowrap">Haex Hub</UiTextGradient>
<UiTextGradient class="text-nowrap">{{
currentVaultName
}}</UiTextGradient>
</NuxtLinkLocale>
</div>
<div class="navbar-end flex items-center gap-4 me-4">
<div
class="dropdown relative inline-flex [--auto-close:inside] [--offset:8] [--placement:bottom-end]"
@ -45,7 +52,9 @@
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>
<span
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
></span>
</div>
</button>
<div
@ -56,7 +65,7 @@
>
<div class="dropdown-header justify-center">
<h6 class="text-base-content text-base">
{{ t("notifications.label") }}
{{ t('notifications.label') }}
</h6>
</div>
<div
@ -70,7 +79,10 @@
:src="notification.image"
:alt="notification.alt ?? 'notification avatar'"
/>
<Icon v-else-if="notification.icon" :name="notification.icon" />
<Icon
v-else-if="notification.icon"
:name="notification.icon"
/>
</div>
</div>
<div class="w-60">
@ -85,80 +97,16 @@
</div>
<a href="#" class="dropdown-footer justify-center gap-1">
<span class="icon-[tabler--eye] size-4"></span>
{{ t("notifications.view_all") }}
{{ 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>
<HaexMenuMain />
</div>
</nav>
<div class="flex h-full">
<div class="flex h-full overflow-hidden">
<aside
id="sidebar"
class="sm:shadow-none transition-all h-full overflow-hidden border-r border-base-300"
@ -179,7 +127,7 @@
</div>
</aside>
<main class="w-full">
<main class="w-full h-full overflow-scroll">
<NuxtPage :transition="{ name: 'fade' }" />
</main>
</div>
@ -187,32 +135,19 @@
</template>
<script setup lang="ts">
import { NuxtLinkLocale } from "#components";
const { t } = useI18n()
const { t } = useI18n();
const { menu, isVisible } = storeToRefs(useSidebarStore());
const sidebarToogleRef = useTemplateRef("sidebarToogleRef");
const { currentVaultName } = storeToRefs(useVaultStore())
/* onClickOutside(sidebarToogleRef, () => {
if (currentScreenSize.value === "xs") {
isVisible.value = false;
}
}); */
const { notifications } = storeToRefs(useNotificationStore());
const { menu, isVisible } = storeToRefs(useSidebarStore())
const { notifications } = storeToRefs(useNotificationStore())
const { extensionLinks } = storeToRefs(useExtensionsStore())
const { isActive } = useExtensionsStore();
const { closeAsync } = useVaultStore();
const { currentScreenSize } = storeToRefs(useUiStore());
const onExtensionSelectAsync = async (id: string) => {};
const { extensionLinks } = storeToRefs(useExtensionsStore());
const toogleSidebar = () => {
isVisible.value = !isVisible.value;
};
const onVaultCloseAsync = async () => {
await closeAsync();
await navigateTo(useLocalePath()({ name: "vaultOpen" }));
};
isVisible.value = !isVisible.value
}
</script>
<i18n lang="yaml">
@ -223,7 +158,7 @@ de:
vault:
close: Vault schließen
sidebar:
close: Sidebar schließen
close: Sidebar ausblenden
show: Sidebar anzeigen
en:
notifications:

View File

@ -1,11 +1,20 @@
<template>
<div class="items-center justify-center min-h-full flex w-full">
<div class="items-center justify-center min-h-full flex w-full relative">
<div class="fixed top-2 right-2">
<UiDropdownLocale @select="setLocale" />
</div>
<div class="flex flex-col justify-center items-center gap-5 max-w-3xl">
<img src="/logo.svg" class="bg-primary p-3 size-16 rounded-full" alt="HaexVault Logo" />
<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">
<span
class="flex flex-wrap font-bold text-pretty text-xl gap-2 justify-center"
>
<p class="whitespace-nowrap">
{{ t("welcome") }}
{{ t('welcome') }}
</p>
<UiTextGradient>Haex Hub</UiTextGradient>
</span>
@ -13,34 +22,15 @@
<div class="flex flex-col md:flex-row gap-4 w-full h-24 md:h-auto">
<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]"
<VaultButtonOpen
v-model:isOpen="passwordPromptOpen"
:path="vaultPath"
/>
<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") }}
{{ t('lastUsed') }}
</div>
<div
@ -53,10 +43,7 @@
>
<button
class="link link-accent flex items-center no-underline justify-between text-nowrap text-sm md:text-base shrink w-full py-2 px-4"
@click="
passwordPromptOpen = true;
vaultPath = vault.path;
"
@click=";(passwordPromptOpen = true), (vaultPath = vault.path)"
>
<span class="block md:hidden">
{{ vault.name }}
@ -68,14 +55,17 @@
<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)" />
<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>
<h4>{{ t('sponsors') }}</h4>
<div>
<button @click="openUrl('https://itemis.com')">
<UiLogoItemis class="text-[#00457C]" />
@ -87,41 +77,21 @@
</template>
<script setup lang="ts">
import { openUrl } from "@tauri-apps/plugin-opener";
const passwordPromptOpen = ref(false);
const vaultPath = ref("");
import { openUrl } from '@tauri-apps/plugin-opener'
definePageMeta({
name: "vaultOpen",
});
name: 'vaultOpen',
})
const { t } = useI18n();
const passwordPromptOpen = ref(false)
const vaultPath = ref('')
const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore();
const { lastVaults } = storeToRefs(useLastVaultStore());
const { t, setLocale } = useI18n()
await syncLastVaultsAsync();
const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore()
const { lastVaults } = storeToRefs(useLastVaultStore())
/* 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(); */
await syncLastVaultsAsync()
</script>
<i18n lang="json">

View File

@ -1,5 +1,5 @@
<template>
<div class="text-white h-full">
<div class="w-full h-full">
<NuxtLayout name="app">
<NuxtPage />
</NuxtLayout>
@ -8,6 +8,6 @@
<script setup lang="ts">
definePageMeta({
middleware: "database",
});
middleware: 'database',
})
</script>

View File

@ -4,7 +4,7 @@
{{ iframeSrc }}
</div> -->
<iframe
v-if="iframeSrc"
v-if="iframeIndex"
class="w-full h-full"
@load=""
ref="iFrameRef"
@ -13,14 +13,25 @@
allow="autoplay; speaker-selection; encrypted-media;"
>
</iframe>
<UiButton @click="go = true">Go</UiButton>
<!-- <p v-else>{{ t("loading") }}</p> -->
<audio controls :src="audioTest">
{{ audioTest }}
<audio v-if="go" controls :src="audioTest">
Dein Browser unterstützt das Audio-Element nicht.
</audio>
<video v-if="go" controls width="600" :src="demoVideo"></video>
<div v-if="audioError">
Fehler beim Laden der Audio-Datei: {{ audioError }}
</div>
</div>
</template>
<script setup lang="ts">
import { convertFileSrc } from '@tauri-apps/api/core'
import { appDataDir, join, resourceDir } from '@tauri-apps/api/path'
definePageMeta({
name: 'haexExtension',
})
@ -30,25 +41,34 @@ const iframeRef = useTemplateRef('iFrameRef')
const { extensionEntry: iframeSrc, currentExtension } = storeToRefs(
useExtensionsStore()
)
const audioTest = computed(() => `${iframeSrc.value}/sounds/music/demo.mp3`)
watch(audioTest, () => console.log('audioTest', audioTest.value), {
immediate: true,
})
const audioAssetUrl = ref('')
const audioError = ref('')
const audioTest = convertFileSrc(
await join(await appDataDir(), 'resources/demo.mp3')
)
//computed(() => `${iframeSrc.value}/sounds/music/demo.mp3`)
const go = ref(false)
const iframeIndex = computed(() => `${iframeSrc.value}/index.html`)
const demoVideo = computed(() => `${iframeSrc.value}/sounds/music/demo.mp3`)
const extensionStore = useExtensionsStore()
watch(iframeSrc, () => console.log('iframeSrc', iframeSrc.value), {
immediate: true,
})
watch(
demoVideo,
async () => {
const res = await fetch(
'/home/haex/.local/share/space.haex.hub/extensions/pokedemo/1.0/sounds/music/demo.mp3'
)
console.log('respo', res)
onMounted(async () => {
/* const minfest = await extensionStore.readManifestFileAsync(
currentExtension.value!.id,
currentExtension.value!.version
);
console.log("manifest", minfest, extensionStore.extensionEntry); */
})
console.log('iframeSrc', iframeSrc.value)
},
{
immediate: true,
}
)
</script>
<i18n lang="yaml">

View File

@ -1,15 +1,15 @@
<template>
<div class="h-full"></div>
<div class="h-full text-base-content"></div>
</template>
<script setup lang="ts">
definePageMeta({
name: "vaultOverview",
});
name: 'vaultOverview',
})
const extensionStore = useExtensionsStore();
const extensionStore = useExtensionsStore()
onMounted(async () => {
await extensionStore.loadExtensionsAsync();
});
await extensionStore.loadExtensionsAsync()
})
</script>

View File

@ -0,0 +1,77 @@
<template>
<div
class="grid grid-rows-2 sm:grid-cols-2 sm:gap-2 p-2 max-w-2xl w-full h-fit"
>
<div class="p-2">{{ t('language') }}</div>
<div><UiDropdownLocale @select="onSelectLocaleAsync" /></div>
<div class="p-2">{{ t('design') }}</div>
<div><UiDropdownTheme @select="onSelectThemeAsync" /></div>
<div class="p-2">{{ t('vaultName') }}</div>
<div>
<UiInput v-model="currentVaultName" :placeholder="t('vaultName')">
<template #append>
<UiTooltip :tooltip="t('save')">
<UiButton class="btn-primary" @click="onSetVaultNameAsync">
<Icon name="mdi:content-save-outline" />
</UiButton>
</UiTooltip>
</template>
</UiInput>
</div>
</div>
</template>
<script setup lang="ts">
import { eq } from 'drizzle-orm'
import { type Locale } from 'vue-i18n'
import { haexSettings } from '~~/src-tauri/database/schemas/vault'
definePageMeta({
name: 'haexSettings',
})
const { t, setLocale } = useI18n()
const { currentVault, currentVaultName } = storeToRefs(useVaultStore())
const { updateVaultNameAsync } = useVaultStore()
const onSelectLocaleAsync = async (locale: Locale) => {
console.log('onSelectLocaleAsync', locale)
const update = await currentVault.value?.drizzle
.update(haexSettings)
.set({ key: 'locale', value: locale })
.where(eq(haexSettings.key, 'locale'))
await setLocale(locale)
console.log('update locale', update)
}
const { currentTheme } = storeToRefs(useUiStore())
const onSelectThemeAsync = async (theme: ITheme) => {
const update = await currentVault.value?.drizzle
.update(haexSettings)
.set({ key: 'theme', value: theme.name })
.where(eq(haexSettings.key, 'theme'))
currentTheme.value = theme
}
const onSetVaultNameAsync = async (vaultName: string) => {
updateVaultNameAsync(vaultName)
}
</script>
<i18n lang="yaml">
de:
language: Sprache
design: Design
vaultName: Vaultname
save: Änderung speichern
en:
language: Language
design: Design
vaultName: Vault Name
save: save changes
</i18n>

View File

@ -46,6 +46,7 @@ export const useBrowserExtensionStore = defineStore(
const initializeAsync = async () => {
const { isInitialized } = storeToRefs(useBrowserExtensionStore());
return
if (isInitialized.value) return;
// Lade Erweiterungen aus dem Erweiterungsverzeichnis
@ -87,4 +88,4 @@ const processNavigation = (url: string) => {
return true;
};
const injectContentScripts = (t: string) => {};
const injectContentScripts = (t: string) => { };

View File

@ -285,12 +285,12 @@ export const useExtensionsStore = defineStore("extensionsStore", () => {
const extensionEntry = computedAsync(
async () => {
try {
console.log("extensionEntry start", currentExtension.value);
const regex = /((href|src)=["'])([^"']+)(["'])/g;
/* console.log("extensionEntry start", currentExtension.value);
const regex = /((href|src)=["'])([^"']+)(["'])/g; */
if (!currentExtension.value?.id || !currentExtension.value.version) {
console.log("extension id or entry missing", currentExtension.value);
return "no mani: " + currentExtension.value;
return ""// "no mani: " + currentExtension.value;
}
const extensionPath = await getExtensionPathAsync(
@ -304,7 +304,7 @@ export const useExtensionsStore = defineStore("extensionsStore", () => {
currentExtension.value.version
);
if (!manifest) return "no manifest readable";
if (!manifest) return ""//"no manifest readable";
const entryPath = await join(extensionPath, manifest.entry);
@ -322,12 +322,12 @@ export const useExtensionsStore = defineStore("extensionsStore", () => {
console.log("entryHtml", entryHtml);
const replacements = [];
let match;
while ((match = regex.exec(entryHtml)) !== null) {
/* while ((match = regex.exec(entryHtml)) !== null) {
const [fullMatch, prefix, attr, resource, suffix] = match;
if (!resource.startsWith("http")) {
replacements.push({ match: fullMatch, resource, prefix, suffix });
}
}
} */
for (const { match, resource, prefix, suffix } of replacements) {
const srcFile = convertFileSrc(await join(extensionPath, resource));

View File

@ -1,5 +1,6 @@
{
"light": "Hell",
"dark": "Dunkel",
"soft": "Soft"
"soft": "Soft",
"corporate": "Corporate"
}

View File

@ -1,5 +1,6 @@
{
"light": "Light",
"dark": "Dark",
"soft": "Soft"
"soft": "Soft",
"corporate": "Corporate"
}

View File

@ -2,6 +2,12 @@ import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
import de from './de.json';
import en from './en.json';
export interface ITheme {
value: string,
name: string,
icon: string
}
export const useUiStore = defineStore('uiStore', () => {
const breakpoints = useBreakpoints(breakpointsTailwind);
@ -28,14 +34,22 @@ export const useUiStore = defineStore('uiStore', () => {
icon: 'line-md:moon-to-sunny-outline-loop-transition',
},
{ value: 'soft', name: t('ui.soft'), icon: 'line-md:paint-drop' },
{
value: 'corporate',
name: t('ui.corporate'),
icon: 'hugeicons:corporate',
},
]);
const currentTheme = ref(availableThemes.value[0].value);
const defaultTheme = ref(availableThemes.value[0])
const currentTheme = ref(defaultTheme);
return {
availableThemes,
breakpoints,
currentScreenSize,
currentTheme,
availableThemes,
defaultTheme,
};
});

View File

@ -13,13 +13,6 @@ export const useSidebarStore = defineStore("sidebarStore", () => {
const isVisible = ref(true);
const menu = ref<ISidebarItem[]>([
{
id: "haex-browser",
name: "Haex Browser",
icon: "solar:global-outline",
to: { name: "haexBrowser" },
},
{
id: "haex-extensions-add",
name: "Haex Extensions",

View File

@ -1,11 +1,9 @@
//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 * as schema from "@/../src-tauri/database/schemas/vault";
import { invoke } from "@tauri-apps/api/core";
import { and, count, eq } from "drizzle-orm";
import { platform } from "@tauri-apps/plugin-os";
import { hostname, platform, type, version } from "@tauri-apps/plugin-os";
import { eq } from "drizzle-orm";
import { drizzle, SqliteRemoteDatabase } from "drizzle-orm/sqlite-proxy";
interface IVault {
name: string;
@ -16,6 +14,7 @@ interface IOpenVaults {
}
export const useVaultStore = defineStore("vaultStore", () => {
const currentVaultId = computed<string | undefined>({
get: () => getSingleRouteParam(useRouter().currentRoute.value.params.vaultId),
set: (newVaultId) => {
@ -23,6 +22,9 @@ export const useVaultStore = defineStore("vaultStore", () => {
},
});
const defaultVaultName = ref("HaexHub")
const currentVaultName = ref(defaultVaultName.value)
const read_only = computed<boolean>({
get: () => {
console.log("query showSidebar", useRouter().currentRoute.value.query.readonly);
@ -53,6 +55,8 @@ export const useVaultStore = defineStore("vaultStore", () => {
{ immediate: true }
);
const hostKey = computedAsync(async () => "".concat(type(), version(), await hostname() ?? ""))
const openAsync = async ({ path = "", password }: { path: string; password: string }) => {
try {
const result = await invoke<string>("open_encrypted_database", {
@ -102,7 +106,11 @@ export const useVaultStore = defineStore("vaultStore", () => {
};
const { addVaultAsync } = useLastVaultStore();
await addVaultAsync({ path });
addVaultAsync({ path });
syncLocaleAsync()
syncThemeAsync()
syncVaultNameAsync()
return vaultId;
} catch (error) {
@ -147,15 +155,86 @@ export const useVaultStore = defineStore("vaultStore", () => {
delete openVaults.value?.[currentVaultId.value];
};
const syncLocaleAsync = async () => {
try {
const app = useNuxtApp()
app.$i18n.availableLocales
//const { availableLocales, defaultLocale, setLocale, locale } = useI18n()
const currentLocaleRow = await currentVault.value?.drizzle
.select()
.from(schema.haexSettings)
.where(eq(schema.haexSettings.key, 'locale'))
if (currentLocaleRow?.[0]?.value) {
const currentLocale = app.$i18n.availableLocales.find(
(locale) => locale === currentLocaleRow[0].value
)
await app.$i18n.setLocale(currentLocale ?? app.$i18n.defaultLocale)
} else {
await currentVault.value?.drizzle
.insert(schema.haexSettings)
.values({ id: crypto.randomUUID(), key: 'locale', value: app.$i18n.locale.value })
}
} catch (error) {
console.log("ERROR syncLocaleAsync", error)
}
}
const syncThemeAsync = async () => {
const { availableThemes, defaultTheme, currentTheme } = storeToRefs(useUiStore())
const currentThemeRow = await currentVault.value?.drizzle
.select()
.from(schema.haexSettings)
.where(eq(schema.haexSettings.key, 'theme'))
if (currentThemeRow?.[0]?.value) {
const theme = availableThemes.value.find(
(theme) => theme.name === currentThemeRow[0].value
)
currentTheme.value = theme ?? defaultTheme.value
} else {
await currentVault.value?.drizzle.insert(schema.haexSettings).values({
id: crypto.randomUUID(),
key: 'theme',
value: currentTheme.value.name,
})
}
}
const syncVaultNameAsync = async () => {
const currentVaultNameRow = await currentVault.value?.drizzle
.select()
.from(schema.haexSettings)
.where(eq(schema.haexSettings.key, 'vaultName'))
if (currentVaultNameRow?.[0]?.value) {
currentVaultName.value = currentVaultNameRow.at(0)?.value ?? defaultVaultName.value
} else {
await currentVault.value?.drizzle.insert(schema.haexSettings).values({
id: crypto.randomUUID(),
key: 'vaultName',
value: currentVaultName.value,
})
}
}
const updateVaultNameAsync = async (newVaultName?: string | null) => {
return currentVault.value?.drizzle.update(schema.haexSettings).set({ value: newVaultName ?? defaultVaultName.value }).where(eq(schema.haexSettings.key, "vaultName"))
}
return {
closeAsync,
createAsync,
currentVault,
currentVaultId,
currentVaultName,
hostKey,
openAsync,
openVaults,
refreshDatabaseAsync,
read_only,
refreshDatabaseAsync,
updateVaultNameAsync,
};
});
@ -174,3 +253,6 @@ const isSelectQuery = (sql: string) => {
const selectRegex = /^\s*SELECT\b/i;
return selectRegex.test(sql);
};

View File

@ -1,148 +0,0 @@
import { defineConfig } from '@nuxtjs/tailwindcss/config';
//import { iconsPlugin, dynamicIconsPlugin } from '@egoist/tailwindcss-icons';
//import colors from 'tailwindcss/colors';
import themes from 'flyonui/src/theming/themes';
//import * as tailwindMotion from 'tailwindcss-motion';
import { addDynamicIconSelectors } from '@iconify/tailwind';
export default defineConfig({
content: ['./src/**/*.{vue,ts,svg}', './node_modules/flyonui/dist/js/*.js'],
darkMode: 'selector',
plugins: [
/* iconsPlugin(),
dynamicIconsPlugin(), */
addDynamicIconSelectors(),
require('flyonui'),
require('flyonui/plugin'),
//tailwindMotion,
],
flyonui: {
themes: [
{
light: {
...themes.light,
/* primary: colors.teal[500],
secondary: colors.purple[500], */
},
soft: {
...themes.soft,
/* primary: colors.teal[500],
secondary: colors.purple[500], */
},
dark: {
...themes.dark,
/* primary: colors.cyan[700], //colors.teal[600],
secondary: colors.purple[500], */
/* 'primary-content': '#000516',
'secondary': '#008f9c',
'secondary-content': '#000709',
'accent': '#007f7a',
'accent-content': '#d3e5e3',
'neutral': '#321a15',
'neutral-content': '#d3ccca',
'base-100': '#002732',
'base-200': '#00202a',
'base-300': '#001a22',
'base-content': '#c8cfd2',
'info': '#0086b2',
'info-content': '#00060c',
'success': '#a5da00',
'success-content': '#0a1100',
'warning': '#ff8d00',
'warning-content': '#160700',
'error': '#c83849',
'error-content': '#f9d9d9', */
},
},
],
/* themes: [
{
dark: {
'primary': colors.teal[500],
'primary-content': '#010811',
'secondary': colors.purple[500],
'secondary-content': '#130201',
'accent': '#9b59b6',
'accent-content': '#ebddf1',
'neutral': '#95a5a6',
'neutral-content': '#080a0a',
'base-100': colors.slate[100],
'base-200': colors.slate[400],
'base-300': colors.slate[900],
'base-content': colors.slate[800],
'info': '#1abc9c',
'info-content': '#000d09',
'success': '#2ecc71',
'success-content': '#010f04',
'warning': '#f1c40f',
'warning-content': '#140e00',
'error': '#e74c3c',
'error-content': '#130201',
},
light: {
'primary': colors.teal[500],
'primary-content': '#010811',
'secondary': colors.purple[500],
'secondary-content': '#130201',
'accent': '#9b59b6',
'accent-content': '#ebddf1',
'neutral': '#95a5a6',
'neutral-content': '#080a0a',
'base-100': '#ecf0f1',
'base-200': '#cdd1d2',
'base-300': '#afb2b3',
'base-content': '#131414',
'info': '#1abc9c',
'info-content': '#000d09',
'success': '#2ecc71',
'success-content': '#010f04',
'warning': '#f1c40f',
'warning-content': '#140e00',
'error': '#e74c3c',
'error-content': '#130201',
},
},
], */
},
/* theme: {
extend: {
colors: {
'primary-active': colors.teal[600],
'primary-focus': colors.teal[400],
'primary-hover': colors.teal[400],
'primary': colors.teal[500],
'dark': {
'primary-active': colors.teal[700],
'primary-focus': colors.teal[600],
'primary-hover': colors.teal[600],
'primary': colors.teal[500],
},
'secondary': colors.sky[500],
},
fontFamily: {
sans: [
'Adelle',
'Roboto Slab',
'DejaVu Serif',
'Georgia',
'Graphik',
'sans-serif',
],
serif: ['Merriweather', 'serif'],
},
screens: {
xs: '360px',
},
transitionProperty: {
height: 'height',
},
},
}, */
}); // satisfies Config;