Use nuxt 4 folder structure
This commit is contained in:
8
app/app.config.ts
Normal file
8
app/app.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export default defineAppConfig({
|
||||
name: "Nuxtor",
|
||||
author: "Nicola Spadari",
|
||||
repo: "https://github.com/NicolaSpadari/nuxtor",
|
||||
tauriSite: "https://v2.tauri.app",
|
||||
nuxtSite: "https://nuxt.com",
|
||||
unoSite: "https://unocss.dev"
|
||||
});
|
36
app/app.vue
Normal file
36
app/app.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<Html min-h-screen>
|
||||
<Body overflow-x-hidden bg-dark-800 text-white>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</Body>
|
||||
</Html>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
html {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
img {
|
||||
-webkit-user-drag: none;
|
||||
@apply select-none;
|
||||
}
|
||||
|
||||
// Transitions
|
||||
.page-enter-active,
|
||||
.page-leave-active {
|
||||
@apply transition-opacity ease-in-out duration-300;
|
||||
}
|
||||
.layout-enter-active,
|
||||
.layout-leave-active {
|
||||
@apply transition-opacity ease-in-out duration-500;
|
||||
}
|
||||
.page-enter-from,
|
||||
.page-leave-to,
|
||||
.layout-enter-from,
|
||||
.layout-leave-to {
|
||||
@apply opacity-0;
|
||||
}
|
||||
</style>
|
4
app/assets/logo.svg
Normal file
4
app/assets/logo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.018652355298399925 149.6599884033203 150.74864196777344">
|
||||
<path d="M74.59 150.73H4.11c-4 0-4.08-.08-4.08-4.15Q0 88.14 0 29.69c0-2.5.7-4.09 2.9-5.32 8.55-4.8 17-9.71 25.51-14.61 1.69-1 2.53-.64 2.52 1.38v2.28c0 27.38 0 54.77-.05 82.15a5.59 5.59 0 0 0 3.24 5.6Q53.3 112 72.3 123.2a4.62 4.62 0 0 0 5.18 0Q95 113 112.51 102.84a.92.92 0 0 1 .21-.13c6.06-1.78 6.63-6.17 6.57-11.77-.28-25.77-.12-51.56-.11-77.34v-2.28c0-1.83.8-2.29 2.38-1.4 2.65 1.48 5.31 3 7.93 4.49 6.06 3.52 12.08 7.11 18.17 10.58 1.79 1 2 2.49 2 4.27v117.15c0 4-.28 4.3-4.34 4.3H74.59z" fill="#00DC82"></path>
|
||||
<path d="M51.79 43.09V3.82C51.8.28 52 .07 55.5.05c2.87 0 5.74.09 8.61 0 2.23-.09 3 .84 2.93 3-.11 13 .56 26 .44 39-.15 16.21 0 32.43 0 48.64 0 3.66-.5 3.9-3.74 2-3.07-1.76-6.12-3.57-9.28-5.18a4.54 4.54 0 0 1-2.69-4.63c.08-13.26 0-26.52 0-39.77zM97.91 43.09v39.55a4.89 4.89 0 0 1-2.94 5c-3.22 1.66-6.25 3.69-9.42 5.45-2.51 1.39-3.41.87-3.33-2 .74-29.39.29-58.79.38-88.18 0-2.24.88-3 3-2.92 3 .11 6.08.16 9.12 0C97.31-.13 98 1 97.93 3.3c-.07 13.26 0 26.53 0 39.79z" fill="#FFC131"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
5
app/components/Btn.vue
Normal file
5
app/components/Btn.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<button class="disabled:(cursor-not-allowed opacity-60)" gap-1 rounded-md bg-emerald-500 px-3.5 py-2.5 text-sm text-white font-semibold shadow-sm transition-colors hover="bg-emerald-600" focus-visible="outline-2 outline-emerald-600 outline-offset-2 outline">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
11
app/components/Design/BottomBlob.vue
Normal file
11
app/components/Design/BottomBlob.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div pointer-events-none absolute inset-x-0 top="1/5" transform-gpu blur-3xl z="-10" aria-hidden="true">
|
||||
<div class="blob" relative left="[calc(50%+36rem)]" aspect="[1155/678]" w="[72.1875rem]" from-emerald-500 to-amber-400 bg-gradient-to-tr opacity-30 translate-x="-1/2" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blob{
|
||||
clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%);
|
||||
}
|
||||
</style>
|
11
app/components/Design/TopBlob.vue
Normal file
11
app/components/Design/TopBlob.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div pointer-events-none absolute inset-x-0 transform-gpu blur-3xl top="-1/4" z="-10" aria-hidden="true">
|
||||
<div class="blob" relative left="[calc(50%-30rem)]" aspect="[1155/678]" w="[72.1875rem]" rotate-30 from-amber-400 to-emerald-500 bg-gradient-to-tr opacity-30 translate-x="-1/2" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blob{
|
||||
clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%);
|
||||
}
|
||||
</style>
|
5
app/components/Hyperlink.vue
Normal file
5
app/components/Hyperlink.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<NuxtLink gap-1 rounded-md bg-emerald-500 px-3.5 py-2.5 text-sm text-white font-semibold shadow-sm transition-colors hover="bg-emerald-600" focus-visible="outline-2 outline-emerald-600 outline-offset-2 outline">
|
||||
<slot />
|
||||
</NuxtLink>
|
||||
</template>
|
24
app/components/Layout/Tile.vue
Normal file
24
app/components/Layout/Tile.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div grid grid-cols-1 lg="grid-cols-2">
|
||||
<div px-6 pb-10 pt-12 lg="px-8 pt-22" sm="pt-16">
|
||||
<div mx-auto max-w-xl lg="mx-0 max-w-lg">
|
||||
<h2 text-3xl text-white font-bold tracking-tight>
|
||||
{{ props.title }}
|
||||
</h2>
|
||||
<p mt-6 text-lg text-gray-300 leading-8>
|
||||
{{ props.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div px-6 pb-10 pt-12 lg="px-8 pt-22" sm="pt-16">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
description: string
|
||||
}>();
|
||||
</script>
|
5
app/components/NavLink.vue
Normal file
5
app/components/NavLink.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<NuxtLink lg="inline text-sm py-0" block py-2 text-white font-semibold leading-6>
|
||||
<slot />
|
||||
</NuxtLink>
|
||||
</template>
|
49
app/components/Site/Navbar.vue
Normal file
49
app/components/Site/Navbar.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<header top-0 z-10>
|
||||
<nav flex items-center justify-between py-6 crate>
|
||||
<div flex lg="flex-1">
|
||||
<NuxtLink to="/" class="home" p-1.5 m="-1.5">
|
||||
<SvgoLogo :font-controlled="false" size-8 />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div flex lg="hidden">
|
||||
<button type="button" inline-flex items-center justify-center rounded-md p-2.5 text-neutral-300 m="-2.5" @click="showSidebar = true">
|
||||
<Icon name="heroicons-solid:bars-3" size-6 />
|
||||
</button>
|
||||
</div>
|
||||
<div hidden lg="flex gap-x-12">
|
||||
<NavLink to="/commands">
|
||||
Commands
|
||||
</NavLink>
|
||||
<NavLink to="/notifications">
|
||||
Notifications
|
||||
</NavLink>
|
||||
<NavLink to="/os">
|
||||
OS Informations
|
||||
</NavLink>
|
||||
<NavLink to="/file">
|
||||
File
|
||||
</NavLink>
|
||||
<NavLink to="/store">
|
||||
Store
|
||||
</NavLink>
|
||||
</div>
|
||||
<div hidden lg="flex flex-1 justify-end">
|
||||
<p text-sm text-white font-semibold leading-6>
|
||||
v{{ tauriVersion }}
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { showSidebar } = useSidebar();
|
||||
const tauriVersion = await useTauriAppGetTauriVersion();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.router-link-exact-active:not(.home){
|
||||
@apply text-emerald-500;
|
||||
}
|
||||
</style>
|
55
app/components/Site/Sidebar.vue
Normal file
55
app/components/Site/Sidebar.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<Transition
|
||||
enter-from-class="opacity-0" enter-active-class="ease-in-out duration-500" enter-to-class="opacity-100"
|
||||
leave-from-class="opacity-100" leave-active-class="ease-in-out duration-500" leave-to-class="opacity-0"
|
||||
>
|
||||
<div v-if="showSidebar" relative z-20 lg="hidden" role="dialog" aria-modal="true">
|
||||
<div fixed inset-0 overflow-hidden>
|
||||
<div bg="neutral-800/70" h-full flex flex-col overflow-y-auto py-6 backdrop-blur-md crate>
|
||||
<div flex items-center justify-between>
|
||||
<NuxtLink to="/" p-1.5 m="-1.5">
|
||||
<SvgoLogo :filled="true" :font-controlled="false" size-8 />
|
||||
</NuxtLink>
|
||||
<button type="button" rounded-md p-2.5 text-neutral-300 m="2.5" @click="showSidebar = false">
|
||||
<Icon name="heroicons-solid:x-mark" size-6 />
|
||||
</button>
|
||||
</div>
|
||||
<div mt-6 flow-root>
|
||||
<div my="-6" divide-y divide="gray-500/25">
|
||||
<div py-6 space-y-2>
|
||||
<NavLink to="/commands">
|
||||
Commands
|
||||
</NavLink>
|
||||
<NavLink to="/notifications">
|
||||
Notifications
|
||||
</NavLink>
|
||||
<NavLink to="/os">
|
||||
OS Informations
|
||||
</NavLink>
|
||||
<NavLink to="/foo">
|
||||
404
|
||||
</NavLink>
|
||||
</div>
|
||||
<div py-6>
|
||||
<p px-3 text-base text-white font-semibold leading-7 mx="-3">
|
||||
v{{ tauriVersion }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const tauriVersion = await useTauriAppGetTauriVersion();
|
||||
const { showSidebar } = useSidebar();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.router-link-exact-active:not(.home){
|
||||
@apply text-emerald-500;
|
||||
}
|
||||
</style>
|
17
app/components/Tab/Row.vue
Normal file
17
app/components/Tab/Row.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div px-4 py-6 sm="grid grid-cols-3 gap-4 px-6">
|
||||
<dt flex items-center text-sm text-light-200 font-medium>
|
||||
{{ props.heading }}
|
||||
</dt>
|
||||
<dd flex items-center text-sm text-neutral-300 leading-6 sm="col-span-2 mt-0">
|
||||
{{ props.body }}
|
||||
</dd>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps<{
|
||||
heading: string
|
||||
body: string
|
||||
}>();
|
||||
</script>
|
7
app/composables/sidebar.ts
Normal file
7
app/composables/sidebar.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const useSidebar = () => {
|
||||
const showSidebar = useState("sidebar", () => false);
|
||||
|
||||
return {
|
||||
showSidebar
|
||||
};
|
||||
};
|
8
app/layouts/blank.vue
Normal file
8
app/layouts/blank.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<SiteNavbar sticky />
|
||||
<SiteSidebar />
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
10
app/layouts/default.vue
Normal file
10
app/layouts/default.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<SiteNavbar sticky />
|
||||
<SiteSidebar />
|
||||
|
||||
<div crate>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
13
app/layouts/home.vue
Normal file
13
app/layouts/home.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<SiteNavbar class="fixed w-full" />
|
||||
<SiteSidebar />
|
||||
|
||||
<div relative overflow-hidden px-6 lg="px-8">
|
||||
<DesignTopBlob />
|
||||
<DesignBottomBlob />
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
4
app/middleware/sidebar.global.ts
Normal file
4
app/middleware/sidebar.global.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default defineNuxtRouteMiddleware(() => {
|
||||
const { showSidebar } = useSidebar();
|
||||
showSidebar.value = false;
|
||||
});
|
39
app/modules/tauri.ts
Normal file
39
app/modules/tauri.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import * as tauriApp from "@tauri-apps/api/app";
|
||||
import * as tauriFs from "@tauri-apps/plugin-fs";
|
||||
import * as tauriNotification from "@tauri-apps/plugin-notification";
|
||||
import * as tauriOs from "@tauri-apps/plugin-os";
|
||||
import * as tauriShell from "@tauri-apps/plugin-shell";
|
||||
import * as tauriStore from "@tauri-apps/plugin-store";
|
||||
import { addImports, defineNuxtModule } from "nuxt/kit";
|
||||
|
||||
const capitalize = (name: string) => {
|
||||
return name.charAt(0).toUpperCase() + name.slice(1);
|
||||
};
|
||||
|
||||
const tauriModules = [
|
||||
{ module: tauriApp, prefix: "App", importPath: "@tauri-apps/api/app" },
|
||||
{ module: tauriShell, prefix: "Shell", importPath: "@tauri-apps/plugin-shell" },
|
||||
{ module: tauriOs, prefix: "Os", importPath: "@tauri-apps/plugin-os" },
|
||||
{ module: tauriNotification, prefix: "Notification", importPath: "@tauri-apps/plugin-notification" },
|
||||
{ module: tauriFs, prefix: "Fs", importPath: "@tauri-apps/plugin-fs" },
|
||||
{ module: tauriStore, prefix: "Store", importPath: "@tauri-apps/plugin-store" },
|
||||
];
|
||||
|
||||
export default defineNuxtModule<ModuleOptions>({
|
||||
meta: {
|
||||
name: "nuxt-tauri",
|
||||
configKey: "nuxt-tauri"
|
||||
},
|
||||
defaults: {
|
||||
prefix: "useTauri"
|
||||
},
|
||||
setup(options) {
|
||||
tauriModules.forEach(({ module, prefix, importPath }) => {
|
||||
Object.keys(module).filter((name) => name !== "default").forEach((name) => {
|
||||
const prefixedName = `${options.prefix}${prefix}` || "";
|
||||
const as = prefixedName ? prefixedName + capitalize(name) : name;
|
||||
addImports({ from: importPath, name, as });
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
26
app/pages/[...all].vue
Normal file
26
app/pages/[...all].vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div grid place-items-center px-6 py-24 sm="py-32" lg="px-8">
|
||||
<div text-center>
|
||||
<p text-base text-emerald-500 font-semibold>
|
||||
404
|
||||
</p>
|
||||
<h1 mt-4 text-3xl text-neutral-300 font-bold tracking-tight sm="text-5xl">
|
||||
Page not found
|
||||
</h1>
|
||||
<p mt-6 text-base text-neutral-400 leading-7>
|
||||
Sorry, we couldn't find the page you're looking for.
|
||||
</p>
|
||||
<div mt-10 flex items-center justify-center gap-x-6>
|
||||
<Hyperlink to="/" px-7>
|
||||
Back
|
||||
</Hyperlink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
layout: "blank"
|
||||
});
|
||||
</script>
|
50
app/pages/commands.vue
Normal file
50
app/pages/commands.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<LayoutTile
|
||||
title="Commands"
|
||||
description="Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application."
|
||||
>
|
||||
<form @submit.prevent="sendCommand()">
|
||||
<div mx-auto max-w-xl lg="mr-0 max-w-lg">
|
||||
<div grid grid-cols-1 gap-x-8 gap-y-6>
|
||||
<div>
|
||||
<label for="command-input" block text-sm text-white font-semibold leading-6>Command input</label>
|
||||
<div mt="2.5">
|
||||
<input id="command-input" v-model="input" type="text" name="command-input" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset">
|
||||
</div>
|
||||
</div>
|
||||
<div flex justify-end>
|
||||
<Btn type="submit">
|
||||
Send command
|
||||
</Btn>
|
||||
</div>
|
||||
<div mt-8>
|
||||
<label for="command-output" block text-sm text-white font-semibold leading-6>Command Output</label>
|
||||
<div mt="2.5">
|
||||
<textarea id="command-output" v-model="result" name="command-output" rows="10" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</LayoutTile>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const input = ref("");
|
||||
const result = ref("");
|
||||
|
||||
const sendCommand = async () => {
|
||||
try {
|
||||
const response = await useTauriShellCommand.create("exec-sh", [
|
||||
"-c",
|
||||
input.value
|
||||
]).execute();
|
||||
|
||||
result.value = JSON.stringify(response, null, 4);
|
||||
} catch (error) {
|
||||
result.value = JSON.stringify(error, null, 4);
|
||||
} finally {
|
||||
input.value = "";
|
||||
}
|
||||
};
|
||||
</script>
|
69
app/pages/file.vue
Normal file
69
app/pages/file.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<LayoutTile
|
||||
title="File System"
|
||||
description="Access the file system. For this demo the only allowed permission is read/write to the Documents folder (no sub directories)."
|
||||
>
|
||||
<form @submit.prevent="createDummyFile()">
|
||||
<div mx-auto max-w-xl lg="mr-0 max-w-lg">
|
||||
<div grid grid-cols-1 gap-x-8 gap-y-6>
|
||||
<div>
|
||||
<label for="filename-input" block text-sm text-white font-semibold leading-6>
|
||||
Text file name
|
||||
<span text-neutral-400 font-light>(with extension)</span>
|
||||
</label>
|
||||
<div mt="2.5">
|
||||
<input id="filename-input" v-model="fileName" type="text" name="filename-input" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset">
|
||||
</div>
|
||||
</div>
|
||||
<div mt-8>
|
||||
<label for="filecontent-input" block text-sm text-white font-semibold leading-6>File content</label>
|
||||
<div mt="2.5">
|
||||
<textarea id="filecontent-input" v-model="fileContent" name="filecontent-input" rows="10" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset" />
|
||||
</div>
|
||||
</div>
|
||||
<div flex justify-end gap-3>
|
||||
<div v-if="done" h-full flex-center text-emerald-400 space-x-1>
|
||||
<p>Done</p>
|
||||
<Icon name="heroicons-solid:check" size-4 />
|
||||
</div>
|
||||
<div v-if="error" h-full flex-center text-red-400 space-x-1>
|
||||
<p>Error</p>
|
||||
<Icon name="heroicons-solid:exclamation-triangle" size-4 />
|
||||
</div>
|
||||
<Btn type="submit" :disabled="fileName === ''">
|
||||
Create file
|
||||
</Btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</LayoutTile>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const fileName = ref("");
|
||||
const fileContent = ref("");
|
||||
const done = ref(false);
|
||||
const error = ref(false);
|
||||
|
||||
const createDummyFile = async () => {
|
||||
try {
|
||||
const fileExists = await useTauriFsExists(fileName.value, {
|
||||
baseDir: useTauriFsBaseDirectory.Document
|
||||
});
|
||||
|
||||
if (!fileExists) {
|
||||
await useTauriFsWriteTextFile(fileName.value, fileContent.value, {
|
||||
baseDir: useTauriFsBaseDirectory.Document
|
||||
});
|
||||
done.value = true;
|
||||
fileName.value = fileContent.value = "";
|
||||
setTimeout(() => done.value = false, 3000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error creating file:", err);
|
||||
error.value = true;
|
||||
setTimeout(() => error.value = false, 3000);
|
||||
}
|
||||
};
|
||||
</script>
|
46
app/pages/index.vue
Normal file
46
app/pages/index.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div relative overflow-hidden px-6 lg="px-8">
|
||||
<div grid mx-auto h-screen max-w-2xl place-content-center>
|
||||
<SvgoLogo :filled="true" :font-controlled="false" mx-auto mb-8 size-40 />
|
||||
|
||||
<div text-center>
|
||||
<h1 animate-pulse text-4xl text-light-300 font-bold tracking-wider font-heading sm="text-6xl">
|
||||
{{ name.toUpperCase() }}
|
||||
</h1>
|
||||
<p mt-5 flex gap-1 text-neutral-300 leading-8>
|
||||
Powered by
|
||||
<NuxtLink :to="nuxtSite" target="_blank" text-accent underline>
|
||||
Nuxt 3
|
||||
</NuxtLink>
|
||||
-
|
||||
<NuxtLink :to="tauriSite" target="_blank" text-accent underline>
|
||||
Tauri 2
|
||||
</NuxtLink>
|
||||
-
|
||||
<NuxtLink :to="unoSite" target="_blank" text-accent underline>
|
||||
UnoCSS
|
||||
</NuxtLink>
|
||||
</p>
|
||||
<div mt-15>
|
||||
<Hyperlink :to="repo">
|
||||
Star on GitHub
|
||||
</Hyperlink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fixed bottom-6 text-sm absolute-center-h>
|
||||
<p text-sm text-neutral-500>
|
||||
Made by {{ author }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { name, author, repo, tauriSite, nuxtSite, unoSite } = useAppConfig();
|
||||
|
||||
definePageMeta({
|
||||
layout: "home"
|
||||
});
|
||||
</script>
|
66
app/pages/notifications.vue
Normal file
66
app/pages/notifications.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<LayoutTile
|
||||
title="Notifications"
|
||||
description="Send native notifications to the client using the notification plugin."
|
||||
>
|
||||
<form @submit.prevent="createNotification()">
|
||||
<div class="mx-auto max-w-xl lg:mr-0 lg:max-w-lg">
|
||||
<div class="grid grid-cols-1 gap-x-8 gap-y-6">
|
||||
<div>
|
||||
<label for="notification-title" block text-sm text-white font-semibold leading-6>Notification title</label>
|
||||
<div mt="2.5">
|
||||
<input id="notification-title" v-model="notificationTitle" type="text" name="notification-title" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="notification-body" block text-sm text-white font-semibold leading-6>Notification body</label>
|
||||
<div mt="2.5">
|
||||
<input id="notification-body" v-model="notificationBody" type="text" name="notification-body" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<Btn type="submit">
|
||||
Send notification
|
||||
</Btn>
|
||||
</div>
|
||||
|
||||
<p v-if="permissionError" mt-3 text-right text-sm text-red-500 font-semibold leading-6>
|
||||
Missing permissions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</LayoutTile>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const notificationTitle = ref("");
|
||||
const notificationBody = ref("");
|
||||
const permissionError = ref(false);
|
||||
|
||||
const createNotification = async () => {
|
||||
let permissionGranted = await useTauriNotificationIsPermissionGranted();
|
||||
|
||||
if (!permissionGranted) {
|
||||
const permission = await useTauriNotificationRequestPermission();
|
||||
permissionGranted = permission === "granted";
|
||||
}
|
||||
|
||||
if (permissionGranted) {
|
||||
useTauriNotificationSendNotification({
|
||||
title: notificationTitle.value,
|
||||
body: notificationBody.value
|
||||
});
|
||||
|
||||
notificationTitle.value = "";
|
||||
notificationBody.value = "";
|
||||
} else {
|
||||
permissionError.value = true;
|
||||
|
||||
useTimeoutFn(() => {
|
||||
permissionError.value = false;
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
</script>
|
21
app/pages/os.vue
Normal file
21
app/pages/os.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<LayoutTile
|
||||
title="OS Information"
|
||||
description="Read information about the operating system using the OS Information plugin."
|
||||
>
|
||||
<div overflow-hidden rounded-lg bg-neutral-800 shadow>
|
||||
<dl divide-y divide-neutral-600>
|
||||
<TabRow heading="Platform" :body="`${currentPlatform} ${currentVersion}`" />
|
||||
<TabRow heading="Arch" :body="currentArch" />
|
||||
<TabRow heading="Locale" :body="currentLocale" />
|
||||
</dl>
|
||||
</div>
|
||||
</LayoutTile>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const currentPlatform = await useTauriOsPlatform();
|
||||
const currentArch = await useTauriOsArch();
|
||||
const currentVersion = await useTauriOsVersion();
|
||||
const currentLocale = await useTauriOsLocale() || "Not detectable";
|
||||
</script>
|
62
app/pages/store.vue
Normal file
62
app/pages/store.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<LayoutTile
|
||||
title="Store"
|
||||
description="Persistent key-value store. Allows you to handle state to a file which can be saved and loaded on demand including between app restarts."
|
||||
>
|
||||
<form @submit.prevent="setStoreValue()">
|
||||
<div mx-auto max-w-xl lg="mr-0 max-w-lg">
|
||||
<div grid grid-cols-1 gap-x-8 gap-y-6>
|
||||
<div>
|
||||
<label for="store-value" block text-sm text-white font-semibold leading-6>Store value</label>
|
||||
<div mt="2.5">
|
||||
<input id="store-value" v-model="input" type="text" name="store-value" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset">
|
||||
</div>
|
||||
</div>
|
||||
<div flex justify-end>
|
||||
<Btn type="submit">
|
||||
Send command
|
||||
</Btn>
|
||||
</div>
|
||||
<div mt-8>
|
||||
<label for="store-data" block text-sm text-white font-semibold leading-6>Current Store data</label>
|
||||
<div mt="2.5">
|
||||
<textarea id="store-data" v-model="result" name="store-data" rows="10" block w-full border-0 rounded-md bg="white/5" px="3.5" py-2 text-white shadow-sm ring-1 ring="white/10" ring-inset sm="text-sm leading-6" focus="ring-2 ring-emerald-500 ring-inset" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</LayoutTile>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const input = ref("");
|
||||
const result = ref("");
|
||||
const autosave = ref(false);
|
||||
|
||||
const store = await useTauriStoreCreateStore("store.bin", {
|
||||
autosave: autosave.value
|
||||
});
|
||||
|
||||
const getStoreValue = async () => {
|
||||
try {
|
||||
result.value = await store.get<string>("myData");
|
||||
} catch (error) {
|
||||
result.value = JSON.stringify(error, null, 4);
|
||||
}
|
||||
};
|
||||
|
||||
await getStoreValue();
|
||||
|
||||
const setStoreValue = async () => {
|
||||
try {
|
||||
await store.set("myData", input.value);
|
||||
|
||||
await getStoreValue();
|
||||
} catch (error) {
|
||||
result.value = JSON.stringify(error, null, 4);
|
||||
} finally {
|
||||
input.value = "";
|
||||
}
|
||||
};
|
||||
</script>
|
BIN
app/public/logo.png
Normal file
BIN
app/public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
app/public/screenshot.png
Normal file
BIN
app/public/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 409 KiB |
Reference in New Issue
Block a user