mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 14:10:52 +01:00
added settings page, cleanup
This commit is contained in:
@ -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"]
|
||||
},
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"opener:allow-open-url",
|
||||
"opener:default",
|
||||
"os:default",
|
||||
"os:allow-hostname",
|
||||
"store:default"
|
||||
]
|
||||
}
|
||||
|
||||
@ -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`;
|
||||
180
src-tauri/database/migrations/meta/0002_snapshot.json
Normal file
180
src-tauri/database/migrations/meta/0002_snapshot.json
Normal 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": {}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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.
@ -41,7 +41,6 @@ pub fn run() {
|
||||
})
|
||||
}
|
||||
}
|
||||
//extension::core::extension_protocol_handler(&context, &request)
|
||||
})
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.manage(DbConnection(Mutex::new(None)))
|
||||
|
||||
@ -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/**"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
59
src/components/haex/menu/main.vue
Normal file
59
src/components/haex/menu/main.vue
Normal 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>
|
||||
54
src/components/ui/dropdown/index.vue
Normal file
54
src/components/ui/dropdown/index.vue
Normal 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>
|
||||
33
src/components/ui/dropdown/locale.vue
Normal file
33
src/components/ui/dropdown/locale.vue
Normal 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>
|
||||
26
src/components/ui/dropdown/theme.vue
Normal file
26
src/components/ui/dropdown/theme.vue
Normal 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>
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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",
|
||||
|
||||
1
src/components/vault/dialog/host/firstTime.vue
Normal file
1
src/components/vault/dialog/host/firstTime.vue
Normal file
@ -0,0 +1 @@
|
||||
<template><div>first time</div></template>
|
||||
@ -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:
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
77
src/pages/vault/[vaultId]/settings.vue
Normal file
77
src/pages/vault/[vaultId]/settings.vue
Normal 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>
|
||||
@ -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) => { };
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"soft": "Soft"
|
||||
"soft": "Soft",
|
||||
"corporate": "Corporate"
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"soft": "Soft"
|
||||
"soft": "Soft",
|
||||
"corporate": "Corporate"
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
});
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
Reference in New Issue
Block a user