diff --git a/Cargo.lock b/Cargo.lock index 85b0ce794c..401bd6891c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3876,17 +3876,16 @@ dependencies = [ "tauri-plugin-store2", "tauri-plugin-task", "tauri-plugin-template", + "tauri-plugin-tracing", "tauri-plugin-tray", "tauri-plugin-updater", "tauri-plugin-webhook", - "tauri-plugin-window-state", "tauri-plugin-windows", "tauri-specta", "thiserror 2.0.16", "tokio", "tokio-stream", "tracing", - "tracing-subscriber", "turso", "url", "uuid", @@ -14163,9 +14162,9 @@ dependencies = [ [[package]] name = "tauri" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d545ccf7b60dcd44e07c6fb5aeb09140966f0aabd5d2aa14a6821df7bc99348" +checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c" dependencies = [ "anyhow", "bytes", @@ -14217,9 +14216,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67945dbaf8920dbe3a1e56721a419a0c3d085254ab24cff5b9ad55e2b0016e0b" +checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f" dependencies = [ "anyhow", "cargo_toml", @@ -15055,6 +15054,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "tauri-plugin-tracing" +version = "0.1.0" +dependencies = [ + "dirs 6.0.0", + "serde", + "serde_json", + "specta", + "specta-typescript", + "tauri", + "tauri-plugin", + "tauri-plugin-sentry", + "tauri-specta", + "thiserror 2.0.16", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + [[package]] name = "tauri-plugin-tray" version = "0.1.0" @@ -15121,21 +15139,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "tauri-plugin-window-state" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5f6fe3291bfa609c7e0b0ee3bedac294d94c7018934086ce782c1d0f2a468e" -dependencies = [ - "bitflags 2.9.3", - "log", - "serde", - "serde_json", - "tauri", - "tauri-plugin", - "thiserror 2.0.16", -] - [[package]] name = "tauri-plugin-windows" version = "0.1.0" @@ -16140,6 +16143,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror 1.0.69", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.30" diff --git a/Cargo.toml b/Cargo.toml index 00459d2cae..a567e998fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,10 +85,10 @@ owhisper-config = { path = "owhisper/owhisper-config", package = "owhisper-confi owhisper-interface = { path = "owhisper/owhisper-interface", package = "owhisper-interface" } owhisper-model = { path = "owhisper/owhisper-model", package = "owhisper-model" } -tauri = "2.7" -tauri-build = "2.3" +tauri = "2.8" +tauri-build = "2.4" tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } -tauri-plugin = "2.3" +tauri-plugin = "2.4" tauri-plugin-clipboard-manager = "2.3" tauri-plugin-deep-link = "2.4" tauri-plugin-dialog = "2.3" @@ -97,6 +97,7 @@ tauri-plugin-http = { version = "2.5", features = ["unsafe-headers"] } tauri-plugin-keygen = { git = "https://github.com/bagindo/tauri-plugin-keygen", branch = "v2" } tauri-plugin-machine-uid = "0.1.1" tauri-plugin-opener = "2.5" +tauri-plugin-sentry = { version = "0.4.1" } tauri-plugin-shell = "2.3" tauri-plugin-store = "2.4" @@ -118,6 +119,7 @@ tauri-plugin-sse = { path = "plugins/sse" } tauri-plugin-store2 = { path = "plugins/store2" } tauri-plugin-task = { path = "plugins/task" } tauri-plugin-template = { path = "plugins/template" } +tauri-plugin-tracing = { path = "plugins/tracing" } tauri-plugin-tray = { path = "plugins/tray" } tauri-plugin-webhook = { path = "plugins/webhook" } tauri-plugin-windows = { path = "plugins/windows" } diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 0a9e8a34a2..ce5ee71d11 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -35,6 +35,7 @@ "@hypr/plugin-task": "workspace:^", "@hypr/plugin-template": "workspace:^", "@hypr/plugin-windows": "workspace:^", + "@hypr/plugin-tracing": "workspace:^", "@hypr/tiptap": "workspace:^", "@hypr/ui": "workspace:^", "@hypr/utils": "workspace:^", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 1f278c1b5f..2da2805978 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -50,31 +50,30 @@ tauri-plugin-sse = { workspace = true } tauri-plugin-store2 = { workspace = true } tauri-plugin-task = { workspace = true } tauri-plugin-template = { workspace = true } +tauri-plugin-tracing = { workspace = true } tauri-plugin-tray = { workspace = true } tauri-plugin-webhook = { workspace = true } tauri-plugin-windows = { workspace = true } tauri = { workspace = true, features = ["specta", "macos-private-api"] } -tauri-plugin-autostart = "2" +tauri-plugin-autostart = "2.5" tauri-plugin-clipboard-manager = { workspace = true } tauri-plugin-deep-link = { workspace = true } tauri-plugin-dialog = { workspace = true } tauri-plugin-fs = { workspace = true, features = ["watch"] } -tauri-plugin-global-shortcut = "2" +tauri-plugin-global-shortcut = "2.3" tauri-plugin-http = { workspace = true } tauri-plugin-keygen = { workspace = true } -tauri-plugin-os = "2" +tauri-plugin-os = "2.3" tauri-plugin-prevent-default = { version = "1.2", features = ["unstable-windows"] } -tauri-plugin-process = "2" -tauri-plugin-sentry = { version = "0.4.1", features = ["tracing"] } +tauri-plugin-process = "2.3" +tauri-plugin-sentry = { workspace = true, features = ["tracing"] } tauri-plugin-shell = { workspace = true } tauri-plugin-single-instance = { version = "2", features = ["deep-link"] } tauri-plugin-store = { workspace = true } -tauri-plugin-updater = "2" -tauri-plugin-window-state = "2" +tauri-plugin-updater = "2.9" tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter", "chrono"] } specta = { workspace = true } specta-typescript = { workspace = true } diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index 687487f5f2..3e597348dc 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -94,6 +94,7 @@ { "url": "http://**" } ] }, - "dialog:allow-save" + "dialog:allow-save", + "tracing:default" ] } diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index a87a528c63..9703a496e8 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -9,26 +9,10 @@ use store::*; use tauri_plugin_opener::OpenerExt; use tauri_plugin_windows::{HyprWindow, WindowsPluginExt}; -use tracing_subscriber::{ - fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, -}; - #[tokio::main] pub async fn main() { tauri::async_runtime::set(tokio::runtime::Handle::current()); - { - let env_filter = EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new("info")) - .add_directive("ort=warn".parse().unwrap()); - - tracing_subscriber::Registry::default() - .with(fmt::layer()) - .with(env_filter) - .with(tauri_plugin_sentry::sentry::integrations::tracing::layer()) - .init(); - } - let sentry_client = tauri_plugin_sentry::sentry::init(( { #[cfg(not(debug_assertions))] @@ -76,6 +60,7 @@ pub async fn main() { .plugin(tauri_plugin_sse::init()) .plugin(tauri_plugin_misc::init()) .plugin(tauri_plugin_db::init()) + .plugin(tauri_plugin_tracing::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_store::Builder::default().build()) .plugin(tauri_plugin_store2::init()) @@ -93,7 +78,6 @@ pub async fn main() { .plugin(tauri_plugin_obsidian::init()) .plugin(tauri_plugin_sfx::init()) .plugin(tauri_plugin_updater::Builder::new().build()) - .plugin(tauri_plugin_window_state::Builder::default().build()) .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_auth::init()) .plugin(tauri_plugin_clipboard_manager::init()) diff --git a/apps/desktop/src/components/settings/views/help-feedback.tsx b/apps/desktop/src/components/settings/views/help-feedback.tsx index 0a404ff8f3..3e9e6ef624 100644 --- a/apps/desktop/src/components/settings/views/help-feedback.tsx +++ b/apps/desktop/src/components/settings/views/help-feedback.tsx @@ -1,22 +1,28 @@ import { Trans } from "@lingui/react/macro"; -import { openUrl } from "@tauri-apps/plugin-opener"; +import { openPath, openUrl } from "@tauri-apps/plugin-opener"; import { Book, Bug, ExternalLinkIcon, MessageSquare } from "lucide-react"; +import { commands as tracingCommands } from "@hypr/plugin-tracing"; + export default function HelpFeedback() { const handleOpenFeedback = () => { openUrl("https://hyprnote.canny.io/feature-requests"); }; const handleOpenDocs = () => { - // You can update this URL to your actual documentation - openUrl("https://hyprnote.com/docs"); + openUrl("https://docs.hyprnote.com"); }; const handleReportBug = () => { - // You can update this URL to your bug reporting page openUrl("https://hyprnote.canny.io/bugs"); }; + const handleOpenLogs = () => { + tracingCommands.logsDir().then((logsDir) => { + openPath(logsDir); + }); + }; + return (
@@ -81,16 +87,26 @@ export default function HelpFeedback() {
-
- -
-

- About -

-

- Hyprnote - Your intelligent note-taking companion -

+ {/* Logs */} + +
); diff --git a/crates/detect/src/list.rs b/crates/detect/src/list.rs index c91959be17..b36ba370a7 100644 --- a/crates/detect/src/list.rs +++ b/crates/detect/src/list.rs @@ -7,6 +7,7 @@ pub struct InstalledApp { pub bundle_path: String, } +#[cfg(target_os = "macos")] pub fn list_installed_apps() -> Vec { let app_dirs = [ "/Applications", @@ -43,6 +44,7 @@ pub fn list_installed_apps() -> Vec { apps } +#[cfg(target_os = "macos")] fn get_app_info(app_path: &std::path::Path) -> Option { let info_plist_path = app_path.join("Contents/Info.plist"); diff --git a/crates/whisper-local/src/model.rs b/crates/whisper-local/src/model.rs index 4f8a0c5b6f..ea092c60ad 100644 --- a/crates/whisper-local/src/model.rs +++ b/crates/whisper-local/src/model.rs @@ -171,7 +171,7 @@ impl Whisper { .join(" "); if !full_text.is_empty() { - tracing::info!(text = ?full_text, "transcribe_completed"); + tracing::info!(text_length = full_text.len(), "transcribe_completed"); self.dynamic_prompt = full_text; } diff --git a/plugins/tracing/.gitignore b/plugins/tracing/.gitignore new file mode 100644 index 0000000000..50d8e32e89 --- /dev/null +++ b/plugins/tracing/.gitignore @@ -0,0 +1,17 @@ +/.vs +.DS_Store +.Thumbs.db +*.sublime* +.idea/ +debug.log +package-lock.json +.vscode/settings.json +yarn.lock + +/.tauri +/target +Cargo.lock +node_modules/ + +dist-js +dist diff --git a/plugins/tracing/Cargo.toml b/plugins/tracing/Cargo.toml new file mode 100644 index 0000000000..2c3b9b5f79 --- /dev/null +++ b/plugins/tracing/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "tauri-plugin-tracing" +version = "0.1.0" +authors = ["You"] +edition = "2021" +exclude = ["/js", "/node_modules"] +links = "tauri-plugin-tracing" +description = "" + +[build-dependencies] +tauri-plugin = { workspace = true, features = ["build"] } + +[dev-dependencies] +specta-typescript = { workspace = true } + +[dependencies] +tauri = { workspace = true, features = ["tray-icon", "image-png"] } +tauri-specta = { workspace = true, features = ["derive", "typescript"] } + +tauri-plugin-sentry = { workspace = true, features = ["tracing"] } + +dirs = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +specta = { workspace = true, features = ["derive"] } +thiserror = { workspace = true } + +tracing = { workspace = true } +tracing-appender = { version = "0.2" } +tracing-subscriber = { workspace = true, features = ["env-filter", "chrono"] } diff --git a/plugins/tracing/build.rs b/plugins/tracing/build.rs new file mode 100644 index 0000000000..08d5ae3894 --- /dev/null +++ b/plugins/tracing/build.rs @@ -0,0 +1,5 @@ +const COMMANDS: &[&str] = &["logs_dir"]; + +fn main() { + tauri_plugin::Builder::new(COMMANDS).build(); +} diff --git a/plugins/tracing/js/bindings.gen.ts b/plugins/tracing/js/bindings.gen.ts new file mode 100644 index 0000000000..525eace9d5 --- /dev/null +++ b/plugins/tracing/js/bindings.gen.ts @@ -0,0 +1,85 @@ +// @ts-nocheck + + +// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. + +/** user-defined commands **/ + + +export const commands = { +async logsDir() : Promise { + return await TAURI_INVOKE("plugin:tracing|logs_dir"); +} +} + +/** user-defined events **/ + + + +/** user-defined constants **/ + + + +/** user-defined types **/ + + + +/** tauri-specta globals **/ + +import { + invoke as TAURI_INVOKE, + Channel as TAURI_CHANNEL, +} from "@tauri-apps/api/core"; +import * as TAURI_API_EVENT from "@tauri-apps/api/event"; +import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow"; + +type __EventObj__ = { + listen: ( + cb: TAURI_API_EVENT.EventCallback, + ) => ReturnType>; + once: ( + cb: TAURI_API_EVENT.EventCallback, + ) => ReturnType>; + emit: null extends T + ? (payload?: T) => ReturnType + : (payload: T) => ReturnType; +}; + +export type Result = + | { status: "ok"; data: T } + | { status: "error"; error: E }; + +function __makeEvents__>( + mappings: Record, +) { + return new Proxy( + {} as unknown as { + [K in keyof T]: __EventObj__ & { + (handle: __WebviewWindow__): __EventObj__; + }; + }, + { + get: (_, event) => { + const name = mappings[event as keyof T]; + + return new Proxy((() => {}) as any, { + apply: (_, __, [window]: [__WebviewWindow__]) => ({ + listen: (arg: any) => window.listen(name, arg), + once: (arg: any) => window.once(name, arg), + emit: (arg: any) => window.emit(name, arg), + }), + get: (_, command: keyof __EventObj__) => { + switch (command) { + case "listen": + return (arg: any) => TAURI_API_EVENT.listen(name, arg); + case "once": + return (arg: any) => TAURI_API_EVENT.once(name, arg); + case "emit": + return (arg: any) => TAURI_API_EVENT.emit(name, arg); + } + }, + }); + }, + }, + ); +} diff --git a/plugins/tracing/js/index.ts b/plugins/tracing/js/index.ts new file mode 100644 index 0000000000..a96e122f03 --- /dev/null +++ b/plugins/tracing/js/index.ts @@ -0,0 +1 @@ +export * from "./bindings.gen"; diff --git a/plugins/tracing/package.json b/plugins/tracing/package.json new file mode 100644 index 0000000000..f72b9ec5b6 --- /dev/null +++ b/plugins/tracing/package.json @@ -0,0 +1,11 @@ +{ + "name": "@hypr/plugin-tracing", + "private": true, + "main": "./js/index.ts", + "scripts": { + "codegen": "cargo test -p tauri-plugin-tracing" + }, + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } +} diff --git a/plugins/tracing/permissions/autogenerated/commands/hi.toml b/plugins/tracing/permissions/autogenerated/commands/hi.toml new file mode 100644 index 0000000000..5155b505ff --- /dev/null +++ b/plugins/tracing/permissions/autogenerated/commands/hi.toml @@ -0,0 +1,13 @@ +# Automatically generated - DO NOT EDIT! + +"$schema" = "../../schemas/schema.json" + +[[permission]] +identifier = "allow-hi" +description = "Enables the hi command without any pre-configured scope." +commands.allow = ["hi"] + +[[permission]] +identifier = "deny-hi" +description = "Denies the hi command without any pre-configured scope." +commands.deny = ["hi"] diff --git a/plugins/tracing/permissions/autogenerated/commands/logs_dir.toml b/plugins/tracing/permissions/autogenerated/commands/logs_dir.toml new file mode 100644 index 0000000000..6b5e6239bb --- /dev/null +++ b/plugins/tracing/permissions/autogenerated/commands/logs_dir.toml @@ -0,0 +1,13 @@ +# Automatically generated - DO NOT EDIT! + +"$schema" = "../../schemas/schema.json" + +[[permission]] +identifier = "allow-logs-dir" +description = "Enables the logs_dir command without any pre-configured scope." +commands.allow = ["logs_dir"] + +[[permission]] +identifier = "deny-logs-dir" +description = "Denies the logs_dir command without any pre-configured scope." +commands.deny = ["logs_dir"] diff --git a/plugins/tracing/permissions/autogenerated/commands/ping.toml b/plugins/tracing/permissions/autogenerated/commands/ping.toml new file mode 100644 index 0000000000..1d1358807e --- /dev/null +++ b/plugins/tracing/permissions/autogenerated/commands/ping.toml @@ -0,0 +1,13 @@ +# Automatically generated - DO NOT EDIT! + +"$schema" = "../../schemas/schema.json" + +[[permission]] +identifier = "allow-ping" +description = "Enables the ping command without any pre-configured scope." +commands.allow = ["ping"] + +[[permission]] +identifier = "deny-ping" +description = "Denies the ping command without any pre-configured scope." +commands.deny = ["ping"] diff --git a/plugins/tracing/permissions/autogenerated/reference.md b/plugins/tracing/permissions/autogenerated/reference.md new file mode 100644 index 0000000000..3e5bbdc15a --- /dev/null +++ b/plugins/tracing/permissions/autogenerated/reference.md @@ -0,0 +1,95 @@ +## Default Permission + +Default permissions for the plugin + +#### This default permission set includes the following: + +- `allow-logs-dir` + +## Permission Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdentifierDescription
+ +`tracing:allow-hi` + + + +Enables the hi command without any pre-configured scope. + +
+ +`tracing:deny-hi` + + + +Denies the hi command without any pre-configured scope. + +
+ +`tracing:allow-logs-dir` + + + +Enables the logs_dir command without any pre-configured scope. + +
+ +`tracing:deny-logs-dir` + + + +Denies the logs_dir command without any pre-configured scope. + +
+ +`tracing:allow-ping` + + + +Enables the ping command without any pre-configured scope. + +
+ +`tracing:deny-ping` + + + +Denies the ping command without any pre-configured scope. + +
diff --git a/plugins/tracing/permissions/default.toml b/plugins/tracing/permissions/default.toml new file mode 100644 index 0000000000..6f67e833fb --- /dev/null +++ b/plugins/tracing/permissions/default.toml @@ -0,0 +1,3 @@ +[default] +description = "Default permissions for the plugin" +permissions = ["allow-logs-dir"] diff --git a/plugins/tracing/permissions/schemas/schema.json b/plugins/tracing/permissions/schemas/schema.json new file mode 100644 index 0000000000..9808994b26 --- /dev/null +++ b/plugins/tracing/permissions/schemas/schema.json @@ -0,0 +1,342 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PermissionFile", + "description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.", + "type": "object", + "properties": { + "default": { + "description": "The default permission set for the plugin", + "anyOf": [ + { + "$ref": "#/definitions/DefaultPermission" + }, + { + "type": "null" + } + ] + }, + "set": { + "description": "A list of permissions sets defined", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionSet" + } + }, + "permission": { + "description": "A list of inlined permissions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Permission" + } + } + }, + "definitions": { + "DefaultPermission": { + "description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.", + "type": "object", + "required": [ + "permissions" + ], + "properties": { + "version": { + "description": "The version of the permission.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 1.0 + }, + "description": { + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", + "type": [ + "string", + "null" + ] + }, + "permissions": { + "description": "All permissions this set contains.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionSet": { + "description": "A set of direct permissions grouped together under a new name.", + "type": "object", + "required": [ + "description", + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "A unique identifier for the permission.", + "type": "string" + }, + "description": { + "description": "Human-readable description of what the permission does.", + "type": "string" + }, + "permissions": { + "description": "All permissions this set contains.", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionKind" + } + } + } + }, + "Permission": { + "description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.", + "type": "object", + "required": [ + "identifier" + ], + "properties": { + "version": { + "description": "The version of the permission.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 1.0 + }, + "identifier": { + "description": "A unique identifier for the permission.", + "type": "string" + }, + "description": { + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", + "type": [ + "string", + "null" + ] + }, + "commands": { + "description": "Allowed or denied commands when using this permission.", + "default": { + "allow": [], + "deny": [] + }, + "allOf": [ + { + "$ref": "#/definitions/Commands" + } + ] + }, + "scope": { + "description": "Allowed or denied scoped when using this permission.", + "allOf": [ + { + "$ref": "#/definitions/Scopes" + } + ] + }, + "platforms": { + "description": "Target platforms this permission applies. By default all platforms are affected by this permission.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "Commands": { + "description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.", + "type": "object", + "properties": { + "allow": { + "description": "Allowed command.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Denied command, which takes priority.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "Scopes": { + "description": "An argument for fine grained behavior control of Tauri commands.\n\nIt can be of any serde serializable type and is used to allow or prevent certain actions inside a Tauri command. The configured scope is passed to the command and will be enforced by the command implementation.\n\n## Example\n\n```json { \"allow\": [{ \"path\": \"$HOME/**\" }], \"deny\": [{ \"path\": \"$HOME/secret.txt\" }] } ```", + "type": "object", + "properties": { + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "PermissionKind": { + "type": "string", + "oneOf": [ + { + "description": "Enables the hi command without any pre-configured scope.", + "type": "string", + "const": "allow-hi", + "markdownDescription": "Enables the hi command without any pre-configured scope." + }, + { + "description": "Denies the hi command without any pre-configured scope.", + "type": "string", + "const": "deny-hi", + "markdownDescription": "Denies the hi command without any pre-configured scope." + }, + { + "description": "Enables the logs_dir command without any pre-configured scope.", + "type": "string", + "const": "allow-logs-dir", + "markdownDescription": "Enables the logs_dir command without any pre-configured scope." + }, + { + "description": "Denies the logs_dir command without any pre-configured scope.", + "type": "string", + "const": "deny-logs-dir", + "markdownDescription": "Denies the logs_dir command without any pre-configured scope." + }, + { + "description": "Enables the ping command without any pre-configured scope.", + "type": "string", + "const": "allow-ping", + "markdownDescription": "Enables the ping command without any pre-configured scope." + }, + { + "description": "Denies the ping command without any pre-configured scope.", + "type": "string", + "const": "deny-ping", + "markdownDescription": "Denies the ping command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-logs-dir`", + "type": "string", + "const": "default", + "markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-logs-dir`" + } + ] + } + } +} \ No newline at end of file diff --git a/plugins/tracing/src/commands.rs b/plugins/tracing/src/commands.rs new file mode 100644 index 0000000000..17c84bf299 --- /dev/null +++ b/plugins/tracing/src/commands.rs @@ -0,0 +1,10 @@ +use std::path::PathBuf; + +use crate::TracingPluginExt; + +#[tauri::command] +#[specta::specta] +pub async fn logs_dir(app: tauri::AppHandle) -> Result { + let bundle_id = app.config().identifier.clone(); + app.logs_dir(bundle_id).map_err(|e| e.to_string()) +} diff --git a/plugins/tracing/src/errors.rs b/plugins/tracing/src/errors.rs new file mode 100644 index 0000000000..9dbce37bb0 --- /dev/null +++ b/plugins/tracing/src/errors.rs @@ -0,0 +1,13 @@ +use serde::{ser::Serializer, Serialize}; + +#[derive(Debug, thiserror::Error)] +pub enum Error {} + +impl Serialize for Error { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} diff --git a/plugins/tracing/src/ext.rs b/plugins/tracing/src/ext.rs new file mode 100644 index 0000000000..5fb5ea5dac --- /dev/null +++ b/plugins/tracing/src/ext.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; + +pub trait TracingPluginExt { + fn logs_dir(&self, bundle_id: impl Into) -> Result; +} + +impl> TracingPluginExt for T { + fn logs_dir(&self, bundle_id: impl Into) -> Result { + let base_dir = dirs::data_dir().unwrap(); + let logs_dir = base_dir.join(bundle_id.into()).join("logs"); + let _ = std::fs::create_dir_all(&logs_dir); + Ok(logs_dir) + } +} diff --git a/plugins/tracing/src/lib.rs b/plugins/tracing/src/lib.rs new file mode 100644 index 0000000000..c84900549b --- /dev/null +++ b/plugins/tracing/src/lib.rs @@ -0,0 +1,93 @@ +mod commands; +mod errors; +mod ext; + +pub use errors::*; +pub use ext::*; + +use std::{fs, path::PathBuf}; +use tauri::Manager; + +use tracing_appender::{non_blocking::WorkerGuard, rolling}; +use tracing_subscriber::{ + fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, +}; + +const PLUGIN_NAME: &str = "tracing"; + +fn make_specta_builder() -> tauri_specta::Builder { + tauri_specta::Builder::::new() + .plugin_name(PLUGIN_NAME) + .events(tauri_specta::collect_events![]) + .commands(tauri_specta::collect_commands![ + commands::logs_dir:: + ]) + .error_handling(tauri_specta::ErrorHandlingMode::Throw) +} + +pub fn init() -> tauri::plugin::TauriPlugin { + let specta_builder = make_specta_builder(); + + tauri::plugin::Builder::new(PLUGIN_NAME) + .invoke_handler(specta_builder.invoke_handler()) + .setup(move |app, _api| { + specta_builder.mount_events(app); + + let env_filter = EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("info")) + .add_directive("ort=warn".parse().unwrap()); + + if let Some((file_writer, guard)) = make_file_writer_if_enabled( + true, + &app.logs_dir(app.config().identifier.clone()).unwrap(), + ) { + tracing_subscriber::Registry::default() + .with(env_filter) + .with(tauri_plugin_sentry::sentry::integrations::tracing::layer()) + .with(fmt::layer()) + .with(fmt::layer().with_ansi(false).with_writer(file_writer)) + .init(); + assert!(app.manage(guard)); + } else { + tracing_subscriber::Registry::default() + .with(env_filter) + .with(tauri_plugin_sentry::sentry::integrations::tracing::layer()) + .with(fmt::layer()) + .init(); + } + + Ok(()) + }) + .build() +} + +fn make_file_writer_if_enabled( + enabled: bool, + logs_dir: &PathBuf, +) -> Option<(tracing_appender::non_blocking::NonBlocking, WorkerGuard)> { + if !enabled { + return None; + } + + let file_appender = rolling::daily(logs_dir, "log"); + let (non_blocking, guard) = tracing_appender::non_blocking(file_appender); + Some((non_blocking, guard)) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn export_types() { + make_specta_builder() + .export( + specta_typescript::Typescript::default() + .header("// @ts-nocheck\n\n") + .formatter(specta_typescript::formatter::prettier) + .bigint(specta_typescript::BigIntExportBehavior::Number), + "./js/bindings.gen.ts", + ) + .unwrap() + } +} diff --git a/plugins/tracing/tsconfig.json b/plugins/tracing/tsconfig.json new file mode 100644 index 0000000000..13b985325d --- /dev/null +++ b/plugins/tracing/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.base.json", + "include": ["./js/*.ts"], + "exclude": ["node_modules"] +} diff --git a/plugins/tray/src/ext.rs b/plugins/tray/src/ext.rs index 41a81d2b5f..b94bfe1d70 100644 --- a/plugins/tray/src/ext.rs +++ b/plugins/tray/src/ext.rs @@ -136,16 +136,15 @@ impl> TrayPluginExt for T { let app_commit = app.get_git_hash(); let app_backends = app.list_ggml_backends(); + let backends_formatted = app_backends + .iter() + .map(|b| format!("{:?}", b)) + .collect::>() + .join("\n"); + let message = format!( - "{} v{}\n\nSHA: {}\n\nBackends: {}", - app_name, - app_version, - app_commit, - app_backends - .iter() - .map(|b| format!("{:?}", b)) - .collect::>() - .join("\n") + "• App Name: {}\n• App Version: {}\n• SHA:\n {}\n• Backends:\n{}", + app_name, app_version, app_commit, backends_formatted ); let app_clone = app.clone(); @@ -153,7 +152,10 @@ impl> TrayPluginExt for T { app.dialog() .message(&message) .title("About Hyprnote") - .buttons(MessageDialogButtons::OkCustom("Copy".to_string())) + .buttons(MessageDialogButtons::OkCancelCustom( + "Copy".to_string(), + "Cancel".to_string(), + )) .show(move |result| { if result { let _ = app_clone.clipboard().write_text(&message); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 815e52b366..bb895b0e77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -229,6 +229,9 @@ importers: '@hypr/plugin-template': specifier: workspace:^ version: link:../../plugins/template + '@hypr/plugin-tracing': + specifier: workspace:^ + version: link:../../plugins/tracing '@hypr/plugin-windows': specifier: workspace:^ version: link:../../plugins/windows @@ -1007,6 +1010,12 @@ importers: specifier: ^2.8.0 version: 2.8.0 + plugins/tracing: + dependencies: + '@tauri-apps/api': + specifier: ^2.8.0 + version: 2.8.0 + plugins/tray: dependencies: '@tauri-apps/api':