feat: base frame

This commit is contained in:
2024-07-16 21:14:07 +08:00
parent 71dc75a092
commit 1d213eae57
33 changed files with 1493 additions and 123 deletions

1
components.d.ts vendored
View File

@@ -11,5 +11,6 @@ declare module 'vue' {
Greet: typeof import('./src/components/Greet.vue')['default'] Greet: typeof import('./src/components/Greet.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
TablerDeviceWatch: typeof import('./src/components/icons/TablerDeviceWatch.vue')['default']
} }
} }

View File

@@ -17,7 +17,12 @@
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1", "@tauri-apps/cli": "^1",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.39",
"sass": "^1.77.8",
"tailwindcss": "^3.4.4",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"unplugin-vue-components": "^0.27.2",
"vite": "^5.3.1", "vite": "^5.3.1",
"vue-tsc": "^2.0.22" "vue-tsc": "^2.0.22"
} }

1023
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

11
src-tauri/Cargo.lock generated
View File

@@ -2670,6 +2670,15 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.7" version = "0.3.7"
@@ -3216,7 +3225,9 @@ dependencies = [
"libc", "libc",
"mio", "mio",
"num_cpus", "num_cpus",
"parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.48.0", "windows-sys 0.48.0",

View File

@@ -15,7 +15,7 @@ tauri = { version = "1", features = [ "window-show", "window-hide", "window-maxi
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
btleplug = { version = "0.11.5", features = ["serde"] } btleplug = { version = "0.11.5", features = ["serde"] }
tokio = { version = "1.38.0", features = ["rt", "rt-multi-thread", "macros"] } tokio = { version = "1.38.0", features = ["full"] }
futures = "0.3.30" futures = "0.3.30"
uuid = "1.10.0" uuid = "1.10.0"
lazy_static = "1.5.0" lazy_static = "1.5.0"

View File

@@ -1,13 +1,17 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{AppHandle, Manager, State};
use tokio;
use tokio::sync::{Mutex};
use btleplug::api::{Central, ScanFilter, Manager as _, Peripheral as _};
use btleplug::api::bleuuid::uuid_from_u16; use btleplug::api::bleuuid::uuid_from_u16;
use btleplug::api::CentralEvent::{
DeviceConnected, DeviceDisconnected, DeviceDiscovered, DeviceUpdated,
ManufacturerDataAdvertisement, ServiceDataAdvertisement, ServicesAdvertisement,
};
use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter};
use btleplug::platform::{Adapter, Manager as BtleManager, Peripheral}; use btleplug::platform::{Adapter, Manager as BtleManager, Peripheral};
use futures::StreamExt; use futures::StreamExt;
use tauri::{AppHandle, Manager, State};
use tokio;
use tokio::sync::Mutex;
#[derive(serde::Serialize, Clone)] #[derive(serde::Serialize, Clone)]
struct BleDevice { struct BleDevice {
@@ -15,30 +19,90 @@ struct BleDevice {
address: String, address: String,
} }
struct BleConnection { struct BleConnection<'a> {
manager: Mutex<Option<BtleManager>>, central: Mutex<Option<Adapter>>,
adapter: Mutex<Option<Adapter>>, peripheral: &'a Mutex<Option<Peripheral>>,
peripheral: Mutex<Option<Peripheral>>, scan_devices: Mutex<Vec<BleDevice>>,
} }
#[tauri::command] #[tauri::command]
async fn scan_devices( async fn request_central_events<'a>(
connection: State<'_, BleConnection> app_handle: AppHandle,
) -> Result<String, String> { connection: State<'a, BleConnection<'a>>,
let adapter = connection.adapter.lock().await; ) -> Result<bool, String> {
let adapter = adapter.as_ref().unwrap(); let central = connection.central.lock().await;
adapter.start_scan(ScanFilter::default()).await.unwrap(); let peripheral = connection.peripheral.lock().await;
tokio::time::sleep(std::time::Duration::from_secs(2)).await; let central = central.as_ref().unwrap();
let central = central.clone();
let mut event_stream = central.events().await.unwrap();
tauri::async_runtime::spawn(async move {
while let Some(event) = event_stream.next().await {
if let DeviceDiscovered(_) | DeviceUpdated(_) = event.clone() {
let mut devices = vec![]; let mut devices = vec![];
for p in adapter.peripherals().await.unwrap() { for p in central.peripherals().await.unwrap() {
devices.push(BleDevice { devices.push(BleDevice {
name: p.properties().await.unwrap().unwrap().local_name.unwrap_or("Unknown".to_string()), name: p
.properties()
.await
.unwrap()
.unwrap()
.local_name
.unwrap_or("Unknown".to_string()),
address: p.address().to_string(), address: p.address().to_string(),
}); });
} }
println!("Devices: {:?}", serde_json::to_string(&devices).unwrap());
app_handle
.emit_all("scan-list-update", serde_json::to_string(&devices).unwrap())
.unwrap();
}
if let DeviceConnected(_) = event.clone() {
let peripheral = peripheral.as_ref().unwrap().clone();
app_handle
.emit_all(
"device-connected",
serde_json::to_string(&BleDevice {
name: peripheral
.properties()
.await
.unwrap()
.unwrap()
.local_name
.unwrap_or("Unknown".to_string()),
address: peripheral.address().to_string(),
})
.unwrap(),
)
.unwrap();
}
}
});
Ok(serde_json::to_string(&devices).unwrap()) Ok(true)
}
#[tauri::command]
async fn start_scan(connection: State<'_, BleConnection>) -> Result<bool, String> {
let central = connection.central.lock().await;
let central = central.as_ref().unwrap();
central
.start_scan(ScanFilter::default())
.await
.unwrap_or_else(|_| {
println!("Failed to start scan");
});
Ok(true)
}
#[tauri::command]
async fn stop_scan(connection: State<'_, BleConnection>) -> Result<bool, String> {
let central = connection.central.lock().await;
let central = central.as_ref().unwrap();
central.stop_scan().await.unwrap_or_else(|_| {
println!("Failed to stop scan");
});
Ok(true)
} }
#[tauri::command] #[tauri::command]
@@ -47,12 +111,17 @@ async fn connect(
connection: State<'_, BleConnection>, connection: State<'_, BleConnection>,
app_handle: AppHandle, app_handle: AppHandle,
) -> Result<bool, String> { ) -> Result<bool, String> {
let adapter = connection.adapter.lock().await; let central = connection.central.lock().await;
let adapter = adapter.as_ref().unwrap(); let central = central.as_ref().unwrap();
// adapter.start_scan(ScanFilter::default()).await.unwrap();
// tokio::time::sleep(std::time::Duration::from_secs(2)).await;
let peripheral = adapter.peripherals().await.unwrap().into_iter().find(|p| p.address().to_string() == address).unwrap().clone(); let peripheral = central
.peripherals()
.await
.unwrap()
.into_iter()
.find(|p| p.address().to_string() == address)
.unwrap()
.clone();
peripheral.connect().await.unwrap(); peripheral.connect().await.unwrap();
*connection.peripheral.lock().await = Some(peripheral.clone()); *connection.peripheral.lock().await = Some(peripheral.clone());
@@ -60,16 +129,21 @@ async fn connect(
peripheral.discover_services().await.unwrap_or_else(|_| { peripheral.discover_services().await.unwrap_or_else(|_| {
println!("Failed to discover services"); println!("Failed to discover services");
}); });
let service = peripheral.services() let service = peripheral
.services()
.into_iter() .into_iter()
.find(|s| s.uuid == uuid_from_u16(0x180D)) .find(|s| s.uuid == uuid_from_u16(0x180D))
.unwrap(); .unwrap();
let characteristic = service.characteristics let characteristic = service
.characteristics
.into_iter() .into_iter()
.find(|c| c.uuid == uuid_from_u16(0x2A37)) .find(|c| c.uuid == uuid_from_u16(0x2A37))
.unwrap(); .unwrap();
peripheral.subscribe(&characteristic).await.unwrap_or_else(|_| { peripheral
.subscribe(&characteristic)
.await
.unwrap_or_else(|_| {
println!("Failed to subscribe to characteristic"); println!("Failed to subscribe to characteristic");
}); });
@@ -77,7 +151,9 @@ async fn connect(
let mut notification_stream = peripheral.notifications().await.unwrap(); let mut notification_stream = peripheral.notifications().await.unwrap();
while let Some(notification) = notification_stream.next().await { while let Some(notification) = notification_stream.next().await {
if notification.uuid == uuid_from_u16(0x2A37) { if notification.uuid == uuid_from_u16(0x2A37) {
app_handle.emit_all("heart-rate", notification.value[1]).unwrap(); app_handle
.emit_all("heart-rate", notification.value[1])
.unwrap();
} }
} }
}); });
@@ -86,9 +162,7 @@ async fn connect(
} }
#[tauri::command] #[tauri::command]
async fn disconnect( async fn disconnect(connection: State<'_, BleConnection>) -> Result<bool, String> {
connection: State<'_, BleConnection>
) -> Result<bool, String> {
let connection = connection.peripheral.lock().await; let connection = connection.peripheral.lock().await;
let connection = connection.as_ref().unwrap(); let connection = connection.as_ref().unwrap();
@@ -98,31 +172,44 @@ async fn disconnect(
} }
#[tauri::command] #[tauri::command]
async fn get_connected_device( async fn get_connected_device(connection: State<'_, BleConnection>) -> Result<String, String> {
connection: State<'_, BleConnection>
) -> Result<String, String> {
let connection = connection.peripheral.lock().await; let connection = connection.peripheral.lock().await;
let connection = connection.as_ref().unwrap(); let connection = connection.as_ref().unwrap();
Ok(serde_json::to_string(&BleDevice { Ok(serde_json::to_string(&BleDevice {
name: connection.properties().await.unwrap().unwrap().local_name.unwrap_or("Unknown".to_string()), name: connection
.properties()
.await
.unwrap()
.unwrap()
.local_name
.unwrap_or("Unknown".to_string()),
address: connection.address().to_string(), address: connection.address().to_string(),
}).unwrap()) })
.unwrap())
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let ble_manager = BtleManager::new().await.unwrap(); let ble_manager = BtleManager::new().await.unwrap();
let adapter = ble_manager.adapters().await.unwrap().into_iter().next().unwrap(); let central = ble_manager
.adapters()
.await
.unwrap()
.into_iter()
.next()
.unwrap();
tauri::Builder::default() tauri::Builder::default()
.manage(BleConnection { .manage(BleConnection {
manager: Mutex::new(Some(ble_manager)), central: Mutex::new(Some(central)),
adapter: Mutex::new(Some(adapter)),
peripheral: Default::default(), peripheral: Default::default(),
scan_devices: Default::default(),
}) })
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
scan_devices, request_central_events,
start_scan,
stop_scan,
connect, connect,
disconnect, disconnect,
get_connected_device get_connected_device

View File

@@ -32,9 +32,9 @@
{ {
"title": "Heartbeat Cat", "title": "Heartbeat Cat",
"minWidth": 800, "minWidth": 800,
"minHeight": 600, "minHeight": 500,
"width": 800, "width": 800,
"height": 600 "height": 500
} }
], ],
"security": { "security": {

View File

@@ -1,11 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { invoke } from "@tauri-apps/api/tauri";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { RouterView } from "vue-router";
import DrawerContainer from "./components/DrawerContainer.vue"; invoke("request_central_events").then(res => {
console.log('central event listening', res);
})
listen("heart-rate", (hr) => { listen("heart-rate", (hr) => {
console.log('Heart Rate', hr); console.log('Heart Rate', hr);
}) })
listen("scan-list-update", (event) => {
console.log('scan-list-update', event.payload);
})
listen("device-connected", (event) => {
console.log('device-connected', event);
})
</script> </script>
<template> <template>
@@ -13,7 +24,7 @@ listen("heart-rate", (hr) => {
<DrawerContainer> <DrawerContainer>
<RouterView v-slot="{ Component }"> <RouterView v-slot="{ Component }">
<Transition name="scale" mode="out-in"> <Transition name="scale" mode="out-in">
<KeepAlive> <KeepAlive :exclude="['settings']">
<component :is="Component" /> <component :is="Component" />
</KeepAlive> </KeepAlive>
</Transition> </Transition>
@@ -23,6 +34,9 @@ listen("heart-rate", (hr) => {
</template> </template>
<style> <style>
@import './assets/css/font.css';
@import './assets/css/style.css';
:root { :root {
--app-background: #f8f8f8; --app-background: #f8f8f8;
@@ -65,13 +79,14 @@ body {
} }
body { body {
font-family: Rubik, 'Noto Sans SC', sans-serif;
font-weight: 400;
background-color: var(--app-background); background-color: var(--app-background);
font-family: MiSans, sans-serif;
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
} }
.app-top-bar { .titlebar {
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
-webkit-app-region: drag; -webkit-app-region: drag;
@@ -90,7 +105,7 @@ body {
overflow: hidden; overflow: hidden;
} }
.app-top-bar .icon { .titlebar .icon {
height: calc(var(--title-bar-height) - 6px); height: calc(var(--title-bar-height) - 6px);
} }
</style> </style>

109
src/assets/css/font.css Normal file
View File

@@ -0,0 +1,109 @@
/* noto-sans-sc-200 - chinese-simplified_latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Noto Sans SC';
font-style: normal;
font-weight: 200;
src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-200.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* noto-sans-sc-regular - chinese-simplified_latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Noto Sans SC';
font-style: normal;
font-weight: 400;
src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-regular.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* noto-sans-sc-600 - chinese-simplified_latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Noto Sans SC';
font-style: normal;
font-weight: 600;
src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-600.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* noto-sans-sc-800 - chinese-simplified_latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Noto Sans SC';
font-style: normal;
font-weight: 800;
src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-800.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* rubik-300 - latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Rubik';
font-style: normal;
font-weight: 300;
src: url('../fonts/rubik-v28-latin-300.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* rubik-300italic - latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Rubik';
font-style: italic;
font-weight: 300;
src: url('../fonts/rubik-v28-latin-300italic.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* rubik-regular - latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Rubik';
font-style: normal;
font-weight: 400;
src: url('../fonts/rubik-v28-latin-regular.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* rubik-italic - latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Rubik';
font-style: italic;
font-weight: 400;
src: url('../fonts/rubik-v28-latin-italic.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* rubik-600 - latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
src: url('../fonts/rubik-v28-latin-600.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* rubik-600italic - latin */
@font-face {
font-display: swap;
/* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Rubik';
font-style: italic;
font-weight: 600;
src: url('../fonts/rubik-v28-latin-600italic.woff2') format('woff2');
/* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}

20
src/assets/css/style.css Normal file
View File

@@ -0,0 +1,20 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
* {
-webkit-user-drag: none;
@apply select-none;
}
}
@layer utilities {
.allow-drag {
-webkit-user-drag: auto;
}
.allow-select {
@apply select-text;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,9 +3,9 @@ import { ref } from 'vue'
const navList = ref([ const navList = ref([
{ title: '设备连接', path: '/' }, { title: '设备连接', path: '/' },
{ title: '心率曲线', path: '/hr-chart' }, { title: '心率曲线', path: '/charts' },
{ title: '桌面组件', path: '/widgets' }, { title: '桌面组件', path: '/widgets' },
{ title: '推流插件', path: '/streaming-plugin' }, { title: '推流插件', path: '/streaming-plugins' },
{ title: '设置', path: '/settings' }, { title: '设置', path: '/settings' },
]) ])
</script> </script>
@@ -14,25 +14,48 @@ const navList = ref([
<div class="drawer"> <div class="drawer">
<div class="side"> <div class="side">
<div class="nav"> <div class="nav">
<router-link <router-link v-for="item in navList" :key="item.path" :to="item.path" class="nav-item"
v-for="item in navList" active-class="active">
:key="item.path" <span>{{ item.title }}</span>
:to="item.path"
class="nav-item"
active-class="active"
>
{{ item.title }}
</router-link> </router-link>
</div> </div>
<div class="status"></div> <div class="status">
status
</div> </div>
</div>
<div class="content">
<slot /> <slot />
</div> </div>
</div>
</template> </template>
<style scoped> <style scoped>
.drawer { .drawer {
display: flex; background-color: var(--app-background);
height: 100%; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23ccc' fill-opacity='0.1' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
@apply w-full h-screen flex;
.side {
background-color: var(--drawer-bar-background);
@apply w-[120px] flex flex-col;
.nav {
@apply flex-1 flex flex-col;
.nav-item {
background-color: var(--drawer-bar-item-background);
@apply flex justify-center items-center px-1 py-3.5 text-xs text-neutral-500 font-semibold transition-colors duration-150;
&.active {
/* background-color: var(--drawer-bar-item-active-background); */
@apply text-pink-400 border-l-4 border-pink-400 bg-pink-400/10 pl-0;
}
}
}
}
.content {
@apply flex-1;
}
} }
</style> </style>

View File

@@ -22,7 +22,7 @@ async function disconnect() {
} }
async function scan() { async function scan() {
invoke("scan_devices").then(res => { invoke("start_scan").then(res => {
console.log(JSON.parse(res as string)); console.log(JSON.parse(res as string));
}) })
} }

View File

@@ -0,0 +1,10 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 9a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3zm3 9v3h6v-3M9 6V3h6v3"></path></svg>
</template>
<script>
export default {
name: 'TablerDeviceWatch'
}
</script>

View File

@@ -2,9 +2,14 @@ import { createApp } from "vue";
import { createRouter, createWebHistory } from "vue-router"; import { createRouter, createWebHistory } from "vue-router";
import App from "./App.vue"; import App from "./App.vue";
import Index from "./pages/index.vue";
const routes = [{ path: "/", component: Index }]; const routes = [
{ path: "/", component: () => import("./pages/index.vue") },
{ path: "/charts", component: () => import("./pages/charts.vue") },
{ path: "/widgets", component: () => import("./pages/widgets.vue") },
{ path: "/streaming-plugins", component: () => import("./pages/streaming-plugins.vue") },
{ path: "/settings", component: () => import("./pages/settings.vue") },
];
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),

13
src/pages/charts.vue Normal file
View File

@@ -0,0 +1,13 @@
<script lang="ts" setup>
</script>
<template>
<div>
Charts
</div>
</template>
<style scoped>
</style>

View File

@@ -3,7 +3,7 @@
<template> <template>
<div> <div>
index <Greet />
</div> </div>
</template> </template>

12
src/pages/settings.vue Normal file
View File

@@ -0,0 +1,12 @@
<script lang="ts" setup>
</script>
<template>
<div>
Settings Page
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,13 @@
<script lang="ts" setup>
</script>
<template>
<div>
Streaming Plugins
</div>
</template>
<style scoped>
</style>

13
src/pages/widgets.vue Normal file
View File

@@ -0,0 +1,13 @@
<script lang="ts" setup>
</script>
<template>
<div>
Widgets Page
</div>
</template>
<style scoped>
</style>

8
tailwind.config.js Normal file
View File

@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -20,6 +20,6 @@
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "include": ["components.d.ts", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
} }

View File

@@ -1,9 +1,15 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import Components from "unplugin-vue-components/vite";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(async () => ({ export default defineConfig(async () => ({
plugins: [vue()], plugins: [
vue(),
Components({
dts: true,
}),
],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
// //